diff --git a/changelog.d/hb-special-disregard.added.md b/changelog.d/hb-special-disregard.added.md new file mode 100644 index 0000000000..8a9d2c8319 --- /dev/null +++ b/changelog.d/hb-special-disregard.added.md @@ -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. diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/income_disregard/special.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/income_disregard/special.yaml new file mode 100644 index 0000000000..e1f45b0c7d --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/income_disregard/special.yaml @@ -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 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.yaml index 7cf5e90192..a2dcadd7cd 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.yaml @@ -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: @@ -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 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_special_disregard.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_special_disregard.yaml new file mode 100644 index 0000000000..af71a63ef8 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_special_disregard.yaml @@ -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 diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py index a7600bf518..7de5c94a3d 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py @@ -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 @@ -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 diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard.py new file mode 100644 index 0000000000..04d731487a --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard.py @@ -0,0 +1,37 @@ +from policyengine_uk.model_api import * + + +class housing_benefit_special_earnings_disregard(Variable): + value_type = float + entity = BenUnit + definition_period = YEAR + unit = GBP + label = "Housing Benefit selected standard earnings disregard" + documentation = ( + "Standard disregard selected before the additional/accommodation " + "amounts: lone-parent precedence, otherwise the special amount for " + "a qualifying family, otherwise the ordinary couple/single amount. " + "It is a replacement, not £20 added to £5 or £10. The total standard " + "deduction is capped at the claimant's and partner's net earnings." + ) + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/schedule/4", + "https://www.legislation.gov.uk/uksi/2006/214/schedule/4", + "https://www.legislation.gov.uk/nisr/2006/405/schedule/5", + "https://www.legislation.gov.uk/nisr/2006/406/schedule/5", + ) + + def formula(benunit, period, parameters): + p = parameters(period).gov.dwp.housing_benefit.means_test.income_disregard + weekly = select( + [ + benunit("is_lone_parent", period), + benunit("housing_benefit_special_earnings_disregard_eligible", period), + benunit("is_couple", period), + ], + [p.lone_parent, p.special, p.couple], + default=p.single, + ) + return min_( + weekly * WEEKS_IN_YEAR, benunit("housing_benefit_net_earnings", period) + ) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py new file mode 100644 index 0000000000..5c15427da6 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py @@ -0,0 +1,61 @@ +from policyengine_uk.model_api import * + + +class housing_benefit_special_earnings_disregard_eligible(Variable): + value_type = bool + entity = BenUnit + definition_period = YEAR + default_value = False + label = "Qualifies for the full Housing Benefit special earnings disregard" + documentation = ( + "Whether the full special standard amount may legally be deducted " + "from the benefit unit's combined earnings, subject to the earnings " + "cap. Derived for working-age disability/severe-premium routes and " + "pension-age qualifying benefit receipt, blindness or carer premium. " + "Other full-amount routes may be pre-assessed as an input, including " + "the applicable job/partner earnings allocation: mere occupation or " + "carer status is not sufficient. Partial carer/occupation allocations " + "below the full amount are not reconstructed by this Boolean. Existing " + "premium/benefit qualification approximations are inherited. No " + "dataset mapping for residual pre-assessment is verified; a false " + "fallback may omit qualifying ESA phase/history/occupation routes." + ) + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/schedule/4", + "https://www.legislation.gov.uk/uksi/2006/214/schedule/4", + "https://www.legislation.gov.uk/nisr/2006/405/schedule/5", + "https://www.legislation.gov.uk/nisr/2006/406/schedule/5", + ) + + def formula(benunit, period, parameters): + working = (benunit("disability_premium", period) > 0) | ( + benunit("severe_disability_premium", period) > 0 + ) + person = benunit.members + qualifying_receipt = ( + add( + person, + period, + [ + "attendance_allowance", + "dla_sc", + "dla_m", + "pip_dl", + "pip_m", + "armed_forces_independence_payment", + ], + ) + > 0 + ) + pension = benunit.any( + person("is_claimant_or_partner", period) + & (qualifying_receipt | person("is_blind", period)) + ) | (benunit("carer_premium", period) > 0) + # Working-age carer/occupation routes restrict which person's/job's + # earnings may be disregarded. Do not automatically apply £20 to an + # ordinary earner solely because their non-earning partner cares. + return where( + benunit("housing_benefit_pension_age_regulations_apply", period), + pension, + working, + )