/** * Vela Payroll Engine — Flat Rate Tax Jurisdiction * * Generic fallback for EU countries and any unsupported jurisdiction. * Uses a single flat income tax rate, with optional employer pension * and employer tax contributions. * * Usage example: * new FlatRateTax({ * flatIncomeTaxRate: 0.25, // 25% flat income tax * employerPensionRate: 0.08, // 8% employer pension * displayName: "Germany", * currency: "EUR", * }) * * This is how EU countries and other unsupported jurisdictions are * handled at launch — with a manual flat percentage override. * Built progressively based on user demand. */ import { TaxJurisdiction, DeductionLineItem, EmployerContribution, round2, } from "../types"; export interface FlatRateTaxOptions { /** Flat income tax rate as decimal (e.g. 0.25 for 25%). */ flatIncomeTaxRate: number; /** Optional employer pension/contribution rate. */ employerPensionRate?: number; /** Optional additional employer tax rate. */ employerTaxRate?: number; /** Display name for the jurisdiction. */ displayName?: string; /** ISO currency code. */ currency?: string; /** ISO country code. */ countryCode?: string; /** Optional tax-free allowance (annual). */ taxFreeAllowance?: number; } export class FlatRateTax implements TaxJurisdiction { readonly name: string; readonly countryCode: string; readonly currency: string; private flatRate: number; private employerPensionRate: number; private employerTaxRate: number; private taxFreeAllowance: number; constructor(options: FlatRateTaxOptions) { this.flatRate = options.flatIncomeTaxRate; this.employerPensionRate = options.employerPensionRate ?? 0; this.employerTaxRate = options.employerTaxRate ?? 0; this.name = options.displayName ?? "Custom Jurisdiction"; this.currency = options.currency ?? "EUR"; this.countryCode = options.countryCode ?? "XX"; this.taxFreeAllowance = options.taxFreeAllowance ?? 0; } calculate(grossAnnual: number): { deductions: DeductionLineItem[]; employerContributions: EmployerContribution[]; } { // Income tax on amount above tax-free allowance const taxableIncome = Math.max(0, grossAnnual - this.taxFreeAllowance); const incomeTax = round2(taxableIncome * this.flatRate); // Employer contributions const employerPension = round2(grossAnnual * this.employerPensionRate); const employerTax = round2(grossAnnual * this.employerTaxRate); const deductions: DeductionLineItem[] = [ { name: "Income Tax", amount: incomeTax, type: "tax", description: `Flat rate ${(this.flatRate * 100).toFixed(1)}%${ this.taxFreeAllowance > 0 ? ` (allowance: ${this.currency} ${this.taxFreeAllowance.toLocaleString()})` : "" }`, }, ]; const employerContributions: EmployerContribution[] = []; if (employerPension > 0) { employerContributions.push({ name: "Employer Pension Contribution", amount: employerPension, description: `${(this.employerPensionRate * 100).toFixed(1)}% of gross`, }); } if (employerTax > 0) { employerContributions.push({ name: "Employer Tax Contribution", amount: employerTax, description: `${(this.employerTaxRate * 100).toFixed(1)}% of gross`, }); } return { deductions, employerContributions }; } }