Skip to content
Draft
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/hb-special-disregard.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added the Housing Benefit special standard earnings disregard with regime-specific qualifying routes, explicit residual pre-assessment and no stacking with ordinary amounts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
description: Weekly Housing Benefit special standard earnings disregard, replacing the ordinary single or couple amount.
values:
2015-04-01: 20
metadata:
period: week
unit: currency-GBP
label: Housing Benefit special earnings disregard
reference:
- title: GB working-age earnings disregards, paragraphs 3 and 5 to 9
href: https://www.legislation.gov.uk/uksi/2006/213/schedule/4
- title: GB pension-age earnings disregards, paragraphs 1 to 7
href: https://www.legislation.gov.uk/uksi/2006/214/schedule/4
- title: NI working-age earnings disregards
href: https://www.legislation.gov.uk/nisr/2006/405/schedule/5
- title: NI pension-age earnings disregards
href: https://www.legislation.gov.uk/nisr/2006/406/schedule/5
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@
# One member must work 30 hours; hours are not added across the couple.
housing_benefit_applicable_income_disregard: 520 # £10 × 52

- name: Disabled single person working 20 hours gets £5 plus the additional disregard (model limitation; the law gives £20 plus it)
- name: Disabled single person working 20 hours gets £20 plus the additional disregard
period: 2026
absolute_error_margin: 0.01
input:
Expand All @@ -448,15 +448,13 @@
# Para 17(2)(b)(iv)(aa): a single claimant with the disability premium
# working 16 hours or more meets the condition.
meets_housing_benefit_additional_earnings_disregard_conditions: true
# KNOWN LIMITATION: the law gives £1,929.20. Intended model limitation.
# In law the same disability premium brings in
# the £20 disregard in place of £5: Sch 4 para 3(2), "This paragraph
# applies where the claimant's applicable amount includes an amount by
# way of the disability premium". The law gives (£20 + £17.10) × 52 =
# £1,929.20 (£8,000 ≥ £1,040 + £889.20). The £20 disregards are out of
# scope for this change and tracked as a follow-up; until then the model
# gives (£5 + £17.10) × 52.
housing_benefit_applicable_income_disregard: 1_149.20
# The disability premium gives the £20 standard disregard under Sch 4
# para 3(2), replacing £5. Net earnings cover that amount and the
# additional disregard: £8,000 ≥ £1,040 + £889.20.
# (£20 + £17.10) × 52 = £1,929.20.
# https://www.legislation.gov.uk/uksi/2006/213/schedule/4/paragraph/3
# https://www.legislation.gov.uk/uksi/2006/213/schedule/4/paragraph/17
housing_benefit_applicable_income_disregard: 1_929.20

- name: Working-age claimant on Income Support has all earnings disregarded
period: 2026
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Cases: working disability/severe; working carer-only; pension carer;
# pension generic disability premium; pension/working DLA; non-dependant DLA;
# pension/working blindness.
- name: ENGLAND statutory regime distinguishes automatic qualifying routes
period: 2024
input:
country: [ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND, ENGLAND]
housing_benefit_pension_age_regulations_apply: [false, false, false, true, true, true, false, true, true, false]
disability_premium: [100, 0, 0, 0, 100, 0, 0, 0, 0, 0]
severe_disability_premium: [0, 100, 0, 0, 0, 0, 0, 0, 0, 0]
carer_premium: [0, 0, 100, 100, 0, 0, 0, 0, 0, 0]
dla_sc: [0, 0, 0, 0, 0, 100, 100, 100, 0, 0]
dla_m: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pip_dl: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pip_m: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
attendance_allowance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
armed_forces_independence_payment: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
is_blind: [false, false, false, false, false, false, false, false, true, true]
is_claimant_or_partner: [true, true, true, true, true, true, true, false, true, true]
housing_benefit_net_earnings: [5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000]
is_lone_parent: [false, false, false, false, false, false, false, false, false, false]
is_couple: [false, false, false, false, false, false, false, false, false, false]
output:
housing_benefit_special_earnings_disregard_eligible: [true, true, false, true, false, true, false, false, true, false]
housing_benefit_special_earnings_disregard: [1040, 1040, 260, 1040, 260, 1040, 260, 260, 1040, 260]

# Cases: working disability/severe; working carer-only; pension carer;
# pension generic disability premium; pension/working DLA; non-dependant DLA;
# pension/working blindness.
- name: NORTHERN_IRELAND statutory regime distinguishes automatic qualifying routes
period: 2024
input:
country: [NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND, NORTHERN_IRELAND]
housing_benefit_pension_age_regulations_apply: [false, false, false, true, true, true, false, true, true, false]
disability_premium: [100, 0, 0, 0, 100, 0, 0, 0, 0, 0]
severe_disability_premium: [0, 100, 0, 0, 0, 0, 0, 0, 0, 0]
carer_premium: [0, 0, 100, 100, 0, 0, 0, 0, 0, 0]
dla_sc: [0, 0, 0, 0, 0, 100, 100, 100, 0, 0]
dla_m: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pip_dl: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pip_m: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
attendance_allowance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
armed_forces_independence_payment: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
is_blind: [false, false, false, false, false, false, false, false, true, true]
is_claimant_or_partner: [true, true, true, true, true, true, true, false, true, true]
housing_benefit_net_earnings: [5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000]
is_lone_parent: [false, false, false, false, false, false, false, false, false, false]
is_couple: [false, false, false, false, false, false, false, false, false, false]
output:
housing_benefit_special_earnings_disregard_eligible: [true, true, false, true, false, true, false, false, true, false]
housing_benefit_special_earnings_disregard: [1040, 1040, 260, 1040, 260, 1040, 260, 260, 1040, 260]

- name: Standard replacement, lone-parent precedence and earnings cap
period: 2024
input:
housing_benefit_special_earnings_disregard_eligible: [true, true, true, true, false, false]
housing_benefit_net_earnings: [5000, 5000, 5000, 500, 5000, 5000]
is_couple: [false, true, false, true, false, true]
is_lone_parent: [false, false, true, false, false, false]
housing_benefit_specified_or_temporary_accommodation_disregard: [0, 0, 0, 0, 0, 0]
housing_benefit_applicable_income_childcare_element: [0, 0, 0, 0, 0, 0]
meets_housing_benefit_additional_earnings_disregard_conditions: [false, false, false, false, false, false]
housing_benefit_on_passporting_benefit: [false, false, false, false, false, false]
output:
housing_benefit_applicable_income_disregard: [1040, 1040, 1300, 500, 260, 520]

- name: Special standard amount participates in additional-disregard threshold
period: 2024
absolute_error_margin: 0.01
input:
housing_benefit_special_earnings_disregard_eligible: [true, true, true]
housing_benefit_net_earnings: [1929.19, 1929.20, 5000]
is_couple: [false, true, false]
is_lone_parent: [false, false, false]
housing_benefit_specified_or_temporary_accommodation_disregard: [0, 0, 0]
housing_benefit_applicable_income_childcare_element: [0, 0, 0]
meets_housing_benefit_additional_earnings_disregard_conditions: [true, true, true]
housing_benefit_on_passporting_benefit: [false, false, true]
output:
housing_benefit_applicable_income_disregard: [1040, 1929.20, 5000]

- name: A zero special-amount reform is not replaced with the ordinary amount
period: 2024
input:
gov.dwp.housing_benefit.means_test.income_disregard.special: 0
housing_benefit_special_earnings_disregard_eligible: true
housing_benefit_net_earnings: 5000
is_lone_parent: false
is_couple: true
output:
housing_benefit_special_earnings_disregard: 0

- name: Missing residual qualification preserves ordinary treatment
period: 2024
input:
disability_premium: 0
severe_disability_premium: 0
housing_benefit_pension_age_regulations_apply: false
housing_benefit_net_earnings: 5000
is_lone_parent: false
is_couple: false
output:
housing_benefit_special_earnings_disregard_eligible: false
housing_benefit_special_earnings_disregard: 260
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

SI 2006/213 Sch 4 (working age) and SI 2006/214 Sch 4 (pension age) disregard
£25 a week of a lone parent's net earnings, £10 of a couple's and £5 of anyone
else's, plus the £17.10 additional earnings disregard where a work condition
else's, or £20 in place of the £5 or £10 for a family that qualifies for the
special disregard (housing_benefit_special_earnings_disregard_eligible), plus the £17.10 additional earnings disregard where a work condition
is met and net earnings at least equal the other disregards, the deductible
childcare charges and £17.10. A claimant who, or whose partner, is on
Universal Credit, Income Support, income-based JSA or income-related ESA has
Expand All @@ -28,11 +29,19 @@
over, where nothing age-dependent (para 12, the age-25 test, National
Insurance above State Pension age) can differ between the two groups, so
it pins the current behaviour rather than independently testing the two
schedules.
schedules. Intended difference: the £20 special disregard has different
conditions at each age. At working age it follows the disability premium,
which the model grants on the disability flag the draws set; at pension age
it needs receipt of a listed benefit, blindness or the carer premium (SI
2006/214 Sch 4 paras 4 and 5), none of which the draws supply. The
comparison therefore covers families whose special eligibility is the same
at both ages, which includes every family without a disabled adult.
7. Differential against the old flat formula (£5/£10/£25 by family type, plus
£37.10 for summed hours over 30, or 16 for lone parents), read without
CPI uprating. They agree for families whose net earnings cover the flat
amount and to whom neither formula gives the additional disregard. They
amount and to whom neither formula gives the additional disregard or the
special disregard; a couple or single person who qualifies for the special
disregard, and whose net earnings cover £20, gets £20 instead. They
must differ where net earnings fall below the flat amount (the new
disregard is the net earnings) and for families without earnings (the new
disregard is zero).
Expand Down Expand Up @@ -159,6 +168,7 @@ def situation(units, earnings_bump=0.0, income_support=0.0, age_map=None):
"housing_benefit_applicable_income",
"housing_benefit_applicable_income_childcare_element",
"meets_housing_benefit_additional_earnings_disregard_conditions",
"housing_benefit_special_earnings_disregard_eligible",
]


Expand Down Expand Up @@ -246,8 +256,13 @@ def test_pension_age_and_working_age_schedules_agree(units):
# Map 25-60 onto 67-90, keeping everyone at or over 25.
pension_age = calculate(units, age_map=lambda age: 67 + (age - 25) * 23 // 35)
key = "housing_benefit_applicable_income_disregard"
special = "housing_benefit_special_earnings_disregard_eligible"
for i, unit in enumerate(units):
assert abs(working_age[key][i] - pension_age[key][i]) < 0.01, unit
if not any(adult["disabled"] for adult in unit["adults"]):
# Nothing else in the draws can make the two routes differ.
assert working_age[special][i] == pension_age[special][i], unit
if working_age[special][i] == pension_age[special][i]:
assert abs(working_age[key][i] - pension_age[key][i]) < 0.01, unit


@PROPERTY_SETTINGS
Expand All @@ -259,6 +274,7 @@ def test_differential_against_the_old_flat_formula(units):
additional = values[
"meets_housing_benefit_additional_earnings_disregard_conditions"
].astype(bool)
special = values["housing_benefit_special_earnings_disregard_eligible"].astype(bool)
for i, unit in enumerate(units):
old = old_flat_formula(unit)
flat = OLD_FLAT[unit["shape"]] * WEEKS
Expand All @@ -272,8 +288,13 @@ def test_differential_against_the_old_flat_formula(units):
assert abs(disregard[i] - net[i]) < 0.01, unit
assert disregard[i] < old, unit
elif not old_worker and not additional[i]:
# Agree: the flat amount, without CPI uprating.
assert abs(disregard[i] - old) < 0.01, unit
if special[i] and unit["shape"] != "lone_parent":
# Must differ: £20 replaces the flat £5 or £10 (lone parents
# keep £25, which takes precedence).
assert abs(disregard[i] - min(20 * WEEKS, net[i])) < 0.01, unit
else:
# Agree: the flat amount, without CPI uprating.
assert abs(disregard[i] - old) < 0.01, unit


@PROPERTY_SETTINGS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
claimant or lone parent under 25, £77.73 at 25 or over; for a couple £97.33
where both are under 18, £61.53 where one is 18 or over but both are under
25, and £119.70 where one is 25 or over. A couple has one amount. It is added
to the £5/£10/£25 disregards (paras 10, 7 and 4), and para 17(3)(a) counts it
in the £17.10 additional disregard's earnings test. The pension-age
to the £5/£10/£20/£25 disregards (paras 10, 7, 3 and 4), and para 17(3)(a)
counts it in the £17.10 additional disregard's earnings test. The pension-age
Regulations (SI 2006/214; NI SR 2006/406) have no such disregard.

Invariants, for 2027 (the first fiscal year the model reads with para 18 in
Expand All @@ -32,9 +32,12 @@
5. Differential: the model agrees with a reference written from the
statutory text. The reference is independent for the para 18 bands and
gates (amounts hard-coded from the instrument, ages, family type, earner
and pension-age status from the generated inputs) and for how paras 4, 7,
10, 17 and 18 combine. It takes net earnings, deductible childcare and
the para 17(2) work condition from the model, which other tests cover.
and pension-age status from the generated inputs) and for how paras 3, 4,
7, 10, 17 and 18 combine. It takes net earnings, deductible childcare,
the para 17(2) work condition and whether the para 3 special £20 applies
from the model, which other tests cover
(test_housing_benefit_earnings_disregard_properties.py tests the special
£20).
Explicit examples pin the age boundaries (couples aged 17/17, 17/18 and
24/25; single claimants and lone parents aged 24 and 25).
6. Scope: families whose claimant and partner are all over State Pension
Expand Down Expand Up @@ -80,6 +83,8 @@
}
# Paras 4, 7 and 10, and para 17.
BASE_WEEKLY = {"single": 5, "lone_parent": 25, "couple": 10, "couple_with_children": 10}
# Para 3: replaces the single or couple amount; a lone parent keeps £25.
SPECIAL_WEEKLY = 20
ADDITIONAL_WEEKLY = 17.10
MAX_ANNUAL = (25 + COUPLE_25_PLUS + ADDITIONAL_WEEKLY) * WEEKS

Expand Down Expand Up @@ -198,6 +203,7 @@ def situation(units, earnings_bump=0.0, income_support=0.0, accommodation=None):
"housing_benefit_applicable_income",
"housing_benefit_applicable_income_childcare_element",
"meets_housing_benefit_additional_earnings_disregard_conditions",
"housing_benefit_special_earnings_disregard_eligible",
"housing_benefit_pension_age_regulations_apply",
]

Expand Down Expand Up @@ -229,10 +235,14 @@ def reference_para_18_weekly(unit):
return SINGLE_25_PLUS if eldest >= 25 else SINGLE_UNDER_25


def reference_disregard(unit, net, childcare, work_condition):
"""Paras 4, 7, 10, 17 and 18 combined, from net earnings, the deductible
childcare charges and whether a para 17(2) work condition is met."""
standard = min(BASE_WEEKLY[unit["shape"]] * WEEKS, net)
def reference_disregard(unit, net, childcare, work_condition, special=False):
"""Paras 3, 4, 7, 10, 17 and 18 combined, from net earnings, the deductible
childcare charges, whether a para 17(2) work condition is met and whether
the para 3 special £20 applies."""
weekly = BASE_WEEKLY[unit["shape"]]
if special and unit["shape"] != "lone_parent":
weekly = SPECIAL_WEEKLY
standard = min(weekly * WEEKS, net)
accommodation = min(reference_para_18_weekly(unit) * WEEKS, net - standard)
additional = ADDITIONAL_WEEKLY * WEEKS
covers = round(net, 2) >= round(
Expand Down Expand Up @@ -368,6 +378,9 @@ def test_differential_against_the_statutory_reference(units):
"meets_housing_benefit_additional_earnings_disregard_conditions"
][i]
),
special=bool(
values["housing_benefit_special_earnings_disregard_eligible"][i]
),
)
total = values["housing_benefit_applicable_income_disregard"][i]
assert abs(total - expected) < 0.02, (unit, total, expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ class housing_benefit_applicable_income_disregard(Variable):
"(housing_benefit_on_passporting_benefit) has all earnings "
"disregarded, at any age: the working-age Regulations then apply "
"(SI 2006/213 reg 5(1)(b)). The "
"£20 disregards for disabled people, carers and some part-time "
"occupations are not modelled, nor is the working-age permitted work "
"£20 standard disregard uses established qualifying routes or an "
"explicit full-amount pre-assessment, without reconstructing partial "
"carer/occupation earnings allocations. The permitted-work "
"disregard (Sch 4 para 10A), which replaces paras 3 to 10 but not "
"para 18."
"para 18, is not yet modelled here."
)
definition_period = YEAR
unit = GBP
Expand All @@ -42,12 +43,7 @@ def formula(benunit, period, parameters):
p = parameters(period).gov.dwp.housing_benefit.means_test.income_disregard
net_earnings = benunit("housing_benefit_net_earnings", period)
# Working age Sch 4 paras 4, 7 and 10; pension age Sch 4 paras 2 and 7.
weekly_amount = select(
[benunit("is_lone_parent", period), benunit("is_couple", period)],
[p.lone_parent, p.couple],
default=p.single,
)
standard = min_(weekly_amount * WEEKS_IN_YEAR, net_earnings)
standard = benunit("housing_benefit_special_earnings_disregard", period)
# Working age Sch 4 para 18 (NI Sch 5 para 18), from 5 October 2026:
# specified or temporary accommodation. It applies alongside paras 3
# to 10A, none of which excludes it, and takes the earnings they
Expand Down
Loading
Loading