UNPKG

saldo

Version:

Portuguese salary calculator library

128 lines (127 loc) 5.85 kB
import { RetentionPathsSchema } from "../config/schemas"; import { taxTablesData } from "../data/retention-tax-tables-data"; // Import the pre-loaded JSON data class TaxBracket { signal; limit; max_marginal_rate; deduction; var1_deduction; var2_deduction; dependent_aditional_deduction; effective_mensal_rate; constructor(data) { this.signal = data.signal; this.limit = data.limit; this.max_marginal_rate = data.max_marginal_rate; this.deduction = data.deduction; this.var1_deduction = data.var1_deduction; this.var2_deduction = data.var2_deduction; this.dependent_aditional_deduction = data.dependent_aditional_deduction; this.effective_mensal_rate = data.effective_mensal_rate; } calculate_deductible(salary) { // In Python, 0.0 is falsy. If var1_deduction or var2_deduction can be 0.0 // and that should prevent this path, this JS/TS equivalent is fine. if (this.var1_deduction && this.var2_deduction) { return this.deduction * this.var1_deduction * (this.var2_deduction - salary); } return this.deduction; } calculate_tax(taxable_income, twelfths_income, number_of_dependents = 0, extra_deduction = 0) { const deduction = this.calculate_deductible(taxable_income); let rate; if (number_of_dependents >= 3) { // https://diariodarepublica.pt/dr/detalhe/despacho/9971-a-2024-885806206#:~:text=h)%20Aos%20titulares,abater%20por%20dependente%3B /* h) Aos titulares de rendimentos de trabalho dependente com três ou mais dependentes que se enquadrem nas tabelas previstas nas alíneas a) e b) do n.º 1, é aplicada uma redução de um ponto percentual à taxa marginal máxima correspondente ao escalão em que se integram, mantendo-se inalterada a parcela a abater e a parcela adicional a abater por dependente; */ rate = this.max_marginal_rate - 0.01; } else { rate = this.max_marginal_rate; } const base_tax = taxable_income * rate - deduction - extra_deduction - number_of_dependents * this.dependent_aditional_deduction; // effective rate is the actual rate that is applied to the income after the deductions // this is what we use to calculate the tax for the twelfths income const effective_rate = taxable_income !== 0 ? base_tax / taxable_income : 0; // Avoid division by zero const twelfths_tax = twelfths_income * effective_rate; const tax = base_tax + twelfths_tax; return Math.max(0, tax); } toJSON() { return { signal: this.signal, limit: this.limit, max_marginal_rate: this.max_marginal_rate, deduction: this.deduction, var1_deduction: this.var1_deduction, var2_deduction: this.var2_deduction, dependent_aditional_deduction: this.dependent_aditional_deduction, effective_mensal_rate: this.effective_mensal_rate }; } } class TaxRetentionTable { region; situation; description; tax_brackets; // Array of TaxBracket class instances dependent_disabled_addition_deduction; constructor(region, situation, description, tax_brackets_data, // Accepts raw data for brackets dependent_disabled_addition_deduction) { this.region = region; this.situation = situation; this.description = description; this.tax_brackets = tax_brackets_data.map(b => new TaxBracket(b)); this.dependent_disabled_addition_deduction = dependent_disabled_addition_deduction; } find_bracket(salary) { for (const bracket of this.tax_brackets) { if (bracket.signal === "max" && salary <= bracket.limit) { return bracket; } else if (bracket.signal === "min" && salary > bracket.limit) { return bracket; } } throw new Error(`No bracket found for salary ${salary}`); } // Renamed from load_from_file and modified to accept data directly static from_data(region, data) { if (!data) { throw new Error(`Tax table data not provided.`); } return new TaxRetentionTable(region, // Region might need to be passed or inferred differently data.situation, data.description, data.brackets, // Pass the raw bracket data data.dependent_disabled_addition_deduction); } static load(period, location, situation_code, year) { const yearStr = String(year); const retentionTablePathGenerator = new RetentionPathsSchema(period, location, situation_code, yearStr); const identifier = retentionTablePathGenerator.path; // This now returns the identifier string const tableData = taxTablesData[identifier]; // Look up in the manifest if (!tableData) { throw new Error(`Tax table not found for identifier: ${identifier}. Ensure it's in retention-tax-tables-data.ts`); } // Assuming 'location' can be used as the region directly. // The original load_from_file hardcoded "continent". // We use the 'location' parameter now, which seems more correct. return TaxRetentionTable.from_data(location, tableData); } toJSON() { return { situation: this.situation, description: this.description, brackets: this.tax_brackets.map(bracket => bracket.toJSON()), dependent_disabled_addition_deduction: this.dependent_disabled_addition_deduction }; } } // Export the classes if they are to be used by other modules export { TaxBracket, TaxRetentionTable };