Speed / src /generators /gia.py
LesterCerioli's picture
Building Speed LLM
d9d7b41
Raw
History Blame Contribute Delete
7.02 kB
from __future__ import annotations
import hashlib
from dataclasses import dataclass, field
from datetime import date
from decimal import Decimal
from pathlib import Path
from src.fiscal.entities import Empresa
def _fmt(v: Decimal) -> str:
return f"{v:.2f}"
@dataclass
class ApuracaoGIA:
"""Dados de apuração do ICMS para a GIA-SP."""
debitos_operacoes_proprias: Decimal = Decimal("0")
debitos_st_retencao: Decimal = Decimal("0") # ST retido nas saídas
estorno_credito: Decimal = Decimal("0") # estorno de créditos indevidos
outros_debitos: Decimal = Decimal("0")
creditos_entradas: Decimal = Decimal("0")
creditos_outros: Decimal = Decimal("0")
estorno_debito: Decimal = Decimal("0") # estorno de débitos indevidos
outros_creditos: Decimal = Decimal("0")
compensacoes: Decimal = Decimal("0")
saldo_credor_anterior: Decimal = Decimal("0")
@dataclass
class ApuracaoGIAST:
"""Dados de ICMS-ST por UF de origem para GIA-ST."""
uf_substituto: str # UF do substituto tributário (remetente)
base_calculo: Decimal
aliquota: Decimal # % ex: Decimal("18")
imposto_retido: Decimal
valor_entradas_st: Decimal = Decimal("0") # ICMS-ST nas entradas (crédito)
class GeradorGIA:
VERSAO_LAYOUT = "013"
def __init__(
self,
empresa: Empresa,
periodo: str, # "YYYY-MM"
apuracao: ApuracaoGIA,
apuracoes_st: list[ApuracaoGIAST] | None = None,
retificadora: bool = False,
):
self.empresa = empresa
self.periodo = periodo
self.apuracao = apuracao
self.apuracoes_st = apuracoes_st or []
self.retificadora = retificadora
# ------------------------------------------------------------------
# Cálculos derivados
# ------------------------------------------------------------------
@property
def total_debitos(self) -> Decimal:
a = self.apuracao
return a.debitos_operacoes_proprias + a.debitos_st_retencao + a.estorno_credito + a.outros_debitos
@property
def total_creditos(self) -> Decimal:
a = self.apuracao
return (a.creditos_entradas + a.creditos_outros
+ a.estorno_debito + a.outros_creditos
+ a.saldo_credor_anterior)
@property
def saldo_periodo(self) -> Decimal:
return self.total_debitos - self.total_creditos - self.apuracao.compensacoes
@property
def icms_a_recolher(self) -> Decimal:
return max(Decimal("0"), self.saldo_periodo)
@property
def saldo_credor_proximo(self) -> Decimal:
return max(Decimal("0"), -self.saldo_periodo)
def data_vencimento(self) -> date:
ano, mes = map(int, self.periodo.split("-"))
if mes == 12:
return date(ano + 1, 1, 20)
return date(ano, mes + 1, 20)
# ------------------------------------------------------------------
# Geração do arquivo AIE
# ------------------------------------------------------------------
def gerar_aie(self) -> str:
ano, mes = self.periodo.split("-")
periodo_aie = f"{ano}{mes}"
hoje = date.today().strftime("%d%m%Y")
a = self.apuracao
ie = self.empresa.ie or ""
cnpj = self.empresa.cnpj
razao = self.empresa.razao_social[:40]
municipio = self.empresa.endereco.municipio[:30] if self.empresa.endereco else "SAO PAULO"
cep = self.empresa.endereco.cep if self.empresa.endereco else "01310100"
linhas: list[str] = []
linhas.append(f"|GIA|{self.VERSAO_LAYOUT}|{cnpj}|{periodo_aie}|{hoje}|{'S' if self.retificadora else 'N'}|")
linhas.append(f"|IDENTIFICACAO|{ie}|{razao}|{municipio}|{cep}|{ie}|")
linhas.append(
f"|APURACAO_ICMS"
f"|{_fmt(a.debitos_operacoes_proprias)}"
f"|{_fmt(a.debitos_st_retencao)}"
f"|{_fmt(a.estorno_credito)}"
f"|{_fmt(a.outros_debitos)}"
f"|{_fmt(self.total_debitos)}"
f"|{_fmt(a.creditos_entradas)}"
f"|{_fmt(a.creditos_outros)}"
f"|{_fmt(a.estorno_debito)}"
f"|{_fmt(a.outros_creditos)}"
f"|{_fmt(self.total_creditos)}"
f"|{_fmt(a.saldo_credor_anterior)}"
f"|{_fmt(a.compensacoes)}"
f"|{_fmt(self.icms_a_recolher)}"
f"|{_fmt(self.saldo_credor_proximo)}"
f"|"
)
total_retido_st = Decimal("0")
total_entradas_st = Decimal("0")
for st in self.apuracoes_st:
saldo_st = st.imposto_retido - st.valor_entradas_st
linhas.append(
f"|GIA_ST"
f"|{st.uf_substituto}"
f"|{_fmt(st.base_calculo)}"
f"|{_fmt(st.aliquota)}"
f"|{_fmt(st.imposto_retido)}"
f"|{_fmt(st.valor_entradas_st)}"
f"|{_fmt(saldo_st)}"
f"|"
)
total_retido_st += st.imposto_retido
total_entradas_st += st.valor_entradas_st
if self.apuracoes_st:
saldo_total_st = total_retido_st - total_entradas_st
linhas.append(
f"|TOTAIS_GIA_ST"
f"|{_fmt(total_retido_st)}"
f"|{_fmt(total_entradas_st)}"
f"|{_fmt(saldo_total_st)}"
f"|"
)
conteudo_sem_encerr = "\n".join(linhas)
hash_md5 = hashlib.md5(conteudo_sem_encerr.encode("utf-8")).hexdigest().upper()
linhas.append(f"|ENCERRAMENTO|{len(linhas) + 1}|{hash_md5}|")
return "\n".join(linhas) + "\n"
def salvar(self, diretorio: str | Path = ".") -> Path:
caminho = Path(diretorio) / f"GIA_{self.empresa.cnpj}_{self.periodo}.aie"
caminho.parent.mkdir(parents=True, exist_ok=True)
caminho.write_text(self.gerar_aie(), encoding="utf-8")
return caminho
def relatorio_resumo(self) -> str:
venc = self.data_vencimento()
linhas = [
f"GIA-SP — {self.empresa.razao_social} — Período: {self.periodo}",
"=" * 60,
f"Total débitos: R$ {self.total_debitos:>12,.2f}",
f"Total créditos: R$ {self.total_creditos:>12,.2f}",
f"Compensações: R$ {self.apuracao.compensacoes:>12,.2f}",
"=" * 60,
]
if self.icms_a_recolher > 0:
linhas.append(f"ICMS A RECOLHER: R$ {self.icms_a_recolher:>12,.2f}")
else:
linhas.append(f"SALDO CREDOR: R$ {self.saldo_credor_proximo:>12,.2f}")
if self.apuracoes_st:
total_st = sum(s.imposto_retido for s in self.apuracoes_st)
linhas.append(f"ICMS-ST retido: R$ {total_st:>12,.2f}")
linhas.append(f"Vencimento: {venc.strftime('%d/%m/%Y')}")
return "\n".join(linhas)