Atualização: template Excel de alíquotas e layout da aba
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -2,6 +2,7 @@ import logging
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
import httpx
|
||||
from sqlalchemy.future import select
|
||||
from app.utils import extrair_dados_pdf
|
||||
from app.database import AsyncSessionLocal
|
||||
@@ -9,6 +10,9 @@ from app.models import Fatura, LogProcessamento
|
||||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
from app.models import SelicMensal
|
||||
from sqlalchemy import select
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -28,6 +32,9 @@ def remover_arquivo_temp(caminho_pdf):
|
||||
logger.warning(f"Falha ao remover arquivo temporário: {e}")
|
||||
|
||||
def salvar_em_uploads(caminho_pdf_temp, nome_original, nota_fiscal):
|
||||
ERROS_DIR = os.path.join("app", "uploads", "erros")
|
||||
os.makedirs(ERROS_DIR, exist_ok=True)
|
||||
erros_detectados = []
|
||||
try:
|
||||
extensao = os.path.splitext(nome_original)[1].lower()
|
||||
nome_destino = f"{nota_fiscal}_{uuid.uuid4().hex[:6]}{extensao}"
|
||||
@@ -35,6 +42,17 @@ def salvar_em_uploads(caminho_pdf_temp, nome_original, nota_fiscal):
|
||||
shutil.copy2(caminho_pdf_temp, destino_final)
|
||||
return destino_final
|
||||
except Exception as e:
|
||||
# Copiar o arquivo com erro
|
||||
extensao = os.path.splitext(nome_original)[1].lower()
|
||||
nome_arquivo = f"{uuid.uuid4().hex[:6]}_erro{extensao}"
|
||||
caminho_pdf = caminho_pdf_temp
|
||||
|
||||
shutil.copy2(caminho_pdf, os.path.join(ERROS_DIR, nome_arquivo))
|
||||
|
||||
mensagem = f"{nome_arquivo}: {str(e)}"
|
||||
|
||||
erros_detectados.append(mensagem)
|
||||
|
||||
logger.error(f"Erro ao salvar em uploads: {e}")
|
||||
return caminho_pdf_temp
|
||||
|
||||
@@ -61,6 +79,10 @@ async def process_single_file(caminho_pdf_temp: str, nome_original: str):
|
||||
"dados": dados,
|
||||
"tempo": f"{duracao}s"
|
||||
}
|
||||
|
||||
data_comp = dados.get("competencia")
|
||||
if data_comp:
|
||||
await garantir_selic_para_competencia(session, data_comp.year, data_comp.month)
|
||||
|
||||
# Salva arquivo final
|
||||
caminho_final = salvar_em_uploads(caminho_pdf_temp, nome_original, dados['nota_fiscal'])
|
||||
@@ -97,7 +119,6 @@ async def process_single_file(caminho_pdf_temp: str, nome_original: str):
|
||||
"trace": erro_str
|
||||
}
|
||||
|
||||
|
||||
async def processar_em_lote():
|
||||
import traceback # para exibir erros
|
||||
resultados = []
|
||||
@@ -128,9 +149,49 @@ async def processar_em_lote():
|
||||
})
|
||||
print(f"Erro ao processar {item['nome_original']}: {e}")
|
||||
print(traceback.format_exc())
|
||||
# Após o loop, salvar TXT com erros
|
||||
erros_txt = []
|
||||
for nome, status in status_arquivos.items():
|
||||
if status['status'] == 'Erro':
|
||||
erros_txt.append(f"{nome} - {status.get('mensagem', 'Erro desconhecido')}")
|
||||
|
||||
if erros_txt:
|
||||
with open(os.path.join(UPLOADS_DIR, "erros", "erros.txt"), "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(erros_txt))
|
||||
|
||||
# Compacta PDFs com erro
|
||||
with ZipFile(os.path.join(UPLOADS_DIR, "erros", "faturas_erro.zip"), "w") as zipf:
|
||||
for nome in status_arquivos:
|
||||
if status_arquivos[nome]['status'] == 'Erro':
|
||||
caminho = os.path.join(UPLOADS_DIR, "temp", nome)
|
||||
if os.path.exists(caminho):
|
||||
zipf.write(caminho, arcname=nome)
|
||||
return resultados
|
||||
|
||||
def limpar_arquivos_processados():
|
||||
status_arquivos.clear()
|
||||
while not fila_processamento.empty():
|
||||
fila_processamento.get_nowait()
|
||||
|
||||
async def garantir_selic_para_competencia(session, ano, mes):
|
||||
# Verifica se já existe
|
||||
result = await session.execute(select(SelicMensal).filter_by(ano=ano, mes=mes))
|
||||
existente = result.scalar_one_or_none()
|
||||
if existente:
|
||||
return # já tem
|
||||
|
||||
# Busca na API do Banco Central
|
||||
url = (
|
||||
f"https://api.bcb.gov.br/dados/serie/bcdata.sgs.4390/dados?"
|
||||
f"formato=json&dataInicial=01/{mes:02d}/{ano}&dataFinal=30/{mes:02d}/{ano}"
|
||||
)
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.get(url)
|
||||
resp.raise_for_status()
|
||||
dados = resp.json()
|
||||
|
||||
if dados:
|
||||
percentual = float(dados[0]["valor"].replace(",", "."))
|
||||
novo = SelicMensal(ano=ano, mes=mes, fator=percentual)
|
||||
session.add(novo)
|
||||
await session.commit()
|
||||
Reference in New Issue
Block a user