G-Scripts/Notas-calamo.py
daniel.rodrigues 0ac2401d76 att
2026-02-11 12:08:26 -03:00

781 lines
30 KiB
Python

import requests
import pandas as pd
import json
import os
import time
import pyodbc
import base64
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta, timezone
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# =============================================================================
# CONFIGURAÇÕES
# =============================================================================
# Configurações da API
API_URL = "https://api.arquivei.com.br/v1/nfe/received"
API_HEADERS = {
"X-API-ID": "3e51eeaeb4c678bb648801cbc545da9cc75682cf",
"X-API-KEY": "73d6941b0c948ac010b35c4f57506072dac44a4f",
"Content-Type": "application/json"
}
# CNPJs permitidos para filtragem
CNPJS_PERMITIDOS = {'06147451000990', '06147451000809'}
# Quantidade de dias para buscar (hoje + X dias para trás)
DIAS_PARA_BUSCAR = 1 # Altere aqui para mudar o período
# =============================================================================
# FUNÇÕES AUXILIARES
# =============================================================================
def create_session_with_retry():
"""Cria uma sessão de requests com configuração de retry e timeout"""
session = requests.Session()
try:
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"],
backoff_factor=1
)
except TypeError:
try:
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS"],
backoff_factor=1
)
except TypeError:
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
backoff_factor=1
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
def get_db_connection():
"""Configuração da conexão SQL Server"""
return pyodbc.connect(
'DRIVER={ODBC Driver 18 for SQL Server};'
'SERVER=10.77.77.10;'
'DATABASE=GINSENG;'
'UID=supginseng;'
'PWD=Ginseng@;'
'PORT=1433;'
'TrustServerCertificate=yes'
)
def fazer_requisicao_robusta(session, url, headers, params, max_tentativas=3):
"""Faz uma requisição HTTP com tratamento robusto de timeout e erros"""
for tentativa in range(max_tentativas):
try:
print(f" Tentativa {tentativa + 1}/{max_tentativas}...", end=" ", flush=True)
response = session.get(url, headers=headers, params=params, timeout=(30, 60))
if response.status_code == 200:
print("Sucesso!")
return response
else:
print(f"Erro {response.status_code}")
if tentativa < max_tentativas - 1:
wait_time = (tentativa + 1) * 2
print(f" Aguardando {wait_time} segundos...")
time.sleep(wait_time)
except requests.exceptions.ConnectTimeout:
print("Timeout de conexão!")
if tentativa < max_tentativas - 1:
time.sleep((tentativa + 1) * 5)
except requests.exceptions.ReadTimeout:
print("Timeout de leitura!")
if tentativa < max_tentativas - 1:
time.sleep((tentativa + 1) * 3)
except requests.exceptions.ConnectionError as e:
print(f"Erro de conexão: {str(e)[:100]}...")
if tentativa < max_tentativas - 1:
time.sleep((tentativa + 1) * 5)
except Exception as e:
print(f"Erro inesperado: {str(e)[:100]}...")
if tentativa < max_tentativas - 1:
time.sleep((tentativa + 1) * 2)
print(" Todas as tentativas falharam")
return None
# =============================================================================
# PARTE 1: BUSCAR NOTAS E IDENTIFICAR NOVAS CHAVES
# =============================================================================
def consultar_chaves_existentes(chaves_list):
"""Consulta quais chaves já existem no banco de dados"""
if not chaves_list:
return set()
try:
conn = get_db_connection()
cursor = conn.cursor()
chaves_str = "', '".join(chaves_list)
query = f"""
SELECT DISTINCT [chave]
FROM [GINSENG].[dbo].[fato_notas_entrada]
WHERE [chave] IN ('{chaves_str}')
"""
cursor.execute(query)
chaves_existentes = {row[0] for row in cursor.fetchall()}
cursor.close()
conn.close()
print(f" Consulta no banco: {len(chaves_existentes):,} chaves já existem de {len(chaves_list):,} consultadas")
return chaves_existentes
except Exception as e:
print(f" Erro ao consultar banco de dados: {e}")
return set()
def inserir_nfes_banco(registros_novos):
"""Insere os registros novos de NFe no banco de dados"""
if not registros_novos:
return 0
try:
conn = get_db_connection()
cursor = conn.cursor()
insert_query = """
INSERT INTO [GINSENG].[dbo].[fato_notas_entrada] (
[chave], [cnf], [serie], [data_emissao], [hora_emissao], [cnpj_emissor],
[nome_emissor], [cnpj_destinatario], [valor_total_produtos], [valor_icmsst],
[valor_fcpst], [valor_frete], [valor_seguro], [valor_outras_despesas],
[valor_ii], [valor_ipi], [valor_ipi_devol], [valor_servicos], [valor_desconto],
[valor_icms_desonerado], [valor_liquido], [tipo_pagamento_json], [numero_fatura],
[qtd_parcelas], [duplicatas_json], [valor_icms], [situacao], [TRIAL119]
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
registros_inseridos = 0
for registro in registros_novos:
try:
cursor.execute(insert_query, (
registro.get('chave', ''),
registro.get('cnf', ''),
registro.get('serie', ''),
registro.get('data_emissao', None),
registro.get('hora_emissao', None),
registro.get('cnpj_emissor', ''),
registro.get('nome_emissor', ''),
registro.get('cnpj_destinatario', ''),
float(registro.get('valor_total_produtos', 0)) if registro.get('valor_total_produtos') else None,
float(registro.get('valor_icmsst', 0)) if registro.get('valor_icmsst') else None,
float(registro.get('valor_fcpst', 0)) if registro.get('valor_fcpst') else None,
float(registro.get('valor_frete', 0)) if registro.get('valor_frete') else None,
float(registro.get('valor_seguro', 0)) if registro.get('valor_seguro') else None,
float(registro.get('valor_outras_despesas', 0)) if registro.get('valor_outras_despesas') else None,
float(registro.get('valor_ii', 0)) if registro.get('valor_ii') else None,
float(registro.get('valor_ipi', 0)) if registro.get('valor_ipi') else None,
float(registro.get('valor_ipi_devol', 0)) if registro.get('valor_ipi_devol') else None,
float(registro.get('valor_servicos', 0)) if registro.get('valor_servicos') else None,
float(registro.get('valor_desconto', 0)) if registro.get('valor_desconto') else None,
float(registro.get('valor_icms_desonerado', 0)) if registro.get('valor_icms_desonerado') else None,
float(registro.get('valor_liquido', 0)) if registro.get('valor_liquido') else None,
registro.get('tipo_pagamento_json', ''),
registro.get('numero_fatura', ''),
int(registro.get('qtd_parcelas', 0)) if registro.get('qtd_parcelas') else None,
registro.get('duplicatas_json', ''),
float(registro.get('valor_icms', 0)) if registro.get('valor_icms') else None,
registro.get('situacao', ''),
registro.get('TRIAL119', '')
))
registros_inseridos += 1
except Exception as e:
print(f" Erro ao inserir registro {registro.get('chave', '')}: {e}")
conn.commit()
cursor.close()
conn.close()
return registros_inseridos
except Exception as e:
print(f" Erro ao conectar com banco de dados: {e}")
return 0
def obter_total_registros_dia(session, data_str):
"""Consulta quantos registros existem em um dia específico"""
params = {
"created_at[from]": data_str,
"created_at[to]": data_str,
"format_type": "JSON",
"limit": 1
}
response = fazer_requisicao_robusta(session, API_URL, API_HEADERS, params, max_tentativas=2)
if response and response.status_code == 200:
data = response.json()
return data.get('count', 0)
return 0
def processar_dia_chaves(session, data_str, all_extracted_data):
"""Processa todos os registros de um dia específico para extrair chaves"""
params = {
"created_at[from]": data_str,
"created_at[to]": data_str,
"format_type": "JSON",
"limit": 50
}
registros_dia = 0
registros_filtrados = 0
page_count = 0
while True:
page_count += 1
print(f" Página {page_count}...", end=" ")
response = fazer_requisicao_robusta(session, API_URL, API_HEADERS, params, max_tentativas=3)
if not response or response.status_code != 200:
print(f"Falha")
break
data = response.json()
nfe_list = data.get('data', [])
if not nfe_list:
print(f"Fim dos dados")
break
print(f"{len(nfe_list)} registros")
for nfe_data in nfe_list:
registros_dia += 1
nfe = nfe_data.get('xml', {}).get('NFe', {})
inf_nfe = nfe.get('infNFe', {})
ide = inf_nfe.get('ide', {})
emit = inf_nfe.get('emit', {})
dest = inf_nfe.get('dest', {})
total = inf_nfe.get('total', {}).get('ICMSTot', {})
cobr = inf_nfe.get('cobr', {})
cnpj_emissor = str(emit.get('CNPJ', '')).strip()
if cnpj_emissor not in CNPJS_PERMITIDOS:
continue
registros_filtrados += 1
registro = {
'chave': str(nfe_data.get('access_key', '')).strip(),
'cnf': str(ide.get('cNF', '')).strip(),
'serie': str(ide.get('serie', '')).strip(),
'data_emissao': ide.get('dhEmi', '').split('T')[0] if ide.get('dhEmi') else '',
'hora_emissao': ide.get('dhEmi', '').split('T')[1].split('-')[0] if ide.get('dhEmi') and 'T' in ide.get('dhEmi', '') else '',
'cnpj_emissor': cnpj_emissor,
'nome_emissor': str(emit.get('xNome', '')).strip(),
'cnpj_destinatario': str(dest.get('CNPJ', '')).strip(),
'valor_total_produtos': str(total.get('vProd', '')).strip(),
'valor_icmsst': str(total.get('vST', '')).strip(),
'valor_fcpst': str(total.get('vFCPST', '')).strip(),
'valor_frete': str(total.get('vFrete', '')).strip(),
'valor_seguro': str(total.get('vSeg', '')).strip(),
'valor_outras_despesas': str(total.get('vOutro', '')).strip(),
'valor_ii': str(total.get('vII', '')).strip(),
'valor_ipi': str(total.get('vIPI', '')).strip(),
'valor_ipi_devol': str(total.get('vIPIDevol', '')).strip(),
'valor_servicos': '',
'valor_desconto': str(total.get('vDesc', '')).strip(),
'valor_icms_desonerado': str(total.get('vICMSDeson', '')).strip(),
'valor_liquido': str(total.get('vNF', '')).strip(),
'tipo_pagamento_json': json.dumps(inf_nfe.get('pag', {}), ensure_ascii=False) if inf_nfe.get('pag') else '',
'numero_fatura': str(cobr.get('fat', {}).get('nFat', '')).strip() if cobr.get('fat') else '',
'qtd_parcelas': len(cobr.get('dup', [])) if cobr.get('dup') else 0,
'duplicatas_json': json.dumps(cobr.get('dup', []), ensure_ascii=False) if cobr.get('dup') else '',
'valor_icms': str(total.get('vICMS', '')).strip(),
'situacao': str(inf_nfe.get('protNFe', {}).get('infProt', {}).get('cStat', '')).strip() if inf_nfe.get('protNFe') else ''
}
all_extracted_data.append(registro)
page_info = data.get('page', {})
next_url = page_info.get('next')
if not next_url:
break
if 'cursor=' in next_url:
cursor_value = next_url.split('cursor=')[1].split('&')[0]
params['cursor'] = cursor_value
else:
break
time.sleep(0.3)
return registros_filtrados
def buscar_notas_e_identificar_novas():
"""
PARTE 1: Busca notas no período e retorna lista de chaves novas
Retorna: lista de chaves que não existem no banco
"""
print("\n" + "=" * 70)
print(" PARTE 1: BUSCANDO NOTAS E IDENTIFICANDO CHAVES NOVAS")
print("=" * 70)
data_atual = datetime.now()
data_inicio = data_atual - timedelta(days=DIAS_PARA_BUSCAR)
print(f" Período: {data_inicio.strftime('%Y-%m-%d')} até {data_atual.strftime('%Y-%m-%d')}")
print(f" CNPJs permitidos: {', '.join(CNPJS_PERMITIDOS)}")
session = create_session_with_retry()
# Analisar dias com dados
print(f"\n Analisando período por dia...")
detalhes_dias = []
current_date = data_inicio
while current_date <= data_atual:
data_str = current_date.strftime("%Y-%m-%d")
print(f" Consultando {data_str}...", end=" ")
registros = obter_total_registros_dia(session, data_str)
if registros > 0:
detalhes_dias.append((data_str, registros))
print(f"{registros:,} registros")
else:
print("0 registros")
current_date += timedelta(days=1)
time.sleep(0.3)
if not detalhes_dias:
print(" Nenhum registro encontrado no período.")
return []
# Extrair dados
print(f"\n Extraindo dados dos dias com registros...")
all_extracted_data = []
for dia, qtd in detalhes_dias:
print(f"\n Processando {dia} ({qtd:,} registros esperados)")
processar_dia_chaves(session, dia, all_extracted_data)
print(f"\n Total extraído (após filtro CNPJ): {len(all_extracted_data):,} registros")
if not all_extracted_data:
print(" Nenhum dado extraído após filtro de CNPJ.")
return []
# Verificar duplicatas
print(f"\n Verificando duplicatas no banco...")
chaves_extraidas = [r['chave'] for r in all_extracted_data if r['chave']]
chaves_existentes = consultar_chaves_existentes(chaves_extraidas)
# Filtrar registros novos
registros_novos = [r for r in all_extracted_data if r['chave'] not in chaves_existentes]
print(f" Registros duplicados: {len(all_extracted_data) - len(registros_novos):,}")
print(f" Registros novos: {len(registros_novos):,}")
if not registros_novos:
print(" Todos os registros já existem no banco.")
return []
# Inserir no banco fato_notas_entrada
print(f"\n Inserindo {len(registros_novos):,} notas no banco...")
inseridos = inserir_nfes_banco(registros_novos)
print(f" {inseridos:,} notas inseridas com sucesso!")
# Retornar lista de chaves novas
chaves_novas = [r['chave'] for r in registros_novos if r['chave']]
return chaves_novas
# =============================================================================
# PARTE 2: BUSCAR ITENS DAS NOTAS NOVAS
# =============================================================================
def buscar_valor_xml(item_element, campo):
"""Busca um valor específico dentro de um elemento XML do item da NFe"""
try:
prod = item_element.find('prod')
imposto = item_element.find('imposto')
if campo in ['cProd', 'cEAN', 'xProd', 'NCM', 'CEST', 'CFOP', 'uCom', 'qCom', 'vUnCom', 'vProd', 'vFrete', 'vSeg', 'vDesc', 'vOutro', 'xPed']:
if prod is not None:
elem = prod.find(campo)
if elem is not None and elem.text:
valor = elem.text.strip()
if campo == 'qCom':
try:
valor_float = float(valor)
if valor_float == int(valor_float):
valor = str(int(valor_float))
else:
valor = str(valor_float).rstrip('0').rstrip('.')
except:
pass
return valor
return ''
elif campo in ['orig', 'CST', 'modBC', 'vBC', 'pICMS', 'vICMS', 'vBCFCP', 'pFCP', 'vFCP', 'modBCST', 'pMVAST', 'vBCST', 'pICMSST', 'vICMSST', 'vBCFCPST', 'pFCPST', 'vFCPST', 'vICMSDeson']:
if imposto is not None:
icms = imposto.find('ICMS')
if icms is not None:
for icms_child in icms:
elem = icms_child.find(campo)
if elem is not None and elem.text:
valor = elem.text.strip()
if campo in ['pICMS', 'pICMSST', 'pFCP', 'pFCPST', 'pMVAST']:
try:
valor_float = float(valor)
valor = str(round(valor_float / 100, 4))
except:
pass
return valor
return ''
elif campo == 'vII':
if imposto is not None:
ii = imposto.find('II')
if ii is not None:
elem = ii.find('vII')
if elem is not None and elem.text:
return elem.text.strip()
return ''
elif campo == 'vIPI':
if imposto is not None:
ipi = imposto.find('IPI')
if ipi is not None:
for ipi_child in ipi:
elem = ipi_child.find('vIPI')
if elem is not None and elem.text:
return elem.text.strip()
return ''
return ''
except:
return ''
def inserir_itens_banco(lista_itens):
"""Insere os itens da NFe no banco de dados"""
if not lista_itens:
return 0
try:
conn = get_db_connection()
cursor = conn.cursor()
try:
cursor.execute("SELECT TOP 0 * FROM [GINSENG].[dbo].[fato_notas_entrada_itens]")
colunas_tabela = [desc[0].lower() for desc in cursor.description]
colunas_tabela = [col for col in colunas_tabela if col not in ['id']]
except Exception as e:
print(f" Erro ao verificar estrutura da tabela: {e}")
colunas_tabela = ['chave', 'n_item', 'cod_produto', 'produto', 'quantidade', 'valor_unitario', 'valor_total_produtos']
campos_mapeados = {
'chave': 'chave',
'n_item': 'n_item',
'data_emissao': 'data_emissao',
'cod_produto': 'cod_produto',
'produto': 'produto',
'cEAN': 'cean',
'NCM': 'ncm',
'CEST': 'cest',
'CFOP': 'cfop',
'unidade_medida': 'unidade_medida',
'quantidade': 'quantidade',
'valor_unitario': 'valor_unitario',
'valor_total_produtos': 'valor_total_produtos',
'valor_frete': 'valor_frete',
'valor_seguro': 'valor_seguro',
'valor_desconto': 'valor_desconto',
'valor_outras_despesas': 'valor_outras_despesas',
'codigo_pedido': 'codigo_pedido',
'cod_origem': 'cod_origem',
'CST': 'cst',
'modalidade_BC_ICMS': 'modalidade_bc_icms',
'valor_BC_ICMS': 'valor_bc_icms',
'aliquota_ICMS': 'aliquota_icms',
'valor_ICMS': 'valor_icms',
'valor_BC_FCP': 'valor_bc_fcp',
'aliquota_FCP': 'aliquota_fcp',
'valor_FCP': 'valor_fcp',
'modalidade_BC_ST': 'modalidade_bc_st',
'aliquota_MVA_ST': 'aliquota_mva_st',
'valor_BC_ST': 'valor_bc_st',
'aliquota_ICMS_ST': 'aliquota_icms_st',
'valor_ICMSST': 'valor_icmsst',
'valor_BC_FCPST': 'valor_bc_fcpst',
'aliquota_FCPST': 'aliquota_fcpst',
'valor_FCPST': 'valor_fcpst',
'valor_II': 'valor_ii',
'valor_IPI': 'valor_ipi',
'valor_ICMS_desonerado': 'valor_icms_desonerado'
}
campos_validos = []
valores_placeholders = []
for campo_item, campo_tabela in campos_mapeados.items():
if campo_tabela in colunas_tabela:
campos_validos.append(f"[{campo_tabela}]")
valores_placeholders.append("?")
if not campos_validos:
print(" Nenhum campo válido encontrado para inserção!")
return 0
insert_query = f"""
INSERT INTO [GINSENG].[dbo].[fato_notas_entrada_itens] ({', '.join(campos_validos)})
VALUES ({', '.join(valores_placeholders)})
"""
registros_inseridos = 0
for item in lista_itens:
try:
valores = []
for campo_item, campo_tabela in campos_mapeados.items():
if campo_tabela in colunas_tabela:
valor = item.get(campo_item, '')
if valor == '':
valor = None
valores.append(valor)
cursor.execute(insert_query, valores)
registros_inseridos += 1
except Exception as e:
print(f" Erro ao inserir item {item.get('n_item', '')}: {e}")
conn.commit()
cursor.close()
conn.close()
return registros_inseridos
except Exception as e:
print(f" Erro ao conectar com banco de dados: {e}")
return 0
def buscar_itens_das_chaves(chaves_novas):
"""
PARTE 2: Busca os itens das notas novas via API
"""
print("\n" + "=" * 70)
print(" PARTE 2: BUSCANDO ITENS DAS NOTAS NOVAS")
print("=" * 70)
if not chaves_novas:
print(" Nenhuma chave nova para processar.")
return 0
print(f" Total de chaves para buscar itens: {len(chaves_novas):,}")
session = create_session_with_retry()
lista_itens_notas = []
chaves_processadas = 0
chaves_com_erro = 0
for i, chave in enumerate(chaves_novas, 1):
print(f"\n [{i}/{len(chaves_novas)}] Processando: {chave[:20]}...")
url = f"{API_URL}?access_key[]={chave}"
try:
response = fazer_requisicao_robusta(session, url, API_HEADERS, None, max_tentativas=3)
if response and response.status_code == 200:
data = response.json()
nfe_list = data.get('data', [])
if not nfe_list:
print(f" Nenhum dado retornado")
continue
for nfe_data in nfe_list:
chave_retornada = nfe_data.get('access_key', '')
xml_base64 = nfe_data.get('xml', '')
if not xml_base64 or not isinstance(xml_base64, str):
print(f" XML não encontrado")
continue
try:
xml_decoded = base64.b64decode(xml_base64).decode('utf-8')
xml_decoded = xml_decoded.replace('xmlns="http://www.portalfiscal.inf.br/nfe"', '')
root = ET.fromstring(xml_decoded)
except Exception as e:
print(f" Erro ao decodificar XML: {e}")
continue
# Buscar data de emissão
data_emissao = ''
dhEmi_elem = root.find('.//ide/dhEmi')
if dhEmi_elem is not None and dhEmi_elem.text:
data_emissao = dhEmi_elem.text[:10]
# Buscar todos os itens
itens = root.findall('.//det')
print(f" {len(itens)} itens encontrados (Emissão: {data_emissao})")
for j, item in enumerate(itens, 1):
n_item = item.get('nItem', str(j))
item_data = {
'chave': chave_retornada,
'n_item': n_item,
'data_emissao': data_emissao,
'cod_produto': buscar_valor_xml(item, 'cProd'),
'produto': buscar_valor_xml(item, 'xProd'),
'cEAN': buscar_valor_xml(item, 'cEAN'),
'NCM': buscar_valor_xml(item, 'NCM'),
'CEST': buscar_valor_xml(item, 'CEST'),
'CFOP': buscar_valor_xml(item, 'CFOP'),
'unidade_medida': buscar_valor_xml(item, 'uCom'),
'quantidade': buscar_valor_xml(item, 'qCom'),
'valor_unitario': buscar_valor_xml(item, 'vUnCom'),
'valor_total_produtos': buscar_valor_xml(item, 'vProd'),
'valor_frete': buscar_valor_xml(item, 'vFrete'),
'valor_seguro': buscar_valor_xml(item, 'vSeg'),
'valor_desconto': buscar_valor_xml(item, 'vDesc'),
'valor_outras_despesas': buscar_valor_xml(item, 'vOutro'),
'codigo_pedido': buscar_valor_xml(item, 'xPed'),
'cod_origem': buscar_valor_xml(item, 'orig'),
'CST': buscar_valor_xml(item, 'CST'),
'modalidade_BC_ICMS': buscar_valor_xml(item, 'modBC'),
'valor_BC_ICMS': buscar_valor_xml(item, 'vBC'),
'aliquota_ICMS': buscar_valor_xml(item, 'pICMS'),
'valor_ICMS': buscar_valor_xml(item, 'vICMS'),
'valor_BC_FCP': buscar_valor_xml(item, 'vBCFCP'),
'aliquota_FCP': buscar_valor_xml(item, 'pFCP'),
'valor_FCP': buscar_valor_xml(item, 'vFCP'),
'modalidade_BC_ST': buscar_valor_xml(item, 'modBCST'),
'aliquota_MVA_ST': buscar_valor_xml(item, 'pMVAST'),
'valor_BC_ST': buscar_valor_xml(item, 'vBCST'),
'aliquota_ICMS_ST': buscar_valor_xml(item, 'pICMSST'),
'valor_ICMSST': buscar_valor_xml(item, 'vICMSST'),
'valor_BC_FCPST': buscar_valor_xml(item, 'vBCFCPST'),
'aliquota_FCPST': buscar_valor_xml(item, 'pFCPST'),
'valor_FCPST': buscar_valor_xml(item, 'vFCPST'),
'valor_II': buscar_valor_xml(item, 'vII'),
'valor_IPI': buscar_valor_xml(item, 'vIPI'),
'valor_ICMS_desonerado': buscar_valor_xml(item, 'vICMSDeson')
}
lista_itens_notas.append(item_data)
chaves_processadas += 1
else:
print(f" Erro na requisição")
chaves_com_erro += 1
except Exception as e:
print(f" Erro: {e}")
chaves_com_erro += 1
print(f"\n Chaves processadas: {chaves_processadas:,}")
print(f" Chaves com erro: {chaves_com_erro:,}")
print(f" Total de itens extraídos: {len(lista_itens_notas):,}")
# Inserir itens no banco
if lista_itens_notas:
print(f"\n Inserindo {len(lista_itens_notas):,} itens no banco...")
inseridos = inserir_itens_banco(lista_itens_notas)
print(f" {inseridos:,} itens inseridos com sucesso!")
return inseridos
return 0
# =============================================================================
# ENVIAR STATUS PARA API
# =============================================================================
def enviar_status_api(sucesso):
"""Envia status para a API de monitoramento"""
try:
print("\n" + "=" * 70)
print(" ENVIANDO STATUS PARA API DE MONITORAMENTO")
print("=" * 70)
url = "https://api.grupoginseng.com.br/api/status/4"
sao_paulo_offset = timedelta(hours=-3)
current_datetime = datetime.now(timezone(sao_paulo_offset)).strftime("%Y-%m-%d %H:%M:%S")
status_code = "OK" if sucesso else "FAIL"
payload = {
"STATUS": status_code,
"DATA": current_datetime
}
headers = {"Content-Type": "application/json"}
response = requests.put(url, json=payload, headers=headers)
print(f" Status: {status_code}")
print(f" Hora: {current_datetime}")
print(f" Response: {response.status_code}")
except Exception as e:
print(f" Erro ao enviar status: {e}")
# =============================================================================
# EXECUÇÃO PRINCIPAL
# =============================================================================
def main():
print("\n" + "=" * 70)
print(" SCRIPT COMPLETO - NOTAS E ITENS")
print(" Data de execução:", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("=" * 70)
sucesso = True
try:
# PARTE 1: Buscar notas e identificar chaves novas
chaves_novas = buscar_notas_e_identificar_novas()
# PARTE 2: Buscar itens das notas novas
if chaves_novas:
itens_inseridos = buscar_itens_das_chaves(chaves_novas)
else:
print("\n Nenhuma chave nova encontrada. Pulando busca de itens.")
itens_inseridos = 0
# Resumo final
print("\n" + "=" * 70)
print(" RESUMO FINAL")
print("=" * 70)
print(f" Notas novas encontradas: {len(chaves_novas):,}")
print(f" Itens inseridos: {itens_inseridos:,}")
except Exception as e:
print(f"\n ERRO GERAL: {e}")
sucesso = False
# Enviar status
enviar_status_api(sucesso)
print("\n" + "=" * 70)
print(" PROCESSAMENTO CONCLUÍDO!")
print("=" * 70)
if __name__ == "__main__":
main()