From fe705bb41119436965fc7017b46371d7353e1e74 Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Tue, 6 Oct 2026 11:19:29 +0100 Subject: [PATCH] =?UTF-8?q?Annualise=20the=20employer=20NI=20secondary=20t?= =?UTF-8?q?hreshold=20to=20the=20statutory=20=C2=A35,000?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 2025-26 store the secondary threshold as 5,000 / 52 a week, the convention SI 2001/1004 reg 11(3A)(c) uses and the model already applies to the primary threshold and upper earnings limit. 52 weeks now give the reg 11(3A)(b) annual £5,000 instead of 52 x £96 = £4,992. The strict xfail in test_ni_threshold_freeze.py becomes an ordinary test. Fixes #1967 Co-Authored-By: Claude Opus 5.5 --- changelog.d/1967.fixed.md | 1 + .../thresholds/secondary_threshold.yaml | 23 +++++--- .../class_1/ni_class_1_employer.yaml | 57 ++++++++++++++++--- .../ni_threshold_freeze.yaml | 11 ++-- .../tests/test_ni_threshold_freeze.py | 26 ++++----- 5 files changed, 81 insertions(+), 37 deletions(-) create mode 100644 changelog.d/1967.fixed.md diff --git a/changelog.d/1967.fixed.md b/changelog.d/1967.fixed.md new file mode 100644 index 0000000000..841d58db04 --- /dev/null +++ b/changelog.d/1967.fixed.md @@ -0,0 +1 @@ +- Store the Class 1 secondary threshold from 2025-26 as £5,000 / 52 a week, so employer National Insurance uses the statutory annual £5,000 (SI 2001/1004 reg 11(3A)(b)) rather than 52 x the rounded weekly £96 = £4,992. Employer NI falls by up to £1.20 a year per employee earning above the threshold. diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml index 85dc6e9eb3..eb92c8362d 100644 --- a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml @@ -7,8 +7,13 @@ metadata: - https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/882271/Table-a4.pdf - https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions unit: currency-GBP -# The model stores the weekly £96 in reg 10(d) and annualises it as 52 weeks -# (£4,992); reg 11(3A)(b) gives £5,000 a year (#1967). +# From 2025-26 the value is the annual £5,000 in SI 2001/1004 reg 11(3A)(b) +# divided by 52 (96.153846 = 5,000 / 52), so that the model's 52-week +# annualisation gives £5,000. This is the convention reg 11(3)(c) and +# 11(2A)(c) use for the primary threshold and upper earnings limit, which the +# model also stores as annual / 52. The rounded weekly figure in reg 10(d) is +# £96, which would annualise to £4,992 (#1967). Before 2025-26 the weekly +# figure times 52 equals the annual one (for example £175 x 52 = £9,100). # Legal status: SI 2001/1004 reg 10 sets the amounts for 2026-27 only. The # 2027-28 to 2030-31 values are announced policy (Autumn Budget 2024 and # Budget 2025), to be set in each year's rates, limits and thresholds @@ -30,13 +35,15 @@ values: value: 175 2024-01-01: 175 2025-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: National Insurance Contributions (Secondary Class 1 Contributions) Act 2025 s.2 href: https://www.legislation.gov.uk/ukpga/2025/11/section/2 + - title: The Social Security (Contributions) Regulations 2001 reg 11(3A)(b) (£5,000 where the earnings period is a year) + href: https://www.legislation.gov.uk/uksi/2001/1004/regulation/11 2026-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: The Social Security (Contributions) Regulations 2001 reg 10(d) and reg 11(3A), for 2026-27 (reg 10's tax year set by SI 2026/231 reg 5(2)(a)) @@ -44,13 +51,13 @@ values: - title: SI 2026/231 reg 5 href: https://www.legislation.gov.uk/uksi/2026/231/regulation/5/made 2027-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: "HMRC: Changes to the Class 1 National Insurance Contributions Secondary Threshold, the Secondary Class 1 National Insurance contributions rate, and the Employment Allowance from 6 April 2025 (Autumn Budget 2024)" href: https://www.gov.uk/government/publications/changes-to-the-class-1-national-insurance-contributions-secondary-threshold-the-secondary-class-1-national-insurance-contributions-rate-and-the-empl/changes-to-the-class-1-national-insurance-contributions-secondary-threshold-the-secondary-class-1-national-insurance-contributions-rate-and-the-empl 2028-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: Budget 2025 (HC 1492) para 4.112 @@ -58,7 +65,7 @@ values: - title: Explanatory Memorandum to SI 2026/231 para 5.26 href: https://www.legislation.gov.uk/uksi/2026/231/pdfs/uksiem_20260231_en_001.pdf 2029-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: Budget 2025 (HC 1492) para 4.112 @@ -66,7 +73,7 @@ values: - title: Explanatory Memorandum to SI 2026/231 para 5.26 href: https://www.legislation.gov.uk/uksi/2026/231/pdfs/uksiem_20260231_en_001.pdf 2030-04-06: - value: 96 + value: 96.153846 metadata: reference: - title: Budget 2025 (HC 1492) para 4.112 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_employer.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_employer.yaml index bbf695a5b9..1d79419929 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_employer.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_employer.yaml @@ -17,8 +17,8 @@ # SSCBA 1992 s.6(3) ends primary Class 1 at pensionable age "without prejudice # to any liability to pay secondary Class 1 contributions". Secondary # contributions are due on earners over 16 (s.6(1)(b)) with no upper age. -# The model annualises the weekly secondary threshold as 52 weeks: 2026-27 is -# £96 x 52 = £4,992 at 15%; 2024-25 is £175 x 52 = £9,100 at 13.8%. +# The annual secondary threshold is £5,000 at 15% in 2026-27 (SI 2001/1004 +# reg 11(3A)(b)) and £175 x 52 = £9,100 at 13.8% in 2024-25. - name: Employer NI is charged on an employee aged 70 (2026) period: 2026 @@ -28,11 +28,11 @@ employment_income: 40_000 output: ni_class_1_employee: 0 - ni_class_1_employer: 5_251.20 # 0.15 x (40,000 - 4,992) - ni_employer: 5_251.20 + ni_class_1_employer: 5_250.00 # 0.15 x (40,000 - 5,000) + ni_employer: 5_250.00 national_insurance: 0 - total_national_insurance: 5_251.20 - employer_cost: 45_251.20 + total_national_insurance: 5_250.00 + employer_cost: 45_250.00 - name: Employer NI is charged on an employee aged 70 (2024) period: 2024 @@ -52,7 +52,7 @@ employment_income: 60_000 output: ni_class_1_employee: 0 - ni_class_1_employer: 8_251.20 # 0.15 x (60,000 - 4,992) + ni_class_1_employer: 8_250.00 # 0.15 x (60,000 - 5,000) - name: Earnings below the secondary threshold over state pension age period: 2026 @@ -77,7 +77,7 @@ ni_liable: true ni_class_1_secondary_liable: true ni_class_1_employee: 2_194.40 # 0.08 x (40,000 - 12,570) - ni_class_1_employer: 5_251.20 + ni_class_1_employer: 5_250.00 - name: At state pension age, only the employer side is charged period: 2026 @@ -90,7 +90,7 @@ ni_liable: false ni_class_1_secondary_liable: true ni_class_1_employee: 0 - ni_class_1_employer: 5_251.20 + ni_class_1_employer: 5_250.00 - name: No employer NI on an earner under 16 period: 2026 @@ -101,3 +101,42 @@ output: ni_class_1_employee: 0 ni_class_1_employer: 0 + +# SI 2001/1004 reg 11(3A)(b): the annual secondary threshold is £5,000 from +# 2025-26, not 52 x the rounded weekly £96 = £4,992 (#1967). + +- name: No employer NI on annual earnings of exactly £5,000 (2025-26) + period: 2025 + absolute_error_margin: 0.01 + input: + age: 40 + ni_class_1_income: 5_000 + output: + ni_class_1_employer: 0 + +- name: Employer NI on annual earnings of £4,992 to £5,000 is nil (2026-27) + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + ni_class_1_income: 4_996 + output: + ni_class_1_employer: 0 + +- name: Employer NI starts above £5,000 a year (2026-27) + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + ni_class_1_income: 6_000 + output: + ni_class_1_employer: 150 # 0.15 x (6,000 - 5,000) + +- name: Employer NI on £40,000 in 2025-26 uses the £5,000 annual threshold + period: 2025 + absolute_error_margin: 0.01 + input: + age: 40 + ni_class_1_income: 40_000 + output: + ni_class_1_employer: 5_250 # 0.15 x (40,000 - 5,000) diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/ni_threshold_freeze.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/ni_threshold_freeze.yaml index 9876a80dca..503399257f 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/ni_threshold_freeze.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/ni_threshold_freeze.yaml @@ -3,7 +3,8 @@ # UPL £50,270, ST £5,000 (£96 a week). 2027-28 was already frozen by # Autumn Statement 2022 (PT, UEL, LPL, UPL) and Autumn Budget 2024 (ST). # Rates: Class 1 8% / 2%, employer 15%, Class 4 6% / 2%. -# The model annualises the weekly ST as 52 x £96 = £4,992. +# The model stores the ST as 5,000 / 52 a week (reg 11(3A)), so 52 weeks give +# £5,000 (#1967). - name: 2027-28 employee Class 1 at the primary threshold period: 2027 @@ -22,7 +23,7 @@ employment_income: 30_000 output: ni_class_1_employee: (30_000 - 12_570) * 0.08 # 1,394.40 - ni_class_1_employer: (30_000 - 4_992) * 0.15 # 3,751.20 + ni_class_1_employer: (30_000 - 5_000) * 0.15 # 3,750.00 - name: 2027-28 employee Class 1 above the UEL period: 2027 @@ -33,7 +34,7 @@ output: ni_class_1_employee_primary: (50_270 - 12_570) * 0.08 # 3,016 ni_class_1_employee_additional: (60_000 - 50_270) * 0.02 # 194.60 - ni_class_1_employer: (60_000 - 4_992) * 0.15 # 8,251.20 + ni_class_1_employer: (60_000 - 5_000) * 0.15 # 8,250.00 - name: 2027-28 Class 4 at the lower profits limit period: 2027 @@ -79,7 +80,7 @@ employment_income: 30_000 output: ni_class_1_employee: (30_000 - 12_570) * 0.08 # 1,394.40 - ni_class_1_employer: (30_000 - 4_992) * 0.15 # 3,751.20 + ni_class_1_employer: (30_000 - 5_000) * 0.15 # 3,750.00 - name: 2030-31 employee Class 1 above the UEL period: 2030 @@ -90,7 +91,7 @@ output: ni_class_1_employee_primary: (50_270 - 12_570) * 0.08 # 3,016 ni_class_1_employee_additional: (60_000 - 50_270) * 0.02 # 194.60 - ni_class_1_employer: (60_000 - 4_992) * 0.15 # 8,251.20 + ni_class_1_employer: (60_000 - 5_000) * 0.15 # 8,250.00 - name: 2030-31 Class 4 at the lower profits limit period: 2030 diff --git a/policyengine_uk/tests/test_ni_threshold_freeze.py b/policyengine_uk/tests/test_ni_threshold_freeze.py index d67317edc2..0ee0151a8e 100644 --- a/policyengine_uk/tests/test_ni_threshold_freeze.py +++ b/policyengine_uk/tests/test_ni_threshold_freeze.py @@ -6,11 +6,11 @@ Earnings Limit is not frozen: it rose with CPI to £129 a week in 2026-27. Model year Y is the fiscal year starting 6 April Y. The model stores PT and UEL -as the annual amounts over 52 (12,570 / 52 and 50,270 / 52, as SI 2001/1004 -reg 11(3)(c) and 11(2A)(c) do for periods of whole weeks) and ST as the reg -10(d) weekly amount (£96), and annualises weekly thresholds as 52 weeks. So the -model's annual ST is £4,992, where reg 11(3A)(b) gives £5,000 (#1967); the -statute table follows the model here, and a strict xfail marks the gap. +and ST as the annual amounts over 52 (12,570 / 52, 50,270 / 52 and 5,000 / 52, +as SI 2001/1004 reg 11(3)(c), 11(2A)(c) and 11(3A)(c) do for periods of whole +weeks), and annualises weekly thresholds as 52 weeks. So the model's annual ST +is the £5,000 in reg 11(3A)(b), not 52 x the rounded reg 10(d) weekly £96 +(£4,992) (#1967). Invariants: @@ -59,8 +59,7 @@ STATUTORY_2026_27 = { "lower_earnings_limit": Fraction(129), # weekly, CPI-uprated "primary_threshold": Fraction(12_570, 52), # annual / 52 - # reg 10(d) weekly, as the model stores it; see #1967 for the annual £5,000. - "secondary_threshold": Fraction(96), + "secondary_threshold": Fraction(5_000, 52), # annual / 52 (#1967) "upper_earnings_limit": Fraction(50_270, 52), # annual / 52 "lower_profits_limit": Fraction(12_570), # annual "upper_profits_limit": Fraction(50_270), # annual @@ -129,15 +128,12 @@ def test_lower_earnings_limit_2025_26_and_2026_27(system): assert threshold(system, "lower_earnings_limit", 2026) == 129 -@pytest.mark.xfail( - strict=True, - reason="#1967: the model annualises the weekly ST as 52 x £96 = £4,992; " - "SI 2001/1004 reg 11(3A)(b) gives £5,000 a year.", -) -@pytest.mark.parametrize("year", FROZEN_YEARS) +@pytest.mark.parametrize("year", [2025, *FROZEN_YEARS]) def test_annual_secondary_threshold_is_statutory(system, year): + # SI 2001/1004 reg 11(3A)(b): £5,000 where the earnings period is a year + # (#1967). 52 x the rounded weekly £96 would give £4,992. annual = 52 * threshold(system, "secondary_threshold", year) - assert annual == pytest.approx(5_000, abs=0.5) + assert annual == pytest.approx(5_000, abs=0.01) # Invariant 2: cash freeze. @@ -328,7 +324,7 @@ def check_liabilities(earnings, profits, years): # Every threshold, a penny and a pound either side, plus seeded random levels. EDGES = [ edge + offset - for edge in [4_992, 12_570, 50_270] + for edge in [4_992, 5_000, 12_570, 50_270] for offset in (-1, -0.01, 0, 0.01, 1) ]