From bce2575ca805d82ab7d9b80724006ace0c1da1c0 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 05:34:28 -0400 Subject: [PATCH 01/21] Assess Oxford Universal Credit applicants on Appendix 3 income Oxford's Council Tax Reduction scheme 2026-27 bands a working-age applicant who receives Universal Credit on Appendix 3's income: DWP's net earned income plus the Universal Credit awarded, with other income counted weekly. Its paragraphs 14 to 27, with their 50% share of pension contributions (para 18), apply only to applicants without Universal Credit. The model banded every working-age applicant on the paragraphs 14 to 27 income. oxford_council_tax_reduction_universal_credit_income adds Universal Credit's earned income before the work allowance (which deducts relievable pension contributions in full, UC Regs 2013 reg 55(5)(a)), its unearned income and the award, the three amounts the four other councils use. Oxford's award bands a Universal Credit applicant on a fifty-second of it, so a reform of Oxford's pension contribution share no longer moves them. Applicants without Universal Credit keep the same arithmetic. Co-Authored-By: Claude Opus 5.5 --- .../pension_contribution_deduction_rate.yaml | 2 +- ...ouncil_tax_reduction_universal_credit.yaml | 276 ++++++++++++++++++ ...ion_pension_contribution_deduction_rate.py | 11 +- .../oxford_council_tax_reduction.py | 11 +- ...l_tax_reduction_universal_credit_income.py | 48 +++ 5 files changed, 341 insertions(+), 7 deletions(-) create mode 100644 policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml create mode 100644 policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml index ebac88d7f5..5f181a63fc 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml @@ -1,4 +1,4 @@ -description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. An applicant who does is assessed on the net earned income DWP provides (Appendix 3), which the model does not yet do (issue 2520). +description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. An applicant who does is assessed on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. values: 2026-04-01: 0.5 metadata: diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml new file mode 100644 index 0000000000..1fe4fbc01b --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -0,0 +1,276 @@ +# Oxford Council Tax Reduction Scheme 2026 to 2027 +# (https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27). +# +# Part 1 para 9 bands two groups of working-age applicants on weekly income: +# those who do not receive Universal Credit, whose income "is calculated in +# accordance with paragraphs 14-27", and those who do, whose income follows +# Appendix 3. The heading before para 14 says "Paragraphs 14-27, and Annexes 5 +# and 6, apply to pensioners, and working-age applicants who do not receive +# Universal Credit". Para 18 deducts "50% of contributions to occupational or +# personal pension schemes" from earnings. +# +# Appendix 3 (page /4): "The income of applicants who receive Universal Credit +# is calculated by adding their net earned income (as provided by DWP) to the +# amount of Universal Credit awarded. Any transitional protection (the +# 'transitional element' of Universal Credit) included in the Universal Credit +# award is ignored. Other (unearned) income is calculated separately on a +# weekly basis." DWP's net earned income deducts "any relievable pension +# contributions made by the person" in full, with income tax and primary Class +# 1 contributions (UC Regs 2013 reg 55(5)(a) and (b)). +# +# Bands, weekly income (Part 1 para 9 and Appendix 3): up to 480.00 100%; +# 480.01 to 530.00 75%; 530.01 to 580.00 50%; 580.01 to 660.00 25%; above +# 660.00 nil. A weekly amount is a fifty-second of the yearly amount. +# +# Common facts: period 2026 (the 2026-27 rates), council tax 1,800, no +# capital, no non-dependants. The applicant, 35, earns 20,000 and pays 2,600 +# (50 a week) into an occupational pension, which the model deducts from pay +# before income tax. +# - Income tax: 20% x (20,000 - 2,600 - 12,570) = 966. +# - National Insurance: 8% x (20,000 - 12,570) = 594.40. +# - DWP net earned income (reg 55(5)): 20,000 - 966 - 594.40 - 2,600 = 15,839.60. +# Universal Credit amounts are DWP's 2026-27 monthly rates: standard +# allowance, single 25 or over, 424.90; child (born on or after 6 April +# 2017) 303.94; lower work allowance 427. The taper is 55% (reg 22). Every +# award below sits under the benefit cap outside Greater London (14,753 +# single, 22,020 otherwise). + +- name: Oxford assesses a Universal Credit claimant on DWP net earned income plus the award + # UC: maximum amount 424.90 x 12 + 12,500 housing = 17,598.80. No child or + # limited capability, so no work allowance (reg 22(2)). Reduction 55% x + # 15,839.60 = 8,711.78. Award 17,598.80 - 8,711.78 = 8,887.02. + # Appendix 3: 15,839.60 + 8,887.02 = 24,726.62 a year, 475.51 a week: + # band 1, 100% of 1,800. + # The paragraphs 14-27 income it replaces deducts half the contributions: + # 20,000 - 966 - 594.40 - 1,300 + 8,887.02 = 26,026.62, 500.51 a week, + # which band 2 would reduce to 75% (1,350). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + income_tax: 966 + national_insurance: 594.40 + uc_earned_income_before_work_allowance: 15_839.60 + uc_work_allowance: 0 + universal_credit: 8_887.02 + oxford_council_tax_reduction_universal_credit_income: 24_726.62 + council_tax_reduction_applicable_income: 26_026.62 + oxford_council_tax_reduction: 1_800 + council_tax_reduction: 1_800 + +- name: A reform of Oxford's pension contribution share leaves a Universal Credit claimant's reduction alone + # Model reform, not current law. With no share deducted, the paragraphs + # 14-27 income rises to 20,000 - 966 - 594.40 + 8,887.02 = 27,326.62, but + # Appendix 3 reads DWP's figure, so the income stays 24,726.62 and the + # reduction 1,800. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.means_test.pension_contribution_deduction_rate: 0 + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_pension_contribution_deduction_rate: 0 + council_tax_reduction_applicable_income: 27_326.62 + oxford_council_tax_reduction_universal_credit_income: 24_726.62 + oxford_council_tax_reduction: 1_800 + +- name: A full Oxford pension contribution share leaves a Universal Credit claimant's reduction alone + # Model reform, not current law. Deducting all the contributions brings the + # paragraphs 14-27 income down to 24,726.62, the Appendix 3 income, which is + # unchanged. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.means_test.pension_contribution_deduction_rate: 1 + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_applicable_income: 24_726.62 + oxford_council_tax_reduction_universal_credit_income: 24_726.62 + oxford_council_tax_reduction: 1_800 + +- name: Oxford still assesses a claimant without Universal Credit under paragraphs 14 to 27 + # Earnings 32,000 and 2,600 contributions, no Universal Credit claim. + # Income tax 20% x (32,000 - 2,600 - 12,570) = 3,366; National Insurance + # 8% x (32,000 - 12,570) = 1,554.40. Para 18: 32,000 - 3,366 - 1,554.40 - + # 50% x 2,600 = 25,779.60 a year, 495.76 a week: band 2, 75% of 1,800. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 32_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: false + claims_all_entitled_benefits: true + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + income_tax: 3_366 + national_insurance: 1_554.40 + universal_credit: 0 + council_tax_reduction_applicable_income: 25_779.60 + oxford_council_tax_reduction: 1_350 + +- name: Oxford's pension contribution share still moves a claimant without Universal Credit + # Model reform, not current law. Deducting all the contributions: 32,000 - + # 3,366 - 1,554.40 - 2,600 = 24,479.60 a year, 470.76 a week: band 1, 100%. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.means_test.pension_contribution_deduction_rate: 1 + people: + person1: + age: 35 + employment_income: 32_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: false + claims_all_entitled_benefits: true + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_applicable_income: 24_479.60 + oxford_council_tax_reduction: 1_800 + +- name: Oxford counts a lone parent's net earned income before the work allowance and no Child Benefit + # A lone parent with one child aged 5. UC: maximum amount (424.90 + 303.94) + # x 12 + 7,000 housing = 15,746.08. Lower work allowance 427 x 12 = 5,124 + # (reg 22(2)). Reduction 55% x (15,839.60 - 5,124) = 5,893.58. Award + # 15,746.08 - 5,893.58 = 9,852.50. + # Appendix 3 adds net earned income, not earned income less the work + # allowance, and the award; Child Benefit is not Universal Credit's + # unearned income: 15,839.60 + 9,852.50 = 25,692.10 a year, 494.08 a week: + # band 2, 75% of 1,800. Less the work allowance it would be 395.54 a week, + # band 1. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 5 + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 7_000 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 15_839.60 + uc_work_allowance: 5_124 + uc_unearned_income: 0 + universal_credit: 9_852.50 + oxford_council_tax_reduction_universal_credit_income: 25_692.10 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts a Universal Credit claimant's other income + # As the first case, with maintenance of 5,200 (100 a week) from a former + # spouse, which Universal Credit counts as unearned income (reg 66(1)(d)) + # and deducts in full: award 17,598.80 - 8,711.78 - 5,200 = 3,687.02. + # Appendix 3 counts it weekly with the rest: 15,839.60 + 5,200 + 3,687.02 = + # 24,726.62 a year, 475.51 a week: band 1, 100%. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + spousal_maintenance_income: 5_200 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_unearned_income: 5_200 + universal_credit: 3_687.02 + oxford_council_tax_reduction_universal_credit_income: 24_726.62 + oxford_council_tax_reduction: 1_800 diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py index d18898ecfa..0ed80f5662 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py @@ -36,11 +36,12 @@ class council_tax_reduction_pension_contribution_deduction_rate(Variable): "(UC Regs 2013 reg 55(5)(a) for employed earnings and reg 57(2) step 4 " "for self-employed earnings): in Wales (WSI 2013/3029 Sch 6 para 9), " "Scotland (SSI 2021/249 regs 49(6)(a) and 51(2)) and Oxford (its " - "scheme's Appendix 3). The model has no such route for them yet " - "(#1962 for Wales and Scotland, #2520 for Oxford), so the share here " - "reaches those applicants too. Merton, Kingston upon Thames, Newham " - "and Westminster already assess them on Universal Credit income " - "(_legacy.py)." + "scheme's Appendix 3). The model has no such route for Wales and " + "Scotland yet (#1962), so the share here reaches those applicants " + "too. Merton, Kingston upon Thames, Newham and Westminster assess " + "them on Universal Credit income (_legacy.py), and Oxford on its " + "Appendix 3 income (oxford_council_tax_reduction_universal_credit_income), " + "so the share here does not reach their awards." ) definition_period = YEAR unit = "/1" diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py index 274ffa526f..07057ca3cc 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py @@ -43,7 +43,16 @@ def formula(benunit, period, parameters): "council_tax_reduction_head_applies_alone", period ) weekly_income = max_(0, annual_income - child_benefit) / WEEKS_IN_YEAR - weekly_income += where(has_uc_award, 0, weekly_tariff_income) + # Paragraphs 14 to 27 assess an applicant without Universal Credit; + # Appendix 3 assesses one with it on DWP's net earned income, the + # award and other income instead (scheme para. 9). + uc_weekly_income = ( + benunit("oxford_council_tax_reduction_universal_credit_income", period) + / WEEKS_IN_YEAR + ) + weekly_income = where( + has_uc_award, uc_weekly_income, weekly_income + weekly_tariff_income + ) relevant_income_based_benefit = benunit( "council_tax_reduction_relevant_income_based_benefit", period, diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py new file mode 100644 index 0000000000..b4c93720a9 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -0,0 +1,48 @@ +from policyengine_uk.model_api import * + + +class oxford_council_tax_reduction_universal_credit_income(Variable): + value_type = float + entity = BenUnit + label = "Oxford Council Tax Reduction income of a Universal Credit applicant" + documentation = ( + "The yearly income on which Oxford's working-age scheme bands an " + "applicant who receives Universal Credit (Appendix 3); the scheme " + "bands a fifty-second of it each week. Appendix 3 adds the " + "applicant's and partner's net earned income, as DWP provides it, " + "to the Universal Credit awarded, and counts other (unearned) income " + "weekly. DWP's net earned income is the Universal Credit calculation " + "of earned income (uc_earned_income_before_work_allowance): earnings " + "less income tax, National Insurance and relievable pension " + "contributions in full (UC Regs 2013 reg. 55(5)(a) and reg. 57(2) " + "step 4), before the work allowance, which belongs to the award " + "(reg. 22). So Oxford's own share of pension contributions (Part 1 " + "para. 18) does not reach these applicants. Other income is the " + "Universal Credit calculation of unearned income (uc_unearned_income, " + "reg. 66), which already leaves out the benefits Appendix 3 lists as " + "ignored. Appendix 3 counts all other unearned income, which can go " + "beyond reg. 66 (Child Benefit, for one, is not on its list); the " + "model counts the reg. 66 amount only. Appendix 3 ignores any " + "transitional element in the award. The model computes no " + "transitional element (uc_maximum_amount), so the award has none to " + "take out. Paragraphs 14 to 27, Annexes 5 and 6 and Appendix 5 apply " + "only to applicants without Universal Credit, so Oxford's tariff " + "income from capital (para. 15) and its Child Benefit disregard do " + "not apply here. The four other councils the model runs assess " + "Universal Credit applicants on the same three amounts (_legacy.py)." + ) + definition_period = YEAR + unit = GBP + reference = ( + "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27", + "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/55", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/57", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/66", + ) + + def formula(benunit, period, parameters): + earned_income = benunit("uc_earned_income_before_work_allowance", period) + unearned_income = benunit("uc_unearned_income", period) + award = benunit("universal_credit", period) + return max_(0, earned_income + unearned_income + award) From 4fd0f80be7a0f310eac1feafe048865071cefa8c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 05:38:01 -0400 Subject: [PATCH 02/21] Ignore Oxford's share of the carer and disabled child elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Appendix 3 of Oxford's Council Tax Reduction scheme 2026-27 ignores £48.15 of the Universal Credit carer element and £84.46 of the disabled child element, the 2026-27 carer and disabled child premiums. Two Oxford parameters hold the weekly amounts. The Appendix 3 income ignores up to each amount for each carer element and each disabled child addition, taking it off the award but not below nil. Co-Authored-By: Claude Opus 5.5 --- .../carer_element_disregard.yaml | 10 + .../disabled_child_element_disregard.yaml | 10 + ...ouncil_tax_reduction_universal_credit.yaml | 196 ++++++++++++++++++ ...l_tax_reduction_universal_credit_income.py | 45 +++- 4 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml new file mode 100644 index 0000000000..9ecd9821dd --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml @@ -0,0 +1,10 @@ +description: Weekly amount of each Universal Credit carer element that the Oxford Council Tax Reduction scheme ignores in the income of a working-age applicant who receives Universal Credit. +values: + 2026-04-01: 48.15 +metadata: + unit: currency-GBP + period: week + label: Oxford Council Tax Reduction Universal Credit carer element disregard + reference: + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (£48.15 of the Universal Credit carer element is ignored) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml new file mode 100644 index 0000000000..cd56be0f40 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml @@ -0,0 +1,10 @@ +description: Weekly amount of each Universal Credit disabled child element that the Oxford Council Tax Reduction scheme ignores in the income of a working-age applicant who receives Universal Credit. +values: + 2026-04-01: 84.46 +metadata: + unit: currency-GBP + period: week + label: Oxford Council Tax Reduction Universal Credit disabled child element disregard + reference: + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (£84.46 of the Universal Credit disabled child element is ignored) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 1fe4fbc01b..b7b4fee5e8 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -274,3 +274,199 @@ universal_credit: 3_687.02 oxford_council_tax_reduction_universal_credit_income: 24_726.62 oxford_council_tax_reduction: 1_800 + +# Appendix 3 also ignores "£48.15 of the Universal Credit carer element" and +# "£84.46 of the Universal Credit disabled child element". An award includes +# a carer element for each claimant who cares (UC Regs 2013 reg 29) and a +# disabled child addition for each disabled child (reg 24(2)). The amounts +# are DWP's 2026-27 carer premium and disabled child premium, which are paid +# for each carer and each disabled child, so the model ignores up to each +# amount for each element, taking it off the award but not below nil. DWP's +# 2026-27 monthly amounts: carer 209.34 (48.31 a week); disabled child +# addition, lower rate 164.79 (38.03 a week), higher rate 514.71 (118.78 a +# week). + +- name: Oxford ignores 48.15 a week of a Universal Credit carer element + # As the first case, with the carer element: maximum amount 17,598.80 + + # 209.34 x 12 = 20,110.88, award 20,110.88 - 8,711.78 = 11,399.10. The + # element is 48.31 a week, so 48.15 x 52 = 2,503.80 of it is ignored. + # Appendix 3: 15,839.60 + 11,399.10 - 2,503.80 = 24,734.90 a year, 475.67 a + # week: band 1, 100% of 1,800. Counting the whole award it would be 523.82 + # a week, band 2. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + uc_carer_element_included_for_claimant: true + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_carer_element: 2_512.08 + universal_credit: 11_399.10 + oxford_council_tax_reduction_universal_credit_income: 24_734.90 + oxford_council_tax_reduction: 1_800 + +- name: Oxford's carer element disregard takes the award no lower than nil + # Housing 2,000: maximum amount 5,098.80 + 2,512.08 + 2,000 = 9,610.88, + # award 9,610.88 - 8,711.78 = 899.10, less than the 2,503.80 ignored, so + # the award counts nil. Appendix 3: 15,839.60 a year, 304.61 a week: band + # 1, 100%. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + uc_carer_element_included_for_claimant: true + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 2_000 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 899.10 + oxford_council_tax_reduction_universal_credit_income: 15_839.60 + oxford_council_tax_reduction: 1_800 + +- name: Oxford ignores the whole lower-rate disabled child addition + # As the lone-parent case, with a disabled child: maximum amount 15,746.08 + # + 164.79 x 12 = 17,723.56, award 17,723.56 - 5,893.58 = 11,829.98. The + # addition, 38.03 a week, is under 84.46, so all 1,977.48 of it is ignored. + # Appendix 3: 15,839.60 + 11,829.98 - 1,977.48 = 25,692.10 a year, 494.08 a + # week: band 2, 75% of 1,800. Counting the whole award it would be 532.11 a + # week, band 3. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 5 + is_disabled_for_benefits: true + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 7_000 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_disability_elements: 1_977.48 + universal_credit: 11_829.98 + oxford_council_tax_reduction_universal_credit_income: 25_692.10 + oxford_council_tax_reduction: 1_350 + +- name: Oxford ignores 84.46 a week of a higher-rate disabled child addition + # Maximum amount 15,746.08 + 514.71 x 12 = 21,922.60, award 21,922.60 - + # 5,893.58 = 16,029.02. The addition is 118.78 a week, so 84.46 x 52 = + # 4,391.92 of it is ignored. Appendix 3: 15,839.60 + 16,029.02 - 4,391.92 = + # 27,476.70 a year, 528.40 a week: band 2, 75% of 1,800. Counting the whole + # award it would be 612.86 a week, band 4. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 5 + is_disabled_for_benefits: true + meets_uc_higher_disabled_child_condition: true + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 7_000 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_disability_elements: 6_176.52 + universal_credit: 16_029.02 + oxford_council_tax_reduction_universal_credit_income: 27_476.70 + oxford_council_tax_reduction: 1_350 + +- name: Oxford ignores part of each disabled child's addition separately + # A lone parent with two disabled children, aged 5 (higher rate) and 8 + # (lower rate), and 3,000 housing. Maximum amount 424.90 x 12 + 2 x 303.94 + # x 12 + 514.71 x 12 + 164.79 x 12 + 3,000 = 23,547.36, award 23,547.36 - + # 5,893.58 = 17,653.78. Ignored: 4,391.92 of the higher-rate addition and + # all 1,977.48 of the lower-rate one, 6,369.40. Appendix 3: 15,839.60 + + # 17,653.78 - 6,369.40 = 27,123.98 a year, 521.61 a week: band 2, 75% of + # 1,800. One 84.46 a week for the whole award would leave 559.64 a week, + # band 3. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 5 + is_disabled_for_benefits: true + meets_uc_higher_disabled_child_condition: true + person3: + age: 8 + is_disabled_for_benefits: true + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 3_000 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_disability_elements: 8_154 + uc_benefit_cap_reduction_after_floor: 0 + universal_credit: 17_653.78 + oxford_council_tax_reduction_universal_credit_income: 27_123.98 + oxford_council_tax_reduction: 1_350 diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index b4c93720a9..ab5ce82201 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -22,7 +22,13 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): "reg. 66), which already leaves out the benefits Appendix 3 lists as " "ignored. Appendix 3 counts all other unearned income, which can go " "beyond reg. 66 (Child Benefit, for one, is not on its list); the " - "model counts the reg. 66 amount only. Appendix 3 ignores any " + "model counts the reg. 66 amount only. Appendix 3 also ignores " + "£48.15 a week of each carer element and £84.46 a week of each " + "disabled child element, which come off the award (but not below " + "nil), and, for a disabled applicant, an amount equal to any " + "disability premium payable before the move to Universal Credit. " + "The model holds no record of that earlier award, so it does not " + "ignore that amount. Appendix 3 ignores any " "transitional element in the award. The model computes no " "transitional element (uc_maximum_amount), so the award has none to " "take out. Paragraphs 14 to 27, Annexes 5 and 6 and Appendix 5 apply " @@ -42,7 +48,42 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): ) def formula(benunit, period, parameters): + p = parameters( + period + ).gov.local_authorities.oxford.council_tax_reduction.universal_credit earned_income = benunit("uc_earned_income_before_work_allowance", period) unearned_income = benunit("uc_unearned_income", period) - award = benunit("universal_credit", period) + # Appendix 3 ignores £48.15 of the carer element and £84.46 of the + # disabled child element. An award has a carer element for each + # claimant who cares (reg. 29) and a disabled child addition for each + # disabled child (reg. 24(2)), and the two amounts are the 2026-27 + # carer and disabled child premiums, one for each carer and each + # disabled child, so each element has its own disregard, up to the + # element. They come off the award, which cannot fall below nil. + carers = add(benunit, period, ["uc_carer_element_included_for_claimant"]) + carer_disregard = min_( + benunit("uc_carer_element", period), + carers * p.carer_element_disregard * WEEKS_IN_YEAR, + ) + person = benunit.members + disabled_child_element = add( + person, + period, + [ + "uc_individual_disabled_child_element", + "uc_individual_severely_disabled_child_element", + ], + ) + disabled_child_disregard = benunit.sum( + min_( + disabled_child_element, + p.disabled_child_element_disregard * WEEKS_IN_YEAR, + ) + ) + award = max_( + 0, + benunit("universal_credit", period) + - carer_disregard + - disabled_child_disregard, + ) return max_(0, earned_income + unearned_income + award) From 95b54932c39a14679db094f60edc94b24d0907ee Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 06:23:55 -0400 Subject: [PATCH 03/21] Test Oxford's Appendix 3 income with properties Hypothesis tests over generated Oxford families on Universal Credit: a reform of Oxford's pension contribution share leaves their reductions unchanged; the Appendix 3 income is DWP's net earned income, unearned income and the award less disregards bounded by the elements; the scheme's band table sets the reduction; more pension contributions never lower it; and the income equals the Universal Credit income Merton, Kingston upon Thames, Newham and Westminster use for the same family. Co-Authored-By: Claude Opus 5.5 --- ...x_reduction_universal_credit_properties.py | 424 ++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py new file mode 100644 index 0000000000..a2ba8ab79d --- /dev/null +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -0,0 +1,424 @@ +"""Property-based tests for Oxford's Council Tax Reduction for applicants who +receive Universal Credit. + +Oxford's Council Tax Reduction Scheme 2026 to 2027 bands a working-age +applicant who receives Universal Credit on Appendix 3's income: net earned +income as DWP provides it, plus the Universal Credit awarded, with other +income counted weekly, ignoring £48.15 a week of each carer element and +£84.46 a week of each disabled child element. Paragraphs 14 to 27, whose para +18 deducts half of pension contributions, apply only to applicants without +Universal Credit. DWP's net earned income deducts relievable pension +contributions in full (UC Regs 2013 reg 55(5)(a)). + +Invariants, for any generated population of working-age Oxford families (one +or two adults, earnings, occupational pension contributions, maintenance, +housing costs, children with or without a disabled child addition, carers), +over the families whose applicant has a Universal Credit award: + +1. Locality: a reform of Oxford's pension contribution share leaves every + such family's Oxford reduction unchanged, while it reaches the paragraphs + 14 to 27 income of every Oxford family (the share the model reads is the + reform's). +2. Appendix 3 income: it is DWP's net earned income plus unearned income + plus the award less the element disregards, never below the first two; + the disregards come off the award but not below nil, and are at most the + elements themselves and at most 48.15 and 84.46 a week for each carer and + each disabled child. With no carer or disabled child it is the sum of the + three amounts exactly. +3. Bands: the reduction is the council tax share times the rate that the + scheme's band table (weekly income up to 480.00, 530.00, 580.00 and + 660.00) gives for a fifty-second of the Appendix 3 income. +4. Monotonicity: more pension contributions never lower the reduction of a + family that keeps its award, since each pound contributed lowers DWP's + net earned income and raises the award by at most the 55% taper. +5. Differential: for families without a carer or disabled child element, + the Appendix 3 income equals the Universal Credit income on which + Merton, Kingston upon Thames, Newham and Westminster assess the same + family, recovered from each council's award. + +The differential against origin/main for families without Universal Credit +lives outside the suite (see the pull request); here the paragraphs 14 to 27 +route is pinned by the YAML cases in +policy/baseline/gov/local_authorities/oxford/council_tax_reduction/. +""" + +import numpy as np +from hypothesis import HealthCheck, example, given, settings +from hypothesis import strategies as st + +from policyengine_uk import Simulation +from policyengine_uk.system import system + +YEAR = 2026 +PROPERTY_SETTINGS = settings( + max_examples=5, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +OXFORD_SHARE = ( + "gov.local_authorities.oxford.council_tax_reduction.means_test." + "pension_contribution_deduction_rate" +) +COUNCIL_TAX = 1_800 +WEEKS = 52 +# The scheme's band table (Part 1 para 9 and Appendix 3): the top of each +# band's weekly income and its rate. Fractions of a penny between bands fall +# in the lower one, as the parameter's thresholds at 480.01, 530.01, 580.01 +# and 660.01 place them. +BANDS = [(480.01, 1.0), (530.01, 0.75), (580.01, 0.5), (660.01, 0.25)] +CARER_DISREGARD = 48.15 +DISABLED_CHILD_DISREGARD = 84.46 +# Float32 storage: amounts of tens of thousands carry errors of about 0.004. +TOLERANCE = 0.05 + +money = st.floats(0, 40_000, allow_nan=False, allow_infinity=False) + + +@st.composite +def families(draw): + adults = [] + for _ in range(draw(st.integers(1, 2))): + earnings = draw(st.one_of(st.just(0.0), money)) + share = draw(st.one_of(st.just(0.0), st.floats(0.01, 0.2))) + adults.append( + dict( + age=draw(st.integers(25, 55)), + employment_income=earnings, + employee_pension_contributions=round(earnings * share, 2), + spousal_maintenance_income=draw( + st.sampled_from([0.0, 0.0, 1_000.0, 5_200.0]) + ), + uc_carer_element_included_for_claimant=draw( + st.sampled_from([False, False, True]) + ), + ) + ) + # Partners within five years of each other, so neither is presumed the + # other's child. + if len(adults) == 2: + adults[1]["age"] = min(55, max(25, adults[0]["age"] + 3)) + children = [ + dict( + age=draw(st.integers(0, 15)), + disability=draw(st.sampled_from([None, None, "lower", "higher"])), + ) + for _ in range(draw(st.integers(0, 2))) + ] + return dict( + adults=adults, + children=children, + housing=draw(st.sampled_from([0.0, 3_000.0, 7_000.0, 12_500.0, 15_000.0])), + ) + + +def situation(units, local_authorities=None, council_tax=COUNCIL_TAX): + """One household for each family, in Oxford unless local_authorities + names each household's council.""" + local_authorities = local_authorities or ["OXFORD"] * len(units) + people, benunits, households = {}, {}, {} + for i, (unit, local_authority) in enumerate(zip(units, local_authorities)): + names = [] + for j, adult in enumerate(unit["adults"]): + name = f"a{i}_{j}" + people[name] = {key: {YEAR: value} for key, value in adult.items()} + names.append(name) + for j, child in enumerate(unit["children"]): + name = f"c{i}_{j}" + people[name] = {"age": {YEAR: child["age"]}} + if child["disability"] is not None: + people[name]["is_disabled_for_benefits"] = {YEAR: True} + people[name]["meets_uc_higher_disabled_child_condition"] = { + YEAR: child["disability"] == "higher" + } + names.append(name) + benunits[f"b{i}"] = { + "members": names, + "would_claim_uc": {YEAR: True}, + "claims_all_entitled_benefits": {YEAR: True}, + "uc_housing_costs_element": {YEAR: unit["housing"]}, + } + households[f"h{i}"] = { + "members": names, + "country": {YEAR: "ENGLAND"}, + "local_authority": {YEAR: local_authority}, + "council_tax": {YEAR: council_tax}, + "savings": {YEAR: 0.0}, + } + return {"people": people, "benunits": benunits, "households": households} + + +def calculate(units, variables, reform=None, **kwargs): + sim = Simulation(situation=situation(units, **kwargs), reform=reform) + return {v: np.asarray(sim.calculate(v, YEAR), dtype=float) for v in variables} + + +def band_rate(weekly_income): + for top, rate in BANDS: + if weekly_income < top: + return rate + return 0.0 + + +def has_award(values, i): + return ( + values["universal_credit"][i] > 0 + and not values["council_tax_reduction_head_applies_alone"][i] + ) + + +def example_families(): + """The YAML cases' families: each has an award, and between them they + cover a carer, a lower-rate and a higher-rate disabled child, two + disabled children and an award the carer disregard takes to nil.""" + + def adult(**extra): + values = dict( + age=35, + employment_income=20_000.0, + employee_pension_contributions=2_600.0, + spousal_maintenance_income=0.0, + uc_carer_element_included_for_claimant=False, + ) + return {**values, **extra} + + carer = dict(uc_carer_element_included_for_claimant=True) + return [ + dict(adults=[adult()], children=[], housing=12_500.0), + dict(adults=[adult(**carer)], children=[], housing=12_500.0), + dict(adults=[adult(**carer)], children=[], housing=2_000.0), + dict( + adults=[adult()], + children=[dict(age=5, disability="lower")], + housing=7_000.0, + ), + dict( + adults=[adult()], + children=[ + dict(age=5, disability="higher"), + dict(age=8, disability="lower"), + ], + housing=3_000.0, + ), + ] + + +LOCALITY_VARIABLES = [ + "universal_credit", + "council_tax_reduction_head_applies_alone", + "council_tax_reduction_pension_contribution_deduction_rate", + "council_tax_reduction_applicable_income", + "oxford_council_tax_reduction", +] + + +@PROPERTY_SETTINGS +@given( + st.lists(families(), min_size=1, max_size=8), + st.sampled_from([0.0, 0.25, 0.75, 1.0]), +) +@example(units=example_families(), share=0.0) +def test_oxford_pension_contribution_share_does_not_move_universal_credit_awards( + units, share +): + base = calculate(units, LOCALITY_VARIABLES) + moved = calculate(units, LOCALITY_VARIABLES, {OXFORD_SHARE: {str(YEAR): share}}) + rate = "council_tax_reduction_pension_contribution_deduction_rate" + for i, unit in enumerate(units): + case = (unit, share) + # The reform is live for every Oxford family. + assert base[rate][i] == 0.5, case + assert abs(moved[rate][i] - share) < 1e-6, case + if has_award(base, i): + # Invariant 1: the share does not reach an Appendix 3 award. + assert ( + abs( + moved["oxford_council_tax_reduction"][i] + - base["oxford_council_tax_reduction"][i] + ) + < TOLERANCE + ), case + + +INCOME_VARIABLES = [ + "universal_credit", + "council_tax_reduction_head_applies_alone", + "uc_earned_income_before_work_allowance", + "uc_unearned_income", + "uc_carer_element", + "uc_disability_elements", + "oxford_council_tax_reduction_universal_credit_income", + "council_tax_reduction_maximum_eligible_liability", + "council_tax_reduction_joint_liability_share", + "oxford_council_tax_reduction_non_dep_deductions", + "oxford_council_tax_reduction", +] + + +@PROPERTY_SETTINGS +@given(st.lists(families(), min_size=1, max_size=8)) +@example(units=example_families()) +def test_appendix_3_income_and_band(units): + values = calculate(units, INCOME_VARIABLES) + for i, unit in enumerate(units): + if not has_award(values, i): + continue + case = unit + earned = values["uc_earned_income_before_work_allowance"][i] + unearned = values["uc_unearned_income"][i] + award = values["universal_credit"][i] + income = values["oxford_council_tax_reduction_universal_credit_income"][i] + carers = sum( + a["uc_carer_element_included_for_claimant"] for a in unit["adults"] + ) + disabled_children = sum(c["disability"] is not None for c in unit["children"]) + # Invariant 2: the disregards come off the award only, not below nil, + # and are bounded by the elements and the weekly amounts. + ignored = earned + unearned + award - income + assert income >= max(0.0, earned + unearned) - TOLERANCE, case + assert ignored >= -TOLERANCE, case + assert ignored <= award + TOLERANCE, case + elements = values["uc_carer_element"][i] + values["uc_disability_elements"][i] + assert ignored <= elements + TOLERANCE, case + weekly_cap = carers * CARER_DISREGARD + disabled_children * ( + DISABLED_CHILD_DISREGARD + ) + assert ignored <= weekly_cap * WEEKS + TOLERANCE, case + if carers == 0 and disabled_children == 0: + assert abs(ignored) < TOLERANCE, case + # Invariant 3: the scheme's band table sets the reduction. + liability = ( + values["council_tax_reduction_maximum_eligible_liability"][i] + * values["council_tax_reduction_joint_liability_share"][i] + ) + expected = max( + 0.0, + liability * band_rate(income / WEEKS) + - values["oxford_council_tax_reduction_non_dep_deductions"][i], + ) + assert abs(values["oxford_council_tax_reduction"][i] - expected) < TOLERANCE, ( + case + ) + + +MONOTONE_VARIABLES = [ + "universal_credit", + "council_tax_reduction_head_applies_alone", + "oxford_council_tax_reduction", +] + + +@PROPERTY_SETTINGS +@given( + st.lists(families(), min_size=1, max_size=8), + st.floats(100, 5_000, allow_nan=False), +) +@example(units=example_families(), extra=1_300.0) +def test_more_pension_contributions_never_lower_the_reduction(units, extra): + raised = [] + for unit in units: + adults = [ + dict( + adult, + employee_pension_contributions=min( + adult["employment_income"], + adult["employee_pension_contributions"] + extra, + ), + ) + for adult in unit["adults"] + ] + raised.append(dict(unit, adults=adults)) + base = calculate(units, MONOTONE_VARIABLES) + more = calculate(raised, MONOTONE_VARIABLES) + for i, unit in enumerate(units): + if has_award(base, i) and has_award(more, i): + # Invariant 4. + assert ( + more["oxford_council_tax_reduction"][i] + >= base["oxford_council_tax_reduction"][i] - TOLERANCE + ), (unit, extra) + + +OTHER_COUNCILS = { + "MERTON": ("merton", "merton_council_tax_reduction"), + "KINGSTON_UPON_THAMES": ( + "kingston_upon_thames", + "kingston_upon_thames_council_tax_reduction", + ), + "NEWHAM": ("newham", "newham_council_tax_reduction"), + "WESTMINSTER": ("westminster", "westminster_council_tax_reduction"), +} +# Council tax high enough that the other councils' awards stay above nil, so +# the income behind each award can be read back from it. +LARGE_COUNCIL_TAX = 50_000 + + +@PROPERTY_SETTINGS +@given(st.lists(families(), min_size=1, max_size=6)) +@example(units=example_families()[:1]) +def test_appendix_3_income_matches_the_other_councils_universal_credit_income( + units, +): + # No carer or disabled child: those disregards are Oxford's alone. + units = [ + dict( + unit, + adults=[ + dict(adult, uc_carer_element_included_for_claimant=False) + for adult in unit["adults"] + ], + children=[dict(child, disability=None) for child in unit["children"]], + ) + for unit in units + ] + # Each family once in Oxford and once in each other council, in one + # simulation: family i of council k is household k x len(units) + i. + councils = ["OXFORD"] + list(OTHER_COUNCILS) + values = calculate( + units * len(councils), + INCOME_VARIABLES + + ["uc_maximum_amount"] + + [variable for _, variable in OTHER_COUNCILS.values()], + local_authorities=[c for c in councils for _ in units], + council_tax=LARGE_COUNCIL_TAX, + ) + parameters = system.parameters(f"{YEAR}-04-30").gov.local_authorities + n = len(units) + for k, authority in enumerate(councils[1:], start=1): + key, variable = OTHER_COUNCILS[authority] + ctr = getattr(parameters, key).council_tax_reduction + for i, unit in enumerate(units): + if not has_award(values, i): + continue + j = k * n + i + case = (unit, authority) + # The council's scheme sees the same award. + assert ( + abs(values["universal_credit"][j] - values["universal_credit"][i]) + < TOLERANCE + ), case + liability = ( + values["council_tax_reduction_maximum_eligible_liability"][j] + * values["council_tax_reduction_joint_liability_share"][j] + ) + award = values[variable][j] + appendix_3_income = values[ + "oxford_council_tax_reduction_universal_credit_income" + ][i] + assert award > 0, case + if award > liability * ctr.maximum_support_rate - TOLERANCE: + # No income above the applicable amount to read back: the + # Universal Credit income is at most the maximum amount. + assert appendix_3_income < values["uc_maximum_amount"][j] + TOLERANCE, ( + case + ) + continue + # award = liability x maximum rate - withdrawal rate x (income - + # applicable amount), with the Universal Credit maximum amount as + # the applicable amount (no non-dependants here). + income = values["uc_maximum_amount"][j] + ( + liability * ctr.maximum_support_rate - award + ) / (ctr.means_test.withdrawal_rate) + # Invariant 5. The withdrawal rate scales float32 errors in the + # award by up to five. + assert abs(income - appendix_3_income) < 5 * TOLERANCE, case From 8314ca8ca333b048a9f1bf057f7fb44b916f7475 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 06:33:19 -0400 Subject: [PATCH 04/21] Add changelog fragment for Oxford's Appendix 3 income Co-Authored-By: Claude Opus 5.5 --- changelog.d/2520.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2520.fixed.md diff --git a/changelog.d/2520.fixed.md b/changelog.d/2520.fixed.md new file mode 100644 index 0000000000..15406c7622 --- /dev/null +++ b/changelog.d/2520.fixed.md @@ -0,0 +1 @@ +Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income: DWP's net earned income, which deducts pension contributions in full, plus the award and other income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. The model had assessed them on the income for applicants without Universal Credit, which deducts half of pension contributions, so a reform of Oxford's pension contribution share moved their reductions. From 6df5ccb0d9afc252741b626f9adc1ceb3b71957f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 06:55:30 -0400 Subject: [PATCH 05/21] Name who acts in Oxford's Appendix 3 docs and tests Cite HB Regs 2006 Sch 3 para 20(7) and (8) for the carer and disabled child premiums being given for each carer and each disabled child. Co-Authored-By: Claude Opus 5.5 --- .../pension_contribution_deduction_rate.yaml | 2 +- .../universal_credit/carer_element_disregard.yaml | 2 +- .../disabled_child_element_disregard.yaml | 2 +- ...ord_council_tax_reduction_universal_credit.yaml | 13 +++++++------ ...il_tax_reduction_universal_credit_properties.py | 14 +++++++------- ...ouncil_tax_reduction_universal_credit_income.py | 4 ++-- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml index 5f181a63fc..9861c663c3 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml @@ -1,4 +1,4 @@ -description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. An applicant who does is assessed on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. +description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. The scheme assesses an applicant who does on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. values: 2026-04-01: 0.5 metadata: diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml index 9ecd9821dd..7827913d37 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml @@ -6,5 +6,5 @@ metadata: period: week label: Oxford Council Tax Reduction Universal Credit carer element disregard reference: - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (£48.15 of the Universal Credit carer element is ignored) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (ignores £48.15 of the Universal Credit carer element) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml index cd56be0f40..d7823c4d25 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml @@ -6,5 +6,5 @@ metadata: period: week label: Oxford Council Tax Reduction Universal Credit disabled child element disregard reference: - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (£84.46 of the Universal Credit disabled child element is ignored) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (ignores £84.46 of the Universal Credit disabled child element) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index b7b4fee5e8..29fc92ee3d 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -279,8 +279,9 @@ # "£84.46 of the Universal Credit disabled child element". An award includes # a carer element for each claimant who cares (UC Regs 2013 reg 29) and a # disabled child addition for each disabled child (reg 24(2)). The amounts -# are DWP's 2026-27 carer premium and disabled child premium, which are paid -# for each carer and each disabled child, so the model ignores up to each +# are the 2026-27 carer premium and disabled child premium, which Housing +# Benefit gives for each carer and each disabled child (HB Regs 2006 Sch 3 +# para 20(7) and (8)), so the model ignores up to each # amount for each element, taking it off the award but not below nil. DWP's # 2026-27 monthly amounts: carer 209.34 (48.31 a week); disabled child # addition, lower rate 164.79 (38.03 a week), higher rate 514.71 (118.78 a @@ -289,7 +290,7 @@ - name: Oxford ignores 48.15 a week of a Universal Credit carer element # As the first case, with the carer element: maximum amount 17,598.80 + # 209.34 x 12 = 20,110.88, award 20,110.88 - 8,711.78 = 11,399.10. The - # element is 48.31 a week, so 48.15 x 52 = 2,503.80 of it is ignored. + # element is 48.31 a week, so Oxford ignores 48.15 x 52 = 2,503.80 of it. # Appendix 3: 15,839.60 + 11,399.10 - 2,503.80 = 24,734.90 a year, 475.67 a # week: band 1, 100% of 1,800. Counting the whole award it would be 523.82 # a week, band 2. @@ -356,7 +357,7 @@ - name: Oxford ignores the whole lower-rate disabled child addition # As the lone-parent case, with a disabled child: maximum amount 15,746.08 # + 164.79 x 12 = 17,723.56, award 17,723.56 - 5,893.58 = 11,829.98. The - # addition, 38.03 a week, is under 84.46, so all 1,977.48 of it is ignored. + # addition, 38.03 a week, is under 84.46, so Oxford ignores all 1,977.48. # Appendix 3: 15,839.60 + 11,829.98 - 1,977.48 = 25,692.10 a year, 494.08 a # week: band 2, 75% of 1,800. Counting the whole award it would be 532.11 a # week, band 3. @@ -392,8 +393,8 @@ - name: Oxford ignores 84.46 a week of a higher-rate disabled child addition # Maximum amount 15,746.08 + 514.71 x 12 = 21,922.60, award 21,922.60 - - # 5,893.58 = 16,029.02. The addition is 118.78 a week, so 84.46 x 52 = - # 4,391.92 of it is ignored. Appendix 3: 15,839.60 + 16,029.02 - 4,391.92 = + # 5,893.58 = 16,029.02. The addition is 118.78 a week, so Oxford ignores + # 84.46 x 52 = 4,391.92 of it. Appendix 3: 15,839.60 + 16,029.02 - 4,391.92 = # 27,476.70 a year, 528.40 a week: band 2, 75% of 1,800. Counting the whole # award it would be 612.86 a week, band 4. period: 2026 diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index a2ba8ab79d..7be4f6acb0 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -37,9 +37,9 @@ family, recovered from each council's award. The differential against origin/main for families without Universal Credit -lives outside the suite (see the pull request); here the paragraphs 14 to 27 -route is pinned by the YAML cases in -policy/baseline/gov/local_authorities/oxford/council_tax_reduction/. +lives outside the suite (see the pull request); here the YAML cases in +policy/baseline/gov/local_authorities/oxford/council_tax_reduction/ pin the +paragraphs 14 to 27 route. """ import numpy as np @@ -94,8 +94,8 @@ def families(draw): ), ) ) - # Partners within five years of each other, so neither is presumed the - # other's child. + # Partners within five years of each other, so the model presumes neither + # the other's child. if len(adults) == 2: adults[1]["age"] = min(55, max(25, adults[0]["age"] + 3)) children = [ @@ -273,7 +273,7 @@ def test_appendix_3_income_and_band(units): ) disabled_children = sum(c["disability"] is not None for c in unit["children"]) # Invariant 2: the disregards come off the award only, not below nil, - # and are bounded by the elements and the weekly amounts. + # and never exceed the elements or the weekly amounts. ignored = earned + unearned + award - income assert income >= max(0.0, earned + unearned) - TOLERANCE, case assert ignored >= -TOLERANCE, case @@ -349,7 +349,7 @@ def test_more_pension_contributions_never_lower_the_reduction(units, extra): "WESTMINSTER": ("westminster", "westminster_council_tax_reduction"), } # Council tax high enough that the other councils' awards stay above nil, so -# the income behind each award can be read back from it. +# the test can read back from each award the income behind it. LARGE_COUNCIL_TAX = 50_000 diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index ab5ce82201..55ac1eb762 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -58,8 +58,8 @@ def formula(benunit, period, parameters): # claimant who cares (reg. 29) and a disabled child addition for each # disabled child (reg. 24(2)), and the two amounts are the 2026-27 # carer and disabled child premiums, one for each carer and each - # disabled child, so each element has its own disregard, up to the - # element. They come off the award, which cannot fall below nil. + # disabled child (SI 2006/213 Sch 3 para 20(7) and (8)), so each + # element has its own disregard, up to the element. They come off the award, which cannot fall below nil. carers = add(benunit, period, ["uc_carer_element_included_for_claimant"]) carer_disregard = min_( benunit("uc_carer_element", period), From e4b5c590e86afd49ecd99c96294d4ca57810f0e3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 08:52:45 -0400 Subject: [PATCH 06/21] Count Oxford's Universal Credit award before deductions for debts Appendix 3 counts "the amount of Universal Credit awarded". The model's deductions for debts (uc_deductions) come out of the payment, not the award, so the Appendix 3 income adds them back and a reform of those deductions no longer moves Oxford reductions. The property tests now check the disregards exactly against DWP's 2026-27 additions, draw deduction rates, and recover the other councils' income net of deductions. The documentation names Universal Credit's assumed yield from capital as part of the other income, and the changelog says current-law reductions move. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2520.fixed.md | 2 +- ...ouncil_tax_reduction_universal_credit.yaml | 42 ++++++++++++- ...x_reduction_universal_credit_properties.py | 60 ++++++++++++------- ...l_tax_reduction_universal_credit_income.py | 25 ++++---- 4 files changed, 94 insertions(+), 35 deletions(-) diff --git a/changelog.d/2520.fixed.md b/changelog.d/2520.fixed.md index 15406c7622..456e8c9f2b 100644 --- a/changelog.d/2520.fixed.md +++ b/changelog.d/2520.fixed.md @@ -1 +1 @@ -Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income: DWP's net earned income, which deducts pension contributions in full, plus the award and other income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. The model had assessed them on the income for applicants without Universal Credit, which deducts half of pension contributions, so a reform of Oxford's pension contribution share moved their reductions. +Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income: DWP's net earned income, which deducts pension contributions in full, plus the award before deductions for debts and other income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. The model had assessed them on the income for applicants without Universal Credit, which deducts half of pension contributions. Some of these applicants' reductions rise and some fall, and a reform of Oxford's pension contribution share no longer moves them. diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 29fc92ee3d..43a4dacae0 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -200,14 +200,16 @@ council_tax_reduction_applicable_income: 24_479.60 oxford_council_tax_reduction: 1_800 -- name: Oxford counts a lone parent's net earned income before the work allowance and no Child Benefit +- name: Oxford counts a lone parent's net earned income before the work allowance # A lone parent with one child aged 5. UC: maximum amount (424.90 + 303.94) # x 12 + 7,000 housing = 15,746.08. Lower work allowance 427 x 12 = 5,124 # (reg 22(2)). Reduction 55% x (15,839.60 - 5,124) = 5,893.58. Award # 15,746.08 - 5,893.58 = 9,852.50. # Appendix 3 adds net earned income, not earned income less the work - # allowance, and the award; Child Benefit is not Universal Credit's - # unearned income: 15,839.60 + 9,852.50 = 25,692.10 a year, 494.08 a week: + # allowance, and the award. The model counts Universal Credit's unearned + # income, which leaves out Child Benefit (Appendix 3's ignore list does not + # name it, so this is a reading): 15,839.60 + 9,852.50 = 25,692.10 a year, + # 494.08 a week: # band 2, 75% of 1,800. Less the work allowance it would be 395.54 a week, # band 1. period: 2026 @@ -471,3 +473,37 @@ universal_credit: 17_653.78 oxford_council_tax_reduction_universal_credit_income: 27_123.98 oxford_council_tax_reduction: 1_350 + +- name: Oxford counts the Universal Credit awarded before deductions for debts + # As the first case, with debts the model repays at 25% of the standard + # allowance: 0.25 x 424.90 x 12 = 1,274.70 comes out of the payment, which + # falls to 8,887.02 - 1,274.70 = 7,612.32. Appendix 3 counts "the amount of + # Universal Credit awarded", 8,887.02, so the income stays 24,726.62 a year, + # 475.51 a week: band 1, 100%. Counting the payment it would be 23,451.92. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0.25 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_deductions: 1_274.70 + universal_credit: 7_612.32 + oxford_council_tax_reduction_universal_credit_income: 24_726.62 + oxford_council_tax_reduction: 1_800 diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 7be4f6acb0..363b424a4a 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -16,15 +16,14 @@ over the families whose applicant has a Universal Credit award: 1. Locality: a reform of Oxford's pension contribution share leaves every - such family's Oxford reduction unchanged, while it reaches the paragraphs - 14 to 27 income of every Oxford family (the share the model reads is the - reform's). + such family's Oxford reduction unchanged, while every Oxford family reads + the reform's share. 2. Appendix 3 income: it is DWP's net earned income plus unearned income - plus the award less the element disregards, never below the first two; - the disregards come off the award but not below nil, and are at most the - elements themselves and at most 48.15 and 84.46 a week for each carer and - each disabled child. With no carer or disabled child it is the sum of the - three amounts exactly. + plus the award before deductions for debts, less the element disregards, + never below the first two. The disregards come off the award but not + below nil: the award or, if less, 48.15 a week of each carer element up + to the element plus 84.46 a week of each disabled child's addition up to + the addition, with the additions at DWP's 2026-27 rates. 3. Bands: the reduction is the council tax share times the rate that the scheme's band table (weekly income up to 480.00, 530.00, 580.00 and 660.00) gives for a fifty-second of the Appendix 3 income. @@ -32,9 +31,10 @@ family that keeps its award, since each pound contributed lowers DWP's net earned income and raises the award by at most the 55% taper. 5. Differential: for families without a carer or disabled child element, - the Appendix 3 income equals the Universal Credit income on which - Merton, Kingston upon Thames, Newham and Westminster assess the same - family, recovered from each council's award. + the Appendix 3 income less any deductions for debts equals the Universal + Credit income on which Merton, Kingston upon Thames, Newham and + Westminster assess the same family, recovered from each council's award + (they count the award after those deductions). The differential against origin/main for families without Universal Credit lives outside the suite (see the pull request); here the YAML cases in @@ -69,6 +69,8 @@ BANDS = [(480.01, 1.0), (530.01, 0.75), (580.01, 0.5), (660.01, 0.25)] CARER_DISREGARD = 48.15 DISABLED_CHILD_DISREGARD = 84.46 +# DWP's 2026-27 disabled child additions, 164.79 and 514.71 a month, weekly. +DISABLED_CHILD_ADDITION = {"lower": 164.79 * 12 / 52, "higher": 514.71 * 12 / 52} # Float32 storage: amounts of tens of thousands carry errors of about 0.004. TOLERANCE = 0.05 @@ -109,6 +111,8 @@ def families(draw): adults=adults, children=children, housing=draw(st.sampled_from([0.0, 3_000.0, 7_000.0, 12_500.0, 15_000.0])), + # Deductions for debts, as a share of the standard allowance. + deduction_rate=draw(st.sampled_from([0.0, 0.0, 0.15])), ) @@ -137,6 +141,7 @@ def situation(units, local_authorities=None, council_tax=COUNCIL_TAX): "would_claim_uc": {YEAR: True}, "claims_all_entitled_benefits": {YEAR: True}, "uc_housing_costs_element": {YEAR: unit["housing"]}, + "uc_deduction_rate": {YEAR: unit.get("deduction_rate", 0.0)}, } households[f"h{i}"] = { "members": names, @@ -242,6 +247,7 @@ def test_oxford_pension_contribution_share_does_not_move_universal_credit_awards INCOME_VARIABLES = [ "universal_credit", + "uc_deductions", "council_tax_reduction_head_applies_alone", "uc_earned_income_before_work_allowance", "uc_unearned_income", @@ -266,7 +272,8 @@ def test_appendix_3_income_and_band(units): case = unit earned = values["uc_earned_income_before_work_allowance"][i] unearned = values["uc_unearned_income"][i] - award = values["universal_credit"][i] + # The award before deductions for debts come out of the payment. + award = values["universal_credit"][i] + values["uc_deductions"][i] income = values["oxford_council_tax_reduction_universal_credit_income"][i] carers = sum( a["uc_carer_element_included_for_claimant"] for a in unit["adults"] @@ -284,8 +291,17 @@ def test_appendix_3_income_and_band(units): DISABLED_CHILD_DISREGARD ) assert ignored <= weekly_cap * WEEKS + TOLERANCE, case - if carers == 0 and disabled_children == 0: - assert abs(ignored) < TOLERANCE, case + # The disregards exactly, from DWP's 2026-27 amounts. + carer_part = min( + values["uc_carer_element"][i], carers * CARER_DISREGARD * WEEKS + ) + child_part = sum( + min(DISABLED_CHILD_ADDITION[c["disability"]], DISABLED_CHILD_DISREGARD) + * WEEKS + for c in unit["children"] + if c["disability"] is not None + ) + assert abs(ignored - min(award, carer_part + child_part)) < TOLERANCE, case # Invariant 3: the scheme's band table sets the reduction. liability = ( values["council_tax_reduction_maximum_eligible_liability"][i] @@ -402,16 +418,18 @@ def test_appendix_3_income_matches_the_other_councils_universal_credit_income( * values["council_tax_reduction_joint_liability_share"][j] ) award = values[variable][j] - appendix_3_income = values[ - "oxford_council_tax_reduction_universal_credit_income" - ][i] + # Those councils count the award after deductions for debts, + # which Oxford adds back. + council_income = ( + values["oxford_council_tax_reduction_universal_credit_income"][i] + - values["uc_deductions"][i] + ) assert award > 0, case if award > liability * ctr.maximum_support_rate - TOLERANCE: # No income above the applicable amount to read back: the # Universal Credit income is at most the maximum amount. - assert appendix_3_income < values["uc_maximum_amount"][j] + TOLERANCE, ( - case - ) + maximum_amount = values["uc_maximum_amount"][j] + assert council_income < maximum_amount + TOLERANCE, case continue # award = liability x maximum rate - withdrawal rate x (income - # applicable amount), with the Universal Credit maximum amount as @@ -421,4 +439,4 @@ def test_appendix_3_income_matches_the_other_councils_universal_credit_income( ) / (ctr.means_test.withdrawal_rate) # Invariant 5. The withdrawal rate scales float32 errors in the # award by up to five. - assert abs(income - appendix_3_income) < 5 * TOLERANCE, case + assert abs(income - council_income) < 5 * TOLERANCE, case diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index 55ac1eb762..85f3170c1c 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -20,9 +20,13 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): "para. 18) does not reach these applicants. Other income is the " "Universal Credit calculation of unearned income (uc_unearned_income, " "reg. 66), which already leaves out the benefits Appendix 3 lists as " - "ignored. Appendix 3 counts all other unearned income, which can go " - "beyond reg. 66 (Child Benefit, for one, is not on its list); the " - "model counts the reg. 66 amount only. Appendix 3 also ignores " + "ignored and counts Universal Credit's assumed yield from capital " + "(uc_tariff_income, reg. 72) in place of Oxford's own tariff income. " + "Appendix 3 counts all other unearned income, which can go beyond " + "reg. 66 (Child Benefit, for one, is not on its list); the model " + "counts the reg. 66 amount only. The award is the amount awarded, " + "before deductions for debts (uc_deductions) come out of the " + "payment. Appendix 3 also ignores " "£48.15 a week of each carer element and £84.46 a week of each " "disabled child element, which come off the award (but not below " "nil), and, for a disabled applicant, an amount equal to any " @@ -59,7 +63,8 @@ def formula(benunit, period, parameters): # disabled child (reg. 24(2)), and the two amounts are the 2026-27 # carer and disabled child premiums, one for each carer and each # disabled child (SI 2006/213 Sch 3 para 20(7) and (8)), so each - # element has its own disregard, up to the element. They come off the award, which cannot fall below nil. + # element has its own disregard, up to the element. They come off + # the award, which cannot fall below nil. carers = add(benunit, period, ["uc_carer_element_included_for_claimant"]) carer_disregard = min_( benunit("uc_carer_element", period), @@ -80,10 +85,10 @@ def formula(benunit, period, parameters): p.disabled_child_element_disregard * WEEKS_IN_YEAR, ) ) - award = max_( - 0, - benunit("universal_credit", period) - - carer_disregard - - disabled_child_disregard, - ) + # Appendix 3 counts "the amount of Universal Credit awarded". The + # deductions for debts (uc_deductions) come out of the payment, not + # the award, so the model adds them back; the benefit cap reduces + # the award itself. + award = benunit("universal_credit", period) + benunit("uc_deductions", period) + award = max_(0, award - carer_disregard - disabled_child_disregard) return max_(0, earned_income + unearned_income + award) From fcd4c5debabf20d14d466688e803870d69478650 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 12:39:12 -0400 Subject: [PATCH 07/21] Count Oxford's Appendix 3 other income and start it in April 2026 Appendix 3 takes "All other unearned income" into account, and Appendix 5's Child Benefit and child maintenance disregards apply only to applicants without Universal Credit. A new Oxford parameter, universal_credit. other_income_sources, lists the receipts counted, with the reason for each: Child Benefit, child maintenance, the receipts in Universal Credit's unearned income, and the other unearned income Appendix 3 does not ignore. Universal Credit's assumed yield from capital and income derived from capital (para 30 and Annex 7) stay out. The income is the applicant's and partner's; a household head who applies alone counts only their own. The model holds Oxford's 2026-27 scheme, so universal_credit.in_effect turns the Appendix 3 route on from April 2026 and earlier years keep the results they had before. YAML cases work the income by hand for Child Benefit, child and spousal maintenance, a private pension, interest, dividends, rent, capital, two carers and a head applying alone, and pin 2024 and 2025 and reforms that move the start. Co-Authored-By: Claude Opus 5.5 --- .../pension_contribution_deduction_rate.yaml | 2 +- .../universal_credit/in_effect.yaml | 21 + .../other_income_sources.yaml | 79 +++ ...ouncil_tax_reduction_universal_credit.yaml | 556 +++++++++++++++++- ...uction_universal_credit_earlier_years.yaml | 328 +++++++++++ ...ion_pension_contribution_deduction_rate.py | 8 +- .../oxford_council_tax_reduction.py | 11 +- ...l_tax_reduction_universal_credit_income.py | 37 +- ...reduction_universal_credit_other_income.py | 58 ++ 9 files changed, 1048 insertions(+), 52 deletions(-) create mode 100644 policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml create mode 100644 policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml index 9861c663c3..5127c1e6e0 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml @@ -1,4 +1,4 @@ -description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. The scheme assesses an applicant who does on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. +description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. The scheme assesses an applicant who does on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. The model applies Appendix 3 from April 2026 (universal_credit.in_effect), so in earlier years this share reaches them too. values: 2026-04-01: 0.5 metadata: diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml new file mode 100644 index 0000000000..fe58d447f4 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml @@ -0,0 +1,21 @@ +description: Oxford's Council Tax Reduction scheme assesses a working-age applicant who receives Universal Credit on the income its Appendix 3 sets when this is true. Otherwise the model assesses them on the income for applicants without Universal Credit, less Child Benefit and without Oxford's assumed income from capital. +values: + # The model holds Oxford's 2026-27 scheme only: its other Oxford parameters + # start on 1 April 2026 and backdating carries them to earlier years. The + # model reads each year's parameters at 30 April, so this switches Appendix + # 3 on in model year 2026 and leaves earlier years as they were. Oxford's + # 2025-26 scheme also had an Appendix 3, with other bands and a Carer's + # Allowance disregard in place of the element disregards, which the model + # does not hold. + 2015-01-01: false + 2026-04-01: true +metadata: + unit: bool + period: year + economy: false + label: Oxford Council Tax Reduction Appendix 3 income for Universal Credit applicants in effect + reference: + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Part 1 para. 9 (applicants who receive Universal Credit satisfy the conditions in Appendix 3) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27 + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (applicants who receive Universal Credit) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml new file mode 100644 index 0000000000..ec2f93dcf3 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml @@ -0,0 +1,79 @@ +description: > + Other (unearned) income that the Oxford Council Tax Reduction scheme counts, + with DWP's net earned income and the Universal Credit awarded, for a + working-age applicant who receives Universal Credit. Appendix 3 lists the + income it ignores and says "All other unearned income is taken into + account". Appendix 5's disregards, including Child Benefit and child + maintenance, apply only to applicants who "do not receive Universal Credit". + The list leaves out what Appendix 3 ignores (Attendance Allowance, + Disability Living Allowance, Personal Independence Payment, Discretionary + Housing Payments, Housing Benefit and discretionary council tax reductions) + and income derived from capital: savings interest, dividends and rent, + which Part 1 para 30 and Annex 7 treat as capital. Appendix 3 also ignores + payments from sub-tenants and boarders. Universal Credit's assumed yield + from capital (uc_tariff_income) is not a receipt and stays out, and so does + Oxford's own assumed income from capital, which para 15 adds only under + paragraphs 14 to 27. Statutory sick, maternity and paternity pay are in + DWP's net earned income (UC Regs 2013 reg 55(4)), so they are not listed + here. Payments that only Scotland makes, other than the carer support + payment, cannot reach an Oxford applicant and are not listed. +metadata: + economy: false + label: Oxford Council Tax Reduction Universal Credit applicant other income sources + period: year + unit: list + reference: + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (other unearned income, the income ignored) and Appendix 5 (applies to applicants who do not receive Universal Credit) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Annex 7 (income derived from capital is treated as capital) + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/3 + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Part 1 paras 15 and 30 + href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27 + - title: The Universal Credit Regulations 2013 reg. 66(1) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/66 +values: + 2026-04-01: + # Appendix 5's Child Benefit and child maintenance disregards apply only + # to applicants without Universal Credit, and Appendix 3 does not list + # either. + - child_benefit + - maintenance_income + # Universal Credit's unearned income (reg 66(1)) that is a receipt: all + # of gov.dwp.universal_credit.means_test.income_definitions.unearned + # except uc_tariff_income. Appendix 3 has no £15 disregard for spousal + # maintenance or Widowed Parent's Allowance; Appendix 5's apply only to + # applicants without Universal Credit. + - spousal_maintenance_income + - private_pension_income + - state_pension + - carers_allowance + # The whole carer support payment, not the part reg 66(1)(b)(iiia) + # counts: Appendix 3 has no cap at the Carer's Allowance rate. + - carer_support_payment + - jsa_contrib + - esa_contrib + - maternity_allowance + - iidb + - widowed_parents_allowance + - incapacity_benefit + - sda + # Unearned income that reg 66 leaves out but Appendix 3 does not ignore. + # War pensions, Armed Forces Independence Payment and Healthy Start are + # in Appendix 5's ignored income, which applies only to applicants + # without Universal Credit. + - bsp + - afcs + - war_widows_pension + - armed_forces_independence_payment + - ssmg + - healthy_start_vouchers + - private_transfer_income + - miscellaneous_income + # Universal Credit replaces these awards, so they are nil for a Universal + # Credit claimant. They are listed so that no receipt escapes "All other + # unearned income". + - income_support + - claimant_or_partner_jsa_income + - claimant_or_partner_esa_income + - tax_credits + - pension_credit diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 43a4dacae0..f4464ee0a5 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -18,6 +18,15 @@ # contributions made by the person" in full, with income tax and primary Class # 1 contributions (UC Regs 2013 reg 55(5)(a) and (b)). # +# Other income. Appendix 3 lists the income it ignores and says "All other +# unearned income is taken into account". Appendix 5, which ignores Child +# Benefit and child maintenance, applies to "pensioners and working-age +# applicants who do not receive Universal Credit", so Oxford counts both for +# these applicants. Part 1 para 30 and Annex 7 treat "any income derived from +# capital" as capital, so interest, dividends and rent stay out. Universal +# Credit's assumed yield from capital (reg 72) reduces the award but is not a +# receipt, so it stays out too. +# # Bands, weekly income (Part 1 para 9 and Appendix 3): up to 480.00 100%; # 480.01 to 530.00 75%; 530.01 to 580.00 50%; 580.01 to 660.00 25%; above # 660.00 nil. A weekly amount is a fifty-second of the yearly amount. @@ -30,10 +39,12 @@ # - National Insurance: 8% x (20,000 - 12,570) = 594.40. # - DWP net earned income (reg 55(5)): 20,000 - 966 - 594.40 - 2,600 = 15,839.60. # Universal Credit amounts are DWP's 2026-27 monthly rates: standard -# allowance, single 25 or over, 424.90; child (born on or after 6 April -# 2017) 303.94; lower work allowance 427. The taper is 55% (reg 22). Every -# award below sits under the benefit cap outside Greater London (14,753 -# single, 22,020 otherwise). +# allowance, single 25 or over, 424.90, couple 666.97; child (born on or +# after 6 April 2017) 303.94; lower work allowance 427, higher 710. The taper +# is 55% (reg 22). Every award below sits under the benefit cap outside +# Greater London (14,753 single, 22,020 otherwise). Child Benefit, at HMRC's +# 2026-27 weekly rates, is 27.05 for the eldest child and 17.90 for each +# other: 1,406.60 a year for one child and 2,337.40 for two. - name: Oxford assesses a Universal Credit claimant on DWP net earned income plus the award # UC: maximum amount 424.90 x 12 + 12,500 housing = 17,598.80. No child or @@ -206,12 +217,10 @@ # (reg 22(2)). Reduction 55% x (15,839.60 - 5,124) = 5,893.58. Award # 15,746.08 - 5,893.58 = 9,852.50. # Appendix 3 adds net earned income, not earned income less the work - # allowance, and the award. The model counts Universal Credit's unearned - # income, which leaves out Child Benefit (Appendix 3's ignore list does not - # name it, so this is a reading): 15,839.60 + 9,852.50 = 25,692.10 a year, - # 494.08 a week: - # band 2, 75% of 1,800. Less the work allowance it would be 395.54 a week, - # band 1. + # allowance, the award and other income, here Child Benefit of 1,406.60: + # 15,839.60 + 9,852.50 + 1,406.60 = 27,098.70 a year, 521.13 a week: band + # 2, 75% of 1,800. Less the work allowance it would be 422.59 a week, band + # 1. period: 2026 absolute_error_margin: 0.01 input: @@ -240,15 +249,19 @@ uc_work_allowance: 5_124 uc_unearned_income: 0 universal_credit: 9_852.50 - oxford_council_tax_reduction_universal_credit_income: 25_692.10 + child_benefit: 1_406.60 + oxford_council_tax_reduction_universal_credit_other_income: 1_406.60 + oxford_council_tax_reduction_universal_credit_income: 27_098.70 oxford_council_tax_reduction: 1_350 - name: Oxford counts a Universal Credit claimant's other income # As the first case, with maintenance of 5,200 (100 a week) from a former # spouse, which Universal Credit counts as unearned income (reg 66(1)(d)) # and deducts in full: award 17,598.80 - 8,711.78 - 5,200 = 3,687.02. - # Appendix 3 counts it weekly with the rest: 15,839.60 + 5,200 + 3,687.02 = - # 24,726.62 a year, 475.51 a week: band 1, 100%. + # Appendix 3 counts it weekly with the rest, with no £15 disregard (Appendix + # 5's applies only to applicants without Universal Credit): 15,839.60 + + # 5,200 + 3,687.02 = 24,726.62 a year, 475.51 a week: band 1, 100%. Each + # pound of it takes a pound off the award, so the income does not move. period: 2026 absolute_error_margin: 0.01 input: @@ -274,6 +287,7 @@ output: uc_unearned_income: 5_200 universal_credit: 3_687.02 + oxford_council_tax_reduction_universal_credit_other_income: 5_200 oxford_council_tax_reduction_universal_credit_income: 24_726.62 oxford_council_tax_reduction: 1_800 @@ -360,9 +374,9 @@ # As the lone-parent case, with a disabled child: maximum amount 15,746.08 # + 164.79 x 12 = 17,723.56, award 17,723.56 - 5,893.58 = 11,829.98. The # addition, 38.03 a week, is under 84.46, so Oxford ignores all 1,977.48. - # Appendix 3: 15,839.60 + 11,829.98 - 1,977.48 = 25,692.10 a year, 494.08 a - # week: band 2, 75% of 1,800. Counting the whole award it would be 532.11 a - # week, band 3. + # Appendix 3, with Child Benefit of 1,406.60: 15,839.60 + 1,406.60 + + # 11,829.98 - 1,977.48 = 27,098.70 a year, 521.13 a week: band 2, 75% of + # 1,800. Counting the whole award it would be 559.16 a week, band 3. period: 2026 absolute_error_margin: 0.01 input: @@ -390,15 +404,16 @@ output: uc_disability_elements: 1_977.48 universal_credit: 11_829.98 - oxford_council_tax_reduction_universal_credit_income: 25_692.10 + oxford_council_tax_reduction_universal_credit_income: 27_098.70 oxford_council_tax_reduction: 1_350 - name: Oxford ignores 84.46 a week of a higher-rate disabled child addition # Maximum amount 15,746.08 + 514.71 x 12 = 21,922.60, award 21,922.60 - # 5,893.58 = 16,029.02. The addition is 118.78 a week, so Oxford ignores - # 84.46 x 52 = 4,391.92 of it. Appendix 3: 15,839.60 + 16,029.02 - 4,391.92 = - # 27,476.70 a year, 528.40 a week: band 2, 75% of 1,800. Counting the whole - # award it would be 612.86 a week, band 4. + # 84.46 x 52 = 4,391.92 of it. Appendix 3, with Child Benefit of 1,406.60: + # 15,839.60 + 1,406.60 + 16,029.02 - 4,391.92 = 28,883.30 a year, 555.45 a + # week: band 3, 50% of 1,800. Counting the whole award it would be 639.91 a + # week, band 4. period: 2026 absolute_error_margin: 0.01 input: @@ -427,18 +442,18 @@ output: uc_disability_elements: 6_176.52 universal_credit: 16_029.02 - oxford_council_tax_reduction_universal_credit_income: 27_476.70 - oxford_council_tax_reduction: 1_350 + oxford_council_tax_reduction_universal_credit_income: 28_883.30 + oxford_council_tax_reduction: 900 - name: Oxford ignores part of each disabled child's addition separately # A lone parent with two disabled children, aged 5 (higher rate) and 8 # (lower rate), and 3,000 housing. Maximum amount 424.90 x 12 + 2 x 303.94 # x 12 + 514.71 x 12 + 164.79 x 12 + 3,000 = 23,547.36, award 23,547.36 - # 5,893.58 = 17,653.78. Ignored: 4,391.92 of the higher-rate addition and - # all 1,977.48 of the lower-rate one, 6,369.40. Appendix 3: 15,839.60 + - # 17,653.78 - 6,369.40 = 27,123.98 a year, 521.61 a week: band 2, 75% of - # 1,800. One 84.46 a week for the whole award would leave 559.64 a week, - # band 3. + # all 1,977.48 of the lower-rate one, 6,369.40. Appendix 3, with Child + # Benefit of 2,337.40: 15,839.60 + 2,337.40 + 17,653.78 - 6,369.40 = + # 29,461.38 a year, 566.57 a week: band 3, 50% of 1,800. One 84.46 a week + # for the whole award would leave 604.59 a week, band 4. period: 2026 absolute_error_margin: 0.01 input: @@ -471,8 +486,8 @@ uc_disability_elements: 8_154 uc_benefit_cap_reduction_after_floor: 0 universal_credit: 17_653.78 - oxford_council_tax_reduction_universal_credit_income: 27_123.98 - oxford_council_tax_reduction: 1_350 + oxford_council_tax_reduction_universal_credit_income: 29_461.38 + oxford_council_tax_reduction: 900 - name: Oxford counts the Universal Credit awarded before deductions for debts # As the first case, with debts the model repays at 25% of the standard @@ -507,3 +522,488 @@ universal_credit: 7_612.32 oxford_council_tax_reduction_universal_credit_income: 24_726.62 oxford_council_tax_reduction: 1_800 + +# Other income, worked from the scheme text. A lone parent earning 300 a week +# (15,600 a year) with two children aged 5 and 8, no pension contributions, +# no housing costs and no capital. Income tax 20% x (15,600 - 12,570) = 606; +# National Insurance 8% x (15,600 - 12,570) = 242.40. DWP net earned income +# 15,600 - 606 - 242.40 = 14,751.60. UC: maximum amount 424.90 x 12 + 2 x +# 303.94 x 12 = 12,393.36; with no housing costs the higher work allowance +# applies, 710 x 12 = 8,520; reduction 55% x (14,751.60 - 8,520) = 3,427.38; +# award 8,965.98. + +- name: Oxford counts Child Benefit in a Universal Credit applicant's income + # Appendix 3: 14,751.60 + 8,965.98 + 2,337.40 Child Benefit = 26,054.98 a + # year, 501.06 a week: band 2, 75% of 1,800. Leaving out Child Benefit it + # would be 456.11 a week, band 1. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 14_751.60 + uc_work_allowance: 8_520 + universal_credit: 8_965.98 + child_benefit: 2_337.40 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts child maintenance in a Universal Credit applicant's income + # As above, with child maintenance of 2,600 (50 a week). Universal Credit + # does not count it (reg 66(1)(d) reaches only maintenance from a spouse or + # former spouse), so the award stays 8,965.98. Appendix 5's child + # maintenance disregard applies only to applicants without Universal + # Credit. Appendix 3: 26,054.98 + 2,600 = 28,654.98 a year, 551.06 a week: + # band 3, 50% of 1,800. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + maintenance_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 4_937.40 + oxford_council_tax_reduction_universal_credit_income: 28_654.98 + oxford_council_tax_reduction: 900 + +- name: Spousal maintenance leaves a Universal Credit applicant's Oxford income unchanged + # As above, with maintenance of 2,600 from a former spouse. Universal + # Credit counts it in full (reg 66(1)(d)): award 8,965.98 - 2,600 = + # 6,365.98. Appendix 3 counts it too: 14,751.60 + 6,365.98 + 2,337.40 + + # 2,600 = 26,054.98 a year, 501.06 a week: band 2, 75%. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + spousal_maintenance_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 6_365.98 + oxford_council_tax_reduction_universal_credit_other_income: 4_937.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: A private pension leaves a Universal Credit applicant's Oxford income unchanged + # As above, with a private pension of 2,600. Universal Credit counts it in + # full (reg 66(1)(a)) and deducts only the tax on earnings from earned + # income (reg 55(5)(b)), so earned income stays 14,751.60 and the award + # falls to 6,365.98. Appendix 3: 14,751.60 + 6,365.98 + 2,337.40 + 2,600 = + # 26,054.98 a year, 501.06 a week: band 2, 75%. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + private_pension_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 14_751.60 + universal_credit: 6_365.98 + oxford_council_tax_reduction_universal_credit_other_income: 4_937.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: Savings interest stay out of a Universal Credit applicant's Oxford income + # As the Child Benefit case, with 2,600 (50 a week) of savings_interest_income. + # Part 1 para 30 and Annex 7 treat "any income derived from capital" as + # capital, and Universal Credit does not count it either, so the award + # stays 8,965.98 and the income 26,054.98 a year, 501.06 a week: band 2, + # 75%. Counted, it would be 551.06 a week, band 3. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + savings_interest_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 14_751.60 + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: Dividends stay out of a Universal Credit applicant's Oxford income + # As the Child Benefit case, with 2,600 (50 a week) of dividend_income. + # Part 1 para 30 and Annex 7 treat "any income derived from capital" as + # capital, and Universal Credit does not count it either, so the award + # stays 8,965.98 and the income 26,054.98 a year, 501.06 a week: band 2, + # 75%. Counted, it would be 551.06 a week, band 3. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + dividend_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 14_751.60 + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: Rent stay out of a Universal Credit applicant's Oxford income + # As the Child Benefit case, with 2,600 (50 a week) of property_income. + # Part 1 para 30 and Annex 7 treat "any income derived from capital" as + # capital, and Universal Credit does not count it either, so the award + # stays 8,965.98 and the income 26,054.98 a year, 501.06 a week: band 2, + # 75%. Counted, it would be 551.06 a week, band 3. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + property_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 14_751.60 + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +- name: Universal Credit's assumed yield from capital stays out of Oxford's income + # As the Child Benefit case, with 16,000 of capital, within Oxford's limit + # (para 10). Universal Credit assumes 4.35 a month for each 250 or part + # above 6,000 (reg 72): 40 x 4.35 x 12 = 2,088, which comes off the award: + # 8,965.98 - 2,088 = 6,877.98. Nobody receives that yield, so Appendix 3 + # does not add it back, and Oxford's own tariff income (para 15) applies + # only under paragraphs 14 to 27: 14,751.60 + 6,877.98 + 2,337.40 = + # 23,966.98 a year, 460.90 a week: band 1, 100%. Counting the yield it + # would be 501.06 a week, band 2. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 16_000 + output: + uc_tariff_income: 2_088 + universal_credit: 6_877.98 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 23_966.98 + oxford_council_tax_reduction: 1_800 + +- name: Capital below Oxford's limit lowers a Universal Credit applicant's income through the award + # As the Child Benefit case, with 11,250 of capital: 21 x 4.35 x 12 = + # 1,096.20 of assumed yield, award 8,965.98 - 1,096.20 = 7,869.78. + # Appendix 3: 14,751.60 + 7,869.78 + 2,337.40 = 24,958.78 a year, 479.98 a + # week: band 1, 100%. Counting the yield it would be 501.06 a week, band 2. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 11_250 + output: + uc_tariff_income: 1_096.20 + universal_credit: 7_869.78 + oxford_council_tax_reduction_universal_credit_income: 24_958.78 + oxford_council_tax_reduction: 1_800 + +- name: Oxford counts Child Benefit for a parent with pension contributions + # The applicant of the first case, with children aged 5 and 8 and no + # housing costs. UC: maximum amount 12,393.36, higher work allowance 8,520, + # reduction 55% x (15,839.60 - 8,520) = 4,025.78, award 8,367.58. Appendix + # 3: 15,839.60 + 8,367.58 + 2,337.40 = 26,544.58 a year, 510.47 a week: + # band 2, 75% of 1,800. Leaving out Child Benefit it would be 465.52 a + # week, band 1. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 15_839.60 + universal_credit: 8_367.58 + oxford_council_tax_reduction_universal_credit_income: 26_544.58 + oxford_council_tax_reduction: 1_350 + +- name: Oxford ignores 48.15 a week of each of two carers' elements + # A couple, both 25 or over and both carers, with no children. One earns + # 12,570, at the personal allowance and the primary threshold, so no income + # tax or National Insurance is due and DWP net earned income is 12,570. + # Housing 10,500. UC: maximum amount 666.97 x 12 + 2 x 209.34 x 12 + 10,500 + # = 23,527.80, no work allowance, reduction 55% x 12,570 = 6,913.50, award + # 16,614.30. Oxford ignores 48.15 x 52 = 2,503.80 of each carer element + # (each 2,512.08), 5,007.60 in all. Appendix 3: 12,570 + 16,614.30 - + # 5,007.60 = 24,176.70 a year, 464.94 a week: band 1, 100% of 1,800. One + # disregard for the couple would leave 513.09 a week, band 2. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 12_570 + uc_carer_element_included_for_claimant: true + person2: + age: 36 + uc_carer_element_included_for_claimant: true + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 10_500 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 12_570 + uc_carer_element: 5_024.16 + universal_credit: 16_614.30 + oxford_council_tax_reduction_universal_credit_income: 24_176.70 + oxford_council_tax_reduction: 1_800 + +- name: A household head who applies alone keeps the income for applicants without Universal Credit + # A grandmother, 50, heads the household and shares a benefit unit with + # parents aged 17 and their baby, who claim Universal Credit. The award and + # the Child Benefit are the couple's, so she applies alone, without an + # award, under paragraphs 14 to 27 on her own income: a private pension of + # 27,400 less income tax of 20% x (27,400 - 12,570) = 2,966 is 24,434 a + # year, 469.88 a week: band 1, 100%. Counting the baby's Child Benefit it + # would be 496.93 a week, band 2. Her Appendix 3 other income is her own + # pension alone. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + grandmother: + age: 50 + is_household_head: true + private_pension_income: 27_400 + young_mother: + age: 17 + is_parent: true + is_household_head: false + young_father: + age: 17 + is_parent: true + is_household_head: false + baby: + age: 0 + is_household_head: false + benunits: + family: + members: [grandmother, young_mother, young_father, baby] + claims_all_entitled_benefits: true + would_claim_uc: true + households: + home: + members: [grandmother, young_mother, young_father, baby] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_head_applies_alone: [true] + is_council_tax_reduction_applicant_or_partner: [true, false, false, false] + child_benefit: 1_406.60 + council_tax_reduction_applicable_income: 24_434 + oxford_council_tax_reduction_universal_credit_other_income: 27_400 + oxford_council_tax_reduction: 1_800 diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml new file mode 100644 index 0000000000..9611fa45ca --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml @@ -0,0 +1,328 @@ +# Oxford Council Tax Reduction before April 2026. +# +# The model holds Oxford's 2026-27 scheme: its parameters start on 1 April +# 2026 and backdating carries them to earlier years, so earlier years read the +# 2026-27 bands (Part 1 para 9: 100% up to 480.00 a week, 75% to 530.00, 50% +# to 580.00, 25% to 660.00). Appendix 3's income applies from April 2026 +# (gov.local_authorities.oxford.council_tax_reduction.universal_credit. +# in_effect). Before then a working-age applicant with a Universal Credit +# award stays on the paragraphs 14 to 27 income, which deducts half of pension +# contributions (para 18), counts the award, leaves out Child Benefit and +# child maintenance, and adds no tariff income from capital. These cases keep +# the results the model gave before Appendix 3 was encoded. +# +# The applicant, 35, earns 20,000 and pays 2,600 into an occupational +# pension, or earns 15,600 with no contributions. Income tax 20% above the +# 12,570 personal allowance and National Insurance 8% above the 12,570 +# primary threshold in both 2024-25 and 2025-26: on 20,000 less 2,600, tax +# 966 and National Insurance 594.40, so DWP net earned income is 15,839.60 +# and the paragraphs 14 to 27 earnings are 20,000 - 966 - 594.40 - 1,300 = +# 17,139.60; on 15,600, tax 606 and National Insurance 242.40, so both are +# 14,751.60. DWP's monthly rates: standard allowance, single 25 or over, +# 393.45 (2024-25) and 400.14 (2025-26); child born on or after 6 April 2017 +# 287.92 and 292.81; higher work allowance 673 and 684. Child Benefit a week: +# 25.60 and 16.95 (2024-25), 26.05 and 17.25 (2025-26). Council tax 1,800, +# no capital and no non-dependants. + +- name: Before April 2026 Oxford bands a single Universal Credit claimant on the paragraphs 14 to 27 income (2024) + # UC: 393.45 x 12 + 12,500 housing - 55% x 15,839.60 = 8,509.62. + # Paragraphs 14 to 27: 17,139.60 + 8,509.62 = 25,649.22 a year, 493.25 a + # week: band 2, 75% of 1,800. Appendix 3 would give 468.25, band 1. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 8_509.62 + council_tax_reduction_applicable_income: 25_649.22 + oxford_council_tax_reduction: 1_350 + +- name: Before April 2026 Oxford bands a single Universal Credit claimant on the paragraphs 14 to 27 income (2025) + # UC: 400.14 x 12 + 12,500 - 8,711.78 = 8,589.90. Paragraphs 14 to 27: + # 17,139.60 + 8,589.90 = 25,729.50 a year, 494.80 a week: band 2, 75%. + # Appendix 3 would give 469.80, band 1. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 8_589.90 + council_tax_reduction_applicable_income: 25_729.50 + oxford_council_tax_reduction: 1_350 + +- name: Before April 2026 Oxford leaves Child Benefit out of a Universal Credit parent's income (2024) + # Children aged 3 and 5, no housing costs. UC: (393.45 + 2 x 287.92) x 12 + # = 11,631.48 less 55% x (15,839.60 - 673 x 12) = 4,269.98: 7,361.50. + # Paragraphs 14 to 27 count the Child Benefit of 2,212.60, which Oxford + # takes off again: 17,139.60 + 7,361.50 = 24,501.10 a year, 471.18 a week: + # band 1, 100%. Appendix 3 would give 488.73, band 2. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 3 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 7_361.50 + child_benefit: 2_212.60 + council_tax_reduction_applicable_income: 26_713.70 + oxford_council_tax_reduction: 1_800 + +- name: Before April 2026 Oxford leaves Child Benefit out of a Universal Credit parent's income (2025) + # UC: (400.14 + 2 x 292.81) x 12 = 11,829.12 less 55% x (15,839.60 - 684 + # x 12) = 4,197.38: 7,631.74. Paragraphs 14 to 27: 17,139.60 + 7,631.74 = + # 24,771.34 a year, 476.37 a week: band 1, 100%. Appendix 3 would give + # 494.67, band 2. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + person2: + age: 3 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 7_631.74 + child_benefit: 2_251.60 + council_tax_reduction_applicable_income: 27_022.94 + oxford_council_tax_reduction: 1_800 + +- name: Before April 2026 Oxford leaves child maintenance out of a Universal Credit parent's income (2024) + # Earnings 15,600, child maintenance 2,600. UC: 11,631.48 less 55% x + # (14,751.60 - 8,076) = 3,671.58: 7,959.90. Paragraphs 14 to 27 exclude + # child maintenance: 14,751.60 + 7,959.90 = 22,711.50 a year, 436.76 a + # week: band 1, 100%. Appendix 3 would give 529.31, band 2. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + maintenance_income: 2_600 + person2: + age: 3 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 7_959.90 + oxford_council_tax_reduction: 1_800 + +- name: Before April 2026 Oxford leaves child maintenance out of a Universal Credit parent's income (2025) + # UC: 11,829.12 less 55% x (14,751.60 - 8,208) = 3,598.98: 8,230.14. + # Paragraphs 14 to 27: 14,751.60 + 8,230.14 = 22,981.74 a year, 441.96 a + # week: band 1, 100%. Appendix 3 would give 535.26, band 3. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + maintenance_income: 2_600 + person2: + age: 3 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 8_230.14 + oxford_council_tax_reduction: 1_800 + +- name: A reform that brings Appendix 3 forward reaches a 2025 Universal Credit claimant + # Model reform, not current law. Appendix 3 in 2025: 15,839.60 + 8,589.90 + # = 24,429.50 a year, 469.80 a week: band 1, 100%. + period: 2025 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.universal_credit.in_effect: true + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + oxford_council_tax_reduction_universal_credit_income: 24_429.50 + oxford_council_tax_reduction: 1_800 + +- name: A reform that brings Appendix 3 forward counts a 2025 parent's child maintenance + # Model reform, not current law. Appendix 3 in 2025: 14,751.60 + 8,230.14 + # + 2,251.60 Child Benefit + 2,600 child maintenance = 27,833.34 a year, + # 535.26 a week: band 3, 50%. + period: 2025 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.universal_credit.in_effect: true + people: + person1: + age: 35 + employment_income: 15_600 + maintenance_income: 2_600 + person2: + age: 3 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + oxford_council_tax_reduction_universal_credit_income: 27_833.34 + oxford_council_tax_reduction: 900 + +- name: A reform that switches Appendix 3 off returns a 2026 Universal Credit claimant to the paragraphs 14 to 27 income + # Model reform, not current law. UC 2026: 424.90 x 12 + 12,500 - 8,711.78 + # = 8,887.02. Paragraphs 14 to 27: 20,000 - 966 - 594.40 - 1,300 + + # 8,887.02 = 26,026.62 a year, 500.51 a week: band 2, 75%. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.local_authorities.oxford.council_tax_reduction.universal_credit.in_effect: false + people: + person1: + age: 35 + employment_income: 20_000 + employee_pension_contributions: 2_600 + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 8_887.02 + council_tax_reduction_applicable_income: 26_026.62 + oxford_council_tax_reduction: 1_350 diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py index 0ed80f5662..15333f87fe 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py @@ -39,9 +39,11 @@ class council_tax_reduction_pension_contribution_deduction_rate(Variable): "scheme's Appendix 3). The model has no such route for Wales and " "Scotland yet (#1962), so the share here reaches those applicants " "too. Merton, Kingston upon Thames, Newham and Westminster assess " - "them on Universal Credit income (_legacy.py), and Oxford on its " - "Appendix 3 income (oxford_council_tax_reduction_universal_credit_income), " - "so the share here does not reach their awards." + "them on Universal Credit income (_legacy.py), so the share here does " + "not reach their awards. Oxford assesses them on its Appendix 3 income " + "(oxford_council_tax_reduction_universal_credit_income) from April " + "2026, the scheme the model holds; in earlier years the share reaches " + "them too." ) definition_period = YEAR unit = "/1" diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py index 07057ca3cc..756d9b41c6 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py @@ -43,16 +43,19 @@ def formula(benunit, period, parameters): "council_tax_reduction_head_applies_alone", period ) weekly_income = max_(0, annual_income - child_benefit) / WEEKS_IN_YEAR + weekly_income += where(has_uc_award, 0, weekly_tariff_income) # Paragraphs 14 to 27 assess an applicant without Universal Credit; # Appendix 3 assesses one with it on DWP's net earned income, the - # award and other income instead (scheme para. 9). + # award and other income instead (scheme para. 9). The model holds + # the 2026-27 scheme, and universal_credit.in_effect turns Appendix 3 + # on from April 2026. Before then an applicant with an award keeps + # the income above, without Oxford's tariff income. + appendix_3 = has_uc_award & ctr.universal_credit.in_effect uc_weekly_income = ( benunit("oxford_council_tax_reduction_universal_credit_income", period) / WEEKS_IN_YEAR ) - weekly_income = where( - has_uc_award, uc_weekly_income, weekly_income + weekly_tariff_income - ) + weekly_income = where(appendix_3, uc_weekly_income, weekly_income) relevant_income_based_benefit = benunit( "council_tax_reduction_relevant_income_based_benefit", period, diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index 85f3170c1c..cad90ee69b 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -17,29 +17,30 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): "contributions in full (UC Regs 2013 reg. 55(5)(a) and reg. 57(2) " "step 4), before the work allowance, which belongs to the award " "(reg. 22). So Oxford's own share of pension contributions (Part 1 " - "para. 18) does not reach these applicants. Other income is the " - "Universal Credit calculation of unearned income (uc_unearned_income, " - "reg. 66), which already leaves out the benefits Appendix 3 lists as " - "ignored and counts Universal Credit's assumed yield from capital " - "(uc_tariff_income, reg. 72) in place of Oxford's own tariff income. " - "Appendix 3 counts all other unearned income, which can go beyond " - "reg. 66 (Child Benefit, for one, is not on its list); the model " - "counts the reg. 66 amount only. The award is the amount awarded, " - "before deductions for debts (uc_deductions) come out of the " + "para. 18) does not reach these applicants. Other income is " + "oxford_council_tax_reduction_universal_credit_other_income: Child " + "Benefit, child maintenance and the other unearned receipts Appendix 3 " + "does not ignore, without Universal Credit's assumed yield from " + "capital or income derived from capital. The award is the amount " + "awarded, before deductions for debts (uc_deductions) come out of the " "payment. Appendix 3 also ignores " "£48.15 a week of each carer element and £84.46 a week of each " "disabled child element, which come off the award (but not below " "nil), and, for a disabled applicant, an amount equal to any " "disability premium payable before the move to Universal Credit. " "The model holds no record of that earlier award, so it does not " - "ignore that amount. Appendix 3 ignores any " - "transitional element in the award. The model computes no " + "ignore that amount, which can overstate the income. Appendix 3 " + "ignores any transitional element in the award. The model computes no " "transitional element (uc_maximum_amount), so the award has none to " "take out. Paragraphs 14 to 27, Annexes 5 and 6 and Appendix 5 apply " "only to applicants without Universal Credit, so Oxford's tariff " "income from capital (para. 15) and its Child Benefit disregard do " - "not apply here. The four other councils the model runs assess " - "Universal Credit applicants on the same three amounts (_legacy.py)." + "not apply here. The DWP figures and the award are the benefit unit's " + "claimant's and partner's, so none of them counts where the household " + "head applies alone. oxford_council_tax_reduction bands on this " + "income from April 2026 " + "(gov.local_authorities.oxford.council_tax_reduction.universal_credit." + "in_effect)." ) definition_period = YEAR unit = GBP @@ -48,7 +49,6 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4", "https://www.legislation.gov.uk/uksi/2013/376/regulation/55", "https://www.legislation.gov.uk/uksi/2013/376/regulation/57", - "https://www.legislation.gov.uk/uksi/2013/376/regulation/66", ) def formula(benunit, period, parameters): @@ -56,7 +56,9 @@ def formula(benunit, period, parameters): period ).gov.local_authorities.oxford.council_tax_reduction.universal_credit earned_income = benunit("uc_earned_income_before_work_allowance", period) - unearned_income = benunit("uc_unearned_income", period) + other_income = benunit( + "oxford_council_tax_reduction_universal_credit_other_income", period + ) # Appendix 3 ignores £48.15 of the carer element and £84.46 of the # disabled child element. An award has a carer element for each # claimant who cares (reg. 29) and a disabled child addition for each @@ -91,4 +93,7 @@ def formula(benunit, period, parameters): # the award itself. award = benunit("universal_credit", period) + benunit("uc_deductions", period) award = max_(0, award - carer_disregard - disabled_child_disregard) - return max_(0, earned_income + unearned_income + award) + # The DWP figures and the award are the claimant's and partner's, not + # a household head's who applies alone. + applicant_family = ~benunit("council_tax_reduction_head_applies_alone", period) + return max_(0, applicant_family * (earned_income + award) + other_income) diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py new file mode 100644 index 0000000000..46789583ef --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py @@ -0,0 +1,58 @@ +from policyengine_uk.model_api import * + + +class oxford_council_tax_reduction_universal_credit_other_income(Variable): + value_type = float + entity = BenUnit + label = "Oxford Council Tax Reduction other income of a Universal Credit applicant" + documentation = ( + "The yearly other (unearned) income that Oxford's working-age scheme " + "adds to DWP's net earned income and the Universal Credit awarded for " + "an applicant who receives Universal Credit (Appendix 3). Appendix 3 " + "lists the income it ignores and takes all other unearned income into " + "account; Appendix 5's disregards, Child Benefit and child maintenance " + "among them, apply only to applicants without Universal Credit. " + "gov.local_authorities.oxford.council_tax_reduction.universal_credit." + "other_income_sources lists the sources and the reason for each. It " + "counts Child Benefit, child maintenance and the receipts in Universal " + "Credit's unearned income, and leaves out Universal Credit's assumed " + "yield from capital (uc_tariff_income) and income derived from capital, " + "which Part 1 para 30 and Annex 7 treat as capital. The income is the " + "applicant's and partner's, as in " + "council_tax_reduction_applicable_income: a household head who applies " + "alone counts only their own, and the benefit unit's awards belong to its " + "claimant and partner. The model does not count student loans or " + "grants, which Annex 8 counts with its own disregards." + ) + definition_period = YEAR + unit = GBP + reference = ( + "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27", + "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/3", + "https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/66", + ) + + def formula(benunit, period, parameters): + p = parameters( + period + ).gov.local_authorities.oxford.council_tax_reduction.universal_credit + # Appendix 3 bases the income on "the income of the applicant and, + # where appropriate, their partner". A person's receipts count for + # the applicant and partner only. The benefit unit's awards (Child + # Benefit and the rest) are its claimant's and partner's, so they do + # not count where the household head applies alone. + person = benunit.members + applicant_or_partner = person( + "is_council_tax_reduction_applicant_or_partner", period + ) + applicant_family = ~benunit("council_tax_reduction_head_applies_alone", period) + income = 0 + for source in p.other_income_sources: + if person.entity.get_variable(source).entity.is_person: + income = income + benunit.sum( + person(source, period) * applicant_or_partner + ) + else: + income = income + benunit(source, period) * applicant_family + return income From 021bc2847f9bc92f6290ca924675ac84cd13e055 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 13:05:46 -0400 Subject: [PATCH 08/21] Test Oxford's Appendix 3 other income, pound for pound and before 2026 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The income property now works other income from the family's own receipts and HMRC's Child Benefit rates, so counting Universal Credit's unearned income in its place fails it. A new property adds £520 of each kind of receipt and £1,000 of capital: receipts Universal Credit deducts leave the income unchanged, Child Benefit and child maintenance raise it by £520, interest leaves it unchanged, and capital lowers it by the rise in the assumed yield. Another checks that 2025 keeps the paragraphs 14 to 27 income. The generated families now carry child maintenance, interest and capital, and the changelog describes the other income and the start date. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2520.fixed.md | 2 +- ...x_reduction_universal_credit_properties.py | 342 ++++++++++++++---- 2 files changed, 280 insertions(+), 64 deletions(-) diff --git a/changelog.d/2520.fixed.md b/changelog.d/2520.fixed.md index 456e8c9f2b..2422f2300a 100644 --- a/changelog.d/2520.fixed.md +++ b/changelog.d/2520.fixed.md @@ -1 +1 @@ -Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income: DWP's net earned income, which deducts pension contributions in full, plus the award before deductions for debts and other income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. The model had assessed them on the income for applicants without Universal Credit, which deducts half of pension contributions. Some of these applicants' reductions rise and some fall, and a reform of Oxford's pension contribution share no longer moves them. +Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income from April 2026: DWP's net earned income, which deducts pension contributions in full, plus the award before deductions for debts and other unearned income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. Other income counts Child Benefit, child maintenance and the other receipts Appendix 3 does not ignore, but not interest, dividends, rent or Universal Credit's assumed yield from capital. The model had assessed these applicants on the income for applicants without Universal Credit, which deducts half of pension contributions and leaves out Child Benefit and child maintenance. Some of their reductions rise and some fall, a reform of Oxford's pension contribution share no longer moves them, and model years before 2026 keep their earlier results. diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 363b424a4a..17fad6abda 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -3,43 +3,64 @@ Oxford's Council Tax Reduction Scheme 2026 to 2027 bands a working-age applicant who receives Universal Credit on Appendix 3's income: net earned -income as DWP provides it, plus the Universal Credit awarded, with other -income counted weekly, ignoring £48.15 a week of each carer element and -£84.46 a week of each disabled child element. Paragraphs 14 to 27, whose para -18 deducts half of pension contributions, apply only to applicants without -Universal Credit. DWP's net earned income deducts relievable pension -contributions in full (UC Regs 2013 reg 55(5)(a)). +income as DWP provides it, plus the Universal Credit awarded, plus other +unearned income counted weekly, ignoring £48.15 a week of each carer element +and £84.46 a week of each disabled child element. Appendix 3 takes "All +other unearned income" into account, and Appendix 5's Child Benefit and +child maintenance disregards apply only to applicants without Universal +Credit; Part 1 para 30 and Annex 7 treat income derived from capital as +capital. Paragraphs 14 to 27, whose para 18 deducts half of pension +contributions, apply only to applicants without Universal Credit. DWP's net +earned income deducts relievable pension contributions in full (UC Regs 2013 +reg 55(5)(a)). The model holds the 2026-27 scheme and applies Appendix 3 +from April 2026. Invariants, for any generated population of working-age Oxford families (one -or two adults, earnings, occupational pension contributions, maintenance, -housing costs, children with or without a disabled child addition, carers), -over the families whose applicant has a Universal Credit award: +or two adults, earnings, occupational pension contributions, spousal and +child maintenance, savings interest, capital, housing costs, children with +or without a disabled child addition, carers), over the families whose +applicant has a Universal Credit award: 1. Locality: a reform of Oxford's pension contribution share leaves every such family's Oxford reduction unchanged, while every Oxford family reads the reform's share. -2. Appendix 3 income: it is DWP's net earned income plus unearned income - plus the award before deductions for debts, less the element disregards, - never below the first two. The disregards come off the award but not - below nil: the award or, if less, 48.15 a week of each carer element up - to the element plus 84.46 a week of each disabled child's addition up to - the addition, with the additions at DWP's 2026-27 rates. +2. Appendix 3 income: other income is Child Benefit at HMRC's 2026-27 rates + plus child and spousal maintenance, with no interest and no assumed yield + from capital. The income is DWP's net earned income plus that other + income plus the award before deductions for debts less the element + disregards, never below the first two. The disregards come off the award + but not below nil: the award or, if less, 48.15 a week of each carer + element up to the element plus 84.46 a week of each disabled child's + addition up to the addition, with the additions at DWP's 2026-27 rates. 3. Bands: the reduction is the council tax share times the rate that the scheme's band table (weekly income up to 480.00, 530.00, 580.00 and 660.00) gives for a fifty-second of the Appendix 3 income. -4. Monotonicity: more pension contributions never lower the reduction of a +4. Pound for pound: while the award stays above nil after the disregards + and no benefit cap applies, £520 more of spousal maintenance or private + pension, which Universal Credit deducts in full, leaves the income + unchanged, because the award falls by £520. £520 more of child + maintenance or Child Benefit, which Universal Credit does not deduct, + raises it by £520. £520 more interest leaves it unchanged. £1,000 more + capital lowers it by exactly the rise in Universal Credit's assumed + yield, which comes off the award and is not added back. +5. Monotonicity: more pension contributions never lower the reduction of a family that keeps its award, since each pound contributed lowers DWP's net earned income and raises the award by at most the 55% taper. -5. Differential: for families without a carer or disabled child element, - the Appendix 3 income less any deductions for debts equals the Universal - Credit income on which Merton, Kingston upon Thames, Newham and +6. Differential: for families without a carer or disabled child element, + the Appendix 3 income less any deductions for debts, with Universal + Credit's unearned income in place of Oxford's other income, equals the + Universal Credit income on which Merton, Kingston upon Thames, Newham and Westminster assess the same family, recovered from each council's award (they count the award after those deductions). - -The differential against origin/main for families without Universal Credit -lives outside the suite (see the pull request); here the YAML cases in -policy/baseline/gov/local_authorities/oxford/council_tax_reduction/ pin the -paragraphs 14 to 27 route. +7. Earlier years: in 2025, before the scheme the model holds, the reduction + of such a family is the band of its paragraphs 14 to 27 income less Child + Benefit, with no tariff income, as before Appendix 3 was encoded. + +The YAML cases in policy/baseline/gov/local_authorities/oxford/ +council_tax_reduction/ pin the paragraphs 14 to 27 route and the hand-worked +Appendix 3 amounts. A differential against an earlier version of the model, +for families without Universal Credit, needs both versions installed, so it +runs outside the suite. """ import numpy as np @@ -50,6 +71,7 @@ from policyengine_uk.system import system YEAR = 2026 +EARLIER_YEAR = 2025 PROPERTY_SETTINGS = settings( max_examples=5, deadline=None, @@ -71,8 +93,14 @@ DISABLED_CHILD_DISREGARD = 84.46 # DWP's 2026-27 disabled child additions, 164.79 and 514.71 a month, weekly. DISABLED_CHILD_ADDITION = {"lower": 164.79 * 12 / 52, "higher": 514.71 * 12 / 52} +# HMRC's 2026-27 Child Benefit, a week: the eldest child and each other child. +# Earnings here stay below the High Income Child Benefit Charge threshold. +CHILD_BENEFIT = (27.05, 17.90) # Float32 storage: amounts of tens of thousands carry errors of about 0.004. TOLERANCE = 0.05 +# The receipts and capital the pound-for-pound invariant adds. +STEP = 520.0 +CAPITAL_STEP = 1_000.0 money = st.floats(0, 40_000, allow_nan=False, allow_infinity=False) @@ -91,6 +119,8 @@ def families(draw): spousal_maintenance_income=draw( st.sampled_from([0.0, 0.0, 1_000.0, 5_200.0]) ), + maintenance_income=draw(st.sampled_from([0.0, 0.0, 1_300.0, 2_600.0])), + savings_interest_income=draw(st.sampled_from([0.0, 0.0, 520.0])), uc_carer_element_included_for_claimant=draw( st.sampled_from([False, False, True]) ), @@ -113,10 +143,13 @@ def families(draw): housing=draw(st.sampled_from([0.0, 3_000.0, 7_000.0, 12_500.0, 15_000.0])), # Deductions for debts, as a share of the standard allowance. deduction_rate=draw(st.sampled_from([0.0, 0.0, 0.15])), + # Capital within Oxford's and Universal Credit's 16,000 limits, some + # above Universal Credit's 6,000 tariff threshold. + savings=draw(st.sampled_from([0.0, 0.0, 6_000.0, 9_000.0, 12_000.0])), ) -def situation(units, local_authorities=None, council_tax=COUNCIL_TAX): +def situation(units, local_authorities=None, council_tax=COUNCIL_TAX, year=YEAR): """One household for each family, in Oxford unless local_authorities names each household's council.""" local_authorities = local_authorities or ["OXFORD"] * len(units) @@ -125,37 +158,39 @@ def situation(units, local_authorities=None, council_tax=COUNCIL_TAX): names = [] for j, adult in enumerate(unit["adults"]): name = f"a{i}_{j}" - people[name] = {key: {YEAR: value} for key, value in adult.items()} + people[name] = {key: {year: value} for key, value in adult.items()} names.append(name) for j, child in enumerate(unit["children"]): name = f"c{i}_{j}" - people[name] = {"age": {YEAR: child["age"]}} + people[name] = {"age": {year: child["age"]}} if child["disability"] is not None: - people[name]["is_disabled_for_benefits"] = {YEAR: True} + people[name]["is_disabled_for_benefits"] = {year: True} people[name]["meets_uc_higher_disabled_child_condition"] = { - YEAR: child["disability"] == "higher" + year: child["disability"] == "higher" } names.append(name) benunits[f"b{i}"] = { "members": names, - "would_claim_uc": {YEAR: True}, - "claims_all_entitled_benefits": {YEAR: True}, - "uc_housing_costs_element": {YEAR: unit["housing"]}, - "uc_deduction_rate": {YEAR: unit.get("deduction_rate", 0.0)}, + "would_claim_uc": {year: True}, + "claims_all_entitled_benefits": {year: True}, + "uc_housing_costs_element": {year: unit["housing"]}, + "uc_deduction_rate": {year: unit.get("deduction_rate", 0.0)}, } + if "child_benefit" in unit: + benunits[f"b{i}"]["child_benefit"] = {year: unit["child_benefit"]} households[f"h{i}"] = { "members": names, - "country": {YEAR: "ENGLAND"}, - "local_authority": {YEAR: local_authority}, - "council_tax": {YEAR: council_tax}, - "savings": {YEAR: 0.0}, + "country": {year: "ENGLAND"}, + "local_authority": {year: local_authority}, + "council_tax": {year: council_tax}, + "savings": {year: unit.get("savings", 0.0)}, } return {"people": people, "benunits": benunits, "households": households} -def calculate(units, variables, reform=None, **kwargs): - sim = Simulation(situation=situation(units, **kwargs), reform=reform) - return {v: np.asarray(sim.calculate(v, YEAR), dtype=float) for v in variables} +def calculate(units, variables, reform=None, year=YEAR, **kwargs): + sim = Simulation(situation=situation(units, year=year, **kwargs), reform=reform) + return {v: np.asarray(sim.calculate(v, year), dtype=float) for v in variables} def band_rate(weekly_income): @@ -172,10 +207,30 @@ def has_award(values, i): ) +def child_benefit(unit): + """A year of Child Benefit for the family's children, at HMRC's rates.""" + weekly = [ + CHILD_BENEFIT[0] if k == 0 else CHILD_BENEFIT[1] + for k in range(len(unit["children"])) + ] + return sum(weekly) * WEEKS + + +def other_income(unit): + """Appendix 3's other income from the family's own receipts: Child + Benefit and child and spousal maintenance, but no interest (Annex 7).""" + return child_benefit(unit) + sum( + adult["maintenance_income"] + adult["spousal_maintenance_income"] + for adult in unit["adults"] + ) + + def example_families(): """The YAML cases' families: each has an award, and between them they cover a carer, a lower-rate and a higher-rate disabled child, two - disabled children and an award the carer disregard takes to nil.""" + disabled children, an award the carer disregard takes to nil, Child + Benefit, child maintenance, interest and capital above the tariff + threshold.""" def adult(**extra): values = dict( @@ -183,11 +238,19 @@ def adult(**extra): employment_income=20_000.0, employee_pension_contributions=2_600.0, spousal_maintenance_income=0.0, + maintenance_income=0.0, + savings_interest_income=0.0, uc_carer_element_included_for_claimant=False, ) return {**values, **extra} carer = dict(uc_carer_element_included_for_claimant=True) + lone_parent = adult( + employment_income=15_600.0, + employee_pension_contributions=0.0, + maintenance_income=2_600.0, + savings_interest_income=520.0, + ) return [ dict(adults=[adult()], children=[], housing=12_500.0), dict(adults=[adult(**carer)], children=[], housing=12_500.0), @@ -205,6 +268,12 @@ def adult(**extra): ], housing=3_000.0, ), + dict( + adults=[lone_parent], + children=[dict(age=5, disability=None), dict(age=8, disability=None)], + housing=0.0, + savings=11_250.0, + ), ] @@ -253,6 +322,9 @@ def test_oxford_pension_contribution_share_does_not_move_universal_credit_awards "uc_unearned_income", "uc_carer_element", "uc_disability_elements", + "uc_benefit_cap_reduction_after_floor", + "uc_tariff_income", + "oxford_council_tax_reduction_universal_credit_other_income", "oxford_council_tax_reduction_universal_credit_income", "council_tax_reduction_maximum_eligible_liability", "council_tax_reduction_joint_liability_share", @@ -261,6 +333,20 @@ def test_oxford_pension_contribution_share_does_not_move_universal_credit_awards ] +def disregards(values, i, unit): + """Appendix 3's element disregards for family i, from DWP's 2026-27 + additions: 48.15 a week of each carer element and 84.46 a week of each + disabled child's addition, each up to its element.""" + carers = sum(a["uc_carer_element_included_for_claimant"] for a in unit["adults"]) + carer_part = min(values["uc_carer_element"][i], carers * CARER_DISREGARD * WEEKS) + child_part = sum( + min(DISABLED_CHILD_ADDITION[c["disability"]], DISABLED_CHILD_DISREGARD) * WEEKS + for c in unit["children"] + if c["disability"] is not None + ) + return carers, carer_part + child_part + + @PROPERTY_SETTINGS @given(st.lists(families(), min_size=1, max_size=8)) @example(units=example_families()) @@ -271,18 +357,26 @@ def test_appendix_3_income_and_band(units): continue case = unit earned = values["uc_earned_income_before_work_allowance"][i] - unearned = values["uc_unearned_income"][i] + # Invariant 2: other income is the family's own receipts, worked from + # its inputs and HMRC's Child Benefit rates, not Universal Credit's + # unearned income. + other = other_income(unit) + assert ( + abs( + values["oxford_council_tax_reduction_universal_credit_other_income"][i] + - other + ) + < TOLERANCE + ), case # The award before deductions for debts come out of the payment. award = values["universal_credit"][i] + values["uc_deductions"][i] income = values["oxford_council_tax_reduction_universal_credit_income"][i] - carers = sum( - a["uc_carer_element_included_for_claimant"] for a in unit["adults"] - ) disabled_children = sum(c["disability"] is not None for c in unit["children"]) - # Invariant 2: the disregards come off the award only, not below nil, - # and never exceed the elements or the weekly amounts. - ignored = earned + unearned + award - income - assert income >= max(0.0, earned + unearned) - TOLERANCE, case + carers, expected_ignored = disregards(values, i, unit) + # The disregards come off the award only, not below nil, and never + # exceed the elements or the weekly amounts. + ignored = earned + other + award - income + assert income >= max(0.0, earned + other) - TOLERANCE, case assert ignored >= -TOLERANCE, case assert ignored <= award + TOLERANCE, case elements = values["uc_carer_element"][i] + values["uc_disability_elements"][i] @@ -292,16 +386,7 @@ def test_appendix_3_income_and_band(units): ) assert ignored <= weekly_cap * WEEKS + TOLERANCE, case # The disregards exactly, from DWP's 2026-27 amounts. - carer_part = min( - values["uc_carer_element"][i], carers * CARER_DISREGARD * WEEKS - ) - child_part = sum( - min(DISABLED_CHILD_ADDITION[c["disability"]], DISABLED_CHILD_DISREGARD) - * WEEKS - for c in unit["children"] - if c["disability"] is not None - ) - assert abs(ignored - min(award, carer_part + child_part)) < TOLERANCE, case + assert abs(ignored - min(award, expected_ignored)) < TOLERANCE, case # Invariant 3: the scheme's band table sets the reduction. liability = ( values["council_tax_reduction_maximum_eligible_liability"][i] @@ -317,6 +402,85 @@ def test_appendix_3_income_and_band(units): ) +def raise_first_adult(unit, **changes): + first = dict(unit["adults"][0]) + for key, change in changes.items(): + first[key] = first.get(key, 0.0) + change + return dict(unit, adults=[first] + unit["adults"][1:]) + + +# Each variant of a family: what changes, and how much Appendix 3 income it +# should add while the award stays above nil after the disregards. +VARIANTS = [ + # Universal Credit deducts spousal maintenance and private pensions in + # full (reg 66(1)(a) and (d)), so the award falls by as much as Appendix + # 3 adds. + ( + "spousal maintenance", + lambda u: raise_first_adult(u, spousal_maintenance_income=STEP), + 0.0, + ), + ( + "private pension", + lambda u: raise_first_adult(u, private_pension_income=STEP), + 0.0, + ), + # Universal Credit does not count child maintenance or Child Benefit. + ( + "child maintenance", + lambda u: raise_first_adult(u, maintenance_income=STEP), + STEP, + ), + ("Child Benefit", lambda u: dict(u, child_benefit=child_benefit(u) + STEP), STEP), + # Income derived from capital is capital (Annex 7), and Universal Credit + # does not count it. + ("interest", lambda u: raise_first_adult(u, savings_interest_income=STEP), 0.0), +] +POUND_VARIABLES = INCOME_VARIABLES + + +@PROPERTY_SETTINGS +@given(st.lists(families(), min_size=1, max_size=4)) +@example(units=example_families()) +def test_pound_for_pound(units): + variants = [make(unit) for _, make, _ in VARIANTS for unit in units] + capital = [ + dict(unit, savings=unit.get("savings", 0.0) + CAPITAL_STEP) for unit in units + ] + values = calculate(units + variants + capital, POUND_VARIABLES) + n = len(units) + income = values["oxford_council_tax_reduction_universal_credit_income"] + + def award_above_nil(j, unit): + # The award after the disregards stays above nil, and no benefit cap + # applies, so a pound off the award is a pound off the income. + award = values["universal_credit"][j] + values["uc_deductions"][j] + return ( + has_award(values, j) + and award - disregards(values, j, unit)[1] > TOLERANCE + and values["uc_benefit_cap_reduction_after_floor"][j] == 0 + ) + + for i, unit in enumerate(units): + if not award_above_nil(i, unit): + continue + for k, (label, make, added) in enumerate(VARIANTS): + j = (k + 1) * n + i + if not award_above_nil(j, make(unit)): + continue + # Invariant 4. + assert abs(income[j] - income[i] - added) < TOLERANCE, (label, unit) + j = (len(VARIANTS) + 1) * n + i + if award_above_nil(j, unit): + # More capital raises Universal Credit's assumed yield, which + # comes off the award and is not counted as income. + yield_rise = values["uc_tariff_income"][j] - values["uc_tariff_income"][i] + assert abs(income[i] - income[j] - yield_rise) < TOLERANCE, ( + "capital", + unit, + ) + + MONOTONE_VARIABLES = [ "universal_credit", "council_tax_reduction_head_applies_alone", @@ -348,7 +512,7 @@ def test_more_pension_contributions_never_lower_the_reduction(units, extra): more = calculate(raised, MONOTONE_VARIABLES) for i, unit in enumerate(units): if has_award(base, i) and has_award(more, i): - # Invariant 4. + # Invariant 5. assert ( more["oxford_council_tax_reduction"][i] >= base["oxford_council_tax_reduction"][i] - TOLERANCE @@ -375,10 +539,12 @@ def test_more_pension_contributions_never_lower_the_reduction(units, extra): def test_appendix_3_income_matches_the_other_councils_universal_credit_income( units, ): - # No carer or disabled child: those disregards are Oxford's alone. + # No carer or disabled child: those disregards are Oxford's alone. No + # capital: each council has its own capital rules. units = [ dict( unit, + savings=0.0, adults=[ dict(adult, uc_carer_element_included_for_claimant=False) for adult in unit["adults"] @@ -419,10 +585,15 @@ def test_appendix_3_income_matches_the_other_councils_universal_credit_income( ) award = values[variable][j] # Those councils count the award after deductions for debts, - # which Oxford adds back. + # which Oxford adds back, and Universal Credit's unearned income, + # where Oxford counts its own other income. council_income = ( values["oxford_council_tax_reduction_universal_credit_income"][i] - values["uc_deductions"][i] + - values["oxford_council_tax_reduction_universal_credit_other_income"][ + i + ] + + values["uc_unearned_income"][i] ) assert award > 0, case if award > liability * ctr.maximum_support_rate - TOLERANCE: @@ -437,6 +608,51 @@ def test_appendix_3_income_matches_the_other_councils_universal_credit_income( income = values["uc_maximum_amount"][j] + ( liability * ctr.maximum_support_rate - award ) / (ctr.means_test.withdrawal_rate) - # Invariant 5. The withdrawal rate scales float32 errors in the + # Invariant 6. The withdrawal rate scales float32 errors in the # award by up to five. assert abs(income - council_income) < 5 * TOLERANCE, case + + +EARLIER_VARIABLES = [ + "universal_credit", + "council_tax_reduction_head_applies_alone", + "council_tax_reduction_applicable_income", + "child_benefit", + "council_tax_reduction_maximum_eligible_liability", + "council_tax_reduction_joint_liability_share", + "oxford_council_tax_reduction_non_dep_deductions", + "oxford_council_tax_reduction", +] + + +@PROPERTY_SETTINGS +@given(st.lists(families(), min_size=1, max_size=8)) +@example(units=example_families()) +def test_earlier_years_keep_the_paragraphs_14_to_27_income(units): + values = calculate(units, EARLIER_VARIABLES, year=EARLIER_YEAR) + for i, unit in enumerate(units): + if not has_award(values, i): + continue + # Invariant 7: before April 2026 an applicant with an award keeps the + # paragraphs 14 to 27 income, less Child Benefit, with no tariff + # income from capital. + weekly = ( + max( + 0.0, + values["council_tax_reduction_applicable_income"][i] + - values["child_benefit"][i], + ) + / WEEKS + ) + liability = ( + values["council_tax_reduction_maximum_eligible_liability"][i] + * values["council_tax_reduction_joint_liability_share"][i] + ) + expected = max( + 0.0, + liability * band_rate(weekly) + - values["oxford_council_tax_reduction_non_dep_deductions"][i], + ) + assert abs(values["oxford_council_tax_reduction"][i] - expected) < TOLERANCE, ( + unit + ) From 8ca2fc2fee18a75e79d1994b3e817d793af9e375 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 13:06:52 -0400 Subject: [PATCH 09/21] Check Oxford's income property on a two-carer couple and debt deductions The YAML cases' couple who both care and a claimant repaying debts join the Appendix 3 income property as an explicit example, so one carer disregard for the couple or the award after deductions fails it. Co-Authored-By: Claude Opus 5.5 --- ...x_reduction_universal_credit_properties.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 17fad6abda..a98a0a8c77 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -277,6 +277,43 @@ def adult(**extra): ] +def couple_and_debt_families(): + """The YAML cases' couple who both care, and a single claimant repaying + debts out of the award.""" + carer = dict( + employment_income=0.0, + employee_pension_contributions=0.0, + spousal_maintenance_income=0.0, + maintenance_income=0.0, + savings_interest_income=0.0, + uc_carer_element_included_for_claimant=True, + ) + return [ + dict( + adults=[ + dict(carer, age=35, employment_income=12_570.0), + dict(carer, age=36), + ], + children=[], + housing=10_500.0, + ), + dict( + adults=[ + dict( + carer, + age=35, + employment_income=20_000.0, + employee_pension_contributions=2_600.0, + uc_carer_element_included_for_claimant=False, + ) + ], + children=[], + housing=12_500.0, + deduction_rate=0.25, + ), + ] + + LOCALITY_VARIABLES = [ "universal_credit", "council_tax_reduction_head_applies_alone", @@ -350,6 +387,7 @@ def disregards(values, i, unit): @PROPERTY_SETTINGS @given(st.lists(families(), min_size=1, max_size=8)) @example(units=example_families()) +@example(units=couple_and_debt_families()) def test_appendix_3_income_and_band(units): values = calculate(units, INCOME_VARIABLES) for i, unit in enumerate(units): From 7238eba63a85c42d9f5a1e18340bccb91f1f3215 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 13:44:21 -0400 Subject: [PATCH 10/21] Supply every family's Child Benefit in Oxford's pound-for-pound property The Child Benefit variant supplies Child Benefit, and a variable supplied for some families reads as nil for the rest, so every family in the property now gets its Child Benefit at HMRC's rates as an input. The property also holds DWP's net earned income fixed between a family and its variant. Co-Authored-By: Claude Opus 5.5 --- ...x_reduction_universal_credit_properties.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index a98a0a8c77..2c10a320e2 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -35,14 +35,14 @@ 3. Bands: the reduction is the council tax share times the rate that the scheme's band table (weekly income up to 480.00, 530.00, 580.00 and 660.00) gives for a fifty-second of the Appendix 3 income. -4. Pound for pound: while the award stays above nil after the disregards - and no benefit cap applies, £520 more of spousal maintenance or private - pension, which Universal Credit deducts in full, leaves the income - unchanged, because the award falls by £520. £520 more of child - maintenance or Child Benefit, which Universal Credit does not deduct, - raises it by £520. £520 more interest leaves it unchanged. £1,000 more - capital lowers it by exactly the rise in Universal Credit's assumed - yield, which comes off the award and is not added back. +4. Pound for pound: while the award stays above nil after the disregards, + no benefit cap applies and DWP's net earned income stays the same, £520 + more of spousal maintenance or private pension, which Universal Credit + deducts in full, leaves the income unchanged, because the award falls by + £520. £520 more of child maintenance or Child Benefit, which Universal + Credit does not deduct, raises it by £520. £520 more interest leaves it + unchanged. £1,000 more capital lowers it by the rise in Universal + Credit's assumed yield, which comes off the award and is not added back. 5. Monotonicity: more pension contributions never lower the reduction of a family that keeps its award, since each pound contributed lowers DWP's net earned income and raises the award by at most the 55% taper. @@ -481,6 +481,10 @@ def raise_first_adult(unit, **changes): @given(st.lists(families(), min_size=1, max_size=4)) @example(units=example_families()) def test_pound_for_pound(units): + # The Child Benefit variant supplies Child Benefit, and a variable + # supplied for some families reads as nil for the rest, so every family + # here gets its Child Benefit at HMRC's rates as an input. + units = [dict(unit, child_benefit=child_benefit(unit)) for unit in units] variants = [make(unit) for _, make, _ in VARIANTS for unit in units] capital = [ dict(unit, savings=unit.get("savings", 0.0) + CAPITAL_STEP) for unit in units @@ -489,14 +493,18 @@ def test_pound_for_pound(units): n = len(units) income = values["oxford_council_tax_reduction_universal_credit_income"] + earned = values["uc_earned_income_before_work_allowance"] + def award_above_nil(j, unit): - # The award after the disregards stays above nil, and no benefit cap - # applies, so a pound off the award is a pound off the income. + # The award after the disregards stays above nil, no benefit cap + # applies and DWP's net earned income stays the same, so a pound off + # the award is a pound off the income. award = values["universal_credit"][j] + values["uc_deductions"][j] return ( has_award(values, j) and award - disregards(values, j, unit)[1] > TOLERANCE and values["uc_benefit_cap_reduction_after_floor"][j] == 0 + and abs(earned[j] - earned[j % n]) < TOLERANCE ) for i, unit in enumerate(units): From ae9bbabd1f3490a1c0c011e4e55835613b920bcb Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 14:12:13 -0400 Subject: [PATCH 11/21] Name who acts in Oxford's Appendix 3 parameter and test docs Co-Authored-By: Claude Opus 5.5 --- .../universal_credit/in_effect.yaml | 2 +- .../other_income_sources.yaml | 20 +++++++++---------- ...ouncil_tax_reduction_universal_credit.yaml | 4 ++-- ...uction_universal_credit_earlier_years.yaml | 2 +- ...x_reduction_universal_credit_properties.py | 10 ++++++---- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml index fe58d447f4..bdd01daa36 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml @@ -1,4 +1,4 @@ -description: Oxford's Council Tax Reduction scheme assesses a working-age applicant who receives Universal Credit on the income its Appendix 3 sets when this is true. Otherwise the model assesses them on the income for applicants without Universal Credit, less Child Benefit and without Oxford's assumed income from capital. +description: When this is true, the model assesses a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the income Oxford's Appendix 3 sets. Otherwise it assesses them on the income for applicants without Universal Credit, less Child Benefit and without Oxford's assumed income from capital. values: # The model holds Oxford's 2026-27 scheme only: its other Oxford parameters # start on 1 April 2026 and backdating carries them to earlier years. The diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml index ec2f93dcf3..485349ab0b 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml @@ -10,13 +10,13 @@ description: > Housing Payments, Housing Benefit and discretionary council tax reductions) and income derived from capital: savings interest, dividends and rent, which Part 1 para 30 and Annex 7 treat as capital. Appendix 3 also ignores - payments from sub-tenants and boarders. Universal Credit's assumed yield - from capital (uc_tariff_income) is not a receipt and stays out, and so does - Oxford's own assumed income from capital, which para 15 adds only under - paragraphs 14 to 27. Statutory sick, maternity and paternity pay are in - DWP's net earned income (UC Regs 2013 reg 55(4)), so they are not listed - here. Payments that only Scotland makes, other than the carer support - payment, cannot reach an Oxford applicant and are not listed. + payments from sub-tenants and boarders. DWP deems Universal Credit's + assumed yield from capital (uc_tariff_income) and nobody receives it, so it + stays out, and so does Oxford's own assumed income from capital, which para + 15 adds only under paragraphs 14 to 27. DWP's net earned income already + holds statutory sick, maternity and paternity pay (UC Regs 2013 reg 55(4)), + and only Scotland pays the Scottish benefits other than the carer support + payment, so the list leaves both out. metadata: economy: false label: Oxford Council Tax Reduction Universal Credit applicant other income sources @@ -25,7 +25,7 @@ metadata: reference: - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (other unearned income, the income ignored) and Appendix 5 (applies to applicants who do not receive Universal Credit) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Annex 7 (income derived from capital is treated as capital) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Annex 7 (Oxford treats income derived from capital as capital) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/3 - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Part 1 paras 15 and 30 href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27 @@ -70,8 +70,8 @@ values: - private_transfer_income - miscellaneous_income # Universal Credit replaces these awards, so they are nil for a Universal - # Credit claimant. They are listed so that no receipt escapes "All other - # unearned income". + # Credit claimant. The list names them so that no receipt escapes "All + # other unearned income". - income_support - claimant_or_partner_jsa_income - claimant_or_partner_esa_income diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index f4464ee0a5..663407db58 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -685,7 +685,7 @@ oxford_council_tax_reduction_universal_credit_income: 26_054.98 oxford_council_tax_reduction: 1_350 -- name: Savings interest stay out of a Universal Credit applicant's Oxford income +- name: Savings interest stays out of a Universal Credit applicant's Oxford income # As the Child Benefit case, with 2,600 (50 a week) of savings_interest_income. # Part 1 para 30 and Annex 7 treat "any income derived from capital" as # capital, and Universal Credit does not count it either, so the award @@ -763,7 +763,7 @@ oxford_council_tax_reduction_universal_credit_income: 26_054.98 oxford_council_tax_reduction: 1_350 -- name: Rent stay out of a Universal Credit applicant's Oxford income +- name: Rent stays out of a Universal Credit applicant's Oxford income # As the Child Benefit case, with 2,600 (50 a week) of property_income. # Part 1 para 30 and Annex 7 treat "any income derived from capital" as # capital, and Universal Credit does not count it either, so the award diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml index 9611fa45ca..b6d1467f60 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_earlier_years.yaml @@ -9,7 +9,7 @@ # award stays on the paragraphs 14 to 27 income, which deducts half of pension # contributions (para 18), counts the award, leaves out Child Benefit and # child maintenance, and adds no tariff income from capital. These cases keep -# the results the model gave before Appendix 3 was encoded. +# the results the model gave before PolicyEngine encoded Appendix 3. # # The applicant, 35, earns 20,000 and pays 2,600 into an occupational # pension, or earns 15,600 with no contributions. Income tax 20% above the diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 2c10a320e2..94a883a878 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -42,7 +42,8 @@ £520. £520 more of child maintenance or Child Benefit, which Universal Credit does not deduct, raises it by £520. £520 more interest leaves it unchanged. £1,000 more capital lowers it by the rise in Universal - Credit's assumed yield, which comes off the award and is not added back. + Credit's assumed yield, which DWP takes off the award and Oxford does not + add back. 5. Monotonicity: more pension contributions never lower the reduction of a family that keeps its award, since each pound contributed lowers DWP's net earned income and raises the award by at most the 55% taper. @@ -54,7 +55,8 @@ (they count the award after those deductions). 7. Earlier years: in 2025, before the scheme the model holds, the reduction of such a family is the band of its paragraphs 14 to 27 income less Child - Benefit, with no tariff income, as before Appendix 3 was encoded. + Benefit, with no tariff income, as the model gave before PolicyEngine + encoded Appendix 3. The YAML cases in policy/baseline/gov/local_authorities/oxford/ council_tax_reduction/ pin the paragraphs 14 to 27 route and the hand-worked @@ -518,8 +520,8 @@ def award_above_nil(j, unit): assert abs(income[j] - income[i] - added) < TOLERANCE, (label, unit) j = (len(VARIANTS) + 1) * n + i if award_above_nil(j, unit): - # More capital raises Universal Credit's assumed yield, which - # comes off the award and is not counted as income. + # More capital raises Universal Credit's assumed yield, which DWP + # takes off the award and Oxford does not count as income. yield_rise = values["uc_tariff_income"][j] - values["uc_tariff_income"][i] assert abs(income[i] - income[j] - yield_rise) < TOLERANCE, ( "capital", From 223a3b4d2365485390ffe6bbe67d0ad1b44a4c80 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 20:12:53 -0400 Subject: [PATCH 12/21] Count Oxford's miscellaneous income once and the Scottish Carer Supplement The model already counts miscellaneous income in DWP's net earned income, so Oxford's Appendix 3 other income no longer adds it a second time. Social Security Scotland pays the Scottish Carer Supplement with the carer support payment, including for 13 weeks after a carer moves to England (SSI 2023/302 regs 14A and 42), and Oxford City Council does not ignore it, so the list now counts it. New hand-worked YAML cases cover miscellaneous income, a regular Armed Forces Compensation Scheme payment, the supplement the model pays and a supplied 13 weeks of it, a carer support payment above DWP's cap, and the Appendix 3 income of a household head who applies alone. The parameter descriptions and the new variables' documentation name Oxford City Council and DWP as the actors and state the one-off receipts limit. Co-Authored-By: Claude Opus 5.5 --- .../pension_contribution_deduction_rate.yaml | 2 +- .../carer_element_disregard.yaml | 4 +- .../disabled_child_element_disregard.yaml | 4 +- .../universal_credit/in_effect.yaml | 10 +- .../other_income_sources.yaml | 96 ++++--- ...ouncil_tax_reduction_universal_credit.yaml | 248 +++++++++++++++++- .../oxford_council_tax_reduction.py | 13 +- ...l_tax_reduction_universal_credit_income.py | 92 ++++--- ...reduction_universal_credit_other_income.py | 46 ++-- 9 files changed, 393 insertions(+), 122 deletions(-) diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml index 5127c1e6e0..4ea2e44fc8 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/means_test/pension_contribution_deduction_rate.yaml @@ -1,4 +1,4 @@ -description: The Oxford Council Tax Reduction scheme deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age applicant who does not receive Universal Credit. The scheme assesses an applicant who does on the net earned income DWP provides (Appendix 3), which deducts relievable pension contributions in full, so this share does not apply to them. The model applies Appendix 3 from April 2026 (universal_credit.in_effect), so in earlier years this share reaches them too. +description: Oxford City Council deducts this share of pension contributions when it calculates the net earnings and self-employed net profit of a working-age Council Tax Reduction applicant who does not receive Universal Credit. The council assesses an applicant who does on the net earned income DWP provides (Appendix 3), and DWP deducts relievable pension contributions in full, so this share does not reach them. The model applies Appendix 3 from April 2026 (universal_credit.in_effect), so in earlier years this share reaches them too. values: 2026-04-01: 0.5 metadata: diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml index 7827913d37..fd2bc218a6 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/carer_element_disregard.yaml @@ -1,4 +1,4 @@ -description: Weekly amount of each Universal Credit carer element that the Oxford Council Tax Reduction scheme ignores in the income of a working-age applicant who receives Universal Credit. +description: Weekly amount of each Universal Credit carer element that Oxford City Council ignores in the income of a working-age Council Tax Reduction applicant who receives Universal Credit. values: 2026-04-01: 48.15 metadata: @@ -6,5 +6,5 @@ metadata: period: week label: Oxford Council Tax Reduction Universal Credit carer element disregard reference: - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (ignores £48.15 of the Universal Credit carer element) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 ("£48.15 of the Universal Credit carer element") href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml index d7823c4d25..6614640e23 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/disabled_child_element_disregard.yaml @@ -1,4 +1,4 @@ -description: Weekly amount of each Universal Credit disabled child element that the Oxford Council Tax Reduction scheme ignores in the income of a working-age applicant who receives Universal Credit. +description: Weekly amount of each Universal Credit disabled child element that Oxford City Council ignores in the income of a working-age Council Tax Reduction applicant who receives Universal Credit. values: 2026-04-01: 84.46 metadata: @@ -6,5 +6,5 @@ metadata: period: week label: Oxford Council Tax Reduction Universal Credit disabled child element disregard reference: - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (ignores £84.46 of the Universal Credit disabled child element) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 ("£84.46 of the Universal Credit disabled child element") href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml index bdd01daa36..d6456678cf 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml @@ -1,12 +1,12 @@ -description: When this is true, the model assesses a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the income Oxford's Appendix 3 sets. Otherwise it assesses them on the income for applicants without Universal Credit, less Child Benefit and without Oxford's assumed income from capital. +description: When this is true, the model assesses a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the income Oxford City Council sets in Appendix 3. Otherwise it assesses them on the income the council sets for applicants without Universal Credit, less Child Benefit and without the council's assumed income from capital. values: # The model holds Oxford's 2026-27 scheme only: its other Oxford parameters # start on 1 April 2026 and backdating carries them to earlier years. The # model reads each year's parameters at 30 April, so this switches Appendix - # 3 on in model year 2026 and leaves earlier years as they were. Oxford's - # 2025-26 scheme also had an Appendix 3, with other bands and a Carer's - # Allowance disregard in place of the element disregards, which the model - # does not hold. + # 3 on in model year 2026 and leaves earlier years as they were. In its + # 2025-26 scheme Oxford City Council also set an Appendix 3 income, with + # other bands and a Carer's Allowance disregard in place of the element + # disregards; the model does not hold that scheme. 2015-01-01: false 2026-04-01: true metadata: diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml index 485349ab0b..42320dac75 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml @@ -1,55 +1,72 @@ description: > - Other (unearned) income that the Oxford Council Tax Reduction scheme counts, - with DWP's net earned income and the Universal Credit awarded, for a - working-age applicant who receives Universal Credit. Appendix 3 lists the - income it ignores and says "All other unearned income is taken into - account". Appendix 5's disregards, including Child Benefit and child - maintenance, apply only to applicants who "do not receive Universal Credit". - The list leaves out what Appendix 3 ignores (Attendance Allowance, + Other (unearned) income that Oxford City Council counts, with DWP's net + earned income and the Universal Credit awarded, for a working-age applicant + who receives Universal Credit. In Appendix 3 the council lists the income it + ignores and takes "All other unearned income" into account; it applies its + Appendix 5 disregards, Child Benefit and child maintenance among them, only + to applicants who "do not receive Universal Credit". The list leaves out the + income Oxford City Council ignores in Appendix 3 (Attendance Allowance, Disability Living Allowance, Personal Independence Payment, Discretionary - Housing Payments, Housing Benefit and discretionary council tax reductions) - and income derived from capital: savings interest, dividends and rent, - which Part 1 para 30 and Annex 7 treat as capital. Appendix 3 also ignores - payments from sub-tenants and boarders. DWP deems Universal Credit's - assumed yield from capital (uc_tariff_income) and nobody receives it, so it - stays out, and so does Oxford's own assumed income from capital, which para - 15 adds only under paragraphs 14 to 27. DWP's net earned income already - holds statutory sick, maternity and paternity pay (UC Regs 2013 reg 55(4)), - and only Scotland pays the Scottish benefits other than the carer support - payment, so the list leaves both out. + Housing Payments, Housing Benefit, discretionary council tax reductions and + payments from sub-tenants and boarders). It also leaves out savings + interest, dividends and rent, which the council treats as capital (Part 1 + para 30 and Annex 7). DWP deems Universal Credit's assumed yield from + capital (uc_tariff_income) and nobody receives it, so it stays out, and so + does Oxford City Council's own assumed income from capital, which the + council adds only under paragraphs 14 to 27 (para 15). DWP counts + statutory sick, maternity and paternity pay in its net earned income (UC + Regs 2013 reg 55(4)), and the model counts miscellaneous income there too + (uc_individual_earned_income_before_mif), so the list leaves them out. + Oxford City Council names some one-off payments as capital in Appendix 6: + the first month's Bereavement Support Payment, Government payments to help + with the cost of living, such as the Household Energy Payment, and payments + for a personal injury. The list leaves those out. It counts bsp and afcs + whole, because bsp_reported and afcs_reported hold the regular payments a + person reports; if a situation enters a lump sum in either input, the model + counts it as income. metadata: economy: false label: Oxford Council Tax Reduction Universal Credit applicant other income sources period: year unit: list reference: - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (other unearned income, the income ignored) and Appendix 5 (applies to applicants who do not receive Universal Credit) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Appendix 3 (other unearned income, the income ignored), Appendix 5 (applies to applicants who do not receive Universal Credit) and Appendix 6 (capital ignored) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/4 - - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Annex 7 (Oxford treats income derived from capital as capital) + - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Annex 7 (Oxford City Council treats income derived from capital as capital) href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27/3 - title: Oxford City Council Council Tax Reduction Scheme 2026 to 2027, Part 1 paras 15 and 30 href: https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27 - title: The Universal Credit Regulations 2013 reg. 66(1) href: https://www.legislation.gov.uk/uksi/2013/376/regulation/66 + - title: The Carer's Assistance (Carer Support Payment) (Scotland) Regulations 2023 reg. 14A (Scottish Carer Supplement for each award week of Carer Support Payment) + href: https://www.legislation.gov.uk/ssi/2023/302/regulation/14A + - title: The Carer's Assistance (Carer Support Payment) (Scotland) Regulations 2023 reg. 42 (13 weeks of Carer Support after a move to another part of the United Kingdom) + href: https://www.legislation.gov.uk/ssi/2023/302/regulation/42 values: 2026-04-01: - # Appendix 5's Child Benefit and child maintenance disregards apply only - # to applicants without Universal Credit, and Appendix 3 does not list - # either. + # Oxford City Council applies its Appendix 5 Child Benefit and child + # maintenance disregards only to applicants without Universal Credit, and + # does not list either in Appendix 3. - child_benefit - maintenance_income - # Universal Credit's unearned income (reg 66(1)) that is a receipt: all - # of gov.dwp.universal_credit.means_test.income_definitions.unearned - # except uc_tariff_income. Appendix 3 has no £15 disregard for spousal - # maintenance or Widowed Parent's Allowance; Appendix 5's apply only to - # applicants without Universal Credit. + # The receipts DWP counts as Universal Credit unearned income (reg 66(1)): + # all of gov.dwp.universal_credit.means_test.income_definitions.unearned + # except uc_tariff_income. Oxford City Council gives no £15 disregard for + # spousal maintenance or Widowed Parent's Allowance in Appendix 3; it gives + # its Appendix 5 ones only to applicants without Universal Credit. - spousal_maintenance_income - private_pension_income - state_pension - carers_allowance - # The whole carer support payment, not the part reg 66(1)(b)(iiia) - # counts: Appendix 3 has no cap at the Carer's Allowance rate. + # The whole carer support payment, not the part DWP counts under reg + # 66(1)(b)(iiia): Oxford City Council ignores the part above Carer's + # Allowance only in Appendix 5. - carer_support_payment + # Social Security Scotland pays the supplement for each week it pays the + # carer support payment (SSI 2023/302 reg 14A), including the 13 weeks + # after a carer moves to another part of the United Kingdom (reg 42). DWP + # does not count it, and Oxford City Council does not ignore it. + - scottish_carer_supplement - jsa_contrib - esa_contrib - maternity_allowance @@ -57,10 +74,12 @@ values: - widowed_parents_allowance - incapacity_benefit - sda - # Unearned income that reg 66 leaves out but Appendix 3 does not ignore. - # War pensions, Armed Forces Independence Payment and Healthy Start are - # in Appendix 5's ignored income, which applies only to applicants - # without Universal Credit. + # Unearned income DWP leaves out of reg 66 and Oxford City Council does + # not ignore in Appendix 3. The council ignores war pensions, Armed Forces + # Independence Payment and Healthy Start in Appendix 5, which it applies + # only to applicants without Universal Credit. bsp and afcs hold regular + # payments; the council treats the first month's Bereavement Support + # Payment and personal injury payments as capital (Appendix 6). - bsp - afcs - war_widows_pension @@ -68,10 +87,13 @@ values: - ssmg - healthy_start_vouchers - private_transfer_income - - miscellaneous_income - # Universal Credit replaces these awards, so they are nil for a Universal - # Credit claimant. The list names them so that no receipt escapes "All - # other unearned income". + # Not miscellaneous_income: the model already counts it in DWP's net + # earned income. DWP lists miscellaneous income as unearned income (reg + # 66(1)(m)); if the model moves it there (issue 1961), DWP will take it + # off the award pound for pound and this list must count it again. + # DWP replaces these awards with Universal Credit, so a claimant receives + # none of them. The list names them so that no receipt escapes "All other + # unearned income". - income_support - claimant_or_partner_jsa_income - claimant_or_partner_esa_income diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 663407db58..c3e61b9389 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -685,6 +685,100 @@ oxford_council_tax_reduction_universal_credit_income: 26_054.98 oxford_council_tax_reduction: 1_350 +- name: Oxford counts miscellaneous income once, in DWP's net earned income + # As the Child Benefit case, with 2,600 of miscellaneous income. The model + # counts it in DWP's net earned income (uc_individual_earned_income_before_mif; + # issue 1961 asks whether DWP counts it as unearned income under reg + # 66(1)(m) instead), so the model leaves it out of Oxford's other income. + # - Income tax: 20% x (15,600 + 2,600 - 12,570) = 1,126. + # - National Insurance, on the earnings alone: 8% x (15,600 - 12,570) = 242.40. + # - DWP net earned income: 15,600 + 2,600 - 1,126 - 242.40 = 16,831.60. + # UC: maximum amount 424.90 x 12 + 2 x 303.94 x 12 = 12,393.36, higher work + # allowance 710 x 12 = 8,520, reduction 55% x (16,831.60 - 8,520) = + # 4,571.38, award 7,821.98. Appendix 3: 16,831.60 + 7,821.98 + 2,337.40 + # Child Benefit = 26,990.98 a year, 519.06 a week: band 2, 75% of 1,800. + # Counting the 2,600 again as other income would give 29,590.98, 569.06 a + # week, band 3 (900). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + miscellaneous_income: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + income_tax: [1_126, 0, 0] + national_insurance: [242.40, 0, 0] + uc_earned_income_before_work_allowance: 16_831.60 + universal_credit: 7_821.98 + child_benefit: 2_337.40 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_990.98 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts a regular Armed Forces Compensation Scheme payment + # As the Child Benefit case, with a Guaranteed Income Payment of 2,600 a + # year from the Armed Forces Compensation Scheme. DWP leaves it out of + # unearned income (reg 66(1)), so the award stays 8,965.98. Oxford City + # Council names no such payment in Appendix 3 and applies its Appendix 5 + # disregards, war pensions among them, only to applicants without + # Universal Credit, so it counts the payment: other income 2,337.40 + + # 2,600 = 4,937.40. Appendix 3: + # 14,751.60 + 8,965.98 + 4,937.40 = 28,654.98 a year, 551.06 a week: band + # 3, 50% of 1,800. Leaving it out would give 501.06 a week, band 2 (1,350). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + afcs_reported: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + afcs: [2_600, 0, 0] + uc_unearned_income: 0 + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 4_937.40 + oxford_council_tax_reduction_universal_credit_income: 28_654.98 + oxford_council_tax_reduction: 900 + - name: Savings interest stays out of a Universal Credit applicant's Oxford income # As the Child Benefit case, with 2,600 (50 a week) of savings_interest_income. # Part 1 para 30 and Annex 7 treat "any income derived from capital" as @@ -960,6 +1054,151 @@ oxford_council_tax_reduction_universal_credit_income: 24_176.70 oxford_council_tax_reduction: 1_800 +# A carer who receives Carer Support from Social Security Scotland and moves +# to another part of the United Kingdom keeps it for 13 weeks (SSI 2023/302 +# reg 42), with the Scottish Carer Supplement for each week of the carer +# support payment (reg 14A). The 2026-27 weekly rates: carer support payment +# 86.45, supplement 11.70. Each case: a lone parent, 35, children 5 and 8, no +# earnings, the carer element in the award (209.34 x 12 = 2,512.08), Child +# Benefit 2,337.40. DWP counts the carer support payment only up to a year of +# Carer's Allowance, 86.45 x 52 = 4,495.40 (reg 66(1)(b)(iiia)), and not the +# supplement, and does not cap an award with a carer element (reg 83(1)(j)). +# Oxford ignores 48.15 x 52 = 2,503.80 of the carer element and counts both +# payments whole, since it ignores the part above Carer's Allowance only in +# Appendix 5, for applicants without Universal Credit. + +- name: Oxford counts the Scottish Carer Supplement the model pays with a carer support payment + # Carer support payment for 13 weeks: 13 x 86.45 = 1,123.85. The model pays + # the supplement for the year whenever the carer support payment is + # positive: 11.70 x 52 = 608.40. UC: maximum amount 424.90 x 12 + 2 x + # 303.94 x 12 + 2,512.08 + 10,100 housing = 25,005.44, award 25,005.44 - + # 1,123.85 = 23,881.59. Other income 2,337.40 + 1,123.85 + 608.40 = + # 4,069.65. Appendix 3: 23,881.59 - 2,503.80 + 4,069.65 = 25,447.44 a year, + # 489.37 a week: band 2, 75% of 1,800. Leaving out the supplement would + # give 477.67 a week, band 1 (1,800). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + is_carer_for_benefits: true + carers_allowance: 0 + would_claim_carers_allowance: false + carer_support_payment: 1_123.85 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 10_100 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + scottish_carer_supplement: [608.40, 0, 0] + uc_carer_element: 2_512.08 + uc_unearned_income: 1_123.85 + uc_benefit_cap_reduction: 0 + universal_credit: 23_881.59 + oxford_council_tax_reduction_universal_credit_other_income: 4_069.65 + oxford_council_tax_reduction_universal_credit_income: 25_447.44 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts 13 weeks of the Scottish Carer Supplement after a move from Scotland + # As above, with the supplement supplied for the 13 weeks: 13 x 11.70 = + # 152.10. The award stays 23,881.59. Other income 2,337.40 + 1,123.85 + + # 152.10 = 3,613.35. Appendix 3: 23,881.59 - 2,503.80 + 3,613.35 = + # 24,991.14 a year, 480.60 a week: band 2, 75% of 1,800. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + is_carer_for_benefits: true + carers_allowance: 0 + would_claim_carers_allowance: false + carer_support_payment: 1_123.85 + scottish_carer_supplement: 152.10 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 10_100 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + universal_credit: 23_881.59 + oxford_council_tax_reduction_universal_credit_other_income: 3_613.35 + oxford_council_tax_reduction_universal_credit_income: 24_991.14 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts a whole carer support payment above a year of Carer's Allowance + # The model takes the carer support payment it is given. Here it holds + # 5,200 (100 a week), more than the 4,495.40 DWP counts. Housing 9,000. UC: + # maximum amount 5,098.80 + 7,294.56 + 2,512.08 + 9,000 = 23,905.44, award + # 23,905.44 - 4,495.40 = 19,410.04. The model pays the supplement for the + # year: 608.40. Other income 2,337.40 + 5,200 + 608.40 = 8,145.80. + # Appendix 3: 19,410.04 - 2,503.80 + 8,145.80 = 25,052.04 a year, 481.77 a + # week: band 2, 75% of 1,800. Counting only DWP's 4,495.40 would give + # 468.22 a week, band 1 (1,800). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + is_carer_for_benefits: true + carers_allowance: 0 + would_claim_carers_allowance: false + carer_support_payment: 5_200 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 9_000 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_unearned_carer_support_payment: [4_495.40, 0, 0] + uc_unearned_income: 4_495.40 + universal_credit: 19_410.04 + oxford_council_tax_reduction_universal_credit_other_income: 8_145.80 + oxford_council_tax_reduction_universal_credit_income: 25_052.04 + oxford_council_tax_reduction: 1_350 + - name: A household head who applies alone keeps the income for applicants without Universal Credit # A grandmother, 50, heads the household and shares a benefit unit with # parents aged 17 and their baby, who claim Universal Credit. The award and @@ -967,8 +1206,11 @@ # award, under paragraphs 14 to 27 on her own income: a private pension of # 27,400 less income tax of 20% x (27,400 - 12,570) = 2,966 is 24,434 a # year, 469.88 a week: band 1, 100%. Counting the baby's Child Benefit it - # would be 496.93 a week, band 2. Her Appendix 3 other income is her own - # pension alone. + # would be 496.93 a week, band 2. The couple's award, with no earnings, is + # the under-25 couple standard allowance 528.34 plus the child element + # 303.94, 832.28 x 12 = 9,987.36 a year. Her Appendix 3 income, which the + # model calculates but does not band, leaves out the couple's award and + # Child Benefit and counts her own pension alone: 27,400. period: 2026 absolute_error_margin: 0.01 input: @@ -1005,5 +1247,7 @@ is_council_tax_reduction_applicant_or_partner: [true, false, false, false] child_benefit: 1_406.60 council_tax_reduction_applicable_income: 24_434 + universal_credit: 9_987.36 oxford_council_tax_reduction_universal_credit_other_income: 27_400 + oxford_council_tax_reduction_universal_credit_income: 27_400 oxford_council_tax_reduction: 1_800 diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py index 756d9b41c6..d14c8d1389 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction.py @@ -44,12 +44,13 @@ def formula(benunit, period, parameters): ) weekly_income = max_(0, annual_income - child_benefit) / WEEKS_IN_YEAR weekly_income += where(has_uc_award, 0, weekly_tariff_income) - # Paragraphs 14 to 27 assess an applicant without Universal Credit; - # Appendix 3 assesses one with it on DWP's net earned income, the - # award and other income instead (scheme para. 9). The model holds - # the 2026-27 scheme, and universal_credit.in_effect turns Appendix 3 - # on from April 2026. Before then an applicant with an award keeps - # the income above, without Oxford's tariff income. + # Oxford City Council assesses an applicant without Universal Credit + # under paragraphs 14 to 27, and one with it under Appendix 3, on + # DWP's net earned income, the award and other income (scheme para. + # 9). The model holds the 2026-27 scheme, and universal_credit. + # in_effect turns Appendix 3 on from April 2026. Before then an + # applicant with an award keeps the income above, without Oxford's + # tariff income. appendix_3 = has_uc_award & ctr.universal_credit.in_effect uc_weekly_income = ( benunit("oxford_council_tax_reduction_universal_credit_income", period) diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index cad90ee69b..3362d667da 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -6,39 +6,36 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): entity = BenUnit label = "Oxford Council Tax Reduction income of a Universal Credit applicant" documentation = ( - "The yearly income on which Oxford's working-age scheme bands an " - "applicant who receives Universal Credit (Appendix 3); the scheme " - "bands a fifty-second of it each week. Appendix 3 adds the " - "applicant's and partner's net earned income, as DWP provides it, " - "to the Universal Credit awarded, and counts other (unearned) income " - "weekly. DWP's net earned income is the Universal Credit calculation " - "of earned income (uc_earned_income_before_work_allowance): earnings " - "less income tax, National Insurance and relievable pension " - "contributions in full (UC Regs 2013 reg. 55(5)(a) and reg. 57(2) " - "step 4), before the work allowance, which belongs to the award " - "(reg. 22). So Oxford's own share of pension contributions (Part 1 " - "para. 18) does not reach these applicants. Other income is " - "oxford_council_tax_reduction_universal_credit_other_income: Child " - "Benefit, child maintenance and the other unearned receipts Appendix 3 " - "does not ignore, without Universal Credit's assumed yield from " - "capital or income derived from capital. The award is the amount " - "awarded, before deductions for debts (uc_deductions) come out of the " - "payment. Appendix 3 also ignores " - "£48.15 a week of each carer element and £84.46 a week of each " - "disabled child element, which come off the award (but not below " - "nil), and, for a disabled applicant, an amount equal to any " - "disability premium payable before the move to Universal Credit. " - "The model holds no record of that earlier award, so it does not " - "ignore that amount, which can overstate the income. Appendix 3 " - "ignores any transitional element in the award. The model computes no " - "transitional element (uc_maximum_amount), so the award has none to " - "take out. Paragraphs 14 to 27, Annexes 5 and 6 and Appendix 5 apply " - "only to applicants without Universal Credit, so Oxford's tariff " - "income from capital (para. 15) and its Child Benefit disregard do " - "not apply here. The DWP figures and the award are the benefit unit's " - "claimant's and partner's, so none of them counts where the household " - "head applies alone. oxford_council_tax_reduction bands on this " - "income from April 2026 " + "The yearly income on which Oxford City Council bands a working-age " + "applicant who receives Universal Credit (Appendix 3); the council " + "bands a fifty-second of it each week. The council adds the " + "applicant's and partner's net earned income, as DWP provides it, to " + "the Universal Credit awarded, and counts other (unearned) income " + "weekly. The model reads DWP's net earned income from its Universal " + "Credit calculation of earned income " + "(uc_earned_income_before_work_allowance): DWP deducts income tax, " + "National Insurance and relievable pension contributions in full from " + "earnings (UC Regs 2013 reg. 55(5)(a) and reg. 57(2) step 4) and " + "applies the work allowance only in the award (reg. 22). So the " + "council's own share of pension contributions (Part 1 para. 18) does " + "not reach these applicants. The model reads other income from " + "oxford_council_tax_reduction_universal_credit_other_income, and the " + "award as the amount DWP awards before it takes deductions for debts " + "(uc_deductions) out of the payment. The council also ignores £48.15 " + "a week of each carer element and £84.46 a week of each disabled child " + "element, which the model takes off the award but not below nil. For " + "a disabled applicant the council ignores an amount equal to any " + "disability premium payable before the move to Universal Credit; the " + "model holds no record of that earlier award and ignores nothing for " + "it, so it can overstate the income. The council ignores any " + "transitional element in the award, and the model computes none " + "(uc_maximum_amount). The council applies paragraphs 14 to 27, " + "Annexes 5 and 6 and Appendix 5 only to applicants without Universal " + "Credit, so its tariff income from capital (para. 15) and its Child " + "Benefit disregard do not reach these applicants. DWP's figures and " + "the award belong to the benefit unit's claimant and partner, so the " + "model counts none of them where the household head applies alone. " + "oxford_council_tax_reduction bands on this income from April 2026 " "(gov.local_authorities.oxford.council_tax_reduction.universal_credit." "in_effect)." ) @@ -59,14 +56,15 @@ def formula(benunit, period, parameters): other_income = benunit( "oxford_council_tax_reduction_universal_credit_other_income", period ) - # Appendix 3 ignores £48.15 of the carer element and £84.46 of the - # disabled child element. An award has a carer element for each - # claimant who cares (reg. 29) and a disabled child addition for each - # disabled child (reg. 24(2)), and the two amounts are the 2026-27 - # carer and disabled child premiums, one for each carer and each - # disabled child (SI 2006/213 Sch 3 para 20(7) and (8)), so each - # element has its own disregard, up to the element. They come off - # the award, which cannot fall below nil. + # Oxford City Council ignores £48.15 of the carer element and £84.46 + # of the disabled child element (Appendix 3). DWP includes a carer + # element for each claimant who cares (reg. 29) and a disabled child + # addition for each disabled child (reg. 24(2)), and the two amounts + # equal the 2026-27 carer and disabled child premiums, which councils + # include in Housing Benefit for each carer and each disabled child + # (SI 2006/213 Sch 3 para 20(7) and (8)). So the model ignores up to + # each amount for each element and takes the disregards off the + # award, not below nil. carers = add(benunit, period, ["uc_carer_element_included_for_claimant"]) carer_disregard = min_( benunit("uc_carer_element", period), @@ -87,13 +85,13 @@ def formula(benunit, period, parameters): p.disabled_child_element_disregard * WEEKS_IN_YEAR, ) ) - # Appendix 3 counts "the amount of Universal Credit awarded". The - # deductions for debts (uc_deductions) come out of the payment, not - # the award, so the model adds them back; the benefit cap reduces - # the award itself. + # Oxford City Council counts "the amount of Universal Credit awarded" + # (Appendix 3). DWP takes the deductions for debts (uc_deductions) out + # of the payment, not the award, so the model adds them back; DWP + # applies the benefit cap to the award itself. award = benunit("universal_credit", period) + benunit("uc_deductions", period) award = max_(0, award - carer_disregard - disabled_child_disregard) - # The DWP figures and the award are the claimant's and partner's, not - # a household head's who applies alone. + # DWP's figures and the award belong to the claimant and partner, not + # to a household head who applies alone. applicant_family = ~benunit("council_tax_reduction_head_applies_alone", period) return max_(0, applicant_family * (earned_income + award) + other_income) diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py index 46789583ef..4c6809338b 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py @@ -6,23 +6,28 @@ class oxford_council_tax_reduction_universal_credit_other_income(Variable): entity = BenUnit label = "Oxford Council Tax Reduction other income of a Universal Credit applicant" documentation = ( - "The yearly other (unearned) income that Oxford's working-age scheme " - "adds to DWP's net earned income and the Universal Credit awarded for " - "an applicant who receives Universal Credit (Appendix 3). Appendix 3 " - "lists the income it ignores and takes all other unearned income into " - "account; Appendix 5's disregards, Child Benefit and child maintenance " - "among them, apply only to applicants without Universal Credit. " + "The yearly other (unearned) income that Oxford City Council adds to " + "DWP's net earned income and the Universal Credit awarded for a " + "working-age applicant who receives Universal Credit (Appendix 3). The " + "council lists the income it ignores in Appendix 3 and takes all other " + "unearned income into account; it applies its Appendix 5 disregards, " + "Child Benefit and child maintenance among them, only to applicants " + "without Universal Credit. " "gov.local_authorities.oxford.council_tax_reduction.universal_credit." - "other_income_sources lists the sources and the reason for each. It " - "counts Child Benefit, child maintenance and the receipts in Universal " - "Credit's unearned income, and leaves out Universal Credit's assumed " - "yield from capital (uc_tariff_income) and income derived from capital, " - "which Part 1 para 30 and Annex 7 treat as capital. The income is the " - "applicant's and partner's, as in " + "other_income_sources lists the sources and the reason for each. The " + "model counts Child Benefit, child maintenance, the receipts DWP counts " + "as Universal Credit unearned income, the Scottish Carer Supplement " + "and the other receipts the council does not ignore. It leaves out " + "Universal Credit's assumed yield from capital (uc_tariff_income), " + "income derived from capital, which the council treats as capital " + "(Part 1 para 30 and Annex 7), and miscellaneous income, which DWP " + "already counts in its net earned income. The model counts the " + "applicant's and partner's income, as in " "council_tax_reduction_applicable_income: a household head who applies " - "alone counts only their own, and the benefit unit's awards belong to its " - "claimant and partner. The model does not count student loans or " - "grants, which Annex 8 counts with its own disregards." + "alone counts only their own, and the benefit unit's awards belong to " + "its claimant and partner. The model does not count student loans or " + "grants, which the council counts under Annex 8 with its own " + "disregards." ) definition_period = YEAR unit = GBP @@ -37,11 +42,12 @@ def formula(benunit, period, parameters): p = parameters( period ).gov.local_authorities.oxford.council_tax_reduction.universal_credit - # Appendix 3 bases the income on "the income of the applicant and, - # where appropriate, their partner". A person's receipts count for - # the applicant and partner only. The benefit unit's awards (Child - # Benefit and the rest) are its claimant's and partner's, so they do - # not count where the household head applies alone. + # Oxford City Council bases the income on "the income of the + # applicant and, where appropriate, their partner" (Appendix 3), so + # the model counts a person's receipts for the applicant and partner + # only. The benefit unit's awards (Child Benefit and the rest) belong + # to its claimant and partner, so the model leaves them out where the + # household head applies alone. person = benunit.members applicant_or_partner = person( "is_council_tax_reduction_applicant_or_partner", period From 290b095e68009bc9ca4ff2d304699352dc4b7f4c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 20:23:03 -0400 Subject: [PATCH 13/21] Test that each Oxford Appendix 3 receipt counts once and whole A new property supplies each receipt to generated Universal Credit families, from a list written out from the scheme rather than read from the parameter. More of a receipt Oxford City Council counts raises other income by the whole amount, a carer support payment above DWP's cap included, and leaves DWP's net earned income alone. Miscellaneous income reaches only DWP's net earned income, income from capital reaches neither, and a child's receipts never count. Co-Authored-By: Claude Opus 5.5 --- ...x_reduction_universal_credit_properties.py | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 94a883a878..5254b8afca 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -57,6 +57,12 @@ of such a family is the band of its paragraphs 14 to 27 income less Child Benefit, with no tariff income, as the model gave before PolicyEngine encoded Appendix 3. +8. Each receipt counts once: more of a receipt Oxford counts as other income + raises other income by the whole amount, a carer support payment above + DWP's cap included, and leaves DWP's net earned income unchanged. More + miscellaneous income, which the model counts in DWP's net earned income, + leaves other income unchanged; more interest, dividends or rent changes + neither; and a child's receipts never change other income. The YAML cases in policy/baseline/gov/local_authorities/oxford/ council_tax_reduction/ pin the paragraphs 14 to 27 route and the hand-worked @@ -170,6 +176,8 @@ def situation(units, local_authorities=None, council_tax=COUNCIL_TAX, year=YEAR) people[name]["meets_uc_higher_disabled_child_condition"] = { year: child["disability"] == "higher" } + for key, value in child.get("inputs", {}).items(): + people[name][key] = {year: value} names.append(name) benunits[f"b{i}"] = { "members": names, @@ -180,6 +188,8 @@ def situation(units, local_authorities=None, council_tax=COUNCIL_TAX, year=YEAR) } if "child_benefit" in unit: benunits[f"b{i}"]["child_benefit"] = {year: unit["child_benefit"]} + for key, value in unit.get("benunit_inputs", {}).items(): + benunits[f"b{i}"][key] = {year: value} households[f"h{i}"] = { "members": names, "country": {year: "ENGLAND"}, @@ -704,3 +714,136 @@ def test_earlier_years_keep_the_paragraphs_14_to_27_income(units): assert abs(values["oxford_council_tax_reduction"][i] - expected) < TOLERANCE, ( unit ) + + +# Invariant 8's receipts, listed here from the scheme and the regulations +# rather than read from the parameter, so a source dropped from the +# parameter fails the test. Oxford City Council counts each as other income: +# Child Benefit and child maintenance (its Appendix 5 disregards apply only +# without Universal Credit), the receipts DWP counts under reg 66 (the +# carer support payment whole), the Scottish Carer Supplement, the regular +# payments Appendix 3 does not ignore, and the awards Universal Credit +# replaces. +COUNTED_PERSON_RECEIPTS = [ + "maintenance_income", + "spousal_maintenance_income", + "private_pension_income", + "state_pension", + "carers_allowance", + "carer_support_payment", + "scottish_carer_supplement", + "jsa_contrib", + "esa_contrib", + "maternity_allowance", + "iidb", + "widowed_parents_allowance", + "incapacity_benefit", + "sda", + "bsp", + "afcs", + "war_widows_pension", + "armed_forces_independence_payment", + "ssmg", + "healthy_start_vouchers", + "private_transfer_income", +] +COUNTED_BENUNIT_RECEIPTS = [ + "child_benefit", + "income_support", + "claimant_or_partner_jsa_income", + "claimant_or_partner_esa_income", + "tax_credits", + "pension_credit", +] +# The model counts miscellaneous income in DWP's net earned income. +EARNED_RECEIPTS = ["miscellaneous_income"] +# Oxford City Council treats income derived from capital as capital (Annex 7). +CAPITAL_RECEIPTS = ["savings_interest_income", "dividend_income", "property_income"] +PERSON_RECEIPTS = COUNTED_PERSON_RECEIPTS + EARNED_RECEIPTS + CAPITAL_RECEIPTS +# A year of carer support payment above a year of Carer's Allowance (86.45 x +# 52 = 4,495.40), the most DWP counts (reg 66(1)(b)(iiia)). +RECEIPT_STEP = {"carer_support_payment": 5_200.0} +RECEIPT_VARIABLES = [ + "universal_credit", + "council_tax_reduction_head_applies_alone", + "uc_earned_income_before_work_allowance", + "oxford_council_tax_reduction_universal_credit_other_income", +] + + +def with_receipts(unit): + """The family with every receipt supplied for every member, at nil + except its own maintenance and interest and HMRC's Child Benefit, since + a variable supplied for some people reads as nil for the rest.""" + adults = [ + {receipt: 0.0 for receipt in PERSON_RECEIPTS} | adult + for adult in unit["adults"] + ] + children = [ + dict(child, inputs={receipt: 0.0 for receipt in PERSON_RECEIPTS}) + for child in unit["children"] + ] + benunit_inputs = {receipt: 0.0 for receipt in COUNTED_BENUNIT_RECEIPTS} + benunit_inputs["child_benefit"] = child_benefit(unit) + return dict(unit, adults=adults, children=children, benunit_inputs=benunit_inputs) + + +def receive(unit, receipt, step, child=False): + """The family with step more of the receipt: the benefit unit's, the last + adult's (the partner in a couple) or the first child's.""" + if receipt in COUNTED_BENUNIT_RECEIPTS: + inputs = dict(unit["benunit_inputs"]) + inputs[receipt] += step + return dict(unit, benunit_inputs=inputs) + if child: + first = unit["children"][0] + inputs = dict(first["inputs"]) + inputs[receipt] += step + children = [dict(first, inputs=inputs)] + unit["children"][1:] + return dict(unit, children=children) + last = dict(unit["adults"][-1]) + last[receipt] += step + return dict(unit, adults=unit["adults"][:-1] + [last]) + + +@PROPERTY_SETTINGS +@given(st.lists(families(), min_size=1, max_size=3)) +@example(units=[example_families()[i] for i in (0, 1, 5)]) +def test_each_receipt_counts_once(units): + units = [with_receipts(unit) for unit in units] + # Each variant: (family, receipt, step, whether a child receives it). + variants = [] + for i, unit in enumerate(units): + for receipt in COUNTED_BENUNIT_RECEIPTS + PERSON_RECEIPTS: + step = RECEIPT_STEP.get(receipt, STEP) + variants.append((i, receipt, step, False)) + if unit["children"] and receipt in PERSON_RECEIPTS: + variants.append((i, receipt, step, True)) + values = calculate( + units + [receive(units[i], r, s, c) for i, r, s, c in variants], + RECEIPT_VARIABLES, + ) + other = values["oxford_council_tax_reduction_universal_credit_other_income"] + earned = values["uc_earned_income_before_work_allowance"] + n = len(units) + for k, (i, receipt, step, child) in enumerate(variants): + j = n + k + if not (has_award(values, i) and has_award(values, j)): + continue + case = (receipt, step, child, units[i]) + added = other[j] - other[i] + if child: + # Oxford counts the applicant's and partner's receipts only. + assert abs(added) < TOLERANCE, case + elif receipt in EARNED_RECEIPTS: + # DWP's net earned income takes it, net of any income tax on it, + # and other income does not count it again. + assert abs(added) < TOLERANCE, case + assert TOLERANCE < earned[j] - earned[i] <= step + TOLERANCE, case + elif receipt in CAPITAL_RECEIPTS: + assert abs(added) < TOLERANCE, case + assert abs(earned[j] - earned[i]) < TOLERANCE, case + else: + # Invariant 8: the whole receipt, once. + assert abs(added - step) < TOLERANCE, case + assert abs(earned[j] - earned[i]) < TOLERANCE, case From 7fa10f27e8e37257d2d20a93639fd21b2f842fa9 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 31 Oct 2026 22:12:06 -0400 Subject: [PATCH 14/21] Name Oxford City Council and DWP as the actors in the 2026 test header and changelog The YAML file's header now gives the scheme's actions to Oxford City Council and DWP and notes that DWP applies no benefit cap to an award with a carer element (reg 83(1)(j)). The changelog fragment adds the Scottish Carer Supplement and miscellaneous income. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2520.fixed.md | 2 +- ...ouncil_tax_reduction_universal_credit.yaml | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/changelog.d/2520.fixed.md b/changelog.d/2520.fixed.md index 2422f2300a..3bd48df1e5 100644 --- a/changelog.d/2520.fixed.md +++ b/changelog.d/2520.fixed.md @@ -1 +1 @@ -Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the scheme's Appendix 3 income from April 2026: DWP's net earned income, which deducts pension contributions in full, plus the award before deductions for debts and other unearned income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. Other income counts Child Benefit, child maintenance and the other receipts Appendix 3 does not ignore, but not interest, dividends, rent or Universal Credit's assumed yield from capital. The model had assessed these applicants on the income for applicants without Universal Credit, which deducts half of pension contributions and leaves out Child Benefit and child maintenance. Some of their reductions rise and some fall, a reform of Oxford's pension contribution share no longer moves them, and model years before 2026 keep their earlier results. +Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the Appendix 3 income Oxford City Council sets from April 2026: DWP's net earned income, from which DWP deducts pension contributions in full, plus the award before deductions for debts and other unearned income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. Other income includes Child Benefit, child maintenance, the Scottish Carer Supplement and the other regular receipts the council does not ignore in Appendix 3, but not interest, dividends, rent, Universal Credit's assumed yield from capital or the miscellaneous income the model already counts in DWP's net earned income. The model had assessed these applicants on the income for applicants without Universal Credit, on which the council deducts only half of pension contributions and leaves out Child Benefit and child maintenance. Some of their reductions rise and some fall, a reform of Oxford's pension contribution share no longer moves them, and model years before 2026 keep their earlier results. diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index c3e61b9389..025de7455e 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -1,31 +1,33 @@ -# Oxford Council Tax Reduction Scheme 2026 to 2027 +# Oxford City Council Council Tax Reduction Scheme 2026 to 2027 # (https://www.oxford.gov.uk/council-tax-reduction/council-tax-reduction-scheme-2026-27). # -# Part 1 para 9 bands two groups of working-age applicants on weekly income: -# those who do not receive Universal Credit, whose income "is calculated in -# accordance with paragraphs 14-27", and those who do, whose income follows -# Appendix 3. The heading before para 14 says "Paragraphs 14-27, and Annexes 5 -# and 6, apply to pensioners, and working-age applicants who do not receive -# Universal Credit". Para 18 deducts "50% of contributions to occupational or -# personal pension schemes" from earnings. +# In Part 1 para 9 Oxford City Council bands two groups of working-age +# applicants on weekly income: those who do not receive Universal Credit, +# whose income it calculates "in accordance with paragraphs 14-27", and those +# who do, whose income follows Appendix 3. In the heading before para 14 the +# council says "Paragraphs 14-27, and Annexes 5 and 6, apply to pensioners, +# and working-age applicants who do not receive Universal Credit". In para 18 +# it deducts "50% of contributions to occupational or personal pension +# schemes" from earnings. # # Appendix 3 (page /4): "The income of applicants who receive Universal Credit # is calculated by adding their net earned income (as provided by DWP) to the # amount of Universal Credit awarded. Any transitional protection (the # 'transitional element' of Universal Credit) included in the Universal Credit # award is ignored. Other (unearned) income is calculated separately on a -# weekly basis." DWP's net earned income deducts "any relievable pension -# contributions made by the person" in full, with income tax and primary Class -# 1 contributions (UC Regs 2013 reg 55(5)(a) and (b)). +# weekly basis." DWP deducts "any relievable pension contributions made by the +# person" in full from net earned income, with income tax and primary Class 1 +# contributions (UC Regs 2013 reg 55(5)(a) and (b)). # -# Other income. Appendix 3 lists the income it ignores and says "All other -# unearned income is taken into account". Appendix 5, which ignores Child -# Benefit and child maintenance, applies to "pensioners and working-age -# applicants who do not receive Universal Credit", so Oxford counts both for -# these applicants. Part 1 para 30 and Annex 7 treat "any income derived from -# capital" as capital, so interest, dividends and rent stay out. Universal -# Credit's assumed yield from capital (reg 72) reduces the award but is not a -# receipt, so it stays out too. +# Other income. In Appendix 3 Oxford City Council lists the income it ignores +# and says "All other unearned income is taken into account". It ignores Child +# Benefit and child maintenance in Appendix 5, which it applies to +# "pensioners and working-age applicants who do not receive Universal +# Credit", so it counts both for these applicants. It treats "any income +# derived from capital" as capital (Part 1 para 30 and Annex 7), so interest, +# dividends and rent stay out. DWP deems Universal Credit's assumed yield from +# capital (reg 72) and takes it off the award, but nobody receives it, so it +# stays out too. # # Bands, weekly income (Part 1 para 9 and Appendix 3): up to 480.00 100%; # 480.01 to 530.00 75%; 530.01 to 580.00 50%; 580.01 to 660.00 25%; above @@ -42,7 +44,8 @@ # allowance, single 25 or over, 424.90, couple 666.97; child (born on or # after 6 April 2017) 303.94; lower work allowance 427, higher 710. The taper # is 55% (reg 22). Every award below sits under the benefit cap outside -# Greater London (14,753 single, 22,020 otherwise). Child Benefit, at HMRC's +# Greater London (14,753 single, 22,020 otherwise), except where DWP includes +# the carer element and so applies no cap (reg 83(1)(j)). Child Benefit, at HMRC's # 2026-27 weekly rates, is 27.05 for the eldest child and 17.90 for each # other: 1,406.60 a year for one child and 2,337.40 for two. From be6b1292e97d620f7da28a8d5b1f8a8ce23384e9 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 05:25:26 -0500 Subject: [PATCH 15/21] Supply the Scottish Carer Supplement in Oxford's Scottish carer cases The model pays the supplement for 52 weeks whenever the carer support payment is positive, while Social Security Scotland pays it for each award week of Carer Support, at most 13 weeks after a move from Scotland (SSI 2023/302 regs 14A and 42). Drop the case that pinned the model's 52 weeks and supply the supplement in the two remaining cases, so a fix to scottish_carer_supplement moves no Oxford case. Co-Authored-By: Claude Opus 5.5 --- ...ouncil_tax_reduction_universal_credit.yaml | 70 +++++-------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 025de7455e..0399289b27 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -1068,17 +1068,19 @@ # supplement, and does not cap an award with a carer element (reg 83(1)(j)). # Oxford ignores 48.15 x 52 = 2,503.80 of the carer element and counts both # payments whole, since it ignores the part above Carer's Allowance only in -# Appendix 5, for applicants without Universal Credit. +# Appendix 5, for applicants without Universal Credit. The model pays the +# supplement for 52 weeks whenever the carer support payment is positive +# (scottish_carer_supplement), so each case supplies the supplement. -- name: Oxford counts the Scottish Carer Supplement the model pays with a carer support payment - # Carer support payment for 13 weeks: 13 x 86.45 = 1,123.85. The model pays - # the supplement for the year whenever the carer support payment is - # positive: 11.70 x 52 = 608.40. UC: maximum amount 424.90 x 12 + 2 x - # 303.94 x 12 + 2,512.08 + 10,100 housing = 25,005.44, award 25,005.44 - - # 1,123.85 = 23,881.59. Other income 2,337.40 + 1,123.85 + 608.40 = - # 4,069.65. Appendix 3: 23,881.59 - 2,503.80 + 4,069.65 = 25,447.44 a year, - # 489.37 a week: band 2, 75% of 1,800. Leaving out the supplement would - # give 477.67 a week, band 1 (1,800). +- name: Oxford counts 13 weeks of the Scottish Carer Supplement after a move from Scotland + # Carer support payment for 13 weeks, 13 x 86.45 = 1,123.85, and the + # supplement for the same 13 weeks, 13 x 11.70 = 152.10. UC: maximum + # amount 424.90 x 12 + 2 x 303.94 x 12 + 2,512.08 + 10,100 housing = + # 25,005.44, award 25,005.44 - 1,123.85 = 23,881.59. Other income + # 2,337.40 + 1,123.85 + 152.10 = 3,613.35. Appendix 3: 23,881.59 - + # 2,503.80 + 3,613.35 = 24,991.14 a year, 480.60 a week: band 2, 75% of + # 1,800. Leaving out the supplement would give 477.67 a week, band 1 + # (1,800). period: 2026 absolute_error_margin: 0.01 input: @@ -1089,6 +1091,7 @@ carers_allowance: 0 would_claim_carers_allowance: false carer_support_payment: 1_123.85 + scottish_carer_supplement: 152.10 person2: age: 5 person3: @@ -1108,50 +1111,9 @@ council_tax: 1_800 savings: 0 output: - scottish_carer_supplement: [608.40, 0, 0] uc_carer_element: 2_512.08 uc_unearned_income: 1_123.85 uc_benefit_cap_reduction: 0 - universal_credit: 23_881.59 - oxford_council_tax_reduction_universal_credit_other_income: 4_069.65 - oxford_council_tax_reduction_universal_credit_income: 25_447.44 - oxford_council_tax_reduction: 1_350 - -- name: Oxford counts 13 weeks of the Scottish Carer Supplement after a move from Scotland - # As above, with the supplement supplied for the 13 weeks: 13 x 11.70 = - # 152.10. The award stays 23,881.59. Other income 2,337.40 + 1,123.85 + - # 152.10 = 3,613.35. Appendix 3: 23,881.59 - 2,503.80 + 3,613.35 = - # 24,991.14 a year, 480.60 a week: band 2, 75% of 1,800. - period: 2026 - absolute_error_margin: 0.01 - input: - people: - person1: - age: 35 - is_carer_for_benefits: true - carers_allowance: 0 - would_claim_carers_allowance: false - carer_support_payment: 1_123.85 - scottish_carer_supplement: 152.10 - person2: - age: 5 - person3: - age: 8 - benunits: - benunit: - members: [person1, person2, person3] - would_claim_uc: true - claims_all_entitled_benefits: true - uc_housing_costs_element: 10_100 - uc_deduction_rate: 0 - households: - household: - members: [person1, person2, person3] - country: ENGLAND - local_authority: OXFORD - council_tax: 1_800 - savings: 0 - output: universal_credit: 23_881.59 oxford_council_tax_reduction_universal_credit_other_income: 3_613.35 oxford_council_tax_reduction_universal_credit_income: 24_991.14 @@ -1161,8 +1123,9 @@ # The model takes the carer support payment it is given. Here it holds # 5,200 (100 a week), more than the 4,495.40 DWP counts. Housing 9,000. UC: # maximum amount 5,098.80 + 7,294.56 + 2,512.08 + 9,000 = 23,905.44, award - # 23,905.44 - 4,495.40 = 19,410.04. The model pays the supplement for the - # year: 608.40. Other income 2,337.40 + 5,200 + 608.40 = 8,145.80. + # 23,905.44 - 4,495.40 = 19,410.04. The case supplies the supplement for a + # year, 11.70 x 52 = 608.40. Other income 2,337.40 + 5,200 + 608.40 = + # 8,145.80. # Appendix 3: 19,410.04 - 2,503.80 + 8,145.80 = 25,052.04 a year, 481.77 a # week: band 2, 75% of 1,800. Counting only DWP's 4,495.40 would give # 468.22 a week, band 1 (1,800). @@ -1176,6 +1139,7 @@ carers_allowance: 0 would_claim_carers_allowance: false carer_support_payment: 5_200 + scottish_carer_supplement: 608.40 person2: age: 5 person3: From ecb6a8689eb6226161ab905eb95880158cb997c4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 05:25:46 -0500 Subject: [PATCH 16/21] Test Oxford's Appendix 3 receipts across family members and benefits Add hand-worked 2026 cases for a younger partner's receipt, a 17-year-old's receipt, Personal Independence Payment, Attendance Allowance and Disability Living Allowance, a self-employed claimant under the minimum income floor, and a Carer's Allowance the model computes and reduces for Widowed Parent's Allowance. Co-Authored-By: Claude Opus 5.5 --- ...ouncil_tax_reduction_universal_credit.yaml | 344 ++++++++++++++++++ 1 file changed, 344 insertions(+) diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml index 0399289b27..1764326000 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit.yaml @@ -1166,6 +1166,350 @@ oxford_council_tax_reduction_universal_credit_income: 25_052.04 oxford_council_tax_reduction: 1_350 +# Oxford City Council counts "the income of the applicant and, where +# appropriate, their partner" (Appendix 3), whichever of them receives it, and +# no one else's. The model heads a benefit unit with its eldest member +# (is_benunit_head). Child Benefit, at HMRC's 2026-27 rates, is 1,406.60 a +# year for one child and 2,337.40 for two. On 15,600 of pay, income tax is +# 20% x (15,600 - 12,570) = 606, National Insurance 8% x (15,600 - 12,570) = +# 242.40 and DWP's net earned income 15,600 - 606 - 242.40 = 14,751.60. + +- name: Oxford counts the younger partner's receipt in a Universal Credit couple's income + # An unmarried couple, 38 and 35, with a child aged 5 and housing of 7,000. + # The elder partner earns 15,600 and heads the benefit unit. The younger partner + # receives child maintenance of 2,600 (50 a week) for the child, which DWP + # does not count (reg 66(1)(d) reaches only maintenance from a spouse or + # former spouse). UC: maximum amount 666.97 x 12 + 303.94 x 12 + 7,000 = + # 18,650.92, lower work allowance 427 x 12 = 5,124, reduction 55% x + # (14,751.60 - 5,124) = 5,295.18, award 13,355.74. Other income 1,406.60 + # Child Benefit + 2,600 = 4,006.60. Appendix 3: 14,751.60 + 13,355.74 + + # 4,006.60 = 32,113.94 a year, 617.58 a week: band 4, 25% of 1,800. + # Leaving out the younger partner's maintenance would give 567.58 a week, + # band 3 (900). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 38 + employment_income: 15_600 + person2: + age: 35 + maintenance_income: 2_600 + person3: + age: 5 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + is_married: false + uc_housing_costs_element: 7_000 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + is_benunit_head: [true, false, false] + is_council_tax_reduction_applicant_or_partner: [true, true, false] + uc_earned_income_before_work_allowance: 14_751.60 + uc_unearned_income: 0 + universal_credit: 13_355.74 + child_benefit: 1_406.60 + oxford_council_tax_reduction_universal_credit_other_income: 4_006.60 + oxford_council_tax_reduction_universal_credit_income: 32_113.94 + oxford_council_tax_reduction: 450 + +- name: Oxford leaves a 17-year-old's receipt out of a Universal Credit applicant's income + # A lone parent, 35, earns 15,600 and has children aged 5 and 17. The + # 17-year-old, in non-advanced education, receives private transfers of + # 2,600 a year. DWP gives the higher first-child amount, 351.88 a month, + # for the elder child, born before 6 April 2017. UC: maximum amount 424.90 + # x 12 + (351.88 + 303.94) x 12 = 12,968.64, higher work allowance 710 x 12 + # = 8,520, reduction 55% x (14,751.60 - 8,520) = 3,427.38, award 9,541.26. + # The 17-year-old is neither the applicant nor the partner, so other income + # is the Child Benefit alone, 2,337.40. Appendix 3: 14,751.60 + 9,541.26 + + # 2,337.40 = 26,630.26 a year, 512.12 a week: band 2, 75% of 1,800. + # Counting the 17-year-old's receipt would give 562.12 a week, band 3 (900). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + person2: + age: 5 + person3: + age: 17 + is_in_non_advanced_education: true + private_transfer_income: 2_600 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + is_council_tax_reduction_applicant_or_partner: [true, false, false] + uc_earned_income_before_work_allowance: 14_751.60 + universal_credit: 9_541.26 + child_benefit: 2_337.40 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_630.26 + oxford_council_tax_reduction: 1_350 + +# In Appendix 3 Oxford City Council ignores "Attendance Allowance, +# Disability Living Allowance, Personal Independence Payments", and DWP +# counts none of them as unearned income (reg 66(1)). + +- name: Oxford ignores Personal Independence Payment in a Universal Credit applicant's income + # The Child Benefit case's lone parent, earning 15,600 with children 5 and + # 8, receives Personal Independence Payment of 2,600 a year. The award + # stays 8,965.98 and other income 2,337.40. Appendix 3: 14,751.60 + + # 8,965.98 + 2,337.40 = 26,054.98 a year, 501.06 a week: band 2, 75% of + # 1,800. Counting the payment would give 551.06 a week, band 3 (900). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 15_600 + pip: 2_600 + person2: + age: 5 + person3: + age: 8 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 0 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + pip: [2_600, 0, 0] + universal_credit: 8_965.98 + oxford_council_tax_reduction_universal_credit_other_income: 2_337.40 + oxford_council_tax_reduction_universal_credit_income: 26_054.98 + oxford_council_tax_reduction: 1_350 + +# An unmarried couple, 67 and 60, with no children and housing of 9,000, claim +# Universal Credit as joint claimants, which reg 3(2)(a) allows when one of +# them is under the qualifying age for State Pension Credit. The elder +# receives a State Pension of 5,000 a year, below the personal allowance, and +# heads the benefit unit; the younger earns 15,600. DWP counts the State Pension in full (reg 66(1)(a)). UC: +# maximum amount 666.97 x 12 + 9,000 = 17,003.64, no work allowance, +# reduction 55% x 14,751.60 = 8,113.38, award 17,003.64 - 8,113.38 - 5,000 +# = 3,890.26. Other income is the State Pension, 5,000. Appendix 3: +# 14,751.60 + 3,890.26 + 5,000 = 23,641.86 a year, 454.65 a week: band 1, +# 100% of 1,800. Counting another 2,600 would give 504.65 a week, band 2 +# (1,350). + +- name: Oxford ignores Attendance Allowance in a Universal Credit couple's income + # The elder partner, 67, also receives Attendance Allowance of 2,600 a + # year. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + state_pension: 5_000 + attendance_allowance: 2_600 + person2: + age: 60 + employment_income: 15_600 + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + is_married: false + uc_housing_costs_element: 9_000 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + attendance_allowance: [2_600, 0] + uc_earned_income_before_work_allowance: 14_751.60 + uc_unearned_income: 5_000 + universal_credit: 3_890.26 + oxford_council_tax_reduction_universal_credit_other_income: 5_000 + oxford_council_tax_reduction_universal_credit_income: 23_641.86 + oxford_council_tax_reduction: 1_800 + +- name: Oxford ignores Disability Living Allowance in a Universal Credit couple's income + # As above, but the elder partner is 70 and receives Disability Living + # Allowance of 2,600 a year. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + state_pension: 5_000 + dla: 2_600 + person2: + age: 60 + employment_income: 15_600 + benunits: + benunit: + members: [person1, person2] + would_claim_uc: true + claims_all_entitled_benefits: true + is_married: false + uc_housing_costs_element: 9_000 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + dla: [2_600, 0] + uc_earned_income_before_work_allowance: 14_751.60 + uc_unearned_income: 5_000 + universal_credit: 3_890.26 + oxford_council_tax_reduction_universal_credit_other_income: 5_000 + oxford_council_tax_reduction_universal_credit_income: 23_641.86 + oxford_council_tax_reduction: 1_800 + +- name: Oxford counts the earned income the minimum income floor gives a self-employed claimant + # A single claimant, 35, with no children and housing of 12,500, has + # self-employed profits of 5,000. DWP treats them as in gainful + # self-employment outside a start-up period, so the minimum income floor + # applies (reg 62; uc_mif_applies). Their individual threshold is 35 + # hours (reg 88(1)) at the 2026-27 National Living Wage of 12.71: + # 12.71 x 35 x 52 = 23,132.20 a year (reg 90(2)(b)). The model converts it + # to a net amount with income tax, 20% x (23,132.20 - 12,570) = 2,112.44, + # and the Class 4 contributions on profits of that size, 6% x (23,132.20 - + # 12,570) = 633.73 (reg 62(4)(b); means_test.minimum_income_floor. + # self_employed_national_insurance): 23,132.20 - 2,112.44 - 633.73 = + # 20,386.03. Their own earned income, 5,000, falls short of it, so DWP + # treats them as having 20,386.03 (reg 62(2)). UC: maximum amount 424.90 x + # 12 + 12,500 = 17,598.80, no work allowance, reduction 55% x 20,386.03 = + # 11,212.32, award 6,386.48. Appendix 3 adds the net earned income DWP uses: + # 20,386.03 + 6,386.48 = 26,772.51 a year, 514.86 a week: band 2, 75% of + # 1,800. Adding the 5,000 they earn would give 218.97 a week, band 1 + # (1,800). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + self_employment_income: 5_000 + uc_mif_applies: true + benunits: + benunit: + members: [person1] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 12_500 + uc_deduction_rate: 0 + households: + household: + members: [person1] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + uc_minimum_income_floor_gross: 23_132.20 + uc_minimum_income_floor: 20_386.03 + uc_individual_earned_income: 20_386.03 + uc_earned_income_before_work_allowance: 20_386.03 + universal_credit: 6_386.48 + oxford_council_tax_reduction_universal_credit_other_income: 0 + oxford_council_tax_reduction_universal_credit_income: 26_772.51 + oxford_council_tax_reduction: 1_350 + +- name: Oxford counts the Carer's Allowance DWP pays after Widowed Parent's Allowance + # A widowed lone parent, 35, whose spouse died before 6 April 2017, has + # children aged 10 and 12, no earnings and housing of 7,500. They care for + # someone 40 hours a week and receive Widowed Parent's Allowance of 2,600 a + # year. The model computes their Carer's Allowance: 86.45 x 52 = 4,495.40, + # reduced by the Widowed Parent's Allowance (Overlapping Benefits Regs 1979 + # reg 12) to 4,495.40 - 2,600 = 1,895.40. DWP counts both as unearned + # income, 1,895.40 + 2,600 = 4,495.40 (reg 66(1)(b)(iii) and (vi)), and + # includes the carer element, 209.34 x 12 = 2,512.08, so no benefit cap + # applies (reg 83(1)(i) and (j)). UC: maximum amount 424.90 x 12 + (351.88 + # + 303.94) x 12 + 2,512.08 + 7,500 = 22,980.72, award 22,980.72 - 4,495.40 + # = 18,485.32. Other income 2,337.40 Child Benefit + 1,895.40 + 2,600 = + # 6,832.80, as it would be with the whole 4,495.40 of Carer's Allowance + # and no Widowed Parent's Allowance. Appendix 3: 18,485.32 - 2,503.80 + + # 6,832.80 = 22,814.32 a year, 438.74 a week: band 1, 100% of 1,800. + # Counting Carer's Allowance before the reduction would give 488.74 a week, + # band 2 (1,350). + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + care_hours: 40 + is_carer_for_benefits: true + would_claim_carers_allowance: true + widowed_parents_allowance_reported: 2_600 + person2: + age: 10 + person3: + age: 12 + benunits: + benunit: + members: [person1, person2, person3] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_housing_costs_element: 7_500 + uc_deduction_rate: 0 + households: + household: + members: [person1, person2, person3] + country: ENGLAND + local_authority: OXFORD + council_tax: 1_800 + savings: 0 + output: + carers_allowance_pre_overlap: [4_495.40, 0, 0] + carers_allowance: [1_895.40, 0, 0] + widowed_parents_allowance: [2_600, 0, 0] + uc_carer_element: 2_512.08 + uc_unearned_income: 4_495.40 + uc_benefit_cap_reduction: 0 + universal_credit: 18_485.32 + child_benefit: 2_337.40 + oxford_council_tax_reduction_universal_credit_other_income: 6_832.80 + oxford_council_tax_reduction_universal_credit_income: 22_814.32 + oxford_council_tax_reduction: 1_800 + - name: A household head who applies alone keeps the income for applicants without Universal Credit # A grandmother, 50, heads the household and shares a benefit unit with # parents aged 17 and their baby, who claim Universal Credit. The award and From 8e37cde33535eeaeb9bbcd3436b96ff6a0c726c7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 05:29:51 -0500 Subject: [PATCH 17/21] Say the model, not DWP, counts miscellaneous income as earnings DWP counts miscellaneous income as unearned income (UC Regs 2013 reg 66(1)(m)); the model counts it in uc_individual_earned_income_before_mif (#1961). Correct the other-income documentation, and name the model as the actor that lists the sources. Co-Authored-By: Claude Opus 5.5 --- ..._tax_reduction_universal_credit_other_income.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py index 4c6809338b..e473bd51ae 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py @@ -10,18 +10,20 @@ class oxford_council_tax_reduction_universal_credit_other_income(Variable): "DWP's net earned income and the Universal Credit awarded for a " "working-age applicant who receives Universal Credit (Appendix 3). The " "council lists the income it ignores in Appendix 3 and takes all other " - "unearned income into account; it applies its Appendix 5 disregards, " + "unearned income into account. It applies its Appendix 5 disregards, " "Child Benefit and child maintenance among them, only to applicants " - "without Universal Credit. " + "without Universal Credit. The model lists the sources, with the " + "reason for each, in " "gov.local_authorities.oxford.council_tax_reduction.universal_credit." - "other_income_sources lists the sources and the reason for each. The " - "model counts Child Benefit, child maintenance, the receipts DWP counts " + "other_income_sources. It counts Child Benefit, child maintenance, the " + "receipts DWP counts " "as Universal Credit unearned income, the Scottish Carer Supplement " "and the other receipts the council does not ignore. It leaves out " "Universal Credit's assumed yield from capital (uc_tariff_income), " "income derived from capital, which the council treats as capital " - "(Part 1 para 30 and Annex 7), and miscellaneous income, which DWP " - "already counts in its net earned income. The model counts the " + "(Part 1 para 30 and Annex 7), and miscellaneous income, which the " + "model already counts in DWP's net earned income " + "(uc_individual_earned_income_before_mif; #1961). The model counts the " "applicant's and partner's income, as in " "council_tax_reduction_applicable_income: a household head who applies " "alone counts only their own, and the benefit unit's awards belong to " From d5403d70a5c7aa781ed0acf259cd7f70b5d344bc Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 05:30:04 -0500 Subject: [PATCH 18/21] Name the model as the actor in Oxford's Appendix 3 parameter docs Say the model reads bsp_reported and afcs_reported as regular payments, that a user enters a lump sum, and that the model, not the list, leaves receipts out. Name backdate_parameters as what carries the 2026-27 values back. Co-Authored-By: Claude Opus 5.5 --- .../universal_credit/in_effect.yaml | 8 +-- .../other_income_sources.yaml | 64 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml index d6456678cf..2efe3a4dc3 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/in_effect.yaml @@ -1,10 +1,10 @@ description: When this is true, the model assesses a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the income Oxford City Council sets in Appendix 3. Otherwise it assesses them on the income the council sets for applicants without Universal Credit, less Child Benefit and without the council's assumed income from capital. values: # The model holds Oxford's 2026-27 scheme only: its other Oxford parameters - # start on 1 April 2026 and backdating carries them to earlier years. The - # model reads each year's parameters at 30 April, so this switches Appendix - # 3 on in model year 2026 and leaves earlier years as they were. In its - # 2025-26 scheme Oxford City Council also set an Appendix 3 income, with + # start on 1 April 2026 and backdate_parameters carries them back to earlier + # years. The model reads each year's parameters at 30 April, so it switches + # Appendix 3 on in model year 2026 and leaves earlier years as they were. In + # its 2025-26 scheme Oxford City Council also set an Appendix 3 income, with # other bands and a Carer's Allowance disregard in place of the element # disregards; the model does not hold that scheme. 2015-01-01: false diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml index 42320dac75..44b2e0bb54 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml @@ -2,28 +2,28 @@ description: > Other (unearned) income that Oxford City Council counts, with DWP's net earned income and the Universal Credit awarded, for a working-age applicant who receives Universal Credit. In Appendix 3 the council lists the income it - ignores and takes "All other unearned income" into account; it applies its + ignores and takes "All other unearned income" into account. It applies its Appendix 5 disregards, Child Benefit and child maintenance among them, only - to applicants who "do not receive Universal Credit". The list leaves out the - income Oxford City Council ignores in Appendix 3 (Attendance Allowance, - Disability Living Allowance, Personal Independence Payment, Discretionary - Housing Payments, Housing Benefit, discretionary council tax reductions and - payments from sub-tenants and boarders). It also leaves out savings - interest, dividends and rent, which the council treats as capital (Part 1 - para 30 and Annex 7). DWP deems Universal Credit's assumed yield from - capital (uc_tariff_income) and nobody receives it, so it stays out, and so - does Oxford City Council's own assumed income from capital, which the - council adds only under paragraphs 14 to 27 (para 15). DWP counts + to applicants who "do not receive Universal Credit". The model leaves out of + this list the income Oxford City Council ignores in Appendix 3 (Attendance + Allowance, Disability Living Allowance, Personal Independence Payment, + Discretionary Housing Payments, Housing Benefit, discretionary council tax + reductions and payments from sub-tenants and boarders). It also leaves out + savings interest, dividends and rent, which the council treats as capital + (Part 1 para 30 and Annex 7). DWP deems Universal Credit's assumed yield + from capital (uc_tariff_income) and nobody receives it, so the model leaves + it out, along with Oxford City Council's own assumed income from capital, + which the council adds only under paragraphs 14 to 27 (para 15). DWP counts statutory sick, maternity and paternity pay in its net earned income (UC Regs 2013 reg 55(4)), and the model counts miscellaneous income there too - (uc_individual_earned_income_before_mif), so the list leaves them out. - Oxford City Council names some one-off payments as capital in Appendix 6: - the first month's Bereavement Support Payment, Government payments to help - with the cost of living, such as the Household Energy Payment, and payments - for a personal injury. The list leaves those out. It counts bsp and afcs - whole, because bsp_reported and afcs_reported hold the regular payments a - person reports; if a situation enters a lump sum in either input, the model - counts it as income. + (uc_individual_earned_income_before_mif), so the model leaves them off this + list. Oxford City Council names some one-off payments as capital in Appendix + 6: the first month's Bereavement Support Payment, Government payments to + help with the cost of living, such as the Household Energy Payment, and + payments for a personal injury. The model leaves those out. It counts bsp + and afcs whole, because it reads bsp_reported and afcs_reported as regular + payments. If a user enters a lump sum in either input, the model counts it + as income. metadata: economy: false label: Oxford Council Tax Reduction Universal Credit applicant other income sources @@ -74,12 +74,13 @@ values: - widowed_parents_allowance - incapacity_benefit - sda - # Unearned income DWP leaves out of reg 66 and Oxford City Council does - # not ignore in Appendix 3. The council ignores war pensions, Armed Forces - # Independence Payment and Healthy Start in Appendix 5, which it applies - # only to applicants without Universal Credit. bsp and afcs hold regular - # payments; the council treats the first month's Bereavement Support - # Payment and personal injury payments as capital (Appendix 6). + # Unearned income DWP does not count under reg 66 and Oxford City Council + # does not ignore in Appendix 3. The council ignores war pensions, Armed + # Forces Independence Payment and Healthy Start in Appendix 5, which it + # applies only to applicants without Universal Credit. The model reads + # bsp and afcs as regular payments. The council treats the first month's + # Bereavement Support Payment and personal injury payments as capital + # (Appendix 6). - bsp - afcs - war_widows_pension @@ -87,13 +88,14 @@ values: - ssmg - healthy_start_vouchers - private_transfer_income - # Not miscellaneous_income: the model already counts it in DWP's net - # earned income. DWP lists miscellaneous income as unearned income (reg - # 66(1)(m)); if the model moves it there (issue 1961), DWP will take it - # off the award pound for pound and this list must count it again. + # The model leaves miscellaneous_income off this list because it already + # counts it in DWP's net earned income. DWP counts miscellaneous income as + # unearned income (reg 66(1)(m)). If the model moves it there (issue + # 1961), the model will take it off the award pound for pound, as DWP + # does, and must count it here again. # DWP replaces these awards with Universal Credit, so a claimant receives - # none of them. The list names them so that no receipt escapes "All other - # unearned income". + # none of them. The model lists them so that it counts any such receipt + # as other unearned income. - income_support - claimant_or_partner_jsa_income - claimant_or_partner_esa_income From aa2f0a579cc25b4a0c283dd9e52e2560169c53bf Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 06:17:48 -0500 Subject: [PATCH 19/21] Split long sentences in Oxford's Appendix 3 docs and changelog Break the other-income documentation, the parameter description and comments, the income variable's disability premium sentence and the changelog fragment into one thought per sentence. No formula, list entry or value changes. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2520.fixed.md | 2 +- .../other_income_sources.yaml | 48 +++++++++---------- ...l_tax_reduction_universal_credit_income.py | 10 ++-- ...reduction_universal_credit_other_income.py | 16 +++---- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/changelog.d/2520.fixed.md b/changelog.d/2520.fixed.md index 3bd48df1e5..c20170dbdc 100644 --- a/changelog.d/2520.fixed.md +++ b/changelog.d/2520.fixed.md @@ -1 +1 @@ -Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the Appendix 3 income Oxford City Council sets from April 2026: DWP's net earned income, from which DWP deducts pension contributions in full, plus the award before deductions for debts and other unearned income, less £48.15 a week of each carer element and £84.46 a week of each disabled child element. Other income includes Child Benefit, child maintenance, the Scottish Carer Supplement and the other regular receipts the council does not ignore in Appendix 3, but not interest, dividends, rent, Universal Credit's assumed yield from capital or the miscellaneous income the model already counts in DWP's net earned income. The model had assessed these applicants on the income for applicants without Universal Credit, on which the council deducts only half of pension contributions and leaves out Child Benefit and child maintenance. Some of their reductions rise and some fall, a reform of Oxford's pension contribution share no longer moves them, and model years before 2026 keep their earlier results. +Assess a working-age Oxford Council Tax Reduction applicant who receives Universal Credit on the Appendix 3 income Oxford City Council sets from April 2026. The council adds DWP's net earned income, from which DWP deducts pension contributions in full, to the award before deductions for debts and other unearned income. It ignores £48.15 a week of each carer element and £84.46 a week of each disabled child element. The model counts as other income Child Benefit, child maintenance, the Scottish Carer Supplement and the other regular receipts the council does not ignore in Appendix 3. It leaves out interest, dividends, rent, Universal Credit's assumed yield from capital and the miscellaneous income it already counts in DWP's net earned income. The model had assessed these applicants on the income for applicants without Universal Credit, on which the council deducts only half of pension contributions and leaves out Child Benefit and child maintenance. Some of their reductions rise and some fall, a reform of Oxford's pension contribution share no longer moves them, and model years before 2026 keep their earlier results. diff --git a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml index 44b2e0bb54..e9a08bc0d5 100644 --- a/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml +++ b/policyengine_uk/parameters/gov/local_authorities/oxford/council_tax_reduction/universal_credit/other_income_sources.yaml @@ -1,29 +1,29 @@ description: > Other (unearned) income that Oxford City Council counts, with DWP's net earned income and the Universal Credit awarded, for a working-age applicant - who receives Universal Credit. In Appendix 3 the council lists the income it - ignores and takes "All other unearned income" into account. It applies its - Appendix 5 disregards, Child Benefit and child maintenance among them, only - to applicants who "do not receive Universal Credit". The model leaves out of - this list the income Oxford City Council ignores in Appendix 3 (Attendance - Allowance, Disability Living Allowance, Personal Independence Payment, - Discretionary Housing Payments, Housing Benefit, discretionary council tax - reductions and payments from sub-tenants and boarders). It also leaves out - savings interest, dividends and rent, which the council treats as capital - (Part 1 para 30 and Annex 7). DWP deems Universal Credit's assumed yield - from capital (uc_tariff_income) and nobody receives it, so the model leaves - it out, along with Oxford City Council's own assumed income from capital, - which the council adds only under paragraphs 14 to 27 (para 15). DWP counts - statutory sick, maternity and paternity pay in its net earned income (UC - Regs 2013 reg 55(4)), and the model counts miscellaneous income there too - (uc_individual_earned_income_before_mif), so the model leaves them off this - list. Oxford City Council names some one-off payments as capital in Appendix - 6: the first month's Bereavement Support Payment, Government payments to - help with the cost of living, such as the Household Energy Payment, and - payments for a personal injury. The model leaves those out. It counts bsp - and afcs whole, because it reads bsp_reported and afcs_reported as regular - payments. If a user enters a lump sum in either input, the model counts it - as income. + who receives Universal Credit. In Appendix 3 the council lists the income + it ignores and takes "All other unearned income" into account. It applies + its Appendix 5 disregards, Child Benefit and child maintenance among them, + only to applicants who "do not receive Universal Credit". The model leaves + out of this list the income Oxford City Council ignores in Appendix 3 + (Attendance Allowance, Disability Living Allowance, Personal Independence + Payment, Discretionary Housing Payments, Housing Benefit, discretionary + council tax reductions and payments from sub-tenants and boarders). It also + leaves out savings interest, dividends and rent, which the council treats + as capital (Part 1 para 30 and Annex 7). DWP deems Universal Credit's + assumed yield from capital (uc_tariff_income) and nobody receives it, so + the model leaves it out. It also leaves out Oxford City Council's own + assumed income from capital, which the council adds only under paragraphs + 14 to 27 (para 15). DWP counts statutory sick, maternity and paternity pay + in its net earned income (UC Regs 2013 reg 55(4)), and the model counts + miscellaneous income there too (uc_individual_earned_income_before_mif). So + the model leaves them off this list. Oxford City Council names some one-off + payments as capital in Appendix 6: the first month's Bereavement Support + Payment, Government payments to help with the cost of living, such as the + Household Energy Payment, and payments for a personal injury. The model + leaves those out. It counts bsp and afcs whole, because it reads + bsp_reported and afcs_reported as regular payments. If a user enters a lump + sum in either input, the model counts it as income. metadata: economy: false label: Oxford Council Tax Reduction Universal Credit applicant other income sources @@ -63,7 +63,7 @@ values: # Allowance only in Appendix 5. - carer_support_payment # Social Security Scotland pays the supplement for each week it pays the - # carer support payment (SSI 2023/302 reg 14A), including the 13 weeks + # carer support payment (SSI 2023/302 reg 14A). That includes the 13 weeks # after a carer moves to another part of the United Kingdom (reg 42). DWP # does not count it, and Oxford City Council does not ignore it. - scottish_carer_supplement diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py index 3362d667da..6af418b323 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_income.py @@ -25,7 +25,7 @@ class oxford_council_tax_reduction_universal_credit_income(Variable): "a week of each carer element and £84.46 a week of each disabled child " "element, which the model takes off the award but not below nil. For " "a disabled applicant the council ignores an amount equal to any " - "disability premium payable before the move to Universal Credit; the " + "disability premium payable before the move to Universal Credit. The " "model holds no record of that earlier award and ignores nothing for " "it, so it can overstate the income. The council ignores any " "transitional element in the award, and the model computes none " @@ -59,10 +59,10 @@ def formula(benunit, period, parameters): # Oxford City Council ignores £48.15 of the carer element and £84.46 # of the disabled child element (Appendix 3). DWP includes a carer # element for each claimant who cares (reg. 29) and a disabled child - # addition for each disabled child (reg. 24(2)), and the two amounts - # equal the 2026-27 carer and disabled child premiums, which councils - # include in Housing Benefit for each carer and each disabled child - # (SI 2006/213 Sch 3 para 20(7) and (8)). So the model ignores up to + # addition for each disabled child (reg. 24(2)). The two amounts equal + # the 2026-27 carer and disabled child premiums, which councils include + # in Housing Benefit for each carer and each disabled child (SI + # 2006/213 Sch 3 para 20(7) and (8)). So the model ignores up to # each amount for each element and takes the disregards off the # award, not below nil. carers = add(benunit, period, ["uc_carer_element_included_for_claimant"]) diff --git a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py index e473bd51ae..a9bc3e5c90 100644 --- a/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py +++ b/policyengine_uk/variables/gov/local_authorities/oxford/council_tax_reduction/oxford_council_tax_reduction_universal_credit_other_income.py @@ -16,14 +16,14 @@ class oxford_council_tax_reduction_universal_credit_other_income(Variable): "reason for each, in " "gov.local_authorities.oxford.council_tax_reduction.universal_credit." "other_income_sources. It counts Child Benefit, child maintenance, the " - "receipts DWP counts " - "as Universal Credit unearned income, the Scottish Carer Supplement " - "and the other receipts the council does not ignore. It leaves out " - "Universal Credit's assumed yield from capital (uc_tariff_income), " - "income derived from capital, which the council treats as capital " - "(Part 1 para 30 and Annex 7), and miscellaneous income, which the " - "model already counts in DWP's net earned income " - "(uc_individual_earned_income_before_mif; #1961). The model counts the " + "receipts DWP counts as Universal Credit unearned income, the Scottish " + "Carer Supplement and the other receipts the council does not ignore. " + "It leaves out Universal Credit's assumed yield from capital " + "(uc_tariff_income) and income derived from capital, which the " + "council treats as capital (Part 1 para 30 and Annex 7). It also " + "leaves out miscellaneous income, which the model already counts in " + "DWP's net earned income (uc_individual_earned_income_before_mif; " + "#1961). The model counts the " "applicant's and partner's income, as in " "council_tax_reduction_applicable_income: a household head who applies " "alone counts only their own, and the benefit unit's awards belong to " From 19378b97fb7b139ca1df7d73b42451bd68e295f8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 1 Nov 2026 09:02:37 -0500 Subject: [PATCH 20/21] Give the pension-share documentation its actors Co-Authored-By: Claude Opus 5.5 --- ...ion_pension_contribution_deduction_rate.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py index 15333f87fe..d926d7ebc6 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_pension_contribution_deduction_rate.py @@ -18,25 +18,28 @@ class council_tax_reduction_pension_contribution_deduction_rate(Variable): documentation = ( "The share of pension contributions deducted from earnings in the " "Council Tax Reduction means test, under the family's own scheme. " - "Each scheme sets its own share, one-half in every scheme modelled: " + "The model reads each scheme's own share, one-half in every scheme " + "modelled: " "the England pensioner scheme (SI 2012/2885 Sch 1 paras 19 and 29), " "Wales (WSI 2013/3029 Sch 1 paras 13 and 23 and Sch 6 paras 15 and " "24), Scotland (SSI 2012/319 regs 33 and 36; SSI 2021/249 reg 50, and " "for the self-employed reg 51(9), which applies SSI 2012/303 regs 37 " "and 38) and the working-age schemes of Merton, Kingston upon Thames, " - "Newham, Westminster and Oxford. None of them refers to Housing " - "Benefit's share, so a reform of one scheme's share moves only that " - "scheme. English working-age families outside those councils, whose " + "Newham, Westminster and Oxford. The model reads no Housing Benefit " + "parameter for these shares, so it applies a reform of one share only " + "to that scheme. English working-age families outside those councils, " + "whose " "reductions the model takes as reported, and families outside Great " "Britain, where there is no Council Tax Reduction, read the England " "default scheme's share (SI 2012/2886 Sch paras 42, 52 and 61). " - "A working-age applicant with an award of Universal Credit is " - "assessed instead on the Secretary of State's Universal Credit " - "figures, which deduct relievable pension contributions in full " - "(UC Regs 2013 reg 55(5)(a) for employed earnings and reg 57(2) step 4 " - "for self-employed earnings): in Wales (WSI 2013/3029 Sch 6 para 9), " - "Scotland (SSI 2021/249 regs 49(6)(a) and 51(2)) and Oxford (its " - "scheme's Appendix 3). The model has no such route for Wales and " + "Councils in Wales (WSI 2013/3029 Sch 6 para 9), Scotland (SSI " + "2021/249 regs 49(6)(a) and 51(2)) and Oxford (its scheme's Appendix " + "3) assess a working-age applicant with an award of Universal Credit " + "on the Secretary of State's Universal Credit figures. DWP deducts " + "relievable pension contributions in full when it calculates those " + "figures (UC Regs 2013 reg 55(5)(a) for employed earnings and reg " + "57(2) step 4 for self-employed earnings). The model has no such " + "route for Wales and " "Scotland yet (#1962), so the share here reaches those applicants " "too. Merton, Kingston upon Thames, Newham and Westminster assess " "them on Universal Credit income (_legacy.py), so the share here does " From 2343a786174659e9e52a834af44808bdb4170e78 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 13:44:18 +0000 Subject: [PATCH 21/21] Count the award before deductions for debts in invariant 6's oracle Merton, Kingston upon Thames, Newham and Westminster now count the Universal Credit award before deductions for debts, as Oxford's Appendix 3 does, so the cross-council property no longer takes uc_deductions off Oxford's income. Add an explicit example of a claimant repaying debts at a quarter of the standard allowance. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6GC8zqh3aNDgFt3nd9 --- ...ax_reduction_universal_credit_properties.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py index 5254b8afca..10102916b0 100644 --- a/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py +++ b/policyengine_uk/tests/test_oxford_council_tax_reduction_universal_credit_properties.py @@ -48,11 +48,11 @@ family that keeps its award, since each pound contributed lowers DWP's net earned income and raises the award by at most the 55% taper. 6. Differential: for families without a carer or disabled child element, - the Appendix 3 income less any deductions for debts, with Universal - Credit's unearned income in place of Oxford's other income, equals the - Universal Credit income on which Merton, Kingston upon Thames, Newham and - Westminster assess the same family, recovered from each council's award - (they count the award after those deductions). + the Appendix 3 income, with Universal Credit's unearned income in place + of Oxford's other income, equals the Universal Credit income on which + Merton, Kingston upon Thames, Newham and Westminster assess the same + family, recovered from each council's award (they, like Oxford, count the + award before deductions for debts). 7. Earlier years: in 2025, before the scheme the model holds, the reduction of such a family is the band of its paragraphs 14 to 27 income less Child Benefit, with no tariff income, as the model gave before PolicyEngine @@ -594,6 +594,7 @@ def test_more_pension_contributions_never_lower_the_reduction(units, extra): @PROPERTY_SETTINGS @given(st.lists(families(), min_size=1, max_size=6)) @example(units=example_families()[:1]) +@example(units=couple_and_debt_families()[1:]) def test_appendix_3_income_matches_the_other_councils_universal_credit_income( units, ): @@ -642,12 +643,11 @@ def test_appendix_3_income_matches_the_other_councils_universal_credit_income( * values["council_tax_reduction_joint_liability_share"][j] ) award = values[variable][j] - # Those councils count the award after deductions for debts, - # which Oxford adds back, and Universal Credit's unearned income, - # where Oxford counts its own other income. + # Those councils, like Oxford, count the award before deductions + # for debts. They count Universal Credit's unearned income where + # Oxford counts its own other income. council_income = ( values["oxford_council_tax_reduction_universal_credit_income"][i] - - values["uc_deductions"][i] - values["oxford_council_tax_reduction_universal_credit_other_income"][ i ]