621 lines
22 KiB
Python
621 lines
22 KiB
Python
import requests
|
|
import json
|
|
import time
|
|
import os
|
|
import pandas as pd
|
|
import pyodbc
|
|
import warnings
|
|
from datetime import datetime, timezone, timedelta
|
|
|
|
# Suprimir avisos do openpyxl
|
|
warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl')
|
|
|
|
# Configurações
|
|
DIRETORIO_TEMP = "/tmp/download"
|
|
|
|
# Lista de colunas na ordem exata do banco
|
|
COLUNAS_BANCO = [
|
|
'SKU', 'SKU_PARA', 'DESCRICAO', 'CATEGORIA', 'CLASSE',
|
|
'FASES PRODUTO', 'LANCAMENTO', 'DESATIVACAO', 'PDV',
|
|
'ESTOQUE ATUAL', 'ESTOQUE EM TRANSITO', 'PEDIDO PENDENTE',
|
|
'COBERTURA ALVO', 'ESTOQUE DE SEGURANCA', 'DDV PREVISTO',
|
|
'COBERTURA ATUAL', 'COBERTURA ATUAL + TRANSITO',
|
|
'COBERTURA PROJETADA', 'ORIGEM'
|
|
]
|
|
|
|
# Headers comuns para as requisições
|
|
HEADERS_API = {
|
|
"accept": "*/*",
|
|
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
|
"authorization": "Basic b2NVc2VySW50ZXJuYWw6Nk5RV0BOU2M1anpEUy1oeg==",
|
|
"content-type": "application/json",
|
|
"origin": "https://extranet.grupoboticario.com.br",
|
|
"priority": "u=1, i",
|
|
"referer": "https://extranet.grupoboticario.com.br/",
|
|
"sec-ch-ua": '"Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
|
|
"sec-ch-ua-mobile": "?0",
|
|
"sec-ch-ua-platform": '"Windows"',
|
|
"sec-fetch-dest": "empty",
|
|
"sec-fetch-mode": "cors",
|
|
"sec-fetch-site": "cross-site",
|
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
|
|
"x-authorization": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InV6YkJManZabTJxVDRsSERBZXdBX3Ewd2ZscTQtVGJnZmhVUzBBUE5HVzQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhNmNkNGZlNi0zZDcxLTQ1NWEtYjk5ZC1mNDU4YTA3Y2MwZDEiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4dHJhbmV0LmdydXBvYm90aWNhcmlvLmNvbS5ici8xZTYzOTJiZC01Mzc3LTQ4ZjAtOWE4ZS00NjdmNWIzODFiMTgvdjIuMC8iLCJleHAiOjE3NTAyNjg5NjAsIm5iZiI6MTc1MDI2NTM2MCwic3ViIjoiNTI0MDliZmYtODNlNC00MjliLThmNGEtYzdjMDc2MGZiMmNlIiwiZW1haWwiOiJkYW5pZWwucm9kcmlndWVAZS1ib3RpY2FyaW8uY29tLmJyIiwibmFtZSI6IkRhbmllbCBKb3NlIE1lZGVpcm9zIFJvZHJpZ3VlcyIsImdpdmVuX25hbWUiOiJEYW5pZWwiLCJmYW1pbHlfbmFtZSI6IlJvZHJpZ3VlcyIsImV4dGVuc2lvbl9DUEYiOiIxMTExMzE3NDQ1NSIsInN0b3JlcyI6WyI0NDk0Il0sInJvbGVzIjpbIkNSRURfQURNIiwiR0NfTEVJVFVSQSIsIkxJVkVTX0ZEViIsIk1BUl9GUkFOUVVFQURPX0FETUlOIiwiUEJfQURNX1BBR0FET1IiLCJQR0lfUkVTVUxUQURPX1BEViIsIlZESV9TVUYiXSwiY3AiOiIxMDI2OSIsInJlZ2lzdHJhdGlvbiI6Ijg2MDk4NDAwIiwibGlueG9tcyI6ImNvbGFib3JhZG9yIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwic2NwIjoiZXh0cmFuZXQuYXBpIiwiYXpwIjoiYjMwMDFlNjAtYThlMC00ZGE4LTgyYmEtYzNhNzAxNDA1ZjA4IiwidmVyIjoiMS4wIiwiaWF0IjoxNzUwMjY1MzYwfQ.klcrMK2sx7GEJlMx_dbwgopc1RFjJwBLh0kLqjLlk__POtHNpJKti42r6xSuAM5AnieVMx0koK2oyg3eGoQJEchttsr4LyVoqSpcKzrqTR69gEHbTMo-EWWh_UglM6tr7ge6dzMF4yg-R_2XHwlrNEoYthVEVnSF1cqBxCHdTlRJcmso0q3ObGUU4heA-55OkzBjZ2Nz1mL7MmujZmNHlQXsoQ2vtOnnM3Ui7SAy08jGsIAIHdH8UKy0Xg-GrzjVrUwqAmyadSpXnvLc1sAE--bbtxP-3ADmnvHdxffkfQFtbPC0lws0MgESaqrIn0I9X6_OkAnUuMA_cCD9QJoyTA",
|
|
"x-correlation-id": "15508cc3-fa35-47bf-ab19-8361f870e197",
|
|
"x-user-id": "163165",
|
|
"x-username": "daniel.rodrigue"
|
|
}
|
|
|
|
# Configurações dos dois grupos de lojas
|
|
LOJAS_GRUPO_1 = [
|
|
"24268", "24258", "23701", "23702", "23705", "23706", "23665", "23709", "23708",
|
|
"23713", "23707", "23156", "24254", "24253", "23813", "20056", "23475", "3546",
|
|
"21647", "12824", "14617", "4560", "21068", "21277", "21296", "21381", "13427",
|
|
"21624", "19103", "14668", "20006", "20057", "20005", "20009", "5699",
|
|
"12522", "12817", "12820", "12829", "12818", "12823", "12826", "12828",
|
|
"12830", "12838", "20441", "20858", "21007", "910173", "910291", "24455"
|
|
]
|
|
|
|
LOJAS_GRUPO_2 = [
|
|
"20992", "21383", "23704", "23703", "20986", "24293", "23712", "20994", "23711",
|
|
"24269", "21000", "21001", "21375", "20970", "20989", "22541", "20988", "20993",
|
|
"20999", "24255", "24257", "20991", "20969", "20998", "20996", "20997", "20995",
|
|
"21495", "20968", "21278", "24458"
|
|
]
|
|
|
|
|
|
def criar_diretorio_temp():
|
|
"""Cria o diretório temporário se não existir."""
|
|
os.makedirs(DIRETORIO_TEMP, exist_ok=True)
|
|
print(f"✓ Diretório temporário criado/verificado: {DIRETORIO_TEMP}")
|
|
|
|
|
|
def fazer_requisicao_stock(store_codes, nome_grupo):
|
|
"""
|
|
Faz uma requisição para a API de exportação de stock.
|
|
|
|
Args:
|
|
store_codes: Lista de códigos de lojas
|
|
nome_grupo: Nome do grupo para identificação
|
|
|
|
Returns:
|
|
dict: Response da API ou None se erro
|
|
"""
|
|
url = "https://mar-orders-bff-api.demanda-abastecimento.grupoboticario.digital/api/export/STOCK"
|
|
|
|
payload = {
|
|
"storeCodes": store_codes,
|
|
"cpId": 10269,
|
|
"fileType": "XLSX",
|
|
"userId": "163165",
|
|
"metadata": {
|
|
"storeCodes": store_codes,
|
|
"userName": "Daniel Jose Medeiros Rodrigues",
|
|
"fileFormattedName": f"Consulta de estoque {nome_grupo}.XLSX",
|
|
"create_at": datetime.now(timezone.utc).isoformat(),
|
|
"exportType": "STOCK"
|
|
},
|
|
"validateOnRequest": True
|
|
}
|
|
|
|
try:
|
|
print(f" Fazendo requisição para {nome_grupo}...")
|
|
response = requests.post(url, headers=HEADERS_API, json=payload)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except requests.exceptions.RequestException as e:
|
|
print(f" ✗ Erro na requisição para {nome_grupo}: {e}")
|
|
return None
|
|
except json.JSONDecodeError as e:
|
|
print(f" ✗ Erro ao decodificar JSON para {nome_grupo}: {e}")
|
|
return None
|
|
|
|
|
|
def tentar_download(request_id):
|
|
"""
|
|
Tenta fazer o download da exportação usando o requestId.
|
|
|
|
Args:
|
|
request_id: ID da requisição
|
|
|
|
Returns:
|
|
dict: Resultado da API de download ou None se erro
|
|
"""
|
|
url = f"https://mar-orders-bff-api.demanda-abastecimento.grupoboticario.digital/api/export/{request_id}/download"
|
|
params = {"redirect": "false"}
|
|
|
|
try:
|
|
response = requests.get(url, headers=HEADERS_API, params=params)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except:
|
|
return None
|
|
|
|
|
|
def aguardar_download_disponivel(request_id, nome_grupo, timeout_minutes=90):
|
|
"""
|
|
Aguarda até que o download esteja disponível.
|
|
|
|
Args:
|
|
request_id: ID da requisição para aguardar
|
|
nome_grupo: Nome do grupo para identificação
|
|
timeout_minutes: Tempo limite em minutos (padrão: 90 min)
|
|
|
|
Returns:
|
|
dict: Resultado final com URL de download ou None se erro/timeout
|
|
"""
|
|
print(f" Aguardando download de {nome_grupo} (ID: {request_id})...")
|
|
|
|
timeout_seconds = timeout_minutes * 60
|
|
start_time = time.time()
|
|
tentativas = 0
|
|
|
|
while True:
|
|
elapsed_time = time.time() - start_time
|
|
if elapsed_time > timeout_seconds:
|
|
print(f" ✗ Timeout atingido para {nome_grupo} ({timeout_minutes} minutos)")
|
|
return None
|
|
|
|
tentativas += 1
|
|
resultado_download = tentar_download(request_id)
|
|
|
|
if resultado_download and 'fileUrl' in resultado_download:
|
|
print(f" ✓ Download de {nome_grupo} disponível após {tentativas} tentativas!")
|
|
return resultado_download
|
|
|
|
if tentativas % 6 == 0: # Log a cada minuto (6 tentativas de 10s)
|
|
print(f" ... Aguardando {nome_grupo} ({tentativas} tentativas, {int(elapsed_time)}s)")
|
|
|
|
time.sleep(10)
|
|
|
|
|
|
def baixar_e_salvar_arquivo(file_url, nome_arquivo):
|
|
"""
|
|
Baixa o arquivo da URL fornecida e salva no diretório temporário.
|
|
|
|
Args:
|
|
file_url: URL do arquivo para download
|
|
nome_arquivo: Nome do arquivo a ser salvo
|
|
|
|
Returns:
|
|
str: Caminho completo do arquivo salvo ou None se erro
|
|
"""
|
|
try:
|
|
caminho_completo = os.path.join(DIRETORIO_TEMP, nome_arquivo)
|
|
print(f" Baixando {nome_arquivo}...")
|
|
|
|
response = requests.get(file_url, stream=True)
|
|
response.raise_for_status()
|
|
|
|
with open(caminho_completo, 'wb') as arquivo:
|
|
for chunk in response.iter_content(chunk_size=8192):
|
|
if chunk:
|
|
arquivo.write(chunk)
|
|
|
|
if os.path.exists(caminho_completo) and os.path.getsize(caminho_completo) > 0:
|
|
tamanho_mb = os.path.getsize(caminho_completo) / (1024 * 1024)
|
|
print(f" ✓ Arquivo baixado: {nome_arquivo} ({tamanho_mb:.2f} MB)")
|
|
return caminho_completo
|
|
else:
|
|
print(f" ✗ Erro: Arquivo não foi salvo corretamente")
|
|
return None
|
|
|
|
except Exception as e:
|
|
print(f" ✗ Erro ao baixar arquivo: {e}")
|
|
return None
|
|
|
|
|
|
def enviar_status_api(status_id, status="OK"):
|
|
"""
|
|
Envia o status da execução para a API.
|
|
|
|
Args:
|
|
status_id: ID do status (2 para grupo 1, 5 para grupo 2)
|
|
status: Status a ser enviado, "OK" ou "FAIL"
|
|
"""
|
|
url = f"https://api.grupoginseng.com.br/api/status/{status_id}"
|
|
sao_paulo_offset = timedelta(hours=-3)
|
|
current_datetime = datetime.now(timezone(sao_paulo_offset)).strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
payload = {"STATUS": status, "DATA": current_datetime}
|
|
headers = {"Content-Type": "application/json"}
|
|
|
|
try:
|
|
response = requests.put(url, json=payload, headers=headers)
|
|
print(f" Status enviado para API (ID {status_id}): {status} - {response.status_code}")
|
|
except Exception as e:
|
|
print(f" ✗ Erro ao enviar status: {e}")
|
|
|
|
|
|
def processar_download_grupo(store_codes, nome_grupo, nome_arquivo, status_id):
|
|
"""
|
|
Processa o download de um grupo de lojas.
|
|
|
|
Args:
|
|
store_codes: Lista de códigos de lojas
|
|
nome_grupo: Nome do grupo para identificação
|
|
nome_arquivo: Nome do arquivo a ser salvo
|
|
status_id: ID do status para enviar à API
|
|
|
|
Returns:
|
|
str: Caminho do arquivo baixado ou None se erro
|
|
"""
|
|
print(f"\n{'='*60}")
|
|
print(f"PROCESSANDO {nome_grupo}")
|
|
print(f"{'='*60}")
|
|
|
|
# 1. Fazer requisição inicial
|
|
resultado_inicial = fazer_requisicao_stock(store_codes, nome_grupo)
|
|
if not resultado_inicial:
|
|
enviar_status_api(status_id, "FAIL")
|
|
return None
|
|
|
|
request_id = resultado_inicial.get('id') or resultado_inicial.get('requestId')
|
|
if not request_id:
|
|
print(f" ✗ ID da requisição não encontrado para {nome_grupo}")
|
|
enviar_status_api(status_id, "FAIL")
|
|
return None
|
|
|
|
print(f" ✓ Requisição iniciada - ID: {request_id}")
|
|
|
|
# 2. Aguardar download ficar disponível
|
|
resultado_download = aguardar_download_disponivel(request_id, nome_grupo)
|
|
if not resultado_download:
|
|
enviar_status_api(status_id, "FAIL")
|
|
return None
|
|
|
|
file_url = resultado_download.get('fileUrl', '')
|
|
if not file_url:
|
|
print(f" ✗ URL do arquivo não encontrada para {nome_grupo}")
|
|
enviar_status_api(status_id, "FAIL")
|
|
return None
|
|
|
|
# 3. Baixar arquivo
|
|
arquivo_salvo = baixar_e_salvar_arquivo(file_url, nome_arquivo)
|
|
if arquivo_salvo:
|
|
enviar_status_api(status_id, "OK")
|
|
return arquivo_salvo
|
|
else:
|
|
enviar_status_api(status_id, "FAIL")
|
|
return None
|
|
|
|
|
|
# ============================================================================
|
|
# FUNÇÕES DE UPLOAD PARA O BANCO DE DADOS
|
|
# ============================================================================
|
|
|
|
def conectar_banco():
|
|
"""Estabelece conexão com o banco de dados."""
|
|
try:
|
|
conn = pyodbc.connect(
|
|
'DRIVER={ODBC Driver 18 for SQL Server};'
|
|
'SERVER=10.77.77.10;'
|
|
'DATABASE=GINSENG;'
|
|
'UID=supginseng;'
|
|
'PWD=Iphone2513@;'
|
|
'PORT=1433;'
|
|
'TrustServerCertificate=yes'
|
|
)
|
|
return conn
|
|
except Exception as e:
|
|
print(f"✗ Erro ao conectar ao banco de dados: {e}")
|
|
raise
|
|
|
|
|
|
def limpar_dados_data_atual(conn):
|
|
"""Remove dados da tabela estoque_mar_historico para a data do estoque (dia atual)."""
|
|
try:
|
|
cursor = conn.cursor()
|
|
# Data do estoque é o dia atual
|
|
data_estoque = datetime.now().strftime("%Y-%m-%d")
|
|
|
|
# Verificar se existem dados para a data do estoque
|
|
cursor.execute(
|
|
"SELECT COUNT(*) FROM [GINSENG].[dbo].[estoque_mar_historico] WHERE CAST([data_estoque] AS DATE) = ?",
|
|
(data_estoque,)
|
|
)
|
|
count = cursor.fetchone()[0]
|
|
|
|
if count > 0:
|
|
print(f" Encontrados {count} registros para a data {data_estoque}")
|
|
print(f" Removendo dados existentes da data {data_estoque}...")
|
|
cursor.execute(
|
|
"DELETE FROM [GINSENG].[dbo].[estoque_mar_historico] WHERE CAST([data_estoque] AS DATE) = ?",
|
|
(data_estoque,)
|
|
)
|
|
conn.commit()
|
|
print(f" ✓ {count} registros removidos com sucesso!")
|
|
else:
|
|
print(f" Nenhum registro encontrado para a data {data_estoque}")
|
|
|
|
return data_estoque
|
|
except Exception as e:
|
|
print(f" ✗ Erro ao limpar dados da data do estoque: {e}")
|
|
raise
|
|
|
|
|
|
def unificar_arquivo(caminho_arquivo):
|
|
"""Unifica as três páginas (BOT, EUD, QDB) de um arquivo Excel."""
|
|
try:
|
|
# Lê cada página do arquivo
|
|
df_bot = pd.read_excel(caminho_arquivo, sheet_name='BOT')
|
|
df_eud = pd.read_excel(caminho_arquivo, sheet_name='EUD')
|
|
df_qdb = pd.read_excel(caminho_arquivo, sheet_name='QDB')
|
|
|
|
# Adiciona coluna de origem
|
|
df_bot['ORIGEM'] = 'BOT'
|
|
df_eud['ORIGEM'] = 'EUD'
|
|
df_qdb['ORIGEM'] = 'QDB'
|
|
|
|
# Concatena os DataFrames
|
|
df_unificado = pd.concat([df_bot, df_eud, df_qdb], ignore_index=True)
|
|
|
|
# Garante que todas as colunas necessárias existem
|
|
for coluna in COLUNAS_BANCO:
|
|
if coluna not in df_unificado.columns:
|
|
df_unificado[coluna] = None
|
|
|
|
# Reordena as colunas na ordem correta do banco
|
|
df_unificado = df_unificado[COLUNAS_BANCO]
|
|
|
|
return df_unificado
|
|
|
|
except Exception as e:
|
|
print(f" ✗ Erro ao unificar arquivo {caminho_arquivo}: {e}")
|
|
return None
|
|
|
|
|
|
def formatar_data(valor):
|
|
"""Formata uma data no formato YYYYMM."""
|
|
try:
|
|
if pd.isna(valor) or valor == '-':
|
|
return None
|
|
data_str = str(int(float(valor)))
|
|
if len(data_str) >= 6:
|
|
return data_str[:6]
|
|
return None
|
|
except:
|
|
return None
|
|
|
|
|
|
def formatar_numero(valor, max_length=50):
|
|
"""Formata um número como string, usando vírgula como separador decimal."""
|
|
try:
|
|
if pd.isna(valor) or valor == '-':
|
|
return None
|
|
num = float(str(valor).replace(',', '.'))
|
|
if num.is_integer():
|
|
return str(int(num))[:max_length]
|
|
return f"{num:.2f}".replace('.', ',')[:max_length]
|
|
except:
|
|
return None
|
|
|
|
|
|
def enviar_para_banco(conn, df, data_estoque):
|
|
"""
|
|
Envia os dados do DataFrame para o banco na tabela estoque_mar_historico.
|
|
|
|
Args:
|
|
conn: Conexão com o banco de dados
|
|
df: DataFrame com os dados a serem inseridos
|
|
data_estoque: Data do estoque no formato YYYY-MM-DD
|
|
|
|
Returns:
|
|
bool: True se sucesso, False se erro
|
|
"""
|
|
try:
|
|
cursor = conn.cursor()
|
|
total_linhas = len(df)
|
|
linhas_processadas = 0
|
|
erros = 0
|
|
|
|
for _, row in df.iterrows():
|
|
try:
|
|
valores = []
|
|
for coluna in COLUNAS_BANCO:
|
|
valor = row[coluna]
|
|
|
|
if pd.isna(valor) or valor == '-':
|
|
valores.append(None)
|
|
elif coluna in ['SKU', 'PDV']:
|
|
valores.append(str(int(float(valor)))[:50])
|
|
elif coluna in ['LANCAMENTO', 'DESATIVACAO']:
|
|
valores.append(formatar_data(valor))
|
|
elif coluna in ['ESTOQUE ATUAL', 'ESTOQUE EM TRANSITO', 'PEDIDO PENDENTE',
|
|
'COBERTURA ALVO', 'ESTOQUE DE SEGURANCA', 'DDV PREVISTO',
|
|
'COBERTURA ATUAL', 'COBERTURA ATUAL + TRANSITO', 'COBERTURA PROJETADA']:
|
|
valores.append(formatar_numero(valor))
|
|
elif coluna == 'DESCRICAO':
|
|
valores.append(str(valor)[:255] if pd.notna(valor) else None)
|
|
elif coluna in ['CATEGORIA', 'CLASSE', 'FASES PRODUTO', 'ORIGEM']:
|
|
valores.append(str(valor)[:100] if pd.notna(valor) else None)
|
|
else:
|
|
if isinstance(valor, (int, float)):
|
|
valor = str(int(valor))
|
|
valores.append(str(valor)[:50] if pd.notna(valor) else None)
|
|
|
|
# Adiciona a data_estoque ao final dos valores
|
|
valores.append(data_estoque)
|
|
|
|
cursor.execute("""
|
|
INSERT INTO [GINSENG].[dbo].[estoque_mar_historico] (
|
|
[SKU], [SKU_PARA], [DESCRICAO], [CATEGORIA], [CLASSE],
|
|
[FASES PRODUTO], [LANCAMENTO], [DESATIVACAO], [PDV],
|
|
[ESTOQUE ATUAL], [ESTOQUE EM TRANSITO], [PEDIDO PENDENTE],
|
|
[COBERTURA ALVO], [ESTOQUE DE SEGURANCA], [DDV PREVISTO],
|
|
[COBERTURA ATUAL], [COBERTURA ATUAL + TRANSITO],
|
|
[COBERTURA PROJETADA], [ORIGEM], [data_estoque]
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""", valores)
|
|
|
|
linhas_processadas += 1
|
|
if linhas_processadas % 5000 == 0:
|
|
conn.commit()
|
|
print(f" Progresso: {linhas_processadas}/{total_linhas} linhas inseridas")
|
|
|
|
except Exception as e:
|
|
erros += 1
|
|
if erros <= 3:
|
|
print(f" ✗ Erro ao inserir linha: {e}")
|
|
continue
|
|
|
|
conn.commit()
|
|
print(f" ✓ Total de linhas processadas: {linhas_processadas}")
|
|
if erros > 0:
|
|
print(f" ⚠ Total de erros: {erros}")
|
|
return linhas_processadas > 0
|
|
|
|
except Exception as e:
|
|
print(f" ✗ Erro ao enviar dados para o banco: {e}")
|
|
conn.rollback()
|
|
return False
|
|
|
|
|
|
def processar_upload_banco(arquivos):
|
|
"""
|
|
Processa o upload dos arquivos para o banco de dados na tabela estoque_mar_historico.
|
|
|
|
Args:
|
|
arquivos: Lista de caminhos dos arquivos a processar
|
|
|
|
Returns:
|
|
bool: True se sucesso, False se erro
|
|
"""
|
|
print(f"\n{'='*60}")
|
|
print("PROCESSANDO UPLOAD PARA O BANCO DE DADOS")
|
|
print(f"{'='*60}")
|
|
|
|
try:
|
|
# Conecta ao banco
|
|
print(" Conectando ao banco de dados...")
|
|
conn = conectar_banco()
|
|
print(" ✓ Conectado ao banco de dados")
|
|
|
|
# Limpa dados da data atual e obtém a data
|
|
data_estoque = limpar_dados_data_atual(conn)
|
|
print(f" Data do estoque: {data_estoque}")
|
|
|
|
# Processa cada arquivo
|
|
arquivos_processados = 0
|
|
for arquivo in arquivos:
|
|
if not arquivo or not os.path.exists(arquivo):
|
|
print(f" ⚠ Arquivo não encontrado: {arquivo}")
|
|
continue
|
|
|
|
nome_arquivo = os.path.basename(arquivo)
|
|
print(f"\n Processando arquivo: {nome_arquivo}")
|
|
|
|
# Unifica as páginas do arquivo
|
|
df_unificado = unificar_arquivo(arquivo)
|
|
if df_unificado is None:
|
|
print(f" ✗ Falha ao unificar arquivo: {nome_arquivo}")
|
|
continue
|
|
|
|
print(f" Total de linhas: {len(df_unificado)}")
|
|
|
|
# Envia para o banco com a data_estoque
|
|
if enviar_para_banco(conn, df_unificado, data_estoque):
|
|
arquivos_processados += 1
|
|
print(f" ✓ Arquivo enviado ao banco com sucesso: {nome_arquivo}")
|
|
else:
|
|
print(f" ✗ Falha ao enviar arquivo: {nome_arquivo}")
|
|
|
|
# Fecha a conexão
|
|
conn.close()
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"✓ Upload finalizado: {arquivos_processados}/{len(arquivos)} arquivos processados")
|
|
print(f"✓ Dados inseridos na tabela estoque_mar_historico com data_estoque = {data_estoque}")
|
|
print(f"{'='*60}")
|
|
|
|
return arquivos_processados > 0
|
|
|
|
except Exception as e:
|
|
print(f"✗ Erro durante o upload: {e}")
|
|
return False
|
|
|
|
|
|
def limpar_arquivos_temporarios():
|
|
"""Remove os arquivos temporários baixados."""
|
|
try:
|
|
if os.path.exists(DIRETORIO_TEMP):
|
|
for arquivo in os.listdir(DIRETORIO_TEMP):
|
|
caminho = os.path.join(DIRETORIO_TEMP, arquivo)
|
|
try:
|
|
os.remove(caminho)
|
|
print(f" ✓ Arquivo removido: {arquivo}")
|
|
except Exception as e:
|
|
print(f" ⚠ Erro ao remover {arquivo}: {e}")
|
|
|
|
# Remove o diretório se estiver vazio
|
|
try:
|
|
os.rmdir(DIRETORIO_TEMP)
|
|
print(f" ✓ Diretório temporário removido")
|
|
except:
|
|
pass
|
|
except Exception as e:
|
|
print(f" ⚠ Erro ao limpar arquivos temporários: {e}")
|
|
|
|
|
|
def main():
|
|
"""Função principal que executa todo o processo."""
|
|
print("\n" + "="*60)
|
|
print("ESTOQUE MAR - PROCESSO UNIFICADO")
|
|
print("="*60)
|
|
print(f"Início: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
print("="*60)
|
|
|
|
# Criar diretório temporário
|
|
criar_diretorio_temp()
|
|
|
|
# Lista para armazenar os arquivos baixados
|
|
arquivos_baixados = []
|
|
|
|
# 1. Baixar arquivo do Grupo 1 (Lojas)
|
|
arquivo_loja = processar_download_grupo(
|
|
LOJAS_GRUPO_1,
|
|
"GRUPO 1 - LOJAS",
|
|
"Estoque_mar_loja.xlsx",
|
|
2 # status_id
|
|
)
|
|
if arquivo_loja:
|
|
arquivos_baixados.append(arquivo_loja)
|
|
|
|
# 2. Baixar arquivo do Grupo 2 (VD)
|
|
arquivo_vd = processar_download_grupo(
|
|
LOJAS_GRUPO_2,
|
|
"GRUPO 2 - VD",
|
|
"Estoque_mar_VD.xlsx",
|
|
5 # status_id
|
|
)
|
|
if arquivo_vd:
|
|
arquivos_baixados.append(arquivo_vd)
|
|
|
|
# 3. Upload para o banco de dados
|
|
if arquivos_baixados:
|
|
sucesso_upload = processar_upload_banco(arquivos_baixados)
|
|
else:
|
|
print("\n✗ Nenhum arquivo foi baixado. Abortando upload.")
|
|
sucesso_upload = False
|
|
|
|
# 4. Limpar arquivos temporários
|
|
print(f"\n{'='*60}")
|
|
print("LIMPANDO ARQUIVOS TEMPORÁRIOS")
|
|
print(f"{'='*60}")
|
|
limpar_arquivos_temporarios()
|
|
|
|
# Resumo final
|
|
print("\n" + "="*60)
|
|
print("RESUMO FINAL")
|
|
print("="*60)
|
|
print(f"Arquivos baixados: {len(arquivos_baixados)}/2")
|
|
print(f"Upload para banco: {'✓ SUCESSO' if sucesso_upload else '✗ FALHA'}")
|
|
print(f"Fim: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
print("="*60 + "\n")
|
|
|
|
return sucesso_upload
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|