Arthur-Attili commited on
Commit
51c6ad9
·
verified ·
1 Parent(s): 11e0111

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -333
app.py CHANGED
@@ -1,101 +1,72 @@
1
- # app_auto.py - VERSÃO SIMILAR AO CÓDIGO DE IMÓVEIS
2
  import pandas as pd
3
  import requests
4
  import io
5
  import itertools
6
  from collections import defaultdict
7
  import gradio as gr
8
- import time
9
- import math
10
 
11
- # 🔧 CONFIGURAÇÕES GLOBAIS
12
- FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X" # ID da planilha de auto
13
  URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
14
 
15
- # Variáveis globais para armazenamento
16
- df = None
17
  df_auto = None
18
  grupos_auto = None
19
  planilha_carregada = False
 
20
 
21
  def carregar_planilha():
22
- """Carrega e prepara a planilha para análise."""
23
- global df, df_auto, grupos_auto, planilha_carregada
24
-
25
  if planilha_carregada:
26
  return True
27
-
28
  try:
29
  print("⏳ Carregando planilha...")
30
  response = requests.get(URL)
31
  response.raise_for_status()
32
  data = io.BytesIO(response.content)
33
  df = pd.read_excel(data)
34
-
35
- # Padroniza nomes das colunas
36
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
37
-
38
- # Converte colunas numéricas
39
  colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
40
- for coluna in colunas_numericas:
41
- if coluna in df.columns:
42
- df[coluna] = pd.to_numeric(df[coluna], errors='coerce').fillna(0)
43
-
44
- # Verifica colunas essenciais
45
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
46
  raise ValueError("Colunas essenciais não encontradas")
47
-
48
- # Remove duplicatas
49
  df.drop_duplicates(inplace=True)
50
-
51
- # Pré-filtra e pré-processa dados de auto
52
  df_auto = df[df['tipo_do_bem'].str.lower() == 'auto'].copy()
53
- grupos_auto = agrupar_por_administradora(df_auto)
54
-
 
 
 
 
55
  planilha_carregada = True
56
- print("✅ Planilha carregada e pré-processada com sucesso")
57
- print(f"✅ Total de cotas de auto: {len(df_auto)}")
58
- print(f"✅ Administradoras disponíveis: {list(grupos_auto.keys())}")
59
  return True
60
-
61
  except Exception as e:
62
- print(f"❌ Erro ao carregar planilha: {str(e)}")
63
  return False
64
 
65
- def agrupar_por_administradora(dataframe):
66
- """Agrupa cotas por administradora."""
67
- grupos = defaultdict(list)
68
- for _, row in dataframe.iterrows():
69
- grupos[row['administradora']].append(row.to_dict())
70
- return grupos
71
 
72
  def calcular_tempo_parcelas(combinacao):
73
- """Calcula o tempo de parcelas baseado nas cotas da combinação."""
74
  if not combinacao:
75
  return ""
76
-
77
  try:
78
  cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0)))
79
  prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao))
80
-
81
  if not prazos or prazos[0] <= 0:
82
  return ""
83
-
84
  resultado = []
85
  prazo_anterior = 0
86
-
87
- for i, prazo_atual in enumerate(prazos):
88
  cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1]
89
  valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas)
90
-
91
  inicio = prazo_anterior + 1
92
  fim = prazo_atual
93
-
94
  if inicio <= fim and valor_parcela_total > 0:
95
  resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês")
96
-
97
  prazo_anterior = prazo_atual
98
-
99
  if prazos and prazo_anterior < max(prazos):
100
  cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior]
101
  if cotas_ativas_finais:
@@ -104,341 +75,131 @@ def calcular_tempo_parcelas(combinacao):
104
  fim_final = max(prazos)
105
  if valor_parcela_final > 0:
106
  resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês")
107
-
108
  return "\n".join(resultado) if resultado else ""
109
-
110
  except Exception as e:
111
- print(f"Erro ao calcular tempo de parcelas: {str(e)}")
112
  return ""
113
 
114
- def buscar_combinacoes_otimizado(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
115
- """Busca combinações de cotas dentro dos parâmetros especificados de forma otimizada."""
116
- global df_auto, grupos_auto
117
-
118
  if not planilha_carregada:
119
  if not carregar_planilha():
120
- return "❌ Erro: Planilha não carregada. Por favor, tente novamente."
121
-
122
- # Filtra administradoras se especificado
123
- if administradoras and len(administradoras) > 0:
124
- grupos_filtrados = {adm: cotas for adm, cotas in grupos_auto.items() if adm in administradoras}
125
- print(f"🔍 Filtrando por administradoras: {administradoras}")
126
  else:
127
- grupos_filtrados = grupos_auto
128
- print("🔍 Buscando em todas as administradoras")
129
-
130
- if not grupos_filtrados:
131
- return "🚫 Nenhuma cota encontrada com os filtros aplicados. Tente ajustar os parâmetros."
132
-
133
- print(f"🔍 Total de administradoras no filtro: {len(grupos_filtrados)}")
134
-
135
- # TOLERÂNCIAS (idênticas ao código de imóveis)
136
- if credito_desejado > 10000000:
137
- tolerancia_credito = 0.50
138
- tolerancia_entrada = 0.50
139
- tolerancia_parcela = 1.00
140
- max_cotas_combinacao = 25
141
  else:
142
- tolerancia_credito = 0.25
143
- tolerancia_entrada = 0.25
144
- tolerancia_parcela = 0.50
145
- max_cotas_combinacao = 15
146
-
147
- credito_min = credito_desejado * (1 - tolerancia_credito)
148
- credito_max = credito_desejado * (1 + tolerancia_credito)
149
- entrada_min = entrada_desejada * (1 - tolerancia_entrada)
150
- entrada_max = entrada_desejada * (1 + tolerancia_entrada)
151
- parcela_min = parcela_desejada * (1 - tolerancia_parcela)
152
- parcela_max = parcela_desejada * (1 + tolerancia_parcela)
153
 
154
- print(f"💰 Valores desejados: Crédito: R$ {credito_desejado:,.2f}, Entrada: R$ {entrada_desejada:,.2f}, Parcela: R$ {parcela_desejada:,.2f}")
 
 
 
 
 
155
 
156
  resultados = []
157
- total_combinacoes_testadas = 0
158
-
159
- for adm, cotas in grupos_filtrados.items():
160
- print(f"🔍 Analisando administradora: {adm} ({len(cotas)} cotas)")
161
-
162
- cotas_para_combinar = cotas
163
-
164
- for r in range(1, min(max_cotas_combinacao + 1, len(cotas_para_combinar) + 1)):
165
- if credito_desejado > 10000000:
166
- max_combinacoes_por_adm = 7000
167
- else:
168
- max_combinacoes_por_adm = 4000
169
- combinacoes_geradas = 0
170
-
171
- for combinacao in itertools.combinations(cotas_para_combinar, r):
172
- if combinacoes_geradas >= max_combinacoes_por_adm:
173
- break
174
-
175
- total_combinacoes_testadas += 1
176
- combinacoes_geradas += 1
177
-
178
- total_credito = sum(c['valor_credito'] for c in combinacao)
179
- total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
180
- total_entrada = total_entrada_base + (total_credito * 0.085)
181
- total_parcelas = sum(c['valor_parcela'] for c in combinacao)
182
-
183
- if (credito_min <= total_credito <= credito_max and
184
- entrada_min <= total_entrada <= entrada_max and
185
- parcela_min <= total_parcelas <= parcela_max):
186
-
187
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
188
- valor_final = round(total_entrada + total_saldo, 2)
189
- diferenca = abs(valor_final - total_credito)
190
-
191
- resultados.append((adm, combinacao, diferenca))
192
-
193
- if len(resultados) >= max_combinacoes * 3:
194
  break
195
-
196
- if len(resultados) >= max_combinacoes * 3:
197
  break
198
-
199
- if len(resultados) >= max_combinacoes * 3:
200
  break
201
 
202
- print(f"🔍 Total de combinações testadas: {total_combinacoes_testadas}")
203
- print(f"🔍 Combinações válidas encontradas: {len(resultados)}")
204
-
205
  if not resultados:
206
- # Busca ampla com tolerâncias maiores
207
- print("🔍 Nenhuma combinação encontrada. Tentando busca ampla...")
208
- return buscar_combinacoes_amplo(credito_desejado, entrada_desejada, parcela_desejada, administradoras, max_combinacoes)
209
-
210
  resultados.sort(key=lambda x: x[2])
211
- return formatar_resultados(resultados[:max_combinacoes])
212
-
213
- def buscar_combinacoes_amplo(credito_desejado, entrada_desejada, parcela_desejada, administradoras=None, max_combinacoes=15):
214
- """Busca combinações com tolerâncias mais amplas para valores altos."""
215
- global grupos_auto
216
-
217
- if administradoras and len(administradoras) > 0:
218
- grupos_filtrados = {adm: cotas for adm, cotas in grupos_auto.items() if adm in administradoras}
219
- else:
220
- grupos_filtrados = grupos_auto
221
-
222
- if credito_desejado > 10000000:
223
- tolerancia_credito = 0.50
224
- tolerancia_entrada = 0.50
225
- tolerancia_parcela = 1.00
226
- max_cotas_combinacao = 25
227
- else:
228
- tolerancia_credito = 0.25
229
- tolerancia_entrada = 0.25
230
- tolerancia_parcela = 0.50
231
- max_cotas_combinacao = 15
232
-
233
- credito_min = credito_desejado * (1 - tolerancia_credito)
234
- credito_max = credito_desejado * (1 + tolerancia_credito)
235
- entrada_min = entrada_desejada * (1 - tolerancia_entrada)
236
- entrada_max = entrada_desejada * (1 + tolerancia_entrada)
237
- parcela_min = parcela_desejada * (1 - tolerancia_parcela)
238
- parcela_max = parcela_desejada * (1 + tolerancia_parcela)
239
-
240
- resultados = []
241
- total_combinacoes_testadas = 0
242
-
243
- for adm, cotas in grupos_filtrados.items():
244
- print(f"🔍 BUSCA AMPLA - Analisando administradora: {adm}")
245
-
246
- for r in range(1, min(max_cotas_combinacao + 1, len(cotas) + 1)):
247
- combinacoes_geradas = 0
248
- max_combinacoes_por_adm = 7000
249
-
250
- for combinacao in itertools.combinations(cotas, r):
251
- if combinacoes_geradas >= max_combinacoes_por_adm:
252
- break
253
-
254
- total_combinacoes_testadas += 1
255
- combinacoes_geradas += 1
256
-
257
- total_credito = sum(c['valor_credito'] for c in combinacao)
258
- total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
259
- total_entrada = total_entrada_base + (total_credito * 0.085)
260
- total_parcelas = sum(c['valor_parcela'] for c in combinacao)
261
-
262
- if (credito_min <= total_credito <= credito_max and
263
- entrada_min <= total_entrada <= entrada_max and
264
- parcela_min <= total_parcelas <= parcela_max):
265
-
266
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
267
- valor_final = round(total_entrada + total_saldo, 2)
268
- diferenca = abs(valor_final - total_credito)
269
-
270
- resultados.append((adm, combinacao, diferenca))
271
-
272
- if len(resultados) >= max_combinacoes * 2:
273
- break
274
-
275
- if len(resultados) >= max_combinacoes * 2:
276
- break
277
-
278
- if len(resultados) >= max_combinacoes * 2:
279
- break
280
-
281
- print(f"🔍 BUSCA AMPLA: Combinações válidas encontradas: {len(resultados)}")
282
-
283
- if not resultados:
284
- return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
285
-
286
- resultados.sort(key=lambda x: x[2])
287
- return formatar_resultados(resultados[:max_combinacoes])
288
-
289
- def formatar_moeda(valor):
290
- return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
291
 
292
  def formatar_resultados(resultados):
293
- """Formata os resultados para exibição incluindo tempo de parcelas."""
294
  if not resultados:
295
- return "🚫 Nenhuma combinação adequada encontrada com os parâmetros fornecidos."
296
-
297
  resposta = ""
298
- for idx, (adm, combinacao, _) in enumerate(resultados, 1):
299
- codigos = [str(c.get('codigo', 'N/A')) for c in combinacao]
300
- total_credito = sum(c['valor_credito'] for c in combinacao)
301
- total_entrada_base = sum(c['valor_entrada'] for c in combinacao)
302
- total_entrada = total_entrada_base + (total_credito * 0.085)
303
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
304
- total_parcelas = sum(c['valor_parcela'] for c in combinacao)
305
- maior_prazo = max(int(c.get('qtd_parcelas', 0)) for c in combinacao)
306
-
307
- valor_final = round(total_entrada + total_saldo, 2)
308
- credito_real = round(total_credito - total_entrada, 2)
309
- taxa_total = total_saldo - credito_real
310
- taxa_percentual = (taxa_total / credito_real) * 100 if credito_real > 0 else 0
311
- taxa_mensal = taxa_percentual / maior_prazo if maior_prazo > 0 else 0
312
-
313
- tempo_parcelas = calcular_tempo_parcelas(combinacao)
314
-
315
  resposta += f"""
316
  --- COMBINAÇÃO {idx} ---
317
  🏢 Administradora: {adm}
318
- 🔢 Códigos: {', '.join(codigos)} ({len(combinacao)} cotas)
319
- 💰 Crédito Total: {formatar_moeda(total_credito)}
320
- 💵 Entrada (+comissão): {formatar_moeda(total_entrada)}
321
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
322
- 💲 Parcela: {formatar_moeda(total_parcelas)}
323
  ⏳ Prazo: {maior_prazo} meses
324
  🔚 Valor Final: {formatar_moeda(valor_final)}
325
- 💠 Crédito Líquido: {formatar_moeda(credito_real)}
326
- 📊 Taxa Total: {taxa_percentual:.2f}% ({formatar_moeda(taxa_total)})
327
  🗓️ Taxa Mensal: {taxa_mensal:.2f}%
328
  🗓️ Taxa Anual: {taxa_mensal * 12:.2f}%
329
  ⏰ TEMPO DE PARCELAS:
330
- {tempo_parcelas if tempo_parcelas else "Não foi possível calcular o tempo de parcelas"}
331
  """
332
  return resposta
333
 
334
- # Carrega a planilha uma vez quando o aplicativo inicia
335
  carregar_planilha()
336
 
337
- def ajustar_e_buscar(credito_desejado, entrada_desejada, parcela_desejada, administradoras):
338
- return buscar_combinacoes_otimizado(
339
- credito_desejado, entrada_desejada, parcela_desejada, administradoras, max_combinacoes=15
340
- )
341
-
342
- def get_administradoras_options():
343
- if df is not None and planilha_carregada:
344
- adm_list = df[df['tipo_do_bem'].str.lower() == 'auto']['administradora'].unique()
345
- return sorted(adm_list)
346
- return []
347
-
348
- # Interface Gradio (idêntica à do imóvel, apenas com título alterado)
349
  with gr.Blocks(title="Sistema de Busca de Consórcio Auto") as demo:
350
- gr.HTML("""
351
- <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
352
- <style>
353
- * {
354
- font-family: 'Poppins', sans-serif !important;
355
- }
356
-
357
- body {
358
- background-color: #ff8f00 !important;
359
- color: black !important;
360
- }
361
-
362
- input[type="number"] {
363
- background-color: white !important;
364
- border: 2px solid #000 !important;
365
- border-radius: 12px !important;
366
- color: black !important;
367
- padding: 10px !important;
368
- font-weight: 500;
369
- }
370
-
371
- input[type="text"], textarea {
372
- background-color: white !important;
373
- border: 2px solid black !important;
374
- border-radius: 12px !important;
375
- color: black !important;
376
- padding: 10px !important;
377
- }
378
-
379
- input::placeholder, textarea::placeholder {
380
- color: #333 !important;
381
- opacity: 0.7;
382
- }
383
-
384
- .gr-checkbox label {
385
- display: flex;
386
- align-items: center;
387
- gap: 8px;
388
- font-weight: 600;
389
- }
390
-
391
- input[type="checkbox"] {
392
- accent-color: black !important;
393
- width: 18px;
394
- height: 18px;
395
- }
396
-
397
- .gr-button {
398
- font-weight: bold;
399
- border-radius: 10px !important;
400
- background-color: white !important;
401
- color: black !important;
402
- border: 2px solid black !important;
403
- }
404
-
405
- .gr-button:hover {
406
- background-color: #fff3e0 !important;
407
- color: black !important;
408
- }
409
-
410
- label {
411
- font-weight: 600 !important;
412
- }
413
- </style>
414
- """)
415
-
416
  gr.Markdown("# 🚗 Busca de Combinações de Consórcio Auto")
417
  gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
418
-
419
  with gr.Row():
420
  credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000)
421
  entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100)
422
  parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10)
423
-
424
- administradoras_multiselect = gr.CheckboxGroup(
425
  label="Filtrar por Administradoras",
426
- choices=get_administradoras_options(),
427
- value=get_administradoras_options() # Seleciona todas por padrão
 
 
428
  )
429
-
430
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
431
-
432
- output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False, placeholder="Os resultados aparecerão aqui...")
433
-
434
  submit_btn.click(
435
- fn=ajustar_e_buscar,
436
- inputs=[credito_input, entrada_input, parcela_input, administradoras_multiselect],
437
  outputs=output_text
438
  )
439
-
440
  gr.Markdown("---")
441
  gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
442
 
443
  if __name__ == "__main__":
444
- demo.launch()
 
 
1
  import pandas as pd
2
  import requests
3
  import io
4
  import itertools
5
  from collections import defaultdict
6
  import gradio as gr
 
 
7
 
8
+ FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
 
9
  URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
10
 
 
 
11
  df_auto = None
12
  grupos_auto = None
13
  planilha_carregada = False
14
+ ADMIN_OPCOES = []
15
 
16
  def carregar_planilha():
17
+ global df_auto, grupos_auto, planilha_carregada, ADMIN_OPCOES
 
 
18
  if planilha_carregada:
19
  return True
 
20
  try:
21
  print("⏳ Carregando planilha...")
22
  response = requests.get(URL)
23
  response.raise_for_status()
24
  data = io.BytesIO(response.content)
25
  df = pd.read_excel(data)
 
 
26
  df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
 
 
27
  colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
28
+ for col in colunas_numericas:
29
+ if col in df.columns:
30
+ df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
 
 
31
  if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
32
  raise ValueError("Colunas essenciais não encontradas")
 
 
33
  df.drop_duplicates(inplace=True)
 
 
34
  df_auto = df[df['tipo_do_bem'].str.lower() == 'auto'].copy()
35
+ df_auto['administradora'] = df_auto['administradora'].astype(str).str.strip().str.lower()
36
+ df_auto = df_auto[df_auto['administradora'].notna() & (df_auto['administradora'] != '')]
37
+ grupos_auto = defaultdict(list)
38
+ for _, row in df_auto.iterrows():
39
+ grupos_auto[row['administradora']].append(row.to_dict())
40
+ ADMIN_OPCOES = sorted(df_auto['administradora'].unique())
41
  planilha_carregada = True
42
+ print(f"✅ Total de cotas: {len(df_auto)}")
43
+ print(f"✅ Administradoras: {ADMIN_OPCOES}")
 
44
  return True
 
45
  except Exception as e:
46
+ print(f"❌ Erro: {e}")
47
  return False
48
 
49
+ def formatar_moeda(valor):
50
+ return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
 
 
 
 
51
 
52
  def calcular_tempo_parcelas(combinacao):
 
53
  if not combinacao:
54
  return ""
 
55
  try:
56
  cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0)))
57
  prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao))
 
58
  if not prazos or prazos[0] <= 0:
59
  return ""
 
60
  resultado = []
61
  prazo_anterior = 0
62
+ for prazo_atual in prazos:
 
63
  cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1]
64
  valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas)
 
65
  inicio = prazo_anterior + 1
66
  fim = prazo_atual
 
67
  if inicio <= fim and valor_parcela_total > 0:
68
  resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês")
 
69
  prazo_anterior = prazo_atual
 
70
  if prazos and prazo_anterior < max(prazos):
71
  cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior]
72
  if cotas_ativas_finais:
 
75
  fim_final = max(prazos)
76
  if valor_parcela_final > 0:
77
  resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês")
 
78
  return "\n".join(resultado) if resultado else ""
 
79
  except Exception as e:
80
+ print(f"Erro tempo: {e}")
81
  return ""
82
 
83
+ def buscar_combinacoes(credito, entrada, parcela, adm_selecionadas):
 
 
 
84
  if not planilha_carregada:
85
  if not carregar_planilha():
86
+ return "❌ Erro: planilha não carregada."
87
+ if adm_selecionadas:
88
+ grupos = {adm: cotas for adm, cotas in grupos_auto.items() if adm in adm_selecionadas}
 
 
 
89
  else:
90
+ grupos = grupos_auto
91
+ if not grupos:
92
+ return "🚫 Nenhuma cota encontrada com os filtros."
93
+
94
+ # Tolerâncias
95
+ if credito > 5000000:
96
+ tol_cred, tol_ent, tol_par = 0.50, 0.50, 1.00
97
+ max_cotas = 25
98
+ elif credito > 100000:
99
+ tol_cred, tol_ent, tol_par = 0.25, 0.25, 0.50
100
+ max_cotas = 15
 
 
 
101
  else:
102
+ tol_cred, tol_ent, tol_par = 0.15, 0.15, 0.30
103
+ max_cotas = 15
 
 
 
 
 
 
 
 
 
104
 
105
+ cred_min = credito * (1 - tol_cred)
106
+ cred_max = credito * (1 + tol_cred)
107
+ ent_min = entrada * (1 - tol_ent)
108
+ ent_max = entrada * (1 + tol_ent)
109
+ parc_min = parcela * (1 - tol_par)
110
+ parc_max = parcela * (1 + tol_par)
111
 
112
  resultados = []
113
+ for adm, cotas in grupos.items():
114
+ for r in range(1, min(max_cotas + 1, len(cotas) + 1)):
115
+ for comb in itertools.combinations(cotas, r):
116
+ total_cred = sum(c['valor_credito'] for c in comb)
117
+ total_ent_base = sum(c['valor_entrada'] for c in comb)
118
+ total_ent = total_ent_base + total_cred * 0.085
119
+ total_parc = sum(c['valor_parcela'] for c in comb)
120
+ if (cred_min <= total_cred <= cred_max and
121
+ ent_min <= total_ent <= ent_max and
122
+ parc_min <= total_parc <= parc_max):
123
+ total_saldo = sum(c['saldo_devedor'] for c in comb)
124
+ valor_final = total_ent + total_saldo
125
+ diff = abs(valor_final - total_cred)
126
+ resultados.append((adm, comb, diff))
127
+ if len(resultados) >= 30:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  break
129
+ if len(resultados) >= 30:
 
130
  break
131
+ if len(resultados) >= 30:
 
132
  break
133
 
 
 
 
134
  if not resultados:
135
+ return "🚫 Nenhuma combinação encontrada."
 
 
 
136
  resultados.sort(key=lambda x: x[2])
137
+ return formatar_resultados(resultados[:15])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  def formatar_resultados(resultados):
 
140
  if not resultados:
141
+ return "🚫 Nenhuma combinação adequada encontrada."
 
142
  resposta = ""
143
+ for idx, (adm, comb, _) in enumerate(resultados, 1):
144
+ codigos = [str(c.get('codigo', 'N/A')) for c in comb]
145
+ total_cred = sum(c['valor_credito'] for c in comb)
146
+ total_ent_base = sum(c['valor_entrada'] for c in comb)
147
+ total_ent = total_ent_base + total_cred * 0.085
148
+ total_saldo = sum(c['saldo_devedor'] for c in comb)
149
+ total_parc = sum(c['valor_parcela'] for c in comb)
150
+ maior_prazo = max(int(c.get('qtd_parcelas', 0)) for c in comb)
151
+ valor_final = total_ent + total_saldo
152
+ cred_real = total_cred - total_ent
153
+ taxa_total = total_saldo - cred_real
154
+ taxa_perc = (taxa_total / cred_real) * 100 if cred_real > 0 else 0
155
+ taxa_mensal = taxa_perc / maior_prazo if maior_prazo > 0 else 0
156
+ tempo = calcular_tempo_parcelas(comb)
 
 
 
157
  resposta += f"""
158
  --- COMBINAÇÃO {idx} ---
159
  🏢 Administradora: {adm}
160
+ 🔢 Códigos: {', '.join(codigos)} ({len(comb)} cotas)
161
+ 💰 Crédito Total: {formatar_moeda(total_cred)}
162
+ 💵 Entrada (+comissão): {formatar_moeda(total_ent)}
163
  📉 Saldo Devedor: {formatar_moeda(total_saldo)}
164
+ 💲 Parcela: {formatar_moeda(total_parc)}
165
  ⏳ Prazo: {maior_prazo} meses
166
  🔚 Valor Final: {formatar_moeda(valor_final)}
167
+ 💠 Crédito Líquido: {formatar_moeda(cred_real)}
168
+ 📊 Taxa Total: {taxa_perc:.2f}% ({formatar_moeda(taxa_total)})
169
  🗓️ Taxa Mensal: {taxa_mensal:.2f}%
170
  🗓️ Taxa Anual: {taxa_mensal * 12:.2f}%
171
  ⏰ TEMPO DE PARCELAS:
172
+ {tempo if tempo else "Não foi possível calcular"}
173
  """
174
  return resposta
175
 
176
+ # Carrega a planilha antes da interface
177
  carregar_planilha()
178
 
179
+ # Interface simplificada (sem HTML customizado)
 
 
 
 
 
 
 
 
 
 
 
180
  with gr.Blocks(title="Sistema de Busca de Consórcio Auto") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  gr.Markdown("# 🚗 Busca de Combinações de Consórcio Auto")
182
  gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
 
183
  with gr.Row():
184
  credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000)
185
  entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100)
186
  parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10)
187
+ adm_dropdown = gr.Dropdown(
 
188
  label="Filtrar por Administradoras",
189
+ choices=ADMIN_OPCOES,
190
+ value=ADMIN_OPCOES,
191
+ multiselect=True,
192
+ interactive=True
193
  )
 
194
  submit_btn = gr.Button("🔍 Buscar Combinações", variant="primary")
195
+ output_text = gr.Textbox(label="Resultados da Busca", lines=20, max_lines=40, interactive=False)
 
 
196
  submit_btn.click(
197
+ fn=buscar_combinacoes,
198
+ inputs=[credito_input, entrada_input, parcela_input, adm_dropdown],
199
  outputs=output_text
200
  )
 
201
  gr.Markdown("---")
202
  gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio ideais.")
203
 
204
  if __name__ == "__main__":
205
+ demo.launch(share=True)