diff --git a/changelog.d/tax-credits-state-pension-step-one.fixed.md b/changelog.d/tax-credits-state-pension-step-one.fixed.md new file mode 100644 index 0000000000..6f44150c05 --- /dev/null +++ b/changelog.d/tax-credits-state-pension-step-one.fixed.md @@ -0,0 +1 @@ +Count the State Pension as step-one pension income in tax credit income, so the £300 disregard on step-one income applies to it (SI 2002/2006 regs 3(1), 5(1)(a) and 7(2); ITEPA 2003 s.577). diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_state_pension_step_one.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_state_pension_step_one.yaml new file mode 100644 index 0000000000..ea3ce3d8fd --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_state_pension_step_one.yaml @@ -0,0 +1,333 @@ +# State Pension is step-one pension income for tax credits, with the £300 +# disregard. +# +# SI 2002/2006 reg 3(1), Step One adds "the pension income (as defined in +# regulation 5(1)), the investment income ..., the property income ..., the +# foreign income ... and the notional income"; "If the result of this step is +# £300 or less, it is treated as nil. If the result of this step is more than +# £300, only the excess is taken into account in the following steps." Step Two +# adds employment income, "the social security income (as defined in +# regulation 7)", student income and miscellaneous income. +# +# Reg 5(1)(a): pension income means "any pension to which section 577 or 629 +# of ITEPA applies"; ITEPA 2003 s.577(1) applies to "the state pension". Reg +# 7(2): "Pensions under the Contributions and Benefits Act which are pension +# income by virtue of regulation 5(1)(a) are not social security income." +# Carer's Allowance (SSCBA 1992 s.70) and contributory ESA (Welfare Reform Act +# 2007 Part 1) are social security income (reg 7(1)(a)); Table 3 disregards +# neither. +# +# 2024-25 rates: +# - WTC basic element £2,435, couple element £2,500 (WTC Regs 2002 Sch 2). +# - Income threshold £7,955, or £19,995 for a claim with no WTC entitlement; +# withdrawal rate 41% (SI 2002/2008 regs 3 and 7, as uprated). +# - CTC child element £3,455; family element £545 (CTC Regs 2002 reg 7). +# - Standard minimum guarantee £218.15 a week single, £332.95 couple (SPC Regs +# 2002 reg 6). WEEKS_IN_YEAR is 52. +# +# Each pensioner reached State Pension age after 6 April 2016, so has no +# savings credit (SPCA 2002 s.3(1)(a)). The CTC family element is entered at +# £545, its statutory amount, which the model uprates. + +- name: State Pension is step one, so the £300 disregard applies to it + # Tax credit income: step one 9,500 - 300 = £9,200; step two £2,600; total + # £11,800. (With State Pension in step two it would be £12,100.) + # WTC: 2,435 - 0.41 x (11,800 - 7,955) = £858.55. + # Pension Credit income is at least 9,500 + 2,600 - 5 x 52 + 858.55 = + # £12,698.55, above the 218.15 x 52 = £11,343.80 guarantee, so no Pension + # Credit, with or without the passport. No income tax: £12,100 is within the + # personal allowance. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 9_500 + working_tax_credit_reported: 1 + benunits: + benunit: + members: [pensioner] + households: + household: + members: [pensioner] + output: + tax_credits_current_year_income: 11_800 + tax_credits_applicable_income: 11_800 + working_tax_credit: 858.55 + pension_credit: 0 + +- name: Other step-one income uses part of the £300, and State Pension the rest + # Step one: 9,500 + 100 - 300 = £9,300; total 9,300 + 2,600 = £11,900. + # (With State Pension in step two: 0 + 2,600 + 9,500 = £12,100.) + # WTC: 2,435 - 0.41 x (11,900 - 7,955) = £817.55. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 9_500 + savings_interest_income: 100 + working_tax_credit_reported: 1 + benunits: + benunit: + members: [pensioner] + households: + household: + members: [pensioner] + output: + tax_credits_current_year_income: 11_900 + working_tax_credit: 817.55 + pension_credit: 0 + +- name: Private pension of £300 or more already uses the disregard + # Step one: 8,500 + 1,000 - 300 = £9,200; total £11,800, as it would be with + # State Pension in step two: (1,000 - 300) + 2,600 + 8,500 = £11,800. + # WTC: 2,435 - 0.41 x (11,800 - 7,955) = £858.55. Pension Credit income + # includes the private pension: at least 8,500 + 1,000 + 2,340 + 858.55 = + # £12,698.55, so none. No income tax on £12,100. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 8_500 + private_pension_income: 1_000 + working_tax_credit_reported: 1 + benunits: + benunit: + members: [pensioner] + households: + household: + members: [pensioner] + output: + tax_credits_current_year_income: 11_800 + working_tax_credit: 858.55 + pension_credit: 0 + +- name: A joint claim has one £300 disregard, across both State Pensions + # Step one is the claimants' aggregate: 8,000 + 7,500 - 300 = £15,200 (not + # less £600). Total: 15,200 + 2,600 = £17,800. + # WTC maximum: 2,435 + 2,500 = £4,935; less 0.41 x (17,800 - 7,955) = + # £4,036.45; so £898.55. (With State Pension in step two: £18,100 and + # £775.55.) + # Pension Credit income is at least 15,500 + 2,600 - 10 x 52 + 898.55 = + # £18,478.55, above the 332.95 x 52 = £17,313.40 couple guarantee. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner_1: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 8_000 + working_tax_credit_reported: 1 + pensioner_2: + age: 70 + is_claimant_or_partner: true + state_pension: 7_500 + benunits: + benunit: + members: [pensioner_1, pensioner_2] + would_claim_uc: false + households: + household: + members: [pensioner_1, pensioner_2] + output: + tax_credits_current_year_income: 17_800 + working_tax_credit: 898.55 + pension_credit: 0 + +- name: A grandparent's Child Tax Credit is tapered on income with the State Pension in step one + # A grandparent aged 70 responsible for a grandchild aged 10, working 10 + # hours a week (no WTC: a single claimant with a child needs 16), claiming + # CTC only. + # Tax credit income: (11,000 - 300) + 9,500 = £20,200, above the £19,995 + # threshold. CTC: 545 + 3,455 = £4,000, less 0.41 x 205 = £84.05, so + # £3,915.95. (With State Pension in step two: £20,500 and £3,792.95.) + # Pension Credit income is above the guarantee: 11,000 + 9,500 less income + # tax of 0.2 x (20,500 - 12,570) = £1,586. SPC Regs reg 6(6)(d) and (11) + # give no child amount to a tax credit recipient; it is entered because the + # model applies neither. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent: + age: 70 + is_claimant_or_partner: true + employment_income: 9_500 + weekly_hours: 10 + state_pension: 11_000 + child_tax_credit_reported: 1 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent, grandchild] + would_claim_uc: false + child_minimum_guarantee_addition: 0 + CTC_family_element: 545 + households: + household: + members: [grandparent, grandchild] + output: + tax_credits_current_year_income: 20_200 + child_tax_credit: 3_915.95 + working_tax_credit: 0 + pension_credit: 0 + +- name: Where Pension Credit is paid without the passport, it falls by the WTC gained + # The case with no consistent passport state (see + # tax_credits_pension_credit_passport.yaml), with no savings interest. + # Tax credit income: (6,600 - 300) + 2,600 = £8,900. + # Passport: with the test lifted WTC is £2,435, and Pension Credit income + # 2,340 + 6,600 + 2,435 = £11,375 is above the £11,343.80 guarantee, so the + # income test stays. + # WTC: 2,435 - 0.41 x (8,900 - 7,955) = £2,047.55. Guarantee Credit: + # 11,343.80 - (2,340 + 6,600 + 2,047.55) = £356.25. + # With State Pension in step two, income is £9,200, WTC £1,924.55 and + # Pension Credit £479.25: the £123 more WTC is £123 less Guarantee Credit. + # Pension Credit earnings are entered net of the SPC Regs Sch VI para 5 + # disregard (£5 a week), which the model does not apply. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 6_600 + working_tax_credit_reported: 1 + benunits: + benunit: + members: [pensioner] + pension_credit_earnings: 2_340 + households: + household: + members: [pensioner] + output: + tax_credits_current_year_income: 8_900 + tax_credits_applicable_income: 8_900 + working_tax_credit: 2_047.55 + pension_credit: 356.25 + +- name: The State Pension disregard can bring a family within the childcare tax credit income limit + # England's two-year-old offer: a child whose parent is entitled to "child + # tax credit where that parent is not also entitled to working tax credit + # ... and has an annual gross income not exceeding £16,190" (SI 2014/2147 + # reg 1(2), "eligible child", as at 1 September 2024). + # Tax credit income: (11,490 - 300) + 5,000 = £16,190, within the limit. + # (With State Pension in step two: £16,490, above it.) + # CTC: £4,000, below the £19,995 threshold. Pension Credit income, + # 11,490 + 5,000 less income tax of 0.2 x (16,490 - 12,570) = £784, is above + # the guarantee. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent: + age: 70 + is_claimant_or_partner: true + employment_income: 5_000 + weekly_hours: 10 + state_pension: 11_490 + child_tax_credit_reported: 1 + grandchild: + age: 2 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent, grandchild] + would_claim_uc: false + child_minimum_guarantee_addition: 0 + CTC_family_element: 545 + working_tax_credit_run_on: false + extended_childcare_entitlement_eligible: false + households: + household: + members: [grandparent, grandchild] + country: ENGLAND + output: + tax_credits_current_year_income: 16_190 + child_tax_credit: 4_000 + pension_credit: 0 + meets_child_tax_credit_criteria_for_targeted_childcare_entitlement: true + targeted_childcare_entitlement_eligible: true + +- name: A State Pension within the £300 is wholly disregarded + # A pensioner whose only State Pension is £200 a year of graduated + # retirement benefit (ITEPA s.577(1)), with no other step-one income: step + # one is nil, and income is the £2,600 of earnings. Pension Credit is not + # claimed, so the income test applies; WTC is the £2,435 maximum. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + pensioner: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 200 + working_tax_credit_reported: 1 + benunits: + benunit: + members: [pensioner] + would_claim_pc: false + households: + household: + members: [pensioner] + output: + tax_credits_current_year_income: 2_600 + tax_credits_applicable_income: 2_600 + working_tax_credit: 2_435 + +- name: Carer's Allowance and contributory ESA stay in step two, with no disregard + # Social security income (reg 7(1)(a)): Carer's Allowance £81.90 x 52 = + # £4,258.80 and contribution-based ESA £5,000; Table 3 disregards neither. + # Step one is nil, so income is 4,258.80 + 5,000 = £9,258.80. + # CTC: £4,000, below the £19,995 threshold. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + carer: + age: 45 + is_claimant_or_partner: true + carers_allowance: 4_258.80 + child_tax_credit_reported: 1 + partner: + age: 45 + is_claimant_or_partner: true + esa_contrib_reported: 5_000 + child: + age: 8 + is_claimant_or_partner: false + benunits: + benunit: + members: [carer, partner, child] + would_claim_uc: false + CTC_family_element: 545 + households: + household: + members: [carer, partner, child] + output: + tax_credits_current_year_income: 9_258.80 + child_tax_credit: 4_000 diff --git a/policyengine_uk/tests/test_tax_credits_state_pension_step_one_properties.py b/policyengine_uk/tests/test_tax_credits_state_pension_step_one_properties.py new file mode 100644 index 0000000000..3aa316f160 --- /dev/null +++ b/policyengine_uk/tests/test_tax_credits_state_pension_step_one_properties.py @@ -0,0 +1,341 @@ +"""State Pension is step-one pension income for tax credits. + +SI 2002/2006 reg 3(1): Step One adds "the pension income (as defined in +regulation 5(1)), the investment income ..., the property income ..."; "If the +result of this step is £300 or less, it is treated as nil. If the result of +this step is more than £300, only the excess is taken into account". Pension +income includes "any pension to which section 577 ... of ITEPA applies" (reg +5(1)(a)), which is the State Pension (ITEPA 2003 s.577(1)), and reg 7(2) takes +it out of social security income, which is step two. + +Properties, for any family: + +1. Law oracle: tax_credits_current_year_income is the claimants' step-one + income (State Pension, private pension, savings interest, dividends and + property income) less £300, floored at nil, plus their step-two income + (earnings, trading income, Carer's Allowance, Carer Support Payment, + contribution-based JSA and ESA, Incapacity Benefit and miscellaneous + income), computed here directly from the inputs. +2. Against the previous formula, which put the State Pension in step two + (applied below as a reform): income never rises, and falls by exactly + min(State Pension, max(0, £300 - other step-one income)), so by at most + £300. It is unchanged where other step-one income is £300 or more. +3. State Pension is treated exactly as private pension income: moving an + amount from one to the other never changes the income. +4. Awards in 2024-25: WTC and CTC never fall, and together rise by at most + 41% of £300 plus the £26 minimum award. Guarantee Credit, which counts + working tax credit as income, never rises, and falls by no more than the + working tax credit gained. +5. In 2025-26, with no tax credit awards, every award and Pension Credit + equal the previous formula's. +6. Every component of social_security_income is counted exactly once: the + State Pension in step one, the rest in step two. +""" + +import numpy as np +from hypothesis import HealthCheck, event, example, given, settings +from hypothesis import strategies as st +from policyengine_core.reforms import Reform + +from policyengine_uk import Simulation +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp import tax_credits_current_year_income as tc +from policyengine_uk.variables.gov.hmrc.income_tax.social_security_income import ( + social_security_income, +) + +ACTIVE_YEAR = 2024 # 2024-25, the last year of tax credit awards +INACTIVE_YEAR = 2025 +DISREGARD = 300 +TAPER = 0.41 +MIN_AWARD = 26 +TOL = 0.05 # float32 arithmetic on sums up to about £100,000 +OTHER_STEP_ONE = [ + "private_pension_income", + "savings_interest_income", + "dividend_income", + "property_income", +] +STEP_TWO = [ + "employment_income", + "self_employment_income", + "carers_allowance", + "carer_support_payment", + "jsa_contrib_reported", + "esa_contrib_reported", + "incapacity_benefit_reported", + "miscellaneous_income", +] +AWARDS = ["working_tax_credit", "child_tax_credit", "tax_credits"] +PENSION_CREDIT = ["guarantee_credit", "savings_credit", "pension_credit"] + + +class previous_tax_credits_current_year_income(tc.tax_credits_current_year_income): + # The formula before this change: the State Pension in step two, through + # social_security_income. + def formula(benunit, period, parameters): + person = benunit.members + members = person("is_claimant_or_partner", period) | person( + "is_child_or_qualifying_young_person_for_child_tax_credit", period + ) + TC = parameters(period).gov.dwp.tax_credits + income = add_for_members(benunit, period, OTHER_STEP_ONE, members) + income = max_(income - TC.means_test.non_earned_disregard, 0) + step_two = [ + "employment_income", + "self_employment_income", + "social_security_income", + "miscellaneous_income", + ] + return income + add_for_members(benunit, period, step_two, members) + + +previous_tax_credits_current_year_income.__name__ = "tax_credits_current_year_income" + + +class state_pension_in_step_two(Reform): + def apply(self): + self.update_variable(previous_tax_credits_current_year_income) + + +# Amounts around the £300 disregard, so other step-one income falls below, at +# and above it. +small = st.sampled_from([0, 0, 0, 50, 150, 299.99, 300, 300.01, 1_000, 5_000]) + + +def sometimes(amount): + return st.sampled_from([0, 0, 0, amount]) + + +@st.composite +def adults(draw): + earnings = draw(st.sampled_from([0, 0, 2_600, 6_000, 12_000, 25_000])) + # Half the adults have no other step-one income, where the change bites. + other_step_one = draw(st.booleans()) + return { + "age": draw(st.integers(25, 90)), + "state_pension": draw( + st.one_of( + st.sampled_from([0, 0, 200, 4_000, 6_600, 9_500, 11_500]), + st.floats(0, 15_000, allow_nan=False, allow_infinity=False), + ) + ), + **{s: draw(small) if other_step_one else 0 for s in OTHER_STEP_ONE}, + "employment_income": earnings, + "weekly_hours": draw(st.sampled_from([10, 16, 20, 30])) if earnings else 0, + "self_employment_income": draw(sometimes(3_000)), + "carers_allowance": draw(sometimes(4_258.80)), + "carer_support_payment": draw(sometimes(4_258.80)), + "jsa_contrib_reported": draw(sometimes(3_000)), + "esa_contrib_reported": draw(sometimes(5_000)), + "incapacity_benefit_reported": draw(sometimes(4_000)), + "miscellaneous_income": draw(sometimes(500)), + "working_tax_credit_reported": draw(st.sampled_from([0, 1])), + "child_tax_credit_reported": draw(st.sampled_from([0, 1])), + } + + +@st.composite +def families(draw): + members = [draw(adults())] + if draw(st.booleans()): + members.append(draw(adults())) + children = [ + {"age": draw(st.integers(0, 15))} for _ in range(draw(st.integers(0, 2))) + ] + benunit = {"would_claim_pc": draw(st.sampled_from([True, True, False]))} + household = {"savings": draw(st.sampled_from([0, 5_000, 12_000]))} + return members, children, benunit, household + + +def situation(units): + def at(value): + return {year: value for year in (ACTIVE_YEAR, INACTIVE_YEAR)} + + people, benunits, households = {}, {}, {} + for i, (members, children, benunit, household) in enumerate(units): + names = [] + for j, inputs in enumerate(members): + name = f"a{i}_{j}" + people[name] = {k: at(v) for k, v in inputs.items()} + people[name]["is_claimant_or_partner"] = at(True) + names.append(name) + for j, inputs in enumerate(children): + name = f"c{i}_{j}" + people[name] = {k: at(v) for k, v in inputs.items()} + people[name]["is_claimant_or_partner"] = at(False) + names.append(name) + benunits[f"b{i}"] = { + "members": names, + "would_claim_uc": at(False), + **{k: at(v) for k, v in benunit.items()}, + } + households[f"h{i}"] = { + "members": names, + **{k: at(v) for k, v in household.items()}, + } + return {"people": people, "benunits": benunits, "households": households} + + +def law(members): + """Reg 3(1) steps one and two, from the claimants' inputs.""" + state_pension = sum(m["state_pension"] for m in members) + other_step_one = sum(m[s] for m in members for s in OTHER_STEP_ONE) + step_two = sum(m[s] for m in members for s in STEP_TWO) + income = max(state_pension + other_step_one - DISREGARD, 0) + step_two + # The previous formula's loss of the disregard on State Pension. + change = min(state_pension, max(0, DISREGARD - other_step_one)) + return income, change + + +def moved_to_private_pension(unit): + members, children, benunit, household = unit + moved = [ + { + **m, + "state_pension": 0, + "private_pension_income": m["private_pension_income"] + m["state_pension"], + } + for m in members + ] + return moved, children, benunit, household + + +def calculate(sim, variables, year): + return {v: np.asarray(sim.calculate(v, year)) for v in variables} + + +SETTINGS = settings( + max_examples=25, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + +# The YAML households (tax_credits_state_pension_step_one.yaml), run as +# examples: State Pension wholly, partly and not at all within the £300, and +# the case where Pension Credit is paid without the passport. +PENSIONER = { + **{k: 0 for k in ["state_pension", *OTHER_STEP_ONE, *STEP_TWO]}, + "age": 70, + "employment_income": 2_600, + "weekly_hours": 20, + "working_tax_credit_reported": 1, + "child_tax_credit_reported": 0, +} +YAML_FAMILIES = [ + [ + ( + [{**PENSIONER, "state_pension": 9_500}], + [], + {"would_claim_pc": True}, + {"savings": 0}, + ) + ], + [ + ( + [{**PENSIONER, "state_pension": 9_500, "savings_interest_income": 100}], + [], + {"would_claim_pc": True}, + {"savings": 0}, + ) + ], + [ + ( + [{**PENSIONER, "state_pension": 8_500, "private_pension_income": 1_000}], + [], + {"would_claim_pc": True}, + {"savings": 0}, + ) + ], + [ + ( + [{**PENSIONER, "state_pension": 6_600}], + [], + {"would_claim_pc": True}, + {"savings": 0}, + ) + ], + [ + ( + [{**PENSIONER, "state_pension": 200}], + [], + {"would_claim_pc": False}, + {"savings": 0}, + ) + ], +] + + +@SETTINGS +@given(st.lists(families(), min_size=1, max_size=6)) +@example(YAML_FAMILIES[0]) +@example(YAML_FAMILIES[1]) +@example(YAML_FAMILIES[2]) +@example(YAML_FAMILIES[3]) +@example(YAML_FAMILIES[4]) +def test_state_pension_is_step_one_income(units): + n = len(units) + sit = situation(units + [moved_to_private_pension(u) for u in units]) + new = Simulation(situation=sit) + old = Simulation(situation=sit) + old.apply_reform(state_pension_in_step_two) + + income = "tax_credits_current_year_income" + outputs = [income, *AWARDS, *PENSION_CREDIT] + new_values = calculate(new, outputs, ACTIVE_YEAR) + old_values = calculate(old, outputs, ACTIVE_YEAR) + new_inactive = calculate(new, outputs, INACTIVE_YEAR) + old_inactive = calculate(old, outputs, INACTIVE_YEAR) + + def gain(v): + return new_values[v][:n] - old_values[v][:n] + + for i, (members, *_rest) in enumerate(units): + expected, change = law(members) + context = (units[i], {k: v[i] for k, v in new_values.items()}) + # 1. The law, computed from the inputs. + assert abs(new_values[income][i] - expected) < TOL, context + # 2. Against the previous formula. + assert abs(old_values[income][i] - new_values[income][i] - change) < TOL, ( + context + ) + assert 0 <= change <= DISREGARD + # 3. State Pension is treated as private pension income. + assert abs(new_values[income][n + i] - new_values[income][i]) < TOL, context + # 5. No awards from 2025-26: only the income figure can differ. + for v in [*AWARDS, *PENSION_CREDIT]: + assert abs(new_inactive[v][i] - old_inactive[v][i]) < TOL, (v, context) + assert new_inactive["tax_credits"][i] == 0 + assert abs(old_inactive[income][i] - new_inactive[income][i] - change) < TOL + + # 4. Awards in 2024-25. + for v in ["working_tax_credit", "child_tax_credit", "tax_credits"]: + assert (gain(v) > -TOL).all(), (v, units) + assert (gain("tax_credits") <= TAPER * DISREGARD + MIN_AWARD + TOL).all(), units + assert (gain("guarantee_credit") < TOL).all(), units + assert (-gain("guarantee_credit") <= gain("working_tax_credit") + TOL).all(), units + + # Coverage (pytest --hypothesis-show-statistics). + for i, (members, *_rest) in enumerate(units): + change = law(members)[1] + event( + "income change: " + + ("none" if change == 0 else "£300" if change == DISREGARD else "partial") + ) + for v in ["working_tax_credit", "child_tax_credit"]: + if gain(v)[i] > TOL: + event(f"{v} raised") + if gain("guarantee_credit")[i] < -TOL: + event("guarantee_credit lowered") + + +def test_social_security_income_is_counted_once(): + # Reg 7(2): the State Pension is pension income, not social security + # income. Every other component of the model's social_security_income is + # reg 7 social security income. A new component must be classified here. + components = set(social_security_income.adds) + assert "state_pension" in tc.STEP_ONE_INCOME + assert "state_pension" not in tc.SOCIAL_SECURITY_INCOME + assert components == set(tc.SOCIAL_SECURITY_INCOME) | {"state_pension"} + assert not set(tc.STEP_ONE_INCOME) & set(tc.SOCIAL_SECURITY_INCOME) diff --git a/policyengine_uk/variables/gov/dwp/tax_credits_current_year_income.py b/policyengine_uk/variables/gov/dwp/tax_credits_current_year_income.py index cbddd40cfc..5678bfbe28 100644 --- a/policyengine_uk/variables/gov/dwp/tax_credits_current_year_income.py +++ b/policyengine_uk/variables/gov/dwp/tax_credits_current_year_income.py @@ -1,5 +1,42 @@ from policyengine_uk.model_api import * +# Step one of SI 2002/2006 reg 3(1): pension income (reg 5), investment income +# (reg 10), property income (reg 11), foreign income (reg 12) and notional +# income (reg 13). Pension income includes "any pension to which section 577 +# ... of ITEPA applies" (reg 5(1)(a)), and ITEPA 2003 s.577(1) applies to "the +# state pension", so the State Pension is step one, with the £300 disregard. +# private_pension_income is pension income (reg 5(1)(b), (d): ITEPA ss.569, +# 579A) or, for a foreign pension, foreign income (reg 12), which is also step +# one. savings_interest_income and dividend_income are investment income (reg +# 10(1)(a), (d)), and property_income is property income. +STEP_ONE_INCOME = [ + "state_pension", + "private_pension_income", + "savings_interest_income", + "dividend_income", + "property_income", +] + +# Social security income (reg 7): benefits payable under the Contributions and +# Benefits Act, the Jobseekers Act 1995 and Part 1 of the Welfare Reform Act +# 2007 (reg 7(1)(a)), and Carer Support Payment, made under s.28 of the Social +# Security (Scotland) Act 2018 (reg 7(1)(ab), from 15 March 2023). It is the +# model's social_security_income without the State Pension: "Pensions under the +# Contributions and Benefits Act which are pension income by virtue of +# regulation 5(1)(a) are not social security income" (reg 7(2)). The model's +# other benefits are disregarded by reg 7(3) Table 3, or are not under an Act +# reg 7(1) lists (Pension Credit, Universal Credit). Two partial disregards +# are not modelled: short-term lower-rate Incapacity Benefit and Incapacity +# Benefit after Invalidity Benefit (item 14), and contribution-based JSA above +# the ITEPA s.674 taxable maximum (item 16). +SOCIAL_SECURITY_INCOME = [ + "incapacity_benefit", + "jsa_contrib_reported", + "esa_contrib_reported", + "carers_allowance", + "carer_support_payment", +] + class tax_credits_current_year_income(Variable): value_type = float @@ -18,6 +55,9 @@ class tax_credits_current_year_income(Variable): reference = ( "https://www.legislation.gov.uk/ukpga/2002/21/section/7", "https://www.legislation.gov.uk/uksi/2002/2006/regulation/3", + "https://www.legislation.gov.uk/uksi/2002/2006/regulation/5", + "https://www.legislation.gov.uk/uksi/2002/2006/regulation/7", + "https://www.legislation.gov.uk/ukpga/2003/1/section/577", ) def formula(benunit, period, parameters): @@ -30,18 +70,19 @@ def formula(benunit, period, parameters): "is_child_or_qualifying_young_person_for_child_tax_credit", period ) TC = parameters(period).gov.dwp.tax_credits - STEP_1_COMPONENTS = [ - "private_pension_income", - "savings_interest_income", - "dividend_income", - "property_income", - ] - income = add_for_members(benunit, period, STEP_1_COMPONENTS, members) + income = add_for_members(benunit, period, STEP_ONE_INCOME, members) + # "If the result of this step is £300 or less, it is treated as nil. If + # the result of this step is more than £300, only the excess is taken + # into account in the following steps." The £300 is per claim, not per + # joint claimant. income = max_(income - TC.means_test.non_earned_disregard, 0) + # Step two: employment income (reg 4), social security income (reg 7) + # and miscellaneous income (reg 18). Trading income is step four, which + # adds it to the same total. STEP_2_COMPONENTS = [ "employment_income", "self_employment_income", - "social_security_income", + *SOCIAL_SECURITY_INCOME, "miscellaneous_income", ] bi = parameters(period).gov.contrib.ubi_center.basic_income