/** * Vela Payroll Engine — United Kingdom Tax Jurisdiction * * Based on HMRC 2025-26 tax year (6 April 2025 – 5 April 2026). * Source: https://www.gov.uk/income-tax-rates * * Income Tax (PAYE) — England, Wales, Northern Ireland * ┌──────────────────────────────────┬─────────────────────────────┐ * │ Band │ Taxable Income │ Tax Rate │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Personal │ Up to £12,570 │ 0% │ * │ Allowance │ │ │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Basic Rate │ £12,571 – │ 20% │ * │ │ £50,270 │ │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Higher Rate │ £50,271 – │ 40% │ * │ │ £125,140 │ │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Additional │ Over £125,140 │ 45% │ * └──────────────────────────────────┴─────────────────────────────┘ * * Note: Personal allowance is reduced by £1 for every £2 of income * over £100,000 (tapered to zero at £125,140). * * National Insurance Class 1 — Employee (Primary) * ┌──────────────────────────────────┬─────────────────────────────┐ * │ Earnings Band │ Rate │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Up to £12,570/yr (Primary │ 0% │ * │ Threshold) │ │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ £12,570 – £50,270 │ 8% │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Over £50,270 │ 2% │ * └──────────────────────────────────┴─────────────────────────────┘ * * National Insurance Class 1 — Employer (Secondary) * ┌──────────────────────────────────┬─────────────────────────────┐ * │ Earnings Band │ Rate │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Up to £9,100/yr (Secondary │ 0% │ * │ Threshold) │ │ * ├──────────────────────────────────┼─────────────────────────────┤ * │ Over £9,100 │ 13.8% │ * └──────────────────────────────────┴─────────────────────────────┘ * * Employment Allowance: Not included in MVP (employer-side relief). */ import { TaxJurisdiction, TaxBracket, DeductionLineItem, EmployerContribution, progressiveTaxCalc, round2, } from "../types"; // ── 2025-26 Income Tax Brackets ── const INCOME_TAX_BRACKETS: TaxBracket[] = [ { min: 0, max: 12_570, rate: 0.0 }, { min: 12_570, max: 50_270, rate: 0.20 }, { min: 50_270, max: 125_140, rate: 0.40 }, { min: 125_140, max: null, rate: 0.45 }, ]; // ── Employee NI (Primary) ── const NI_PRIMARY_PT = 12_570; // Primary Threshold const NI_PRIMARY_UPPER = 50_270; // Upper Earnings Limit const NI_PRIMARY_RATE_LOWER = 0.08; // 8% between PT and UEL const NI_PRIMARY_RATE_UPPER = 0.02; // 2% above UEL // ── Employer NI (Secondary) ── const NI_SECONDARY_PT = 9_100; // Secondary Threshold const NI_SECONDARY_RATE = 0.138; // 13.8% above ST export class UKTax implements TaxJurisdiction { readonly name = "United Kingdom"; readonly countryCode = "GB"; readonly currency = "GBP"; calculate(grossAnnual: number): { deductions: DeductionLineItem[]; employerContributions: EmployerContribution[]; } { // 1. Calculate Personal Allowance (tapered above £100k) let personalAllowance = 12_570; if (grossAnnual > 100_000) { const reduction = Math.floor((grossAnnual - 100_000) / 2); personalAllowance = Math.max(0, 12_570 - reduction); } // 2. Income Tax (PAYE) // Adjust brackets with actual personal allowance const taxBrackets: TaxBracket[] = [ { min: 0, max: personalAllowance, rate: 0.0 }, { min: personalAllowance, max: 50_270, rate: 0.20 }, { min: 50_270, max: 125_140, rate: 0.40 }, { min: 125_140, max: null, rate: 0.45 }, ]; const incomeTax = progressiveTaxCalc(grossAnnual, taxBrackets); // 3. Employee National Insurance let employeeNI = 0; if (grossAnnual > NI_PRIMARY_PT) { const lowerBand = Math.min(grossAnnual, NI_PRIMARY_UPPER) - NI_PRIMARY_PT; employeeNI += lowerBand * NI_PRIMARY_RATE_LOWER; } if (grossAnnual > NI_PRIMARY_UPPER) { employeeNI += (grossAnnual - NI_PRIMARY_UPPER) * NI_PRIMARY_RATE_UPPER; } employeeNI = round2(employeeNI); // 4. Employer National Insurance let employerNI = 0; if (grossAnnual > NI_SECONDARY_PT) { employerNI = (grossAnnual - NI_SECONDARY_PT) * NI_SECONDARY_RATE; } employerNI = round2(employerNI); // Build results const deductions: DeductionLineItem[] = [ { name: "Income Tax (PAYE)", amount: round2(incomeTax), type: "tax", description: `HMRC PAYE (2025-26, PA: £${personalAllowance.toLocaleString()})`, }, { name: "Employee National Insurance", amount: employeeNI, type: "insurance", description: `NI Class 1 — 8% / 2%`, }, ]; const employerContributions: EmployerContribution[] = [ { name: "Employer National Insurance", amount: employerNI, description: "NI Class 1 — 13.8% above £9,100", }, ]; return { deductions, employerContributions }; } }