diff --git a/changelog.d/2014.fixed.md b/changelog.d/2014.fixed.md new file mode 100644 index 0000000000..7f69203350 --- /dev/null +++ b/changelog.d/2014.fixed.md @@ -0,0 +1 @@ +- Charge Universal Credit, Housing Benefit and national Council Tax Reduction non-dependant deductions for members of the claimant's own benefit unit who are not the claimant, partner or a child or young person, such as an adult son (UC Regs 2013 Sch 4 para 9; HB Regs 2006 and HB (SPC) Regs 2006 reg 3; SI 2012/2885 reg 9 and the Welsh and Scottish equivalents). Housing Benefit and Council Tax Reduction apportion every non-dependant between joint occupiers or jointly liable applicants, a lodger's adult son included, and a household head is never her own benefit unit's non-dependant. Apply the Universal Credit renter exemption from housing cost contributions (Sch 4 para 15), and give the para 16(2)(b) Pension Credit exemption only to the claimant or partner who receives it, so a pensioner's adult son is no longer exempt by her award. diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/benefit_unit_non_dependants.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/benefit_unit_non_dependants.yaml new file mode 100644 index 0000000000..1edd5322e9 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/benefit_unit_non_dependants.yaml @@ -0,0 +1,1363 @@ +# Non-dependants within the claimant's own benefit unit. +# +# A benefit unit can include someone aged 16 or over who is neither the +# claimant or partner nor a child or young person the claimant is responsible +# for, such as an adult son or a 19-year-old who has left education. In law +# that person is a non-dependant of the renter (UC Regs 2013 Sch 4 para +# 9(1)(c) and (2); HB Regs 2006 and HB (SPC) Regs 2006 reg 3; CTR SI +# 2012/2885 reg 9, WSI 2013/3029 reg 9, SSI 2021/249 reg 8), so their family's +# claim bears a deduction for them, subject to each scheme's exemptions. +# +# Amounts for 2026-27: +# - UC housing cost contribution £96.55 a month (Sch 4 para 14(1)), aged 21 +# or over (para 16(2)(a)): £1,158.60 a year. +# - HB non-dependant aged 18 or over not in remunerative work £20.40 a week +# (reg 74(1)(b)): £1,060.80 a year. In remunerative work with gross income +# of £300 a week: £64.35 (reg 74(2)(c)), £3,346.20 a year. The HB cases +# give part-time earners £50 a week for 10 hours, below remunerative work +# (16 hours, reg 6) and inside the lowest band. + +- name: UC, an adult son in the renter's benefit unit bears a housing cost contribution + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + is_benefit_unit_non_dependant_for_universal_credit: [false, true] + uc_individual_non_dep_deduction: [0, 1_158.60] + uc_non_dep_deductions: 1_158.60 + +- name: UC, no contribution for a non-dependant in the benefit unit under 21 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + # Left education: not a qualifying young person. + age: 19 + is_claimant_or_partner: false + is_qualifying_young_person_for_universal_credit: false + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + is_benefit_unit_non_dependant_for_universal_credit: [false, true] + uc_non_dep_deductions: 0 + +- name: UC, a qualifying young person the renter is responsible for is not a non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + daughter: + age: 19 + is_claimant_or_partner: false + is_qualifying_young_person_for_universal_credit: true + benunits: + benunit: + members: [parent, daughter] + households: + household: + members: [parent, daughter] + output: + is_benefit_unit_non_dependant_for_universal_credit: [false, false] + uc_non_dep_deductions: 0 + +# Sch 4 para 9(2)(g): a qualifying young person for whom no one in the +# renter's extended benefit unit is responsible is not a non-dependant. +- name: UC, a qualifying young person looked after by a local authority is not a non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + carer: + age: 50 + is_household_head: true + is_claimant_or_partner: true + young_person: + age: 17 + is_claimant_or_partner: false + is_qualifying_young_person_for_universal_credit: true + is_looked_after_by_local_authority: true + benunits: + benunit: + members: [carer, young_person] + households: + household: + members: [carer, young_person] + output: + is_child_or_qualifying_young_person_for_universal_credit: [false, false] + is_benefit_unit_non_dependant_for_universal_credit: [false, false] + uc_non_dep_deductions: 0 + +# Sch 4 para 15: no contribution for any non-dependant if the renter, or +# either joint renter of a couple, receives the PIP daily living component. +- name: UC, no contribution where the renter receives the PIP daily living component + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + pip_dl: 3_000 + son: + age: 25 + is_claimant_or_partner: false + aunt: + # A non-dependant in another family. + age: 30 + benunits: + benunit_1: + members: [parent, son] + benunit_2: + members: [aunt] + households: + household: + members: [parent, son, aunt] + output: + uc_non_dep_deductions_renter_exempt: [true, false] + uc_non_dep_deductions: [0, 0] + +- name: UC, no contribution where the renter's partner is blind + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent_1: + age: 52 + is_household_head: true + is_claimant_or_partner: true + parent_2: + age: 50 + is_claimant_or_partner: true + is_blind: true + son: + age: 25 + is_claimant_or_partner: false + benunits: + benunit: + members: [parent_1, parent_2, son] + households: + household: + members: [parent_1, parent_2, son] + output: + uc_non_dep_deductions: 0 + +# Para 15(1)(a) exempts "any renter who is a single person to whom +# sub-paragraph (2) applies": the renter's own disability, not a child's. +- name: UC, a disabled child in the renter's family does not exempt the renter + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + child: + age: 10 + is_claimant_or_partner: false + dla_sc_category: MIDDLE + benunits: + benunit: + members: [parent, son, child] + households: + household: + members: [parent, son, child] + output: + # A child under 16 is never a non-dependant (WRA 2012 s.40). + is_benefit_unit_non_dependant_for_universal_credit: [false, true, false] + is_benefit_unit_non_dependant_for_legacy_benefits: [false, true, false] + uc_non_dep_deductions_renter_exempt: false + uc_non_dep_deductions: 1_158.60 + +# Sch 4 para 16(2)(d): no contribution for a non-dependant receiving +# Attendance Allowance. +- name: UC, no contribution for a non-dependant in the benefit unit receiving Attendance Allowance + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + attendance_allowance: 3_000 + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + uc_non_dep_deduction_exempt: [false, true] + uc_non_dep_deductions: 0 + +# Pension Credit is the claimant's award: an adult son in a family on Pension +# Credit is not receiving it, so para 16(2)(b) does not exempt him. +- name: UC, a family's Pension Credit does not exempt the non-dependant in its benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 70 + is_claimant_or_partner: true + son: + age: 40 + is_claimant_or_partner: false + benunits: + benunit: + members: [parent, son] + pension_credit: 2_000 + output: + uc_non_dep_deduction_exempt: [true, false] + +- name: UC, the head's family bears non-dependants in its own benefit unit and in other families + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + aunt: + age: 60 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [parent, son] + benunit_2: + members: [aunt] + households: + household: + members: [parent, son, aunt] + output: + uc_non_dep_deductions: [2_317.20, 0] + +# A non-dependant family's own adult member is a non-dependant of the +# household head, counted once, in the head's claim (Sch 4 para 9(2)(f)). The +# head is below State Pension age, so has a Universal Credit claim to count +# them in (uc_non_dependants_counted). +- name: UC, an adult in a non-dependant family is the household head's non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 60 + is_household_head: true + is_claimant_or_partner: true + daughter: + age: 35 + is_claimant_or_partner: true + grandson: + age: 23 + is_claimant_or_partner: false + benunits: + benunit_1: + members: [head] + benunit_2: + members: [daughter, grandson] + households: + household: + members: [head, daughter, grandson] + output: + uc_non_dep_deductions: [2_317.20, 0] + +# A sharer of the rent bears the non-dependant in its own benefit unit; the +# household head's family does not (para 9(2)(f)). +- name: UC, a sharer of the rent bears the non-dependant in its own benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + sharer_son: + age: 26 + is_claimant_or_partner: false + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer, sharer_son] + liable_for_share_of_household_rent: true + households: + household: + members: [head, sharer, sharer_son] + rent: 12_000 + output: + uc_non_dep_deductions: [0, 1_158.60] + +- name: UC, the housing costs element nets off the contribution for an adult son + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + benunits: + benunit: + members: [parent, son] + benunit_tenure_type: RENT_FROM_COUNCIL + benunit_rent: 6_000 + households: + household: + members: [parent, son] + output: + # £6,000 - £1,158.60. + uc_housing_costs_element: 4_841.40 + +- name: HB, an adult son in the claimant's benefit unit working part time + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + employment_income: 2_600 + weekly_hours: 10 + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + is_benefit_unit_non_dependant_for_legacy_benefits: [false, true] + housing_benefit_non_dep_deductions: 1_060.80 + +- name: HB, an adult daughter in the claimant's benefit unit working full time on £300 a week + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + daughter: + age: 22 + is_claimant_or_partner: false + employment_income: 15_600 + weekly_hours: 37.5 + benunits: + benunit: + members: [parent, daughter] + households: + household: + members: [parent, daughter] + output: + housing_benefit_non_dep_deductions: 3_346.20 + +- name: HB, no deduction for a non-dependant in the benefit unit under 18 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 17 + is_claimant_or_partner: false + is_child_or_qualifying_young_person_for_child_benefit: false + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + is_benefit_unit_non_dependant_for_legacy_benefits: [false, true] + housing_benefit_non_dep_deductions: 0 + +# HB reg 19: a Child Benefit qualifying young person is in the claimant's +# family, not a non-dependant. +- name: HB, a young person in the claimant's family is not a non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + daughter: + age: 19 + is_claimant_or_partner: false + is_child_or_qualifying_young_person_for_child_benefit: true + benunits: + benunit: + members: [parent, daughter] + households: + household: + members: [parent, daughter] + output: + is_benefit_unit_non_dependant_for_legacy_benefits: [false, false] + housing_benefit_non_dep_deductions: 0 + +# A joint tenant's adult son normally resides with both joint tenants (reg +# 3(1) and (4)) and is in neither's family under reg 3(2)(a), so he is a +# non-dependant of each, apportioned by their shares of the rent (reg 74(5)). +- name: HB, joint tenants share the deduction for one tenant's adult son + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + employment_income: 2_600 + weekly_hours: 10 + sharer: + age: 48 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head, son] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + households: + household: + members: [head, son, sharer] + rent: 12_000 + output: + share_of_household_rent: [0.5, 0.5] + housing_benefit_non_dep_deductions: [530.40, 530.40] + +# The same son coded as his own benefit unit gives the same deductions: the +# result does not depend on how the survey groups him. +- name: HB, joint tenants share the deduction for an adult son in his own benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 10 + sharer: + age: 48 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + benunit_3: + members: [son] + households: + household: + members: [head, son, sharer] + rent: 12_000 + output: + housing_benefit_non_dep_deductions: [530.40, 530.40, 0] + +# A lodger's adult son is the lodger's non-dependant, and the householder's +# too: reg 3(2)(e)(i) excludes only the person liable to pay the householder. +# The lodger is not a joint occupier with the householder, so reg 74(5) does +# not apportion him and each bears the whole deduction. +- name: HB, a lodger's adult son is a non-dependant of the lodger and of the householder + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 60 + is_household_head: true + is_claimant_or_partner: true + lodger: + age: 45 + is_claimant_or_partner: true + rent_paid_as_lodger: 5_200 + lodger_son: + age: 21 + is_claimant_or_partner: false + employment_income: 2_600 + weekly_hours: 10 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [lodger, lodger_son] + households: + household: + members: [head, lodger, lodger_son] + rent: 9_000 + output: + housing_benefit_non_dep_deductions: [1_060.80, 1_060.80] + +- name: CTR, the claimant's family bears the non-dependant in its benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + council_tax_reduction_individual_non_dep_deduction_eligible: [false, true] + council_tax_reduction_non_dep_deductions: 300 + +- name: CTR, no deduction for a non-dependant in the benefit unit under 18 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 17 + is_claimant_or_partner: false + is_child_or_qualifying_young_person_for_child_benefit: false + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + council_tax_reduction_individual_non_dep_deduction_eligible: [false, false] + council_tax_reduction_non_dep_deductions: 0 + +# An applicant's adult son normally resides with both jointly liable people +# and is in neither's family (SI 2012/2885 reg 9(1), (2)(a)), so the deduction +# is apportioned equally between them (Sch 1 para 8(5)). +- name: CTR, jointly liable applicants share the deduction for one applicant's adult son + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + council_tax_reduction_individual_non_dep_deduction: 300 + sharer: + age: 48 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head, son] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + households: + household: + members: [head, son, sharer] + rent: 12_000 + output: + council_tax_reduction_joint_liability_share: [0.5, 0.5] + council_tax_reduction_non_dep_deductions: [150, 150] + +# Para 8(5) still apportions a non-dependant from another family between the +# jointly liable people. +- name: CTR, jointly liable families share the deduction for another family's adult + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + friend: + age: 30 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + benunit_3: + members: [friend] + households: + household: + members: [head, sharer, friend] + rent: 12_000 + output: + council_tax_reduction_non_dep_deductions: [150, 150, 0] + +# SI 2012/2885 reg 9(2)(e) excludes only the person liable to pay the +# applicant, so a lodger's adult son who pays nothing himself is the +# applicant's non-dependant. +- name: CTR, a lodger's adult son is the applicant's non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 60 + is_household_head: true + is_claimant_or_partner: true + lodger: + age: 45 + is_claimant_or_partner: true + rent_paid_as_lodger: 5_200 + lodger_son: + age: 21 + is_claimant_or_partner: false + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [lodger, lodger_son] + households: + household: + members: [head, lodger, lodger_son] + rent: 9_000 + output: + council_tax_reduction_individual_non_dep_deduction_eligible: [false, false, true] + council_tax_reduction_non_dep_deductions: [300, 0] + +# Only a family that claims bears deductions; the non-dependant's own family +# does not. +- name: CTR, a non-dependant family with an adult member bears no deductions itself + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + is_household_head: true + is_claimant_or_partner: true + daughter: + age: 45 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 200 + grandson: + age: 23 + is_claimant_or_partner: false + council_tax_reduction_individual_non_dep_deduction: 100 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [daughter, grandson] + households: + household: + members: [head, daughter, grandson] + output: + council_tax_reduction_individual_non_dep_deduction_eligible: [false, true, true] + council_tax_reduction_non_dep_deductions: [300, 0] + +# With the Housing Benefit and Council Tax Reduction scales and exemptions as +# legislated (#2004): a non-dependant within the benefit unit is banded on +# their own income and exempt only on their own circumstances. + +# HB reg 74(1)(b): a non-dependant aged 18 or over not in remunerative work +# pays the lowest amount, £20.40 a week, whatever their income. +- name: HB, an adult son in the claimant's benefit unit with no income pays the lowest amount + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + current_education: NOT_IN_EDUCATION + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + output: + housing_benefit_non_dep_deductions: 1_060.80 + +# HB reg 74(7)(c); CTR SI 2012/2885 Sch 1 para 8(7)(c). +- name: HB and CTR, no deduction for a full-time student in the claimant's benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 70 + is_household_head: true + is_claimant_or_partner: true + son: + age: 20 + is_claimant_or_partner: false + current_education: TERTIARY + benunits: + benunit: + members: [parent, son] + households: + household: + members: [parent, son] + country: ENGLAND + local_authority: MAIDSTONE + output: + housing_benefit_non_dep_deductions: 0 + council_tax_reduction_non_dep_deductions: 0 + +# HB reg 74(8)(a) exempts a non-dependant under 25 who is on Income Support. +# The family's Income Support is the parent's award, so the 22-year-old son is +# not on it and is not exempt. +- name: HB, a parent's Income Support does not exempt the adult son in their benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 22 + is_claimant_or_partner: false + current_education: NOT_IN_EDUCATION + benunits: + benunit: + members: [parent, son] + income_support: 3_000 + households: + household: + members: [parent, son] + output: + housing_benefit_non_dep_deduction_exempt: [false, false] + housing_benefit_non_dep_deductions: 1_060.80 + +# HB reg 74(2)-(4): a non-dependant couple pays one deduction, the higher, on +# their joint income; their adult son is a non-dependant in his own right, on +# his own income. Father and son each earn £300 a week working full time, so +# the couple and the son each pay the £279-£365 band's £64.35 (not one £119.85 +# on a £600 family income): £128.70 a week, £6,692.40 a year. +- name: HB, a non-dependant couple and their adult son pay two deductions + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 75 + is_household_head: true + is_claimant_or_partner: true + father: + age: 55 + is_claimant_or_partner: true + employment_income: 15_600 + weekly_hours: 37.5 + current_education: NOT_IN_EDUCATION + mother: + age: 52 + is_claimant_or_partner: true + current_education: NOT_IN_EDUCATION + son: + age: 23 + is_claimant_or_partner: false + employment_income: 15_600 + weekly_hours: 37.5 + current_education: NOT_IN_EDUCATION + benunits: + head_unit: + members: [head] + family: + members: [father, mother, son] + would_claim_uc: false + households: + household: + members: [head, father, mother, son] + output: + household_benefits_individual_non_dep_deduction: [0, 3_346.20, 1_060.80, 3_346.20] + housing_benefit_non_dep_deductions: [6_692.40, 0] + +# SI 2012/2885 Sch 1 para 8(2)(c): £576.92 a week is in the £485-£605 band, +# £13.30 a week in 2026-27, £691.60 a year. +- name: CTR, England pensioner scheme charges the working son in the claimant's benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_household_head: true + is_claimant_or_partner: true + son: + age: 40 + is_claimant_or_partner: false + employment_income: 30_000 + weekly_hours: 37.5 + current_education: NOT_IN_EDUCATION + benunits: + benunit: + members: [pensioner, son] + households: + household: + members: [pensioner, son] + country: ENGLAND + local_authority: MAIDSTONE + output: + council_tax_reduction_individual_non_dep_deduction: [0, 691.60] + council_tax_reduction_non_dep_deductions: 691.60 + +# SI 2012/2885 Sch 1 para 8(6): the exemption belongs to each applicant ("the +# applicant or his partner"). With two jointly liable applicants, one's +# blindness does not exempt the other's claim. +- name: CTR, a blind household head does not exempt a jointly liable sharer + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + is_blind: true + sharer: + age: 48 + is_claimant_or_partner: true + friend: + age: 30 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + benunit_3: + members: [friend] + households: + household: + members: [head, sharer, friend] + rent: 12_000 + country: SCOTLAND + output: + council_tax_reduction_applicant_has_non_dep_exemption: [true, false, false] + council_tax_reduction_non_dep_deductions: [0, 150, 0] + +- name: CTR, a blind sharer is exempt but the household head is not + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + is_blind: true + friend: + age: 30 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + benunit_3: + members: [friend] + households: + household: + members: [head, sharer, friend] + rent: 12_000 + country: SCOTLAND + output: + council_tax_reduction_applicant_has_non_dep_exemption: [false, true, false] + council_tax_reduction_non_dep_deductions: [150, 0, 0] + +# The sharer's adult son is a non-dependant of both jointly liable people. +# The blind sharer is exempt; the head is not and bears half (para 8(5)). +- name: CTR, a blind sharer is exempt for its own adult son but the head still bears half + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + is_blind: true + sharer_son: + age: 25 + is_claimant_or_partner: false + council_tax_reduction_individual_non_dep_deduction: 300 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer, sharer_son] + liable_for_share_of_household_rent: true + households: + household: + members: [head, sharer, sharer_son] + rent: 12_000 + country: SCOTLAND + output: + council_tax_reduction_non_dep_deductions: [150, 0] + +# The English working-age local schemes keep their own aggregation, which +# charges only other families' non-dependants. Widening the shared +# eligibility must not make a jointly liable sharer bear the head's own son. +- name: Local CTR (Newham), a jointly liable sharer does not bear the head's own in-unit son + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + son: + age: 25 + is_claimant_or_partner: false + employment_income: 20_000 + weekly_hours: 37.5 + sharer: + age: 48 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head, son] + would_claim_council_tax_reduction: true + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + would_claim_council_tax_reduction: true + households: + household: + members: [head, son, sharer] + rent: 12_000 + region: LONDON + local_authority: NEWHAM + tenure_type: RENT_PRIVATELY + council_tax: 1_800 + output: + council_tax_reduction_joint_liability_share: [0.5, 0.5] + newham_council_tax_reduction_non_dep_deductions: [0, 0] + newham_council_tax_reduction: [630, 630] + +# SI 2012/2885 reg 9(2)(a): the applicant's own family are not its +# non-dependants, so an individual deduction set for the applicant (here as +# an input) is never charged to the applicant's own claim. +- name: CTR, an applicant is never charged a deduction for itself + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 300 + friend: + age: 30 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 200 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [friend] + households: + household: + members: [head, friend] + rent: 12_000 + output: + council_tax_reduction_claimant_benunit: [true, false] + council_tax_reduction_non_dep_deductions: [200, 0] + +# HB reg 74(5) apportions the sharer's son between the joint occupiers; reg +# 74(6) then removes the deduction from the blind sharer's claim only. The son +# earns £384.62 a week: £105.20 (reg 74(2)(d)) x 52 = £5,470.40, half each. +- name: HB, a blind sharer is exempt for its own adult son but the head still bears half + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + is_blind: true + sharer_son: + age: 25 + is_claimant_or_partner: false + employment_income: 20_000 + weekly_hours: 37.5 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer, sharer_son] + liable_for_share_of_household_rent: true + households: + household: + members: [head, sharer, sharer_son] + rent: 12_000 + country: SCOTLAND + output: + household_benefits_individual_non_dep_deduction: [0, 0, 5_470.40] + housing_benefit_non_dep_deductions: [2_735.20, 0] + +# A lodger on the PIP daily living component is exempt for its own son (reg +# 74(6)); the householder, who is not exempt, still bears him in full. +- name: HB, an exempt lodger is not charged for its own adult son but the householder is + period: 2026 + absolute_error_margin: 0.01 + input: + people: + householder: + age: 60 + is_household_head: true + is_claimant_or_partner: true + lodger: + age: 45 + is_claimant_or_partner: true + pip_dl: 3_000 + rent_paid_as_lodger: 5_200 + lodger_son: + age: 25 + is_claimant_or_partner: false + employment_income: 20_000 + weekly_hours: 37.5 + benunits: + benunit_1: + members: [householder] + benunit_2: + members: [lodger, lodger_son] + households: + household: + members: [householder, lodger, lodger_son] + rent: 9_000 + country: SCOTLAND + output: + housing_benefit_non_dep_deductions: [5_470.40, 0] + +# A household head who is liable but is not her benefit unit's claimant or +# partner applies alone (council_tax_reduction_head_applies_alone). The young +# couple in her benefit unit are then outside her family and are her +# non-dependants (SI 2012/2885 reg 9(1), (2)(a)); as a couple they pay one +# deduction, the higher (Sch 1 para 8(3)). +- name: CTR, a head applying alone is charged once for the couple in her benefit unit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + grandmother: + age: 70 + is_household_head: true + is_claimant_or_partner: false + son: + age: 30 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 300 + partner: + age: 29 + is_claimant_or_partner: true + council_tax_reduction_individual_non_dep_deduction: 200 + benunits: + benunit: + members: [grandmother, son, partner] + households: + household: + members: [grandmother, son, partner] + rent: 12_000 + output: + council_tax_reduction_head_applies_alone: [true] + is_benefit_unit_non_dependant_for_council_tax_reduction: [false, true, true] + council_tax_reduction_non_dep_deductions: [300] + +# Sch 4 para 9(2)(f): an in-unit non-dependant counts in his own family's +# Universal Credit claim if it makes one. A pension-age head makes none, so the +# claim that counts the household's non-dependants, the jointly liable +# sharer's, counts him. +- name: UC, a non-claiming head's adult son counts in the sharer's claim + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + is_household_head: true + is_claimant_or_partner: true + son: + age: 30 + is_claimant_or_partner: false + sharer: + age: 40 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head, son] + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + households: + household: + members: [head, son, sharer] + rent: 12_000 + output: + uc_non_dependants_counted: [false, true] + uc_non_dep_deductions: [0, 1_158.60] + +# Sch 4 para 16(2)(b) exempts a non-dependant "in receipt of state pension +# credit". Pension Credit is the claimant's award, so a pensioner's adult son +# in her benefit unit is not exempt by it. +- name: UC, a pensioner non-dependant's Pension Credit does not exempt her adult son + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + pensioner: + age: 75 + is_claimant_or_partner: true + pensioner_son: + age: 35 + is_claimant_or_partner: false + benunits: + benunit_1: + members: [head] + benunit_2: + members: [pensioner, pensioner_son] + households: + household: + members: [head, pensioner, pensioner_son] + rent: 12_000 + output: + uc_non_dep_receives_pension_credit: [false, true] + uc_non_dep_deduction_exempt: [false, true, false] + uc_non_dep_deductions: [1_158.60, 0] + +# The household head is the householder in whose name the accommodation is +# rented, so she is no one's non-dependant for UC (Sch 4 para 9(2)(d)) or HB +# (reg 3(2)(d)), even where her benefit unit's claimant and partner are the +# young couple. For CTR she applies alone and they are her non-dependants. +- name: UC and HB, a head who applies for CTR alone is not her benefit unit's non-dependant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + grandmother: + age: 70 + is_household_head: true + is_claimant_or_partner: false + son: + age: 30 + is_claimant_or_partner: true + employment_income: 25_000 + weekly_hours: 37.5 + partner: + age: 29 + is_claimant_or_partner: true + benunits: + benunit: + members: [grandmother, son, partner] + households: + household: + members: [grandmother, son, partner] + rent: 12_000 + output: + is_benefit_unit_non_dependant_for_universal_credit: [false, false, false] + is_benefit_unit_non_dependant_for_legacy_benefits: [false, false, false] + is_benefit_unit_non_dependant_for_council_tax_reduction: [false, true, true] + uc_non_dep_deductions: [0] + housing_benefit_non_dep_deductions: [0] + +# Sch 4 para 9(2)(f): a family that makes no Universal Credit claim treats +# no one as its non-dependant, so a non-claiming lodger's adult son counts in +# the householder's claim. "Making a claim" is the model's would-claim flag. +- name: UC, a non-claiming lodger's adult son counts in the householder's claim + period: 2026 + absolute_error_margin: 0.01 + input: + people: + householder: + age: 40 + is_household_head: true + is_claimant_or_partner: true + lodger: + age: 39 + is_claimant_or_partner: true + rent_paid_as_lodger: 5_200 + lodger_son: + age: 25 + is_claimant_or_partner: false + benunits: + benunit_1: + members: [householder] + benunit_2: + members: [lodger, lodger_son] + would_claim_uc: false + households: + household: + members: [householder, lodger, lodger_son] + rent: 9_000 + output: + uc_non_dependants_counted: [true, false] + uc_non_dep_deductions: [1_158.60, 0] + +# A family bearing a share of the rent is a joint occupier, never a +# non-dependant, even where the rent is nil (HB reg 3(2)(d)). Its own adult son +# is still a non-dependant of both joint occupiers, apportioned by their +# shares (reg 74(5)). +- name: HB, a nil-rent sharer is not a non-dependant but its adult son is shared + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + sharer: + age: 48 + is_claimant_or_partner: true + household_benefits_individual_non_dep_deduction: 2_000 + sharer_son: + age: 25 + is_claimant_or_partner: false + household_benefits_individual_non_dep_deduction: 1_000 + benunits: + benunit_1: + members: [head] + benunit_2: + members: [sharer, sharer_son] + liable_for_share_of_household_rent: true + households: + household: + members: [head, sharer, sharer_son] + rent: 0 + output: + share_of_household_rent: [0.5, 0.5] + housing_benefit_non_dep_deductions: [500, 500] + +# Sch 4 para 9(2)(f): the head's family is flagged not to claim Universal +# Credit, so its adult son counts in the jointly liable sharer's claim. +- name: UC, a non-claiming head's adult son counts in a claiming sharer's claim + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 50 + is_household_head: true + is_claimant_or_partner: true + employment_income: 150_000 + weekly_hours: 37.5 + son: + age: 25 + is_claimant_or_partner: false + sharer: + age: 45 + is_claimant_or_partner: true + benunits: + benunit_1: + members: [head, son] + would_claim_uc: false + benunit_2: + members: [sharer] + liable_for_share_of_household_rent: true + households: + household: + members: [head, son, sharer] + rent: 12_000 + output: + uc_non_dependants_counted: [false, true] + uc_non_dep_deductions: [0, 1_158.60] diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.yaml index e27118fd0b..cf142dd2a3 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.yaml @@ -1,7 +1,7 @@ - name: Eligible period: 2025 input: - benunit_is_rent_liable: false + is_non_dependant_of_household_head: true age: 18 # Without this the age-based default puts an 18-year-old in tertiary # education, and full-time students are exempt (reg 74(7)(c)). @@ -15,7 +15,7 @@ - name: Age ineligible period: 2025 input: - benunit_is_rent_liable: false + is_non_dependant_of_household_head: true age: 17 output: housing_benefit_individual_non_dep_deduction_eligible: false @@ -28,10 +28,31 @@ output: housing_benefit_individual_non_dep_deduction_eligible: false +# HB Regs 2006 reg 3: a member of the claimant's own benefit unit who is not +# the claimant, partner or a child or young person. Aged 25 or over, so the +# reg 74(8) exemptions for younger non-dependants on benefits do not apply. +- name: Non-dependant within the benefit unit + period: 2025 + input: + is_non_dependant_of_household_head: false + is_benefit_unit_non_dependant_for_legacy_benefits: true + age: 30 + output: + housing_benefit_individual_non_dep_deduction_eligible: true + +- name: Non-dependant within the benefit unit, under 18 + period: 2025 + input: + is_non_dependant_of_household_head: false + is_benefit_unit_non_dependant_for_legacy_benefits: true + age: 17 + output: + housing_benefit_individual_non_dep_deduction_eligible: false + - name: Exempt non-dependant is ineligible period: 2025 input: - benunit_is_rent_liable: false + is_non_dependant_of_household_head: true age: 20 current_education: TERTIARY output: diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction.yaml index 9a9ca3cced..b25b8b51f5 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction.yaml @@ -6,6 +6,8 @@ person: age: 22 uc_non_dep_deduction_exempt: false + # A member of another family who is not liable for rent. + is_non_dependant_of_household_head: true benunits: benunit: members: person @@ -21,6 +23,7 @@ person: age: 22 uc_non_dep_deduction_exempt: true + is_non_dependant_of_household_head: true benunits: benunit: members: person @@ -36,6 +39,8 @@ person: age: 20 uc_non_dep_deduction_exempt: false + # A member of another family who is not liable for rent. + is_non_dependant_of_household_head: true benunits: benunit: members: person diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.yaml index 11fe57ddce..99888fb21a 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.yaml @@ -4,7 +4,7 @@ input: age: 21 uc_non_dep_deduction_exempt: false - benunit_is_rent_liable: false + is_non_dependant_of_household_head: true output: uc_individual_non_dep_deduction_eligible: true @@ -14,6 +14,40 @@ input: age: 20 uc_non_dep_deduction_exempt: false - benunit_is_rent_liable: false + is_non_dependant_of_household_head: true + output: + uc_individual_non_dep_deduction_eligible: false + +- name: Exempt + period: 2020 + absolute_error_margin: 0 + input: + age: 21 + uc_non_dep_deduction_exempt: true + is_non_dependant_of_household_head: true + output: + uc_individual_non_dep_deduction_eligible: false + +# UC Regs 2013 Sch 4 para 9(1)(c): a member of the renter's own benefit unit +# who is not the claimant, partner or a qualifying young person. +- name: Non-dependant within the benefit unit + period: 2020 + absolute_error_margin: 0 + input: + age: 25 + uc_non_dep_deduction_exempt: false + is_non_dependant_of_household_head: false + is_benefit_unit_non_dependant_for_universal_credit: true + output: + uc_individual_non_dep_deduction_eligible: true + +- name: The renter is not their own non-dependant + period: 2020 + absolute_error_margin: 0 + input: + age: 25 + uc_non_dep_deduction_exempt: false + is_non_dependant_of_household_head: false + is_benefit_unit_non_dependant_for_universal_credit: false output: uc_individual_non_dep_deduction_eligible: false diff --git a/policyengine_uk/tests/test_benefit_unit_non_dependant_properties.py b/policyengine_uk/tests/test_benefit_unit_non_dependant_properties.py new file mode 100644 index 0000000000..bf211f4a82 --- /dev/null +++ b/policyengine_uk/tests/test_benefit_unit_non_dependant_properties.py @@ -0,0 +1,578 @@ +"""Property-based tests for non-dependants within a benefit unit. + +A benefit unit can include members aged 16 or over who are neither the +claimant or partner nor a qualifying young person: in law they are +non-dependants of the renter (UC Regs 2013 Sch 4 para 9; HB Regs 2006 reg 3; +CTR SI 2012/2885 reg 9 and equivalents). Invariants, for any generated +population of households: + +1. Oracle: each family's Universal Credit, Housing Benefit and Council Tax + Reduction non-dependant deductions, and who is eligible for an individual + deduction, equal an independent calculation from the generated household + structure: who is a non-dependant, of whom, how a non-dependant couple + pays one deduction (the higher), and how a non-dependant of several joint + occupiers is apportioned (by rent or liability share, whichever family + the non-dependant is in). +2. Conservation: every Housing Benefit non-dependant deduction (a couple's + counted once) is borne in full across the joint occupiers, and once more + by a boarder or lodger for a non-dependant in its own benefit unit, + unless a claimant is exempt; each Council Tax Reduction claiming family + bears every non-dependant, under its own scheme, in proportion to its + joint-liability share + unless the applicant is exempt; every Universal Credit contribution is + borne by exactly one family unless that family's renter is exempt. +3. Exemptions: a family whose renter is exempt (UC Sch 4 para 15; HB reg + 74(6); CTR Sch 1 para 8(6)) has no deductions; no deduction is charged for + a non-dependant under 21 (UC) or under 18 (HB, CTR), or for a qualifying + young person. +4. No-op: in households without non-dependants inside a benefit unit, the + deductions equal the previous formulas, which charged only members of + other families. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_core.periods import period as make_period + +from policyengine_uk import Simulation +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._national_non_dependants import ( + household_head_family_is_pensioner, + individual_non_dep_deduction, +) + +# Situation inputs without a period key are set for 2025, so the properties +# are checked in 2025, when the contribution was £93.02 a month. +YEAR = 2025 +UC_CONTRIBUTION = 93.02 * 12 +PROPERTY_SETTINGS = settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +# Members of a benefit unit who are not the claimant or partner. +# dependant: a qualifying young person the claimant is responsible for; +# looked_after: a qualifying young person placed by a local authority, for +# whom no one in the family is responsible; +# non_dependant: aged 16 or over and not a qualifying young person. +KINDS = ["dependant", "looked_after", "non_dependant"] +ROLES = ["sharer", "lodger", "non_dependant"] + + +@st.composite +def extra_member(draw): + kind = draw(st.sampled_from(KINDS)) + age = draw(st.integers(16, 19) if kind != "non_dependant" else st.integers(16, 60)) + return dict( + kind=kind, + age=age, + earnings=draw(st.sampled_from([0, 2_600, 15_600, 30_000])), + hours=draw(st.sampled_from([0, 10, 37.5])), + attendance_allowance=draw(st.booleans()), + ) + + +@st.composite +def family(draw, role): + # The household head's family has the household's oldest members, so it + # is unambiguously the head's (and the council tax claimant's) family. + adult_age = st.integers(61, 90) if role == "head" else st.integers(25, 59) + return dict( + role=role, + adult_ages=draw(st.lists(adult_age, min_size=1, max_size=2)), + extras=draw(st.lists(extra_member(), min_size=0, max_size=2)), + child=draw(st.booleans()), + renter_pip=draw(st.booleans()), + # Take-up: a family flagged not to claim Universal Credit passes its + # in-unit non-dependants to the claim that counts the household's. + would_claim_uc=draw(st.booleans()), + payment=draw(st.floats(1_000, 10_000, allow_nan=False)), + ) + + +@st.composite +def household(draw): + head = draw(family("head")) + others = [ + draw(family(draw(st.sampled_from(ROLES)))) + for _ in range(draw(st.integers(0, 2))) + ] + return dict( + families=[head] + others, + rent=draw(st.floats(3_000, 20_000, allow_nan=False)), + tenure=draw(st.sampled_from(["RENT_FROM_COUNCIL", "RENT_PRIVATELY"])), + ) + + +population = st.lists(household(), min_size=1, max_size=5) + + +def build(population, convert=None): + """One situation for the whole population, and a record per person and + per family for the oracle. convert maps extra members' kinds to others.""" + people, benunits, homes = {}, {}, {} + person_rows, family_rows = [], [] + for h, house in enumerate(population): + members = [] + for f, fam in enumerate(house["families"]): + name = f"h{h}_f{f}" + ids = [] + head_family = f == 0 + # The head's family has the household's oldest member. + for i, age in enumerate(fam["adult_ages"]): + pid = f"{name}_adult_{i}" + pip = fam["renter_pip"] and i == 0 + people[pid] = { + "age": age, + "is_claimant_or_partner": True, + "is_household_head": head_family and i == 0, + "pip_dl": 3_000.0 if pip else 0.0, + } + ids.append(pid) + person_rows.append( + dict( + household=h, + family=len(family_rows), + kind="adult", + age=age, + disability_benefit=pip, + ) + ) + for i, extra in enumerate(fam["extras"]): + kind = (convert or {}).get(extra["kind"], extra["kind"]) + pid = f"{name}_extra_{i}" + young = kind != "non_dependant" + people[pid] = { + "age": min(extra["age"], 19) if young else extra["age"], + "is_claimant_or_partner": False, + "is_household_head": False, + "is_qualifying_young_person_for_universal_credit": young, + "is_child_or_qualifying_young_person_for_child_benefit": young, + "is_looked_after_by_local_authority": kind == "looked_after", + "employment_income": float(extra["earnings"]), + "weekly_hours": float(extra["hours"]), + "attendance_allowance": ( + 3_000.0 if extra["attendance_allowance"] else 0.0 + ), + } + ids.append(pid) + person_rows.append( + dict( + household=h, + family=len(family_rows), + kind=kind, + age=people[pid]["age"], + disability_benefit=extra["attendance_allowance"], + ) + ) + if fam["child"]: + pid = f"{name}_child" + # Inputs set for some members are inputs for all, so the + # child's Child Benefit status is stated, not left to default. + people[pid] = { + "age": 8, + "is_claimant_or_partner": False, + "is_household_head": False, + "is_child_or_qualifying_young_person_for_child_benefit": True, + } + ids.append(pid) + person_rows.append( + dict( + household=h, + family=len(family_rows), + kind="child", + age=8, + disability_benefit=False, + ) + ) + benunit = {"members": ids, "would_claim_uc": fam["would_claim_uc"]} + if fam["role"] == "sharer": + benunit["liable_for_share_of_household_rent"] = True + if fam["role"] == "lodger": + people[ids[0]]["rent_paid_as_lodger"] = fam["payment"] + benunits[name] = benunit + members.extend(ids) + family_rows.append( + dict( + household=h, + role=fam["role"], + renter_pip=fam["renter_pip"], + n_liable=len(fam["adult_ages"]), + ) + ) + homes[f"h{h}"] = { + "members": members, + "rent": house["rent"], + "tenure_type": house["tenure"], + "brma": "MAIDSTONE", + } + situation = {"people": people, "benunits": benunits, "households": homes} + return situation, person_rows, family_rows + + +def calc(sim, variable, map_to=None): + return np.asarray(sim.calculate(variable, YEAR, map_to=map_to), dtype=float) + + +def counted_once_per_couple(person_rows, amounts): + """A couple pays one deduction, the higher (HB reg 74(3); CTR Sch 1 para + 8(3)): keep the larger amount of each family's adults (its claimant and + partner) and zero the other; anyone else counts in full.""" + counted = np.array(amounts, dtype=float) + adults = {} + for p, row in enumerate(person_rows): + if row["kind"] == "adult": + adults.setdefault(row["family"], []).append(p) + for members in adults.values(): + keep = max(members, key=lambda p: (counted[p], -p)) + for p in members: + if p != keep: + counted[p] = 0.0 + return counted + + +def oracle( + person_rows, + family_rows, + uc, + hb, + ctr, + pension_credit, + claims, + ctr_share, + uc_counted, + uc_own_claim, + ctr_other, + ctr_own_scheme_is_heads, +): + """Expected deductions per family from the generated structure. + + uc, hb and ctr are each person's individual deduction from the model; + the oracle decides independently who is whose non-dependant. Which + Universal Credit claim counts the household's non-dependants + (uc_non_dependants_counted), whether a family makes its own claim, the + CTR claim flags and the joint-liability shares are read from the model. + ctr is each person's deduction under the scheme of the household head's + family; ctr_other under the other scheme, which a claiming family on that + scheme bears (SI 2012/2885 reg 3). + """ + n = len(family_rows) + exp_uc, exp_hb, exp_ctr = np.zeros(n), np.zeros(n), np.zeros(n) + expected_uc_individual = np.zeros(len(person_rows)) + hb = counted_once_per_couple(person_rows, hb) + ctr = counted_once_per_couple(person_rows, ctr) + ctr_other = counted_once_per_couple(person_rows, ctr_other) + households = sorted({f["household"] for f in family_rows}) + for h in households: + fams = [i for i, f in enumerate(family_rows) if f["household"] == h] + # Sch 4 para 9(2)(f): one claim counts the household's non-dependants. + counted = [i for i in fams if uc_counted[i]] + counted_family = counted[0] if counted else None + # Families liable for the household's rent: the head's and sharers'. + liable = [i for i in fams if family_rows[i]["role"] in ("head", "sharer")] + total_liable = sum(family_rows[i]["n_liable"] for i in liable) + share = { + i: (family_rows[i]["n_liable"] / total_liable if i in liable else 0.0) + for i in fams + } + for p, row in enumerate(person_rows): + if row["household"] != h: + continue + f = row["family"] + role = family_rows[f]["role"] + other_family = role == "non_dependant" + own_family = row["kind"] == "non_dependant" + # UC: a member of a family not liable for rent counts in the + # claim that counts the household's non-dependants; a non-QYP + # member aged 16+ of any other family counts in their own family's + # claim if it makes one, otherwise in that same counted claim. + # Para 16: Attendance Allowance or PIP daily living, or Pension + # Credit, which only a claimant or partner receives. + uc_exempt = row["disability_benefit"] or ( + row["kind"] == "adult" and pension_credit[f] > 0 + ) + uc_non_dependant = other_family or own_family + if uc_non_dependant and row["age"] >= 21 and not uc_exempt: + expected_uc_individual[p] = UC_CONTRIBUTION + own_claim = own_family and not other_family and uc_own_claim[f] + bearer = f if own_claim else counted_family + if ( + uc_non_dependant + and bearer is not None + and not family_rows[bearer]["renter_pip"] + ): + exp_uc[bearer] += uc[p] + # HB: every non-dependant, in any family, is pooled across the + # joint occupiers by rent share; a boarder or lodger, who is not + # a joint occupier, also bears its own in full. + if other_family or own_family: + for i in fams: + exp_hb[i] += share[i] * hb[p] + if own_family and role == "lodger": + exp_hb[f] += hb[p] + # CTR: every non-dependant, a lodger's included, is pooled across + # the claiming families by joint-liability share, except that a + # family's own claimant or partner is not its non-dependant. + if other_family or own_family: + for i in fams: + if i == f and not own_family: + continue + amount = ctr[p] if ctr_own_scheme_is_heads[i] else ctr_other[p] + exp_ctr[i] += claims[i] * ctr_share[i] * amount + # A claimant or partner on PIP daily living exempts their own family + # from all HB and CTR deductions (reg 74(6); Sch 1 para 8(6)). + for i in fams: + if family_rows[i]["renter_pip"]: + exp_hb[i] = 0.0 + exp_ctr[i] = 0.0 + return exp_uc, exp_hb, exp_ctr, expected_uc_individual + + +def ctr_by_scheme(sim, person_rows, family_rows): + """Each person's CTR deduction under the other scheme (pensioner or + working-age) from the household head's family, and whether each family is + on the head's scheme. Computed with the aggregator's own helper, since a + jointly liable applicant on the other scheme has its deductions + recomputed under that scheme.""" + person = sim.populations["person"] + period = make_period(YEAR) + head_pensioner = np.asarray(household_head_family_is_pensioner(person, period)) + eligible = ( + calc(sim, "council_tax_reduction_individual_non_dep_deduction_eligible") > 0 + ) + other = np.asarray( + individual_non_dep_deduction( + person, period, sim.tax_benefit_system.parameters, ~head_pensioner + ), + dtype=float, + ) + first_member = {} + for p, row in enumerate(person_rows): + first_member.setdefault(row["family"], p) + family_head_pensioner = np.array( + [head_pensioner[first_member[f]] for f in range(len(family_rows))] + ) + family_pensioner = calc(sim, "council_tax_reduction_pensioner") > 0 + return dict( + ctr_other=eligible * other, + ctr_own_scheme_is_heads=family_pensioner == family_head_pensioner, + ) + + +def pool_under_own_scheme(person_rows, family_rows, v, mask=None): + """For each family, the household's CTR deductions (a couple's once) + under that family's own scheme, over the members in mask (everyone by + default).""" + mask = np.ones(len(person_rows), dtype=bool) if mask is None else mask + person_household = np.array([row["household"] for row in person_rows]) + family_household = np.array([row["household"] for row in family_rows]) + pools = {} + for scheme, amounts in (("head", v["ctr"]), ("other", v["ctr_other"])): + counted = counted_once_per_couple(person_rows, amounts) * mask + by_household = np.bincount( + person_household, weights=counted, minlength=family_household.max() + 1 + ) + pools[scheme] = by_household[family_household] + return np.where(v["ctr_own_scheme_is_heads"], pools["head"], pools["other"]) + + +def run(population, convert=None): + situation, person_rows, family_rows = build(population, convert) + sim = Simulation(situation=situation) + values = dict( + uc=calc(sim, "uc_individual_non_dep_deduction"), + hb=calc(sim, "household_benefits_individual_non_dep_deduction"), + ctr=calc(sim, "council_tax_reduction_individual_non_dep_deduction"), + pension_credit=calc(sim, "pension_credit"), + claims=calc(sim, "council_tax_reduction_claimant_benunit"), + ctr_share=calc(sim, "council_tax_reduction_joint_liability_share"), + uc_counted=calc(sim, "uc_non_dependants_counted") > 0, + **ctr_by_scheme(sim, person_rows, family_rows), + uc_own_claim=( + (calc(sim, "benunit_is_rent_liable") > 0) + & (calc(sim, "is_uc_eligible") > 0) + & (calc(sim, "claims_universal_credit") > 0) + ), + ) + return sim, person_rows, family_rows, values + + +@PROPERTY_SETTINGS +@given(population) +def test_deductions_match_the_oracle(population): + sim, person_rows, family_rows, v = run(population) + exp_uc, exp_hb, exp_ctr, exp_uc_individual = oracle(person_rows, family_rows, **v) + # 1. Oracle. The members each scheme classes as in-unit non-dependants + # are exactly the generated non-QYP members. + kinds = np.array([row["kind"] for row in person_rows]) + for in_unit_variable in ( + "is_benefit_unit_non_dependant_for_universal_credit", + "is_benefit_unit_non_dependant_for_legacy_benefits", + "is_benefit_unit_non_dependant_for_council_tax_reduction", + ): + assert np.array_equal( + calc(sim, in_unit_variable) > 0, kinds == "non_dependant" + ), in_unit_variable + assert np.allclose(v["uc"], exp_uc_individual, atol=0.01) + assert np.allclose(calc(sim, "uc_non_dep_deductions"), exp_uc, atol=0.01) + assert np.allclose( + calc(sim, "housing_benefit_non_dep_deductions"), exp_hb, atol=0.01 + ) + assert np.allclose( + calc(sim, "council_tax_reduction_non_dep_deductions"), exp_ctr, atol=0.01 + ) + + # HB and CTR eligibility: a non-dependant aged 18 or over, either in a + # family not liable for rent or within their own benefit unit. + def eligible(own_family_roles): + return np.array( + [ + row["age"] >= 18 + and ( + family_rows[row["family"]]["role"] == "non_dependant" + or ( + row["kind"] == "non_dependant" + and family_rows[row["family"]]["role"] in own_family_roles + ) + ) + for row in person_rows + ] + ) + + # Any family's own non-dependants, a boarder's or lodger's included. HB + # eligibility also excludes an exempt non-dependant (reg 74(7)-(10)); CTR + # applies its exemptions in the amount. + everyone = eligible(("head", "sharer", "lodger", "non_dependant")) + hb_exempt = calc(sim, "housing_benefit_non_dep_deduction_exempt") > 0 + assert np.array_equal( + calc(sim, "housing_benefit_individual_non_dep_deduction_eligible") > 0, + everyone & ~hb_exempt, + ) + assert np.array_equal( + calc(sim, "council_tax_reduction_individual_non_dep_deduction_eligible") > 0, + everyone, + ) + + +@PROPERTY_SETTINGS +@given(population) +def test_conservation_and_exemptions(population): + sim, person_rows, family_rows, v = run(population) + ages = np.array([row["age"] for row in person_rows]) + kinds = np.array([row["kind"] for row in person_rows]) + # 2. Conservation. HB: each deduction, a couple's once, is borne in full + # across the joint occupiers, and a lodger's own non-dependant once more + # by the lodger, where no claimant is exempt. + hb_counted = counted_once_per_couple(person_rows, v["hb"]) + hb_families = sim.map_result( + calc(sim, "housing_benefit_non_dep_deductions"), "benunit", "household" + ) + lodgers_own = np.array( + [ + row["kind"] == "non_dependant" + and family_rows[row["family"]]["role"] == "lodger" + for row in person_rows + ] + ) + hb_people = sim.map_result(hb_counted * (1 + lodgers_own), "person", "household") + hb_exempt = ( + sim.map_result( + calc(sim, "housing_benefit_non_dep_deductions_claimant_exempt"), + "benunit", + "household", + ) + > 0 + ) + assert np.allclose(hb_families[~hb_exempt], hb_people[~hb_exempt], atol=0.01) + assert np.all(hb_families <= hb_people + 0.01) + # CTR: every non-dependant is borne by each claiming family, under its + # own scheme, in proportion to its joint-liability share (which follows + # the regulations' per-person wording, so shares need not sum to one); + # none where the applicant is exempt. This holds on the generator's + # domain (rent > 0, claimants liable for it), where no claiming family's + # own claimant or partner has a deduction. + ctr_families = calc(sim, "council_tax_reduction_non_dep_deductions") + expected_ctr = ( + v["claims"] + * v["ctr_share"] + * pool_under_own_scheme(person_rows, family_rows, v) + ) + ctr_exempt = calc(sim, "council_tax_reduction_applicant_has_non_dep_exemption") > 0 + assert np.allclose(ctr_families[~ctr_exempt], expected_ctr[~ctr_exempt], atol=0.01) + assert np.all(ctr_families[ctr_exempt] == 0) + # UC: each contribution is borne once, except where the bearer is exempt. + uc_families = sim.map_result( + calc(sim, "uc_non_dep_deductions"), "benunit", "household" + ) + uc_people = sim.map_result(v["uc"], "person", "household") + assert np.all(uc_families <= uc_people + 0.01) + # 3. Exemptions. + renter_exempt = calc(sim, "uc_non_dep_deductions_renter_exempt") > 0 + assert np.all(calc(sim, "uc_non_dep_deductions")[renter_exempt] == 0) + hb_claimant_exempt = ( + calc(sim, "housing_benefit_non_dep_deductions_claimant_exempt") > 0 + ) + assert np.all( + calc(sim, "housing_benefit_non_dep_deductions")[hb_claimant_exempt] == 0 + ) + assert np.all(v["uc"][ages < 21] == 0) + assert np.all(v["hb"][ages < 18] == 0) + assert np.all(v["ctr"][ages < 18] == 0) + # Nobody in a family liable for rent is charged except its own + # non-dependants. + not_non_dependant = kinds != "non_dependant" + liable_family = np.array( + [family_rows[row["family"]]["role"] != "non_dependant" for row in person_rows] + ) + for amounts in (v["uc"], v["hb"], v["ctr"]): + assert np.all(amounts[not_non_dependant & liable_family] == 0) + + +@PROPERTY_SETTINGS +@given(population) +def test_no_op_without_non_dependants_in_a_benefit_unit(population): + every_kind_dependant = {kind: "dependant" for kind in KINDS} + sim, person_rows, family_rows, v = run(population, every_kind_dependant) + # 4. The previous formulas: only members of other families (a couple's + # higher amount once), charged to the claim that counts them (UC), by rent share + # (HB) or by claim share (CTR), with the claimants' exemptions. + person_household = np.array([row["household"] for row in person_rows]) + family_household = np.array([row["household"] for row in family_rows]) + other = calc(sim, "is_non_dependant_of_household_head") > 0 + + def other_families(amounts): + by_household = np.bincount( + person_household, + weights=amounts * other, + minlength=family_household.max() + 1, + ) + return by_household[family_household] + + renter_exempt = calc(sim, "uc_non_dep_deductions_renter_exempt") > 0 + assert np.allclose( + calc(sim, "uc_non_dep_deductions"), + np.where(renter_exempt, 0, v["uc_counted"] * other_families(v["uc"])), + atol=0.01, + ) + hb_claimant_exempt = ( + calc(sim, "housing_benefit_non_dep_deductions_claimant_exempt") > 0 + ) + hb_counted = counted_once_per_couple(person_rows, v["hb"]) + assert np.allclose( + calc(sim, "housing_benefit_non_dep_deductions"), + np.where( + hb_claimant_exempt, + 0, + calc(sim, "share_of_household_rent") * other_families(hb_counted), + ), + atol=0.01, + ) + ctr_exempt = calc(sim, "council_tax_reduction_applicant_has_non_dep_exemption") > 0 + pool = pool_under_own_scheme(person_rows, family_rows, v, mask=other) + assert np.allclose( + calc(sim, "council_tax_reduction_non_dep_deductions"), + np.where(ctr_exempt, 0, v["claims"] * v["ctr_share"] * pool), + atol=0.01, + ) diff --git a/policyengine_uk/tests/test_council_tax_reduction_claimant_properties.py b/policyengine_uk/tests/test_council_tax_reduction_claimant_properties.py index 6424555e09..214ad859bb 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_claimant_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_claimant_properties.py @@ -33,10 +33,13 @@ 3. Simulated reductions: no family outside the claimants gets one, and a household's total never exceeds its council tax. (Reported reductions, used where a scheme is not modelled, are outside this test.) -4. Non-dependants: no claimant or partner of a claimant family is a - non-dependant (the applicant's family, SI 2012/2885 reg 9(2)(a)). Pin: no - member of a sharer family is one, and every adult outside the claimant - families, the sharer families and boarders' and lodgers' families is one. +4. Non-dependants: no applicant or partner of a claimant family + (is_council_tax_reduction_applicant_or_partner) is a non-dependant (the + applicant's family, SI 2012/2885 reg 9(2)(a)). Pin: no member of a sharer + family is one except a member outside its applicant's family + (is_benefit_unit_non_dependant_for_council_tax_reduction), and every + adult outside the claimant families, the sharer families and boarders' + and lodgers' families is one. 5. Differential: where the input flags exactly one member of a household, or no household in the simulation has the input (so the flag's formula picks the eldest), the family holding the head is the one holding the @@ -314,8 +317,10 @@ def test_claimant_invariants(population): person_sharer = sharer[facts["benunit"]] lodger = calc(sim, "pays_rent_to_householder") adult = age >= 18 - assert not np.any(non_dep & person_claimant & claimant_or_partner) - assert not np.any(non_dep & person_sharer) + applicant_or_partner = calc(sim, "is_council_tax_reduction_applicant_or_partner") + in_unit = calc(sim, "is_benefit_unit_non_dependant_for_council_tax_reduction") + assert not np.any(non_dep & person_claimant & applicant_or_partner) + assert not np.any(non_dep & person_sharer & ~in_unit) assert np.all(non_dep[adult & ~person_claimant & ~person_sharer & ~lodger]) # 5. Differential with the person-level household head flag. Once anyone in diff --git a/policyengine_uk/tests/test_household_head_agreement_properties.py b/policyengine_uk/tests/test_household_head_agreement_properties.py index 4862578ad5..26135828ed 100644 --- a/policyengine_uk/tests/test_household_head_agreement_properties.py +++ b/policyengine_uk/tests/test_household_head_agreement_properties.py @@ -28,13 +28,17 @@ if and only if their family is neither the head's nor liable for rent. - Universal Credit non-dependants: at most one family in a household counts them (uc_non_dependants_counted; UC Regs 2013 Sch 4 para - 9(2)(f)). Pin: deductions fall only on that family's claim, and where - the head's family is liable for the rent and claims Universal Credit, - it is the head's. Only families with a share of the rent have Housing - Benefit non-dependant deductions. + 9(2)(f)). Pin: other families' non-dependants fall only on that + family's claim, and a family's own in-unit non-dependants on its own + claim; where the head's family is liable for the rent and claims + Universal Credit, the counting claim is the head's. Only families with + a share of the rent have Housing Benefit non-dependant deductions, + except a boarder or lodger for a non-dependant in its own benefit unit. - Council Tax Reduction: only the head's family and sharers claim; where the head is 18 or over, the head's family claims and no one in it is a - non-dependant. + non-dependant except a member who is outside the applicant's family + (is_benefit_unit_non_dependant_for_council_tax_reduction; SI 2012/2885 + reg 9(2)(a)). - A family paying the householder as boarders or lodgers is never the head's, and the maintenance loan never has the head living with parents. 4. Metamorphic: every value the simulation computes, other than the @@ -395,7 +399,11 @@ def test_every_programme_reads_one_head(population): # counts the household's non-dependants: the head's family's wherever it # is liable for the rent and claims. counted = calc(sim, "uc_non_dependants_counted") - assert np.all(counted[calc(sim, "uc_non_dep_deductions") > 0]) + # A family's own in-unit non-dependants count in its own claim. + has_uc_in_unit = any_in_benunit( + calc(sim, "is_benefit_unit_non_dependant_for_universal_credit"), facts + ) + assert np.all((counted | has_uc_in_unit)[calc(sim, "uc_non_dep_deductions") > 0]) household_of_benunit = np.zeros(head_family.size, dtype=int) household_of_benunit[benunit_of] = facts["household"] assert np.all(sum_in_household(counted, household_of_benunit) <= 1) @@ -409,7 +417,16 @@ def test_every_programme_reads_one_head(population): in_household = head_claims_uc[household_of_benunit] assert np.array_equal(counted[in_household], head_family[in_household]) # Housing Benefit non-dependant deductions. - assert np.all(calc(sim, "housing_benefit_non_dep_deductions")[share == 0] == 0) + # A boarder or lodger bears a non-dependant in its own benefit unit. + has_legacy_in_unit = any_in_benunit( + calc(sim, "is_benefit_unit_non_dependant_for_legacy_benefits"), facts + ) + assert np.all( + calc(sim, "housing_benefit_non_dep_deductions")[ + (share == 0) & ~has_legacy_in_unit + ] + == 0 + ) # Council Tax Reduction. claimant = calc(sim, "council_tax_reduction_claimant_benunit") assert not np.any(claimant & ~head_family & ~sharer) @@ -418,7 +435,8 @@ def test_every_programme_reads_one_head(population): ctr_non_dependant = calc( sim, "council_tax_reduction_individual_non_dep_deduction_eligible" ) - assert not np.any(ctr_non_dependant & adult_head_family[benunit_of]) + ctr_in_unit = calc(sim, "is_benefit_unit_non_dependant_for_council_tax_reduction") + assert not np.any(ctr_non_dependant & adult_head_family[benunit_of] & ~ctr_in_unit) # Boarders and lodgers; the maintenance loan. assert not np.any(calc(sim, "pays_rent_to_householder") & person_head_family) arrangement = calc(sim, "maintenance_loan_living_arrangement") diff --git a/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py b/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py index 0162003f6b..c983d811db 100644 --- a/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py +++ b/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py @@ -27,6 +27,8 @@ boarder's or lodger's family has no Universal Credit or Housing Benefit deductions; no sharer, boarder or lodger is a Council Tax Reduction non-dependant. + (The generated families have no non-dependants within their own + benefit unit, which test_benefit_unit_non_dependant_properties covers.) 5. Category: the Universal Credit LHA category does not depend on the household input is_shared_accommodation; the Housing Benefit category is the shared rate for any family entitled to one bedroom that lacks diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.py index a02f8f8da8..be4b9b7810 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_individual_non_dep_deduction_eligible.py @@ -5,15 +5,27 @@ class housing_benefit_individual_non_dep_deduction_eligible(Variable): value_type = bool entity = Person label = "eligible person for the Housing Benefit non-dependent deduction" + documentation = ( + "A non-dependant aged 18 or over: either a member of another family " + "who is not liable for rent (see is_non_dependant_of_household_head) " + "or a member of their own benefit unit who is not the claimant, " + "partner or a child or young person (see " + "is_benefit_unit_non_dependant_for_legacy_benefits), who is not " + "exempt (housing_benefit_non_dep_deduction_exempt)." + ) definition_period = YEAR reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/3", "https://www.legislation.gov.uk/uksi/2006/213/regulation/74", "https://www.legislation.gov.uk/uksi/2006/214/regulation/55", ) def formula(person, period, parameters): - rent_liable = person.benunit("benunit_is_rent_liable", period) p = parameters(period).gov.dwp.housing_benefit.non_dep_deduction + non_dependant = person("is_non_dependant_of_household_head", period) | person( + "is_benefit_unit_non_dependant_for_legacy_benefits", period + ) age_eligible = person("age", period) >= p.age_threshold + # An exempt non-dependant attracts no deduction (reg 74(7)-(10)). exempt = person("housing_benefit_non_dep_deduction_exempt", period) - return age_eligible & ~rent_liable & ~exempt + return non_dependant & age_eligible & ~exempt diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.py b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.py index 40710ed628..1c4287e33c 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.py @@ -1,6 +1,5 @@ from policyengine_uk.model_api import * from policyengine_uk.variables.gov.dwp.housing_benefit.non_dep_deduction._non_dependants import ( - charged_to_other_families, deduction_per_family, ) @@ -10,48 +9,69 @@ class housing_benefit_non_dep_deductions(Variable): entity = BenUnit label = "non-dependent deductions" documentation = ( - "Deductions for the non-dependants residing with the claimant: one " - "for each non-dependant couple, the higher of the two members' " - "amounts, and one for each other member, apportioned between the " - "families liable for the rent by their shares; none if the claimant " - "or partner is exempt." + "Deductions for the non-dependants residing with the claimant: " + "members of other families of the household who are not liable for " + "rent, and members of a family liable for the rent who are not its " + "claimant, partner or a child or young person. One deduction for " + "each non-dependant couple, the higher of the two members' amounts, " + "and one for each other non-dependant. Each is apportioned between " + "joint occupiers by their shares of the rent, whichever family they " + "are in; a boarder or lodger also bears the whole deduction for a " + "non-dependant in its own benefit unit. None if the claimant or " + "partner is exempt." ) definition_period = YEAR unit = GBP reference = ( "https://www.legislation.gov.uk/uksi/2006/213/regulation/3", "https://www.legislation.gov.uk/uksi/2006/213/regulation/74", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/3", "https://www.legislation.gov.uk/uksi/2006/214/regulation/55", + "https://assets.publishing.service.gov.uk/media/5a7ce2b840f0b6629523c64c/hbgm-a5-calculating-benefit.pdf", ) def formula(benunit, period, parameters): # Deductions are made for non-dependants residing with the claimant # (HB Regs 2006 reg 74; HB (SPC) Regs 2006 reg 55). Joint occupiers, # boarders, lodgers and the landlord's household are not - # non-dependants (reg 3(2)(d)-(e), 3(4)). A couple pays one deduction, - # the higher (reg 74(3)). A non-dependant of more than one joint - # occupier is apportioned between them by their shares of the - # payments (reg 74(5); SPC reg 55(5)), so each family liable for the - # household's rent bears its share; a boarder or lodger bears none. - # A family bearing a share is a joint occupier, never a + # non-dependants (reg 3(2)(d)-(e), 3(4)). A family bearing a share + # of the rent is a joint occupier, never another family's # non-dependant, even where the rent is nil and so the family is not # benunit_is_rent_liable. person = benunit.members - joint_occupier = person.benunit("share_of_household_rent", period) > 0 - non_dependant = ( - person("is_non_dependant_of_household_head", period) & ~joint_occupier + deductions = person("household_benefits_individual_non_dep_deduction", period) + in_joint_occupier_family = person.benunit("share_of_household_rent", period) > 0 + other_family = ( + person("is_non_dependant_of_household_head", period) + & ~in_joint_occupier_family ) - deductions = ( - person("household_benefits_individual_non_dep_deduction", period) - * non_dependant + # A member of a rent-liable family who is not in its claimant's + # family under reg 3(2)(a) and (c) is a non-dependant too. + own_family = ( + person("is_benefit_unit_non_dependant_for_legacy_benefits", period) + & ~other_family ) - deduction_for_benunit = deduction_per_family(benunit, period, deductions, False) + # A non-dependant normally resides with each joint occupier (reg 3(1) + # and (4)), whichever family they belong to: another family's adult, + # a joint occupier's own adult son, or a boarder's or lodger's (reg + # 3(2)(e)(i) excludes only the person liable to pay the claimant). + # A couple pays one deduction, the higher (reg 74(3)), and each other + # non-dependant separately, counted once per family. One who is a + # non-dependant of more than one joint occupier is apportioned + # between them by their shares of the payments (reg 74(5); SPC reg + # 55(5)). + family_amount = deduction_per_family( + benunit, period, deductions * (other_family | own_family), False + ) + counted = person("is_benunit_head", period) * benunit.project(family_amount) share = benunit("share_of_household_rent", period) + from_joint_occupiers = share * benunit.max(person.household.sum(counted)) + # A boarder or lodger is not a joint occupier with the householder, + # so it also bears the whole deduction for a non-dependant in its own + # benefit unit. The model charges it for no one else. + joint_occupier = share > 0 + from_own_family = where(joint_occupier, 0, benunit.sum(deductions * own_family)) claimant_exempt = benunit( "housing_benefit_non_dep_deductions_claimant_exempt", period ) - return where( - claimant_exempt, - 0, - share * charged_to_other_families(benunit, period, deduction_for_benunit), - ) + return where(claimant_exempt, 0, from_joint_occupiers + from_own_family) diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_legacy_benefits.py b/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_legacy_benefits.py new file mode 100644 index 0000000000..7069ff4524 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_legacy_benefits.py @@ -0,0 +1,54 @@ +from policyengine_uk.model_api import * + + +class is_benefit_unit_non_dependant_for_legacy_benefits(Variable): + value_type = bool + entity = Person + label = "Non-dependant within the benefit unit for Housing Benefit and Council Tax Reduction" + documentation = ( + "A member of the benefit unit who is neither the claimant or partner " + "nor a child or young person: someone aged 16 or over who is not a " + "Child Benefit qualifying young person. The benefit unit follows the " + "survey's family, which can include an adult child or a 16 to 19 year " + "old who is not a qualifying young person. Under the Housing Benefit " + "and Council Tax Reduction schemes such a person is not in the " + "claimant's family (SSCBA 1992 s.137(1), with young person defined " + "by Child Benefit) but normally resides with the claimant, so is a " + "non-dependant. A child or young person is either in the family or, " + "if placed with the claimant by a local authority, excluded from " + "being a non-dependant. Such a person's own benefit awards are not " + "modelled, so the exemptions for a non-dependant on an income-related " + "benefit or on Universal Credit with no earned income (HB reg 74(8), " + "CTR Sch 1 para 8(8)) never apply to them. The household head " + "(is_resolved_household_head), a householder in whose name the " + "accommodation is rented, is liable for it and so never a " + "non-dependant (HB reg 3(2)(d)-(e)), even where the benefit unit's " + "claimant and partner are someone else. Council Tax Reduction uses " + "is_benefit_unit_non_dependant_for_council_tax_reduction, built on " + "the CTR applicant." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/3", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/19", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/3", + "https://www.legislation.gov.uk/uksi/2012/2885/regulation/9", + "https://www.legislation.gov.uk/wsi/2013/3029/regulation/9", + "https://www.legislation.gov.uk/ssi/2021/249/regulation/8", + "https://www.legislation.gov.uk/ssi/2012/319/regulation/3", + "https://www.legislation.gov.uk/ukpga/1992/4/section/137", + ) + + def formula(person, period, parameters): + # HB Regs 2006 and HB (SPC) Regs 2006 reg 3(2)(a), (c) with regs 19 + # and 21(3); SI 2012/2885 and WSI 2013/3029 reg 9(2)(a), (c); SSI + # 2021/249 reg 8(2)(a)-(b); SSI 2012/319 reg 3. A Child Benefit child + # is under 16. + claimant_or_partner = person("is_claimant_or_partner", period) + child_or_young_person = person( + "is_child_or_qualifying_young_person_for_child_benefit", period + ) + # Reg 3(2)(d)-(e): the household head is liable for the dwelling. + household_head = person("is_resolved_household_head", period) + return ~claimant_or_partner & ~child_or_young_person & ~household_head diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_universal_credit.py b/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_universal_credit.py new file mode 100644 index 0000000000..80dcda54e2 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_benefit_unit_non_dependant_for_universal_credit.py @@ -0,0 +1,47 @@ +from policyengine_uk.model_api import * + + +class is_benefit_unit_non_dependant_for_universal_credit(Variable): + value_type = bool + entity = Person + label = "Non-dependant within the benefit unit for Universal Credit" + documentation = ( + "A member of the benefit unit, aged 16 or over, who is neither the " + "claimant or partner nor a qualifying young person. The benefit unit " + "follows the survey's family, which can include an adult child or a " + "16 to 19 year old who is not a qualifying young person. In Universal " + "Credit law such a person is not in the claimant's family but is a " + "non-dependant of the renter: they normally live in the accommodation " + "and are none of the persons excluded by Sch 4 para 9(2). A qualifying " + "young person the renter is responsible for is a member of the " + "renter's extended benefit unit, and one no one is responsible for, " + "for example one looked after by a local authority, is excluded by " + "para 9(2)(g). A child, under 16, is always one or the other. The " + "household head (is_resolved_household_head), a householder in whose " + "name the accommodation is rented, is liable for it and so never a " + "non-dependant (para 9(2)(d)), even where the benefit unit's " + "claimant and partner are someone else." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/5", + "https://www.legislation.gov.uk/ukpga/2012/5/section/40", + ) + + def formula(person, period, parameters): + # UC Regs 2013 Sch 4 para 9(1)(a)-(b) and (2)(a), (g); WRA 2012 s.40. + not_a_child = ~person("is_child_for_universal_credit", period) + claimant_or_partner = person("is_claimant_or_partner", period) + qualifying_young_person = person( + "is_qualifying_young_person_for_universal_credit", period + ) + # Para 9(2)(d): the household head is liable for the accommodation. + household_head = person("is_resolved_household_head", period) + return ( + not_a_child + & ~claimant_or_partner + & ~qualifying_young_person + & ~household_head + ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.py index abd9869256..d47928de45 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_individual_non_dep_deduction_eligible.py @@ -5,14 +5,27 @@ class uc_individual_non_dep_deduction_eligible(Variable): value_type = bool entity = Person label = "Eligible person for the Universal Credit non-dependent deduction" + documentation = ( + "A non-dependant of a renter, aged 21 or over and not otherwise " + "exempt: either a member of another family who is not liable for rent " + "(see is_non_dependant_of_household_head) or a member of their own " + "benefit unit who is not the claimant, partner or a qualifying young " + "person (see is_benefit_unit_non_dependant_for_universal_credit)." + ) definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/13", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/16", + ) def formula(person, period, parameters): - not_rent_liable = ~person.benunit("benunit_is_rent_liable", period) p = parameters( period ).gov.dwp.universal_credit.elements.housing.non_dep_deduction - age = person("age", period) - age_eligible = age >= p.age_threshold + non_dependant = person("is_non_dependant_of_household_head", period) | person( + "is_benefit_unit_non_dependant_for_universal_credit", period + ) + age_eligible = person("age", period) >= p.age_threshold exempt = person("uc_non_dep_deduction_exempt", period) - return ~exempt & age_eligible & not_rent_liable + return non_dependant & age_eligible & ~exempt diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deduction_exempt.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deduction_exempt.py index be35b6197f..695a753059 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deduction_exempt.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deduction_exempt.py @@ -16,7 +16,8 @@ class uc_non_dep_deduction_exempt(Variable): "with the claimant or partner; the existing Universal Credit child " "age measure excludes children looked after by a local authority. Underlying " "entitlement reduced to nil by overlapping benefits does not qualify " - "for the carer exemption." + "for the carer exemption. Pension Credit is awarded to a benefit unit's claimant and partner, " + "so another adult in the unit is not receiving it." ) reference = "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/16" @@ -32,7 +33,12 @@ def formula(person, period, parameters): < p.child_responsibility_age_limit ) return ( - person.benunit("uc_non_dep_receives_pension_credit", period) + # Pension Credit is the claimant's and partner's award, not that of + # another adult in their benefit unit (para 16(2)(b)). + ( + person("is_claimant_or_partner", period) + & person.benunit("uc_non_dep_receives_pension_credit", period) + ) | person("dla_sc_middle_plus", period) | (person("pip_dl", period) > 0) | (person("attendance_allowance", period) > 0) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions.py index c09e43cc46..9f06dbb16d 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions.py @@ -5,22 +5,59 @@ class uc_non_dep_deductions(Variable): value_type = float entity = BenUnit label = "Universal Credit non-dependent deductions" + documentation = ( + "Housing cost contributions for the renter's non-dependants. Those in " + "other families of the household count in one claim, the one " + "uc_non_dependants_counted picks (the household head's family if it " + "claims, otherwise the first claiming sharer). Those within a " + "renter's own benefit unit count in that family's claim if it makes " + "one, otherwise in the same counted claim. Making a claim is the " + "model's claims_universal_credit flag for a renting, UC-eligible " + "family, so a family flagged as claiming counts its own non-dependants " + "even where its award is nil. None if the renter or a joint renter is " + "exempt." + ) definition_period = YEAR unit = GBP reference = ( "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/13", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/15", ) def formula(benunit, period, parameters): # A housing cost contribution is deducted for each non-dependant in # the renter's extended benefit unit (UC Regs 2013 Sch 4 para 13). - # Non-dependants are not liable for rent (para 9(2)(d)), and each - # counts in one Universal Credit claim only (para 9(2)(f)): see - # uc_non_dependants_counted. person = benunit.members - deductions = person("uc_individual_non_dep_deduction", period) * person( - "is_non_dependant_of_household_head", period + deductions = person("uc_individual_non_dep_deduction", period) + # Non-dependants from other families are not liable for rent (para + # 9(2)(d)), and each counts in one Universal Credit claim only (para + # 9(2)(f)): see uc_non_dependants_counted. + other_family = person("is_non_dependant_of_household_head", period) + # A member of a renter's own benefit unit who is not the claimant, + # partner or a qualifying young person is a non-dependant of that + # renter (para 9(1)(c) and (2)(a), (g)). Their own family's claim + # counts them if it makes one; otherwise the claim that counts the + # household's other non-dependants does (para 9(2)(f)). + in_unit = ( + person("is_benefit_unit_non_dependant_for_universal_credit", period) + & ~other_family + ) + own_claim = ( + benunit("benunit_is_rent_liable", period) + & benunit("is_uc_eligible", period) + & benunit("claims_universal_credit", period) ) + to_own_claim = in_unit & benunit.project(own_claim) counted = benunit("uc_non_dependants_counted", period) - return counted * benunit.max(person.household.sum(deductions)) + from_household = counted * benunit.max( + person.household.sum( + deductions * ((other_family | in_unit) & ~to_own_claim) + ) + ) + from_own_family = benunit.sum(deductions * to_own_claim) + # Sch 4 para 15: no contribution for any non-dependant where the + # renter, or either joint renter, is blind or receives a qualifying + # disability benefit. + renter_exempt = benunit("uc_non_dep_deductions_renter_exempt", period) + return where(renter_exempt, 0, from_household + from_own_family) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions_renter_exempt.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions_renter_exempt.py new file mode 100644 index 0000000000..187b0ec1ab --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/non_dep_deduction/uc_non_dep_deductions_renter_exempt.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class uc_non_dep_deductions_renter_exempt(Variable): + value_type = bool + entity = BenUnit + label = "Universal Credit renter exempt from all housing cost contributions" + documentation = ( + "No housing cost contribution is deducted for any non-dependant if the " + "renter, or either joint renter of a couple, is blind or receives " + "Attendance Allowance, the middle or highest rate of the DLA care " + "component or the PIP daily living component. Scottish disability " + "assistance (adult and pension age disability payments, child " + "disability payment, Scottish adult DLA) has no separate input, and " + "entitlement suspended in hospital is not modelled." + ) + definition_period = YEAR + reference = "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/15" + + def formula(benunit, period, parameters): + # UC Regs 2013 Sch 4 para 15: "joint renters" are joint claimants + # (para 1(2)), so the claimant and partner. + person = benunit.members + renter = person("is_claimant_or_partner", period) + qualifying = ( + person("is_blind", period) + | person("dla_sc_middle_plus", period) + | (person("attendance_allowance", period) > 0) + | (person("pip_dl", period) > 0) + ) + return benunit.any(renter & qualifying) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py index 52524b2828..1822c9f781 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py @@ -4,6 +4,21 @@ ) +def in_other_non_liable_family(person, period): + """Whether each person is outside every family claiming Council Tax + Reduction for the household, outside every family liable for a share of + its rent, and not paying the householder for their own occupation: all + such people aged 18 or over are the applicants' non-dependants (SI + 2012/2885 reg 9(2)(a), (d)-(e) and the Welsh and Scottish equivalents). + The eligibility for a non-dependant deduction and the local schemes' + aggregation both use this, so they stay on one definition.""" + return ( + ~person.benunit("council_tax_reduction_claimant_benunit", period) + & ~person.benunit("liable_for_share_of_household_rent", period) + & ~person("pays_rent_to_householder", period) + ) + + def is_full_time_student_non_dep(person, period): return (person("current_education", period) != EducationType.NOT_IN_EDUCATION) | ( person("in_HE", period) @@ -83,7 +98,14 @@ def local_non_dep_deductions( individual_deduction_variable, one_deduction_for_uc_couples=True, ): - deductions = benunit.members(individual_deduction_variable, period) + # The local schemes still charge only the non-dependants of other + # families. Non-dependants within a family's own benefit unit, now + # eligible for the national schemes, are left out here until each + # council's aggregation is brought into line. + person = benunit.members + deductions = person(individual_deduction_variable, period) * ( + in_other_non_liable_family(person, period) + ) deduction_for_benunit = benunit.max(deductions) if not one_deduction_for_uc_couples: has_uc = benunit("universal_credit", period) > 0 diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py index 861bd2dc56..b05ef2f086 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py @@ -1,4 +1,7 @@ from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._legacy import ( + in_other_non_liable_family, +) class council_tax_reduction_individual_non_dep_deduction_eligible(Variable): @@ -6,24 +9,32 @@ class council_tax_reduction_individual_non_dep_deduction_eligible(Variable): entity = Person label = "eligible person for CTR non-dependent deduction" documentation = ( - "An adult outside every family claiming Council Tax Reduction for the " - "household. Someone jointly and severally liable for the council tax " - "with the claimant (a joint tenant or other sharer of the rent) and " - "someone liable to the claimant on a commercial basis (a boarder or " - "lodger) is not a non-dependant." + "A non-dependant aged 18 or over: an adult outside every family " + "claiming Council Tax Reduction for the household who is not liable " + "for rent, or a member of any family's benefit unit who is not its " + "applicant, partner or a child or young person (see " + "is_benefit_unit_non_dependant_for_council_tax_reduction), including " + "a boarder's or lodger's adult son. Someone jointly and severally " + "liable for the council tax with the applicant (a joint tenant or " + "other sharer of the rent) and a boarder or lodger who pays the " + "applicant are not non-dependants." ) definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2012/2885/regulation/9", - "https://www.legislation.gov.uk/wsi/2013/3029", - "https://www.legislation.gov.uk/ssi/2021/249", + "https://www.legislation.gov.uk/wsi/2013/3029/regulation/9", + "https://www.legislation.gov.uk/ssi/2021/249/regulation/8", + "https://www.legislation.gov.uk/ssi/2012/319/regulation/3", ) def formula(person, period, parameters): - # SI 2012/2885 reg 9(2)(d)-(e) and the Welsh and Scottish equivalents. - return ( - (person("age", period) >= 18) - & ~person.benunit("council_tax_reduction_claimant_benunit", period) - & ~person.benunit("liable_for_share_of_household_rent", period) - & ~person("pays_rent_to_householder", period) + # SI 2012/2885 reg 9(2)(a), (c)-(e) and the Welsh and Scottish + # equivalents; deductions are for non-dependants aged 18 or over. + other_family = in_other_non_liable_family(person, period) + # A member of any family's benefit unit who is not in its applicant's + # family. Reg 9(2)(e) excludes only the person liable to pay the + # applicant, so a boarder's or lodger's adult son is a non-dependant. + in_unit = person( + "is_benefit_unit_non_dependant_for_council_tax_reduction", period ) + return (person("age", period) >= 18) & (other_family | in_unit) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py index f1741d1f5c..873cb1ca48 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py @@ -1,6 +1,5 @@ from policyengine_uk.model_api import * from policyengine_uk.variables.gov.dwp.housing_benefit.non_dep_deduction._non_dependants import ( - charged_to_other_families, deduction_per_family, ) from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._national_non_dependants import ( @@ -17,16 +16,18 @@ class council_tax_reduction_non_dep_deductions(Variable): entity = BenUnit label = "CTR non-dependent deductions" documentation = ( - "Deductions for the non-dependants in other benefit units of the " - "household: one per couple, the higher of the two members' amounts " - "(both, in the Welsh scheme for people who are not pensioners, when " - "the couple has a Universal Credit award), one for each other member, " - "each under the applicant family's own scheme and apportioned between " - "jointly liable applicants; none if the applicant or partner is exempt." + "Deductions for a claiming family's non-dependants, whether in its " + "own benefit unit or another family's: one per couple, the higher of " + "the two members' amounts (both, in the Welsh scheme for people who " + "are not pensioners, when the couple has a Universal Credit award), " + "one for each other non-dependant, each under the applicant family's " + "own scheme and apportioned equally between jointly liable " + "applicants; none if the applicant or partner is exempt." ) definition_period = YEAR unit = GBP reference = ( + "https://www.legislation.gov.uk/uksi/2012/2885/regulation/9", "https://www.legislation.gov.uk/uksi/2012/2885/schedule/1/paragraph/8", "https://www.legislation.gov.uk/wsi/2013/3029/schedule/1/paragraph/3", "https://www.legislation.gov.uk/wsi/2013/3029/schedule/6/paragraph/5", @@ -57,28 +58,44 @@ def formula(benunit, period, parameters): ) * individual_non_dep_deduction(person, period, parameters, ~head_pensioner), } + # Every eligible non-dependant normally resides with each liable + # person (SI 2012/2885 reg 9(1)): another family's adult, an + # applicant's own adult son, or a boarder's or lodger's (reg 9(2)(e) + # excludes only the person liable to pay the applicant). Members of + # the applicant's own family are not its non-dependants (reg + # 9(2)(a)); the non-dependants in its benefit unit are. + in_unit = person( + "is_benefit_unit_non_dependant_for_council_tax_reduction", period + ) head_family_pensioner = benunit.any(head_pensioner) - charged = {} + non_dependants = {} for scheme, amounts in deductions.items(): pensioner = head_family_pensioner ^ (scheme == "other") - deduction_for_benunit = deduction_per_family( - benunit, period, amounts, universal_credit_couple & ~pensioner - ) - # Each non-dependant family's amount counted once. - charged[scheme] = charged_to_other_families( - benunit, period, deduction_for_benunit - ) + both = universal_credit_couple & ~pensioner + family_amount = deduction_per_family(benunit, period, amounts, both) + # Each family's amount counted once. + counted = person("is_benunit_head", period) * benunit.project(family_amount) + in_household = benunit.max(person.household.sum(counted)) + own_in_unit = deduction_per_family(benunit, period, amounts * in_unit, both) + non_dependants[scheme] = in_household - family_amount + own_in_unit own_scheme_is_heads = ( benunit("council_tax_reduction_pensioner", period) == head_family_pensioner ) # A non-dependant of two or more jointly liable people is apportioned # equally between them (Sch 1 para 8(5)). share = benunit("council_tax_reduction_joint_liability_share", period) + # Only a family that claims bears deductions; a non-dependant's own + # family does not. + claims = benunit("council_tax_reduction_claimant_benunit", period) applicant_exempt = benunit( "council_tax_reduction_applicant_has_non_dep_exemption", period ) return where( applicant_exempt, 0, - where(own_scheme_is_heads, charged["head"], charged["other"]) * share, + claims + * where( + own_scheme_is_heads, non_dependants["head"], non_dependants["other"] + ) + * share, ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_benefit_unit_non_dependant_for_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_benefit_unit_non_dependant_for_council_tax_reduction.py new file mode 100644 index 0000000000..97d82a2e47 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_benefit_unit_non_dependant_for_council_tax_reduction.py @@ -0,0 +1,42 @@ +from policyengine_uk.model_api import * + + +class is_benefit_unit_non_dependant_for_council_tax_reduction(Variable): + value_type = bool + entity = Person + label = "Non-dependant within the benefit unit for Council Tax Reduction" + documentation = ( + "A member of the benefit unit who is neither the Council Tax " + "Reduction applicant or partner (is_council_tax_reduction_applicant_or_partner) " + "nor a child or young person: someone aged 16 or over who is not a " + "Child Benefit qualifying young person. Such a person is outside the " + "applicant's family but normally resides with the applicant, so is a " + "non-dependant. Usually the applicant is the benefit unit's claimant, " + "and this is the same set as " + "is_benefit_unit_non_dependant_for_legacy_benefits. Where a household " + "head who is not the benefit unit's claimant or partner applies alone " + "(council_tax_reduction_head_applies_alone), the benefit unit's " + "claimant and partner are the head's non-dependants. This person's own " + "benefit awards are not modelled, so the exemptions for a " + "non-dependant on an income-related benefit or on Universal Credit " + "with no earned income never apply to them." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2012/2885/regulation/9", + "https://www.legislation.gov.uk/wsi/2013/3029/regulation/9", + "https://www.legislation.gov.uk/ssi/2021/249/regulation/8", + "https://www.legislation.gov.uk/ssi/2012/319/regulation/3", + ) + + def formula(person, period, parameters): + # SI 2012/2885 and WSI 2013/3029 reg 9(1), (2)(a), (c); SSI 2021/249 + # reg 8(2)(a)-(b); SSI 2012/319 reg 3: anyone normally residing with the + # applicant except the applicant's own family. + applicant_or_partner = person( + "is_council_tax_reduction_applicant_or_partner", period + ) + child_or_young_person = person( + "is_child_or_qualifying_young_person_for_child_benefit", period + ) + return ~applicant_or_partner & ~child_or_young_person