diff --git a/changelog.d/abolish-working-age-housing-benefit.added.md b/changelog.d/abolish-working-age-housing-benefit.added.md new file mode 100644 index 0000000000..e815895034 --- /dev/null +++ b/changelog.d/abolish-working-age-housing-benefit.added.md @@ -0,0 +1 @@ +- Added a specified or temporary accommodation input to preserve working-age Housing Benefit, permit new claims alongside Universal Credit and exclude the corresponding Universal Credit housing costs. diff --git a/changelog.d/abolish-working-age-housing-benefit.fixed.md b/changelog.d/abolish-working-age-housing-benefit.fixed.md new file mode 100644 index 0000000000..ec9f047b23 --- /dev/null +++ b/changelog.d/abolish-working-age-housing-benefit.fixed.md @@ -0,0 +1 @@ +- Abolish working-age Housing Benefit awards outside specified and temporary accommodation from 1 July 2026 in Great Britain (SI 2025/1148 art. 7, inserted by SI 2026/409) and 1 October 2026 in Northern Ireland (SR 2025/176 art. 7, inserted by SR 2026/115). The new `gov.dwp.housing_benefit.working_age_awards_payable` parameter is day-weighted across each fiscal year, so 2026-27 pays a working-age continuing award for 86/365 of the year in Great Britain and 178/365 in Northern Ireland, and from 2027-28 there is none. Families meeting the Pension Credit age conditions keep their awards: a single claimant or both members of a couple over the qualifying age, and mixed-age couples with the SI 2019/37 art. 4 saving (SI 2014/1230 reg 6A(4)-(5)). A family whose award is abolished claims Universal Credit if `would_claim_uc_at_legacy_closure` is true, which ends the claimant's and partner's income-related ESA. diff --git a/docs/book/programs/gov/dwp/housing-benefit.ipynb b/docs/book/programs/gov/dwp/housing-benefit.ipynb index a3e9c25f03..f19a9c5f67 100644 --- a/docs/book/programs/gov/dwp/housing-benefit.ipynb +++ b/docs/book/programs/gov/dwp/housing-benefit.ipynb @@ -7,7 +7,7 @@ "source": [ "# Housing Benefit\n", "\n", - "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit that is being phased out: new working-age claimants are routed to the Universal Credit housing element instead, and a managed migration of the remaining HB caseload to UC is in progress. Housing Benefit continues to operate for two groups — pensioners and tenants of supported/temporary accommodation — and PolicyEngine models it alongside the UC housing element so reform analyses can move between the two systems cleanly.\n", + "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit: most new working-age claimants use the Universal Credit housing element, and working-age awards were abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland, subject to statutory savings. Housing Benefit continues for pensioners, protected mixed-age couples, tenants of specified or temporary accommodation and other saved cases. PolicyEngine models the accommodation saving through an explicit input; its coverage of the saved cases and accommodation-specific award rules remains partial.\n", "\n", "Parameters live in `policyengine_uk/parameters/gov/dwp/housing_benefit/` and the formulas in `policyengine_uk/variables/gov/dwp/housing_benefit/`." ] @@ -19,12 +19,31 @@ "source": [ "## Eligibility\n", "\n", - "`housing_benefit_eligible` requires the benunit to be either in **social housing** or eligible for the **Local Housing Allowance** rate (broadly, private renters), with assessable capital at or below the working-age (or pension-age) capital limit, and to reach Housing Benefit by one of two routes:\n", + "`housing_benefit_eligible` requires the benunit to be either in **social housing** or eligible for the **Local Housing Allowance** rate (broadly, private renters), with assessable capital at or below the working-age (or pension-age) capital limit, and to reach Housing Benefit by one of three routes:\n", "\n", "- **Pension age**: the claimant and any partner have reached the qualifying age for State Pension Credit (State Pension age), or the family is a mixed-age couple that keeps the pension-age route: one entitled to Pension Credit or pension-age Housing Benefit on 14 May 2019 and ever since ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), or any mixed-age couple not reported on Universal Credit before 15 May 2019 (`meets_pension_credit_age_conditions`). New Housing Benefit claims are open to them ([SI 2014/1230 reg. 6A(4) and (5)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A)), and Universal Credit is not, so neither a previous claim nor `would_claim_uc` matters.\n", - "- **Continuing award**: any other benunit was **already claiming** Housing Benefit (proxied by a positive `housing_benefit_reported`) and is **not claiming Universal Credit**. Working-age families, and mixed-age couples without the saving, cannot make new claims; they claim the Universal Credit housing element instead.\n", + "- **Specified or temporary accommodation**: `in_specified_or_temporary_accommodation` is a boolean benunit input, defaulting to `False`. When supplied as `True`, it permits new Housing Benefit claims without a reported previous award, under [SI 2014/1230 reg. 6A(2)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) and [SR 2016/226 reg. 4A(2)](https://www.legislation.gov.uk/nisr/2016/226/regulation/4A). The input refers to the statutory definitions: [GB specified accommodation](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3A) and [temporary accommodation](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3B), and their [NI specified](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4) and [temporary](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4A) equivalents. It is not inferred from tenure or a general supported-housing label.\n", + "- **Continuing award**: any other benunit was **already claiming** Housing Benefit (proxied by a positive `housing_benefit_reported`), is **not claiming Universal Credit**, and has a positive `housing_benefit_payable_share`. Working-age families, and mixed-age couples without the saving, cannot make new claims; they claim the Universal Credit housing element instead.\n", "\n", - "The pension-age test counts the same benunit members as Universal Credit's working-age test, and the continuing-award route requires not claiming Universal Credit, so no benunit receives both. Working-age awards outside specified or temporary accommodation ended on 1 July 2026 in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7)); PolicyEngine does not yet model that. Claims for specified or temporary accommodation, which remain open at any age, are not modelled.\n", + "The accommodation route permits Housing Benefit alongside Universal Credit: these Housing Benefit awards do not terminate on a UC claim ([SI 2014/1230 reg. 8(3)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/8); [NI reg. 6(3)](https://www.legislation.gov.uk/nisr/2016/226/regulation/6)). When the input is `True`, PolicyEngine excludes the UC housing costs element, as the corresponding rent payments are excluded from UC housing costs ([SI 2013/376 Schedule 1 para. 3(h)-(i)](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3); [NI Schedule 1 para. 3(e)-(ea)](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/3)). Existing rental, capital, entitlement and Housing Benefit take-up checks still apply. The input does not implement the special eligible-rent or benefit-cap treatment of these accommodation types.\n", + "\n", + "### Abolition of working-age awards\n", + "\n", + "Working-age Housing Benefit awards were abolished from **1 July 2026** in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7), inserted by [SI 2026/409](https://www.legislation.gov.uk/uksi/2026/409/made)) and **1 October 2026** in Northern Ireland ([SR 2025/176 art. 7](https://www.legislation.gov.uk/nisr/2025/176/article/7), inserted by [SR 2026/115](https://www.legislation.gov.uk/nisr/2026/115/made)), subject to these savings:\n", + "\n", + "- Article 7(4)(a) saves the cases in [SI 2014/1230 reg. 6A(2)-(5)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) and [NI reg. 4A(2)-(5)](https://www.legislation.gov.uk/nisr/2016/226/regulation/4A): specified or temporary accommodation; the qualifying final UC assessment period when the claimant reaches Pension Credit age and the relevant Decisions and Appeals provision applies, with entitlement from reaching that age; pension-age single claimants, couples where both have reached that age and polygamous marriages where all have; and mixed-age couples whose 2019 saving remains effective.\n", + "- Article 7(4)(b) saves an award where the claimant was entitled to income-related ESA immediately before the abolition date and the article 3A(2) saving applies: an appointee was appointed immediately before that date, or the Secretary of State (NI: Department) determined in the preceding six months that an appointee was likely to be needed, whether or not one was appointed. Article 3A(3) still permits termination following a UC claim or failure to claim by the migration deadline.\n", + "- Article 7(2) delays abolition where, immediately before the abolition date, the claimant was prevented from receiving UC by regulation 19(1)(b) (prisoners) or (c) (serving a sentence of imprisonment while detained in hospital). The appointed abolition date is then the day after the last day that restriction applies; this is not a general saving for anyone who becomes a prisoner later.\n", + "\n", + "The dates live in `gov.dwp.housing_benefit.working_age_awards_payable` (`great_britain` and `northern_ireland`), with 1 while working-age awards are payable and 0 after. PolicyEngine UK evaluates a year's parameters as one value for the fiscal year starting 6 April. By default that value is the one in force on 30 April, which would have left the whole of 2026-27 unaffected. This parameter is instead day-weighted across the fiscal year (`fiscal_year_blend`), so 2026 (2026-27) takes the share of the year before the abolition: 86/365 in Great Britain (6 April to 30 June) and 178/365 in Northern Ireland (6 April to 30 September). From 2027 it is 0. Annual formulas read the converted annual value at the start of the model year; later values do not switch a YEAR formula partway through that year.\n", + "\n", + "In a `Simulation` reform dictionary, a bare-year key such as `2026` covers the entire fiscal year and can set its share to 1, and a date range applies over its stated dates. A key dated inside the year, such as `2026-07-01`, leaves that model year's share unchanged (86/365 for Great Britain in 2026), so use whole-year values or a range for annual reform scenarios.\n", + "\n", + "`housing_benefit_payable_share` applies this per benunit. It is 1 when `in_specified_or_temporary_accommodation` is `True`, in every model year. It is also 1 for a benunit meeting the Pension Credit age conditions (`meets_pension_credit_age_conditions`): a single claimant, or both members of a couple, over the qualifying age (reg 6A(4)), or a mixed-age couple with the 2019 saving ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4); reg 6A(5)), which the model infers from current receipt. A mixed-age couple without the saving loses its award like a working-age family. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. An unprotected working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", + "\n", + "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families without the accommodation input that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on Employment and Support Allowance), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "\n", + "No current dataset supplies `in_specified_or_temporary_accommodation`, so its default of `False` leaves existing dataset outputs unchanged. The other savings requiring histories or decisions remain unmodelled: the qualifying final UC assessment-period case, the ESA appointee or likely-needed-appointee determination, and the restricted prisoner cases. The pension-age and protected mixed-age routes remain approximations. The Enhanced FRS therefore still has no working-age Housing Benefit from 2027. Accommodation-specific eligible-rent and benefit-cap rules also remain gaps even when the accommodation input is supplied. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", "\n", "**Guarantee Credit passport.** If the claimant or partner is in receipt of a guarantee credit (`in_receipt_of_guarantee_credit`: the benunit is eligible for Pension Credit, claims it, and its guarantee credit is positive, so the Pension Credit paid includes a guarantee credit), the whole of their capital and income is disregarded ([SI 2006/214 reg. 26](https://www.legislation.gov.uk/uksi/2006/214/regulation/26); Northern Ireland: [SR 2006/406 reg. 24](https://www.legislation.gov.uk/nisr/2006/406/regulation/24)). `housing_benefit_assessable_capital`, `housing_benefit_tariff_income` and `housing_benefit_applicable_income` are then zero, so the capital limit cannot bite and no income is tapered. A guarantee credit computed for a family that does not claim Pension Credit (`would_claim_pc`), or that PolicyEngine does not treat as eligible for it (`is_pension_credit_eligible`), does not count. The passport applies within the pension-age regulations (`housing_benefit_pension_age_regulations_apply`). `is_pension_credit_eligible` retains mixed-age couples covered by the [SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4) saving through `meets_pension_credit_age_conditions`; these couples keep the passport when they receive Guarantee Credit. When Pension Credit is frozen, receipt follows the baseline award, including its Guarantee Credit component. Savings Credit alone follows the separate assessment in [reg. 27](https://www.legislation.gov.uk/uksi/2006/214/regulation/27), including its capital limit, and does not give this passport.\n", "\n", diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml index 26c847e978..c5d60a37c8 100644 --- a/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml @@ -1,7 +1,8 @@ description: Share of eligible Housing Benefit recipients that participate. Not used by the model, which takes Housing Benefit take-up from reported claims in survey data and assumes full take-up in household calculations. Pension-age families can make new - claims; working-age families only continue existing awards. + claims; working-age families only continued existing awards, until those were + abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland. metadata: economy: false label: Housing benefit take-up rate diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml new file mode 100644 index 0000000000..99f735cafe --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml @@ -0,0 +1,51 @@ +description: >- + Whether working-age Housing Benefit awards outside specified and temporary + accommodation are still payable (1) or abolished (0). Housing Benefit was + abolished for these awards from 1 July 2026 in Great Britain and 1 October + 2026 in Northern Ireland; statutory savings are described in + housing_benefit_payable_share. Annual formulas read parameters once, at the + start of the model year, after fiscal-year conversion. Parameters normally + sample 30 April, but these numeric flags use fiscal_year_blend to take the + day-weighted average from 6 April to 5 April. In a Simulation reform + dictionary a bare year overrides the whole fiscal year, while a key dated + inside the year (for example 2026-07-01) leaves that model year's value + unchanged. Use whole-year values or a date range for annual reform scenarios. +great_britain: + description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable in Great Britain. Section 33(1)(d) of the Welfare Reform Act 2012 came into force for them on 1 July 2026. + values: + 1988-04-01: 1 + 2026-07-01: 0 + metadata: + label: Working-age housing benefit awards payable in Great Britain + unit: /1 + period: year + fiscal_year_blend: true + reference: + - title: SI 2025/1148 art. 7 (inserted by SI 2026/409 art. 2(6)) + href: https://www.legislation.gov.uk/uksi/2025/1148/article/7 + - title: SI 2026/409, The Welfare Reform Act 2012 (Commencement No. 35) (Abolition of Benefits) (Amendment) Order 2026 + href: https://www.legislation.gov.uk/uksi/2026/409/made + - title: Welfare Reform Act 2012 s. 33(1)(d) + href: https://www.legislation.gov.uk/ukpga/2012/5/section/33 + - title: DWP HB circular A6/2026, closure of working age Housing Benefit + href: https://www.gov.uk/government/publications/housing-benefit-adjudication-circulars-2026/a62026-closure-of-working-age-housing-benefit +northern_ireland: + description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable in Northern Ireland. Article 39(1)(d) of the Welfare Reform (Northern Ireland) Order 2015 came into operation for them on 1 October 2026. + values: + 1988-04-01: 1 + 2026-10-01: 0 + metadata: + label: Working-age housing benefit awards payable in Northern Ireland + unit: /1 + period: year + fiscal_year_blend: true + reference: + - title: SR 2025/176 art. 7 (inserted by SR 2026/115) + href: https://www.legislation.gov.uk/nisr/2025/176/article/7 + - title: SR 2026/115 + href: https://www.legislation.gov.uk/nisr/2026/115/made + - title: Welfare Reform (Northern Ireland) Order 2015 art. 39(1)(d) + href: https://www.legislation.gov.uk/nisi/2015/2006/article/39 +metadata: + label: Working-age housing benefit awards payable + economy: false diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 11657e674c..743a097fb5 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -224,12 +224,12 @@ programs: full_name: Housing Benefit category: Benefits agency: DWP - status: complete + status: partial coverage: UK variable: housing_benefit parameter_prefix: gov.dwp.housing_benefit verified_start_year: 2022 - notes: Legacy benefit being replaced by Universal Credit + notes: Legacy benefit replaced by Universal Credit. Working-age abolition applies from 1 July 2026 (GB) and 1 October 2026 (NI). An explicit specified-or-temporary-accommodation input preserves payable awards and new claims alongside UC without UC rent support; datasets do not supply it. The protected mixed-age saving is inferred from current receipt. Other historical savings and accommodation-specific eligible-rent and benefit-cap rules are not modelled. - id: income_support name: Income Support diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_specified_or_temporary_accommodation_disregard.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_specified_or_temporary_accommodation_disregard.yaml index d6027e8a89..6047d9fc2a 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_specified_or_temporary_accommodation_disregard.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/applicable_income/housing_benefit_specified_or_temporary_accommodation_disregard.yaml @@ -768,6 +768,9 @@ benunit: members: [person] would_claim_uc: false + # Its award is abolished in 2027; it does not claim Universal Credit + # then, so it is not passported. + would_claim_uc_at_legacy_closure: false guarantee_credit: 0 housing_benefit_applicable_amount: 4_968.60 households: diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_earnings_disregards.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_earnings_disregards.yaml index 9c7887b289..87a6f464dd 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_earnings_disregards.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_earnings_disregards.yaml @@ -84,6 +84,9 @@ benunit: members: [person] would_claim_uc: false + # Does not claim Universal Credit when the award is abolished, so the + # award continues until then. + would_claim_uc_at_legacy_closure: false # Working age, so no Pension Credit; set so that no form of the # Guarantee Credit passport can apply. guarantee_credit: 0 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml index 3a1990c8a0..38ce39a589 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml @@ -1,3 +1,7 @@ +# Continuing-award route for a working-age family (age defaults to 40) in +# 2021, before working-age awards were abolished: a reported award, no +# Universal Credit claim, and social housing or the Local Housing Allowance. + - name: Social housing eligible period: 2021 input: diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_income_related_benefit_passport.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_income_related_benefit_passport.yaml index 0fee7bed44..d49a7f5c4a 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_income_related_benefit_passport.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_income_related_benefit_passport.yaml @@ -165,9 +165,10 @@ # SI 2006/214 reg 5(2) disapplies the pension-age ones), so Sch 5 para 4 and # Sch 6 para 5 disregard the State Pension and the savings. Guarantee Credit # is set to nil to show the result does not come from the Pension Credit - # passport. Members over State Pension age keep the award all year. The - # private pension would otherwise take the couple's income above their - # applicable amount. + # passport. The private pension would otherwise take the couple's income + # above their applicable amount. The couple has no SI 2019/37 art. 4 + # saving, so its working-age award ends on 1 July 2026 (SI 2025/1148 + # art. 7); not claiming Universal Credit, it is paid for 86/365 of 2026-27. period: 2026 absolute_error_margin: 0.01 input: @@ -183,6 +184,7 @@ benunit: members: [older, younger] would_claim_uc: false + would_claim_uc_at_legacy_closure: false guarantee_credit: 0 esa_income: 6_000 households: @@ -196,7 +198,8 @@ housing_benefit_assessable_capital: 0 housing_benefit_applicable_income: 0 housing_benefit_entitlement: 6_240 - housing_benefit: 6_240 + # 6,240 x 86/365. + housing_benefit: 1_470.25 - name: Mixed-age couple with contributory ESA only is not passported period: 2026 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_pension_age_claims.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_pension_age_claims.yaml index 80544aada7..b5f06ab1c9 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_pension_age_claims.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_pension_age_claims.yaml @@ -190,6 +190,9 @@ is_uc_eligible: true - name: Mixed-age couple with a continuing award keeps HB and gets no UC + # Without the SI 2019/37 art. 4 saving the award ends on 1 July 2026 (SI + # 2025/1148 art. 7); a couple that does not claim Universal Credit keeps it + # until then. period: 2026 absolute_error_margin: 0.01 input: @@ -204,6 +207,7 @@ benunit: members: [older, younger] would_claim_uc: false + would_claim_uc_at_legacy_closure: false households: household: members: [older, younger] diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml new file mode 100644 index 0000000000..c71efae5bc --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml @@ -0,0 +1,641 @@ +# Abolition of working-age Housing Benefit outside specified and temporary +# accommodation: 1 July 2026 in Great Britain (SI 2025/1148 art. 7, inserted +# by SI 2026/409) and 1 October 2026 in Northern Ireland (SR 2025/176 art. 7, +# inserted by SR 2026/115). Pension-age claimants and protected mixed-age +# couples keep their awards (art. 7(4)(a), SI 2014/1230 reg 6A(4)-(5)). +# +# Each fiscal year runs 6 April to 5 April (365 days in 2026-27). A GB award +# is payable for the 86 days from 6 April to 30 June 2026 and an NI award for +# the 178 days from 6 April to 30 September 2026, so the 2026 amount is the +# annual entitlement times 86/365 or 178/365. Every case below fixes +# housing_benefit_applicable_income at 0, so entitlement equals the £5,200 +# rent (£100 a week). + +- name: GB working-age continuing award is paid in full before the abolition year + period: 2025 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: GB working-age continuing award is paid until 30 June 2026 (86 of 365 days) + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + # 86 / 365 + housing_benefit_payable_share: 0.235616 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + # 5,200 * 86 / 365 + housing_benefit: 1_225.21 + +- name: GB working-age continuing award has ended by 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + universal_credit: 0 + +- name: Scotland follows the Great Britain date + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_HA + region: SCOTLAND + output: + housing_benefit_payable_share: 0.235616 + housing_benefit: 1_225.21 + +- name: Wales follows the Great Britain date + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: WALES + output: + housing_benefit_payable_share: 0.235616 + housing_benefit: 1_225.21 + +- name: NI working-age continuing award is paid until 30 September 2026 (178 of 365 days) + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + # 178 / 365 + housing_benefit_payable_share: 0.487671 + housing_benefit_eligible: true + # 5,200 * 178 / 365 + housing_benefit: 2_535.89 + +- name: NI working-age continuing award has ended by 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Working-age couple with a continuing award loses it from 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 45 + housing_benefit_reported: 1 + partner: + age: 43 + benunits: + benunit: + members: [claimant, partner] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant, partner] + rent: 5_200 + tenure_type: RENT_FROM_HA + region: NORTH_WEST + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Protected mixed-age couple keeps its award after the abolition + # The older member, 75 in 2027-28, had reached the qualifying age by 14 May + # 2019 and reports pension-age Housing Benefit, so the couple keeps the SI + # 2019/37 art. 4 saving (reg 6A(5)). + period: 2027 + absolute_error_margin: 0.01 + input: + people: + older: + age: 75 + housing_benefit_reported: 1 + younger: + age: 60 + benunits: + benunit: + members: [older, younger] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [older, younger] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Mixed-age couple without the 2019 saving loses its award after the abolition + # The older member, 68 in 2027-28, reached the qualifying age after 14 May + # 2019, so the couple has no SI 2019/37 art. 4 saving and is outside reg + # 6A(4)-(5). Not claiming Universal Credit, it gets no award from 2027-28. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + older: + age: 68 + housing_benefit_reported: 1 + younger: + age: 50 + benunits: + benunit: + members: [older, younger] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [older, younger] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + meets_pension_credit_age_conditions: false + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Pensioner with an 18 year old dependant keeps a continuing award after the abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 70 + is_parent: true + housing_benefit_reported: 1 + dependant: + age: 18 + current_education: UPPER_SECONDARY + benunits: + benunit: + members: [claimant, dependant] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant, dependant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Pension-age new claim is unaffected by the abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 75 + benunits: + benunit: + members: [claimant] + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Working-age family claiming Universal Credit gets no Housing Benefit in any year + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: true + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_eligible: false + housing_benefit: 0 + +- name: GB protected working-age continuing award is paid in full after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: NI protected working-age continuing award is paid in full after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: GB protected accommodation permits a working-age new claim after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: NI protected accommodation permits a working-age new claim after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: GB unprotected working-age new claim receives no award after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + claims_all_entitled_benefits: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: NI unprotected working-age new claim receives no award after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + claims_all_entitled_benefits: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Protected accommodation still respects the working-age capital limit + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + savings: 20_000 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: GB protected accommodation receives Housing Benefit alongside non-housing UC + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: true + housing_benefit_applicable_income: 0 + # Fix the non-housing allowance to isolate the rent-cost routing. + uc_standard_allowance: 5_000 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + uc_housing_costs_element: 0 + universal_credit: 5_000 + +- name: NI protected accommodation receives Housing Benefit alongside non-housing UC + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: true + housing_benefit_applicable_income: 0 + # Fix the non-housing allowance to isolate the rent-cost routing. + uc_standard_allowance: 5_000 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + uc_housing_costs_element: 0 + universal_credit: 5_000 + +- name: A capped family keeping protected Housing Benefit alongside Universal Credit is capped once + # HB Regs 2006 reg 75F(1)(g): the Housing Benefit cap does not apply to a + # claimant receiving Universal Credit. UC Regs 2013 reg 80(2A): Universal + # Credit's cap counts that Housing Benefit as nil, so its reduction is + # 57,471.94 - 30,000 - 25,323 = 2,148.94. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + partner: + age: 35 + child1: + age: 2 + child2: + age: 4 + child3: + age: 6 + child4: + age: 8 + benunits: + benunit: + members: [parent, partner, child1, child2, child3, child4] + would_claim_uc: true + in_specified_or_temporary_accommodation: true + households: + household: + members: [parent, partner, child1, child2, child3, child4] + rent: 30_000 + tenure_type: RENT_FROM_HA + region: LONDON + output: + is_housing_benefit_benefit_cap_exempt: true + housing_benefit_benefit_cap_reduction: 0 + housing_benefit: 30_000 + uc_housing_costs_element: 0 + benefit_cap_welfare_benefits: 57_471.94 + uc_benefit_cap: 25_323 + uc_benefit_cap_reduction: 2_148.94 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claims_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claims_universal_credit.yaml index 4e72a3c1c8..db2e9cc23f 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claims_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claims_universal_credit.yaml @@ -81,7 +81,7 @@ housing_benefit: 0 universal_credit: 0 -- name: Housing Benefit alone has not closed, so a Housing Benefit family keeps its award +- name: A Housing Benefit family claims Universal Credit when working-age awards are abolished period: 2027 input: people: @@ -99,8 +99,54 @@ tenure_type: RENT_FROM_COUNCIL output: legacy_benefits_closed: false + claims_uc_at_legacy_closure: true + claims_universal_credit: true + housing_benefit_eligible: false + +- name: In the abolition year a claiming family gets Universal Credit for the year and no Housing Benefit + period: 2026 + input: + people: + adult: + age: 40 + housing_benefit_reported: 4_000 + esa_income_reported: 5_000 + benunits: + benunit: + members: [adult] + would_claim_uc: false + would_claim_uc_at_legacy_closure: true + households: + household: + members: [adult] + tenure_type: RENT_FROM_COUNCIL + output: + claims_universal_credit: true + housing_benefit: 0 + # The claim brings the abolition of income-related ESA into force. + esa_income: 0 + +- name: In the abolition year a family that does not claim keeps its Housing Benefit and income-related ESA + period: 2026 + input: + people: + adult: + age: 40 + housing_benefit_reported: 4_000 + esa_income_reported: 5_000 + benunits: + benunit: + members: [adult] + would_claim_uc: false + would_claim_uc_at_legacy_closure: false + households: + household: + members: [adult] + tenure_type: RENT_FROM_COUNCIL + output: claims_universal_credit: false housing_benefit_eligible: true + esa_income_eligible: true universal_credit: 0 - name: Income-related ESA continues before tax credits close diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml index 082ac4d353..f0fcc73962 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml @@ -1154,7 +1154,9 @@ # 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. + # Benefit is paid in full. Working-age awards end on 1 July 2026 (SI + # 2025/1148 art. 7); a family that does not claim Universal Credit then is + # paid for 86/365 of 2026-27. period: 2026 input: people: @@ -1173,6 +1175,7 @@ benunit: members: [parent, young_person, child1, child2] would_claim_uc: false + would_claim_uc_at_legacy_closure: false households: household: members: [parent, young_person, child1, child2] @@ -1184,8 +1187,9 @@ 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 + # 30,000 x 86/365. + housing_benefit_pre_benefit_cap: 7_068.49 + housing_benefit: 7_068.49 absolute_error_margin: 1 - name: A 16-year-old out of education with armed forces independence payment does not lift the cap diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/mixed_age_couples.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/mixed_age_couples.yaml index 83e9f1ca82..a827b3d4d8 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/mixed_age_couples.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/mixed_age_couples.yaml @@ -105,6 +105,7 @@ benunit: members: [older, younger] would_claim_uc: false + would_claim_uc_at_legacy_closure: false households: household: members: [older, younger] @@ -114,7 +115,8 @@ has_mixed_age_couple_pension_credit_saving: false is_pension_credit_eligible: false pension_credit: 0 - # continuing award route (TP reg 8(2A)), unchanged from main + # continuing award route (TP reg 8(2A)), until the working-age abolition + # on 1 July 2026 for a couple that does not claim Universal Credit housing_benefit_eligible: true - name: A UC award ends the saving (art. 4(2)) diff --git a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py index 083de4e693..512739cd59 100644 --- a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py @@ -1,10 +1,14 @@ """Property-based tests for who can claim Housing Benefit. -New Housing Benefit claims are barred except where the claimant, and any -partner, has reached the qualifying age for State Pension Credit (SI 2014/1230 -reg 6A(4)); Universal Credit is not available to them (Welfare Reform Act 2012 -s.4(1)(b)). Other families keep Housing Benefit only while they continue an -existing award and do not claim Universal Credit. +These tests omit the accommodation input, so its default False applies. +For this route, new Housing Benefit claims are barred except where the +claimant, and any partner, has reached the qualifying age for State Pension +Credit (SI 2014/1230 reg 6A(4)); the model approximates this with members aged +18 or over. Other families keep Housing Benefit only while they continue an +existing award and do not claim Universal Credit. YEAR is 2026-27, when +working-age awards are still payable for part of the year, so eligibility +follows the same rule; test_housing_benefit_working_age_abolition_properties.py +covers the abolition itself. Invariants, for any generated population of families: @@ -31,6 +35,8 @@ Housing Benefit, Pension Credit and Universal Credit alike (the dependant is not a claimant or partner), so that family can claim Housing Benefit and not Universal Credit, and the mutual exclusion holds for that shape too. +Accommodation protection and the HB/non-housing-UC route are tested in the +abolition property file. Every member's is_claimant_or_partner is set from its generated role, as the FRS supplies it. Without that input an 18- or 19-year-old partner of a @@ -120,6 +126,10 @@ def situation(units, claims_all=None, flip_would_claim_uc=False, pension_bump=0. benunit = { "members": names, "would_claim_uc": {YEAR: unit["would_claim_uc"] ^ flip_would_claim_uc}, + # Working-age awards are abolished during 2026; these families do + # not claim Universal Credit at the abolition, so a continuing + # award runs until then. + "would_claim_uc_at_legacy_closure": {YEAR: False}, } # claims_all_entitled_benefits sums reported benefits across the whole # simulation, so set it per family whenever any family reports one. diff --git a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py new file mode 100644 index 0000000000..a9aed3348f --- /dev/null +++ b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py @@ -0,0 +1,461 @@ +"""Property-based tests for the abolition of working-age Housing Benefit. + +Working-age Housing Benefit awards outside specified and temporary +accommodation were abolished from 1 July 2026 in Great Britain (SI 2025/1148 +art. 7, inserted by SI 2026/409) and 1 October 2026 in Northern Ireland +(SR 2025/176 art. 7, inserted by SR 2026/115). Claimants over the qualifying +age for State Pension Credit and protected mixed-age couples keep their awards +(art. 7(4)(a) and SI 2014/1230 reg 6A(4)-(5)). + +Invariants, for any generated population of families: + +1. Parameter: each fiscal year's working_age_awards_payable value equals the + share of 6 April to 5 April falling before the statutory date, counted + independently here from the two dates (1 before 2026-27, 0 after). +2. Bounds: 0 <= housing_benefit_payable_share <= 1, and it is 1 for every + family meeting the Pension Credit age conditions (reg 6A(4)-(5)) or with + the protected-accommodation input, in every model year. +3. Abolition: from 2027 no other family receives Housing Benefit, in any + country or claim mode. +4. Differential against the pre-abolition rule: with working-age awards kept + payable by a reform, Housing Benefit is what the continuing-award rule paid + before. Under current law, Housing Benefit before the benefit cap is + exactly that amount times the country's payable share for a family outside + those savings, and exactly the same amount for every other family (the cap + applies afterwards, so the final amount is compared by 5). + Mixed populations rarely hold a 2026 working-age continuing award, so a + targeted test builds only those, for each year and jurisdiction. +5. Monotonic: the abolition never raises anyone's Housing Benefit and never + changes Universal Credit. +6. Structural: only protected-accommodation families can get both Housing + Benefit and Universal Credit, their UC housing costs element is zero, + and Housing Benefit stays within 0 and the rent. +7. Default: omitting the protected-accommodation input produces the same + Housing Benefit and Universal Credit outputs as setting it to False. +""" + +import datetime + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_uk import CountryTaxBenefitSystem, Simulation + +PROPERTY_SETTINGS = settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +ABOLITION_DATES = { + "great_britain": datetime.date(2026, 7, 1), + "northern_ireland": datetime.date(2026, 10, 1), +} +PARAMETER = "gov.dwp.housing_benefit.working_age_awards_payable" +KEEP_WORKING_AGE_AWARDS = { + f"{PARAMETER}.{jurisdiction}": {"2015-01-01.2040-12-31": 1} + for jurisdiction in ABOLITION_DATES +} +REGIONS = ["LONDON", "NORTH_EAST", "SCOTLAND", "WALES", "NORTHERN_IRELAND"] +TENURES = ["RENT_FROM_COUNCIL", "RENT_FROM_HA", "RENT_PRIVATELY", "OWNED_OUTRIGHT"] +# State Pension age is 66 until the rise to 67 phases in from 2026-27; 68 and +# over is unambiguously pension age and 60 and under unambiguously working age. +PENSION_AGE = st.integers(68, 100) +WORKING_AGE = st.integers(18, 60) +SHAPES = { + "single_pension": [("claimant", PENSION_AGE)], + "mixed_age": [("claimant", PENSION_AGE), ("partner", WORKING_AGE)], + "single_working": [("claimant", WORKING_AGE)], + "couple_working": [("claimant", WORKING_AGE), ("partner", WORKING_AGE)], + "pension_with_dependant": [ + ("claimant", PENSION_AGE), + ("dependant", st.integers(18, 19)), + ], +} +money = st.floats(0, 20_000, allow_nan=False, allow_infinity=False) + + +def fiscal_year_share_before(date, year): + """Share of the fiscal year starting 6 April `year` falling before `date`.""" + start = datetime.date(year, 4, 6) + end = datetime.date(year + 1, 4, 6) + before = (min(max(date, start), end) - start).days + return before / (end - start).days + + +@st.composite +def families(draw): + shape = draw(st.sampled_from(sorted(SHAPES))) + return dict( + shape=shape, + members=[(role, draw(age)) for role, age in SHAPES[shape]], + region=draw(st.sampled_from(REGIONS)), + tenure=draw(st.sampled_from(TENURES)), + rent=draw(money), + earnings=draw(st.one_of(st.just(0.0), money)), + savings=draw(st.one_of(st.just(0.0), st.floats(0, 20_000))), + would_claim_uc=draw(st.booleans()), + hb_reported=draw(st.sampled_from([0.0, 1.0])), + in_specified_or_temporary_accommodation=draw(st.booleans()), + ) + + +def situation(units, year, claims_all): + years = [year] if isinstance(year, int) else list(year) + + def annual(value): + return {year: value for year in years} + + people, benunits, households = {}, {}, {} + for i, unit in enumerate(units): + names = [] + for j, (role, age) in enumerate(unit["members"]): + name = f"p{i}_{j}" + person = {"age": annual(age)} + if role == "claimant": + person["employment_income"] = annual(unit["earnings"]) + person["is_parent"] = annual(unit["shape"] == "pension_with_dependant") + if unit["hb_reported"]: + person["housing_benefit_reported"] = annual(unit["hb_reported"]) + if role == "dependant": + person["current_education"] = annual("UPPER_SECONDARY") + people[name] = person + names.append(name) + benunits[f"b{i}"] = { + "members": names, + "would_claim_uc": annual(unit["would_claim_uc"]), + # These properties are about the award of a family that does not + # claim Universal Credit when its award is abolished; the claim + # itself is tested in test_uc_legacy_closure_properties.py. + "would_claim_uc_at_legacy_closure": annual( + unit.get("claims_at_closure", False) + ), + "in_specified_or_temporary_accommodation": annual( + unit["in_specified_or_temporary_accommodation"] + ), + # claims_all_entitled_benefits sums reported benefits across the + # whole simulation, so set it per family. + "claims_all_entitled_benefits": annual(claims_all), + } + households[f"h{i}"] = { + "members": names, + "region": annual(unit["region"]), + "rent": annual(unit["rent"]), + "tenure_type": annual(unit["tenure"]), + "savings": annual(unit["savings"]), + } + return {"people": people, "benunits": benunits, "households": households} + + +VARIABLES = [ + "meets_pension_credit_age_conditions", + "housing_benefit", + "housing_benefit_pre_benefit_cap", + "housing_benefit_eligible", + "housing_benefit_payable_share", + "universal_credit", + "uc_housing_costs_element", + "benunit_rent", +] + + +def calculate(units, year, claims_all, reform=None): + sim = Simulation(situation=situation(units, year, claims_all), reform=reform) + return {v: np.asarray(sim.calculate(v, year)) for v in VARIABLES} + + +def jurisdiction(unit): + return ( + "northern_ireland" if unit["region"] == "NORTHERN_IRELAND" else "great_britain" + ) + + +def check_structural(values, units): + hb = values["housing_benefit"] + protected = np.array( + [unit["in_specified_or_temporary_accommodation"] for unit in units] + ) + assert np.all(hb >= 0) + assert np.all(hb <= values["benunit_rent"] + 0.01) + assert not np.any((hb > 0) & (values["universal_credit"] > 0) & ~protected) + assert np.all(values["uc_housing_costs_element"][protected] == 0) + + +@pytest.fixture(scope="module") +def baseline_tax_benefit_system(): + """Share immutable baseline parameters across the annual-value checks.""" + return CountryTaxBenefitSystem() + + +@pytest.mark.parametrize("year", range(2015, 2041)) +def test_payable_share_is_the_day_share_before_the_statutory_date( + year, baseline_tax_benefit_system +): + parameter = baseline_tax_benefit_system.parameters( + str(year) + ).gov.dwp.housing_benefit.working_age_awards_payable + for name, date in ABOLITION_DATES.items(): + expected = fiscal_year_share_before(date, year) + assert getattr(parameter, name) == pytest.approx(expected, abs=1e-12) + + +def test_day_shares_match_the_hand_counted_days(): + # 6 April to 30 June 2026 is 86 days; 6 April to 30 September is 178. + assert fiscal_year_share_before(ABOLITION_DATES["great_britain"], 2026) == 86 / 365 + assert ( + fiscal_year_share_before(ABOLITION_DATES["northern_ireland"], 2026) == 178 / 365 + ) + + +def test_dated_reform_is_ignored_but_year_reform_covers_the_whole_fiscal_year(): + unit = dict( + shape="single_working", + members=[("claimant", 40)], + region="LONDON", + tenure="RENT_FROM_COUNCIL", + rent=5_200, + earnings=0, + savings=0, + would_claim_uc=False, + hb_reported=1, + in_specified_or_temporary_accommodation=False, + ) + parameter = f"{PARAMETER}.great_britain" + one_day = Simulation( + situation=situation([unit], 2026, claims_all=False), + reform={parameter: {"2026-07-01": 1}}, + ) + whole_year = Simulation( + situation=situation([unit], 2026, claims_all=False), + reform={parameter: {"2026": 1}}, + ) + # A key dated inside the year leaves the model year's share at the + # baseline 86 days: it is not applied. + assert one_day.calculate("housing_benefit_payable_share", 2026)[0] == pytest.approx( + 86 / 365 + ) + assert whole_year.calculate("housing_benefit_payable_share", 2026)[0] == 1 + + +@PROPERTY_SETTINGS +@given( + st.lists(families(), min_size=1, max_size=25), + st.sampled_from([2025, 2026, 2027, 2030]), + st.booleans(), +) +def test_abolition_matches_the_pre_abolition_rule_scaled_by_the_payable_share( + units, year, claims_all +): + current = calculate(units, year, claims_all) + kept = calculate(units, year, claims_all, reform=KEEP_WORKING_AGE_AWARDS) + check_structural(current, units) + check_structural(kept, units) + share = current["housing_benefit_payable_share"] + assert np.all((share >= 0) & (share <= 1)) + assert np.all(kept["housing_benefit_payable_share"] == 1) + pre_cap = "housing_benefit_pre_benefit_cap" + for i, unit in enumerate(units): + # Reg 6A(4)-(5) save a family meeting the Pension Credit age + # conditions, including a mixed-age couple with the SI 2019/37 + # art. 4 saving; reg 6A(2) saves the accommodation input. + if ( + current["meets_pension_credit_age_conditions"][i] + or unit["in_specified_or_temporary_accommodation"] + ): + assert share[i] == 1, unit + expected_share = 1 + assert current["housing_benefit"][i] == pytest.approx( + kept["housing_benefit"][i], abs=0.01 + ), unit + else: + expected_share = fiscal_year_share_before( + ABOLITION_DATES[jurisdiction(unit)], year + ) + assert share[i] == pytest.approx(expected_share, abs=1e-9), unit + if year >= 2027: + assert current["housing_benefit"][i] == 0, unit + assert not current["housing_benefit_eligible"][i], unit + assert current[pre_cap][i] == pytest.approx( + kept[pre_cap][i] * expected_share, abs=0.01 + ), unit + assert current["housing_benefit"][i] <= kept["housing_benefit"][i] + 0.01 + assert current["universal_credit"][i] == pytest.approx( + kept["universal_credit"][i], abs=0.01 + ), unit + + +@st.composite +def working_age_continuing_awards(draw, region): + """A working-age family continuing an award: reported HB, no UC claim.""" + shape = draw(st.sampled_from(["single_working", "couple_working"])) + return dict( + shape=shape, + members=[(role, draw(age)) for role, age in SHAPES[shape]], + region=region, + tenure=draw(st.sampled_from(TENURES[:3])), + rent=draw(st.floats(1_000, 20_000, allow_nan=False, allow_infinity=False)), + earnings=draw(st.one_of(st.just(0.0), money)), + savings=draw(st.one_of(st.just(0.0), st.floats(0, 15_000))), + would_claim_uc=False, + hb_reported=1.0, + in_specified_or_temporary_accommodation=False, + ) + + +@pytest.mark.parametrize("year", [2025, 2026, 2027]) +@pytest.mark.parametrize("region", ["LONDON", "NORTHERN_IRELAND"]) +@settings(PROPERTY_SETTINGS, max_examples=5) +@given(data=st.data()) +def test_working_age_continuing_award_is_paid_for_the_payable_share(year, region, data): + # Every family here continues an award, and the first has no earnings, so + # at least one has a positive entitlement in every example. + units = data.draw( + st.lists(working_age_continuing_awards(region), min_size=1, max_size=8) + ) + units[0]["earnings"] = 0.0 + current = calculate(units, year, claims_all=False) + kept = calculate(units, year, claims_all=False, reform=KEEP_WORKING_AGE_AWARDS) + pre_cap = "housing_benefit_pre_benefit_cap" + assert kept[pre_cap][0] > 0, units[0] + expected_share = fiscal_year_share_before( + ABOLITION_DATES[jurisdiction(units[0])], year + ) + assert np.allclose(current["housing_benefit_payable_share"], expected_share) + assert np.allclose(current[pre_cap], kept[pre_cap] * expected_share, atol=0.01) + assert np.all( + current["housing_benefit_eligible"] + == (kept["housing_benefit_eligible"] & (expected_share > 0)) + ) + + +@pytest.mark.parametrize("region", ["LONDON", "NORTHERN_IRELAND"]) +@settings(PROPERTY_SETTINGS, max_examples=5) +@given(data=st.data()) +def test_protected_working_age_award_is_payable_in_full_in_every_year(region, data): + units = data.draw( + st.lists(working_age_continuing_awards(region), min_size=1, max_size=8) + ) + for unit in units: + unit["in_specified_or_temporary_accommodation"] = True + years = range(2015, 2041) + sim = Simulation(situation=situation(units, years, claims_all=False)) + for year in years: + share = sim.calculate("housing_benefit_payable_share", year) + assert np.all(share == 1), (year, region, units) + + +@settings(PROPERTY_SETTINGS, max_examples=5) +@given( + st.lists(families(), min_size=1, max_size=8), + st.sampled_from([2025, 2026, 2027, 2030]), + st.booleans(), +) +def test_omitted_accommodation_input_matches_explicit_false(units, year, claims_all): + for unit in units: + unit["in_specified_or_temporary_accommodation"] = False + explicit = calculate(units, year, claims_all) + omitted = situation(units, year, claims_all) + for benunit in omitted["benunits"].values(): + benunit.pop("in_specified_or_temporary_accommodation") + sim = Simulation(situation=omitted) + for variable in VARIABLES: + np.testing.assert_array_equal( + sim.calculate(variable, year), explicit[variable], err_msg=variable + ) + + +@pytest.mark.parametrize("year", [2025, 2026, 2027]) +@pytest.mark.parametrize("region", ["LONDON", "NORTHERN_IRELAND"]) +@settings(PROPERTY_SETTINGS, max_examples=5) +@given(data=st.data()) +def test_a_family_claiming_at_the_abolition_moves_to_universal_credit( + year, region, data +): + # From the abolition year a working-age family that claims Universal + # Credit when its award is abolished gets no Housing Benefit and the + # Universal Credit it would get on any claim; before then nothing changes. + units = data.draw( + st.lists(working_age_continuing_awards(region), min_size=1, max_size=8) + ) + for unit in units: + unit["claims_at_closure"] = True + claiming = calculate(units, year, claims_all=False) + for unit in units: + unit["claims_at_closure"] = False + staying = calculate(units, year, claims_all=False) + for unit in units: + unit["would_claim_uc"] = True + anyway = calculate(units, year, claims_all=False) + if year < 2026: + for v in VARIABLES: + assert np.allclose(claiming[v], staying[v], atol=0.01), v + return + assert np.all(claiming["housing_benefit"] == 0) + assert np.allclose( + claiming["universal_credit"], anyway["universal_credit"], atol=0.01 + ) + assert np.all(staying["universal_credit"] == 0) + + +@pytest.mark.parametrize("year", [2025, 2026, 2027]) +@pytest.mark.parametrize("claims", [True, False]) +def test_the_abolition_claim_ends_only_the_couples_legacy_awards(year, claims): + # A family that claims Universal Credit when its working-age Housing + # Benefit is abolished ends the claimant's and partner's income-related + # ESA by that claim. An adult claiming ESA in their own right is not part + # of that claim and keeps their award (#2395), and a family that does not + # claim keeps both. Before the abolition nothing changes. + members = { + "claimant": { + "age": 30, + "is_claimant_or_partner": True, + "housing_benefit_reported": 1, + "esa_income_reported": 5_000, + }, + "partner": {"age": 30, "is_claimant_or_partner": True}, + "outside": { + "age": 40, + "is_claimant_or_partner": False, + "current_education": "NOT_IN_EDUCATION", + "esa_income_reported": 5_000, + }, + } + situation = { + "people": { + name: {key: {year: value} for key, value in inputs.items()} + for name, inputs in members.items() + }, + "benunits": { + "unit": { + "members": list(members), + "would_claim_uc": {year: False}, + "would_claim_uc_at_legacy_closure": {year: claims}, + "esa_income_assessable_capital": {year: 0}, + } + }, + "households": { + "household": { + "members": list(members), + "region": {year: "LONDON"}, + "tenure_type": {year: "RENT_FROM_COUNCIL"}, + "rent": {year: 5_200}, + } + }, + } + sim = Simulation(situation=situation) + moved = claims and year >= 2026 + assert not sim.calculate("legacy_benefits_closed", year)[0] + assert bool(sim.calculate("claims_uc_at_legacy_closure", year)[0]) == moved + assert sim.calculate("person_legacy_benefits_closed", year).tolist() == [ + moved, + moved, + False, + ] + assert sim.calculate("claimant_or_partner_esa_income", year)[0] == ( + 0 if moved else 5_000 + ) + assert sim.calculate("is_on_income_related_esa", year).tolist()[2] + if moved: + assert sim.calculate("housing_benefit", year)[0] == 0 diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py index fab1207355..a9ba4bb3f5 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py @@ -8,14 +8,23 @@ class housing_benefit_eligible(Variable): documentation = ( "Whether this family can receive Housing Benefit. A family whose " "claimant and any partner have reached the qualifying age for State " - "Pension Credit " - "can make a new claim; Universal Credit is not available to it. Any " - "other family keeps Housing Benefit only while it continues an " - "existing award (reported Housing Benefit), does not claim " - "Universal Credit and has not been moved off a closed legacy benefit " - "(legacy_benefits_closed). Claims for specified or temporary " - "accommodation " - "are not modelled." + "Pension Credit can make a new claim; it claims Universal Credit only " + "if DWP moved it there from Working Tax Credit " + "(left_pension_route_at_legacy_closure). " + "in_specified_or_temporary_accommodation also opens " + "the new-claim route at any age, including alongside Universal Credit, " + "and preserves existing awards. Other families keep Housing Benefit " + "only while they continue an existing award (reported Housing " + "Benefit), do not claim Universal Credit and have not been moved off " + "a closed legacy benefit (legacy_benefits_closed). Unprotected working-age " + "awards ended on 1 July 2026 in Great Britain and 1 October 2026 in " + "Northern Ireland, so from then only families meeting the Pension " + "Credit age conditions or with the accommodation input continue one. " + "Other unmodelled " + "statutory savings are listed in housing_benefit_payable_share's " + "documentation. Rental, capital and take-up rules still apply; " + "accommodation-specific eligible rent and benefit-cap rules are not " + "modelled." ) definition_period = YEAR reference = ( @@ -24,13 +33,16 @@ class housing_benefit_eligible(Variable): "https://www.legislation.gov.uk/uksi/2014/1230/regulation/8", "https://www.legislation.gov.uk/uksi/2014/1230/regulation/46", "https://www.legislation.gov.uk/uksi/2019/37/article/4", + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", + "https://www.legislation.gov.uk/nisr/2025/176/article/7", "https://www.legislation.gov.uk/ukpga/2012/5/section/4", "https://www.legislation.gov.uk/nisr/2016/226/regulation/4A", ) def formula(benunit, period, parameters): person = benunit.members - # New claims are barred except where the claimant, and any partner, + # Apart from the accommodation exception, new claims are barred + # except where the claimant, and any partner, # has reached the qualifying age for State Pension Credit # (SI 2014/1230 reg 6A(4); NI: SR 2016/226 reg 4A(4)), or the claim # is made by the older member of a mixed-age couple that keeps the @@ -54,15 +66,25 @@ def formula(benunit, period, parameters): # (since 15 May 2019), claim Universal Credit instead. They keep an # existing award until they claim it (reg 8(2A)), so this route also # rules out receiving Universal Credit. Working-age awards outside - # specified or temporary accommodation ended on 1 July 2026 in Great - # Britain (SI 2025/1148 art. 7); that is not modelled. + # specified or temporary accommodation were abolished from 1 July + # 2026 in Great Britain (SI 2025/1148 art. 7) and 1 October 2026 in + # Northern Ireland (SR 2025/176 art. 7); a family outside the reg + # 6A(2)-(5) savings continues one only for the part of the year + # before that date (housing_benefit_payable_share). already_claiming = add(benunit, period, ["housing_benefit_reported"]) > 0 claiming_uc = benunit("claims_universal_credit", period) # Once another of the family's legacy benefits has closed, DWP's # migration notice ends this award even if the family does not claim # Universal Credit (reg 46(1)(a), (2)). migrated = benunit("legacy_benefits_closed", period) - continuing_award = already_claiming & ~claiming_uc & ~migrated + still_payable = benunit("housing_benefit_payable_share", period) > 0 + continuing_award = already_claiming & ~claiming_uc & ~migrated & still_payable + # Reg 6A(2) permits new accommodation claims irrespective of UC; + # reg 8(3) preserves accommodation HB when UC is claimed. + # NI equivalents: SR 2016/226 regs 4A(2) and 6(3). + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) social = benunit.any(person("in_social_housing", period)) lha_eligible = benunit("LHA_eligible", period) pension_age_regulations = benunit( @@ -76,7 +98,7 @@ def formula(benunit, period, parameters): hb_capital.working_age.limit, ) return ( - (pension_age | continuing_award) + (pension_age | continuing_award | protected_accommodation) & (social | lha_eligible) & (capital <= limit) ) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py new file mode 100644 index 0000000000..d33141a7cc --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py @@ -0,0 +1,67 @@ +from policyengine_uk.model_api import * + + +class housing_benefit_payable_share(Variable): + value_type = float + entity = BenUnit + label = "Share of the year Housing Benefit is payable" + documentation = ( + "Share of the year for which this family's Housing Benefit award stays " + "payable. Unprotected working-age awards outside specified and temporary " + "accommodation were abolished from 1 July 2026 in Great Britain and " + "1 October 2026 in Northern Ireland, so for a family outside the " + "pension-age savings this is the share of the year before that " + "date, unless in_specified_or_temporary_accommodation is True. " + "That input preserves a share of 1 in every year. A family meeting " + "the Pension Credit age conditions (meets_pension_credit_age_conditions: " + "a single claimant, or both members of a couple, over the qualifying " + "age, or a mixed-age couple with the SI 2019/37 art. 4 saving) also " + "has a share of 1; a mixed-age couple without the saving does not. " + "Other saved working-age awards are not modelled, so their awards " + "wrongly end on the abolition date: qualifying HB claims during the " + "final UC assessment period on reaching pension-credit age, with " + "entitlement from reaching that age and the relevant Decisions and " + "Appeals Schedule 1 paragraph 26 applying (reg 6A(3); NI reg 4A(3)); " + "income-related " + "ESA entitlement immediately before abolition with an appointee then, " + "or a determination in the preceding six months that one was likely " + "needed (art 7(4)(b) and 3A(2)); and the delayed termination for " + "claimants excluded from UC immediately before abolition under UC " + "reg 19(1)(b) or (c), until the day after the last day of that " + "exclusion (art 7(2)). These need award history, decisions or dates " + "which no inputs supply." + ) + definition_period = YEAR + unit = "/1" + reference = ( + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A", + "https://www.legislation.gov.uk/nisr/2025/176/article/7", + "https://www.legislation.gov.uk/uksi/2019/37/article/4", + ) + + def formula(benunit, period, parameters): + payable = parameters(period).gov.dwp.housing_benefit.working_age_awards_payable + country = benunit.household("country", period) + # Great Britain's date applies everywhere but Northern Ireland, + # including an unknown country. + working_age_share = where( + country == country.possible_values.NORTHERN_IRELAND, + payable.northern_ireland, + payable.great_britain, + ) + # SI 2025/1148 art. 7(4)(a) saves claimants within SI 2014/1230 + # reg 6A(2)-(5) (NI: SR 2025/176 art. 7(4)(a), reg 4A(2)-(5)). + # Reg 6A(4) (a single claimant, or both members of a couple, over the + # qualifying age for State Pension Credit) and 6A(5) (a mixed-age + # couple saved by SI 2019/37 art. 4) are the Pension Credit age + # conditions; the art. 4 saving is inferred from current receipt + # (has_mixed_age_couple_pension_credit_saving). Specified and + # temporary accommodation is supplied explicitly under reg 6A(2) (NI + # reg 4A(2)). The other savings described in the variable + # documentation have no inputs for their history and date conditions. + pension_age_saving = benunit("meets_pension_credit_age_conditions", period) + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) + return where(pension_age_saving | protected_accommodation, 1, working_age_share) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py index 5d54f3e83d..073c18810c 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py @@ -11,8 +11,11 @@ class housing_benefit_pre_benefit_cap(Variable): def formula(benunit, period, parameters): eligible = benunit("housing_benefit_eligible", period) would_claim = benunit("would_claim_housing_benefit", period) + # A working-age award abolished part-way through the year is paid + # for the part before the abolition date. + payable_share = benunit("housing_benefit_payable_share", period) return where( eligible & would_claim, - benunit("housing_benefit_entitlement", period), + benunit("housing_benefit_entitlement", period) * payable_share, 0, ) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py b/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py index 263e66ec9d..32da413447 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py @@ -14,7 +14,10 @@ class in_specified_or_temporary_accommodation(Variable): "equivalents are Transitional Provisions Regulations 2016 regs 2 " "and 4A(2), and Universal Credit Regulations 2016 Schedule 1 " "paragraphs 4(2)-(5) and 4A. This is a supplied status, not inferred " - "from tenure or a generic supported-housing label. The working-age " + "from tenure or a generic supported-housing label. It protects " + "working-age awards from abolition and permits new Housing Benefit " + "claims, subject to the model's other entitlement and take-up rules. " + "The working-age " "Housing Benefit earnings disregard for specified or temporary " "accommodation (SI 2006/213 Sch 4 para 18; NI SR 2006/405 Sch 5 para " "18), which refers to the same paragraphs, reads it. " diff --git a/policyengine_uk/variables/gov/dwp/income_support.py b/policyengine_uk/variables/gov/dwp/income_support.py index 1f9cd8ec94..b3d88a3397 100644 --- a/policyengine_uk/variables/gov/dwp/income_support.py +++ b/policyengine_uk/variables/gov/dwp/income_support.py @@ -15,5 +15,11 @@ def formula(benunit, period, parameters): # Once one of the family's legacy benefits has closed, DWP's # migration notice ends this award too: on a Universal Credit claim # (SI 2014/1230 reg 8(2A)) or at the notice's deadline (reg 46(1)(a)). - closed = benunit("legacy_benefits_closed", period) + # A family that claims Universal Credit when its Housing Benefit is + # abolished ends this award by that claim too. A family already + # claiming Universal Credit is left to the overlap rules (#1914). + closed = benunit("legacy_benefits_closed", period) | ( + benunit("claims_uc_at_legacy_closure", period) + & ~benunit("would_claim_uc", period) + ) return where(closed, 0, benunit("income_support_entitlement", period)) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/claims_uc_at_legacy_closure.py b/policyengine_uk/variables/gov/dwp/universal_credit/claims_uc_at_legacy_closure.py index 2ee3f940cb..5f7352b818 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/claims_uc_at_legacy_closure.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/claims_uc_at_legacy_closure.py @@ -7,13 +7,25 @@ class claims_uc_at_legacy_closure(Variable): label = "Claims Universal Credit when its legacy benefits close" documentation = ( "Whether this family claims Universal Credit because DWP moved it off " - "its legacy benefits (legacy_benefits_closed) and it would claim then " + "its legacy benefits (legacy_benefits_closed) or its working-age " + "Housing Benefit award was abolished, and it would claim then " "(would_claim_uc_at_legacy_closure)." ) definition_period = YEAR - reference = "https://www.legislation.gov.uk/uksi/2014/1230/regulation/44" + reference = ( + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/44", + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", + "https://www.legislation.gov.uk/nisr/2025/176/article/7", + ) def formula(benunit, period, parameters): - return benunit("legacy_benefits_closed", period) & benunit( - "would_claim_uc_at_legacy_closure", period - ) + # The working-age Housing Benefit abolition (SI 2025/1148 art. 7; NI: + # SR 2025/176 art. 7) also prompts a claim. Unlike a migration notice + # it ends no other award for a family that does not claim, which keeps + # its Housing Benefit for the part of the year before abolition + # (housing_benefit_payable_share). + housing_benefit_abolished = ( + add(benunit, period, ["housing_benefit_reported"]) > 0 + ) & (benunit("housing_benefit_payable_share", period) < 1) + closed = benunit("legacy_benefits_closed", period) | housing_benefit_abolished + return closed & benunit("would_claim_uc_at_legacy_closure", period) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py index 62f7c4d102..e309016f9a 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py @@ -7,6 +7,16 @@ class uc_housing_costs_element(Variable): label = "Universal Credit housing costs element" definition_period = YEAR unit = GBP + documentation = ( + "Universal Credit support for rent, excluding payments for specified " + "or temporary accommodation when " + "in_specified_or_temporary_accommodation is supplied as True. " + "Those payments can instead be covered by Housing Benefit." + ) + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3", + "https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/3", + ) def formula(benunit, period, parameters): # The family's own tenure: a boarder or lodger is a private renter @@ -28,4 +38,11 @@ def formula(benunit, period, parameters): default=0, ) non_dependent_deductions = benunit("uc_non_dep_deductions", period) - return max_(max_housing_costs - non_dependent_deductions, 0) + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) + return where( + protected_accommodation, + 0, + max_(max_housing_costs - non_dependent_deductions, 0), + ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/person_legacy_benefits_closed.py b/policyengine_uk/variables/gov/dwp/universal_credit/person_legacy_benefits_closed.py index 9276abf287..6b3ba2a958 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/person_legacy_benefits_closed.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/person_legacy_benefits_closed.py @@ -14,12 +14,17 @@ class person_legacy_benefits_closed(Variable): "claiming in their own right use only their own reports and Pension " "Credit qualifying age. Their reports neither close nor shield the " "couple's award, and the couple's reports do not close theirs. No new " - "notice, deadline, run-on or legal relationship inference is made." + "notice, deadline, run-on or legal relationship inference is made. " + "The claimant's and partner's awards also end when the family claims " + "Universal Credit because its working-age Housing Benefit was " + "abolished (claims_uc_at_legacy_closure without would_claim_uc)." ) definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2014/1230/regulation/44", "https://www.legislation.gov.uk/uksi/2014/1230/regulation/46", + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/8", + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", ) def formula(person, period, parameters): @@ -30,8 +35,19 @@ def reports(*variables): own_closed = legacy_award_is_closed( reports, pension_age, pension_age & False, parameters(period).gov.dwp ) + benunit = person.benunit + # A family that claims Universal Credit when its working-age Housing + # Benefit is abolished (SI 2025/1148 art. 7) ends the claimant's and + # partner's other legacy awards by that claim, as a claim after a + # migration notice does. A family already claiming Universal Credit + # is left to the overlap rules (#1914), and a member claiming in + # their own right is not part of the family's claim. + couple_closed = benunit("legacy_benefits_closed", period) | ( + benunit("claims_uc_at_legacy_closure", period) + & ~benunit("would_claim_uc", period) + ) return where( person("is_claimant_or_partner", period), - person.benunit("legacy_benefits_closed", period), + couple_closed, own_closed, )