From 78f352228f3aa14822209e45f11f9dea61594ab9 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 7 Jun 2026 03:33:27 +0200 Subject: [PATCH] Add Westminster council tax support --- .../council-tax-reduction-westminster.md | 1 + .../maximum_support_rate.yaml | 10 ++ .../means_test/capital_limit.yaml | 10 ++ .../means_test/tariff_income_step.yaml | 10 ++ .../means_test/tariff_income_threshold.yaml | 10 ++ .../means_test/withdrawal_rate.yaml | 10 ++ .../non_dep_deduction/amount.yaml | 27 +++ .../remunerative_work_hours.yaml | 10 ++ .../council_tax_reduction.yaml | 157 ++++++++++++++++++ .../council_tax_reduction/config.py | 11 ++ ...simulated_council_tax_reduction_benunit.py | 1 + .../westminster_council_tax_reduction.py | 43 +++++ ..._reduction_individual_non_dep_deduction.py | 32 ++++ ...ouncil_tax_reduction_non_dep_deductions.py | 19 +++ 14 files changed, 351 insertions(+) create mode 100644 changelog.d/council-tax-reduction-westminster.md create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/maximum_support_rate.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/capital_limit.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_step.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_threshold.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/withdrawal_rate.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/amount.yaml create mode 100644 policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/remunerative_work_hours.yaml create mode 100644 policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction.py create mode 100644 policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_individual_non_dep_deduction.py create mode 100644 policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_non_dep_deductions.py diff --git a/changelog.d/council-tax-reduction-westminster.md b/changelog.d/council-tax-reduction-westminster.md new file mode 100644 index 000000000..c016a023d --- /dev/null +++ b/changelog.d/council-tax-reduction-westminster.md @@ -0,0 +1 @@ +- Add working-age Council Tax Support for Westminster. diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/maximum_support_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/maximum_support_rate.yaml new file mode 100644 index 000000000..0a532f993 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/maximum_support_rate.yaml @@ -0,0 +1,10 @@ +description: Maximum share of eligible Council Tax liability covered for working-age households under the Westminster Council Tax Support scheme. +values: + 2026-04-01: 1 +metadata: + unit: /1 + period: year + label: Westminster Council Tax Support maximum support rate + reference: + - title: Council Tax Support scheme for 2026/27 + href: https://www.westminster.gov.uk/sites/default/files/media/documents/council-tax-support-scheme-for-2026-27.pdf diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/capital_limit.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/capital_limit.yaml new file mode 100644 index 000000000..cc9949d3d --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/capital_limit.yaml @@ -0,0 +1,10 @@ +description: Capital limit for working-age households under the Westminster Council Tax Support scheme. +values: + 2026-04-01: 16_000 +metadata: + unit: currency-GBP + period: year + label: Westminster Council Tax Support capital limit + reference: + - title: Income and savings + href: https://www.westminster.gov.uk/benefits/guide-housing-benefit-and-council-tax-support/income-and-savings diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_step.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_step.yaml new file mode 100644 index 000000000..bb25693c5 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_step.yaml @@ -0,0 +1,10 @@ +description: Capital step above which one pound of weekly tariff income is added for working-age non-Universal Credit claimants under the Westminster Council Tax Support scheme. +values: + 2026-04-01: 250 +metadata: + unit: currency-GBP + period: week + label: Westminster Council Tax Support tariff income step + reference: + - title: Income and savings + href: https://www.westminster.gov.uk/benefits/guide-housing-benefit-and-council-tax-support/income-and-savings diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_threshold.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_threshold.yaml new file mode 100644 index 000000000..e36492b11 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/tariff_income_threshold.yaml @@ -0,0 +1,10 @@ +description: Capital threshold above which tariff income is added for working-age non-Universal Credit claimants under the Westminster Council Tax Support scheme. +values: + 2026-04-01: 6_000 +metadata: + unit: currency-GBP + period: year + label: Westminster Council Tax Support tariff income threshold + reference: + - title: Income and savings + href: https://www.westminster.gov.uk/benefits/guide-housing-benefit-and-council-tax-support/income-and-savings diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/withdrawal_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/withdrawal_rate.yaml new file mode 100644 index 000000000..bb37b03ae --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/means_test/withdrawal_rate.yaml @@ -0,0 +1,10 @@ +description: Withdrawal rate for working-age households under the Westminster Council Tax Support scheme. +values: + 2026-04-01: 0.2 +metadata: + unit: /1 + period: year + label: Westminster Council Tax Support withdrawal rate + reference: + - title: Council Tax Support scheme for 2026/27 + href: https://www.westminster.gov.uk/sites/default/files/media/documents/council-tax-support-scheme-for-2026-27.pdf diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/amount.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/amount.yaml new file mode 100644 index 000000000..61f35966b --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/amount.yaml @@ -0,0 +1,27 @@ +description: Weekly working-age non-dependant deduction schedule under the Westminster Council Tax Support scheme. +brackets: + - threshold: + 2026-04-01: 0 + amount: + 2026-04-01: 5.20 + - threshold: + 2026-04-01: 279 + amount: + 2026-04-01: 10.60 + - threshold: + 2026-04-01: 485 + amount: + 2026-04-01: 13.30 + - threshold: + 2026-04-01: 605 + amount: + 2026-04-01: 15.95 +metadata: + amount_unit: currency-GBP + period: week + threshold_unit: currency-GBP + type: single_amount + label: Westminster Council Tax Support non-dependant deduction schedule + reference: + - title: Council Tax Support scheme for 2026/27 + href: https://www.westminster.gov.uk/sites/default/files/media/documents/council-tax-support-scheme-for-2026-27.pdf diff --git a/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/remunerative_work_hours.yaml b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/remunerative_work_hours.yaml new file mode 100644 index 000000000..c15e252c2 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/westminster/council_tax_reduction/non_dep_deduction/remunerative_work_hours.yaml @@ -0,0 +1,10 @@ +description: Weekly hours threshold for remunerative work in the Westminster Council Tax Support non-dependant deduction schedule. +values: + 2026-04-01: 16 +metadata: + unit: hour + period: week + label: Westminster Council Tax Support remunerative work hours + reference: + - title: Council Tax Support scheme for 2026/27 + href: https://www.westminster.gov.uk/sites/default/files/media/documents/council-tax-support-scheme-for-2026-27.pdf diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml index 2dcf3d0b5..c1fb81a3e 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml @@ -565,3 +565,160 @@ output: uc_assessable_capital: 0 council_tax_reduction: 1_260 + +- name: Westminster working-age claimant can receive full support + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + claims_all_entitled_benefits: true + households: + household: + members: [claimant] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_scheme_supported: true + simulated_council_tax_reduction_benunit: 1_800 + council_tax_reduction: 1_800 + council_tax_less_benefit: 0 + +- name: Westminster working-age claimant above capital limit gets no local support + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 35 + council_tax_benefit_reported: 500 + benunits: + benunit: + members: [claimant] + claims_all_entitled_benefits: true + households: + household: + members: [claimant] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 16_001 + output: + council_tax_reduction_scheme_supported: true + simulated_council_tax_reduction_benunit: 0 + council_tax_benefit: 0 + council_tax_reduction: 0 + +- name: Westminster adds one weekly pound of tariff income for any partial 250 pounds above 6,000 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + claims_all_entitled_benefits: true + council_tax_reduction_applicable_amount: 0 + council_tax_reduction_applicable_income: 0 + households: + household: + members: [claimant] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 6_001 + output: + westminster_council_tax_reduction: 1_789.60 + council_tax_reduction: 1_789.60 + council_tax_less_benefit: 10.40 + +- name: Westminster applies its 2026 local non-dependant deduction schedule + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + non_dep: + age: 25 + employment_income: 5_200 + private_pension_income: 26_260 + weekly_hours: 16 + benunits: + claimant_benunit: + members: [claimant] + would_claim_uc: false + claims_all_entitled_benefits: true + non_dep_benunit: + members: [non_dep] + households: + household: + members: [claimant, non_dep] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_scheme_supported: true + council_tax_reduction: 970.60 + +- name: Westminster UC claimant uses UC maximum amount and DWP income + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + benunits: + claimant_benunit: + members: [claimant] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_maximum_amount: 5_000 + uc_income_reduction: 2_500 + uc_earned_income: 6_000 + uc_unearned_income: 0 + households: + household: + members: [claimant] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 0 + output: + universal_credit: 2_500 + council_tax_reduction: 1_100 + +- name: Westminster UC claimant uses UC-reported capital + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + benunits: + claimant_benunit: + members: [claimant] + would_claim_uc: true + claims_all_entitled_benefits: true + uc_reported_capital: 0 + households: + household: + members: [claimant] + country: ENGLAND + local_authority: WESTMINSTER + council_tax: 1_800 + savings: 20_000 + output: + uc_assessable_capital: 0 + council_tax_reduction: 1_800 diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/config.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/config.py index 05f21306f..ecd0dc6ba 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/config.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/config.py @@ -43,6 +43,16 @@ def is_newham_working_age(local_authority, country, has_pensioner): return (country == Country.ENGLAND) & ~has_pensioner & is_newham(local_authority) +def is_westminster(local_authority): + return local_authority == LocalAuthority.WESTMINSTER + + +def is_westminster_working_age(local_authority, country, has_pensioner): + return ( + (country == Country.ENGLAND) & ~has_pensioner & is_westminster(local_authority) + ) + + def is_supported_scheme(country, has_pensioner, local_authority): return ( is_england_pensioner_scheme(country, has_pensioner) @@ -51,4 +61,5 @@ def is_supported_scheme(country, has_pensioner, local_authority): | is_merton_working_age(local_authority, country, has_pensioner) | is_kingston_upon_thames_working_age(local_authority, country, has_pensioner) | is_newham_working_age(local_authority, country, has_pensioner) + | is_westminster_working_age(local_authority, country, has_pensioner) ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/simulated_council_tax_reduction_benunit.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/simulated_council_tax_reduction_benunit.py index eeb0d4f0f..42a37352e 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/simulated_council_tax_reduction_benunit.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/simulated_council_tax_reduction_benunit.py @@ -9,6 +9,7 @@ "merton_council_tax_reduction", "kingston_upon_thames_council_tax_reduction", "newham_council_tax_reduction", + "westminster_council_tax_reduction", ] diff --git a/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction.py new file mode 100644 index 000000000..3c86931ae --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction.py @@ -0,0 +1,43 @@ +from policyengine_uk.model_api import * +import numpy as np +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._legacy import ( + legacy_council_tax_reduction, +) +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction.config import ( + is_westminster_working_age, +) + + +class westminster_council_tax_reduction(Variable): + value_type = float + entity = BenUnit + label = "Westminster Council Tax Support" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + ctr = parameters(period).gov.local_authorities.westminster.council_tax_reduction + household = benunit.household + working_age = is_westminster_working_age( + household("local_authority", period), + household("country", period), + household("council_tax_reduction_household_has_pensioner", period), + ) + has_uc_award = benunit("universal_credit", period) > 0 + capital = household("savings", period) + weekly_tariff_income = np.ceil( + max_(0, capital - ctr.means_test.tariff_income_threshold) + / ctr.means_test.tariff_income_step + ) + return legacy_council_tax_reduction( + benunit, + period, + ctr, + working_age, + "westminster_council_tax_reduction_non_dep_deductions", + additional_applicable_income=where( + has_uc_award, + 0, + weekly_tariff_income * WEEKS_IN_YEAR, + ), + ) diff --git a/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_individual_non_dep_deduction.py b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_individual_non_dep_deduction.py new file mode 100644 index 000000000..d5666a67a --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_individual_non_dep_deduction.py @@ -0,0 +1,32 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._legacy import ( + normal_gross_income_non_dep_deduction, +) +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction.config import ( + is_westminster_working_age, +) + + +class westminster_council_tax_reduction_individual_non_dep_deduction(Variable): + value_type = float + entity = Person + label = "Westminster CTR individual non-dependent deduction" + definition_period = YEAR + unit = GBP + defined_for = "council_tax_reduction_individual_non_dep_deduction_eligible" + + def formula(person, period, parameters): + ctr = parameters(period).gov.local_authorities.westminster.council_tax_reduction + household = person.household + working_age = is_westminster_working_age( + household("local_authority", period), + household("country", period), + household("council_tax_reduction_household_has_pensioner", period), + ) + return normal_gross_income_non_dep_deduction( + person, + period, + ctr, + working_age, + exempt_uc_no_earned_income=True, + ) diff --git a/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_non_dep_deductions.py b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_non_dep_deductions.py new file mode 100644 index 000000000..feefaa281 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/westminster/council_tax_reduction/westminster_council_tax_reduction_non_dep_deductions.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction._legacy import ( + local_non_dep_deductions, +) + + +class westminster_council_tax_reduction_non_dep_deductions(Variable): + value_type = float + entity = BenUnit + label = "Westminster CTR non-dependent deductions" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + return local_non_dep_deductions( + benunit, + period, + "westminster_council_tax_reduction_individual_non_dep_deduction", + )