|
|
| 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") |
| estorno_credito: Decimal = Decimal("0") |
| outros_debitos: Decimal = Decimal("0") |
| |
| creditos_entradas: Decimal = Decimal("0") |
| creditos_outros: Decimal = Decimal("0") |
| estorno_debito: Decimal = Decimal("0") |
| 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 |
| base_calculo: Decimal |
| aliquota: Decimal |
| imposto_retido: Decimal |
| valor_entradas_st: Decimal = Decimal("0") |
|
|
|
|
| class GeradorGIA: |
| |
|
|
| VERSAO_LAYOUT = "013" |
|
|
| def __init__( |
| self, |
| empresa: Empresa, |
| periodo: str, |
| 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 |
|
|
| |
| |
| |
|
|
| @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) |
|
|
| |
| |
| |
|
|
| 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) |
|
|