diff --git a/changelog.d/council-tax-reduction-national.md b/changelog.d/council-tax-reduction-national.md new file mode 100644 index 000000000..da961bdf5 --- /dev/null +++ b/changelog.d/council-tax-reduction-national.md @@ -0,0 +1 @@ +- Add national Council Tax Reduction support for Scotland, Wales, and English pensioner households, while preserving reported CTB/CTR for unsupported English working-age local schemes. diff --git a/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/maximum_support_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/maximum_support_rate.yaml new file mode 100644 index 000000000..bb0c52367 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/maximum_support_rate.yaml @@ -0,0 +1,10 @@ +description: Maximum share of eligible Council Tax liability covered for pensioner households under the England Council Tax Reduction scheme. +values: + 2013-04-01: 1 +metadata: + unit: /1 + period: year + label: England Council Tax Reduction pensioner maximum support rate + reference: + - title: The Council Tax Reduction Schemes (Prescribed Requirements) (England) Regulations 2012 + href: https://www.legislation.gov.uk/uksi/2012/2885/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/capital_limit.yaml b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/capital_limit.yaml new file mode 100644 index 000000000..c6543dad0 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/capital_limit.yaml @@ -0,0 +1,10 @@ +description: Capital limit for pensioner households under the England Council Tax Reduction scheme. +values: + 2013-04-01: 16_000 +metadata: + unit: currency-GBP + period: year + label: England Council Tax Reduction pensioner capital limit + reference: + - title: The Council Tax Reduction Schemes (Prescribed Requirements) (England) Regulations 2012 + href: https://www.legislation.gov.uk/uksi/2012/2885/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/withdrawal_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/withdrawal_rate.yaml new file mode 100644 index 000000000..c8ca6101a --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/england/council_tax_reduction/pensioners/means_test/withdrawal_rate.yaml @@ -0,0 +1,10 @@ +description: Withdrawal rate for pensioner households under the England Council Tax Reduction scheme. +values: + 2013-04-01: 0.2 +metadata: + unit: /1 + period: year + label: England Council Tax Reduction pensioner withdrawal rate + reference: + - title: The Council Tax Reduction Schemes (Prescribed Requirements) (England) Regulations 2012 + href: https://www.legislation.gov.uk/uksi/2012/2885/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/maximum_support_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/maximum_support_rate.yaml new file mode 100644 index 000000000..d2042b610 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/maximum_support_rate.yaml @@ -0,0 +1,10 @@ +description: Maximum share of eligible Council Tax liability covered under the Scotland Council Tax Reduction scheme. +values: + 2013-04-01: 1 +metadata: + unit: /1 + period: year + label: Scotland Council Tax Reduction maximum support rate + reference: + - title: The Council Tax Reduction (Scotland) Regulations 2012 + href: https://www.legislation.gov.uk/ssi/2012/303/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/capital_limit.yaml b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/capital_limit.yaml new file mode 100644 index 000000000..8a659bb8a --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/capital_limit.yaml @@ -0,0 +1,10 @@ +description: Capital limit under the Scotland Council Tax Reduction scheme. +values: + 2013-04-01: 16_000 +metadata: + unit: currency-GBP + period: year + label: Scotland Council Tax Reduction capital limit + reference: + - title: The Council Tax Reduction (Scotland) Regulations 2012 + href: https://www.legislation.gov.uk/ssi/2012/303/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/withdrawal_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/withdrawal_rate.yaml new file mode 100644 index 000000000..21e164870 --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/scotland/council_tax_reduction/means_test/withdrawal_rate.yaml @@ -0,0 +1,10 @@ +description: Withdrawal rate under the Scotland Council Tax Reduction scheme. +values: + 2013-04-01: 0.2 +metadata: + unit: /1 + period: year + label: Scotland Council Tax Reduction withdrawal rate + reference: + - title: The Council Tax Reduction (Scotland) Regulations 2012 + href: https://www.legislation.gov.uk/ssi/2012/303/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/maximum_support_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/maximum_support_rate.yaml new file mode 100644 index 000000000..377bc0caf --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/maximum_support_rate.yaml @@ -0,0 +1,10 @@ +description: Maximum share of eligible Council Tax liability covered under the Wales Council Tax Reduction scheme. +values: + 2013-04-01: 1 +metadata: + unit: /1 + period: year + label: Wales Council Tax Reduction maximum support rate + reference: + - title: The Council Tax Reduction Schemes and Prescribed Requirements (Wales) Regulations 2012 + href: https://www.legislation.gov.uk/wsi/2012/3144/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/capital_limit.yaml b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/capital_limit.yaml new file mode 100644 index 000000000..c3e4c1f7c --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/capital_limit.yaml @@ -0,0 +1,10 @@ +description: Capital limit under the Wales Council Tax Reduction scheme. +values: + 2013-04-01: 16_000 +metadata: + unit: currency-GBP + period: year + label: Wales Council Tax Reduction capital limit + reference: + - title: The Council Tax Reduction Schemes and Prescribed Requirements (Wales) Regulations 2012 + href: https://www.legislation.gov.uk/wsi/2012/3144/contents/made diff --git a/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/withdrawal_rate.yaml b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/withdrawal_rate.yaml new file mode 100644 index 000000000..620e0f32b --- /dev/null +++ b/policyengine_uk/parameters/gov/local_authorities/wales/council_tax_reduction/means_test/withdrawal_rate.yaml @@ -0,0 +1,10 @@ +description: Withdrawal rate under the Wales Council Tax Reduction scheme. +values: + 2013-04-01: 0.2 +metadata: + unit: /1 + period: year + label: Wales Council Tax Reduction withdrawal rate + reference: + - title: The Council Tax Reduction Schemes and Prescribed Requirements (Wales) Regulations 2012 + href: https://www.legislation.gov.uk/wsi/2012/3144/contents/made diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 022c4c068..23cd899a0 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -471,6 +471,18 @@ programs: variable: council_tax verified_start_year: 2022 + - id: council_tax_reduction + name: Council Tax Reduction + full_name: Council Tax Reduction + category: Benefits + agency: Local + status: partial + coverage: UK + variable: council_tax_reduction + parameter_prefix: gov.local_authorities + verified_start_year: 2013 + notes: First-pass national support for Scotland, Wales, and England pensioner schemes, with reported CTB/CTR retained for unsupported English working-age local schemes. + # --- Treasury programs --- - id: cost_of_living_support name: Cost of Living Support diff --git a/policyengine_uk/tests/policy/baseline/consumption/council_tax_less_benefit.yaml b/policyengine_uk/tests/policy/baseline/consumption/council_tax_less_benefit.yaml new file mode 100644 index 000000000..85c66ffe9 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/consumption/council_tax_less_benefit.yaml @@ -0,0 +1,22 @@ +- name: Council Tax less reported fallback CTR is floored at zero + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 35 + council_tax_benefit_reported: 2_500 + employment_income: 10_000 + benunits: + benunit: + members: [claimant] + households: + household: + members: [claimant] + country: ENGLAND + local_authority: MAIDSTONE + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction: 2_500 + council_tax_less_benefit: 0 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 new file mode 100644 index 000000000..7c3c091aa --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml @@ -0,0 +1,95 @@ +- name: Unsupported English working-age schemes keep reported CTB + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 35 + council_tax_benefit_reported: 500 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + households: + household: + members: [claimant] + country: ENGLAND + local_authority: MAIDSTONE + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_scheme_supported: false + simulated_council_tax_reduction_benunit: 0 + council_tax_benefit: 500 + council_tax_reduction: 500 + +- name: Wales CTR simulates full support for a no-income claimant + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 35 + council_tax_benefit_reported: 500 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + households: + household: + members: [claimant] + country: WALES + local_authority: CARDIFF + council_tax: 1_800 + savings: 0 + output: + council_tax_reduction_scheme_supported: true + simulated_council_tax_reduction_benunit: 1_800 + council_tax_benefit: 1_800 + council_tax_reduction: 1_800 + +- name: Wales CTR applies the capital limit + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 35 + council_tax_benefit_reported: 500 + benunits: + benunit: + members: [claimant] + households: + household: + members: [claimant] + country: WALES + local_authority: CARDIFF + 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: England pensioner CTR is supported nationally + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 70 + benunits: + benunit: + members: [claimant] + households: + household: + members: [claimant] + country: ENGLAND + local_authority: MAIDSTONE + 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 diff --git a/policyengine_uk/tests/policy/integration/entitledto_scenarios.yaml b/policyengine_uk/tests/policy/integration/entitledto_scenarios.yaml index 96d268cd8..1fb5d597d 100644 --- a/policyengine_uk/tests/policy/integration/entitledto_scenarios.yaml +++ b/policyengine_uk/tests/policy/integration/entitledto_scenarios.yaml @@ -93,7 +93,9 @@ universal_credit: 0 housing_benefit: 0 pension_credit: 3_607.72 + council_tax_reduction: 1_800 + council_tax_less_benefit: 0 winter_fuel_allowance: 200 income_tax: 0 national_insurance: 0 - household_net_income: 15_833.18 + household_net_income: 17_633.18 diff --git a/policyengine_uk/variables/gov/dwp/council_tax_benefit.py b/policyengine_uk/variables/gov/dwp/council_tax_benefit.py index 630c0790e..d2861ead5 100644 --- a/policyengine_uk/variables/gov/dwp/council_tax_benefit.py +++ b/policyengine_uk/variables/gov/dwp/council_tax_benefit.py @@ -8,4 +8,8 @@ class council_tax_benefit(Variable): definition_period = YEAR unit = GBP - adds = ["council_tax_benefit_reported"] + def formula(benunit, period, parameters): + supported = benunit.household("council_tax_reduction_scheme_supported", period) + simulated = benunit("simulated_council_tax_reduction_benunit", period) + reported = benunit("council_tax_benefit_reported", period) + return where(supported, simulated, reported) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/benunit_contains_household_head.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/benunit_contains_household_head.py new file mode 100644 index 000000000..6a2d45c07 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/benunit_contains_household_head.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class benunit_contains_household_head(Variable): + value_type = bool + entity = BenUnit + label = "Benefit unit contains the oldest adult in the household" + definition_period = YEAR + + def formula(benunit, period, parameters): + person = benunit.members + household = person.household + benunit_max_age = benunit.max(person("age", period)) + household_max_age = benunit.max(household.max(person("age", period))) + return benunit_max_age == household_max_age 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 new file mode 100644 index 000000000..bbb774f34 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/config.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.country import Country + + +def is_england_pensioner_scheme(country, has_pensioner): + return (country == Country.ENGLAND) & has_pensioner + + +def is_scotland_scheme(country): + return country == Country.SCOTLAND + + +def is_wales_scheme(country): + return country == Country.WALES + + +def is_supported_scheme(country, has_pensioner): + return ( + is_england_pensioner_scheme(country, has_pensioner) + | is_scotland_scheme(country) + | is_wales_scheme(country) + ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction.py new file mode 100644 index 000000000..90922d959 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction(Variable): + value_type = float + entity = Household + label = "Council Tax Reduction" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + person = household.members + return household.sum( + person.benunit("council_tax_benefit", period) + * person("is_benunit_head", period) + ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_amount.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_amount.py new file mode 100644 index 000000000..c09642958 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_amount.py @@ -0,0 +1,45 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_applicable_amount(Variable): + value_type = float + entity = BenUnit + label = "applicable Council Tax Reduction amount" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + p = parameters(period).gov.dwp.housing_benefit.allowances + any_over_SP_age = benunit.any(benunit.members("is_SP_age", period)) + eldest_age = benunit("eldest_adult_age", period) + older_age_threshold = p.age_threshold.older + younger_age_threshold = p.age_threshold.younger + u_18 = eldest_age < younger_age_threshold + u_25 = eldest_age < older_age_threshold + o_25 = (eldest_age >= older_age_threshold) & ~any_over_SP_age + o_18 = (eldest_age >= younger_age_threshold) & ~any_over_SP_age + single = benunit("is_single_person", period) + couple = benunit("is_couple", period) + lone_parent = benunit("is_lone_parent", period) + single_personal_allowance = ( + u_25 * p.single.younger + + o_25 * p.single.older + + any_over_SP_age * p.single.aged + ) + couple_personal_allowance = ( + u_18 * p.couple.younger + + o_18 * p.couple.older + + any_over_SP_age * p.couple.aged + ) + lone_parent_personal_allowance = ( + u_18 * p.lone_parent.younger + + o_18 * p.lone_parent.older + + any_over_SP_age * p.lone_parent.aged + ) + personal_allowance = ( + single * single_personal_allowance + + couple * couple_personal_allowance + + lone_parent * lone_parent_personal_allowance + ) * WEEKS_IN_YEAR + premiums = benunit("benefits_premiums", period) + return personal_allowance + premiums diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py new file mode 100644 index 000000000..8cf5f764b --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_applicable_income.py @@ -0,0 +1,47 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_applicable_income(Variable): + value_type = float + entity = BenUnit + label = "relevant income for Council Tax Reduction means test" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + benunit_means_tested_benefits = [ + "child_benefit", + "income_support", + "jsa_income", + "esa_income", + "universal_credit", + ] + personal_benefits = [ + "carers_allowance", + "esa_contrib", + "jsa_contrib", + "state_pension", + "maternity_allowance", + "statutory_sick_pay", + "statutory_maternity_pay", + "ssmg", + ] + income_components = [ + "employment_income", + "self_employment_income", + "property_income", + "private_pension_income", + ] + bi = parameters(period).gov.contrib.ubi_center.basic_income + benefits = add(benunit, period, benunit_means_tested_benefits) + income = add(benunit, period, income_components) + personal_benefit_income = add(benunit, period, personal_benefits) + credits = add(benunit, period, ["tax_credits"]) + increased_income = income + personal_benefit_income + credits + benefits + + if not bi.interactions.include_in_means_tests: + increased_income -= add(benunit, period, ["basic_income"]) + + pension_contributions = add(benunit, period, ["pension_contributions"]) * 0.5 + tax = add(benunit, period, ["income_tax", "national_insurance"]) + return max_(0, increased_income - tax - pension_contributions) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_household_has_pensioner.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_household_has_pensioner.py new file mode 100644 index 000000000..555231a5a --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_household_has_pensioner.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_household_has_pensioner(Variable): + value_type = bool + entity = Household + label = "CTR claimant benefit unit has a pension-age member" + definition_period = YEAR + + def formula(household, period, parameters): + person = household.members + claimant_benunit = person.benunit("benunit_contains_household_head", period) + return household.any(claimant_benunit & person("is_SP_age", period)) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction.py new file mode 100644 index 000000000..102c093f6 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_individual_non_dep_deduction(Variable): + value_type = float + entity = Person + label = "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): + p = parameters(period).gov.dwp.housing_benefit.non_dep_deduction + weekly_total_income = person("total_income", period) / WEEKS_IN_YEAR + return p.amount.calc(weekly_total_income, right=True) * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py new file mode 100644 index 000000000..36cb046b4 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction_eligible.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_individual_non_dep_deduction_eligible(Variable): + value_type = bool + entity = Person + label = "eligible person for CTR non-dependent deduction" + definition_period = YEAR + + def formula(person, period, parameters): + return (person("age", period) >= 18) & ~person.benunit( + "benunit_contains_household_head", period + ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_maximum_eligible_liability.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_maximum_eligible_liability.py new file mode 100644 index 000000000..d4fde52c0 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_maximum_eligible_liability.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_maximum_eligible_liability(Variable): + value_type = float + entity = Household + label = "Maximum Council Tax liability eligible for CTR" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + return household("council_tax", period) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py new file mode 100644 index 000000000..c6b5fc1d8 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_non_dep_deductions.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class council_tax_reduction_non_dep_deductions(Variable): + value_type = float + entity = BenUnit + label = "CTR non-dependent deductions" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + deductions = benunit.members( + "council_tax_reduction_individual_non_dep_deduction", period + ) + deductions_in_household = benunit.max(benunit.members.household.sum(deductions)) + deductions_in_benunit = benunit.sum(deductions) + return deductions_in_household - deductions_in_benunit diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_scheme_supported.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_scheme_supported.py new file mode 100644 index 000000000..3dcec3845 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/council_tax_reduction_scheme_supported.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction.config import ( + is_supported_scheme, +) + + +class council_tax_reduction_scheme_supported(Variable): + value_type = bool + entity = Household + label = "Supported CTR scheme is available" + definition_period = YEAR + + def formula(household, period, parameters): + country = household("country", period) + has_pensioner = household( + "council_tax_reduction_household_has_pensioner", period + ) + return is_supported_scheme(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 new file mode 100644 index 000000000..0e172f8c9 --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/simulated_council_tax_reduction_benunit.py @@ -0,0 +1,84 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.local_authorities.council_tax_reduction.config import ( + is_england_pensioner_scheme, + is_scotland_scheme, + is_wales_scheme, +) + + +class simulated_council_tax_reduction_benunit(Variable): + value_type = float + entity = BenUnit + label = "Simulated Council Tax Reduction" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + local_authority_parameters = parameters(period).gov.local_authorities + england_pensioners_ctr = ( + local_authority_parameters.england.council_tax_reduction.pensioners + ) + scotland_ctr = local_authority_parameters.scotland.council_tax_reduction + wales_ctr = local_authority_parameters.wales.council_tax_reduction + + country = benunit.household("country", period) + has_pensioner = benunit.household( + "council_tax_reduction_household_has_pensioner", period + ) + england_pensioners = is_england_pensioner_scheme(country, has_pensioner) + scotland = is_scotland_scheme(country) + wales = is_wales_scheme(country) + national_scheme = england_pensioners | scotland | wales + + is_household_head_benunit = benunit("benunit_contains_household_head", period) + would_claim = benunit("would_claim_council_tax_reduction", period) + liability = benunit.household( + "council_tax_reduction_maximum_eligible_liability", period + ) + applicable_amount = benunit("council_tax_reduction_applicable_amount", period) + applicable_income = benunit("council_tax_reduction_applicable_income", period) + non_dep_deductions = benunit("council_tax_reduction_non_dep_deductions", period) + + max_support = select( + [england_pensioners, scotland, wales], + [ + england_pensioners_ctr.maximum_support_rate, + scotland_ctr.maximum_support_rate, + wales_ctr.maximum_support_rate, + ], + default=0.0, + ) + withdrawal_rate = select( + [england_pensioners, scotland, wales], + [ + england_pensioners_ctr.means_test.withdrawal_rate, + scotland_ctr.means_test.withdrawal_rate, + wales_ctr.means_test.withdrawal_rate, + ], + default=0.0, + ) + capital_limit = select( + [england_pensioners, scotland, wales], + [ + england_pensioners_ctr.means_test.capital_limit, + scotland_ctr.means_test.capital_limit, + wales_ctr.means_test.capital_limit, + ], + default=0.0, + ) + + excess_income = max_(0, applicable_income - applicable_amount) + preliminary_award = max_( + 0, + liability * max_support + - excess_income * withdrawal_rate + - non_dep_deductions, + ) + capital_eligible = benunit.household("savings", period) <= capital_limit + return ( + national_scheme + * is_household_head_benunit + * would_claim + * capital_eligible + * preliminary_award + ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/would_claim_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/would_claim_council_tax_reduction.py new file mode 100644 index 000000000..68fdd511c --- /dev/null +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/would_claim_council_tax_reduction.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class would_claim_council_tax_reduction(Variable): + value_type = bool + entity = BenUnit + label = "Would claim Council Tax Reduction" + documentation = ( + "Whether this benefit unit would claim Council Tax Reduction if eligible." + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + claims_all_entitled_benefits = benunit("claims_all_entitled_benefits", period) + reported_ctr = benunit("council_tax_benefit_reported", period) > 0 + return claims_all_entitled_benefits | reported_ctr diff --git a/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py b/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py index a59b1c969..4a999a301 100644 --- a/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py +++ b/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py @@ -2,13 +2,11 @@ class council_tax_less_benefit(Variable): - label = "Council Tax (less CTB)" + label = "Council Tax after Council Tax Reduction" documentation = ( - "Council Tax minus Council Tax Benefit / CTR. Kept as a public " - "convenience variable for downstream consumers (e.g. policyengine-app, " - "council-tax-ctr-map, policyengine-uk-data calibration targets) that " - "need the net council tax bill on a single line; not consumed inside " - "the core model itself." + "Gross Council Tax liability minus Council Tax Reduction, floored " + "at zero. During the CTR transition, the reduction may be modelled " + "for supported schemes or reported for unsupported schemes." ) entity = Household definition_period = YEAR @@ -16,9 +14,8 @@ class council_tax_less_benefit(Variable): unit = GBP def formula(household, period, parameters): - person = household.members - council_tax_benefit = household.sum( - person.benunit("council_tax_benefit", period) - * person("is_benunit_head", period) + return max_( + 0, + household("council_tax", period) + - household("council_tax_reduction", period), ) - return household("council_tax", period) - council_tax_benefit diff --git a/policyengine_uk/variables/input/consumption/property/council_tax.py b/policyengine_uk/variables/input/consumption/property/council_tax.py index 7cbccebbd..8f035610b 100644 --- a/policyengine_uk/variables/input/consumption/property/council_tax.py +++ b/policyengine_uk/variables/input/consumption/property/council_tax.py @@ -5,7 +5,11 @@ class council_tax(Variable): value_type = float entity = Household label = "Council Tax" - documentation: str = "Gross amount spent on Council Tax, before discounts" + documentation = ( + "Gross annual Council Tax liability before Council Tax Reduction. " + "This is currently supplied by the household dataset rather than " + "recomputed from local authority council tax schedules." + ) definition_period = YEAR unit = GBP quantity_type = FLOW