Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/council-tax-reduction-merton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Merton working-age Council Tax Reduction.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
description: Maximum share of eligible Council Tax liability covered for working-age households under the Merton Council Tax Support scheme.
values:
2026-04-01: 1
metadata:
unit: /1
period: year
label: Merton Council Tax Support maximum support rate
reference:
- title: The London Borough of Merton Local Council Tax Support Scheme Rules 2026
href: https://www.merton.gov.uk/sites/default/files/2026-03/Council%20Tax%20Reduction%20Scheme%20L%20B%20Merton%202026-27.pdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
description: Capital limit for working-age households under the Merton Council Tax Support scheme.
values:
2026-04-01: 16_000
metadata:
unit: currency-GBP
period: year
label: Merton Council Tax Support capital limit
reference:
- title: The London Borough of Merton Local Council Tax Support Scheme Rules 2026
href: https://www.merton.gov.uk/sites/default/files/2026-03/Council%20Tax%20Reduction%20Scheme%20L%20B%20Merton%202026-27.pdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
description: Withdrawal rate for working-age households under the Merton Council Tax Support scheme.
values:
2026-04-01: 0.2
metadata:
unit: /1
period: year
label: Merton Council Tax Support withdrawal rate
reference:
- title: The London Borough of Merton Local Council Tax Support Scheme Rules 2026
href: https://www.merton.gov.uk/sites/default/files/2026-03/Council%20Tax%20Reduction%20Scheme%20L%20B%20Merton%202026-27.pdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
description: Weekly non-dependant deduction schedule under the Merton 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: Merton Council Tax Support non-dependant deduction schedule
reference:
- title: The London Borough of Merton Local Council Tax Support Scheme Rules 2026
href: https://www.merton.gov.uk/sites/default/files/2026-03/Council%20Tax%20Reduction%20Scheme%20L%20B%20Merton%202026-27.pdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
description: Minimum weekly hours for remunerative work under the Merton Council Tax Support non-dependant deduction rules.
values:
2026-04-01: 16
metadata:
unit: hour
period: week
label: Merton Council Tax Support remunerative work hours
reference:
- title: The London Borough of Merton Local Council Tax Support Scheme Rules 2026
href: https://www.merton.gov.uk/sites/default/files/2026-03/Council%20Tax%20Reduction%20Scheme%20L%20B%20Merton%202026-27.pdf
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,161 @@
council_tax_reduction_scheme_supported: true
simulated_council_tax_reduction_benunit: 1_800
council_tax_reduction: 1_800

- name: Merton 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: MERTON
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: Merton 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: MERTON
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: Merton 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: 15_000
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: MERTON
council_tax: 1_800
savings: 0
output:
council_tax_reduction_scheme_supported: true
council_tax_reduction: 1_248.80

- name: Merton exempts UC non-dependants with no earned income
period: 2026
absolute_error_margin: 0.01
input:
people:
claimant:
age: 35
non_dep:
age: 25
weekly_hours: 16
benunits:
claimant_benunit:
members: [claimant]
would_claim_uc: false
claims_all_entitled_benefits: true
non_dep_benunit:
members: [non_dep]
would_claim_uc: true
households:
household:
members: [claimant, non_dep]
country: ENGLAND
local_authority: MERTON
council_tax: 1_800
savings: 0
output:
council_tax_reduction: 1_800

- name: Merton 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: MERTON
council_tax: 1_800
savings: 0
output:
universal_credit: 2_500
council_tax_reduction: 1_100

- name: Merton 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: MERTON
council_tax: 1_800
savings: 20_000
output:
uc_assessable_capital: 0
council_tax_reduction: 1_800
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from policyengine_uk.model_api import *
from policyengine_uk.variables.household.demographic.highest_education import (
EducationType,
)


def is_full_time_student_non_dep(person, period):
return (person("current_education", period) != EducationType.NOT_IN_EDUCATION) | (
person("in_HE", period)
)


def legacy_council_tax_reduction(
benunit,
period,
ctr,
working_age,
non_dep_deductions_variable,
):
is_household_head_benunit = benunit("benunit_contains_household_head", period)
would_claim = benunit("would_claim_council_tax_reduction", period)
applicable_amount = benunit("council_tax_reduction_applicable_amount", period)
applicable_income = benunit("council_tax_reduction_applicable_income", period)
universal_credit = benunit("universal_credit", period)
has_uc_award = universal_credit > 0
uc_applicable_amount = benunit("uc_maximum_amount", period)
uc_applicable_income = (
benunit("uc_earned_income", period)
+ benunit("uc_unearned_income", period)
+ universal_credit
)
applicable_amount = where(has_uc_award, uc_applicable_amount, applicable_amount)
applicable_income = where(has_uc_award, uc_applicable_income, applicable_income)
relevant_income_based_benefit = benunit(
"council_tax_reduction_relevant_income_based_benefit",
period,
)
liability = benunit.household(
"council_tax_reduction_maximum_eligible_liability", period
)
non_dep_deductions = benunit(non_dep_deductions_variable, period)
excess_income = max_(0, applicable_income - applicable_amount)
excess_income = where(
relevant_income_based_benefit & ~has_uc_award, 0, excess_income
)
preliminary_award = max_(
0,
liability * ctr.maximum_support_rate
- excess_income * ctr.means_test.withdrawal_rate
- non_dep_deductions,
)
capital = where(
has_uc_award,
benunit("uc_assessable_capital", period),
benunit.household("savings", period),
)
capital_eligible = capital <= ctr.means_test.capital_limit
return (
working_age
* is_household_head_benunit
* would_claim
* capital_eligible
* preliminary_award
)


def local_non_dep_deductions(
benunit,
period,
individual_deduction_variable,
one_deduction_for_uc_couples=True,
):
deductions = benunit.members(individual_deduction_variable, period)
deduction_for_benunit = benunit.max(deductions)
if not one_deduction_for_uc_couples:
has_uc = benunit("universal_credit", period) > 0
deduction_for_benunit = where(
has_uc,
benunit.sum(deductions),
deduction_for_benunit,
)
is_benunit_head = benunit.members("is_benunit_head", period)
deductions_to_count = is_benunit_head * benunit.project(deduction_for_benunit)
deductions_in_household = benunit.max(
benunit.members.household.sum(deductions_to_count)
)
return deductions_in_household - deduction_for_benunit


def normal_gross_income_non_dep_deduction(
person,
period,
ctr,
working_age,
exempt_income_based_benefits=True,
exempt_uc_no_earned_income=True,
):
gross_income_components = [
"employment_income",
"self_employment_income",
"property_income",
"private_pension_income",
"savings_interest_income",
"dividend_income",
"state_pension",
]
earned_income_components = [
"employment_income",
"self_employment_income",
]
gross_income = add(person, period, gross_income_components)
earned_income = add(person, period, earned_income_components)
weekly_benunit_gross_income = person.benunit.sum(gross_income) / WEEKS_IN_YEAR
weekly_benunit_earned_income = person.benunit.sum(earned_income) / WEEKS_IN_YEAR
benunit_weekly_hours = person.benunit.max(person("weekly_hours", period))
in_remunerative_work = (
benunit_weekly_hours >= ctr.non_dep_deduction.remunerative_work_hours
)
weekly_deduction = where(
in_remunerative_work,
ctr.non_dep_deduction.amount.calc(weekly_benunit_gross_income),
ctr.non_dep_deduction.amount.calc(0),
)
claimant_exempt = person.household(
"council_tax_reduction_household_has_non_dep_exemption", period
)
full_time_student = is_full_time_student_non_dep(person, period)
income_based_benefit = (
(person.benunit("income_support", period) > 0)
| (person.benunit("jsa_income", period) > 0)
| (person.benunit("esa_income", period) > 0)
| (person.benunit("pension_credit", period) > 0)
)
has_uc = person.benunit("universal_credit", period) > 0
no_earned_income = weekly_benunit_earned_income <= 0
exempt = (
claimant_exempt
| full_time_student
| (exempt_income_based_benefits & income_based_benefit)
| (exempt_uc_no_earned_income & has_uc & no_earned_income)
)
return working_age * where(exempt, 0.0, weekly_deduction * WEEKS_IN_YEAR)
Loading
Loading