Skip to content
Merged
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
1 change: 1 addition & 0 deletions changelog.d/1818.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added Universal Credit benefit cap earnings exception regression tests (Universal Credit Regulations 2013 reg. 82), including the per-claimant earned income cases (tax on pension income, one partner's pension contributions, contributions after 75), and cited regs. 55 and 57 and Finance Act 2004 s. 188 on `is_uc_benefit_cap_exempt_earnings`.
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,88 @@
is_uc_benefit_cap_exempt_specified_benefit: false
is_housing_benefit_benefit_cap_exempt_specified_benefit: true

- name: A dependent young person's AFIP does not lift the Universal Credit cap, so the award is capped
# For Universal Credit, AFIP is within "attendance allowance" (UC Regs
# 2013 reg 2), and reg 83(1)(c) excepts only where "a claimant is
# receiving" it. The 17-year-old is a qualifying young person, not a
# claimant, so the family's Universal Credit is capped at the London
# family rate of 25,323 a year. AFIP also earns no disabled child
# addition (reg 24(2) lists DLA, PIP and the Scottish equivalents, and
# blindness for the higher rate), so the maximum amount is the standard
# allowance, three child elements and the 30,000 housing costs element.
period: 2026
input:
people:
parent:
age: 45
young_person:
age: 17
current_education: UPPER_SECONDARY
armed_forces_independence_payment: 5_740
child1:
age: 10
child2:
age: 8
benunits:
benunit:
members: [parent, young_person, child1, child2]
households:
household:
members: [parent, young_person, child1, child2]
region: LONDON
tenure_type: RENT_FROM_COUNCIL
rent: 30_000
output:
is_qualifying_young_person_for_child_benefit: [false, true, false, false]
is_uc_assessed_claimant: [true, false, false, false]
is_uc_benefit_cap_exempt_specified_benefit: false
is_uc_benefit_cap_exempt: false
uc_benefit_cap: 25_323
uc_disability_elements: 0
universal_credit_pre_benefit_cap: 46_615.92
uc_benefit_cap_reduction: 24_561.12
universal_credit: 22_054.80
absolute_error_margin: 1

- name: A dependent young person's AFIP lifts the Housing Benefit cap, so the award is paid in full
# HB Regs 2006 reg 75F(1)(ea) names "the claimant, the claimant's partner
# or a young person for whom the claimant or the claimant's partner is
# responsible". The same family on legacy Housing Benefit (an existing
# award, no Universal Credit claim) is outside the cap, so its Housing
# Benefit is paid in full.
period: 2026
input:
people:
parent:
age: 45
housing_benefit_reported: 10_000
young_person:
age: 17
current_education: UPPER_SECONDARY
armed_forces_independence_payment: 5_740
child1:
age: 10
child2:
age: 8
benunits:
benunit:
members: [parent, young_person, child1, child2]
would_claim_uc: false
households:
household:
members: [parent, young_person, child1, child2]
region: LONDON
tenure_type: RENT_FROM_COUNCIL
rent: 30_000
output:
universal_credit_pre_benefit_cap: 0
is_housing_benefit_benefit_cap_exempt_specified_benefit: true
is_housing_benefit_benefit_cap_exempt: true
housing_benefit_benefit_cap_reduction: 0
housing_benefit_pre_benefit_cap: 30_000
housing_benefit: 30_000
absolute_error_margin: 1

- name: A 16-year-old out of education with armed forces independence payment does not lift the cap
period: 2025
input:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Universal Credit benefit cap earnings exception, Universal Credit
# Regulations 2013 reg. 82.
# 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_uc_benefit_cap_exempt_earnings: true
is_uc_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_uc_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_uc_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_uc_benefit_cap_exempt_earnings: false

- name: Tax on private pension income does not reduce earned income for the earnings exception
# Reg. 55(5)(b) deducts only income tax paid "in respect of the
# employment". The 5,000 private pension uses up the personal allowance,
# so earned_income_tax (tax on non-savings, non-dividend income) is 546,
# but none of it is in respect of the 10,300 of wages. Earned income is
# 10,300, which clears 846 x 12 = 10,152, so the cap does not reduce the
# family's Universal Credit.
period: 2025
input:
people:
parent:
age: 35
employment_income: 10_300
private_pension_income: 5_000
child1:
age: 5
child2:
age: 8
benunits:
benunit:
members: [parent, child1, child2]
households:
household:
members: [parent, child1, child2]
region: LONDON
tenure_type: RENT_FROM_COUNCIL
rent: 30_000
output:
earned_income_tax: [546, 0, 0]
uc_income_tax_on_earnings: [0, 0, 0]
is_uc_benefit_cap_exempt_earnings: true
benefit_cap_reduction: 0
universal_credit: 33_876.72
absolute_error_margin: 1

- name: One partner's pension contributions do not reduce the other partner's earnings
# Reg. 82(1)(a) adds the couple's earned income, and each person's earned
# income is their own earnings less their own deductions. The
# self-employed partner's 100 of profit less 3,000 of relievable pension
# contributions is nil, not minus 2,900 (reg. 57(2), steps 3 and 4).
# Combined earned income is 10,600, which clears 881 x 12 = 10,572.
period: 2026
input:
people:
employee:
age: 35
employment_income: 10_600
self_employed:
age: 35
self_employment_income: 100
personal_pension_contributions: 3_000
child1:
age: 5
child2:
age: 8
benunits:
benunit:
members: [employee, self_employed, child1, child2]
households:
household:
members: [employee, self_employed, child1, child2]
region: LONDON
tenure_type: RENT_FROM_COUNCIL
rent: 30_000
output:
uc_individual_earned_income_before_mif: [10_600, 0, 0, 0]
is_uc_benefit_cap_exempt_earnings: true
benefit_cap_reduction: 0
universal_credit: 31_074.08
absolute_error_margin: 1

- name: Pension contributions after 75 are not deducted from earned income
# Contributions paid after the individual reaches 75 are not relievable
# (Finance Act 2004 s. 188(3)(a)), so reg. 55(5)(a) does not deduct them.
# The mixed-age couple's Universal Credit award is capped like any other
# (reg. 3(2)(a); no age exception reaches it), and the older partner's
# 11,000 of earnings clears 881 x 12 = 10,572 before and after the
# 1,000 contribution.
period: 2026
input:
people:
older_partner:
age: 76
employment_income: 11_000
employee_pension_contributions: 1_000
younger_partner:
age: 40
child1:
age: 5
child2:
age: 8
benunits:
benunit:
members: [older_partner, younger_partner, child1, child2]
households:
household:
members: [older_partner, younger_partner, child1, child2]
region: LONDON
tenure_type: RENT_FROM_COUNCIL
rent: 30_000
output:
is_uc_assessed_claimant: [true, true, false, false]
housing_benefit_pension_age_regulations_apply: false
uc_individual_earned_income_before_mif: [11_000, 0, 0, 0]
is_uc_benefit_cap_exempt_earnings: true
benefit_cap_reduction: 0
universal_credit: 42_066.40
absolute_error_margin: 1

# --- Earnings threshold follows the national living wage, not a fixed 10,152 ---

- 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_uc_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_uc_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_uc_benefit_cap_exempt_earnings: true
25 changes: 23 additions & 2 deletions policyengine_uk/tests/test_uc_single_claim_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"pip_dl": 0.0,
"afcs": 0.0,
"esa_contrib": 0.0,
"armed_forces_independence_payment": 0.0,
}


Expand All @@ -132,6 +133,7 @@ def adult(draw, ages):
pip_dl=draw(pip),
afcs=draw(sometimes_paid),
esa_contrib=draw(sometimes_paid),
armed_forces_independence_payment=draw(sometimes_paid),
)


Expand Down Expand Up @@ -160,6 +162,7 @@ def adult_inputs(age, **inputs):
pip_dl=0.0,
afcs=0.0,
esa_contrib=0.0,
armed_forces_independence_payment=0.0,
)
values.update(inputs)
return values
Expand Down Expand Up @@ -259,6 +262,16 @@ def example_families():
tenure="RENT_FROM_COUNCIL",
region="NORTH_EAST",
),
# An other member with armed forces independence payment and nothing
# else.
dict(
claimant=adult_inputs(40),
partner=adult_inputs(40, armed_forces_independence_payment=5_000.0),
children=[],
rent=0.0,
tenure="RENT_FROM_COUNCIL",
region="NORTH_EAST",
),
]


Expand All @@ -272,6 +285,7 @@ def example_families():
afcs_partner=6,
esa_partner=7,
afcs_claimant=8,
afip_partner=9,
)
EXAMPLE_FAMILIES = example_families()

Expand Down Expand Up @@ -497,12 +511,12 @@ def test_deduction_is_the_joint_claimants(families, year):
@given(
families=populations,
year=st.sampled_from([2021, 2026]),
balances=st.lists(st.floats(0, 30_000), min_size=18, max_size=18),
balances=st.lists(st.floats(0, 30_000), min_size=20, max_size=20),
)
@example(
families=example_families(),
year=2026,
balances=[8_000.0, 16_000.0, 0.0, 30_000.0, 2_000.0, 0.0] + [5_000.0] * 12,
balances=[8_000.0, 16_000.0, 0.0, 30_000.0, 2_000.0, 0.0] + [5_000.0] * 14,
)
def test_capital_includes_the_other_members(families, year, balances):
families = copy.deepcopy(families)
Expand Down Expand Up @@ -700,6 +714,13 @@ def test_examples_reach_the_cases():
assert joint["is_uc_benefit_cap_exempt_specified_benefit"][family]
assert single["is_housing_benefit_benefit_cap_exempt_specified_benefit"][family]
assert single["is_uc_benefit_cap_exempt_specified_benefit"][i["afcs_claimant"]]
# Reg. 83(1)(c) with reg. 2: armed forces independence payment is
# attendance allowance, which lifts the UC cap only for a claimant. HB
# reg. 75F(1)(ea): the partner's lifts the HB cap either way.
family = i["afip_partner"]
assert not single["is_uc_benefit_cap_exempt_specified_benefit"][family]
assert joint["is_uc_benefit_cap_exempt_specified_benefit"][family]
assert single["is_housing_benefit_benefit_cap_exempt_specified_benefit"][family]
# Reg. 80A(2) and Sch 4 para 28(2): single rate and shared accommodation.
# HB regs. 2(1), 13D(2) and 75CA(2): a couple either way.
family = i["young_renter"]
Expand Down
Loading
Loading