Arthur-Attili commited on
Commit
554f833
·
verified ·
1 Parent(s): 44de8f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +331 -276
app.py CHANGED
@@ -4,309 +4,364 @@ os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
4
 
5
  import pandas as pd
6
  import requests
7
- import io
8
- import itertools
9
  from collections import defaultdict
10
  import gradio as gr
 
 
11
 
12
- # 🔧 CONFIGURAÇÕES DO ARQUIVO
13
- FILE_ID = "1EoH8V3Fyls0mNa95VcBN6ZL1B552ty3X"
14
- URL = f"https://drive.google.com/uc?export=download&id={FILE_ID}"
15
 
16
- # Variáveis globais
17
- df_auto = None
18
- grupos_auto = None
19
- planilha_carregada = False
20
- ADMIN_OPCOES = []
21
-
22
-
23
- def carregar_planilha():
24
- global df_auto, grupos_auto, planilha_carregada, ADMIN_OPCOES
25
-
26
- if planilha_carregada:
27
- return True
28
 
 
 
 
 
 
29
  try:
30
- print(" Carregando planilha de automóveis...")
31
- response = requests.get(URL)
32
- response.raise_for_status()
33
- data = io.BytesIO(response.content)
34
- df = pd.read_excel(data)
35
-
36
- df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
37
-
38
- colunas_numericas = ['valor_credito', 'valor_entrada', 'valor_parcela', 'saldo_devedor', 'qtd_parcelas']
39
- for col in colunas_numericas:
40
- if col in df.columns:
41
- df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
42
-
43
- if 'tipo_do_bem' not in df.columns or 'administradora' not in df.columns:
44
- raise ValueError("Colunas essenciais não encontradas")
45
-
46
- df.drop_duplicates(inplace=True)
47
-
48
- df_auto = df[df['tipo_do_bem'].str.lower() == 'auto'].copy()
49
- df_auto['administradora'] = df_auto['administradora'].astype(str).str.strip().str.lower()
50
- df_auto = df_auto[df_auto['administradora'].notna() & (df_auto['administradora'] != '')]
51
-
52
- grupos_auto = defaultdict(list)
53
- for _, row in df_auto.iterrows():
54
- grupos_auto[row['administradora']].append(row.to_dict())
55
-
56
- ADMIN_OPCOES = sorted(df_auto['administradora'].unique())
57
- planilha_carregada = True
58
-
59
- print(f"✅ Total de cotas AUTO: {len(df_auto)}")
60
- print(f"✅ Administradoras AUTO: {ADMIN_OPCOES}")
61
- return True
62
-
63
- except Exception as e:
64
- print(f"❌ Erro ao carregar planilha AUTO: {e}")
65
  return False
66
-
67
-
68
- def formatar_moeda(valor):
69
- return f"R$ {valor:,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
70
-
71
-
72
- def definir_margens_absolutas(credito, entrada, parcela):
73
- margem_credito = 20_000 # ex.: 30.000 → 10.000–50.000
74
- margem_entrada = 2_000
75
- margem_parcela = 100
76
-
77
- cred_min = max(0, credito - margem_credito)
78
- cred_max = credito + margem_credito
79
- ent_min = max(0, entrada - margem_entrada)
80
- ent_max = entrada + margem_entrada
81
- parc_min = max(0, parcela - margem_parcela)
82
- parc_max = parcela + margem_parcela
83
-
84
- return cred_min, cred_max, ent_min, ent_max, parc_min, parc_max
85
-
86
-
87
- def filtrar_cotas_por_faixa(cotas, cred_min, cred_max):
88
- return [c for c in cotas if c['valor_credito'] < cred_max * 2]
89
-
90
-
91
- def calcular_tempo_parcelas(combinacao):
92
- if not combinacao:
93
- return ""
94
-
95
  try:
96
- cotas_ordenadas = sorted(combinacao, key=lambda x: int(x.get('qtd_parcelas', 0)))
97
- prazos = sorted(set(int(c.get('qtd_parcelas', 0)) for c in combinacao))
98
-
99
- if not prazos or prazos[0] <= 0:
100
- return ""
101
-
102
- resultado = []
103
- prazo_anterior = 0
104
-
105
- for prazo_atual in prazos:
106
- cotas_ativas = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) >= prazo_anterior + 1]
107
- valor_parcela_total = sum(c.get('valor_parcela', 0) for c in cotas_ativas)
108
-
109
- inicio = prazo_anterior + 1
110
- fim = prazo_atual
111
-
112
- if inicio <= fim and valor_parcela_total > 0:
113
- resultado.append(f"{inicio}x à {fim}x: {formatar_moeda(valor_parcela_total)}/mês")
114
-
115
- prazo_anterior = prazo_atual
116
-
117
- if prazos and prazo_anterior < max(prazos):
118
- cotas_ativas_finais = [c for c in cotas_ordenadas if int(c.get('qtd_parcelas', 0)) > prazo_anterior]
119
- if cotas_ativas_finais:
120
- valor_parcela_final = sum(c.get('valor_parcela', 0) for c in cotas_ativas_finais)
121
- inicio_final = prazo_anterior + 1
122
- fim_final = max(prazos)
123
- if valor_parcela_final > 0:
124
- resultado.append(f"{inicio_final}x à {fim_final}x: {formatar_moeda(valor_parcela_final)}/mês")
125
-
126
- return "\\n".join(resultado) if resultado else ""
127
-
128
  except Exception as e:
129
- print(f"❌ Erro calcular tempo de parcelas AUTO: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  return ""
131
-
132
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  def buscar_combinacoes(credito, entrada, parcela, adm_selecionadas):
134
- if not planilha_carregada:
135
- if not carregar_planilha():
136
- return "❌ Erro: planilha de automóveis não carregada."
137
-
138
- # Filtra administradoras
139
- if adm_selecionadas:
140
- grupos = {adm: cotas for adm, cotas in grupos_auto.items() if adm in adm_selecionadas}
141
- else:
142
- grupos = grupos_auto
143
-
144
- if not grupos:
145
- return "🚫 Nenhuma cota de automóvel encontrada com os filtros."
146
-
147
- # Margens absolutas
148
- cred_min, cred_max, ent_min, ent_max, parc_min, parc_max = definir_margens_absolutas(
149
- float(credito or 0),
150
- float(entrada or 0),
151
- float(parcela or 0)
152
- )
153
-
154
- print(f"💰 AUTO - Crédito: R$ {credito:,.2f} → [{cred_min:,.2f}, {cred_max:,.2f}]")
155
-
156
- # Limites de busca
157
- max_cotas_combinacao = 15
158
- max_combinacoes_por_adm = 4000
159
-
160
- resultados = []
161
- total_combinacoes_testadas = 0
162
-
163
- for adm, cotas in grupos.items():
164
- print(f"🔍 AUTO - Administradora: {adm} ({len(cotas)} cotas)")
165
- cotas_filtradas = filtrar_cotas_por_faixa(cotas, cred_min, cred_max)
166
-
167
- for r in range(1, min(max_cotas_combinacao + 1, len(cotas_filtradas) + 1)):
168
- combinacoes_geradas = 0
169
- for combinacao in itertools.combinations(cotas_filtradas, r):
170
- if combinacoes_geradas >= max_combinacoes_por_adm:
171
- break
172
-
173
- total_combinacoes_testadas += 1
174
- combinacoes_geradas += 1
175
-
176
- total_cred = sum(c['valor_credito'] for c in combinacao)
177
- total_ent_base = sum(c['valor_entrada'] for c in combinacao)
178
- total_ent = total_ent_base + total_cred * 0.085
179
- total_parc = sum(c['valor_parcela'] for c in combinacao)
180
-
181
- if (cred_min <= total_cred <= cred_max and
182
- ent_min <= total_ent <= ent_max and
183
- parc_min <= total_parc <= parc_max):
184
-
185
- total_saldo = sum(c['saldo_devedor'] for c in combinacao)
186
- valor_final = round(total_ent + total_saldo, 2)
187
- diff = abs(valor_final - total_cred)
188
- resultados.append((adm, combinacao, diff))
189
-
190
- if len(resultados) >= 45:
191
  break
192
- if len(resultados) >= 45:
193
- break
194
- if len(resultados) >= 45:
195
- break
196
-
197
- print(f"🔍 AUTO - Total de combinações testadas: {total_combinacoes_testadas}")
198
-
199
- if not resultados:
200
- return "🚫 Nenhuma combinação de auto encontrada com os parâmetros fornecidos."
201
-
202
- resultados.sort(key=lambda x: x[2])
203
- return formatar_resultados(resultados[:15])
204
-
205
-
206
- def formatar_resultados(resultados):
207
- if not resultados:
208
- return "🚫 Nenhuma combinação de auto adequada encontrada."
209
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  resposta = ""
211
- for idx, (adm, comb, _) in enumerate(resultados, 1):
212
- codigos = [str(c.get('codigo', 'N/A')) for c in comb]
213
- total_cred = sum(c['valor_credito'] for c in comb)
214
- total_ent_base = sum(c['valor_entrada'] for c in comb)
215
- total_ent = total_ent_base + total_cred * 0.085
216
- total_saldo = sum(c['saldo_devedor'] for c in comb)
217
- total_parc = sum(c['valor_parcela'] for c in comb)
218
- maior_prazo = max(int(c.get('qtd_parcelas', 0)) for c in comb)
219
- valor_final = round(total_ent + total_saldo, 2)
220
- cred_real = total_cred - total_ent
221
- taxa_total = total_saldo - cred_real
222
- taxa_perc = (taxa_total / cred_real) * 100 if cred_real > 0 else 0
223
- taxa_mensal = taxa_perc / maior_prazo if maior_prazo > 0 else 0
224
- tempo = calcular_tempo_parcelas(comb)
225
-
 
 
 
 
 
 
 
 
 
 
 
226
  resposta += f"""
227
- --- COMBINAÇÃO {idx} AUTO ---
228
- 🏢 Administradora: {adm}
229
- 🔢 Códigos: {', '.join(codigos)} ({len(comb)} cotas)
230
- 💰 Crédito Total: {formatar_moeda(total_cred)}
231
- 💵 Entrada (+comissão): {formatar_moeda(total_ent)}
232
- 📉 Saldo Devedor: {formatar_moeda(total_saldo)}
233
- 💲 Parcela: {formatar_moeda(total_parc)}
234
- ⏳ Prazo: {maior_prazo} meses
235
  🔚 Valor Final: {formatar_moeda(valor_final)}
236
- 💠 Crédito Líquido: {formatar_moeda(cred_real)}
237
- 📊 Taxa Total: {taxa_perc:.2f}% ({formatar_moeda(taxa_total)})
238
  🗓️ Taxa Mensal: {taxa_mensal:.2f}%
239
- 🗓️ Taxa Anual: {taxa_mensal * 12:.2f}%
240
  ⏰ TEMPO DE PARCELAS:
241
- {tempo if tempo else "Não foi possível calcular"}
242
  """
 
243
  return resposta
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- # 🔁 Carregar a planilha uma vez, no início do programa
247
- carregar_planilha()
248
-
249
-
250
- # Interface Gradio (exatamente igual ao imóvel, com Blocks)
251
- with gr.Blocks(title="🚗 Sistema de Busca de Consórcio Auto") as demo_auto:
252
  gr.HTML("""
253
- <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
254
- <style>
255
- * { font-family: 'Poppins', sans-serif !important; }
256
- body { background-color: #ff8f00 !important; color: black !important; }
257
- input[type="number"], textarea {
258
- background-color: white !important;
259
- border: 2px solid black !important;
260
- border-radius: 12px !important;
261
- color: black !important;
262
- padding: 10px !important;
263
- }
264
- .gr-button {
265
- font-weight: bold;
266
- border-radius: 10px !important;
267
- background-color: white !important;
268
- color: black !important;
269
- border: 2px solid black !important;
270
- }
271
- </style>
272
  """)
273
-
274
- gr.Markdown("# 🚗 Busca de Combinações de Consórcio Auto")
275
- gr.Markdown("Insira os valores desejados para crédito, entrada e parcela, e filtre por administradora.")
276
-
277
  with gr.Row():
278
- credito_input = gr.Number(label="Crédito Desejado (R$)", value=30000, step=1000)
279
- entrada_input = gr.Number(label="Entrada Desejada (R$)", value=3000, step=100)
280
- parcela_input = gr.Number(label="Parcela Desejada (R$)", value=500, step=10)
281
-
282
- adm_input = gr.Dropdown(
283
- choices=ADMIN_OPCOES,
284
- label="Filtrar por Administradoras",
285
- multiselect=True,
286
- value=ADMIN_OPCOES,
287
- interactive=True
288
- )
289
-
290
- submit_btn = gr.Button("🔍 Buscar Combinações AUTO", variant="primary")
291
- output_text = gr.Textbox(
292
- label="Resultados AUTO",
293
- lines=20,
294
- max_lines=40,
295
- interactive=False,
296
- placeholder="Os resultados aparecerão aqui..."
297
- )
298
-
299
- submit_btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  fn=buscar_combinacoes,
301
  inputs=[credito_input, entrada_input, parcela_input, adm_input],
302
  outputs=output_text
303
  )
 
 
 
 
 
 
 
 
 
304
 
305
- gr.Markdown("---")
306
- gr.Markdown("Desenvolvido para auxiliar na busca por cotas de consórcio automotivo.")
307
-
308
-
309
- # Executar o app
310
  if __name__ == "__main__":
311
- demo_auto.launch()
312
- #
 
 
4
 
5
  import pandas as pd
6
  import requests
7
+ import json
 
8
  from collections import defaultdict
9
  import gradio as gr
10
+ from datetime import datetime
11
+ import hashlib
12
 
13
+ # ==================== CONFIGURAÇÕES ====================
14
+ API_BASE_URL = "https://api.maxconconsorcios.com.br"
15
+ WHATSAPP_NUMBER = "5516997865738"
16
 
17
+ # Cache para evitar muitas requisições
18
+ cache = {}
19
+ CACHE_TTL = 300 # 5 minutos
 
 
 
 
 
 
 
 
 
20
 
21
+ # ==================== FUNÇÕES AUXILIARES ====================
22
+ def formatar_moeda(valor):
23
+ """Formata valor para moeda brasileira"""
24
+ if valor is None:
25
+ return "R$ 0,00"
26
  try:
27
+ return f"R$ {float(valor):,.2f}".replace(",", "v").replace(".", ",").replace("v", ".")
28
+ except:
29
+ return "R$ 0,00"
30
+
31
+ def get_cache_key(prefix, *args, **kwargs):
32
+ """Gera chave de cache"""
33
+ key_data = {'prefix': prefix, 'args': args, 'kwargs': kwargs}
34
+ key_string = json.dumps(key_data, sort_keys=True)
35
+ return f"{prefix}_{hashlib.md5(key_string.encode()).hexdigest()}"
36
+
37
+ def is_cache_valid(key):
38
+ """Verifica se cache é válido"""
39
+ if key not in cache:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  return False
41
+ timestamp, _ = cache[key]
42
+ return (datetime.now() - timestamp).total_seconds() < CACHE_TTL
43
+
44
+ def set_cache(key, value):
45
+ """Armazena no cache"""
46
+ cache[key] = (datetime.now(), value)
47
+
48
+ def get_cache(key):
49
+ """Recupera do cache"""
50
+ if is_cache_valid(key):
51
+ return cache[key][1]
52
+ return None
53
+
54
+ # ==================== BUSCA DE COTAS VIA SUPABASE ====================
55
+ def buscar_cotas_supabase(filtros=None):
56
+ """
57
+ Busca cotas diretamente do Supabase através da API
58
+ (mesma fonte do backend da Foto 1)
59
+ """
60
+ cache_key = get_cache_key('cotas', filtros)
61
+ cached = get_cache(cache_key)
62
+ if cached:
63
+ print("📦 Cache HIT para cotas")
64
+ return cached
65
+
 
 
 
 
66
  try:
67
+ print(f"🔍 Buscando cotas na API: {API_BASE_URL}/api/cotas")
68
+
69
+ # Prepara payload para a API
70
+ payload = {}
71
+ if filtros:
72
+ if filtros.get('categoria'):
73
+ payload['categoria'] = filtros['categoria']
74
+ if filtros.get('valor_credito_min'):
75
+ payload['valor_credito'] = filtros['valor_credito_min']
76
+ if filtros.get('valor_parcela_max'):
77
+ payload['valor_parcela'] = filtros['valor_parcela_max']
78
+ if filtros.get('administradora_id'):
79
+ payload['administradora_id'] = filtros['administradora_id']
80
+
81
+ response = requests.post(
82
+ f"{API_BASE_URL}/api/cotas",
83
+ json=payload,
84
+ headers={'Content-Type': 'application/json'},
85
+ timeout=30
86
+ )
87
+
88
+ if response.status_code == 200:
89
+ cotas = response.json()
90
+ print(f"✅ {len(cotas)} cotas encontradas")
91
+ set_cache(cache_key, cotas)
92
+ return cotas
93
+ else:
94
+ print(f"❌ Erro na API: {response.status_code}")
95
+ return []
96
+
 
 
97
  except Exception as e:
98
+ print(f"❌ Erro ao buscar cotas: {str(e)}")
99
+ return []
100
+
101
+ def get_administradoras_list():
102
+ """Obtém lista de administradoras disponíveis"""
103
+ cache_key = get_cache_key('administradoras')
104
+ cached = get_cache(cache_key)
105
+ if cached:
106
+ return cached
107
+
108
+ cotas = buscar_cotas_supabase()
109
+ administradoras = set()
110
+
111
+ for cota in cotas:
112
+ admin_id = cota.get('administradora_id')
113
+ if admin_id:
114
+ administradoras.add(get_administradora_nome(admin_id))
115
+
116
+ resultado = sorted(list(administradoras))
117
+ set_cache(cache_key, resultado)
118
+ return resultado
119
+
120
+ def get_administradora_nome(admin_id):
121
+ """Mapeia ID da administradora para nome"""
122
+ admin_map = {
123
+ 1:'Itaú', 2:'Bradesco', 3:'Santander', 4:'Honda', 5:'Volkswagen',
124
+ 6:'Serello', 7:'Ademicon', 8:'Remaza', 9:'Porto Seguro VP',
125
+ 10:'Banrisul', 11:'Embracon', 12:'Serrana', 13:'Caixa XS5',
126
+ 14:'Caixa CNP', 15:'Unifisa', 16:'HS Consorcios', 17:'Caixa Seguradora',
127
+ 18:'Porto Seguro', 19:'Mycon', 20:'Rodobens', 21:'Ford',
128
+ 22:'Sicoob Unicoob', 23:'Zema', 24:'Maggi', 25:'Ancora', 26:'Yamaha',
129
+ 27:'Sponchiado', 28:'Scania', 29:'Disal', 30:'Servopa', 31:'Canopus',
130
+ 32:'Bradesco', 33:'Banco do Brasil', 34:'Santander', 35:'Sicredi'
131
+ }
132
+ return admin_map.get(admin_id, 'Não especificado')
133
+
134
+ def calcular_entrada_total(cota):
135
+ """Calcula entrada total incluindo comissão de 8.5%"""
136
+ credito = float(cota.get('valor_credito', 0))
137
+ entrada = float(cota.get('entrada', 0))
138
+ comissao = credito * 0.085
139
+ return entrada + comissao
140
+
141
+ def formatar_tempo_parcelas(cota):
142
+ """Formata o tempo de parcelas com escalonamento (1-21x e 22-27x)"""
143
+ parcelas = int(cota.get('parcelas', 0))
144
+ valor_parcela = float(cota.get('valor_parcela', 0))
145
+
146
+ if parcelas == 0:
147
  return ""
148
+
149
+ resultado = []
150
+
151
+ # Primeiro período: 1x à 21x
152
+ primeiro_periodo = min(21, parcelas)
153
+ resultado.append(f"{1}x à {primeiro_periodo}x: {formatar_moeda(valor_parcela)}/mês")
154
+
155
+ # Segundo período: se houver mais de 21 parcelas
156
+ if parcelas > 21:
157
+ # Valor reduzido (aproximadamente 44% do valor original)
158
+ valor_reduzido = valor_parcela * 0.44
159
+ resultado.append(f"{22}x à {parcelas}x: {formatar_moeda(valor_reduzido)}/mês")
160
+
161
+ return "\\n".join(resultado)
162
+
163
+ # ==================== FUNÇÃO PRINCIPAL DE BUSCA ====================
164
  def buscar_combinacoes(credito, entrada, parcela, adm_selecionadas):
165
+ """
166
+ Busca combinações de cotas usando a API real do Supabase
167
+ """
168
+ if not credito and not entrada and not parcela:
169
+ return "❌ Informe pelo menos um valor (Crédito, Entrada ou Parcela)"
170
+
171
+ try:
172
+ # Converte valores para float
173
+ credito_val = float(credito) if credito else 0
174
+ entrada_val = float(entrada) if entrada else 0
175
+ parcela_val = float(parcela) if parcela else 0
176
+
177
+ print(f"🔍 Buscando - Crédito: R$ {credito_val:,.2f}, Entrada: R$ {entrada_val:,.2f}, Parcela: R$ {parcela_val:,.2f}")
178
+
179
+ # Busca cotas na API
180
+ cotas = buscar_cotas_supabase()
181
+
182
+ if not cotas:
183
+ return "❌ Erro ao carregar cotas. Tente novamente mais tarde."
184
+
185
+ # Filtra cotas disponíveis
186
+ cotas = [c for c in cotas if c.get('reserva') != 'reservado']
187
+
188
+ # Filtra por administradora
189
+ if adm_selecionadas:
190
+ adm_ids = []
191
+ for adm_nome in adm_selecionadas:
192
+ for admin_id, nome in get_admin_map().items():
193
+ if nome.lower() == adm_nome.lower():
194
+ adm_ids.append(admin_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  break
196
+
197
+ if adm_ids:
198
+ cotas = [c for c in cotas if c.get('administradora_id') in adm_ids]
199
+
200
+ # Filtra por crédito mínimo
201
+ if credito_val > 0:
202
+ cotas = [c for c in cotas if float(c.get('valor_credito', 0)) >= credito_val]
203
+
204
+ # Filtra por parcela máxima
205
+ if parcela_val > 0:
206
+ cotas = [c for c in cotas if float(c.get('valor_parcela', 0)) <= parcela_val]
207
+
208
+ # Filtra por entrada máxima
209
+ if entrada_val > 0:
210
+ cotas = [c for c in cotas if calcular_entrada_total(c) <= entrada_val]
211
+
212
+ # Ordena por crédito (maior para menor)
213
+ cotas.sort(key=lambda x: float(x.get('valor_credito', 0)), reverse=True)
214
+
215
+ if not cotas:
216
+ return "🚫 Nenhuma cota encontrada com os filtros selecionados."
217
+
218
+ # Limita para exibição
219
+ cotas = cotas[:15]
220
+
221
+ # Formata resultados
222
+ return formatar_resultados(cotas)
223
+
224
+ except Exception as e:
225
+ print(f"❌ Erro na busca: {str(e)}")
226
+ return f"❌ Erro ao buscar combinações: {str(e)}"
227
+
228
+ def formatar_resultados(cotas):
229
+ """Formata os resultados para exibição"""
230
+ if not cotas:
231
+ return "🚫 Nenhuma combinação encontrada."
232
+
233
  resposta = ""
234
+
235
+ for idx, cota in enumerate(cotas, 1):
236
+ adm_nome = get_administradora_nome(cota.get('administradora_id'))
237
+ codigo = cota.get('codigo', 'N/A')
238
+ credito = float(cota.get('valor_credito', 0))
239
+ entrada_total = calcular_entrada_total(cota)
240
+ saldo = float(cota.get('saldo', 0))
241
+ parcela = float(cota.get('valor_parcela', 0))
242
+ prazo = int(cota.get('parcelas', 0))
243
+
244
+ # Cálculos financeiros
245
+ credito_real = credito - entrada_total
246
+ valor_final = entrada_total + saldo
247
+ taxa_total = saldo - credito_real
248
+ taxa_percentual = (taxa_total / credito_real * 100) if credito_real > 0 else 0
249
+
250
+ # Taxas mensal e anual
251
+ if prazo > 0:
252
+ taxa_mensal = taxa_percentual / prazo
253
+ taxa_anual = taxa_mensal * 12
254
+ else:
255
+ taxa_mensal = 0
256
+ taxa_anual = 0
257
+
258
+ tempo_parcelas = formatar_tempo_parcelas(cota)
259
+
260
  resposta += f"""
261
+ --- COMBINAÇÃO {idx} ---
262
+ 🏢 Administradora: {adm_nome}
263
+ 🔢 Código: {codigo}
264
+ 💰 Crédito Total: {formatar_moeda(credito)}
265
+ 💵 Entrada Total: {formatar_moeda(entrada_total)}
266
+ 📉 Saldo Devedor: {formatar_moeda(saldo)}
267
+ 💲 Parcela: {formatar_moeda(parcela)}
268
+ ⏳ Prazo: {prazo} meses
269
  🔚 Valor Final: {formatar_moeda(valor_final)}
270
+ 💠 Crédito Líquido: {formatar_moeda(credito_real)}
271
+ 📊 Taxa Total: {taxa_percentual:.2f}% ({formatar_moeda(taxa_total)})
272
  🗓️ Taxa Mensal: {taxa_mensal:.2f}%
273
+ 🗓️ Taxa Anual: {taxa_anual:.2f}%
274
  ⏰ TEMPO DE PARCELAS:
275
+ {tempo_parcelas if tempo_parcelas else "Não disponível"}
276
  """
277
+
278
  return resposta
279
 
280
+ def get_admin_map():
281
+ """Retorna mapeamento de administradoras"""
282
+ return {
283
+ 1: 'Itaú', 2: 'Bradesco', 3: 'Santander', 4: 'Honda', 5: 'Volkswagen',
284
+ 6: 'Serello', 7: 'Ademicon', 8: 'Remaza', 9: 'Porto Seguro VP',
285
+ 10: 'Banrisul', 11: 'Embracon', 12: 'Serrana', 13: 'Caixa XS5',
286
+ 14: 'Caixa CNP', 15: 'Unifisa', 16: 'HS Consorcios', 17: 'Caixa Seguradora',
287
+ 18: 'Porto Seguro', 19: 'Mycon', 20: 'Rodobens', 21: 'Ford',
288
+ 22: 'Sicoob Unicoob', 23: 'Zema', 24: 'Maggi', 25: 'Ancora', 26: 'Yamaha',
289
+ 27: 'Sponchiado', 28: 'Scania', 29: 'Disal', 30: 'Servopa', 31: 'Canopus',
290
+ 32: 'Bradesco', 33: 'Banco do Brasil', 34: 'Santander', 35: 'Sicredi'
291
+ }
292
+
293
+ # ==================== INTERFACE GRADIO ====================
294
+ def obter_administradoras():
295
+ """Obtém lista de administradoras para o dropdown"""
296
+ try:
297
+ return get_administradoras_list()
298
+ except:
299
+ return ["Porto Seguro", "Itaú", "Bradesco", "Santander", "Banco do Brasil"]
300
 
301
+ with gr.Blocks(title="🚗 MAXCON - Busca de Consórcio Auto", theme=gr.themes.Soft()) as demo_auto:
 
 
 
 
 
302
  gr.HTML("""
303
+ <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1a3a6c 0%, #2a5a9c 100%); border-radius: 12px; margin-bottom: 20px;">
304
+ <h1 style="color: white; margin: 0;">🚗 MAXCON Consórcios</h1>
305
+ <p style="color: #ffd166; margin: 10px 0 0 0;">Encontre as melhores cotas para seu veículo</p>
306
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  """)
308
+
 
 
 
309
  with gr.Row():
310
+ with gr.Column(scale=1):
311
+ credito_input = gr.Number(
312
+ label="💰 Crédito Desejado (mínimo)",
313
+ value=100000,
314
+ step=1000,
315
+ precision=2
316
+ )
317
+ entrada_input = gr.Number(
318
+ label="💵 Entrada (máximo)",
319
+ value=50000,
320
+ step=1000,
321
+ precision=2
322
+ )
323
+ parcela_input = gr.Number(
324
+ label="💲 Parcela (máximo)",
325
+ value=2000,
326
+ step=100,
327
+ precision=2
328
+ )
329
+
330
+ adm_input = gr.Dropdown(
331
+ choices=obter_administradoras(),
332
+ label="🏢 Administradoras",
333
+ multiselect=True,
334
+ interactive=True
335
+ )
336
+
337
+ buscar_btn = gr.Button("🔍 Buscar Combinações", variant="primary", size="lg")
338
+
339
+ with gr.Column(scale=2):
340
+ output_text = gr.Textbox(
341
+ label="📋 Resultados",
342
+ lines=25,
343
+ max_lines=40,
344
+ interactive=False,
345
+ placeholder="Os resultados aparecerão aqui após a busca..."
346
+ )
347
+
348
+ buscar_btn.click(
349
  fn=buscar_combinacoes,
350
  inputs=[credito_input, entrada_input, parcela_input, adm_input],
351
  outputs=output_text
352
  )
353
+
354
+ gr.Markdown("""
355
+ ---
356
+ ### 📌 Sobre a Busca
357
+
358
+ - Os resultados são buscados **em tempo real** do nosso banco de dados
359
+ - Valores atualizados diariamente
360
+ - Entre em contato pelo WhatsApp para negociar
361
+ """)
362
 
363
+ # ==================== INICIALIZAÇÃO ====================
 
 
 
 
364
  if __name__ == "__main__":
365
+ print("🚀 Iniciando servidor MAXCON no Hugging Face...")
366
+ print(f"📡 API Base: {API_BASE_URL}")
367
+ demo_auto.launch()