This commit is contained in:
daniel.rodrigues 2026-01-16 09:36:39 -03:00
parent e568673a14
commit 45858dc952

View File

@ -460,10 +460,92 @@ BASE_HEADERS = {
# 4) LOOP POR LOJA # 4) LOOP POR LOJA
# =============================== # ===============================
def process_stores(stores_to_process, all_data, base_headers, token, is_retry=False): def save_store_data_to_db(store_data, stats):
""" """
Processa uma lista de lojas e retorna os dados coletados e as lojas que falharam. Salva os dados de uma loja no banco de dados imediatamente.
Se is_retry=True, remove dados existentes da loja antes de adicionar novos (evita duplicação). Deleta os pedidos existentes e insere os novos.
Retorna True se salvou com sucesso, False caso contrário.
"""
if not store_data:
return True # Nada para salvar, considera sucesso
try:
conn = pyodbc.connect(DB_CONNECTION_STRING)
cursor = conn.cursor()
# Obter lista única de pedidos para deletar
unique_orders = list(set([record["Pedido"] for record in store_data]))
stats["pedidos_unicos"] += len(unique_orders)
# Deletar registros existentes dos pedidos
if unique_orders:
placeholders = ','.join(['?' for _ in unique_orders])
delete_query = f"DELETE FROM [GINSENG].[dbo].[extrato_pedidos_mar] WHERE [Pedido] IN ({placeholders})"
cursor.execute(delete_query, unique_orders)
stats["registros_deletados"] += cursor.rowcount
conn.commit()
# Query de inserção
insert_query = """
INSERT INTO [GINSENG].[dbo].[extrato_pedidos_mar]
([Loja], [Pedido], [Data], [Tipo], [Status], [Valor], [PDV], [Observacao],
[SellBOT], [SellEUD], [SellQDB], [Itens], [SKUs], [ValorTotal], [PercentualAtendido],
[ItemBU], [SKU], [DescricaoItem], [StatusItem], [PrecoUnitario],
[QtdSolicitada], [QtdAceita], [QtdAtendida], [QtdFaturada], [PercentualItem],
[NFNumero], [NFDataMAR], [DataEntrega], [CodigoAtendimento], [MotivoRecusa])
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
# Inserir dados
for record in store_data:
cursor.execute(insert_query,
record["Loja"],
record["Pedido"],
to_date(record["Data"]),
record["Tipo"],
record["Status"],
to_decimal(record["Valor (R$)"]),
record["PDV"] if record["PDV"] else None,
record["Observação"] if record["Observação"] else None,
record["Sell BOT"] if record["Sell BOT"] else None,
record["Sell EUD"] if record["Sell EUD"] else None,
record["Sell QDB"] if record["Sell QDB"] else None,
to_int(record["Itens"]),
str(record["SKUs"]) if record["SKUs"] else None,
to_decimal(record["Valor Total"]),
record["% Atendido"] if record["% Atendido"] else None,
record["Item BU"] if record["Item BU"] else None,
record["SKU"] if record["SKU"] else None,
record["Descrição Item"] if record["Descrição Item"] else None,
record["Status Item"] if record["Status Item"] else None,
to_decimal(record["Preço Unit."]),
to_int(record["Qtd Solicitada"]),
to_int(record["Qtd Aceita"]),
to_int(record["Qtd Atendida"]),
to_int(record["Qtd Faturada"]),
to_int(record["% Item"]),
record["NF Número"] if record["NF Número"] else None,
to_date(record["NF Data MAR"]),
to_date(record["Data Entrega"]),
record["Código Atendimento"] if record["Código Atendimento"] else None,
record["Motivo Recusa"] if record["Motivo Recusa"] else None
)
stats["registros_inseridos"] += 1
conn.commit()
cursor.close()
conn.close()
return True
except Exception as e:
print(f" [ERRO BD] Erro ao salvar no banco: {e}")
return False
def process_stores(stores_to_process, base_headers, token, stats):
"""
Processa uma lista de lojas e salva no banco imediatamente após cada loja.
Retorna as lojas que falharam e o token atualizado.
""" """
failed = [] failed = []
processed_count = 0 processed_count = 0
@ -500,28 +582,23 @@ def process_stores(stores_to_process, all_data, base_headers, token, is_retry=Fa
base_headers["authorization"] = token base_headers["authorization"] = token
base_headers["x-authorization"] = token base_headers["x-authorization"] = token
# Se é retry e a loja foi processada com sucesso, remove dados antigos dessa loja
# para evitar duplicação (caso tenha coletado dados parciais antes de falhar)
if is_retry and len(store_data) > 0:
old_count = len(all_data)
all_data[:] = [record for record in all_data if record["Loja"] != store]
removed = old_count - len(all_data)
if removed > 0:
print(f" [RETRY] Removidos {removed} registros antigos da loja {store} para evitar duplicação")
# Adicionar dados da loja ao total
all_data.extend(store_data)
processed_count += 1 processed_count += 1
if len(store_data) == 0: if len(store_data) == 0:
failed.append(store) failed.append(store)
print(f"Loja {store}: 0 registros (falha)")
print(f"Loja {store} processada: {len(store_data)} registros") else:
# Salvar no banco imediatamente
print(f" Salvando {len(store_data)} registros no banco...")
if save_store_data_to_db(store_data, stats):
print(f" ✓ Loja {store}: {len(store_data)} registros salvos")
stats["lojas_salvas"] += 1
else:
failed.append(store)
print(f" ✗ Loja {store}: erro ao salvar no banco")
return failed, token return failed, token
all_data = []
failed_stores = []
# Estatísticas para relatório final # Estatísticas para relatório final
stats = { stats = {
@ -530,14 +607,16 @@ stats = {
"lojas_falha_inicial": [], "lojas_falha_inicial": [],
"lojas_recuperadas_retry": [], "lojas_recuperadas_retry": [],
"lojas_falha_final": [], "lojas_falha_final": [],
"lojas_salvas": 0,
"registros_inseridos": 0, "registros_inseridos": 0,
"registros_deletados": 0, "registros_deletados": 0,
"duplicatas_removidas": 0,
"pedidos_unicos": 0 "pedidos_unicos": 0
} }
failed_stores = []
# Primeira passagem: processar todas as lojas # Primeira passagem: processar todas as lojas
failed_stores, TOKEN = process_stores(STORES, all_data, BASE_HEADERS, TOKEN) failed_stores, TOKEN = process_stores(STORES, BASE_HEADERS, TOKEN, stats)
# Guardar lojas que falharam na primeira tentativa # Guardar lojas que falharam na primeira tentativa
stats["lojas_falha_inicial"] = failed_stores.copy() stats["lojas_falha_inicial"] = failed_stores.copy()
@ -564,9 +643,9 @@ while failed_stores and retry_round < FAILED_STORES_MAX_RETRIES:
# Guardar lojas antes do retry para comparar depois # Guardar lojas antes do retry para comparar depois
lojas_antes_retry = set(failed_stores) lojas_antes_retry = set(failed_stores)
# Tentar processar as lojas que falharam (is_retry=True para evitar duplicação) # Tentar processar as lojas que falharam
stores_to_retry = failed_stores.copy() stores_to_retry = failed_stores.copy()
failed_stores, TOKEN = process_stores(stores_to_retry, all_data, BASE_HEADERS, TOKEN, is_retry=True) failed_stores, TOKEN = process_stores(stores_to_retry, BASE_HEADERS, TOKEN, stats)
# Identificar lojas que foram recuperadas neste retry # Identificar lojas que foram recuperadas neste retry
lojas_recuperadas = lojas_antes_retry - set(failed_stores) lojas_recuperadas = lojas_antes_retry - set(failed_stores)
@ -585,141 +664,6 @@ if failed_stores:
print(f" {', '.join(failed_stores)}") print(f" {', '.join(failed_stores)}")
print(f"{'=' * 60}") print(f"{'=' * 60}")
# ===============================
# 5) SALVAR NO BANCO DE DADOS
# ===============================
if all_data:
# Remover duplicatas em all_data antes de inserir
# Usa uma chave composta: Loja + Pedido + SKU
print(f"\nVerificando duplicatas em all_data...")
seen = set()
unique_data = []
duplicates_removed = 0
for record in all_data:
# Criar chave única baseada nos campos que identificam um registro
key = (
record.get("Loja", ""),
record.get("Pedido", ""),
record.get("SKU", "")
)
if key not in seen:
seen.add(key)
unique_data.append(record)
else:
duplicates_removed += 1
if duplicates_removed > 0:
print(f"[AVISO] Removidas {duplicates_removed} linhas duplicadas de all_data")
all_data = unique_data
stats["duplicatas_removidas"] = duplicates_removed
print(f"Total de registros após deduplicação: {len(all_data)}")
print(f"\nConectando ao banco de dados...")
try:
# Conectar ao banco de dados
conn = pyodbc.connect(DB_CONNECTION_STRING)
cursor = conn.cursor()
print(f"Conexão estabelecida com sucesso!")
# Obter lista única de pedidos para deletar
unique_orders = list(set([record["Pedido"] for record in all_data]))
stats["pedidos_unicos"] = len(unique_orders)
print(f"Total de pedidos únicos a processar: {len(unique_orders)}")
# Deletar registros existentes em lotes de 500
print(f"Deletando registros existentes dos pedidos...")
batch_size = 500
deleted_count = 0
for i in range(0, len(unique_orders), batch_size):
batch = unique_orders[i:i + batch_size]
placeholders = ','.join(['?' for _ in batch])
delete_query = f"DELETE FROM [GINSENG].[dbo].[extrato_pedidos_mar] WHERE [Pedido] IN ({placeholders})"
cursor.execute(delete_query, batch)
deleted_count += cursor.rowcount
conn.commit()
print(f" Deletados {deleted_count} registros (lote {i//batch_size + 1}/{(len(unique_orders)-1)//batch_size + 1})...")
stats["registros_deletados"] = deleted_count
print(f"Total de registros deletados: {deleted_count}")
# Query de inserção
print(f"\nInserindo {len(all_data)} novos registros...")
insert_query = """
INSERT INTO [GINSENG].[dbo].[extrato_pedidos_mar]
([Loja], [Pedido], [Data], [Tipo], [Status], [Valor], [PDV], [Observacao],
[SellBOT], [SellEUD], [SellQDB], [Itens], [SKUs], [ValorTotal], [PercentualAtendido],
[ItemBU], [SKU], [DescricaoItem], [StatusItem], [PrecoUnitario],
[QtdSolicitada], [QtdAceita], [QtdAtendida], [QtdFaturada], [PercentualItem],
[NFNumero], [NFDataMAR], [DataEntrega], [CodigoAtendimento], [MotivoRecusa])
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
# Inserir dados em lote
records_inserted = 0
for record in all_data:
cursor.execute(insert_query,
record["Loja"], # varchar
record["Pedido"], # varchar
to_date(record["Data"]), # date
record["Tipo"], # varchar
record["Status"], # nvarchar
to_decimal(record["Valor (R$)"]), # decimal
record["PDV"] if record["PDV"] else None, # varchar
record["Observação"] if record["Observação"] else None, # varchar
record["Sell BOT"] if record["Sell BOT"] else None, # varchar
record["Sell EUD"] if record["Sell EUD"] else None, # varchar
record["Sell QDB"] if record["Sell QDB"] else None, # varchar
to_int(record["Itens"]), # int
str(record["SKUs"]) if record["SKUs"] else None, # varchar (não int!)
to_decimal(record["Valor Total"]), # decimal
record["% Atendido"] if record["% Atendido"] else None, # varchar
record["Item BU"] if record["Item BU"] else None, # varchar
record["SKU"] if record["SKU"] else None, # varchar
record["Descrição Item"] if record["Descrição Item"] else None, # nvarchar
record["Status Item"] if record["Status Item"] else None, # varchar
to_decimal(record["Preço Unit."]), # decimal
to_int(record["Qtd Solicitada"]), # int
to_int(record["Qtd Aceita"]), # int
to_int(record["Qtd Atendida"]), # int
to_int(record["Qtd Faturada"]), # int
to_int(record["% Item"]), # int
record["NF Número"] if record["NF Número"] else None, # varchar
to_date(record["NF Data MAR"]), # date
to_date(record["Data Entrega"]), # date
record["Código Atendimento"] if record["Código Atendimento"] else None, # varchar
record["Motivo Recusa"] if record["Motivo Recusa"] else None # varchar
)
records_inserted += 1
# Commit a cada 100 registros para melhor performance
if records_inserted % 100 == 0:
conn.commit()
print(f" {records_inserted}/{len(all_data)} registros inseridos...")
# Commit final
conn.commit()
stats["registros_inseridos"] = records_inserted
print(f"\n✓ Dados salvos com sucesso no banco de dados!")
print(f"Total de registros inseridos: {records_inserted}")
# Fechar conexão
cursor.close()
conn.close()
except Exception as e:
print(f"\n✗ Erro ao salvar no banco de dados: {e}")
else:
print("\nNenhum dado encontrado para salvar.")
# =============================== # ===============================
# 6) RELATÓRIO FINAL # 6) RELATÓRIO FINAL
# =============================== # ===============================
@ -748,7 +692,6 @@ if stats['lojas_falha_final']:
print("\n📦 ESTATÍSTICAS DE DADOS:") print("\n📦 ESTATÍSTICAS DE DADOS:")
print(f" Pedidos únicos processados: {stats['pedidos_unicos']}") print(f" Pedidos únicos processados: {stats['pedidos_unicos']}")
print(f" Duplicatas removidas (memória): {stats['duplicatas_removidas']}")
print(f" Registros deletados do banco: {stats['registros_deletados']}") print(f" Registros deletados do banco: {stats['registros_deletados']}")
print(f" Registros inseridos no banco: {stats['registros_inseridos']}") print(f" Registros inseridos no banco: {stats['registros_inseridos']}")