252 lines
9.1 KiB
Python
252 lines
9.1 KiB
Python
"""Cálculo do custo de contratação — regime Empregado CLT.
|
|
|
|
Fórmulas portadas 1:1 da planilha de referência do escritório
|
|
(`projects/planilha de custo/1972 - CUSTO EMPREGADO SALÁRIO.xlsx`), com duas
|
|
correções deliberadas e explicitamente pedidas pelo usuário:
|
|
|
|
1. A planilha original nunca soma a dedução por dependente à base do IRRF
|
|
quando usa o desconto real de INSS (só quando usa o desconto simplificado
|
|
— onde a dedução por dependente não se aplica por lei, já que os dois
|
|
métodos são mutuamente exclusivos). Aqui, quando o desconto real de INSS
|
|
é o escolhido, a dedução por dependente também é subtraída da base do
|
|
IRRF.
|
|
2. A partir de janeiro/2026, a Lei nº 15.270/2025 (art. 3º-A da Lei nº
|
|
9.250/1995) passou a reduzir o IRRF já calculado pela tabela progressiva
|
|
tradicional — ver `_reducao_lei_15270` abaixo. Essa lei não mudou a
|
|
tabela progressiva em si, só acrescenta essa redução por cima.
|
|
|
|
Os parâmetros fiscais (faixas de INSS/IRRF, dedução por dependente, os
|
|
coeficientes da Lei 15.270/2025 etc.) não são mais constantes deste módulo —
|
|
mudam todo ano, então vêm de fora via `ParametrosFiscais` (montado a partir
|
|
de `ParametroFiscalCustoContratacao`, editável pela própria tela; ver
|
|
`portal_api/models.py`). `tabelas.py` continua existindo só como o
|
|
seed/default dessa configuração.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class ParametrosFiscais:
|
|
faixas_inss: list[tuple[float, float, float]]
|
|
teto_desconto_inss: float
|
|
faixas_irrf: list[tuple[float, float, float]]
|
|
aliquota_irrf_topo: float
|
|
deduzir_irrf_topo: float
|
|
desconto_simplificado_irrf: float
|
|
deducao_por_dependente: float
|
|
reducao_lei_15270_coeficiente_a: float
|
|
reducao_lei_15270_coeficiente_b: float
|
|
reducao_lei_15270_limite: float
|
|
|
|
|
|
@dataclass
|
|
class EntradaSimulacaoEmpregado:
|
|
nome_referencia: str
|
|
empresa: str
|
|
cargo: str
|
|
salario_contratual: float
|
|
adicional_art62: float
|
|
comissao: float
|
|
dsr: float
|
|
outros_itens_remuneracao: list[tuple[str, float]] # somam à base de INSS/FGTS/13º/férias/IRRF, como salário
|
|
percentual_inss_empregador: float # fração, ex.: 0.26
|
|
percentual_fgts: float # fração, ex.: 0.08
|
|
dependentes: int
|
|
vale_transporte: float
|
|
seguro_vida: float
|
|
vale_alimentacao: float
|
|
assiduidade: float
|
|
fundo_formacao: float
|
|
outros_beneficios: list[tuple[str, float]] # custo variável do empregador, cada item some no PDF
|
|
desconto_vale_transporte: float
|
|
desconto_vale_alimentacao: float
|
|
desconto_sindicato: float
|
|
outros_descontos: list[tuple[str, float]] # descontado direto do líquido do empregado
|
|
|
|
|
|
@dataclass
|
|
class ResultadoSimulacaoEmpregado:
|
|
entrada: EntradaSimulacaoEmpregado
|
|
|
|
inss_salario: float
|
|
fgts_salario: float
|
|
|
|
decimo_terceiro_avo: float
|
|
inss_decimo_terceiro: float
|
|
fgts_decimo_terceiro: float
|
|
|
|
ferias_avo: float
|
|
ferias_um_terco: float
|
|
inss_ferias: float
|
|
fgts_ferias: float
|
|
|
|
multa_40_fgts: float
|
|
|
|
subtotal_custos_fixos: float
|
|
subtotal_custos_variaveis: float
|
|
custo_total_empresa: float
|
|
|
|
base_inss_empregado: float
|
|
aliquota_inss_empregado: float
|
|
desconto_inss_empregado: float
|
|
|
|
base_irrf: float
|
|
aliquota_irrf: float
|
|
desconto_irrf_tabela: float
|
|
reducao_lei_15270: float
|
|
desconto_irrf: float
|
|
|
|
liquido_empregado: float
|
|
|
|
|
|
def _desconto_inss(base: float, parametros: ParametrosFiscais) -> tuple[float, float]:
|
|
"""(alíquota, desconto) do INSS do empregado — progressivo, com teto fixo."""
|
|
for limite, aliquota, deduzir in parametros.faixas_inss:
|
|
if base <= limite:
|
|
return aliquota, (base * aliquota) - deduzir
|
|
return 0.0, parametros.teto_desconto_inss
|
|
|
|
|
|
def _desconto_irrf_tabela(base: float, parametros: ParametrosFiscais) -> tuple[float, float]:
|
|
"""(alíquota, desconto) do IRRF pela tabela progressiva tradicional;
|
|
desconto zerado se <= R$10 (regra de dispensa de retenção pra valores
|
|
muito baixos, já presente na planilha original)."""
|
|
aliquota, deduzir = parametros.aliquota_irrf_topo, parametros.deduzir_irrf_topo
|
|
for limite, faixa_aliquota, faixa_deduzir in parametros.faixas_irrf:
|
|
if base <= limite:
|
|
aliquota, deduzir = faixa_aliquota, faixa_deduzir
|
|
break
|
|
desconto_bruto = (base * aliquota) - deduzir
|
|
return aliquota, desconto_bruto if desconto_bruto > 10 else 0.0
|
|
|
|
|
|
def _reducao_lei_15270(rendimento_bruto_mensal: float, parametros: ParametrosFiscais) -> float:
|
|
"""Redução adicional de IRRF da Lei nº 15.270/2025 — isenção total até
|
|
R$5.000,00 de rendimento bruto mensal, decrescente até zerar em
|
|
`reducao_lei_15270_limite` (R$7.350,00 na regra vigente). Aplicada sobre
|
|
o rendimento bruto (antes do INSS), não sobre a base já líquida de INSS."""
|
|
if rendimento_bruto_mensal >= parametros.reducao_lei_15270_limite:
|
|
return 0.0
|
|
reducao = parametros.reducao_lei_15270_coeficiente_a - (
|
|
parametros.reducao_lei_15270_coeficiente_b * rendimento_bruto_mensal
|
|
)
|
|
return max(0.0, reducao)
|
|
|
|
|
|
def calcula_custo_empregado(
|
|
entrada: EntradaSimulacaoEmpregado, parametros: ParametrosFiscais
|
|
) -> ResultadoSimulacaoEmpregado:
|
|
perc_inss = entrada.percentual_inss_empregador
|
|
perc_fgts = entrada.percentual_fgts
|
|
|
|
base_remuneracao = (
|
|
entrada.salario_contratual
|
|
+ entrada.adicional_art62
|
|
+ entrada.comissao
|
|
+ entrada.dsr
|
|
+ sum(valor for _, valor in entrada.outros_itens_remuneracao)
|
|
)
|
|
|
|
inss_salario = base_remuneracao * perc_inss
|
|
fgts_salario = base_remuneracao * perc_fgts
|
|
|
|
decimo_terceiro_avo = base_remuneracao / 12
|
|
inss_decimo_terceiro = decimo_terceiro_avo * perc_inss
|
|
fgts_decimo_terceiro = decimo_terceiro_avo * perc_fgts
|
|
|
|
ferias_avo = base_remuneracao / 12
|
|
ferias_um_terco = ferias_avo / 3
|
|
inss_ferias = (ferias_avo + ferias_um_terco) * perc_inss
|
|
fgts_ferias = (ferias_avo + ferias_um_terco) * perc_fgts
|
|
|
|
multa_40_fgts = (fgts_salario + fgts_ferias + fgts_decimo_terceiro) * 0.4
|
|
|
|
subtotal_custos_fixos = (
|
|
base_remuneracao
|
|
+ inss_salario
|
|
+ fgts_salario
|
|
+ decimo_terceiro_avo
|
|
+ inss_decimo_terceiro
|
|
+ fgts_decimo_terceiro
|
|
+ ferias_avo
|
|
+ ferias_um_terco
|
|
+ inss_ferias
|
|
+ fgts_ferias
|
|
+ multa_40_fgts
|
|
)
|
|
|
|
subtotal_custos_variaveis = (
|
|
entrada.vale_transporte
|
|
+ entrada.seguro_vida
|
|
+ entrada.vale_alimentacao
|
|
+ entrada.assiduidade
|
|
+ entrada.fundo_formacao
|
|
+ sum(valor for _, valor in entrada.outros_beneficios)
|
|
)
|
|
|
|
custo_total_empresa = subtotal_custos_fixos + subtotal_custos_variaveis
|
|
|
|
base_inss_empregado = base_remuneracao
|
|
aliquota_inss_empregado, desconto_inss_empregado = _desconto_inss(base_inss_empregado, parametros)
|
|
|
|
if desconto_inss_empregado >= parametros.desconto_simplificado_irrf:
|
|
base_irrf = (
|
|
base_inss_empregado
|
|
- desconto_inss_empregado
|
|
- (entrada.dependentes * parametros.deducao_por_dependente)
|
|
)
|
|
else:
|
|
base_irrf = base_inss_empregado - parametros.desconto_simplificado_irrf
|
|
|
|
aliquota_irrf, desconto_irrf_tabela = _desconto_irrf_tabela(base_irrf, parametros)
|
|
reducao_lei_15270 = _reducao_lei_15270(base_remuneracao, parametros)
|
|
desconto_irrf = max(0.0, desconto_irrf_tabela - reducao_lei_15270)
|
|
|
|
total_descontos_diversos = (
|
|
entrada.desconto_vale_transporte
|
|
+ entrada.desconto_vale_alimentacao
|
|
+ entrada.desconto_sindicato
|
|
+ sum(valor for _, valor in entrada.outros_descontos)
|
|
)
|
|
liquido_empregado = base_remuneracao - desconto_inss_empregado - desconto_irrf - total_descontos_diversos
|
|
|
|
return ResultadoSimulacaoEmpregado(
|
|
entrada=entrada,
|
|
inss_salario=inss_salario,
|
|
fgts_salario=fgts_salario,
|
|
decimo_terceiro_avo=decimo_terceiro_avo,
|
|
inss_decimo_terceiro=inss_decimo_terceiro,
|
|
fgts_decimo_terceiro=fgts_decimo_terceiro,
|
|
ferias_avo=ferias_avo,
|
|
ferias_um_terco=ferias_um_terco,
|
|
inss_ferias=inss_ferias,
|
|
fgts_ferias=fgts_ferias,
|
|
multa_40_fgts=multa_40_fgts,
|
|
subtotal_custos_fixos=subtotal_custos_fixos,
|
|
subtotal_custos_variaveis=subtotal_custos_variaveis,
|
|
custo_total_empresa=custo_total_empresa,
|
|
base_inss_empregado=base_inss_empregado,
|
|
aliquota_inss_empregado=aliquota_inss_empregado,
|
|
desconto_inss_empregado=desconto_inss_empregado,
|
|
base_irrf=base_irrf,
|
|
aliquota_irrf=aliquota_irrf,
|
|
desconto_irrf_tabela=desconto_irrf_tabela,
|
|
reducao_lei_15270=reducao_lei_15270,
|
|
desconto_irrf=desconto_irrf,
|
|
liquido_empregado=liquido_empregado,
|
|
)
|
|
|
|
|
|
def formata_moeda(valor: float) -> str:
|
|
"""8500.0 -> 'R$ 8.500,00'; -12.3 -> '-R$ 12,30'."""
|
|
negativo = valor < 0
|
|
inteiro, decimal = f"{abs(valor):,.2f}".split(".")
|
|
texto = f"R$ {inteiro.replace(',', '.')},{decimal}"
|
|
return f"-{texto}" if negativo else texto
|
|
|
|
|
|
def formata_percentual(fracao: float) -> str:
|
|
"""0.26 -> '26,00%'."""
|
|
return f"{fracao * 100:.2f}".replace(".", ",") + "%"
|