|
|
| from __future__ import annotations |
|
|
| from dataclasses import dataclass, field |
| from datetime import date |
| from decimal import Decimal |
| from typing import Optional |
|
|
| from src.calculators import ResultadoCalculo, arred |
|
|
|
|
| |
| |
| |
| |
| |
|
|
| TABELA_SIMPLES: dict[str, list[tuple[Decimal, Decimal, Decimal]]] = { |
| "I": [ |
| (Decimal("180000.00"), Decimal("4.00"), Decimal("0.00")), |
| (Decimal("360000.00"), Decimal("7.30"), Decimal("5940.00")), |
| (Decimal("720000.00"), Decimal("9.50"), Decimal("13860.00")), |
| (Decimal("1800000.00"), Decimal("10.70"), Decimal("22500.00")), |
| (Decimal("3600000.00"), Decimal("14.30"), Decimal("87300.00")), |
| (Decimal("4800000.00"), Decimal("19.00"), Decimal("378000.00")), |
| ], |
| "II": [ |
| (Decimal("180000.00"), Decimal("4.50"), Decimal("0.00")), |
| (Decimal("360000.00"), Decimal("7.80"), Decimal("5940.00")), |
| (Decimal("720000.00"), Decimal("10.00"), Decimal("13860.00")), |
| (Decimal("1800000.00"), Decimal("11.20"), Decimal("22500.00")), |
| (Decimal("3600000.00"), Decimal("14.70"), Decimal("85500.00")), |
| (Decimal("4800000.00"), Decimal("30.00"), Decimal("720000.00")), |
| ], |
| "III": [ |
| (Decimal("180000.00"), Decimal("6.00"), Decimal("0.00")), |
| (Decimal("360000.00"), Decimal("11.20"), Decimal("9360.00")), |
| (Decimal("720000.00"), Decimal("13.50"), Decimal("17640.00")), |
| (Decimal("1800000.00"), Decimal("16.00"), Decimal("35640.00")), |
| (Decimal("3600000.00"), Decimal("21.00"), Decimal("125640.00")), |
| (Decimal("4800000.00"), Decimal("33.00"), Decimal("648000.00")), |
| ], |
| "IV": [ |
| (Decimal("180000.00"), Decimal("4.50"), Decimal("0.00")), |
| (Decimal("360000.00"), Decimal("9.00"), Decimal("8100.00")), |
| (Decimal("720000.00"), Decimal("10.20"), Decimal("12420.00")), |
| (Decimal("1800000.00"), Decimal("14.00"), Decimal("39780.00")), |
| (Decimal("3600000.00"), Decimal("22.00"), Decimal("183780.00")), |
| (Decimal("4800000.00"), Decimal("33.00"), Decimal("828000.00")), |
| ], |
| "V": [ |
| (Decimal("180000.00"), Decimal("15.50"), Decimal("0.00")), |
| (Decimal("360000.00"), Decimal("18.00"), Decimal("4500.00")), |
| (Decimal("720000.00"), Decimal("19.50"), Decimal("9900.00")), |
| (Decimal("1800000.00"), Decimal("20.50"), Decimal("17100.00")), |
| (Decimal("3600000.00"), Decimal("23.00"), Decimal("62100.00")), |
| (Decimal("4800000.00"), Decimal("30.50"), Decimal("540000.00")), |
| ], |
| } |
|
|
|
|
| PARTILHA_DAS: dict[str, list[dict[str, Decimal]]] = { |
| "I": [ |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.74"), "PIS": Decimal("2.76"), "CPP": Decimal("41.50"), "ICMS": Decimal("34.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.74"), "PIS": Decimal("2.76"), "CPP": Decimal("41.50"), "ICMS": Decimal("34.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.74"), "PIS": Decimal("2.76"), "CPP": Decimal("42.00"), "ICMS": Decimal("33.50")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.74"), "PIS": Decimal("2.76"), "CPP": Decimal("42.00"), "ICMS": Decimal("33.50")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.74"), "PIS": Decimal("2.76"), "CPP": Decimal("42.00"), "ICMS": Decimal("33.50")}, |
| {"IRPJ": Decimal("13.5"), "CSLL": Decimal("10.0"), "COFINS": Decimal("28.27"), "PIS": Decimal("6.13"), "CPP": Decimal("42.10"), "ICMS": Decimal("0.00")}, |
| ], |
| "II": [ |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("11.51"), "PIS": Decimal("2.49"), "CPP": Decimal("37.50"), "IPI": Decimal("7.50"), "ICMS": Decimal("32.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("11.51"), "PIS": Decimal("2.49"), "CPP": Decimal("37.50"), "IPI": Decimal("7.50"), "ICMS": Decimal("32.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("11.51"), "PIS": Decimal("2.49"), "CPP": Decimal("37.50"), "IPI": Decimal("7.50"), "ICMS": Decimal("32.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("11.51"), "PIS": Decimal("2.49"), "CPP": Decimal("37.50"), "IPI": Decimal("7.50"), "ICMS": Decimal("32.00")}, |
| {"IRPJ": Decimal("5.5"), "CSLL": Decimal("3.5"), "COFINS": Decimal("11.51"), "PIS": Decimal("2.49"), "CPP": Decimal("37.50"), "IPI": Decimal("7.50"), "ICMS": Decimal("32.00")}, |
| {"IRPJ": Decimal("8.0"), "CSLL": Decimal("7.5"), "COFINS": Decimal("20.96"), "PIS": Decimal("4.54"), "CPP": Decimal("23.50"), "IPI": Decimal("35.50"), "ICMS": Decimal("0.00")}, |
| ], |
| "III": [ |
| {"IRPJ": Decimal("4.0"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.82"), "PIS": Decimal("2.78"), "CPP": Decimal("43.40"), "ISS": Decimal("33.50")}, |
| {"IRPJ": Decimal("4.0"), "CSLL": Decimal("3.5"), "COFINS": Decimal("14.05"), "PIS": Decimal("3.05"), "CPP": Decimal("43.40"), "ISS": Decimal("32.00")}, |
| {"IRPJ": Decimal("4.0"), "CSLL": Decimal("3.5"), "COFINS": Decimal("13.64"), "PIS": Decimal("2.96"), "CPP": Decimal("43.40"), "ISS": Decimal("32.50")}, |
| {"IRPJ": Decimal("4.0"), "CSLL": Decimal("3.5"), "COFINS": Decimal("13.64"), "PIS": Decimal("2.96"), "CPP": Decimal("43.40"), "ISS": Decimal("32.50")}, |
| {"IRPJ": Decimal("4.0"), "CSLL": Decimal("3.5"), "COFINS": Decimal("12.82"), "PIS": Decimal("2.78"), "CPP": Decimal("43.40"), "ISS": Decimal("33.50")}, |
| {"IRPJ": Decimal("35.0"), "CSLL": Decimal("15.0"), "COFINS": Decimal("16.03"), "PIS": Decimal("3.47"), "CPP": Decimal("30.50"), "ISS": Decimal("0.00")}, |
| ], |
| "IV": [ |
| {"IRPJ": Decimal("18.8"), "CSLL": Decimal("15.2"), "COFINS": Decimal("17.67"), "PIS": Decimal("3.83"), "ISS": Decimal("44.50")}, |
| {"IRPJ": Decimal("19.8"), "CSLL": Decimal("15.2"), "COFINS": Decimal("20.55"), "PIS": Decimal("4.45"), "ISS": Decimal("40.00")}, |
| {"IRPJ": Decimal("20.8"), "CSLL": Decimal("15.2"), "COFINS": Decimal("19.73"), "PIS": Decimal("4.27"), "ISS": Decimal("40.00")}, |
| {"IRPJ": Decimal("17.8"), "CSLL": Decimal("19.2"), "COFINS": Decimal("18.90"), "PIS": Decimal("4.10"), "ISS": Decimal("40.00")}, |
| {"IRPJ": Decimal("18.8"), "CSLL": Decimal("19.2"), "COFINS": Decimal("18.08"), "PIS": Decimal("3.92"), "ISS": Decimal("40.00")}, |
| {"IRPJ": Decimal("53.5"), "CSLL": Decimal("21.5"), "COFINS": Decimal("20.55"), "PIS": Decimal("4.45"), "ISS": Decimal("0.00")}, |
| ], |
| } |
|
|
| LIMITE_SIMPLES = Decimal("4800000.00") |
| LIMITE_MEI = Decimal("81000.00") |
|
|
|
|
| @dataclass |
| class ResultadoSimples: |
| receita_bruta_mes: Decimal |
| receita_bruta_acumulada_12m: Decimal |
| anexo: str |
| faixa: int |
| aliq_nominal: Decimal |
| deducao: Decimal |
| aliq_efetiva: Decimal |
| valor_das: Decimal |
| partilha: dict[str, Decimal] = field(default_factory=dict) |
| periodo: str = "" |
| dentro_limite: bool = True |
|
|
|
|
| def _calcular_aliq_efetiva( |
| rec_12m: Decimal, |
| aliq_nominal: Decimal, |
| deducao: Decimal, |
| ) -> Decimal: |
| if rec_12m <= 0: |
| return Decimal("0") |
| return arred(((rec_12m * aliq_nominal / 100) - deducao) / rec_12m * 100, 6) |
|
|
|
|
| def _obter_faixa(rec_12m: Decimal, tabela: list) -> tuple[int, Decimal, Decimal]: |
| """Retorna (índice_faixa, aliq_nominal, deducao).""" |
| for i, (limite, aliq, ded) in enumerate(tabela): |
| if rec_12m <= limite: |
| return i, aliq, ded |
| |
| return len(tabela) - 1, tabela[-1][1], tabela[-1][2] |
|
|
|
|
| def calcular_das( |
| receita_bruta_mes: Decimal, |
| receita_bruta_acumulada_12m: Decimal, |
| anexo: str = "I", |
| periodo: str = "", |
| ) -> ResultadoSimples: |
| """ |
| Calcula o DAS (Documento de Arrecadação do Simples Nacional) mensal. |
| |
| Args: |
| receita_bruta_mes: Receita bruta do mês de referência |
| receita_bruta_acumulada_12m: Soma dos últimos 12 meses (ou desde o início) |
| anexo: "I", "II", "III", "IV" ou "V" |
| periodo: "YYYY-MM" |
| """ |
| dentro_limite = receita_bruta_acumulada_12m <= LIMITE_SIMPLES |
| tabela = TABELA_SIMPLES.get(anexo, TABELA_SIMPLES["I"]) |
|
|
| faixa_idx, aliq_nominal, deducao = _obter_faixa(receita_bruta_acumulada_12m, tabela) |
| aliq_efetiva = _calcular_aliq_efetiva(receita_bruta_acumulada_12m, aliq_nominal, deducao) |
| valor_das = arred(receita_bruta_mes * aliq_efetiva / 100) |
|
|
| |
| partilha_tabela = PARTILHA_DAS.get(anexo, []) |
| partilha: dict[str, Decimal] = {} |
| if partilha_tabela and faixa_idx < len(partilha_tabela): |
| perc_faixa = partilha_tabela[faixa_idx] |
| for tributo, perc in perc_faixa.items(): |
| partilha[tributo] = arred(valor_das * perc / 100) |
|
|
| return ResultadoSimples( |
| receita_bruta_mes=receita_bruta_mes, |
| receita_bruta_acumulada_12m=receita_bruta_acumulada_12m, |
| anexo=anexo, |
| faixa=faixa_idx + 1, |
| aliq_nominal=aliq_nominal, |
| deducao=deducao, |
| aliq_efetiva=aliq_efetiva, |
| valor_das=valor_das, |
| partilha=partilha, |
| periodo=periodo, |
| dentro_limite=dentro_limite, |
| ) |
|
|
|
|
| def calcular_fator_r( |
| folha_12m: Decimal, |
| rec_12m: Decimal, |
| ) -> Decimal: |
| """ |
| Fator R: relação folha de pagamento / receita bruta nos últimos 12 meses. |
| Se Fator R >= 28%, o contribuinte pode optar pelo Anexo III ao invés do V. |
| """ |
| if rec_12m <= 0: |
| return Decimal("0") |
| return arred(folha_12m / rec_12m * 100, 4) |
|
|
|
|
| def determinar_anexo_servicos( |
| fator_r: Decimal, |
| lista_servico: str, |
| ) -> str: |
| """ |
| Determina o anexo aplicável para empresas de serviços. |
| Serviços do Anexo V com Fator R >= 28% podem usar o Anexo III. |
| """ |
| |
| anexo_iv = { |
| "construcao_civil", "servicos_vigilancia", "limpeza", |
| "transporte_cargas", "cuidados_pessoais", |
| } |
| |
| anexo_iii = { |
| "agencia_viagens", "academias", "agronegocio", |
| "arquitetura_engenharia_simples", |
| } |
| |
| fator_r_atividades = { |
| "medicina", "odontologia", "psicologia", "fisioterapia", |
| "advocacia", "contabilidade", "consultoria", "ti_software", |
| "engenharia", "arquitetura", |
| } |
|
|
| if lista_servico in anexo_iv: |
| return "IV" |
| if lista_servico in anexo_iii: |
| return "III" |
| if lista_servico in fator_r_atividades: |
| return "III" if fator_r >= Decimal("28") else "V" |
| return "III" |
|
|
|
|
| @dataclass |
| class ResultadoPGDAS: |
| """Resultado do PGDAS-D mensal.""" |
| cnpj: str |
| periodo: str |
| receita_bruta_total: Decimal |
| das_total: Decimal |
| das_por_estabelecimento: list[ResultadoSimples] = field(default_factory=list) |
| data_vencimento: Optional[date] = None |
|
|
| @property |
| def resumo_partilha(self) -> dict[str, Decimal]: |
| totais: dict[str, Decimal] = {} |
| for r in self.das_por_estabelecimento: |
| for tributo, valor in r.partilha.items(): |
| totais[tributo] = totais.get(tributo, Decimal("0")) + valor |
| return totais |
|
|
|
|
| def calcular_pgdas( |
| cnpj: str, |
| periodo: str, |
| receitas_por_atividade: list[dict], |
| receita_acumulada_12m: Decimal, |
| ) -> ResultadoPGDAS: |
| """ |
| Calcula o PGDAS-D completo para um período. |
| |
| Args: |
| cnpj: CNPJ da empresa |
| periodo: "YYYY-MM" |
| receitas_por_atividade: [{"valor": ..., "anexo": "I/II/III/IV/V"}] |
| receita_acumulada_12m: Receita dos últimos 12 meses |
| """ |
| das_total = Decimal("0") |
| resultados = [] |
|
|
| for ativ in receitas_por_atividade: |
| valor = Decimal(str(ativ.get("valor", 0))) |
| anexo = ativ.get("anexo", "I") |
| resultado = calcular_das(valor, receita_acumulada_12m, anexo, periodo) |
| das_total += resultado.valor_das |
| resultados.append(resultado) |
|
|
| |
| ano, mes = map(int, periodo.split("-")) |
| mes_vcto = mes + 1 if mes < 12 else 1 |
| ano_vcto = ano if mes < 12 else ano + 1 |
| vcto = date(ano_vcto, mes_vcto, 20) |
|
|
| return ResultadoPGDAS( |
| cnpj=cnpj, |
| periodo=periodo, |
| receita_bruta_total=sum(Decimal(str(a["valor"])) for a in receitas_por_atividade), |
| das_total=arred(das_total), |
| das_por_estabelecimento=resultados, |
| data_vencimento=vcto, |
| ) |
|
|
|
|
|
|
| MEI_VALORES_FIXOS = { |
| "comercio_industria": Decimal("70.60"), |
| "servicos": Decimal("79.60"), |
| "comercio_servicos": Decimal("80.60"), |
| } |
|
|