Correção arredondamento dasalíquotas e valor taxa
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:
46
app/main.py
46
app/main.py
@@ -31,6 +31,7 @@ from fastapi import Query
|
||||
from sqlalchemy import select as sqla_select
|
||||
from app.models import AliquotaUF
|
||||
import pandas as pd
|
||||
from fastapi.responses import Response
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
@@ -499,31 +500,54 @@ async def export_excel(
|
||||
cols = [c for c in df.columns if c != "Arquivo PDF"] + ["Arquivo PDF"]
|
||||
df = df[cols]
|
||||
|
||||
# garante que colunas percentuais estejam numéricas (se existirem)
|
||||
for col in ["ICMS (%)", "ICMS (%) (UF/Ref)", "Dif. ICMS (pp)", "PIS (%)", "COFINS (%)"]:
|
||||
# converte colunas numéricas (percentuais, R$, etc.)
|
||||
percent_cols = ["ICMS (%)", "ICMS (%) (UF/Ref)", "Dif. ICMS (pp)", "PIS (%)", "COFINS (%)"]
|
||||
money_cols = ["Valor Total", "ICMS (R$)", "PIS (R$)", "COFINS (R$)",
|
||||
"Base ICMS (R$)", "Base PIS (R$)", "Base COFINS (R$)"]
|
||||
other_dec6 = ["Tarifa", "Consumo (kWh)"]
|
||||
|
||||
from decimal import Decimal
|
||||
for col in percent_cols + money_cols + other_dec6:
|
||||
if col in df.columns:
|
||||
df[col] = df[col].map(lambda x: float(x) if isinstance(x, Decimal) else x)
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
# --- gera o XLSX ---
|
||||
with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
|
||||
df.to_excel(writer, index=False, sheet_name="Relatório")
|
||||
|
||||
# aplica formatação 6 casas decimais
|
||||
wb = writer.book
|
||||
ws = writer.sheets["Relatório"]
|
||||
fmt_4dec = wb.add_format({"num_format": "0.000000"})
|
||||
|
||||
for col in ["ICMS (%)", "ICMS (%) (UF/Ref)", "Dif. ICMS (pp)", "PIS (%)", "COFINS (%)", "Consumo (kWh)"]:
|
||||
fmt_dec6 = wb.add_format({"num_format": "0.000000"})
|
||||
fmt_money6 = wb.add_format({"num_format": "#,##0.000000"})
|
||||
fmt_money2 = wb.add_format({"num_format": "#,##0.00"})
|
||||
|
||||
for col in percent_cols:
|
||||
if col in df.columns:
|
||||
i = df.columns.get_loc(col)
|
||||
# largura automática básica + formato; ajuste a largura se quiser (ex.: 12)
|
||||
ws.set_column(i, i, None, fmt_4dec)
|
||||
ws.set_column(i, i, 14, fmt_dec6)
|
||||
|
||||
for col in money_cols:
|
||||
if col in df.columns:
|
||||
i = df.columns.get_loc(col)
|
||||
ws.set_column(i, i, 14, fmt_money6) # ou fmt_money2 se quiser 2 casas
|
||||
|
||||
for col in other_dec6:
|
||||
if col in df.columns:
|
||||
i = df.columns.get_loc(col)
|
||||
ws.set_column(i, i, 14, fmt_dec6)
|
||||
|
||||
# IMPORTANTE: só aqui, FORA do with
|
||||
output.seek(0)
|
||||
data = output.getvalue()
|
||||
|
||||
return StreamingResponse(
|
||||
output,
|
||||
return Response(
|
||||
content=data,
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'}
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{filename}"',
|
||||
"Content-Length": str(len(data)),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -26,22 +26,22 @@ class Fatura(Base):
|
||||
classificacao_tarifaria = Column("classificacao_tarifaria", String)
|
||||
unidade_consumidora = Column("unidade_consumidora", String)
|
||||
referencia = Column(String)
|
||||
valor_total = Column(Float)
|
||||
valor_total = Column(Numeric(18, 6, asdecimal=True))
|
||||
|
||||
pis_aliq = Column("pis_aliq", Float)
|
||||
pis_valor = Column("pis_valor", Float)
|
||||
pis_base = Column("pis_base", Float)
|
||||
pis_aliq = Column(Numeric(8, 6, asdecimal=True))
|
||||
pis_valor = Column(Numeric(18, 6, asdecimal=True))
|
||||
pis_base = Column(Numeric(18, 6, asdecimal=True))
|
||||
|
||||
icms_aliq = Column("icms_aliq", Float)
|
||||
icms_valor = Column("icms_valor", Float)
|
||||
icms_base = Column("icms_base", Float)
|
||||
icms_aliq = Column(Numeric(8, 6, asdecimal=True))
|
||||
icms_valor = Column(Numeric(18, 6, asdecimal=True))
|
||||
icms_base = Column(Numeric(18, 6, asdecimal=True))
|
||||
|
||||
cofins_aliq = Column("cofins_aliq", Float)
|
||||
cofins_valor = Column("cofins_valor", Float)
|
||||
cofins_base = Column("cofins_base", Float)
|
||||
cofins_aliq = Column(Numeric(8, 6, asdecimal=True))
|
||||
cofins_valor = Column(Numeric(18, 6, asdecimal=True))
|
||||
cofins_base = Column(Numeric(18, 6, asdecimal=True))
|
||||
|
||||
consumo = Column("consumo", Float)
|
||||
tarifa = Column("tarifa", Float)
|
||||
consumo = Column(Numeric(14, 6, asdecimal=True))
|
||||
tarifa = Column(Numeric(12, 6, asdecimal=True))
|
||||
|
||||
nota_fiscal = Column(String)
|
||||
data_processamento = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
@@ -64,6 +64,41 @@ async def process_single_file(caminho_pdf_temp: str, nome_original: str, cliente
|
||||
dados = extrair_dados_pdf(caminho_pdf_temp)
|
||||
dados['arquivo_pdf'] = nome_original
|
||||
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
|
||||
_Q6 = Decimal("0.000000")
|
||||
|
||||
def _to_percent_6(x):
|
||||
"""Converte para percent (se vier em fração) e quantiza em 6 casas."""
|
||||
if x is None:
|
||||
return None
|
||||
try:
|
||||
v = Decimal(str(x))
|
||||
except Exception:
|
||||
return None
|
||||
# se vier em fração (ex.: 0.012872), vira 1.2872… (percentual)
|
||||
if Decimal("0") < v <= Decimal("1"):
|
||||
v = v * Decimal("100")
|
||||
return v.quantize(_Q6, rounding=ROUND_HALF_UP)
|
||||
|
||||
def _to_dec6(x):
|
||||
"""Apenas 6 casas, sem % (use para tarifa, bases, etc.)."""
|
||||
if x is None:
|
||||
return None
|
||||
try:
|
||||
v = Decimal(str(x))
|
||||
except Exception:
|
||||
return None
|
||||
return v.quantize(_Q6, rounding=ROUND_HALF_UP)
|
||||
|
||||
dados['icms_aliq'] = _to_percent_6(dados.get('icms_aliq'))
|
||||
dados['pis_aliq'] = _to_percent_6(dados.get('pis_aliq'))
|
||||
dados['cofins_aliq'] = _to_percent_6(dados.get('cofins_aliq'))
|
||||
|
||||
# tarifa NÃO é percentual: apenas 6 casas
|
||||
dados['tarifa'] = _to_dec6(dados.get('tarifa'))
|
||||
|
||||
|
||||
# Verifica se a fatura já existe
|
||||
existente_result = await session.execute(
|
||||
select(Fatura).filter_by(
|
||||
|
||||
Reference in New Issue
Block a user