from __future__ import annotations from dataclasses import dataclass from datetime import date from decimal import Decimal from pathlib import Path from typing import Optional from src.calculators.pis_cofins import ( CreditoPisCofins, ReceitaBruta, ResultadoPisCofins, calcular_pis_cofins, ) from src.fiscal.entities import Empresa, NotaFiscal, PeriodoApuracao, RegimeTributario from src.generators.sped_writer import ArquivoSPED, criar_registro VERSAO_LEIAUTE = "006" @dataclass class ConfigEFDContrib: versao: str = VERSAO_LEIAUTE finalidade: str = "0" ind_situacao_especial: str = "" class GeradorEFDContribuicoes: def __init__( self, periodo: PeriodoApuracao, creditos: Optional[list[CreditoPisCofins]] = None, cfg: Optional[ConfigEFDContrib] = None, ): self.periodo = periodo self.creditos = creditos or [] self.cfg = cfg or ConfigEFDContrib() self._arquivo = ArquivoSPED( f"EFD_CONTRIB_{periodo.empresa.cnpj}_{periodo.data_inicio.strftime('%Y%m')}.txt" ) def _bloco_0(self) -> None: emp = self.periodo.empresa end = emp.endereco self._arquivo.adicionar_linha_raw(criar_registro( "0000", self.cfg.versao, self.cfg.finalidade, self.periodo.data_inicio, self.periodo.data_fim, emp.razao_social, emp.cnpj, emp.cnpj[:8], # cod_atividade (raiz CNPJ simplificado) emp.ie, emp.im, emp.suframa, self.cfg.ind_situacao_especial, emp.regime_tributario.value, emp.endereco.uf.value, )) self._arquivo.adicionar_linha_raw(criar_registro("0001", "0")) ind_metodo_apropriacao = "1" # 1=Método de Apropriação Direta if emp.regime_tributario == RegimeTributario.LUCRO_REAL: cod_inc_trib = "1" # Lucro Real elif emp.regime_tributario == RegimeTributario.LUCRO_PRESUMIDO: cod_inc_trib = "2" # Lucro Presumido else: cod_inc_trib = "3" # Imunes/Isentas self._arquivo.adicionar_linha_raw(criar_registro( "0110", cod_inc_trib, ind_metodo_apropriacao, "1", # cod_cred_pis_cofins_nao_aprop )) self._arquivo.adicionar_linha_raw(criar_registro( "0140", emp.cnpj, emp.nome_fantasia or emp.razao_social, end.uf.value, emp.ie, end.cod_municipio, emp.im, emp.suframa, )) self._arquivo.adicionar_linha_raw(criar_registro( "0990", self._arquivo.total_linhas() + 1 )) def _bloco_c(self) -> None: self._arquivo.adicionar_linha_raw(criar_registro("C001", "0")) for nf in self.periodo.notas_saida: tipo_oper = "1" # Saída self._arquivo.adicionar_linha_raw(criar_registro( "C100", tipo_oper, "0", # ind_emit (0=emissão própria) nf.destinatario.cnpj, nf.modelo, nf.serie, nf.numero, nf.chave_acesso, nf.data_emissao, nf.data_saida_entrada, nf.valor_produtos, Decimal("0"), # vl_desc nf.valor_ipi, nf.valor_total, )) for item in nf.itens: self._arquivo.adicionar_linha_raw(criar_registro( "C170", item.numero_item, item.produto.codigo, item.produto.descricao, item.quantidade, item.produto.unidade, item.valor_unitario, item.desconto, item.cfop, item.produto.cst_pis, Decimal("0"), # vl_bc_pis Decimal("0"), # aliq_pis Decimal("0"), # quant_bc_pis Decimal("0"), # aliq_pis_quant Decimal("0"), # vl_pis item.produto.cst_cofins, Decimal("0"), # vl_bc_cofins Decimal("0"), # aliq_cofins Decimal("0"), # quant_bc_cofins Decimal("0"), # aliq_cofins_quant Decimal("0"), # vl_cofins "", # cod_cta )) self._arquivo.adicionar_linha_raw(criar_registro( "C990", self._arquivo.total_linhas() )) def _bloco_m(self, resultado: ResultadoPisCofins) -> None: self._arquivo.adicionar_linha_raw(criar_registro("M001", "0")) self._arquivo.adicionar_linha_raw(criar_registro( "M100", "01", # cod_cred_aprop "1", # ind_cred_ori (1=apurado no período) resultado.base_pis, # vl_bc_pis resultado.aliq_pis, # aliq_pis_aplic Decimal("0"), # quant_bc_pis Decimal("0"), # aliq_pis_quant resultado.creditos_pis, # vl_cred Decimal("0"), # vl_ajustes_a_cred resultado.creditos_pis, # vl_cred_dif Decimal("0"), # vl_cred_disp resultado.creditos_pis, # vl_cred_desc_pa_ant Decimal("0"), # vl_cred_per_pa_ant Decimal("0"), # vl_cred_dcomp_pa_ant resultado.creditos_pis, # vl_saldo_cred_retornar )) self._arquivo.adicionar_linha_raw(criar_registro( "M200", resultado.valor_pis_debito, # vl_tot_cont_nc_per Decimal("0"), # vl_tot_cred_desc Decimal("0"), # vl_tot_cred_desc_ant resultado.creditos_pis, # vl_tot_cred_desc_per resultado.pis_a_recolher, # vl_tot_cont_nc_dev Decimal("0"), # vl_ret_nc resultado.pis_a_recolher, # vl_out_ded_nc resultado.pis_a_recolher, # vl_cont_nc_rec Decimal("0"), # vl_tot_cont_cum_per Decimal("0"), # vl_ret_cum Decimal("0"), # vl_out_ded_cum Decimal("0"), # vl_cont_cum_rec resultado.pis_a_recolher, # vl_tot_cont_rec )) self._arquivo.adicionar_linha_raw(criar_registro( "M600", resultado.valor_cofins_debito, # vl_tot_cont_nc_per Decimal("0"), # vl_tot_cred_desc Decimal("0"), # vl_tot_cred_desc_ant resultado.creditos_cofins, # vl_tot_cred_desc_per resultado.cofins_a_recolher, # vl_tot_cont_nc_dev Decimal("0"), # vl_ret_nc resultado.cofins_a_recolher, # vl_out_ded_nc resultado.cofins_a_recolher, # vl_cont_nc_rec Decimal("0"), # vl_tot_cont_cum_per Decimal("0"), # vl_ret_cum Decimal("0"), # vl_out_ded_cum Decimal("0"), # vl_cont_cum_rec resultado.cofins_a_recolher, # vl_tot_cont_rec )) self._arquivo.adicionar_linha_raw(criar_registro( "M990", self._arquivo.total_linhas() )) def _bloco_1(self) -> None: self._arquivo.adicionar_linha_raw(criar_registro("1001", "1")) self._arquivo.adicionar_linha_raw(criar_registro( "1990", 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: receitas = [ ReceitaBruta( descricao=f"Vendas NF {nf.numero}", valor=nf.valor_produtos, cst_pis="01", cst_cofins="01", ) for nf in self.periodo.notas_saida ] resultado = calcular_pis_cofins( receitas=receitas, regime=self.periodo.empresa.regime_tributario, creditos=self.creditos, ) self._bloco_0() self._bloco_c() self._bloco_m(resultado) self._bloco_1() self._bloco_9() return self._arquivo.salvar(diretorio)