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)