Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/1818.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Benefit cap earnings exception (Universal Credit Regulations 2013 reg. 82) now deducts only the tax attributable to earnings, rather than the claimant's whole income tax liability, so property, savings, dividend and pension income no longer erode net earnings and wrongly remove the exception. Relievable pension contributions are now deducted as reg. 55(5) requires.
- Moved the benefit cap earnings threshold out of three hardcoded copies of `10_152` into the dated parameter `gov.dwp.benefit_cap_earnings_exemption`, with values from 2020-21 to 2026-27 (2026-27 rises to £881 a month) derived from 16 hours a week at the National Living Wage.
- Armed Forces Independence Payment now excepts a benefit unit from the benefit cap, as GOV.UK and the regulations provide. The variable has no dataset source, so this changes household calculations only.
- Removed dead copy-pasted code from the three `is_benefit_cap_exempt_*` variables, which computed exemption conditions that were never returned.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
description: Monthly earned income at or above which a Universal Credit claimant (or couple, jointly) is excepted from the benefit cap. Set by regulation 82 of the Universal Credit Regulations 2013 as 16 hours per week at the National Living Wage rate in regulation 4 of the National Minimum Wage Regulations 2015, converted to a monthly amount by multiplying by 52 and dividing by 12, and rounded down to whole pounds by DWP. Earned income for this test is net of income tax, National Insurance contributions and relievable pension contributions on the earnings, and excludes income treated as earned under the minimum income floor.
values:
2020-04-01: 604
2021-04-01: 617
2022-04-01: 658
2023-04-01: 722
2024-04-01: 793
2025-04-01: 846
2026-04-01: 881
metadata:
label: Benefit cap earnings exemption threshold
unit: currency-GBP
period: month
reference:
- title: The Universal Credit Regulations 2013 reg. 82 (Exceptions - earnings)
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/82
- title: The Universal Credit Regulations 2013 reg. 55 (Employed earnings)
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/55
- title: GOV.UK - Benefit cap - When you're not affected
href: https://www.gov.uk/benefit-cap/when-youre-not-affected
- title: GOV.UK - National Minimum Wage and National Living Wage rates
href: https://www.gov.uk/national-minimum-wage-rates
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Benefit cap exceptions, Universal Credit Regulations 2013 regs. 82-83.
# https://www.legislation.gov.uk/uksi/2013/376/regulation/82
# https://www.gov.uk/benefit-cap/when-youre-not-affected

# --- Earnings exception: only tax on the earnings is deducted (reg. 55(5)) ---

- name: Unearned income does not reduce net earnings for the earnings exception
# Regulation 55(5) deducts the income tax paid "in respect of the
# employment", not the claimant's whole income tax liability. Property
# income of 40,000 pushes total income tax to over 8,000, but the tax
# attributable to the 13,000 of earnings is under 100, so net earnings stay
# well above the 846/month threshold and the exception applies.
period: 2025
input:
people:
person:
age: 35
employment_income: 13_000
property_income: 40_000
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_earnings: true
is_benefit_cap_exempt: true

- name: Earnings below the threshold do not except, even with large unearned income
# The same unearned income with earnings of 8,000 leaves net earnings below
# 846 x 12 = 10,152, so the exception does not apply.
period: 2025
input:
people:
person:
age: 35
employment_income: 8_000
property_income: 40_000
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_earnings: false

- name: A couple's earnings are combined for the earnings exception
# Reg. 82(1)(a) applies to "the claimant's and their partner's combined
# earned income". Neither partner alone reaches 10,152; together they do.
period: 2025
input:
people:
person1:
age: 35
employment_income: 6_000
person2:
age: 35
employment_income: 6_000
benunits:
benunit:
members: [person1, person2]
output:
is_benefit_cap_exempt_earnings: true

- name: Relievable pension contributions reduce earned income
# Reg. 55(5)(a) deducts relievable pension contributions from employed
# earnings, so a contribution that takes net earnings below the threshold
# removes the exception.
period: 2025
input:
people:
person:
age: 35
employment_income: 11_000
employee_pension_contributions: 1_500
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_earnings: false

# --- Earnings threshold is a dated parameter, not a hardcoded constant ---

- name: The 2025-26 earnings threshold is 846 a month
period: 2025
input:
people:
person:
age: 35
employment_income: 10_300
benunits:
benunit:
members: [person]
output:
# 10,300 clears 846 x 12 = 10,152.
is_benefit_cap_exempt_earnings: true

- name: The 2026-27 earnings threshold rises to 881 a month
period: 2026
input:
people:
person:
age: 35
employment_income: 10_300
benunits:
benunit:
members: [person]
output:
# The same earnings fall short of 881 x 12 = 10,572.
is_benefit_cap_exempt_earnings: false

- name: Earnings above the 2026-27 threshold except from the cap
period: 2026
input:
people:
person:
age: 35
employment_income: 10_600
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_earnings: true

# --- Armed Forces Independence Payment is a statutory exception ---

- name: Armed Forces Independence Payment excepts from the benefit cap
period: 2026
input:
people:
person:
age: 35
armed_forces_independence_payment: 5_740
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_health_disability: true
is_benefit_cap_exempt: true

- name: Without Armed Forces Independence Payment the cap applies
period: 2026
input:
people:
person:
age: 35
armed_forces_independence_payment: 0
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_health_disability: false
is_benefit_cap_exempt: false

- name: A child's Armed Forces Independence Payment excepts the benefit unit
period: 2026
input:
people:
parent:
age: 35
child:
age: 10
armed_forces_independence_payment: 5_740
benunits:
benunit:
members: [parent, child]
output:
is_benefit_cap_exempt_health_disability: true

# --- Other exceptions are unchanged ---

- name: State pension age excepts from the benefit cap
period: 2026
input:
people:
person:
age: 70
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_other: true
is_benefit_cap_exempt: true

- name: Armed Forces Compensation Scheme payments except from the benefit cap
period: 2026
input:
people:
person:
age: 35
afcs_reported: 2_000
benunits:
benunit:
members: [person]
output:
is_benefit_cap_exempt_other: true
is_benefit_cap_exempt_health_disability: true
2 changes: 1 addition & 1 deletion policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class is_benefit_cap_exempt(Variable):
value_type = bool
entity = BenUnit
label = "Whether exempt from the benefits cap because of health or disability"
label = "Whether exempt from the benefits cap"
definition_period = YEAR
reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"

Expand Down
79 changes: 30 additions & 49 deletions policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,38 @@
class is_benefit_cap_exempt_earnings(Variable):
value_type = bool
entity = BenUnit
label = "Whether exempt from the benefits cap for non-health/disability reasons"
label = "Whether exempt from the benefits cap because of earnings"
definition_period = YEAR
reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"
reference = "https://www.legislation.gov.uk/uksi/2013/376/regulation/82"

def formula(benunit, period, parameters):
# Check if anyone in benefit unit is over state pension age
person = benunit.members
over_pension_age = person("is_SP_age", period)
has_pensioner = benunit.any(over_pension_age)

# UC-specific exemptions
# Limited capability for work and work-related activity
has_lcwra = benunit.any(person("uc_limited_capability_for_WRA", period))

# Carer element in UC indicates caring for someone with disability
gets_uc_carer_element = benunit("uc_carer_element", period) > 0

# Earnings exemption for UC (£846/month = £10,152/year)
# Note: Only check earned income, not UC amount itself to avoid circular dependency
uc_earned = benunit.sum(
benunit.members("employment_income", period)
+ benunit.members("self_employment_income", period)
- benunit.members("income_tax", period)
- benunit.members("national_insurance", period)
# Regulation 82 of the Universal Credit Regulations 2013 excepts a
# claimant (or couple, on their combined income) from the benefit cap
# where their earned income reaches 16 hours a week at the National
# Living Wage. Earned income here is employed and self-employed
# earnings net of the income tax, National Insurance and relievable
# pension contributions attributable to those earnings (reg. 55(5),
# reg. 57), not net of total tax on all income.
#
# Only earned income is read, not the Universal Credit award itself,
# to avoid a circular dependency with the cap.
earnings = add(
benunit,
period,
["employment_income", "self_employment_income"],
)
earnings_threshold = 10_152
meets_earnings_test = uc_earned >= earnings_threshold

# Disability and carer benefits that exempt from cap
QUAL_PERSONAL_BENEFITS = [
"attendance_allowance",
"carers_allowance",
"dla", # Disability Living Allowance (includes components)
"pip_dl", # PIP daily living component
"pip_m", # PIP mobility component
"iidb", # Industrial injuries disability benefit
]

# ESA and Working Tax Credit
QUAL_BENUNIT_BENEFITS = [
"esa_income", # Income-based ESA
"working_tax_credit", # If getting WTC, likely working enough
]

qualifying_personal_benefits = add(benunit, period, QUAL_PERSONAL_BENEFITS)
qualifying_benunit_benefits = add(benunit, period, QUAL_BENUNIT_BENEFITS)

# Check for Armed Forces Compensation Scheme payments
afcs = benunit("afcs", period) > 0

# ESA contribution-based with support component
esa_support_component = benunit("esa_contrib", period) > 0
# earned_income_tax is the tax on non-savings, non-dividend income,
# which excludes property, savings and dividend income. It is the
# closest available measure of tax on earnings; it still includes tax
# on private and state pension income, which is the approximation we
# accept here (households with pension income are generally already
# exempt through the state pension age exception).
deductions = add(
benunit,
period,
["earned_income_tax", "national_insurance", "pension_contributions"],
)
net_earnings = max_(0, earnings - deductions)

return meets_earnings_test
monthly_threshold = parameters(period).gov.dwp.benefit_cap_earnings_exemption
return net_earnings >= monthly_threshold * MONTHS_IN_YEAR
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,14 @@ class is_benefit_cap_exempt_health_disability(Variable):
reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"

def formula(benunit, period, parameters):
# Check if anyone in benefit unit is over state pension age
person = benunit.members
over_pension_age = person("is_SP_age", period)
has_pensioner = benunit.any(over_pension_age)

# UC-specific exemptions
# Limited capability for work and work-related activity
has_lcwra = benunit.any(person("uc_limited_capability_for_WRA", period))

# Carer element in UC indicates caring for someone with disability
gets_uc_carer_element = benunit("uc_carer_element", period) > 0

# Earnings exemption for UC (£846/month = £10,152/year)
# Note: Only check earned income, not UC amount itself to avoid circular dependency
uc_earned = benunit.sum(
benunit.members("employment_income", period)
+ benunit.members("self_employment_income", period)
- benunit.members("income_tax", period)
- benunit.members("national_insurance", period)
)
earnings_threshold = 10_152
meets_earnings_test = uc_earned >= earnings_threshold

# Disability and carer benefits that exempt from cap
QUAL_PERSONAL_BENEFITS = [
"attendance_allowance",
Expand All @@ -41,6 +26,8 @@ def formula(benunit, period, parameters):
"pip_dl", # PIP daily living component
"pip_m", # PIP mobility component
"iidb", # Industrial injuries disability benefit
# Armed Forces Independence Payment is a statutory exemption
"armed_forces_independence_payment",
]

# ESA and Working Tax Credit
Expand Down
Loading