From 9129c8a16af2ce377e4e52545aca5861880fc8bd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 07:45:33 -0400 Subject: [PATCH 1/6] Charge the winter fuel payment back through income tax, person by person MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ITEPA 2003 s.681I (FA 2026 Sch 10, from 2025-26) charges a person entitled to a winter fuel payment the whole payment when their own total income exceeds £35,000 and they are not entitled to IS, income-based JSA, Pension Credit, income-related ESA or UC. It covers the Winter Fuel Payment in England and Wales (SI 2025/969) and Northern Ireland (NISR 2025/142) and Scotland's Pension Age Winter Heating Payment (SSI 2024/351). - winter_fuel_payment_charge (Person) under gov.hmrc.income_tax.charges, added to income_tax after tax reductions (ITA 2007 s.23 Step 7, s.30). - Payments stay gross; from 2025 the WFP pays everyone of pensionable age (require_benefits false), so entitlement, and the shared amounts, come before the charge. - Means tests deduct income_tax_before_winter_fuel_payment_charge: the charge recovers a payment, and it depends on PC/UC receipt. - pawhp counts in household_benefits and HBAI income, like the WFP. - The household income test is deprecated (off from 2025; boundary <=). Co-Authored-By: Claude Opus 5.5 --- .../winter-fuel-payment-charge.added.md | 2 + .../winter-fuel-payment-charge.changed.md | 1 + .../winter-fuel-payment-charge.fixed.md | 2 + .../eligibility/require_benefits.yaml | 35 +- .../maximum_taxable_income.yaml | 10 +- .../use_maximum_taxable_income.yaml | 8 +- .../charges/winter_fuel_payment/README.md | 1 + .../winter_fuel_payment/in_effect.yaml | 14 + .../winter_fuel_payment/income_threshold.yaml | 13 + .../relevant_benefits.yaml | 15 + policyengine_uk/programs.yaml | 6 +- .../housing_benefit_applicable_income.yaml | 10 +- .../legacy_family_definitions.yaml | 36 +- .../income/uc_earned_income.yaml | 4 +- .../claimant_partner_income_and_carers.yaml | 6 +- .../relevant_benefit_receipt.yaml | 20 +- .../charges/winter_fuel_payment_charge.yaml | 485 ++++++++++++++++++ .../taxable_income_test.yaml | 85 ++- .../charge_parameters.yaml | 51 ++ .../test_winter_heating_payment_properties.py | 128 ++++- .../housing_benefit_applicable_income.py | 5 +- .../dwp/income_support_applicable_income.py | 5 +- .../gov/dwp/is_benefit_cap_exempt_earnings.py | 2 +- ...is_benefit_cap_exempt_health_disability.py | 2 +- .../gov/dwp/is_benefit_cap_exempt_other.py | 2 +- .../pension_credit/pension_credit_income.py | 6 +- .../income/uc_earned_income.py | 7 +- .../gov/dwp/winter_fuel_allowance.py | 5 +- .../variables/gov/dwp/winter_fuel_payment.py | 3 +- .../gov/dwp/winter_fuel_payment_eligible.py | 24 +- .../charges/winter_fuel_payment_charge.py | 38 ++ .../gov/hmrc/income_tax/income_tax.py | 30 +- ...e_tax_before_winter_fuel_payment_charge.py | 28 + ...council_tax_reduction_applicable_income.py | 5 +- .../gov/social_security_scotland/pawhp.py | 5 +- .../pension_age_winter_heating_payment.py | 3 +- .../household/income/hbai_benefits.py | 1 + .../income/hbai_household_net_income.py | 1 + .../household/income/household_benefits.py | 1 + 39 files changed, 983 insertions(+), 122 deletions(-) create mode 100644 changelog.d/winter-fuel-payment-charge.added.md create mode 100644 changelog.d/winter-fuel-payment-charge.changed.md create mode 100644 changelog.d/winter-fuel-payment-charge.fixed.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/in_effect.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/relevant_benefits.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml create mode 100644 policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/income_tax_before_winter_fuel_payment_charge.py diff --git a/changelog.d/winter-fuel-payment-charge.added.md b/changelog.d/winter-fuel-payment-charge.added.md new file mode 100644 index 0000000000..c368d8465d --- /dev/null +++ b/changelog.d/winter-fuel-payment-charge.added.md @@ -0,0 +1,2 @@ +- Add the winter fuel payment charge (`winter_fuel_payment_charge`, ITEPA 2003 s.681I, from 2025-26) with parameters under `gov.hmrc.income_tax.charges.winter_fuel_payment`. It is charged person by person: the whole Winter Fuel Payment (England, Wales and Northern Ireland) or Pension Age Winter Heating Payment, when the person's own total income exceeds £35,000 and they are not entitled to Income Support, income-based JSA, Pension Credit, income-related ESA or Universal Credit. It is added to `income_tax` after tax reductions (ITA 2007 s.23 Step 7). +- Add `income_tax_before_winter_fuel_payment_charge`. The Pension Credit, Income Support, Housing Benefit, Council Tax Reduction, Universal Credit and benefit cap means tests now deduct it instead of `income_tax` (Universal Credit deducted `tax`, income tax plus National Insurance). The charge recovers a payment rather than taxing the income those tests assess, and it depends on Pension Credit and Universal Credit receipt, so deducting it would make the model circular. diff --git a/changelog.d/winter-fuel-payment-charge.changed.md b/changelog.d/winter-fuel-payment-charge.changed.md new file mode 100644 index 0000000000..d99a959352 --- /dev/null +++ b/changelog.d/winter-fuel-payment-charge.changed.md @@ -0,0 +1 @@ +- Deprecate `gov.dwp.winter_fuel_payment.eligibility.taxable_income_test`: it is off from 2025 and has effect only in a reform that requires a relevant benefit. Its limit now admits total income equal to it. Use `gov.hmrc.income_tax.charges.winter_fuel_payment` for the charge. diff --git a/changelog.d/winter-fuel-payment-charge.fixed.md b/changelog.d/winter-fuel-payment-charge.fixed.md new file mode 100644 index 0000000000..97b3a984df --- /dev/null +++ b/changelog.d/winter-fuel-payment-charge.fixed.md @@ -0,0 +1,2 @@ +- From the 2025 qualifying week, pay the Winter Fuel Payment to everyone of pensionable age in England, Wales and Northern Ireland (SI 2025/969 reg. 2; NISR 2025/142 reg. 2) and recover it through the winter fuel payment charge, in place of a household income test. The test paid a whole household if any member of pensionable age had total income under £35,000. It applied only in England and Wales, and it charged nobody in Northern Ireland or on the Pension Age Winter Heating Payment. Its boundary was also wrong: total income of exactly £35,000 is not charged. +- Count the Pension Age Winter Heating Payment in `household_benefits`, `hbai_household_net_income` and `hbai_benefits`, as the Winter Fuel Payment is. diff --git a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/require_benefits.yaml b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/require_benefits.yaml index a85099df07..85dac65b48 100644 --- a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/require_benefits.yaml +++ b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/require_benefits.yaml @@ -1,19 +1,28 @@ -description: Whether a relevant benefit is a condition of the Winter Fuel Payment. It was for the 2024 qualifying week. From the 2025 qualifying week the regulations pay everyone of pensionable age, and the winter fuel payment charge (ITEPA 2003 s.681I) recovers the payment from people with total income over £35,000 who are not on a relevant benefit. The model keeps this true from 2025 and lets people who are not on a relevant benefit through the income test in taxable_income_test, which stands in for the charge. +description: Whether a relevant benefit is a condition of the Winter Fuel Payment. It was for the 2024 qualifying week only. Up to the 2023 week, and from the 2025 week, everyone of pensionable age resident where the payment is made is entitled; from 2025-26 the winter fuel payment charge (gov.hmrc.income_tax.charges.winter_fuel_payment) recovers the payment from people with total income over £35,000 who are not on a relevant benefit. values: - 2000-01-01: false + 2000-01-01: + value: false + reference: + - title: The Social Fund Winter Fuel Payment Regulations 2000, reg. 2 (as in force for the 2023 qualifying week) + href: https://www.legislation.gov.uk/uksi/2000/729/regulation/2/2023-09-18 + # Dated 1 January so that the year's value is the one for its September + # qualifying week; SI 2024/869 came into force on 16 September 2024. 2024-01-01: value: true - metadata: - reference: - - title: The Social Fund Winter Fuel Payment Regulations 2024 regulation 2 - href: https://www.legislation.gov.uk/uksi/2024/869/regulation/2 + reference: + - title: The Social Fund Winter Fuel Payment Regulations 2024, reg. 2(2)(b) + href: https://www.legislation.gov.uk/uksi/2024/869/regulation/2/made + - title: The Social Fund Winter Fuel Payment Regulations (Northern Ireland) 2024, reg. 2(2)(b) + href: https://www.legislation.gov.uk/nisr/2024/160/regulation/2/made + # SI 2025/969 came into force on 15 September 2025, the first day of the + # 2025 qualifying week. + 2025-01-01: + value: false + reference: + - title: The Social Fund Winter Fuel Payment Regulations 2025, reg. 2 + href: https://www.legislation.gov.uk/uksi/2025/969/regulation/2/made + - title: The Social Fund Winter Fuel Payment Regulations (Northern Ireland) 2025, reg. 2 + href: https://www.legislation.gov.uk/nisr/2025/142/regulation/2 metadata: unit: bool label: Winter Fuel Payment means-tested benefits requirement - reference: - - href: https://www.legislation.gov.uk/uksi/2000/729/regulation/2 - title: The Social Fund Winter Fuel Payment Regulations 2000 - - title: The Social Fund Winter Fuel Payment Regulations 2025, reg. 2 - href: https://www.legislation.gov.uk/uksi/2025/969/regulation/2/made - - title: Finance Act 2026, Sch. 10 (ITEPA 2003 s.681I, winter fuel payment charge) - href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 diff --git a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml index 29e4138a20..4b926ae091 100644 --- a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml +++ b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml @@ -1,11 +1,7 @@ -description: Households in England or Wales with pensioners earning under this amount of yearly taxable income are eligible for the Winter Fuel Payment. This stands in for the winter fuel payment charge, which recovers the payment from a person whose own total income exceeds £35,000 and who is not entitled to a relevant benefit. +description: Deprecated; use gov.hmrc.income_tax.charges.winter_fuel_payment.income_threshold for the winter fuel payment charge. The household income test (use_maximum_taxable_income) pays a person not on a relevant benefit if a member of their household who has reached pensionable age has total income at or under this amount. It has effect only when that test is switched on and a relevant benefit is required (require_benefits). values: 2000-01-01: 0 - 2025-01-01: - value: 35_000 - reference: - - title: Finance Act 2026, Sch. 10 para. 1 (ITEPA 2003 s.681I(1)(b), for 2025-26 on) - href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 + 2025-01-01: 35_000 metadata: unit: currency-GBP - label: Winter Fuel Payment maximum taxable income + label: Winter Fuel Payment household income test limit (deprecated) diff --git a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml index 594e55d980..1871b17ffd 100644 --- a/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml +++ b/policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml @@ -1,10 +1,6 @@ -description: When this is true, households in England or Wales with pensioners earning under the maximum taxable income are eligible for the Winter Fuel Payment. This stands in for the winter fuel payment charge on people with total income over £35,000 (ITEPA 2003 s.681I, from 2025-26). +description: Deprecated; use gov.hmrc.income_tax.charges.winter_fuel_payment for the winter fuel payment charge. When this is true and a relevant benefit is required (require_benefits), a person who is not on one is still paid if a member of their household who has reached pensionable age has total income at or under maximum_taxable_income. No regulations have set such a test. It stood in for the winter fuel payment charge for 2025 before the model computed the charge, and is kept, off, so that reforms which set it still load. It has no effect while require_benefits is false, as it is from 2025. values: 2000-01-01: false - 2025-01-01: true metadata: unit: bool - label: WFA maximum taxable income eligibility - reference: - - title: Finance Act 2026, Sch. 10 para. 1 (ITEPA 2003 s.681I) - href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 + label: Winter Fuel Payment household income test (deprecated) diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/README.md new file mode 100644 index 0000000000..5bf15396bd --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/README.md @@ -0,0 +1 @@ +# Winter fuel payment charge diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/in_effect.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/in_effect.yaml new file mode 100644 index 0000000000..a5972ca01a --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/in_effect.yaml @@ -0,0 +1,14 @@ +description: Whether the winter fuel payment charge applies. It has effect for the tax year 2025-26 and later tax years. +values: + 2000-01-01: false + # Dated 1 January so that the year's value is the one for the tax year + # beginning in its April (2025 = 2025-26), whose qualifying week is in + # September 2025. + 2025-01-01: + value: true + reference: + - title: Finance Act 2026, Sch. 10 para. 5 (commencement) + href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 +metadata: + unit: bool + label: Winter fuel payment charge in effect diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml new file mode 100644 index 0000000000..f82887986d --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml @@ -0,0 +1,13 @@ +description: A person entitled to a winter fuel payment is liable to the winter fuel payment charge when their total income for the tax year exceeds this amount, unless they are entitled to a relevant benefit in the qualifying week. Total income of exactly this amount is not charged. The Treasury may change the amount by order for later tax years. +values: + 2025-01-01: + value: 35_000 + reference: + - title: Income Tax (Earnings and Pensions) Act 2003, s. 681I(1)(b), inserted by Finance Act 2026, Sch. 10 para. 1 + href: https://www.legislation.gov.uk/ukpga/2003/1/section/681I + - title: Income Tax (Earnings and Pensions) Act 2003, s. 681J (alteration of income limit by Treasury order) + href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 +metadata: + unit: currency-GBP + period: year + label: Winter fuel payment charge income threshold diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/relevant_benefits.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/relevant_benefits.yaml new file mode 100644 index 0000000000..5014f654e5 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/relevant_benefits.yaml @@ -0,0 +1,15 @@ +description: Benefits that exempt a person from the winter fuel payment charge when they are entitled to one on any day in the qualifying week. The model takes a person to be entitled when their own award, or for a claimant or partner their couple's award, is positive in the year. +values: + 2025-01-01: + value: + - is_on_income_support + - is_on_income_based_jsa + - is_on_pension_credit + - is_on_income_related_esa + - is_on_universal_credit + reference: + - title: Income Tax (Earnings and Pensions) Act 2003, s. 681I(4) and (5) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/681I +metadata: + unit: list + label: Winter fuel payment charge relevant benefits diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 9dddf799d9..4da990e017 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -15,7 +15,7 @@ programs: variable: income_tax parameter_prefix: gov.hmrc.income_tax verified_start_year: 2019 - notes: Includes Scottish rates via devolved tax bands + notes: Includes Scottish rates via devolved tax bands, the high income child benefit charge and, from 2025-26, the winter fuel payment charge - id: national_insurance name: National Insurance @@ -352,7 +352,7 @@ programs: variable: winter_fuel_payment parameter_prefix: gov.dwp.winter_fuel_payment verified_start_year: 2022 - notes: Great Britain to winter 2023-24, then England and Wales; Northern Ireland's mirror scheme uses the same parameters + notes: Great Britain to winter 2023-24, then England and Wales; Northern Ireland's mirror scheme uses the same parameters. From winter 2025-26 paid to everyone of pensionable age and recovered by the winter fuel payment charge (income tax) - id: ssmg name: Sure Start Maternity Grant @@ -593,4 +593,4 @@ programs: variable: pension_age_winter_heating_payment parameter_prefix: gov.social_security_scotland.pawhp verified_start_year: 2024 - notes: Replaced the Winter Fuel Payment in Scotland from winter 2024-25 + notes: Replaced the Winter Fuel Payment in Scotland from winter 2024-25. From winter 2025-26 recovered by the winter fuel payment charge (income tax) above £35,000 of total income diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income.yaml index 135bd6cebc..c648170b46 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income.yaml @@ -12,7 +12,7 @@ property_income: 0 private_pension_income: 0 # Tax - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 # Other tax_credits: 0 @@ -36,7 +36,7 @@ property_income: 0 private_pension_income: 0 # Tax - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 # Other tax_credits: 0 @@ -60,7 +60,7 @@ property_income: 0 private_pension_income: 0 # Tax - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 # Other tax_credits: 0 @@ -84,7 +84,7 @@ property_income: 1_000 private_pension_income: 0 # Tax - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 # Other tax_credits: 1_000 @@ -108,7 +108,7 @@ property_income: 1_000 private_pension_income: 0 # Tax - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 # Other tax_credits: 1_000 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support/legacy_family_definitions.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support/legacy_family_definitions.yaml index 00ec78032a..1e12b9080e 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support/legacy_family_definitions.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support/legacy_family_definitions.yaml @@ -15,7 +15,7 @@ people: claimant: age: 17 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -37,14 +37,14 @@ claimant: age: 17 is_parent: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 employment_income: 10000 child: age: 1 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -66,7 +66,7 @@ claimant: age: 20 is_parent: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -74,7 +74,7 @@ young_person: age: 18 is_in_non_advanced_education: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -96,7 +96,7 @@ claimant: age: 20 is_parent: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -105,7 +105,7 @@ age: 17 is_in_non_advanced_education: false is_in_approved_training: false - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -126,7 +126,7 @@ people: claimant: age: 30 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -135,7 +135,7 @@ partner: age: 17 is_claimant_or_partner: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -156,7 +156,7 @@ people: claimant: age: 24 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -165,7 +165,7 @@ partner: age: 17 is_claimant_or_partner: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -186,7 +186,7 @@ people: claimant: age: 17 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -195,7 +195,7 @@ partner: age: 17 is_claimant_or_partner: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -217,21 +217,21 @@ claimant: age: 40 is_parent: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 employment_income: 10000 partner: age: 40 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 young_person: age: 19 is_in_non_advanced_education: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -253,7 +253,7 @@ people: claimant: age: 24 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 @@ -272,7 +272,7 @@ people: claimant: age: 25 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 social_security_income: 0 pension_contributions: 0 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/income/uc_earned_income.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/income/uc_earned_income.yaml index 62a0fd3051..1ea4f6b2ef 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/income/uc_earned_income.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/income/uc_earned_income.yaml @@ -6,7 +6,7 @@ miscellaneous_income: 100 uc_work_allowance: 10 # The claimant's own tax (UC reg 22 counts the claimant's earnings). - tax: 1 + income_tax_before_winter_fuel_payment_charge: 1 output: uc_earned_income: 1100 - 11 @@ -18,6 +18,6 @@ miscellaneous_income: 100 uc_work_allowance: 2_000 # The claimant's own tax (UC reg 22 counts the claimant's earnings). - tax: 1 + income_tax_before_winter_fuel_payment_charge: 1 output: uc_earned_income: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml index dbf4bb5dbf..d6023485ce 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml @@ -90,7 +90,7 @@ is_in_non_advanced_education: true age_started_or_accepted_current_education_or_training: 18 employment_income: 3_000 - income_tax: 100 + income_tax_before_winter_fuel_payment_charge: 100 national_insurance: 100 pension_contributions: 2_000 benunits: @@ -139,7 +139,7 @@ state_pension: 5_000 employment_income: 1_000 jsa_contrib: 100 - income_tax: 100 + income_tax_before_winter_fuel_payment_charge: 100 national_insurance: 0 pension_contributions: 200 is_carer_for_benefits: true @@ -148,7 +148,7 @@ private_pension_income: 2_000 self_employment_income: 500 esa_contrib: 200 - income_tax: 50 + income_tax_before_winter_fuel_payment_charge: 50 national_insurance: 25 pension_contributions: 100 is_carer_for_benefits: true diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/winter_fuel_payment/relevant_benefit_receipt.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/winter_fuel_payment/relevant_benefit_receipt.yaml index 7c650a46e6..e4cfce1804 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/winter_fuel_payment/relevant_benefit_receipt.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/winter_fuel_payment/relevant_benefit_receipt.yaml @@ -4,10 +4,10 @@ # - 2024: SI 2024/869 regs 2 to 4; # - 2025 on: SI 2025/969 regs 1(3) and 2 to 4. # Pension Credit and the other benefit-unit awards are entered on the benefit -# unit so that each case turns only on the receipt test. From 2025 the model -# stands in for the winter fuel payment charge (ITEPA 2003 s.681I) with its -# income test: a household passes when a member of pensionable age has total -# income below £35,000. +# unit so that each case turns only on the receipt test. From 2025 everyone +# of pensionable age is entitled (SI 2025/969 reg. 2), and the winter fuel +# payment charge (ITEPA 2003 s.681I) recovers the payment through income tax: +# see tests/policy/baseline/gov/hmrc/income_tax/charges/. - name: 2024 a non-dependant's income-related ESA does not entitle the pensioner # The pensioner is not entitled to a relevant benefit, nor is any partner, @@ -290,9 +290,9 @@ tax_credit_award: [0, 26] winter_fuel_payment: [0, 200] -- name: 2025 a non-dependant's income-related ESA does not exempt the pensioner from the income test +- name: 2025 a non-dependant's income-related ESA does not exempt the pensioner from the charge # Under SI 2025/969 the pensioner is entitled (reg. 2) to £200 (reg. 3(1)), - # but the winter fuel payment charge recovers it: total income over + # and the winter fuel payment charge recovers it: total income over # £35,000 and not entitled to a relevant benefit (ITEPA 2003 s.681I(1) and # (4)). The son's ESA is his own award, so it does not exempt the pensioner. period: 2025 @@ -316,10 +316,11 @@ country: ENGLAND output: is_on_winter_fuel_payment_relevant_benefit: [false, true] - winter_fuel_payment: [0, 0] - winter_fuel_allowance: 0 + winter_fuel_payment: [200, 0] + winter_fuel_payment_charge: [200, 0] + winter_fuel_allowance: 200 -- name: 2025 a pensioner under the income limit living with a non-dependant on ESA gets £200 +- name: 2025 a pensioner with total income under £35,000 living with a non-dependant on ESA keeps £200 # Reg. 3(1): under 80 and not living with another person entitled (the son # is under pensionable age). period: 2025 @@ -343,6 +344,7 @@ country: ENGLAND output: winter_fuel_payment: [200, 0] + winter_fuel_payment_charge: [0, 0] winter_fuel_allowance: 200 - name: 2025 a pensioner on Pension Credit and one who is not, in separate benefit units diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml new file mode 100644 index 0000000000..3678a95928 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml @@ -0,0 +1,485 @@ +# The winter fuel payment charge, ITEPA 2003 s.681I (inserted by FA 2026 +# Sch. 10 para. 1, for 2025-26 on). Expected values are worked from the law: +# - P is liable if entitled to a winter fuel payment for the qualifying week +# and P's own total income for the tax year exceeds £35,000 (s.681I(1)); +# - the charge equals the payment (s.681I(3)); +# - P is not liable if entitled to income support, income-based JSA, Pension +# Credit, income-related ESA or Universal Credit (s.681I(4) and (5)); +# - "winter fuel payment" covers SI 2025/969 (England and Wales), SSI 2024/351 +# (Scotland's Pension Age Winter Heating Payment) and NISR 2025/142 +# (Northern Ireland) (s.681I(6)(a)); +# - the charge is additional tax at Step 7 of the income tax calculation (ITA +# 2007 ss.23 and 30). +# The payments are worked from SI 2025/969 regs 2 to 4, NISR 2025/142 regs 2 +# and 3, and SSI 2024/351 reg 10 as substituted by SSI 2025/282 (amounts from +# SSI 2025/100 and SSI 2026/170). Entitlement to a payment, and so who counts +# as "a person entitled to a winter fuel payment" for the shared amounts, does +# not depend on the charge. Benefit-unit awards are entered directly. + +- name: 2025 total income of exactly £35,000 is not charged + # s.681I(1)(b): total income must exceed £35,000. Reg. 2 and 3(1): £200. + period: 2025 + input: + people: + pensioner: + age: 70 + total_income: 35_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: ENGLAND + output: + winter_fuel_payment: [200] + winter_fuel_payment_charge: [0] + winter_fuel_allowance: 200 + +- name: 2025 total income of £35,001 is charged the whole payment + # s.681I(1) and (3). + period: 2025 + input: + people: + pensioner: + age: 70 + total_income: 35_001 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: ENGLAND + output: + winter_fuel_payment: [200] + winter_fuel_payment_charge: [200] + winter_fuel_allowance: 200 + +- name: 2025 a pensioner aged 85 with £50,000 is charged £300 + # Reg. 3(4): £300 at 80 or over, living alone. s.681I(3): charge £300. + period: 2025 + input: + people: + pensioner: + age: 85 + total_income: 50_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: WALES + output: + winter_fuel_payment: [300] + winter_fuel_payment_charge: [300] + +- name: 2025 a couple is charged on each partner's own income + # Neither is on a relevant benefit and each lives with a person entitled: + # reg. 3(2), £100 each. Only the partner with total income over £35,000 is + # charged (s.681I(1)(b)); the other keeps £100. + period: 2025 + input: + people: + partner_1: + age: 70 + total_income: 50_000 + partner_2: + age: 72 + total_income: 10_000 + benunits: + couple: + members: [partner_1, partner_2] + pension_credit: 0 + households: + household: + members: [partner_1, partner_2] + country: ENGLAND + output: + winter_fuel_payment: [100, 100] + winter_fuel_payment_charge: [100, 0] + winter_fuel_allowance: 200 + +- name: 2025 a couple aged 82 and 85 both over £35,000 are each charged £150 + # Reg. 3(6): over 80, no relevant benefit, living with an entitled person + # who has reached 80: £150 each. Both charged in full. + period: 2025 + input: + people: + partner_1: + age: 82 + total_income: 40_000 + partner_2: + age: 85 + total_income: 40_000 + benunits: + couple: + members: [partner_1, partner_2] + pension_credit: 0 + households: + household: + members: [partner_1, partner_2] + country: ENGLAND + output: + winter_fuel_payment: [150, 150] + winter_fuel_payment_charge: [150, 150] + winter_fuel_allowance: 300 + +- name: 2025 a person charged in full still counts as entitled for the shared amounts + # Two pensioners in separate benefit units. The 70-year-old is entitled + # under reg. 2 whatever their income, so each lives with a person entitled: + # the 70-year-old gets £100 (reg. 3(2)) and the 85-year-old £200 (reg. + # 3(5), living with an entitled person under 80), not £300. The charge + # recovers the 70-year-old's £100 (total income over £35,000). + period: 2025 + input: + people: + high_income: + age: 70 + total_income: 50_000 + aged_85: + age: 85 + total_income: 10_000 + benunits: + unit_1: + members: [high_income] + pension_credit: 0 + unit_2: + members: [aged_85] + pension_credit: 0 + households: + household: + members: [high_income, aged_85] + country: ENGLAND + output: + winter_fuel_payment: [100, 200] + winter_fuel_payment_charge: [100, 0] + winter_fuel_allowance: 300 + +- name: 2025 a lodger's own income is charged, not the Pension Credit recipient's + # The Pension Credit recipient: reg. 3(1), £200, not liable (s.681I(4)). + # The lodger, aged 72, on no relevant benefit and living with a person + # entitled: reg. 3(2), £100, charged (total income £40,000). + period: 2025 + input: + people: + on_pension_credit: + age: 70 + lodger: + age: 72 + total_income: 40_000 + benunits: + unit_1: + members: [on_pension_credit] + pension_credit: 1_000 + lodger_unit: + members: [lodger] + pension_credit: 0 + households: + household: + members: [on_pension_credit, lodger] + country: ENGLAND + output: + winter_fuel_payment: [200, 100] + winter_fuel_payment_charge: [0, 100] + winter_fuel_allowance: 300 + +- name: 2025 a non-dependant's income-related ESA does not exempt the pensioner + # s.681I(4) asks whether P is entitled to a relevant benefit. The son's ESA + # is his own award. The pensioner: reg. 3(1), £200, charged in full. + period: 2025 + input: + people: + pensioner: + age: 70 + total_income: 50_000 + son: + age: 40 + benunits: + pensioner_unit: + members: [pensioner] + pension_credit: 0 + son_unit: + members: [son] + esa_income: 4_000 + households: + household: + members: [pensioner, son] + country: ENGLAND + output: + winter_fuel_payment: [200, 0] + winter_fuel_payment_charge: [200, 0] + winter_fuel_allowance: 200 + +- name: 2025 a pensioner in a Universal Credit couple is not charged + # A couple claims Universal Credit jointly. The 70-year-old is the only + # member of pensionable age: reg. 3(1), £200, not liable (s.681I(4) and + # (5)(e)) despite total income of £40,000. + period: 2025 + input: + people: + claimant: + age: 60 + pensioner: + age: 70 + total_income: 40_000 + benunits: + couple: + members: [claimant, pensioner] + universal_credit: 1_000 + households: + household: + members: [claimant, pensioner] + country: ENGLAND + output: + winter_fuel_payment: [0, 200] + winter_fuel_payment_charge: [0, 0] + +- name: 2025 the partner of a Pension Credit claimant is not entitled and so not charged + # One payment for the couple, to the claimant who reports the award (reg. + # 4(2)(a)). The partner is not entitled to a winter fuel payment, so + # s.681I(1)(a) is not met whatever their income; the claimant is on Pension + # Credit (s.681I(4)). + period: 2025 + input: + people: + claimant: + age: 70 + pension_credit_reported: 1_000 + partner: + age: 72 + total_income: 50_000 + benunits: + couple: + members: [claimant, partner] + pension_credit: 1_000 + households: + household: + members: [claimant, partner] + country: ENGLAND + output: + winter_fuel_payment: [200, 0] + winter_fuel_payment_charge: [0, 0] + winter_fuel_allowance: 200 + +- name: 2025 Northern Ireland pays everyone of pensionable age + # NISR 2025/142 reg. 2 has no relevant-benefit condition; reg. 3(1): £200. + period: 2025 + input: + people: + pensioner: + age: 70 + total_income: 20_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: NORTHERN_IRELAND + output: + winter_fuel_payment: [200] + winter_fuel_payment_charge: [0] + winter_fuel_allowance: 200 + +- name: 2025 the charge recovers a Northern Ireland payment + # s.681I(6)(a) includes payments under NISR 2025/142. + period: 2025 + input: + people: + pensioner: + age: 70 + total_income: 50_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: NORTHERN_IRELAND + output: + winter_fuel_payment: [200] + winter_fuel_payment_charge: [200] + +- name: 2025 the charge recovers Scotland's Pension Age Winter Heating Payment + # SSI 2024/351 reg. 10(5)(a) as substituted by SSI 2025/282: £203.40. + # s.681I(6)(a) includes payments under SSI 2024/351. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + total_income: 50_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: SCOTLAND + output: + winter_fuel_payment: [0] + pension_age_winter_heating_payment: [203.40] + winter_fuel_payment_charge: [203.40] + pawhp: 203.40 + +- name: 2026 a Scottish couple aged 82 and 85 is charged on each partner's own income + # Reg. 10(6)(c) at the SSI 2026/170 amount: £158.35 each. Only the partner + # with total income over £35,000 is charged. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + partner_1: + age: 82 + total_income: 40_000 + partner_2: + age: 85 + total_income: 20_000 + benunits: + couple: + members: [partner_1, partner_2] + pension_credit: 0 + households: + household: + members: [partner_1, partner_2] + country: SCOTLAND + output: + pension_age_winter_heating_payment: [158.35, 158.35] + winter_fuel_payment_charge: [158.35, 0] + pawhp: 316.70 + +- name: 2025 a Scottish Pension Credit recipient is not charged + # Reg. 10(4)(a): £305.10 at 80 or over on a relevant benefit. Not liable + # (s.681I(4)). + period: 2025 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 82 + total_income: 40_000 + benunits: + unit: + members: [pensioner] + pension_credit: 1_000 + households: + household: + members: [pensioner] + country: SCOTLAND + output: + pension_age_winter_heating_payment: [305.10] + winter_fuel_payment_charge: [0] + +- name: 2024 no charge before the tax year 2025-26 + # FA 2026 Sch. 10 para. 5: the charge has effect from 2025-26. For the 2024 + # week a relevant benefit was a condition of entitlement (SI 2024/869 reg. + # 2(2)(b)): £200 on Pension Credit, not charged. + period: 2024 + input: + people: + pensioner: + age: 70 + total_income: 50_000 + benunits: + unit: + members: [pensioner] + pension_credit: 1_000 + households: + household: + members: [pensioner] + country: ENGLAND + output: + winter_fuel_payment: [200] + winter_fuel_payment_charge: [0] + +- name: 2024 Northern Ireland paid only people on a relevant benefit + # NISR 2024/160 reg. 2(2)(b), as SI 2024/869 for England and Wales. + period: 2024 + input: + people: + pensioner: + age: 70 + total_income: 20_000 + benunits: + unit: + members: [pensioner] + pension_credit: 0 + households: + household: + members: [pensioner] + country: NORTHERN_IRELAND + output: + winter_fuel_payment: [0] + winter_fuel_payment_charge: [0] + +- name: 2025 the charge is added to income tax + # Private pension £50,000, no other income, England. Personal allowance + # £12,570; £37,430 at the basic rate of 20% = £7,486 (within the £37,700 + # basic rate band). The charge, £200, is added at Step 7: £7,686. HBAI net + # income counts the payment and deducts the tax: 50,000 + 200 - 7,686 = + # £42,514. + period: 2025 + input: + people: + pensioner: + age: 70 + private_pension_income: 50_000 + households: + household: + members: [pensioner] + country: ENGLAND + output: + total_income: [50_000] + income_tax_before_winter_fuel_payment_charge: [7_486] + winter_fuel_payment_charge: [200] + income_tax: [7_686] + winter_fuel_allowance: 200 + household_benefits: 200 + hbai_household_net_income: 42_514 + +- name: 2025 the payment is kept at £35,000 of private pension + # £22,430 at 20% = £4,486; no charge. HBAI net income: 35,000 + 200 - + # 4,486 = £30,714. + period: 2025 + input: + people: + pensioner: + age: 70 + private_pension_income: 35_000 + households: + household: + members: [pensioner] + country: ENGLAND + output: + winter_fuel_payment_charge: [0] + income_tax: [4_486] + hbai_household_net_income: 30_714 + +- name: 2025 Scotland's payment is household income + # PAWHP counts in household benefits and HBAI income, like the Winter Fuel + # Payment it replaced in Scotland. Reg. 10(5)(a): £203.40; no charge. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + private_pension_income: 20_000 + households: + household: + members: [pensioner] + country: SCOTLAND + output: + winter_fuel_payment_charge: [0] + pawhp: 203.40 + household_benefits: 203.40 + hbai_benefits: 203.40 diff --git a/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_allowance/taxable_income_test.yaml b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_allowance/taxable_income_test.yaml index be70a4d8a8..8b30f9c78b 100644 --- a/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_allowance/taxable_income_test.yaml +++ b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_allowance/taxable_income_test.yaml @@ -1,3 +1,10 @@ +# The household income test in +# gov.dwp.winter_fuel_payment.eligibility.taxable_income_test is deprecated: +# no regulations set it, and from 2025 the winter fuel payment charge (ITEPA +# 2003 s.681I, gov.hmrc.income_tax.charges.winter_fuel_payment) recovers the +# payment instead. The test still works in a reform that requires a relevant +# benefit (require_benefits), as for the 2024 qualifying week. + - name: 2024 baseline - pensioner without benefits gets no WFA period: 2024 absolute_error_margin: 1 @@ -45,7 +52,10 @@ output: winter_fuel_allowance: 0 -- name: Enable income test - pensioner exactly at threshold gets no WFA +- name: Enable income test - pensioner exactly at threshold gets WFA + # The limit is a maximum: total income at the limit passes, as the winter + # fuel payment charge reaches only total income exceeding £35,000 (ITEPA + # 2003 s.681I(1)(b)). period: 2024 absolute_error_margin: 1 input: @@ -59,7 +69,7 @@ esa_income: 0 jsa_income: 0 output: - winter_fuel_allowance: 0 + winter_fuel_allowance: 200 - name: Enable income test - pensioner £1 under threshold gets WFA period: 2024 @@ -199,4 +209,73 @@ country: "ENGLAND" total_income: 50000 output: - winter_fuel_allowance: 200 \ No newline at end of file + winter_fuel_allowance: 200 + +- name: 2025 baseline - pensioner without benefits is paid, and the charge recovers it above £35,000 + # SI 2025/969 reg. 2 pays everyone of pensionable age; ITEPA 2003 s.681I. + period: 2025 + absolute_error_margin: 1 + input: + age: 67 + country: "ENGLAND" + total_income: 50000 + pension_credit: 0 + income_support: 0 + esa_income: 0 + jsa_income: 0 + output: + winter_fuel_allowance: 200 + winter_fuel_payment_charge: 200 + +- name: 2025 the deprecated household income test has no effect while no relevant benefit is required + period: 2025 + absolute_error_margin: 1 + input: + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.use_maximum_taxable_income: true + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.maximum_taxable_income: 20000 + age: 67 + country: "ENGLAND" + total_income: 50000 + pension_credit: 0 + income_support: 0 + esa_income: 0 + jsa_income: 0 + output: + winter_fuel_allowance: 200 + winter_fuel_payment_charge: 200 + +- name: 2025 the deprecated household income test applies when a reform requires a relevant benefit - over the limit + period: 2025 + absolute_error_margin: 1 + input: + gov.dwp.winter_fuel_payment.eligibility.require_benefits: true + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.use_maximum_taxable_income: true + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.maximum_taxable_income: 20000 + age: 67 + country: "ENGLAND" + total_income: 50000 + pension_credit: 0 + income_support: 0 + esa_income: 0 + jsa_income: 0 + output: + winter_fuel_allowance: 0 + winter_fuel_payment_charge: 0 + +- name: 2025 the deprecated household income test applies when a reform requires a relevant benefit - under the limit + period: 2025 + absolute_error_margin: 1 + input: + gov.dwp.winter_fuel_payment.eligibility.require_benefits: true + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.use_maximum_taxable_income: true + gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.maximum_taxable_income: 20000 + age: 67 + country: "ENGLAND" + total_income: 15000 + pension_credit: 0 + income_support: 0 + esa_income: 0 + jsa_income: 0 + output: + winter_fuel_allowance: 200 + winter_fuel_payment_charge: 0 diff --git a/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml new file mode 100644 index 0000000000..e92b38e9b4 --- /dev/null +++ b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml @@ -0,0 +1,51 @@ +# Reforms to the winter fuel payment charge parameters +# (gov.hmrc.income_tax.charges.winter_fuel_payment). + +- name: A higher income threshold - total income under it is not charged + period: 2025 + input: + gov.hmrc.income_tax.charges.winter_fuel_payment.income_threshold: 50_000 + age: 70 + country: ENGLAND + total_income: 45_000 + pension_credit: 0 + output: + winter_fuel_payment: 200 + winter_fuel_payment_charge: 0 + +- name: A higher income threshold - total income over it is charged + period: 2025 + input: + gov.hmrc.income_tax.charges.winter_fuel_payment.income_threshold: 50_000 + age: 70 + country: ENGLAND + total_income: 50_001 + pension_credit: 0 + output: + winter_fuel_payment: 200 + winter_fuel_payment_charge: 200 + +- name: Repealing the charge leaves the payment with everyone + period: 2025 + input: + gov.hmrc.income_tax.charges.winter_fuel_payment.in_effect: false + age: 70 + country: ENGLAND + total_income: 50_000 + pension_credit: 0 + output: + winter_fuel_payment: 200 + winter_fuel_payment_charge: 0 + +- name: Charging a universal payment for the 2024 qualifying week + period: 2024 + input: + gov.dwp.winter_fuel_payment.eligibility.require_benefits: false + gov.hmrc.income_tax.charges.winter_fuel_payment.in_effect: true + age: 70 + country: ENGLAND + total_income: 50_000 + pension_credit: 0 + output: + winter_fuel_payment: 200 + winter_fuel_payment_charge: 200 diff --git a/policyengine_uk/tests/test_winter_heating_payment_properties.py b/policyengine_uk/tests/test_winter_heating_payment_properties.py index 325878fae5..fd3e84e0f4 100644 --- a/policyengine_uk/tests/test_winter_heating_payment_properties.py +++ b/policyengine_uk/tests/test_winter_heating_payment_properties.py @@ -1,14 +1,22 @@ """The Winter Fuel Payment and the Pension Age Winter Heating Payment follow -each person's own receipt of a relevant benefit. +each person's own receipt of a relevant benefit, and the winter fuel payment +charge follows each person's own total income. Both schemes entitle and pay a person (SI 2000/729 reg 2; SI 2024/869 regs -2 to 4; SI 2025/969 regs 2 to 4; SSI 2024/351 regs 5, 9 and 10). A person is -on a relevant benefit through their own award or, for the claimant and -partner, their couple's award. Properties: +2 to 4; SI 2025/969 regs 2 to 4; NISR 2025/142 regs 2 to 4; SSI 2024/351 +regs 5, 9 and 10). A person is on a relevant benefit through their own award +or, for the claimant and partner, their couple's award. From 2025-26 the +winter fuel payment charge (ITEPA 2003 s.681I) recovers a person's payment +through income tax when their own total income exceeds £35,000 and they are +not entitled to a relevant benefit. Properties: - the model matches a reference implementation written directly from the - regulations, person by person, over households of up to three benefit - units, in every country and in the 2023 to 2026 qualifying weeks; + regulations and s.681I, person by person, over households of up to three + benefit units, in every country and in the 2023 to 2026 qualifying weeks: + payments, charges, and income tax with and without the charge; +- incomes never change anyone's payment, only the charge: entitlement, and + so who counts as another entitled person for the shared amounts, comes + before the charge; - adding a non-dependant under pensionable age, with or without a relevant benefit of their own, never changes what anyone else is paid; - from the 2025 qualifying week, a household whose pension-age members all @@ -35,7 +43,8 @@ # Ages either side of pensionable age (66 to 67 over these years) and of 80. PENSION_AGES = [67, 70, 79, 80, 85] AGES = [40, 60, *PENSION_AGES] -INCOMES = [5_000, 20_000, 50_000] +# Either side of the £35,000 charge threshold, and exactly on it. +INCOMES = [5_000, 20_000, 35_000, 35_001, 50_000] AWARD = 1_000 # The benefit-unit award a couple can be on, and the input that carries it. AWARD_INPUTS = { @@ -93,7 +102,9 @@ # SSI 2026/170 reg 15(3). 2026: (211.15, 316.70, 105.55, 211.15, 158.35), } -INCOME_LIMIT = 35_000 +# ITEPA 2003 s.681I(1)(b) and (5), from the tax year 2025-26. +CHARGE_THRESHOLD = 35_000 +CHARGE_RELEVANT = {"IS", "JSA", "PC", "ESA", "UC"} @st.composite @@ -259,17 +270,11 @@ def scheme(resident, relevant, means_test, amounts): if year <= 2023: wfp_resident = True - wfp_means = True else: wfp_resident = country in ("ENGLAND", "WALES", "NORTHERN_IRELAND") - if year == 2024: - wfp_means = False - else: - # The model's stand-in for the winter fuel payment charge. - wfp_means = country in ("ENGLAND", "WALES") and any( - p[0]["age"] >= 67 and p[0]["total_income"] < INCOME_LIMIT - for p in people - ) + # Only the 2024 regulations (SI 2024/869 reg 2(2)(b); NISR 2024/160) + # made a relevant benefit a condition of entitlement. + wfp_means = year != 2024 wfp = scheme(wfp_resident, WFP_RELEVANT[year], wfp_means, WFP_AMOUNTS) if year >= 2024 and country == "SCOTLAND": pawhp = scheme(True, PAWHP_RELEVANT[year], year >= 2025, PAWHP_AMOUNTS[year]) @@ -278,6 +283,27 @@ def scheme(resident, relevant, means_test, amounts): return wfp, pawhp +def reference_charges(household, year, payments): + """The winter fuel payment charge on each person (ITEPA 2003 s.681I). + + ``payments`` is each person's winter fuel payment of any kind (s.681I(6) + (a)). The charge is the whole payment (s.681I(3)) when the person's own + total income exceeds £35,000 (s.681I(1)(b)) and they are not entitled to + a relevant benefit (s.681I(4) and (5)), from 2025-26. + """ + if year < 2025: + return [0.0] * len(payments) + units = household["units"] + charges = [] + for (inputs, u, cp), payment in zip(household_people(household), payments): + award = units[u]["award"] if cp else inputs.get("own_award") + liable = ( + inputs["total_income"] > CHARGE_THRESHOLD and award not in CHARGE_RELEVANT + ) + charges.append(payment if liable else 0.0) + return charges + + SETTINGS = settings( max_examples=12, deadline=None, @@ -292,14 +318,34 @@ def test_payments_match_reference(year, drawn): sim = Simulation(situation=situation(drawn, year)) wfp = sim.calculate("winter_fuel_payment", year) pawhp = sim.calculate("pension_age_winter_heating_payment", year) + charge = sim.calculate("winter_fuel_payment_charge", year) + income_tax = sim.calculate("income_tax", year) + before_charge = sim.calculate("income_tax_before_winter_fuel_payment_charge", year) wfa_household = sim.calculate("winter_fuel_allowance", year) pawhp_household = sim.calculate("pawhp", year) start = 0 for h, household in enumerate(drawn): expected_wfp, expected_pawhp = reference_payments(household, year) + expected_charge = reference_charges( + household, year, [a + b for a, b in zip(expected_wfp, expected_pawhp)] + ) n = len(expected_wfp) got_wfp = wfp[start : start + n] got_pawhp = pawhp[start : start + n] + got_charge = charge[start : start + n] + assert np.allclose(got_charge, expected_charge, atol=0.005), ( + year, + household, + got_charge, + expected_charge, + ) + # The charge is added to income tax after everything else (ITA 2007 + # s.23 Step 7). + assert np.allclose( + income_tax[start : start + n] - before_charge[start : start + n], + expected_charge, + atol=0.005, + ) assert np.allclose(got_wfp, expected_wfp, atol=0.005), ( year, household, @@ -400,7 +446,7 @@ def one_unit_households(draw): if not any(a["age"] >= 67 for a in adults): adults[0]["age"] = draw(st.sampled_from(PENSION_AGES)) return { - "country": draw(st.sampled_from(["ENGLAND", "WALES", "SCOTLAND"])), + "country": draw(st.sampled_from(COUNTRIES)), "units": [ { "adults": adults, @@ -438,3 +484,49 @@ def test_one_benefit_unit_receives_the_full_amount(year, drawn): else: assert wfa[h] == (300 if any_80 else 200), (year, household) assert pawhp[h] == 0 + + +def with_incomes(household, income): + """The household with every member's total income set to ``income``.""" + units = [ + {**u, "adults": [{**a, "total_income": income} for a in u["adults"]]} + for u in household["units"] + ] + other = household["other"] + if other is not None: + other = {**other, "total_income": income} + return {**household, "units": units, "other": other} + + +@SETTINGS +@given( + st.sampled_from([2025, 2026]), + st.lists(households(), min_size=1, max_size=6), +) +def test_incomes_change_only_the_charge(year, drawn): + """The same households with everyone on £5,000 and everyone on £50,000: + the payments are identical (entitlement, and so the shared amounts, does + not depend on income), nobody on £5,000 is charged, and everyone on + £50,000 who is paid and not on a relevant benefit is charged their whole + payment.""" + low = [with_incomes(h, 5_000) for h in drawn] + high = [with_incomes(h, 50_000) for h in drawn] + sim = Simulation(situation=situation(low + high, year)) + paid = sim.calculate("winter_fuel_payment", year) + sim.calculate( + "pension_age_winter_heating_payment", year + ) + charge = sim.calculate("winter_fuel_payment_charge", year) + relevant = np.zeros_like(charge, dtype=bool) + for variable in [ + "is_on_income_support", + "is_on_income_based_jsa", + "is_on_pension_credit", + "is_on_income_related_esa", + "is_on_universal_credit", + ]: + relevant |= sim.calculate(variable, year).astype(bool) + n = len(paid) // 2 + assert np.allclose(paid[:n], paid[n:], atol=0.005), (year, drawn) + assert np.all(charge[:n] == 0), (year, drawn) + assert np.allclose(charge[n:], np.where(relevant[n:], 0, paid[n:]), atol=0.005) + assert np.all(charge <= paid + 0.005) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py index e90ebf86a6..9a839041f2 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py @@ -65,7 +65,10 @@ def formula(benunit, period, parameters): pension_contributions = ( add_for_members(benunit, period, ["pension_contributions"], members) * 0.5 ) - TAX_COMPONENTS = ["income_tax", "national_insurance"] + TAX_COMPONENTS = [ + "income_tax_before_winter_fuel_payment_charge", + "national_insurance", + ] tax = add_for_members(benunit, period, TAX_COMPONENTS, members) increased_income_reduced_by_tax_and_pensions = ( increased_income - tax - pension_contributions diff --git a/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py b/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py index 17aa562d36..3eb631ad99 100644 --- a/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py +++ b/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py @@ -30,7 +30,10 @@ def formula(benunit, period, parameters): INCOME_COMPONENTS.append("basic_income") income = add_for_members(benunit, period, INCOME_COMPONENTS, members) tax = add_for_members( - benunit, period, ["income_tax", "national_insurance"], members + benunit, + period, + ["income_tax_before_winter_fuel_payment_charge", "national_insurance"], + members, ) income += add_for_members(benunit, period, ["social_security_income"], members) income += benunit("income_support_tariff_income", period) diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py index c27e13c39d..949999022a 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py @@ -26,7 +26,7 @@ def formula(benunit, period, parameters): uc_earned = benunit.sum( benunit.members("employment_income", period) + benunit.members("self_employment_income", period) - - benunit.members("income_tax", period) + - benunit.members("income_tax_before_winter_fuel_payment_charge", period) - benunit.members("national_insurance", period) ) earnings_threshold = 10_152 diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py index 473155bfd2..79debe651f 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py @@ -26,7 +26,7 @@ def formula(benunit, period, parameters): uc_earned = benunit.sum( benunit.members("employment_income", period) + benunit.members("self_employment_income", period) - - benunit.members("income_tax", period) + - benunit.members("income_tax_before_winter_fuel_payment_charge", period) - benunit.members("national_insurance", period) ) earnings_threshold = 10_152 diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py index 0fb1547506..17ac279ac0 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py @@ -26,7 +26,7 @@ def formula(benunit, period, parameters): uc_earned = benunit.sum( benunit.members("employment_income", period) + benunit.members("self_employment_income", period) - - benunit.members("income_tax", period) + - benunit.members("income_tax_before_winter_fuel_payment_charge", period) - benunit.members("national_insurance", period) ) earnings_threshold = 10_152 diff --git a/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_income.py b/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_income.py index 1160800f3a..60381b3033 100644 --- a/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_income.py +++ b/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_income.py @@ -41,7 +41,11 @@ def formula(benunit, period, parameters): person("pension_contributions", period) * is_claimant_or_partner ) tax = benunit.sum( - add(person, period, ["income_tax", "national_insurance"]) + add( + person, + period, + ["income_tax_before_winter_fuel_payment_charge", "national_insurance"], + ) * is_claimant_or_partner ) pen_con_deduction_rate = pc.income.pension_contributions_deduction diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py index 6e496b8681..becb81249d 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py @@ -25,7 +25,12 @@ def formula(benunit, period, parameters): disregards = add_for_members( benunit, period, - ["uc_work_allowance", "tax", "pension_contributions"], + [ + "uc_work_allowance", + "income_tax_before_winter_fuel_payment_charge", + "national_insurance", + "pension_contributions", + ], members, ) return max_(0, personal_gross_earned_income - disregards) diff --git a/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py index b0a404dec5..51c01a5e0d 100644 --- a/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py +++ b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py @@ -6,7 +6,10 @@ class winter_fuel_allowance(Variable): documentation = ( "Winter Fuel Payments made to the members of this household " "(winter_fuel_payment). Payments in Scotland from the 2024 " - "qualifying week are the Pension Age Winter Heating Payment (pawhp)." + "qualifying week are the Pension Age Winter Heating Payment (pawhp). " + "The payments are counted in full: from 2025-26 the winter fuel " + "payment charge recovers them from some members through income tax " + "(winter_fuel_payment_charge)." ) entity = Household definition_period = YEAR diff --git a/policyengine_uk/variables/gov/dwp/winter_fuel_payment.py b/policyengine_uk/variables/gov/dwp/winter_fuel_payment.py index 33fda18f95..13e5205d65 100644 --- a/policyengine_uk/variables/gov/dwp/winter_fuel_payment.py +++ b/policyengine_uk/variables/gov/dwp/winter_fuel_payment.py @@ -11,7 +11,8 @@ class winter_fuel_payment(Variable): "benefit receives the full amount (the higher amount if they or their " "partner have reached 80), once per couple. Anyone else receives the " "full amount if no other person in their household is eligible, and " - "a shared amount otherwise." + "a shared amount otherwise. This is the amount paid, before any " + "winter fuel payment charge (winter_fuel_payment_charge)." ) definition_period = YEAR unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/winter_fuel_payment_eligible.py b/policyengine_uk/variables/gov/dwp/winter_fuel_payment_eligible.py index 12927fd4ad..6f200dd788 100644 --- a/policyengine_uk/variables/gov/dwp/winter_fuel_payment_eligible.py +++ b/policyengine_uk/variables/gov/dwp/winter_fuel_payment_eligible.py @@ -13,16 +13,17 @@ class winter_fuel_payment_eligible(Variable): "Whether this person is entitled to a Winter Fuel Payment: they have " "reached pensionable age and live where the payment is made, they " "are not the partner of the person paid for a couple on a relevant " - "benefit, and they meet the model's means condition. The means " - "condition is a relevant benefit of their own (or their couple's) " - "where gov.dwp.winter_fuel_payment.eligibility.require_benefits is " - "set, as for the 2024 qualifying week (SI 2024/869 reg 2(2)(b)). " - "From 2025 the regulations pay everyone of pensionable age (SI " - "2025/969 reg 2) and the winter fuel payment charge recovers the " - "payment from people with total income over £35,000 who are not on a " - "relevant benefit (ITEPA 2003 s.681I); the model represents the " - "charge with the household income test in " - "gov.dwp.winter_fuel_payment.eligibility.taxable_income_test." + "benefit, and, for the 2024 qualifying week only, they are on a " + "relevant benefit of their own or their couple's (SI 2024/869 reg " + "2(2)(b)). Up to the 2023 week and from the 2025 week everyone of " + "pensionable age is entitled (SI 2000/729 reg 2; SI 2025/969 reg 2; " + "NISR 2025/142 reg 2). From 2025-26 the winter fuel payment charge " + "recovers the payment through income tax from a person whose own " + "total income exceeds £35,000 and who is not on a relevant benefit " + "(winter_fuel_payment_charge); entitlement, and so the shared " + "amounts, are unaffected by it. The deprecated household income test " + "in gov.dwp.winter_fuel_payment.eligibility.taxable_income_test " + "applies only in a reform that requires a relevant benefit." ) definition_period = YEAR reference = ( @@ -31,6 +32,7 @@ class winter_fuel_payment_eligible(Variable): "https://www.legislation.gov.uk/uksi/2024/869/regulation/4", "https://www.legislation.gov.uk/uksi/2025/969/regulation/2", "https://www.legislation.gov.uk/uksi/2025/969/regulation/4", + "https://www.legislation.gov.uk/nisr/2025/142/regulation/2", ) def formula(person, period, parameters): @@ -47,7 +49,7 @@ def formula(person, period, parameters): meets_income_test = ( person.household.any( is_SP_age - & (person("total_income", period) < income_test.maximum_taxable_income) + & (person("total_income", period) <= income_test.maximum_taxable_income) ) & np.isin(country, ["ENGLAND", "WALES"]) & income_test.use_maximum_taxable_income diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.py b/policyengine_uk/variables/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.py new file mode 100644 index 0000000000..c1dab9a309 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.py @@ -0,0 +1,38 @@ +from policyengine_uk.model_api import * + +# The payments s.681I(6)(a) defines as a "winter fuel payment": the Winter +# Fuel Payment in England and Wales (SI 2025/969) and Northern Ireland (NISR +# 2025/142), and Scotland's Pension Age Winter Heating Payment (SSI 2024/351). +WINTER_FUEL_PAYMENTS = [ + "winter_fuel_payment", + "pension_age_winter_heating_payment", +] + + +class winter_fuel_payment_charge(Variable): + value_type = float + entity = Person + label = "winter fuel payment charge" + documentation = ( + "The charge to income tax that recovers a person's winter fuel " + "payment: the whole payment, when the person's own total income for " + "the tax year exceeds the threshold and they are not entitled to a " + "relevant benefit in the qualifying week. It is additional tax at " + "Step 7 of the income tax calculation, so tax reductions do not " + "offset it. The payment itself, and who counts as another entitled " + "person for the shared amounts, are unaffected by the charge." + ) + definition_period = YEAR + unit = GBP + reference = ( + "https://www.legislation.gov.uk/ukpga/2003/1/section/681I", + "https://www.legislation.gov.uk/ukpga/2007/3/section/30", + "https://www.legislation.gov.uk/ukpga/2026/11/schedule/10", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.hmrc.income_tax.charges.winter_fuel_payment + payment = add(person, period, WINTER_FUEL_PAYMENTS) + over_threshold = person("total_income", period) > p.income_threshold + on_relevant_benefit = add(person, period, p.relevant_benefits) > 0 + return p.in_effect * payment * over_threshold * ~on_relevant_benefit diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/income_tax.py b/policyengine_uk/variables/gov/hmrc/income_tax/income_tax.py index 8a1596bc94..352fb5396e 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/income_tax.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/income_tax.py @@ -5,19 +5,25 @@ class income_tax(Variable): value_type = float entity = Person label = "Income Tax" - documentation = "Total Income Tax liability" + documentation = ( + "Total Income Tax liability: the liability before the winter fuel " + "payment charge, plus the charge, which is additional tax added at " + "Step 7 after tax reductions." + ) definition_period = YEAR unit = GBP - reference = dict( - title="Income Tax Act 2007 s. 23", - href="https://www.legislation.gov.uk/ukpga/2007/3/section/23", + reference = ( + dict( + title="Income Tax Act 2007 s. 23", + href="https://www.legislation.gov.uk/ukpga/2007/3/section/23", + ), + dict( + title="Income Tax Act 2007 s. 30", + href="https://www.legislation.gov.uk/ukpga/2007/3/section/30", + ), ) category = TAX - - def formula(person, period, parameters): - p = parameters(period).gov.hmrc.income_tax - - additions = add(person, period, p.income_tax_additions) - subtractions = add(person, period, p.income_tax_subtractions) - - return max_(0, additions - subtractions) + adds = [ + "income_tax_before_winter_fuel_payment_charge", + "winter_fuel_payment_charge", + ] diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/income_tax_before_winter_fuel_payment_charge.py b/policyengine_uk/variables/gov/hmrc/income_tax/income_tax_before_winter_fuel_payment_charge.py new file mode 100644 index 0000000000..dfd8b453d9 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/income_tax_before_winter_fuel_payment_charge.py @@ -0,0 +1,28 @@ +from policyengine_uk.model_api import * + + +class income_tax_before_winter_fuel_payment_charge(Variable): + value_type = float + entity = Person + label = "Income Tax before the winter fuel payment charge" + documentation = ( + "Income Tax liability before the winter fuel payment charge is added " + "at Step 7. The means tests deduct this rather than income_tax: the " + "charge is not tax on the income they assess but the recovery of a " + "winter fuel payment, and it depends on receipt of Pension Credit " + "and Universal Credit, so deducting it would make the model circular." + ) + definition_period = YEAR + unit = GBP + reference = dict( + title="Income Tax Act 2007 s. 23", + href="https://www.legislation.gov.uk/ukpga/2007/3/section/23", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.hmrc.income_tax + + additions = add(person, period, p.income_tax_additions) + subtractions = add(person, period, p.income_tax_subtractions) + + return max_(0, additions - subtractions) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py index 2d20faa8a1..fa49f6adcf 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py @@ -66,6 +66,9 @@ def formula(benunit, period, parameters): add_for_members(benunit, period, ["pension_contributions"], members) * 0.5 ) tax = add_for_members( - benunit, period, ["income_tax", "national_insurance"], members + benunit, + period, + ["income_tax_before_winter_fuel_payment_charge", "national_insurance"], + members, ) return max_(0, increased_income - tax - pension_contributions) diff --git a/policyengine_uk/variables/gov/social_security_scotland/pawhp.py b/policyengine_uk/variables/gov/social_security_scotland/pawhp.py index d1c565a7e7..a282bdbc62 100644 --- a/policyengine_uk/variables/gov/social_security_scotland/pawhp.py +++ b/policyengine_uk/variables/gov/social_security_scotland/pawhp.py @@ -5,7 +5,10 @@ class pawhp(Variable): label = "Pension Age Winter Heating Payment" documentation = ( "Pension Age Winter Heating Payments made to the members of this " - "household (pension_age_winter_heating_payment)." + "household (pension_age_winter_heating_payment). The payments are " + "counted in full: from 2025-26 the winter fuel payment charge " + "recovers them from some members through income tax " + "(winter_fuel_payment_charge)." ) entity = Household definition_period = YEAR diff --git a/policyengine_uk/variables/gov/social_security_scotland/pension_age_winter_heating_payment.py b/policyengine_uk/variables/gov/social_security_scotland/pension_age_winter_heating_payment.py index aa3e0c0cca..c869030212 100644 --- a/policyengine_uk/variables/gov/social_security_scotland/pension_age_winter_heating_payment.py +++ b/policyengine_uk/variables/gov/social_security_scotland/pension_age_winter_heating_payment.py @@ -12,7 +12,8 @@ class pension_age_winter_heating_payment(Variable): "(the higher amount if they or their partner have reached 80), once " "per couple. From the 2025 qualifying week anyone else receives the " "full amount if they do not live with another entitled individual, " - "and a shared amount otherwise." + "and a shared amount otherwise. This is the amount paid, before any " + "winter fuel payment charge (winter_fuel_payment_charge)." ) definition_period = YEAR unit = GBP diff --git a/policyengine_uk/variables/household/income/hbai_benefits.py b/policyengine_uk/variables/household/income/hbai_benefits.py index 0adf25977d..2edd99bfda 100644 --- a/policyengine_uk/variables/household/income/hbai_benefits.py +++ b/policyengine_uk/variables/household/income/hbai_benefits.py @@ -35,6 +35,7 @@ class hbai_benefits(Variable): "ssmg", "cost_of_living_support_payment", "winter_fuel_allowance", + "pawhp", "tax_free_childcare", "childcare_grant", "parents_learning_allowance", diff --git a/policyengine_uk/variables/household/income/hbai_household_net_income.py b/policyengine_uk/variables/household/income/hbai_household_net_income.py index f3272ebc5b..aedfb3e19a 100644 --- a/policyengine_uk/variables/household/income/hbai_household_net_income.py +++ b/policyengine_uk/variables/household/income/hbai_household_net_income.py @@ -45,6 +45,7 @@ "ssmg", "cost_of_living_support_payment", "winter_fuel_allowance", + "pawhp", "tax_free_childcare", "healthy_start_vouchers", "scottish_child_payment", diff --git a/policyengine_uk/variables/household/income/household_benefits.py b/policyengine_uk/variables/household/income/household_benefits.py index 77f35aaa7d..e69f62ec76 100644 --- a/policyengine_uk/variables/household/income/household_benefits.py +++ b/policyengine_uk/variables/household/income/household_benefits.py @@ -34,6 +34,7 @@ "cost_of_living_support_payment", "energy_bills_rebate", "winter_fuel_allowance", + "pawhp", "tax_free_childcare", "extended_childcare_entitlement", "universal_childcare_entitlement", From eeb4698a8ac0d4c97d176c9cf0cea9dc0352d195 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 08:30:52 -0400 Subject: [PATCH 2/6] Test that tax credits do not exempt a person from the winter fuel payment charge ITEPA 2003 s.681I(5) lists five relevant benefits and no tax credits. A mutant that exempted tax credit recipients survived the other cases. Co-Authored-By: Claude Opus 5.5 --- .../charge_parameters.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml index e92b38e9b4..2a5c82c656 100644 --- a/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml +++ b/policyengine_uk/tests/policy/reforms/parametric/winter_fuel_payment_charge/charge_parameters.yaml @@ -49,3 +49,21 @@ output: winter_fuel_payment: 200 winter_fuel_payment_charge: 200 + +- name: Tax credits do not exempt a person from the charge + # Charging the 2024 qualifying week's payments: a tax credit award of at + # least £26 entitled a person to that payment (SI 2024/869 reg. 2(2)(b) + # (ii)), but tax credits are not among the relevant benefits of ITEPA 2003 + # s.681I(5), so the person is charged. + period: 2024 + input: + gov.hmrc.income_tax.charges.winter_fuel_payment.in_effect: true + age: 70 + country: ENGLAND + total_income: 50_000 + pension_credit: 0 + tax_credits: 1_000 + output: + is_on_winter_fuel_payment_relevant_benefit: true + winter_fuel_payment: 200 + winter_fuel_payment_charge: 200 From 4cdedd604e76cf8cc0e7484a68f0a8e1c6fa5130 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 12:47:13 -0400 Subject: [PATCH 3/6] Say why the property allowance route compares tax before the charge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The model's total_income is before the property allowance, so the charge is the same on either route; reading income_tax in the route branches would only recompute the means-tested benefits there (a probe with income_tax in the route made the same choice and raised no CycleError). Correct the YAML case's comment to match: total income is £35,300 on either route. Co-Authored-By: Claude Opus 5.5 --- .../charges/winter_fuel_payment_charge.yaml | 18 ++++++++---------- .../allowances/uses_property_allowance.py | 11 ++++++----- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml index f03d8a811c..6f6f0d2842 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml @@ -512,16 +512,14 @@ household_benefits: 203.40 hbai_benefits: 203.40 -- name: 2025 the property allowance election compares tax before the charge - # Private pension £32,000; property receipts £3,500, expenses £200, finance - # costs £1,500. With expenses: total income £35,300, tax 22,730 x 20% = - # £4,546 less the finance-cost reduction 20% x £1,500 = £300, so £4,246. - # With partial relief (£1,000 allowance, no reduction): total income - # £34,500, tax 21,930 x 20% = £4,386. The election lowers the tax before the - # charge only by keeping expenses, so they are kept. The charge depends on - # Pension Credit, which depends on property income and so on this choice, - # so the election does not weigh it: total income £35,300 is charged £200, - # which partial relief would have avoided. +- name: 2025 a landlord's property allowance election is made before the charge + # Private pension £32,000; property receipts £3,500, expenses £200 (profit + # £3,300), finance costs £1,500. With expenses: (35,300 - 12,570) x 20% = + # £4,546, less the finance-cost reduction 20% x £1,500 = £300, so £4,246. + # With partial relief (£1,000 allowance for £200 of expenses, no + # reduction): (34,500 - 12,570) x 20% = £4,386. Expenses are kept. The + # model's total_income is before the property allowance, £35,300 on either + # route, so the £200 charge is added on either. period: 2025 input: people: diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/uses_property_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/uses_property_allowance.py index 8aa5a649f9..f63250b5c8 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/uses_property_allowance.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/uses_property_allowance.py @@ -22,11 +22,12 @@ def income_tax_with_route(simulation, name, period, uses_allowance): """Each person's income tax with the given choices of property allowance. - The tax is before the winter fuel payment charge: the charge depends on - receipt of Pension Credit and Universal Credit, which depend on property - income and so on this choice. The branch shares the simulation's cached - arrays until it writes, and is dropped afterwards so a later period - starts from a fresh branch. + The tax is before the winter fuel payment charge. The charge turns on + total_income, which is before the property allowance and so the same on + either route, and on receipt of means-tested benefits, which reading it + here would compute again in each route's branch. The branch shares the + simulation's cached arrays until it writes, and is dropped afterwards so + a later period starts from a fresh branch. """ while name in simulation.branches or name == simulation.branch_name: name += "_" From 03154a1e9004a2679570bd0350db4ec0b58fb498 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 13:59:20 -0400 Subject: [PATCH 4/6] Enter only the Income Support award in the IS partner case Without universal_credit: 0 the model computes a Universal Credit award for the couple, which makes both members joint claimants and exempts the partner. Zero Universal Credit and Pension Credit, as the other cases do. Co-Authored-By: Claude Opus 5.5 --- .../hmrc/income_tax/charges/winter_fuel_payment_charge.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml index 6f6f0d2842..9951c75687 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml @@ -285,6 +285,10 @@ couple: members: [claimant, partner] income_support: 1_000 + # Only the Income Support award: a Universal Credit award would make + # both members joint claimants, entitled to a relevant benefit. + universal_credit: 0 + pension_credit: 0 households: household: members: [claimant, partner] From aede16d237c02f3f0e54312f05a0787a87218538 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:25:58 -0400 Subject: [PATCH 5/6] Compare the means-test property tests with tax before the charge Three property tests that CI failed on 03154a1e9 compared a means test with income_tax, which now includes the winter fuel payment charge: - test_uc_earnings_deductions_properties: the pre-#1942 UC earned income formula deducted benunit_tax, which now reads the charge and so UC receipt (CycleError). It deducts the benefit unit's income tax before the charge and its NI instead. - test_council_tax_reduction_carer_income_properties and test_housing_benefit_savings_credit_only_properties: CTR and HB income deduct income_tax_before_winter_fuel_payment_charge, so the identities add back that liability, not income_tax. The HB boundary test also excludes the charge, as it excludes the other payments that depend on Pension Credit receipt. A YAML case pins CTR income to the tax before the charge, which no YAML case covered. Co-Authored-By: Claude Opus 5.5 --- .../charges/winter_fuel_payment_charge.yaml | 18 +++++++++++ ...l_tax_reduction_carer_income_properties.py | 17 ++++++---- ..._benefit_savings_credit_only_properties.py | 31 +++++++++++++------ .../test_uc_earnings_deductions_properties.py | 11 ++++++- 4 files changed, 61 insertions(+), 16 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml index 9951c75687..bc02de5e3b 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml @@ -478,6 +478,24 @@ household_benefits: 200 hbai_household_net_income: 42_514 +- name: 2025 Council Tax Reduction income deducts income tax before the charge + # The pensioner above. Council Tax Reduction income under the general rules + # deducts income tax and National Insurance, but not the charge, which + # recovers the payment: 50,000 - 7,486 = £42,514, not 50,000 - 7,686. + period: 2025 + input: + people: + pensioner: + age: 70 + private_pension_income: 50_000 + households: + household: + members: [pensioner] + country: ENGLAND + output: + winter_fuel_payment_charge: [200] + council_tax_reduction_applicable_income: [42_514] + - name: 2025 the payment is kept at £35,000 of private pension # £22,430 at 20% = £4,486; no charge. HBAI net income: 35,000 + 200 - # 4,486 = £30,714. diff --git a/policyengine_uk/tests/test_council_tax_reduction_carer_income_properties.py b/policyengine_uk/tests/test_council_tax_reduction_carer_income_properties.py index 8795cdd8c6..0f698c3dce 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_carer_income_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_carer_income_properties.py @@ -20,7 +20,9 @@ all of a family's income tax from its Council Tax Reduction income, an existing approximation, so there the supplement, which is taxable (SI 2026/93), reaches that income through the tax charged on it and in no other -way. +way. The tax deducted is the liability before the winter fuel payment charge +(ITEPA 2003 s. 681I), which recovers a winter payment rather than taxing the +income assessed; "income tax" below means that liability. Invariants, for single people and couples in Scotland in 2026 in which one member is a carer, all of working age and taking no Universal Credit or all @@ -82,6 +84,9 @@ CARER_SUPPORT_PAYMENT = 86.45 * WEEKS SCOTTISH_CARER_SUPPLEMENT = 11.70 * WEEKS WITHDRAWAL_RATE = 0.2 +# The income tax Council Tax Reduction income deducts: the liability before +# the winter fuel payment charge. +INCOME_TAX = "income_tax_before_winter_fuel_payment_charge" PROPERTY_SETTINGS = settings( max_examples=10, deadline=None, @@ -128,7 +133,7 @@ "carer_support_payment", "carer_support_payment_pre_overlap", "scottish_carer_supplement", - "income_tax", + INCOME_TAX, "national_insurance", ] CLAIMS = (True, False) @@ -230,7 +235,7 @@ def grid(families, reform=None): # from the other benefits it counts. g["income_before_tax"] = ( g["council_tax_reduction_applicable_income"] - + g["income_tax"] + + g[INCOME_TAX] + g["national_insurance"] - g["other_counted_benefits"] ) @@ -330,8 +335,8 @@ def test_council_tax_reduction_income_ignores_the_scottish_carer_supplement( compared = (income > 0) & (income_without > 0) & general assert compared.any(), fam assert np.allclose( - (income + g["income_tax"][i])[compared], - (income_without + without["income_tax"][i])[compared], + (income + g[INCOME_TAX][i])[compared], + (income_without + without[INCOME_TAX][i])[compared], atol=0.05, ), fam award = g["council_tax_benefit"][i] @@ -341,7 +346,7 @@ def test_council_tax_reduction_income_ignores_the_scottish_carer_supplement( # follows invariant 4). assert np.all(award >= award_without - 0.01), fam both_partial = partial(g, i) & partial(without, i) & general - tax_on_supplement = g["income_tax"][i] - without["income_tax"][i] + tax_on_supplement = g[INCOME_TAX][i] - without[INCOME_TAX][i] assert np.allclose( (award - award_without)[both_partial], WITHDRAWAL_RATE * tax_on_supplement[both_partial], diff --git a/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py b/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py index 6d19738615..7ff84bd23a 100644 --- a/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py @@ -27,19 +27,24 @@ award at both ends, household net income, excluding the TV licence, does not fall. 2. Boundary: on the step where savings credit ends, household net income does - not fall, excluding the TV licence, the Winter Fuel Payment and the - Scottish Pension Age Winter Heating Payment, which the model makes depend - on receiving Pension Credit (the free licence at 75, the Scottish payment's - Pension Credit rates, and the Winter Fuel Payment above £35,000 of taxable - income). Housing Benefit switches from the Secretary of State's figure to - its own rules there. + not fall, excluding the TV licence, the Scottish Pension Age Winter + Heating Payment and the winter fuel payment charge, which the model makes + depend on receiving Pension Credit (the free licence at 75, the Scottish + payment's Pension Credit rates, and the charge, from which Pension Credit + exempts a person with more than £35,000 of total income: ITEPA 2003 s. + 681I), and the Winter Fuel Payment, which depended on it before 2025-26. + Housing Benefit switches from the Secretary of State's figure to its own + rules there. 3. Differential: a savings-credit-only family's Housing Benefit income is the Pension Credit income, recomputed here from pensions, income tax and the deemed income on savings, plus the savings credit, less the earnings disregard; its tariff income is zero and its capital is its savings. Other families keep the Housing Benefit rules: zero income with a guarantee credit, otherwise pensions less income tax plus tariff income less the - earnings disregard. + earnings disregard. The income tax both routes deduct is the liability + before the winter fuel payment charge (ITEPA 2003 s. 681I), which recovers + the Winter Fuel Payment or the Scottish payment rather than taxing the + pensions. Carers are not drawn. """ @@ -176,7 +181,12 @@ def grid(families): variable: np.asarray(simulation.calculate(variable, YEAR)).reshape(shape) for variable in BENUNIT_VARIABLES + HOUSEHOLD_VARIABLES } - for variable in ["state_pension", "private_pension_income", "income_tax"]: + for variable in [ + "state_pension", + "private_pension_income", + "income_tax_before_winter_fuel_payment_charge", + "winter_fuel_payment_charge", + ]: values[variable] = np.asarray( simulation.calculate(variable, YEAR, map_to="benunit") ).reshape(shape) @@ -230,6 +240,7 @@ def test_net_income_does_not_fall_where_savings_credit_ends(families): + g["tv_licence"] - g["winter_fuel_allowance"] - g["pawhp"] + + g["winter_fuel_payment_charge"] ) step = np.diff(net, axis=1) ends = g["savings_credit_only"][:, :-1] & (g["pension_credit"][:, 1:] <= 0) @@ -254,7 +265,9 @@ def test_income_follows_the_route_for_the_award(families): savings = np.array([fam["savings"] for fam in families])[:, None] capital_income = deemed_income(savings) pensions_after_tax = ( - g["state_pension"] + g["private_pension_income"] - g["income_tax"] + g["state_pension"] + + g["private_pension_income"] + - g["income_tax_before_winter_fuel_payment_charge"] ) disregard = g["housing_benefit_applicable_income_disregard"] sc_only = g["savings_credit_only"] diff --git a/policyengine_uk/tests/test_uc_earnings_deductions_properties.py b/policyengine_uk/tests/test_uc_earnings_deductions_properties.py index bc961fe791..6032a8c3b5 100644 --- a/policyengine_uk/tests/test_uc_earnings_deductions_properties.py +++ b/policyengine_uk/tests/test_uc_earnings_deductions_properties.py @@ -292,10 +292,19 @@ def formula(benunit, period, parameters): period, ["employment_income", "self_employment_income", "miscellaneous_income"], ) + # The whole benefit unit's tax, as benunit_tax was before the + # winter fuel payment charge joined income tax: the charge depends + # on Universal Credit receipt, so deducting it here would be + # circular. disregards = add( benunit, period, - ["uc_work_allowance", "benunit_tax", "pension_contributions"], + [ + "uc_work_allowance", + "income_tax_before_winter_fuel_payment_charge", + "national_insurance", + "pension_contributions", + ], ) return max_(0, gross - disregards) From 97e0c57fa798f662b7dc76720b64b6352b667911 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 08:40:48 -0400 Subject: [PATCH 6/6] Compare the UC and CTR property tests with tax before the charge; tidy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review r3 (lost to the 10/12 reboot) found two properties false inside their own generators on aede16d23, both comparing a means test with income_tax, which now includes the winter fuel payment charge: - test_deductions_equal_tax_and_ni_on_earnings_alone: a pensioner in a mixed-age couple earning £40,000 off UC is charged £200. UC deducts tax on earnings (reg 55(5)), so the test compares with the tax before the charge. - check_routes in the CTR Pension Credit properties: MAIN_DEDUCTIONS now uses the tax before the charge, as the CTR means test does. Both counterexamples are pinned as plain tests that also assert the charge is levied. Also: - the inert income_tax: 0 inputs in two means-test YAML files now set the variable the means tests read; - the property-allowance YAML case is labelled a known gap: the law's partial relief would bring total income under £35,000; - s.681J links to ITEPA, not FA 2026; - the changelog no longer says the household test "charged" anyone; - the HB property's reason for excluding the WFP fits its 2026 year. Co-Authored-By: Claude Opus 5.5 --- .../winter-fuel-payment-charge.fixed.md | 2 +- .../winter_fuel_payment/income_threshold.yaml | 2 +- ...ousing_benefit_pension_age_means_test.yaml | 4 +- .../claimant_partner_income_and_carers.yaml | 10 ++-- .../charges/winter_fuel_payment_charge.yaml | 11 +++-- ...tax_reduction_pension_credit_properties.py | 33 ++++++++++++- ..._benefit_savings_credit_only_properties.py | 3 +- .../test_uc_earnings_deductions_properties.py | 47 ++++++++++++++++++- 8 files changed, 96 insertions(+), 16 deletions(-) diff --git a/changelog.d/winter-fuel-payment-charge.fixed.md b/changelog.d/winter-fuel-payment-charge.fixed.md index 97b3a984df..6d862676e3 100644 --- a/changelog.d/winter-fuel-payment-charge.fixed.md +++ b/changelog.d/winter-fuel-payment-charge.fixed.md @@ -1,2 +1,2 @@ -- From the 2025 qualifying week, pay the Winter Fuel Payment to everyone of pensionable age in England, Wales and Northern Ireland (SI 2025/969 reg. 2; NISR 2025/142 reg. 2) and recover it through the winter fuel payment charge, in place of a household income test. The test paid a whole household if any member of pensionable age had total income under £35,000. It applied only in England and Wales, and it charged nobody in Northern Ireland or on the Pension Age Winter Heating Payment. Its boundary was also wrong: total income of exactly £35,000 is not charged. +- From the 2025 qualifying week, pay the Winter Fuel Payment to everyone of pensionable age in England, Wales and Northern Ireland (SI 2025/969 reg. 2; NISR 2025/142 reg. 2) and recover it through the winter fuel payment charge, in place of a household income test. The test paid a whole household if any member of pensionable age had total income under £35,000. It applied only in England and Wales, so pensioners in Northern Ireland not on a relevant benefit were paid nothing, and nothing stood in for the charge on the Pension Age Winter Heating Payment. Its boundary was also wrong: total income of exactly £35,000 is not charged. - Count the Pension Age Winter Heating Payment in `household_benefits`, `hbai_household_net_income` and `hbai_benefits`, as the Winter Fuel Payment is. diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml index f82887986d..e2e1f93805 100644 --- a/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/charges/winter_fuel_payment/income_threshold.yaml @@ -6,7 +6,7 @@ values: - title: Income Tax (Earnings and Pensions) Act 2003, s. 681I(1)(b), inserted by Finance Act 2026, Sch. 10 para. 1 href: https://www.legislation.gov.uk/ukpga/2003/1/section/681I - title: Income Tax (Earnings and Pensions) Act 2003, s. 681J (alteration of income limit by Treasury order) - href: https://www.legislation.gov.uk/ukpga/2026/11/schedule/10 + href: https://www.legislation.gov.uk/ukpga/2003/1/section/681J metadata: unit: currency-GBP period: year diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/housing_benefit_pension_age_means_test.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/housing_benefit_pension_age_means_test.yaml index 4e721cbe6b..6f0d4263ec 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/housing_benefit_pension_age_means_test.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/housing_benefit_pension_age_means_test.yaml @@ -82,7 +82,7 @@ months_since_last_birthday: 4 is_male: true employment_income: 1_000 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 benunits: @@ -123,7 +123,7 @@ months_since_last_birthday: 4 is_male: true employment_income: 1_000 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 benunits: diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml index 6602c17a7d..9d58b08e50 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_income_and_carers.yaml @@ -6,7 +6,7 @@ claimant: age: 75 is_parent: true - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 young_person: @@ -52,7 +52,7 @@ age: 75 is_parent: true state_pension: 8_000 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 young_person: @@ -82,7 +82,7 @@ age: 75 is_parent: true state_pension: 10_000 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 young_person: @@ -114,7 +114,7 @@ is_parent: true state_pension: 8_000 basic_income: 100 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 young_person: @@ -197,7 +197,7 @@ age: 75 is_parent: true state_pension: 10_000 - income_tax: 0 + income_tax_before_winter_fuel_payment_charge: 0 national_insurance: 0 pension_contributions: 0 young_person: diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml index bc02de5e3b..2ac8ffbe10 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/charges/winter_fuel_payment_charge.yaml @@ -12,7 +12,8 @@ # 2007 ss.23 and 30). # The payments are worked from SI 2025/969 regs 2 to 4, NISR 2025/142 regs 2 # and 3, and SSI 2024/351 reg 10 as substituted by SSI 2025/282 (amounts from -# SSI 2025/100 and SSI 2026/170). Entitlement to a payment, and so who counts +# SSI 2025/100 and SSI 2026/170). One case, marked "model, not law", pins a +# known gap instead. Entitlement to a payment, and so who counts # as "a person entitled to a winter fuel payment" for the shared amounts, does # not depend on the charge. Benefit-unit awards are entered directly. @@ -534,14 +535,18 @@ household_benefits: 203.40 hbai_benefits: 203.40 -- name: 2025 a landlord's property allowance election is made before the charge +- name: 2025 a landlord's property allowance election is made before the charge (model, not law) # Private pension £32,000; property receipts £3,500, expenses £200 (profit # £3,300), finance costs £1,500. With expenses: (35,300 - 12,570) x 20% = # £4,546, less the finance-cost reduction 20% x £1,500 = £300, so £4,246. # With partial relief (£1,000 allowance for £200 of expenses, no # reduction): (34,500 - 12,570) x 20% = £4,386. Expenses are kept. The # model's total_income is before the property allowance, £35,300 on either - # route, so the £200 charge is added on either. + # route, so the £200 charge is added on either: £4,446. + # KNOWN GAP, pinned on purpose: under the law, partial relief puts total + # income at £34,500, under the threshold, so a landlord would elect it and + # pay £4,386 with no charge. The model does not deduct the property or + # trading allowance from total_income (see the PR's Limits). period: 2025 input: people: diff --git a/policyengine_uk/tests/test_council_tax_reduction_pension_credit_properties.py b/policyengine_uk/tests/test_council_tax_reduction_pension_credit_properties.py index e6d1de97c7..e443985ad3 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_pension_credit_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_pension_credit_properties.py @@ -285,7 +285,10 @@ def situation( "esa_income", "universal_credit", ] -MAIN_DEDUCTIONS = ["income_tax", "national_insurance"] +# The means tests deduct income tax before the winter fuel payment charge +# (ITEPA 2003 s.681I), which recovers a payment rather than taxing the income +# they assess. A pensioner over £35,000 of total income is charged it. +MAIN_DEDUCTIONS = ["income_tax_before_winter_fuel_payment_charge", "national_insurance"] # The pensioner earnings disregards in 2026, written out from the law, not # read from the parameters: £25 a week for a lone parent, £10 with a partner, # £5 otherwise, plus £17.10 (SI 2012/2885 Sch 4 paras 2, 8 and 10; WSI @@ -511,6 +514,34 @@ def test_not_claiming_pension_credit_never_raises_a_recipients_ctr(units, guaran ), unit +def test_ctr_income_deducts_tax_before_the_winter_fuel_payment_charge(): + """A pensioner with £39,500 of total income is charged the winter fuel + payment (ITEPA 2003 s.681I) through income tax. CTR deducts the tax + before the charge, so the routes agree with the main definition.""" + family = dict( + shape="single_pension", + ages=[70], + country="ENGLAND", + council_tax=4_000.0, + savings=0.0, + other_property=0.0, + state_pension=25_000.0, + private_pension=5_000.0, + employment_income=0.0, + self_employment_income=0.0, + property_income=5_000.0, + carers_allowance=4_500.0, + child_ages=[], + would_claim_uc=False, + would_claim_pc=True, + non_dependant_income=None, + ) + data, _ = situation([family]) + sim = Simulation(situation=data) + assert sim.calculate("winter_fuel_payment_charge", YEAR).sum() > 0 + check_routes([family], calculate([family])) + + def test_savings_credit_only_award_counts_the_frozen_savings_credit(): # A savings credit rate rise raises the reform's savings credit from # 558.62 to 869.89, but the freeze keeps paying the baseline 558.62, and diff --git a/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py b/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py index 7ff84bd23a..0fcb74c3f0 100644 --- a/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_savings_credit_only_properties.py @@ -32,7 +32,8 @@ depend on receiving Pension Credit (the free licence at 75, the Scottish payment's Pension Credit rates, and the charge, from which Pension Credit exempts a person with more than £35,000 of total income: ITEPA 2003 s. - 681I), and the Winter Fuel Payment, which depended on it before 2025-26. + 681I), and the Winter Fuel Payment, whose amount depends on it (one + payment for a couple on a relevant benefit, shared amounts otherwise). Housing Benefit switches from the Secretary of State's figure to its own rules there. 3. Differential: a savings-credit-only family's Housing Benefit income is the diff --git a/policyengine_uk/tests/test_uc_earnings_deductions_properties.py b/policyengine_uk/tests/test_uc_earnings_deductions_properties.py index 6032a8c3b5..64d185e41d 100644 --- a/policyengine_uk/tests/test_uc_earnings_deductions_properties.py +++ b/policyengine_uk/tests/test_uc_earnings_deductions_properties.py @@ -97,6 +97,7 @@ "uc_income_tax_on_earnings", "uc_national_insurance_on_earnings", "income_tax", + "income_tax_before_winter_fuel_payment_charge", "national_insurance", ] @@ -259,9 +260,13 @@ def test_deductions_equal_tax_and_ni_on_earnings_alone(units, year): full = calculate(units, year) alone = calculate(units, year, earnings_only=True) adult = full["is_adult_person"] + # Income tax in respect of earnings (UC Regs 2013 reg 55(5)): the + # liability before the winter fuel payment charge, which recovers a + # payment rather than taxing the earnings. A pensioner of a mixed-age + # couple earning over £35,000 off UC is charged it. np.testing.assert_allclose( full["uc_income_tax_on_earnings"][adult], - alone["income_tax"][adult], + alone["income_tax_before_winter_fuel_payment_charge"][adult], atol=0.01, err_msg=str(units), ) @@ -272,12 +277,50 @@ def test_deductions_equal_tax_and_ni_on_earnings_alone(units, year): err_msg=str(units), ) # Never more than the person's own tax and NI. - assert np.all(full["uc_income_tax_on_earnings"] <= full["income_tax"] + 0.01) + assert np.all( + full["uc_income_tax_on_earnings"] + <= full["income_tax_before_winter_fuel_payment_charge"] + 0.01 + ) assert np.all( full["uc_national_insurance_on_earnings"] <= full["national_insurance"] + 0.01 ) +def test_earnings_deduction_excludes_the_winter_fuel_payment_charge(): + """A pensioner in a mixed-age couple earning £40,000 off UC is charged + the winter fuel payment (ITEPA 2003 s.681I) through income tax. UC + deducts the tax on their earnings, which the charge is not part of.""" + units = [ + dict( + adults=[ + dict( + age=70, + employment_income=30_000.0, + self_employment_income=10_000.0, + uc_is_in_startup_period=False, + married_couples_allowance=0.0, + ), + dict( + age=30, + employment_income=0.0, + self_employment_income=0.0, + uc_is_in_startup_period=False, + married_couples_allowance=0.0, + ), + ], + children=[], + tenure="OWNED_OUTRIGHT", + rent=0.0, + region="LONDON", + ) + ] + sim = Simulation(situation=situation(units, 2026, earnings_only=True)) + assert sim.calculate("winter_fuel_payment_charge", 2026)[0] > 0 + test_deductions_equal_tax_and_ni_on_earnings_alone.hypothesis.inner_test( + units, 2026 + ) + + def _use_formula_before_1942(simulation): class uc_earned_income(Variable): value_type = float