diff --git a/changelog.d/fix-abolish-council-tax-net.fixed.md b/changelog.d/fix-abolish-council-tax-net.fixed.md new file mode 100644 index 000000000..6d59903f2 --- /dev/null +++ b/changelog.d/fix-abolish-council-tax-net.fixed.md @@ -0,0 +1 @@ +- Include `council_tax_benefit` in `household_benefits`, `gov_spending`, `hbai_household_net_income`, and `pre_budget_change_household_benefits`. Previously CTR was absent from these aggregates, so abolishing council tax via `gov.contrib.abolish_council_tax` refunded the gross billed amount to households (and removed gross revenue from the government balance) rather than the net out-of-pocket amount, overstating household savings by about £4 billion in aggregate. diff --git a/policyengine_uk/tests/microsimulation/test_abolish_council_tax_reform.py b/policyengine_uk/tests/microsimulation/test_abolish_council_tax_reform.py index 81c14f70a..8f5a0bf67 100644 --- a/policyengine_uk/tests/microsimulation/test_abolish_council_tax_reform.py +++ b/policyengine_uk/tests/microsimulation/test_abolish_council_tax_reform.py @@ -37,3 +37,48 @@ def test_abolish_council_tax_removes_budget_impact(): assert reform_household_tax == baseline_household_tax - 2000 assert reform_gov_balance == baseline_gov_balance - 2000 assert reform_hbai == baseline_hbai + 2000 + + +CTR_SITUATION = { + "people": { + "person": { + "age": {YEAR: 40}, + "employment_income": {YEAR: 8000}, + "council_tax_benefit_reported": {YEAR: 800}, + } + }, + "benunits": {"benunit": {"members": ["person"]}}, + "households": { + "household": { + "members": ["person"], + "council_tax": {YEAR: 2000}, + "council_tax_band": {YEAR: "C"}, + } + }, +} + + +@pytest.mark.microsimulation +def test_abolish_council_tax_nets_out_council_tax_benefit(): + """CTR recipient should see net-CT saving, not gross-CT saving. + + Pre-reform: household pays £2,000 gross council tax and receives £800 + CTR, so out-of-pocket is £1,200 (the net bill). Abolishing council + tax saves them the £1,200, not the £2,000 — because the £800 CTR + rebate disappears alongside the council tax it was rebating. + """ + baseline = Microsimulation(situation=CTR_SITUATION) + reform = Microsimulation( + situation=CTR_SITUATION, + reform={"gov.contrib.abolish_council_tax": True}, + ) + + baseline_hbai = baseline.calculate("hbai_household_net_income", YEAR).sum() + reform_hbai = reform.calculate("hbai_household_net_income", YEAR).sum() + baseline_gov_balance = baseline.calculate("gov_balance", YEAR).sum() + reform_gov_balance = reform.calculate("gov_balance", YEAR).sum() + + # Net council tax = £2,000 - £800 CTR = £1,200 saving. + assert reform_hbai == pytest.approx(baseline_hbai + 1200, abs=1) + # Government loses net council tax revenue (gross - CTR forgone). + assert reform_gov_balance == pytest.approx(baseline_gov_balance - 1200, abs=1) diff --git a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py index ffb2577f6..2323a3e95 100644 --- a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py +++ b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py @@ -10,6 +10,7 @@ class pre_budget_change_household_benefits(Variable): unit = GBP adds = [ "child_benefit", + "council_tax_benefit", "esa_income", "housing_benefit", "income_support", diff --git a/policyengine_uk/variables/gov/gov_spending.py b/policyengine_uk/variables/gov/gov_spending.py index 25ac15362..eb3cb1270 100644 --- a/policyengine_uk/variables/gov/gov_spending.py +++ b/policyengine_uk/variables/gov/gov_spending.py @@ -10,6 +10,7 @@ class gov_spending(Variable): unit = GBP adds = [ "child_benefit", + "council_tax_benefit", "esa_income", "esa_contrib", "housing_benefit", @@ -57,3 +58,10 @@ class gov_spending(Variable): "nhs_spending", "carer_support_payment", ] + + def formula(household, period, parameters): + variables = list(gov_spending.adds) + abolish_council_tax = parameters.gov.contrib.abolish_council_tax(period) + if abolish_council_tax: + variables = [v for v in variables if v != "council_tax_benefit"] + return add(household, period, variables) diff --git a/policyengine_uk/variables/household/income/hbai_household_net_income.py b/policyengine_uk/variables/household/income/hbai_household_net_income.py index 5c33f72c8..fe4e515df 100644 --- a/policyengine_uk/variables/household/income/hbai_household_net_income.py +++ b/policyengine_uk/variables/household/income/hbai_household_net_income.py @@ -26,6 +26,7 @@ class hbai_household_net_income(Variable): "free_school_milk", "free_tv_licence_value", "child_benefit", + "council_tax_benefit", "esa_income", "esa_contrib", "housing_benefit", @@ -74,15 +75,13 @@ class hbai_household_net_income(Variable): def formula(household, period, parameters): abolish_council_tax = parameters.gov.contrib.abolish_council_tax(period) if abolish_council_tax: - return add( - household, - period, - hbai_household_net_income.adds, - ) - add( - household, - period, - [s for s in hbai_household_net_income.subtracts if s != "council_tax"], - ) + adds = [ + a for a in hbai_household_net_income.adds if a != "council_tax_benefit" + ] + subtracts = [ + s for s in hbai_household_net_income.subtracts if s != "council_tax" + ] + return add(household, period, adds) - add(household, period, subtracts) return add(household, period, hbai_household_net_income.adds) - add( household, period, hbai_household_net_income.subtracts ) diff --git a/policyengine_uk/variables/household/income/household_benefits.py b/policyengine_uk/variables/household/income/household_benefits.py index d7cd2faa2..138186ebc 100644 --- a/policyengine_uk/variables/household/income/household_benefits.py +++ b/policyengine_uk/variables/household/income/household_benefits.py @@ -10,6 +10,7 @@ class household_benefits(Variable): unit = GBP adds = [ "child_benefit", + "council_tax_benefit", "esa_income", "esa_contrib", "housing_benefit",