diff --git a/changelog.d/1986.fixed.md b/changelog.d/1986.fixed.md new file mode 100644 index 0000000000..3d2f2dd6b0 --- /dev/null +++ b/changelog.d/1986.fixed.md @@ -0,0 +1 @@ +- Targeted (two-year-old) childcare and the Kingston upon Thames, Merton, Newham and Westminster Council Tax Reduction schemes now read Universal Credit earned income before the work allowance (`uc_earned_income_before_work_allowance`), as SI 2014/2147 reg 1(3)(a) and the schemes require. diff --git a/docs/book/programs/gov/dfe/targeted-childcare-entitlement.ipynb b/docs/book/programs/gov/dfe/targeted-childcare-entitlement.ipynb index 3b41a8f8a5..0c1c2cb9d4 100644 --- a/docs/book/programs/gov/dfe/targeted-childcare-entitlement.ipynb +++ b/docs/book/programs/gov/dfe/targeted-childcare-entitlement.ipynb @@ -28,7 +28,7 @@ "\n", "#### Eligibility conditions\n", "Families may also qualify based on income thresholds if they receive:\n", - "* **Universal Credit** with household earned income of **£15,400 or less** per year\n", + "* **Universal Credit** with earned income of **£15,400 or less** per year (combined for a couple), as Universal Credit calculates it: earnings after the person's own tax, National Insurance and pension contributions, and before the work allowance (`uc_earned_income_before_work_allowance`)\n", "* **Tax Credits** (Working Tax Credit or Child Tax Credit) with household income of **£16,190 or less** per year\n", "\n", "#### Geographic scope\n", diff --git a/policyengine_uk/tests/policy/baseline/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.yaml b/policyengine_uk/tests/policy/baseline/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.yaml new file mode 100644 index 0000000000..85809ad22e --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.yaml @@ -0,0 +1,247 @@ +# Targeted (two-year-old) childcare, Universal Credit route. Local Authority +# (Duty to Secure Early Years Provision Free of Charge) Regulations 2014 +# reg. 1(2), definition of "eligible child" (a)(vii), and reg. 1(3)-(4): +# https://www.legislation.gov.uk/uksi/2014/2147/regulation/1 +# +# The parent must be entitled to universal credit with earned income not +# exceeding the applicable amount. Reg. 1(3)(a): "earned income" means income +# for the purposes of Chapter 2 of Part 6 of the UC Regulations 2013, which +# is net of the person's own tax, NI and pension contributions and comes +# before the work allowance (reg. 22, Part 3). Reg. 1(3)(c) reads the limit +# against a couple's combined income. The limit here is 15,400 a year. +# In 2026-27 the personal allowance and the primary threshold are 12,570. + +- name: "#1986 counterexample: earnings of 17,000 exceed the limit" + # Tax (17,000 - 12,570) x 20% = 886; NI (17,000 - 12,570) x 8% = 354.40; + # earned income 15,759.60 > 15,400. After the 5,124 work allowance it + # would be 10,635.60, which is how the model wrongly passed it before. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 17_000 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 15_759.60 + uc_earned_income: 10_635.60 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false + +- name: Earned income just under the limit meets the criterion + # Tax (16,500 - 12,570) x 20% = 786; NI 3,930 x 8% = 314.40; earned income + # 15,399.60 <= 15,400. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 16_500 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 15_399.60 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: true + +- name: Earned income just over the limit does not + # Tax 3,931 x 20% = 786.20; NI 3,931 x 8% = 314.48; earned income + # 15,400.32 > 15,400. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 16_501 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 15_400.32 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false + +- name: A couple's combined earned income is tested + # Reg. 1(3)(c): 8,000 + 8,000 = 16,000 > 15,400, although each partner is + # under the limit alone. + period: 2026 + input: + people: + parent1: + age: 30 + employment_income: 8_000 + parent2: + age: 30 + employment_income: 8_000 + child: + age: 2 + benunits: + benunit: + members: [parent1, parent2, child] + would_claim_uc: true + households: + household: + members: [parent1, parent2, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 16_000 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false + +- name: Combined earned income exactly at the limit meets the criterion + # "not exceeding": 7,700 + 7,700 = 15,400 <= 15,400. Each is under the + # personal allowance and primary threshold, so no tax or NI. + period: 2026 + input: + people: + parent1: + age: 30 + employment_income: 7_700 + parent2: + age: 30 + employment_income: 7_700 + child: + age: 2 + benunits: + benunit: + members: [parent1, parent2, child] + would_claim_uc: true + households: + household: + members: [parent1, parent2, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 15_400 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: true + +- name: Income treated as earned under the minimum income floor counts + # Reg. 1(3)(a) uses UC Part 6 Chapter 2, which includes reg. 62. The + # partner who is not the child's carer has profits of 3,000 and is subject + # to the floor (35 hours at the national living wage), so the couple's + # earned income is far above 15,400, although actual earnings are 3,000. + # The couple nominate the other partner as the child's responsible carer + # (reg. 86); the responsible carer of a child under 3 is not subject to + # the floor (uc_work_related_group_apart_from_earnings). + period: 2026 + input: + people: + self_employed_partner: + age: 30 + self_employment_income: 3_000 + uc_is_in_startup_period: false + uc_is_responsible_carer: false + carer: + age: 30 + uc_is_responsible_carer: true + child: + age: 2 + benunits: + benunit: + members: [self_employed_partner, carer, child] + would_claim_uc: true + households: + household: + members: [self_employed_partner, carer, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_mif_applies: [true, false, false] + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false + +- name: Without pension contributions, earnings of 16,800 exceed the limit + # Tax 4,230 x 20% = 846; NI 4,230 x 8% = 338.40; earned income 15,615.60. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 16_800 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + uc_earned_income_before_work_allowance: 15_615.60 + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false + +- name: Pension contributions come off earned income for the limit + # Reg. 55(5)(a): the 1,000 contribution brings earned income to at most + # 16,800 - 1,000 - 338.40 - 646 = 14,815.60 (with tax relief at source), + # under 15,400. + period: 2026 + input: + people: + parent: + age: 30 + employment_income: 16_800 + employee_pension_contributions: 1_000 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + meets_universal_credit_criteria_for_targeted_childcare_entitlement: true + +- name: No Universal Credit entitlement, no Universal Credit route + period: 2026 + input: + people: + parent: + age: 30 + employment_income: 5_000 + child: + age: 2 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + households: + household: + members: [parent, child] + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + output: + meets_universal_credit_criteria_for_targeted_childcare_entitlement: false 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 daaec21e26..6bffa6850d 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 @@ -215,7 +215,7 @@ claims_all_entitled_benefits: true uc_maximum_amount: 5_000 uc_income_reduction: 2_500 - uc_earned_income: 6_000 + uc_earned_income_before_work_allowance: 6_000 uc_unearned_income: 0 households: household: @@ -228,6 +228,43 @@ universal_credit: 2_500 council_tax_reduction: 1_100 +- name: "Merton UC claimant's income counts earnings before the work allowance" + # #1986. Single parent, child aged 6, earning 12,000 (under the personal + # allowance and primary threshold, so earned income is 12,000), council rent, + # council tax 1,800. The award has a housing costs element, so the work + # allowance is 427 x 12 = 5,124 and UC = maximum - 55% x (12,000 - 5,124) = + # maximum - 3,781.80. Income (scheme para. 37) is earned income plus the + # award: 12,000 + maximum - 3,781.80, which exceeds the applicable amount + # (the maximum, para. 28) by 8,218.20. CTR = 1,800 - 20% x 8,218.20 = + # 156.36. Deducting the work allowance from income would have given 1,181.16. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 12_000 + child: + age: 6 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + claims_all_entitled_benefits: true + households: + household: + members: [parent, child] + country: ENGLAND + region: LONDON + local_authority: MERTON + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 12_000 + council_tax_reduction: 156.36 + - name: Merton UC claimant uses UC-reported capital period: 2026 absolute_error_margin: 0.01 @@ -371,7 +408,7 @@ claims_all_entitled_benefits: true uc_maximum_amount: 5_000 uc_income_reduction: 2_500 - uc_earned_income: 6_000 + uc_earned_income_before_work_allowance: 6_000 uc_unearned_income: 0 households: household: @@ -384,6 +421,43 @@ universal_credit: 2_500 council_tax_reduction: 1_100 +- name: "Kingston upon Thames UC claimant's income counts earnings before the work allowance" + # #1986. Single parent, child aged 6, earning 12,000 (under the personal + # allowance and primary threshold, so earned income is 12,000), council rent, + # council tax 1,800. The award has a housing costs element, so the work + # allowance is 427 x 12 = 5,124 and UC = maximum - 55% x (12,000 - 5,124) = + # maximum - 3,781.80. Income (scheme para. 37) is earned income plus the + # award: 12,000 + maximum - 3,781.80, which exceeds the applicable amount + # (the maximum, para. 28) by 8,218.20. CTR = 1,800 - 20% x 8,218.20 = + # 156.36. Deducting the work allowance from income would have given 1,181.16. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 12_000 + child: + age: 6 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + claims_all_entitled_benefits: true + households: + household: + members: [parent, child] + country: ENGLAND + region: LONDON + local_authority: KINGSTON_UPON_THAMES + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 12_000 + council_tax_reduction: 156.36 + - name: Kingston upon Thames UC claimant uses UC-reported capital period: 2026 absolute_error_margin: 0.01 @@ -542,6 +616,41 @@ output: council_tax_reduction: 867.92 +- name: Newham UC claimant's income counts earnings before the work allowance + # #1986. Single parent, child aged 6, earning 4,000, council rent, council + # tax 1,800. Earnings are under the 5,124 work allowance, so UC is the + # maximum. Income is 4,000 + maximum, 4,000 over the applicable amount, so + # CTR = 70% x 1,800 - 25% x 4,000 = 260. Deducting the work allowance from + # income would have left no excess and given 1,260. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 4_000 + child: + age: 6 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + claims_all_entitled_benefits: true + households: + household: + members: [parent, child] + country: ENGLAND + region: LONDON + local_authority: NEWHAM + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 4_000 + uc_earned_income: 0 + council_tax_reduction: 260 + - name: Newham UC claimant uses UC-reported capital against the local limit period: 2026 absolute_error_margin: 0.01 @@ -686,7 +795,7 @@ claims_all_entitled_benefits: true uc_maximum_amount: 5_000 uc_income_reduction: 2_500 - uc_earned_income: 6_000 + uc_earned_income_before_work_allowance: 6_000 uc_unearned_income: 0 households: household: @@ -699,6 +808,43 @@ universal_credit: 2_500 council_tax_reduction: 1_100 +- name: "Westminster UC claimant's income counts earnings before the work allowance" + # #1986. Single parent, child aged 6, earning 12,000 (under the personal + # allowance and primary threshold, so earned income is 12,000), council rent, + # council tax 1,800. The award has a housing costs element, so the work + # allowance is 427 x 12 = 5,124 and UC = maximum - 55% x (12,000 - 5,124) = + # maximum - 3,781.80. Income (scheme para. 37) is earned income plus the + # award: 12,000 + maximum - 3,781.80, which exceeds the applicable amount + # (the maximum, para. 28) by 8,218.20. CTR = 1,800 - 20% x 8,218.20 = + # 156.36. Deducting the work allowance from income would have given 1,181.16. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + employment_income: 12_000 + child: + age: 6 + benunits: + benunit: + members: [parent, child] + would_claim_uc: true + claims_all_entitled_benefits: true + households: + household: + members: [parent, child] + country: ENGLAND + region: LONDON + local_authority: WESTMINSTER + tenure_type: RENT_FROM_COUNCIL + rent: 9_600 + council_tax: 1_800 + savings: 0 + output: + uc_earned_income_before_work_allowance: 12_000 + council_tax_reduction: 156.36 + - name: Westminster UC claimant uses UC-reported capital period: 2026 absolute_error_margin: 0.01 diff --git a/policyengine_uk/tests/test_uc_earned_income_before_work_allowance_properties.py b/policyengine_uk/tests/test_uc_earned_income_before_work_allowance_properties.py new file mode 100644 index 0000000000..e40c9a449a --- /dev/null +++ b/policyengine_uk/tests/test_uc_earned_income_before_work_allowance_properties.py @@ -0,0 +1,177 @@ +"""Property-based tests for Universal Credit earned income before the work +allowance, and the two rules that read it (#1986, items 2 and 3). + +Targeted (two-year-old) childcare tests earned income "for the purposes of +Chapter 2 of Part 6 of the Universal Credit Regulations 2013" (SI 2014/2147 +reg. 1(3)(a)), and the Kingston upon Thames, Merton, Newham and Westminster +Council Tax Reduction schemes take the Secretary of State's calculation of the +claimant's income (Default Scheme para. 37). Neither subtracts the work +allowance, which is a step in the award (UC Regs 2013 reg. 22(1)(b)). + +Invariants, for single people and couples with a child aged 2, earnings, +self-employment and pension contributions, in 2025, 2026 and 2027: + +1. Differential against the award: uc_earned_income equals + max(0, uc_earned_income_before_work_allowance - uc_work_allowance), so the + award's earned income is unchanged by this PR. +2. Differential against the formula this replaced: the childcare criterion + now implies the criterion main used (earned income after the work + allowance at or below the limit), because earned income before the work + allowance is never lower. +3. Monotone: more employment income for one adult never turns the childcare + criterion on. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_uk import Simulation + +PROPERTY_SETTINGS = settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +YEARS = [2025, 2026, 2027] +TENURES = ["RENT_FROM_COUNCIL", "RENT_FROM_HA", "RENT_PRIVATELY"] +VARIABLES = [ + "uc_earned_income", + "uc_earned_income_before_work_allowance", + "uc_work_allowance", + "universal_credit", + "meets_universal_credit_criteria_for_targeted_childcare_entitlement", +] + + +@st.composite +def adult(draw): + return dict( + age=draw(st.integers(18, 60)), + employment_income=draw( + st.one_of(st.just(0.0), st.floats(0, 40_000), st.floats(14_000, 19_000)) + ), + self_employment_income=draw(st.one_of(st.just(0.0), st.floats(0, 10_000))), + pension_contributions=draw(st.one_of(st.just(0.0), st.floats(0, 2_000))), + ) + + +@st.composite +def families(draw): + return dict( + adults=[draw(adult()) for _ in range(draw(st.sampled_from([1, 2])))], + tenure=draw(st.sampled_from(TENURES)), + rent=draw(st.floats(4_000, 20_000)), + ) + + +units = st.lists(families(), min_size=1, max_size=6) + + +def situation(units, year, bump=None): + """One simulation holding every family. ``bump`` is (family, adult, + amount) of extra employment income.""" + people, benunits, households = {}, {}, {} + for i, unit in enumerate(units): + names = [] + for j, a in enumerate(unit["adults"]): + person = {k: {year: v} for k, v in a.items()} + # Roles are given, not inferred; neither adult is under the + # minimum income floor (a start-up period), so the floor does + # not move earned income here. + person["is_claimant_or_partner"] = {year: True} + person["uc_is_in_startup_period"] = {year: True} + if bump is not None and bump[:2] == (i, j): + person["employment_income"] = {year: a["employment_income"] + bump[2]} + people[f"p{i}_{j}"] = person + names.append(f"p{i}_{j}") + people[f"c{i}"] = {"age": {year: 2}, "is_claimant_or_partner": {year: False}} + names.append(f"c{i}") + benunits[f"b{i}"] = {"members": names, "would_claim_uc": {year: True}} + households[f"h{i}"] = { + "members": names, + "tenure_type": {year: unit["tenure"]}, + "rent": {year: unit["rent"]}, + "region": {year: "NORTH_EAST"}, + } + return {"people": people, "benunits": benunits, "households": households} + + +def calculate(units, year, **kwargs): + sim = Simulation(situation=situation(units, year, **kwargs)) + values = {v: np.asarray(sim.calculate(v, year)) for v in VARIABLES} + values["limit"] = float( + sim.tax_benefit_system.parameters( + f"{year}-01-01" + ).gov.dfe.targeted_childcare_entitlement.income_limit.universal_credit + ) + return values + + +@PROPERTY_SETTINGS +@given(units=units, year=st.sampled_from(YEARS)) +def test_award_earned_income_is_before_work_allowance_less_the_allowance(units, year): + v = calculate(units, year) + expected = np.maximum( + 0, v["uc_earned_income_before_work_allowance"] - v["uc_work_allowance"] + ) + np.testing.assert_allclose(v["uc_earned_income"], expected, atol=0.01) + assert ( + v["uc_earned_income_before_work_allowance"] >= v["uc_earned_income"] - 0.01 + ).all() + + +@PROPERTY_SETTINGS +@given(units=units, year=st.sampled_from(YEARS)) +def test_childcare_criterion_implies_the_old_after_allowance_test(units, year): + v = calculate(units, year) + old = (v["universal_credit"] > 0) & (v["uc_earned_income"] <= v["limit"]) + new = v["meets_universal_credit_criteria_for_targeted_childcare_entitlement"] + assert not (new & ~old).any(), units + # And the new test is exactly the regulation's: UC in payment and earned + # income before the work allowance at or below the limit. + expected = (v["universal_credit"] > 0) & ( + v["uc_earned_income_before_work_allowance"] <= v["limit"] + ) + assert (new == expected).all(), units + + +@PROPERTY_SETTINGS +@given( + units=units, + year=st.sampled_from(YEARS), + data=st.data(), +) +def test_more_pay_never_turns_the_childcare_criterion_on(units, year, data): + i = data.draw(st.integers(0, len(units) - 1)) + j = data.draw(st.integers(0, len(units[i]["adults"]) - 1)) + amount = data.draw(st.floats(100, 5_000)) + before = calculate(units, year) + after = calculate(units, year, bump=(i, j, amount)) + criterion = "meets_universal_credit_criteria_for_targeted_childcare_entitlement" + assert not (after[criterion] & ~before[criterion]).any(), (units, i, j, amount) + + +def test_crossing_the_limit_by_one_pound_of_pay(): + """A fixed crossing, so the monotone property is not vacuous: a single + parent aged 30 earning £16,500 (£15,399.60 after tax and NI) meets the + 2026-27 criterion, and £16,501 (£15,400.32) does not.""" + family = dict( + adults=[ + dict( + age=30, + employment_income=16_500.0, + self_employment_income=0.0, + pension_contributions=0.0, + ) + ], + tenure="RENT_FROM_COUNCIL", + rent=9_600.0, + ) + criterion = "meets_universal_credit_criteria_for_targeted_childcare_entitlement" + under = calculate([family], 2026) + over = calculate([family], 2026, bump=(0, 0, 1.0)) + assert under["universal_credit"][0] > 0 and over["universal_credit"][0] > 0 + assert abs(under["uc_earned_income_before_work_allowance"][0] - 15_399.60) < 0.01 + assert under[criterion][0] and not over[criterion][0] diff --git a/policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.py b/policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.py index 52a75c07be..b8e4c7f41e 100644 --- a/policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.py +++ b/policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.py @@ -5,7 +5,19 @@ class meets_universal_credit_criteria_for_targeted_childcare_entitlement(Variabl value_type = bool entity = BenUnit label = "meets Universal Credit criteria for targeted childcare entitlement" + documentation = ( + "Whether the parents are entitled to Universal Credit with earned " + "income, as Universal Credit calculates it and before the work " + "allowance, not exceeding the limit." + ) definition_period = YEAR + reference = dict( + title=( + "Local Authority (Duty to Secure Early Years Provision Free of " + "Charge) Regulations 2014 reg. 1(2), (3) and (4)" + ), + href="https://www.legislation.gov.uk/uksi/2014/2147/regulation/1", + ) def formula(benunit, period, parameters): p = parameters(period).gov.dfe.targeted_childcare_entitlement @@ -13,11 +25,13 @@ def formula(benunit, period, parameters): # Check if receiving Universal Credit uc = benunit("universal_credit", period) - # For Universal Credit recipients, we must use the definition of "earned income" - # The Local Authority (Duty to Secure Early Years Provision Free of Charge) (Amendment) Regulations 2018 - part 2.c - # https://www.legislation.gov.uk/uksi/2018/146/made - - # UC-specific earned income as specified by 2(c)(3)(a) of the Regulations - earned_income = benunit("uc_earned_income", period) + # Reg. 1(3)(a): "earned income" means income for the purposes of + # Chapter 2 of Part 6 of the Universal Credit Regulations 2013, and + # reg. 1(3)(c) reads the limit against a couple's combined income. + # Chapter 2 is each person's earnings after their own tax, National + # Insurance and pension contributions, with the minimum income floor + # (reg. 62). The work allowance is in reg. 22 (Part 3, the award), so + # it is not deducted here. + earned_income = benunit("uc_earned_income_before_work_allowance", period) return (uc > 0) & (earned_income <= p.income_limit.universal_credit) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py index 9a69a049ac..640a2b2cac 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py @@ -28,11 +28,9 @@ def formula(benunit, period, parameters): # Reg. 22(1)(b) deducts "the claimant's earned income (or, in the case # of joint claimants, their combined earned income)". Each claimant's # earned income is net of their own deductions (reg. 55(5), reg. - # 57(2)); the work allowance then comes off the combined earned - # income before the taper. - claimants = benunit.members("is_uc_assessed_claimant", period) - earned_income = add_for_members( - benunit, period, ["uc_individual_earned_income"], claimants - ) + # 57(2)), summed over the claimants + # (uc_earned_income_before_work_allowance); the work allowance then + # comes off the combined earned income before the taper. + earned_income = benunit("uc_earned_income_before_work_allowance", period) work_allowance = benunit("uc_work_allowance", period) return max_(0, earned_income - work_allowance) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income_before_work_allowance.py b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income_before_work_allowance.py new file mode 100644 index 0000000000..2ae3f44cbf --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income_before_work_allowance.py @@ -0,0 +1,43 @@ +from policyengine_uk.model_api import * + + +class uc_earned_income_before_work_allowance(Variable): + value_type = float + entity = BenUnit + label = "Universal Credit earned income before the work allowance" + documentation = ( + "The claimant's earned income, or joint claimants' combined earned " + "income, as Universal Credit calculates it (UC Regs 2013 Part 6 " + "Chapter 2): each claimant's earnings less their own pension " + "contributions, income tax and National Insurance, or the amount the " + "minimum income floor treats them as having. Earnings of a child, a " + "qualifying young person or anyone else in the benefit unit who is " + "not a claimant do not count. The work allowance, which belongs to " + "the award calculation in reg. 22, is not deducted. Rules that refer " + "to the Universal Credit calculation of earned income use this amount." + ) + definition_period = YEAR + unit = GBP + reference = [ + dict( + title="Universal Credit Regulations 2013 reg. 52", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/52", + ), + dict( + title="Universal Credit Regulations 2013 reg. 22(1)(b)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/22", + ), + dict( + title="Welfare Reform Act 2012 s. 8(3) and (4)", + href="https://www.legislation.gov.uk/ukpga/2012/5/section/8", + ), + ] + + def formula(benunit, period, parameters): + # Reg. 22(1)(b) takes "the claimant's earned income (or, in the case + # of joint claimants, their combined earned income)", so only the + # claimants' earned income counts. + claimants = benunit.members("is_uc_assessed_claimant", period) + return add_for_members( + benunit, period, ["uc_individual_earned_income"], claimants + ) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py index 52524b2828..5d96830c62 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/_legacy.py @@ -36,8 +36,19 @@ def legacy_council_tax_reduction( universal_credit = benunit("universal_credit", period) has_uc_award = applicant_has_universal_credit(benunit, period) uc_applicable_amount = benunit("uc_maximum_amount", period) + # The schemes use "the calculation or estimate of the amount of the + # income of the applicant ... made by the Secretary of State for the + # purpose of determining the award of universal credit", plus the award + # (Council Tax Reduction Schemes (Default Scheme) (England) Regulations + # 2012, Sch. para. 37(1) and (3)(a); Kingston upon Thames and Merton + # reproduce it, Newham refers to the same calculation, and Westminster's + # scheme is based on the Default Scheme). That income is + # earned income after tax, National Insurance and pension contributions + # and unearned income. The work allowance and taper are steps in the + # award (UC Regs 2013 reg. 22), not in the income, so the work allowance + # is not deducted here. uc_applicable_income = ( - benunit("uc_earned_income", period) + benunit("uc_earned_income_before_work_allowance", period) + benunit("uc_unearned_income", period) + universal_credit )