"""Calculadora de ICMS com suporte a ST, DIFAL e FCP.""" from __future__ import annotations from dataclasses import dataclass, field from decimal import Decimal from typing import Optional from src.calculators import ResultadoCalculo, arred from src.fiscal.constants import ALIQ_ICMS_INTERNA, aliq_icms_interestadual @dataclass class ParametrosICMS: valor_mercadoria: Decimal aliquota: Decimal cst: str = "000" uf_origem: str = "SP" uf_destino: str = "SP" reducao_base: Decimal = Decimal("0") frete: Decimal = Decimal("0") seguro: Decimal = Decimal("0") outras_despesas: Decimal = Decimal("0") desconto: Decimal = Decimal("0") # Substituição Tributária aliq_st: Optional[Decimal] = None mva: Optional[Decimal] = None base_st_retido: Decimal = Decimal("0") # FCP (Fundo de Combate à Pobreza) aliq_fcp: Decimal = Decimal("0") aliq_fcp_st: Decimal = Decimal("0") # DIFAL (Diferencial de Alíquota - EMENDA 87) calcular_difal: bool = False consumidor_final: bool = False @dataclass class ResultadoICMS: base_calculo: Decimal = Decimal("0") aliquota: Decimal = Decimal("0") valor_icms: Decimal = Decimal("0") # ST base_st: Decimal = Decimal("0") aliq_st: Decimal = Decimal("0") valor_st: Decimal = Decimal("0") # FCP base_fcp: Decimal = Decimal("0") valor_fcp: Decimal = Decimal("0") valor_fcp_st: Decimal = Decimal("0") # DIFAL valor_difal_origem: Decimal = Decimal("0") valor_difal_destino: Decimal = Decimal("0") # Total valor_total_icms: Decimal = Decimal("0") def calcular_icms(p: ParametrosICMS) -> ResultadoICMS: r = ResultadoICMS() if p.cst in {"040", "041", "050", "051", "060"}: return r base_bruta = ( p.valor_mercadoria + p.frete + p.seguro + p.outras_despesas - p.desconto ) if p.cst in {"020", "070"}: r.base_calculo = arred(base_bruta * (1 - p.reducao_base / 100)) else: r.base_calculo = arred(base_bruta) r.aliquota = p.aliquota r.valor_icms = arred(r.base_calculo * p.aliquota / 100) # FCP if p.aliq_fcp > 0: r.base_fcp = r.base_calculo r.valor_fcp = arred(r.base_fcp * p.aliq_fcp / 100) # Substituição Tributária if p.cst in {"010", "030", "070"} and p.aliq_st is not None and p.mva is not None: aliq_interna_dest = ALIQ_ICMS_INTERNA.get(p.uf_destino, Decimal("18")) r.base_st = arred(r.base_calculo * (1 + p.mva / 100)) r.aliq_st = aliq_interna_dest icms_proprio_dest = arred(r.base_st * aliq_interna_dest / 100) r.valor_st = max(Decimal("0"), arred(icms_proprio_dest - r.valor_icms)) if p.aliq_fcp_st > 0: r.valor_fcp_st = arred(r.base_st * p.aliq_fcp_st / 100) # DIFAL (EC 87/2015) para operações interestaduais para consumidor final if p.calcular_difal and p.consumidor_final and p.uf_origem != p.uf_destino: aliq_interestadual = aliq_icms_interestadual(p.uf_origem, p.uf_destino) aliq_interna_dest = ALIQ_ICMS_INTERNA.get(p.uf_destino, Decimal("18")) difal = arred(r.base_calculo * (aliq_interna_dest - aliq_interestadual) / 100) # Partilha DIFAL: 2019+ => 100% destino r.valor_difal_destino = arred(difal * Decimal("0.60")) r.valor_difal_origem = arred(difal * Decimal("0.40")) r.valor_total_icms = arred( r.valor_icms + r.valor_st + r.valor_fcp + r.valor_fcp_st + r.valor_difal_destino + r.valor_difal_origem ) return r def calcular_icms_credito_entrada( valor_nf: Decimal, aliquota: Decimal, percentual_aproveitamento: Decimal = Decimal("100"), ) -> Decimal: credito_bruto = arred(valor_nf * aliquota / 100) return arred(credito_bruto * percentual_aproveitamento / 100) def calcular_icms_apuracao( debitos: list[Decimal], creditos: list[Decimal], saldo_anterior: Decimal = Decimal("0"), ) -> ResultadoCalculo: total_debitos = arred(sum(debitos)) total_creditos = arred(sum(creditos) + saldo_anterior) saldo = total_creditos - total_debitos valor_a_recolher = max(Decimal("0"), arred(total_debitos - total_creditos)) saldo_credor = max(Decimal("0"), arred(total_creditos - total_debitos)) return ResultadoCalculo( base_calculo=total_debitos, aliquota=Decimal("0"), valor_tributo=total_debitos, deducoes=Decimal("0"), creditos=total_creditos, valor_a_recolher=valor_a_recolher, detalhes={ "total_debitos": total_debitos, "total_creditos": total_creditos, "saldo_devedor": valor_a_recolher, "saldo_credor": saldo_credor, }, )