57 lines
2.3 KiB
Python
57 lines
2.3 KiB
Python
"""Conversões e erros compartilhados pelos parsers de PDF (`parser.py`, leiaute
|
|
Questor, e `parser_contabit.py`, leiaute Contabit). Vivem aqui, e não em
|
|
`parser.py`, só pra evitar import circular: `parser.py` despacha para
|
|
`parser_contabit.py`, que precisa das mesmas conversões. `parser.py`
|
|
reexporta `ExtracaoInvalidaError`, então quem já importava de lá continua
|
|
funcionando."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
from decimal import Decimal, InvalidOperation
|
|
|
|
|
|
class ExtracaoInvalidaError(Exception):
|
|
"""PDF não tem o formato esperado (cabeçalho/tabela do Balancete/DRE não
|
|
encontrados) — a view converte isso numa resposta 400 genérica, mesmo
|
|
padrão de `planos_saude`/`indicadores`."""
|
|
|
|
|
|
class CodigoEmpresaAusenteError(ExtracaoInvalidaError):
|
|
"""O leiaute Contabit não traz o código da empresa no PDF, então ele vem
|
|
do nome do arquivo ("1512 - Balancete 082026.pdf"). Subclasse própria pra
|
|
a view mostrar uma mensagem específica em vez da genérica de formato
|
|
inválido: o PDF em si está certo, só o nome do arquivo não segue o
|
|
padrão."""
|
|
|
|
|
|
def para_decimal(texto: str) -> Decimal:
|
|
texto = texto.strip()
|
|
# Algumas empresas têm linhas de DRE negativas no PDF com só o parêntese
|
|
# de abertura ("(1.114.989,29", sem o "()" de fechamento) — confirmado
|
|
# direto nos caracteres brutos do PDF (não é um artefato de
|
|
# _reconstroi_linhas), então o "(" sozinho já basta pra marcar negativo.
|
|
negativo = texto.startswith("(")
|
|
if negativo:
|
|
texto = texto[1:]
|
|
if texto.endswith(")"):
|
|
texto = texto[:-1]
|
|
texto = texto.replace(".", "").replace(",", ".")
|
|
try:
|
|
valor = Decimal(texto)
|
|
except InvalidOperation as exc:
|
|
raise ExtracaoInvalidaError(f"Valor monetário inválido: {texto!r}") from exc
|
|
return -valor if negativo else valor
|
|
|
|
|
|
def para_percentual(texto: str) -> Decimal:
|
|
"""Mesma conversão de `para_decimal`, só removendo o "%" antes — o sinal
|
|
negativo continua expresso só pelo parêntese (ex. "(7,33%)") no Questor,
|
|
ou pelo "-" no Contabit, igual ao valor monetário."""
|
|
return para_decimal(texto.replace("%", ""))
|
|
|
|
|
|
def para_data(texto: str) -> date:
|
|
dia, mes, ano = texto.split("/")
|
|
return date(int(ano), int(mes), int(dia))
|