from __future__ import annotations from dataclasses import dataclass, field from datetime import date from decimal import Decimal from pathlib import Path from typing import Optional from src.calculators.irpj_csll import ( ResultadoCSLL, ResultadoIRPJ, calcular_csll_lucro_presumido, calcular_csll_lucro_real, calcular_irpj_lucro_presumido, calcular_irpj_lucro_real, ) from src.fiscal.entities import Empresa, PeriodoApuracao, RegimeTributario from src.generators.sped_writer import ArquivoSPED, criar_registro VERSAO_LEIAUTE = "009" @dataclass class DadosLucroReal: lucro_contabil: Decimal adicoes: Decimal = Decimal("0") exclusoes: Decimal = Decimal("0") compensacoes_prejuizo: Decimal = Decimal("0") incentivos_fiscais: Decimal = Decimal("0") csll_indedutivel: Decimal = Decimal("0") multas_indedutíveis: Decimal = Decimal("0") provisoes_indedutíveis: Decimal = Decimal("0") @dataclass class DadosLucroPresumido: receita_venda_mercadorias: Decimal = Decimal("0") receita_prestacao_servicos: Decimal = Decimal("0") receita_outras_atividades: Decimal = Decimal("0") ganhos_capital: Decimal = Decimal("0") rendimentos_aplicacoes: Decimal = Decimal("0") outras_receitas: Decimal = Decimal("0") atividade_principal: str = "venda_mercadorias" @dataclass class ConfigECF: versao: str = VERSAO_LEIAUTE finalidade: str = "0" # 0=Original, 1=Retificadora situacao_especial: str = "" ano_referencia: int = field(default_factory=lambda: date.today().year - 1) class GeradorECF: """ Gera o arquivo ECF conforme leiaute da Receita Federal. Obrigação anual: Lucro Real, Lucro Presumido e Lucro Arbitrado. Prazo: último dia útil de julho do ano seguinte. """ def __init__( self, empresa: Empresa, ano: int, dados_lr: Optional[DadosLucroReal] = None, dados_lp: Optional[DadosLucroPresumido] = None, cfg: Optional[ConfigECF] = None, contador: Optional[object] = None, ): self.empresa = empresa self.ano = ano self.dados_lr = dados_lr self.dados_lp = dados_lp self.cfg = cfg or ConfigECF(ano_referencia=ano) self.contador = contador self._arquivo = ArquivoSPED(f"ECF_{empresa.cnpj}_{ano}.txt") def _bloco_0(self) -> None: emp = self.empresa end = emp.endereco self._arquivo.adicionar_linha_raw(criar_registro( "0000", self.cfg.versao, self.cfg.finalidade, emp.cnpj, emp.regime_tributario.value, emp.razao_social, end.uf.value, emp.ie, end.cod_municipio, self.cfg.situacao_especial, "", # nr_rec_anterior (retificadora) date(self.ano, 1, 1), date(self.ano, 12, 31), "N", # ind_grandes_contribuintes self.cfg.finalidade, "1", # ind_pub_soc_emp )) self._arquivo.adicionar_linha_raw(criar_registro("0001", "0")) # 0010 - Parâmetros da Tributação ind_sit_ini = "0" # 0=Regular if emp.regime_tributario == RegimeTributario.LUCRO_REAL: forma_trib = "3" # 3=Lucro Real Anual com Estimativas elif emp.regime_tributario == RegimeTributario.LUCRO_PRESUMIDO: forma_trib = "6" # 6=Lucro Presumido else: forma_trib = "6" self._arquivo.adicionar_linha_raw(criar_registro( "0010", ind_sit_ini, forma_trib, "0", # ind_calculo_csll "N", # ind_part_consol "N", # ind_part_spe "N", # ind_rep_eco "N", # ind_ativ_imob "0", # ind_reidi "N", # ind_outras_deducoes "N", # ind_prev_privada "N", # ind_pat "N", # ind_simples_concomitante )) # 0020 - Parâmetros complementares self._arquivo.adicionar_linha_raw(criar_registro( "0020", "0", # ind_calc_pl_pj "0", # ind_opc_irpj_ativ_rural "0", # ind_cnpj_prep "0", # ind_lucro_infl "0", # ind_ativ_preponderante "0", # ind_sat "1", # ind_tipo_entidade )) if self.contador: c = self.contador self._arquivo.adicionar_linha_raw(criar_registro( "0930", c.nome, c.cpf, c.crc, c.cnpj_escritorio if hasattr(c, "cnpj_escritorio") else "", c.contato.email if hasattr(c, "contato") else "", c.contato.telefone if hasattr(c, "contato") else "", )) self._arquivo.adicionar_linha_raw(criar_registro( "0990", self._arquivo.total_linhas() + 1 )) def _bloco_c(self) -> None: """Bloco C — Identificação e Caracterização da PJ.""" self._arquivo.adicionar_linha_raw(criar_registro("C001", "0")) emp = self.empresa end = emp.endereco # C010 - Identificação self._arquivo.adicionar_linha_raw(criar_registro( "C010", emp.cnpj, emp.razao_social, emp.nome_fantasia or emp.razao_social, end.logradouro, end.numero, end.complemento, end.bairro, end.cep, end.municipio, end.uf.value, emp.contato.telefone, emp.contato.email, emp.ie, end.cod_municipio, )) # C050 - Atividade Econômica self._arquivo.adicionar_linha_raw(criar_registro( "C050", "6201-5/00", # CNAE principal (exemplo: desenvolvimento software) "1", # ind_ativ_principal date(self.ano, 1, 1), date(self.ano, 12, 31), )) self._arquivo.adicionar_linha_raw(criar_registro( "C990", self._arquivo.total_linhas() )) def _bloco_e(self) -> None: """Bloco E — IRPJ e CSLL por tipo de receita.""" self._arquivo.adicionar_linha_raw(criar_registro("E001", "0")) # E010 - Receitas por atividade receita_total = Decimal("0") if self.dados_lp: receita_total = ( self.dados_lp.receita_venda_mercadorias + self.dados_lp.receita_prestacao_servicos + self.dados_lp.receita_outras_atividades ) elif self.dados_lr: pass # No Lucro Real, a receita vem da contabilidade self._arquivo.adicionar_linha_raw(criar_registro( "E010", receita_total, # vl_rec_brt_ativ Decimal("0"), # vl_rec_brt_ativ_nao_fin Decimal("0"), # vl_dev_trib Decimal("0"), # vl_dev_nao_trib Decimal("0"), # vl_descto_trib Decimal("0"), # vl_descto_nao_trib receita_total, # vl_rec_liq_ativ Decimal("0"), # vl_rec_liq_ativ_nao_fin )) self._arquivo.adicionar_linha_raw(criar_registro( "E990", self._arquivo.total_linhas() )) def _bloco_l(self, irpj: ResultadoIRPJ, csll: ResultadoCSLL) -> None: """Bloco L — Lucro Real (LALUR/LACS).""" self._arquivo.adicionar_linha_raw(criar_registro("L001", "0")) if not self.dados_lr: self._arquivo.adicionar_linha_raw(criar_registro("L990", "2")) return d = self.dados_lr # L010 - Identificação do Período self._arquivo.adicionar_linha_raw(criar_registro( "L010", date(self.ano, 1, 1), date(self.ano, 12, 31), "0", # ind_periodo )) # L020 - IRPJ — Lucro Real (LALUR) self._arquivo.adicionar_linha_raw(criar_registro( "L020", d.lucro_contabil, # VL_REC_LIQ d.lucro_contabil, # VL_LUC_LIQ_ANT_CSLL d.csll_indedutivel, # VL_CSLL_IND d.lucro_contabil + d.csll_indedutivel, # VL_LUC_LIQ d.adicoes, # VL_TOTAL_ADICOES d.exclusoes, # VL_TOTAL_EXCLUSOES irpj.lucro_real_ajustado, # VL_LUC_REAL_ANT_COMP irpj.lucro_real_ajustado - irpj.base_calculo, # VL_COMP_PREJ_PERIODO irpj.base_calculo, # VL_LUC_REAL )) # L030 - CSLL — Lucro Real (LACS) self._arquivo.adicionar_linha_raw(criar_registro( "L030", d.lucro_contabil, d.adicoes, d.exclusoes, csll.base_calculo, csll.base_calculo, csll.base_calculo, )) # L100 - Adições ao Lucro Líquido if d.multas_indedutíveis > 0: self._arquivo.adicionar_linha_raw(criar_registro( "L100", "054", "Multas Indedutíveis", d.multas_indedutíveis )) if d.provisoes_indedutíveis > 0: self._arquivo.adicionar_linha_raw(criar_registro( "L100", "012", "Provisões Indedutíveis", d.provisoes_indedutíveis )) self._arquivo.adicionar_linha_raw(criar_registro( "L990", self._arquivo.total_linhas() )) def _bloco_n(self, irpj: ResultadoIRPJ, csll: ResultadoCSLL) -> None: """Bloco N — Cálculo do IRPJ e da CSLL.""" self._arquivo.adicionar_linha_raw(criar_registro("N001", "0")) # N010 — IRPJ self._arquivo.adicionar_linha_raw(criar_registro( "N010", date(self.ano, 1, 1), date(self.ano, 12, 31), )) self._arquivo.adicionar_linha_raw(criar_registro( "N620", irpj.base_calculo, # VL_BC_IRPJ irpj.valor_base, # VL_IRPJ_ALIQ_15 irpj.base_adicional, # VL_BC_ADIC_IRPJ irpj.valor_adicional, # VL_ADIC_IRPJ irpj.valor_irpj_total, # VL_IRPJ_DEVIDO irpj.incentivos_fiscais, # VL_IRPJ_INCENTIVOS Decimal("0"), # VL_IRPJ_ESTIM_SUSP irpj.irpj_a_recolher, # VL_IRPJ_A_REC Decimal("0"), # VL_IRPJ_A_COMP Decimal("0"), # VL_IRPJ_A_REC_FINAL )) self._arquivo.adicionar_linha_raw(criar_registro( "N630", csll.base_calculo, # VL_BC_CSLL csll.aliquota, # ALIQ_CSLL csll.valor_csll, # VL_CSLL_DEVIDA Decimal("0"), # VL_CSLL_ESTIM_SUSP csll.csll_a_recolher, # VL_CSLL_A_REC Decimal("0"), # VL_CSLL_A_COMP csll.csll_a_recolher, # VL_CSLL_A_REC_FINAL )) self._arquivo.adicionar_linha_raw(criar_registro( "N990", self._arquivo.total_linhas() )) def _bloco_p(self, irpj: ResultadoIRPJ, csll: ResultadoCSLL) -> None: """Bloco P — Lucro Presumido.""" self._arquivo.adicionar_linha_raw(criar_registro("P001", "0")) if not self.dados_lp: self._arquivo.adicionar_linha_raw(criar_registro("P990", "2")) return d = self.dados_lp # Cada trimestre do Lucro Presumido for trim, (di, df) in enumerate([ (date(self.ano, 1, 1), date(self.ano, 3, 31)), (date(self.ano, 4, 1), date(self.ano, 6, 30)), (date(self.ano, 7, 1), date(self.ano, 9, 30)), (date(self.ano, 10, 1), date(self.ano, 12, 31)), ], 1): # P010 - Período self._arquivo.adicionar_linha_raw(criar_registro("P010", di, df)) fator = Decimal("0.25") rec_merc = d.receita_venda_mercadorias * fator rec_serv = d.receita_prestacao_servicos * fator rec_outras = d.receita_outras_atividades * fator self._arquivo.adicionar_linha_raw(criar_registro( "P020", rec_merc, # receita mercadorias (8%) rec_serv, # receita serviços (32%) rec_outras, # outras receitas (8%) d.ganhos_capital * fator, # ganhos de capital d.rendimentos_aplicacoes * fator, # rendimentos de aplicações d.outras_receitas * fator, # outras receitas financeiras )) lp_merc = rec_merc * Decimal("0.08") lp_serv = rec_serv * Decimal("0.32") lp_outras = rec_outras * Decimal("0.08") lp_total = lp_merc + lp_serv + lp_outras + d.ganhos_capital * fator + d.rendimentos_aplicacoes * fator self._arquivo.adicionar_linha_raw(criar_registro( "P030", lp_merc, # vl_rec_liq_ativ_3 lp_serv, # vl_rec_liq_ativ_32 lp_outras, # vl_rec_liq_ativ_8 lp_total, # vl_lp_irpj lp_total, # vl_bc_irpj )) lp_csll_merc = rec_merc * Decimal("0.12") lp_csll_serv = rec_serv * Decimal("0.32") lp_csll_total = lp_csll_merc + lp_csll_serv + d.ganhos_capital * fator self._arquivo.adicionar_linha_raw(criar_registro( "P100", lp_csll_merc, lp_csll_serv, lp_csll_total, lp_csll_total, )) self._arquivo.adicionar_linha_raw(criar_registro( "P990", self._arquivo.total_linhas() )) def _bloco_y(self) -> None: """Bloco Y — Informações Gerais.""" self._arquivo.adicionar_linha_raw(criar_registro("Y001", "0")) # Y520 — Bens e Direitos no Exterior (simplificado) # Y540 — Dívidas no Exterior # Y570 — Pagamentos/Remessas ao Exterior # Y600 — Sócios/Titulares da PJ self._arquivo.adicionar_linha_raw(criar_registro( "Y600", self.empresa.cnpj, "", # CPF (quando PF) "SOCIO", # nome Decimal("100"), # percentual participação date(self.ano, 1, 1), date(self.ano, 12, 31), )) self._arquivo.adicionar_linha_raw(criar_registro( "Y990", self._arquivo.total_linhas() )) def _bloco_9(self) -> None: self._arquivo.adicionar_linha_raw(criar_registro("9001", "0")) qt = self._arquivo.total_linhas() + 3 self._arquivo.adicionar_linha_raw(criar_registro("9900", "0000", "1")) self._arquivo.adicionar_linha_raw(criar_registro("9990", qt)) self._arquivo.adicionar_linha_raw(criar_registro("9999", qt + 1)) def gerar(self, diretorio: str | Path = ".") -> Path: emp = self.empresa dados_lr = self.dados_lr dados_lp = self.dados_lp if emp.regime_tributario == RegimeTributario.LUCRO_REAL and dados_lr: irpj = calcular_irpj_lucro_real( lucro_antes_ir=dados_lr.lucro_contabil, adicoes=dados_lr.adicoes, exclusoes=dados_lr.exclusoes, compensacoes_prejuizo=dados_lr.compensacoes_prejuizo, incentivos_fiscais=dados_lr.incentivos_fiscais, ) csll = calcular_csll_lucro_real( lucro_antes_csll=dados_lr.lucro_contabil, adicoes=dados_lr.adicoes, exclusoes=dados_lr.exclusoes, ) else: d = dados_lp or DadosLucroPresumido() receita = ( d.receita_venda_mercadorias + d.receita_prestacao_servicos + d.receita_outras_atividades ) irpj = calcular_irpj_lucro_presumido( receita_bruta=receita, outras_receitas=d.ganhos_capital + d.rendimentos_aplicacoes + d.outras_receitas, atividade=d.atividade_principal, ) csll = calcular_csll_lucro_presumido( receita_bruta=receita, outras_receitas=d.ganhos_capital + d.rendimentos_aplicacoes + d.outras_receitas, atividade="comercio_industria" if d.atividade_principal == "venda_mercadorias" else "servicos_em_geral", ) self._bloco_0() self._bloco_c() self._bloco_e() if emp.regime_tributario == RegimeTributario.LUCRO_REAL: self._bloco_l(irpj, csll) else: self._arquivo.adicionar_linha_raw(criar_registro("L001", "1")) self._arquivo.adicionar_linha_raw(criar_registro("L990", "2")) self._bloco_p(irpj, csll) self._bloco_n(irpj, csll) self._bloco_y() self._bloco_9() return self._arquivo.salvar(diretorio)