aboutsummaryrefslogtreecommitdiff
path: root/api/src/utils/tax-calculator.ts
blob: 36ffc7e9f142fa6c51a5aaa1e115d0e812227206 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
 * Calculadora de impostos brasileiros (INSS + IRRF).
 * Tabelas vigentes: 2025.
 *
 * IMPORTANTE: As alíquotas e faixas mudam anualmente por decreto.
 * Verifique a legislação vigente a cada ano fiscal.
 *
 * Fonte: Portaria MPS / IN RFB 2.141/2023 e atualizações.
 */

// ---------------------------------------------------------------------------
// INSS — Tabela Progressiva 2025
// ---------------------------------------------------------------------------
const INSS_TABLE = [
  { upTo: 1_518.0, rate: 0.075 },
  { upTo: 2_793.88, rate: 0.09 },
  { upTo: 4_190.83, rate: 0.12 },
  { upTo: 8_157.41, rate: 0.14 },
] as const;

const INSS_TETO = 908.86; // Valor máximo de desconto do INSS

/**
 * Calcula o desconto do INSS com a tabela progressiva.
 * Cada faixa incide apenas sobre a parte do salário dentro dela.
 */
export function calcularINSS(salarioBruto: number): number {
  if (salarioBruto <= 0) return 0;
  if (salarioBruto > INSS_TABLE[INSS_TABLE.length - 1].upTo) return INSS_TETO;

  let inss = 0;
  let salarioRestante = salarioBruto;
  let limiteAnterior = 0;

  for (const faixa of INSS_TABLE) {
    if (salarioRestante <= 0) break;

    const baseNaFaixa = Math.min(salarioBruto, faixa.upTo) - limiteAnterior;
    inss += baseNaFaixa * faixa.rate;

    salarioRestante -= baseNaFaixa;
    limiteAnterior = faixa.upTo;
  }

  return parseFloat(inss.toFixed(2));
}

// ---------------------------------------------------------------------------
// IRRF — Tabela Progressiva 2025
// ---------------------------------------------------------------------------
interface FaixaIRRF {
  upTo: number | null; // null = sem limite superior
  rate: number;
  deduction: number;
}

const IRRF_TABLE: FaixaIRRF[] = [
  { upTo: 2_259.2, rate: 0, deduction: 0 },
  { upTo: 2_826.65, rate: 0.075, deduction: 169.44 },
  { upTo: 3_751.05, rate: 0.15, deduction: 381.44 },
  { upTo: 4_664.68, rate: 0.225, deduction: 662.77 },
  { upTo: null, rate: 0.275, deduction: 896.0 },
];

/**
 * Calcula o desconto do IRRF.
 * Base de cálculo: Salário Bruto − INSS (sem considerar dependentes).
 */
export function calcularIRRF(salarioBruto: number, inss: number): number {
  const baseCalculo = salarioBruto - inss;
  if (baseCalculo <= 0) return 0;

  const faixa = IRRF_TABLE.find(
    (f) => f.upTo === null || baseCalculo <= f.upTo,
  )!;

  const irrf = baseCalculo * faixa.rate - faixa.deduction;
  return parseFloat(Math.max(0, irrf).toFixed(2));
}

// ---------------------------------------------------------------------------
// Resultado consolidado
// ---------------------------------------------------------------------------
export interface ResultadoCalculo {
  salarioBruto: number;
  descontoINSS: number;
  descontoIRRF: number;
  totalDescontos: number;
  salarioLiquidoCalculado: number;
}

/**
 * Calcula o salário líquido completo a partir do salário bruto.
 * Retorna um objeto com o detalhamento de cada desconto.
 */
export function calcularSalarioLiquido(salarioBruto: number): ResultadoCalculo {
  const descontoINSS = calcularINSS(salarioBruto);
  const descontoIRRF = calcularIRRF(salarioBruto, descontoINSS);
  const totalDescontos = parseFloat((descontoINSS + descontoIRRF).toFixed(2));
  const salarioLiquidoCalculado = parseFloat(
    (salarioBruto - totalDescontos).toFixed(2),
  );

  return {
    salarioBruto,
    descontoINSS,
    descontoIRRF,
    totalDescontos,
    salarioLiquidoCalculado,
  };
}