diff --git a/changelog.d/pc-child-amount-tax-credit-award.fixed.md b/changelog.d/pc-child-amount-tax-credit-award.fixed.md new file mode 100644 index 0000000000..445c489458 --- /dev/null +++ b/changelog.d/pc-child-amount-tax-credit-award.fixed.md @@ -0,0 +1,2 @@ +- Pay no Pension Credit child amount (SPC Regs 2002 Schedule IIA) to a family awarded child tax credit or working tax credit, including an award at a nil rate (reg 6(6)(d) and (11); TCA 2002 s.14(3)). The new variable `has_tax_credit_award` records the award. +- Pay no Schedule IIA amount before it began on 1 February 2019 (SI 2018/676 reg 2). The new parameter `gov.dwp.pension_credit.guarantee_credit.child.in_effect` dates it. diff --git a/policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/child/in_effect.yaml b/policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/child/in_effect.yaml new file mode 100644 index 0000000000..f8cca78047 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/child/in_effect.yaml @@ -0,0 +1,15 @@ +description: Whether the Pension Credit minimum guarantee includes the Schedule IIA additional amount for a child or qualifying young person. +values: + 2003-10-06: false + # SI 2018/676 reg 2(2)(c) and (4) added reg 6(6)(d) and Schedule IIA from + # 1 February 2019. Before then, support for a pensioner's child came through + # Child Tax Credit. + 2019-02-01: true +metadata: + unit: bool + label: Pension Credit child amount in effect + reference: + - title: The State Pension Credit Regulations 2002 reg. 6(6)(d) + href: https://www.legislation.gov.uk/uksi/2002/1792/regulation/6 + - title: The State Pension Credit (Additional Amount for Child or Qualifying Young Person) (Amendment) Regulations 2018 reg. 2 + href: https://www.legislation.gov.uk/uksi/2018/676/regulation/2 diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/guarantee_credit/child_amount_tax_credit_award.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/guarantee_credit/child_amount_tax_credit_award.yaml new file mode 100644 index 0000000000..2b8e58ff0e --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/guarantee_credit/child_amount_tax_credit_award.yaml @@ -0,0 +1,496 @@ +# No Schedule IIA child amount for a tax credit award. +# +# SPC Regs 2002 reg 6(6)(d) adds Schedule IIA's amount for a child or +# qualifying young person "except where paragraph (11) applies". Reg 6(11): +# "This paragraph applies in the case of a person who is awarded, or who is +# treated as having an award of, a tax credit under the Tax Credits Act." Reg +# 6(17): "a tax credit" includes a child tax credit and a working tax credit. +# Reg 6(6)(d), (11)-(17) and Schedule IIA came into force on 1 February 2019 +# (SI 2018/676 reg 2); the model takes a year's rules at the start of the +# fiscal year, so they apply from 2019-20. +# +# An award need not be positive: HMRC may award a tax credit at a nil rate +# (TCA 2002 s.14(3)). The model treats a family as awarded a tax credit when +# it claims one and meets its conditions other than the income test. Tax +# credits ended on 5 April 2025. +# +# Rates (DWP, Benefit and pension rates): +# - 2024-25: standard minimum guarantee £332.95 a week for a couple; Schedule +# IIA £76.79 for the eldest child born before 6 April 2017 (para 10). +# - 2025-26: £346.60 couple; £78.10 eldest child born before 6 April 2017. +# - 2019-20: £255.25 couple; £63.84 eldest child. 2018-19: £248.80 couple. +# - WTC 2024-25: basic element £2,435, couple element £2,500; threshold +# £7,955; withdrawal rate 41%. CTC child element £3,455; family element £545 +# (entered when the model uprated it; since #1925 the model's own value is +# the same). +# - Savings credit 2024-25: threshold £301.22 a week for a couple, so the +# maximum is 0.6 x (332.95 - 301.22) x 52 = £989.98 (SPCA 2002 s.3(7)). +# The model works in annual amounts and does not round the weekly +# maximum; DWP publishes it rounded, £19.04 a week (£990.08 over 52 +# weeks). +# - Capital: Pension Credit and pension-age Housing Benefit count £1 a week +# for each £500 over £10,000 (SPC Regs 2002 reg 15(6); SI 2006/214 +# reg 29(2)); pension-age Housing Benefit's capital limit is £16,000 (SI +# 2006/214 reg 43). +# WEEKS_IN_YEAR is 52. +# +# The couples in the 2024-25 and 2025-26 cases are aged 70 in 2024-25, so +# both reached pensionable age after 6 April 2016 and there is no savings +# credit (SPCA 2002 s.3(1)(a)). The 2018-19 and 2019-20 couples have no +# income, so no savings credit either. The Housing Benefit cases' couple is +# aged 80, so savings credit applies to it. The grandchild is 10 (11 in +# 2025-26), born before 6 April 2017. Pension Credit earnings are entered net +# of the SPC Regs Sch VI para 5 disregard (£10 a week for a couple), which +# the model does not apply. £10,000 of savings gives no Pension Credit tariff +# income, and £300 of interest uses up the £300 step-one disregard of tax +# credit income (SI 2002/2006 reg 3). No one pays income tax or National +# Insurance. + +- name: A Pension Credit couple awarded CTC and WTC gets no child amount + # Tax credit income: State Pension 8,000 + interest (300 - 300) + earnings + # 2,600 = £10,600. The older partner is over 60 and works 20 hours, which + # meets the WTC hours condition (WTC Regs 2002 reg 4(1), second condition). + # The family claims both credits, so it is awarded a tax credit and reg + # 6(11) removes the Schedule IIA amount. Minimum guarantee: 332.95 x 52 = + # £17,313.40. + # With the income test lifted (SI 2002/2008 reg 4(1)(d)): WTC 2,435 + 2,500 + # = £4,935, CTC 545 + 3,455 = £4,000. Pension Credit income: 8,000 + 2,080 + # + 4,935 = £15,015 (CTC is not Pension Credit income), so Guarantee Credit + # is 17,313.40 - 15,015 = £2,298.40 and the passport holds. + # (Paying the child amount as well would give 2,298.40 + 76.79 x 52 = + # £6,291.48.) + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 4_000 + savings_interest_income: 300 + child_tax_credit_reported: 1 + working_tax_credit_reported: 1 + grandparent_2: + age: 70 + is_claimant_or_partner: true + state_pension: 4_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_080 # 2,600 - 10 x 52 + CTC_family_element: 545 + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + has_tax_credit_award: true + child_minimum_guarantee_addition: 0 + minimum_guarantee: 17_313.40 + tax_credits_applicable_income: 0 + working_tax_credit: 4_935 + child_tax_credit: 4_000 + guarantee_credit: 2_298.40 + pension_credit: 2_298.40 + +- name: The same couple with no tax credit claim gets the child amount + # Schedule IIA para 10: 76.79 x 52 = £3,993.08. Minimum guarantee: + # 17,313.40 + 3,993.08 = £21,306.48. Pension Credit income: 8,000 + 2,080 = + # £10,080, so Guarantee Credit is £11,226.48. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 4_000 + savings_interest_income: 300 + grandparent_2: + age: 70 + is_claimant_or_partner: true + state_pension: 4_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_080 + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + has_tax_credit_award: false + child_minimum_guarantee_addition: 3_993.08 + minimum_guarantee: 21_306.48 + working_tax_credit: 0 + child_tax_credit: 0 + guarantee_credit: 11_226.48 + pension_credit: 11_226.48 + +- name: A WTC award alone also removes the child amount + # Reg 6(17): "a tax credit" includes a working tax credit. The first case's + # couple claiming WTC but not CTC: no Schedule IIA amount; the passported + # WTC of £4,935 leaves Guarantee Credit of £2,298.40, as before. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 4_000 + savings_interest_income: 300 + working_tax_credit_reported: 1 + grandparent_2: + age: 70 + is_claimant_or_partner: true + state_pension: 4_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_080 + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + has_tax_credit_award: true + child_minimum_guarantee_addition: 0 + tax_credits_applicable_income: 0 + working_tax_credit: 4_935 + child_tax_credit: 0 + pension_credit: 2_298.40 + +- name: An award at a nil rate is still an award + # Tax credit income: 9,000 + 8,000 + (300 - 300) + 3,500 = £20,500. WTC on + # the income test: 4,935 - 0.41 x (20,500 - 7,955) < 0, so nil. + # Pension Credit income: 17,000 + (3,500 - 520) = £19,980, above the + # £17,313.40 guarantee, so no Pension Credit, and with the passported WTC + # (£4,935) still none: the income test applies. + # The family has claimed WTC and meets its conditions, so it holds an award + # at a nil rate (TCA 2002 s.14(3)) and gets no Schedule IIA amount. Had the + # award followed a positive amount, the child amount would give Guarantee + # Credit of 21,306.48 - 19,980 = £1,326.48 with the income test still + # applied, though that Pension Credit would lift it. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + employment_income: 3_500 + weekly_hours: 20 + state_pension: 9_000 + savings_interest_income: 300 + working_tax_credit_reported: 1 + grandparent_2: + age: 70 + is_claimant_or_partner: true + state_pension: 8_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_980 # 3,500 - 10 x 52 + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + has_tax_credit_award: true + tax_credits_applicable_income: 20_500 + working_tax_credit: 0 + child_minimum_guarantee_addition: 0 + pension_credit: 0 + +- name: An award entered directly removes the child amount + # The award follows reported tax credits, so an award the reported amounts + # do not show (one reported as nil, or one treated as continuing under reg + # 6(12) and (13)) is entered as has_tax_credit_award. The second case's + # couple with an award entered: no Schedule IIA amount, so the minimum + # guarantee is £17,313.40 and Guarantee Credit 17,313.40 - 10,080 = + # £7,233.40. No tax credit is claimed, so none is paid. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 4_000 + savings_interest_income: 300 + grandparent_2: + age: 70 + is_claimant_or_partner: true + state_pension: 4_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_080 + has_tax_credit_award: true + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + child_minimum_guarantee_addition: 0 + minimum_guarantee: 17_313.40 + working_tax_credit: 0 + child_tax_credit: 0 + guarantee_credit: 7_233.40 + pension_credit: 7_233.40 + +- name: Without the child amount, a CTC award can end the Housing Benefit passport + # A couple aged 80 with a grandchild, claiming CTC, with State Pension of + # £9,000 each, £18,000 of savings and £12,000 a year of council rent. + # Pension Credit income: 18,000 + tariff income (18,000 - 10,000) / 500 = + # £16 a week, 16 x 52 = £832, so £18,832. + # With the CTC award there is no Schedule IIA amount: the minimum guarantee + # is £17,313.40, below income, so no Guarantee Credit. Savings credit + # (SPCA 2002 s.3(2)-(4)): A is the maximum, £989.98 (60% of qualifying + # income over the threshold, 0.6 x (18,832 - 301.22 x 52) = £1,901.14, is + # more); B = 0.4 x (18,832 - 17,313.40) = £607.44; so 989.98 - 607.44 = + # £382.54. CTC: 545 + 3,455 = £4,000, the income test lifted by the savings + # credit (SI 2002/2008 reg 4(1)(d)). + # Housing Benefit: with no Guarantee Credit there is no passport (SI + # 2006/214 reg 26), and capital of £18,000 is over the £16,000 limit, so + # none. The case enters the Housing Benefit claim: the model infers + # take-up from reported benefits, so a reported tax credit alone would + # leave Housing Benefit unclaimed and nil whatever the passport. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 80 + is_claimant_or_partner: true + state_pension: 9_000 + child_tax_credit_reported: 1 + grandparent_2: + age: 80 + is_claimant_or_partner: true + state_pension: 9_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + would_claim_housing_benefit: true + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 18_000 + rent: 12_000 + tenure_type: RENT_FROM_COUNCIL + output: + has_tax_credit_award: true + child_minimum_guarantee_addition: 0 + minimum_guarantee: 17_313.40 + pension_credit_income: 18_832 + guarantee_credit: 0 + savings_credit: 382.54 + pension_credit: 382.54 + child_tax_credit: 4_000 + housing_benefit_assessable_capital: 18_000 + housing_benefit: 0 + +- name: The same household with no tax credit claim keeps the passport + # Schedule IIA para 10: 76.79 x 52 = £3,993.08, so the minimum guarantee is + # 17,313.40 + 3,993.08 = £21,306.48 and Guarantee Credit 21,306.48 - 18,832 + # = £2,474.48. Savings credit: income is below the minimum guarantee, so B + # is nil and the credit is the maximum, £989.98. Pension Credit £3,464.46. + # Housing Benefit: the Guarantee Credit passport disregards all capital and + # income (SI 2006/214 reg 26), so the full eligible rent, £12,000. The + # Housing Benefit claim is entered as in the previous case. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 80 + is_claimant_or_partner: true + state_pension: 9_000 + grandparent_2: + age: 80 + is_claimant_or_partner: true + state_pension: 9_000 + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + would_claim_housing_benefit: true + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 18_000 + rent: 12_000 + tenure_type: RENT_FROM_COUNCIL + output: + has_tax_credit_award: false + child_minimum_guarantee_addition: 3_993.08 + minimum_guarantee: 21_306.48 + guarantee_credit: 2_474.48 + savings_credit: 989.98 + pension_credit: 3_464.46 + child_tax_credit: 0 + housing_benefit_assessable_capital: 0 + housing_benefit: 12_000 + +- name: With tax credits ended, a reported award no longer removes the child amount + # Tax credits ended on 5 April 2025, so there is no award in 2025-26. + # Schedule IIA: 78.10 x 52 = £4,061.20. Minimum guarantee: 346.60 x 52 + + # 4,061.20 = 18,023.20 + 4,061.20 = £22,084.40. Guarantee Credit: + # 22,084.40 - 10,080 = £12,004.40. The standard minimum guarantee is + # entered at its statutory amount: it was entered when the model projected + # 2025-26 by CPI, and since #1925 the model's own value is the same. + # The couple cannot claim Universal Credit when its tax credits close: both + # are over the qualifying age for Pension Credit (WRA 2012 s.4(1)(b)), and + # SI 2014/1230 reg 60A waives that only for a Working Tax Credit claimant + # who "is not entitled to an award of state pension credit" + # (reg 60A(1)(a)(iii)), which this couple is (the first case). The model's + # closure routing does not test that condition, so the case enters that the + # couple does not claim Universal Credit then. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 71 + is_claimant_or_partner: true + employment_income: 2_600 + weekly_hours: 20 + state_pension: 4_000 + savings_interest_income: 300 + child_tax_credit_reported: 1 + working_tax_credit_reported: 1 + grandparent_2: + age: 71 + is_claimant_or_partner: true + state_pension: 4_000 + grandchild: + age: 11 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + pension_credit_earnings: 2_080 + standard_minimum_guarantee: 18_023.20 # 346.60 x 52 + would_claim_uc_at_legacy_closure: false + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 10_000 + output: + has_tax_credit_award: false + working_tax_credit: 0 + child_tax_credit: 0 + child_minimum_guarantee_addition: 4_061.20 + minimum_guarantee: 22_084.40 + guarantee_credit: 12_004.40 + +- name: In 2019-20 the child amount applies, except with a tax credit award + # Schedule IIA from 1 February 2019. 2019-20: 63.84 x 52 = £3,319.68; + # couple guarantee 255.25 x 52 = £13,273. The first couple has a CTC award, + # the second none. + period: 2019 + absolute_error_margin: 0.01 + input: + people: + awarded_1: + age: 70 + is_claimant_or_partner: true + child_tax_credit_reported: 1 + awarded_2: + age: 70 + is_claimant_or_partner: true + awarded_grandchild: + age: 10 + is_claimant_or_partner: false + not_awarded_1: + age: 70 + is_claimant_or_partner: true + not_awarded_2: + age: 70 + is_claimant_or_partner: true + not_awarded_grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + awarded: + members: [awarded_1, awarded_2, awarded_grandchild] + would_claim_uc: false + not_awarded: + members: [not_awarded_1, not_awarded_2, not_awarded_grandchild] + would_claim_uc: false + households: + awarded: + members: [awarded_1, awarded_2, awarded_grandchild] + not_awarded: + members: [not_awarded_1, not_awarded_2, not_awarded_grandchild] + output: + has_tax_credit_award: [true, false] + child_minimum_guarantee_addition: [0, 3_319.68] + minimum_guarantee: [13_273, 16_592.68] + +- name: Before February 2019 there was no Schedule IIA amount + # 2018-19 starts on 6 April 2018, before SI 2018/676 added Schedule IIA. + # The couple guarantee is 248.80 x 52 = £12,937.60, with nothing for the + # grandchild, whom Child Tax Credit supported instead. + period: 2018 + absolute_error_margin: 0.01 + input: + people: + grandparent_1: + age: 70 + is_claimant_or_partner: true + grandparent_2: + age: 70 + is_claimant_or_partner: true + grandchild: + age: 10 + is_claimant_or_partner: false + benunits: + benunit: + members: [grandparent_1, grandparent_2, grandchild] + would_claim_uc: false + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + output: + has_tax_credit_award: false + child_minimum_guarantee_addition: 0 + minimum_guarantee: 12_937.60 diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_pension_credit_passport.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_pension_credit_passport.yaml index 14edc7aaaf..8de54538e5 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_pension_credit_passport.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits_pension_credit_passport.yaml @@ -206,10 +206,9 @@ # = £866.64, so £123.34. # CTC: £545 + £3,455 = £4,000, not 4,000 - 0.41 x 205 = £3,915.95. # SPC Regs 2002 reg 6(6)(d) and (11) give no Schedule IIA child amount to - # a person awarded a tax credit; it is entered because the model pays it - # (#2061). The CTC family element has been £545 since 2011-12; it was - # entered when the model uprated it, and since #1925 the model's own value - # is the same. + # a person awarded a tax credit. The CTC family element has been £545 since + # 2011-12; it was entered when the model uprated it, and since #1925 the + # model's own value is the same. period: 2024 absolute_error_margin: 0.01 input: @@ -233,7 +232,6 @@ members: [grandparent_1, grandparent_2, grandchild] would_claim_uc: false pension_credit_earnings: 2_480 # 3,000 - 10 x 52 - child_minimum_guarantee_addition: 0 CTC_family_element: 545 households: household: @@ -280,7 +278,6 @@ members: [grandparent_1, grandparent_2, grandchild] would_claim_uc: false pension_credit_earnings: 2_480 - child_minimum_guarantee_addition: 0 CTC_family_element: 0 working_tax_credit_run_on: false extended_childcare_entitlement_eligible: false diff --git a/policyengine_uk/tests/test_claimant_not_own_child_properties.py b/policyengine_uk/tests/test_claimant_not_own_child_properties.py index 6ebe6e2e94..19aba766f6 100644 --- a/policyengine_uk/tests/test_claimant_not_own_child_properties.py +++ b/policyengine_uk/tests/test_claimant_not_own_child_properties.py @@ -39,7 +39,12 @@ test_child_and_adult_definitions_properties.py covers. Tax credit eligibility is supplied too (is_CTC_eligible): without it both CTC disabled child elements are zero and invariants 2 and 5 would hold for them -trivially. +trivially. With it and the claim the model presumes, each unit would have a +tax credit award while tax credits ran, and an award removes the Pension +Credit child addition (SPC Regs 2002 reg 6(6)(d) and (11)). So the award is +entered as absent (has_tax_credit_award), and the invariants exercise the +addition in both test years: a unit with a child or qualifying young person +gets it, and one without gets none. """ from itertools import product @@ -126,7 +131,12 @@ def simulate(units, year): # Both CTC disabled child elements are zero for a unit that is not # eligible, and no generated unit reports a tax credit award. sim.set_input("is_CTC_eligible", year, [True] * len(units)) - sim.input_variables = [*people[0], "is_CTC_eligible"] + # That eligibility and the presumed claim would make a tax credit award, + # which removes the Pension Credit child addition (SPC Regs 2002 reg + # 6(6)(d) and (11)); no award is entered, so the addition stays under + # test for every unit with a dependant. + sim.set_input("has_tax_credit_award", year, [False] * len(units)) + sim.input_variables = [*people[0], "is_CTC_eligible", "has_tax_credit_award"] sim.reset_calculations() return sim diff --git a/policyengine_uk/tests/test_pension_credit_child_amount_tax_credit_properties.py b/policyengine_uk/tests/test_pension_credit_child_amount_tax_credit_properties.py new file mode 100644 index 0000000000..87929cd78a --- /dev/null +++ b/policyengine_uk/tests/test_pension_credit_child_amount_tax_credit_properties.py @@ -0,0 +1,354 @@ +"""No Pension Credit child amount for a tax credit award. + +SPC Regs 2002 reg 6(6)(d) adds the Schedule IIA amount for each child or +qualifying young person "except where paragraph (11) applies": where the +person "is awarded, or ... is treated as having an award of, a tax credit" +(reg 6(11)), child tax credit or working tax credit (reg 6(17)). Reg 6(6)(d) +and Schedule IIA apply from 1 February 2019 (SI 2018/676), so from 2019-20 in +the model. Tax credits ended on 5 April 2025. HMRC may award a tax credit at a +nil rate (TCA 2002 s.14(3)), so the award follows the claim and the +entitlement conditions other than the income test. + +Properties, for any family: + +1. The award is the claim and the conditions of entitlement, read from + would_claim_CTC, is_CTC_eligible, would_claim_WTC and is_WTC_eligible, in + sampled years with tax credits (2019-20, 2021-22 and 2024-25). This checks + the wiring: it cannot find an error in those variables, which infer a + claim from a positive reported award. +2. Differential: the child amount is nil where there is an award, and + otherwise equals the amount in a simulation with the award entered as + absent. That amount equals a separate Schedule IIA calculation (para + 9(1)(a) and para 10). The calculation reads the same rate parameters and + the model's dates of birth, so it checks the formula, not the rates or + the exact 6 April 2017 boundary. +3. After tax credits ended (2025-26 on), no family has an award, and the + child amount is the Schedule IIA amount. +4. Before 2019-20 the child amount is nil. +5. Removing the child amount never raises Pension Credit plus tax credits, + and changes nothing for a family with no award. Other support can fall: + losing Guarantee Credit ends the Housing Benefit passport (a pinned + family below and the YAML cases). +6. The results do not depend on the order in which the child amount, the + award, Pension Credit, the tax credit income test, Housing Benefit and + household income are requested, and no request raises a dependency cycle. +""" + +import numpy as np +from hypothesis import HealthCheck, example, given, settings +from hypothesis import strategies as st + +from policyengine_uk import Simulation + +TAX_CREDIT_YEARS = [2019, 2021, 2024] +NO_AWARD_YEARS = [2025, 2026] +BEFORE_SCHEDULE_IIA_YEARS = [2015, 2018] +SUPPORT = ["pension_credit", "working_tax_credit", "child_tax_credit"] +OUTPUTS = [ + "child_minimum_guarantee_addition", + "has_tax_credit_award", + "tax_credits_applicable_income", + "pension_credit", + "working_tax_credit", + "child_tax_credit", + "housing_benefit", +] + + +@st.composite +def adults(draw, partner): + age = draw(st.integers(40 if partner else 60, 90)) + earnings = draw(st.sampled_from([0, 0, 2_600, 6_000, 12_000])) + return { + "age": age, + "employment_income": earnings, + "weekly_hours": draw(st.sampled_from([16, 20, 30])) if earnings else 0, + "state_pension": ( + draw(st.sampled_from([0, 4_000, 6_000, 9_000, 11_500])) if age >= 66 else 0 + ), + "private_pension_income": draw(st.sampled_from([0, 0, 1_500, 5_000])), + "savings_interest_income": draw(st.sampled_from([0, 0, 300, 500])), + "working_tax_credit_reported": draw(st.sampled_from([0, 1])), + "child_tax_credit_reported": draw(st.sampled_from([0, 1, 1])), + } + + +@st.composite +def families(draw): + members = [draw(adults(partner=False))] + if draw(st.booleans()): + members.append(draw(adults(partner=True))) + children = [ + {"age": draw(st.integers(0, 15))} for _ in range(draw(st.integers(0, 3))) + ] + benunit = { + "would_claim_pc": draw(st.sampled_from([True, True, True, False])), + # A Universal Credit claim leaves a reported CTC award ineligible. + "would_claim_uc": draw(st.sampled_from([False, False, False, True])), + } + household = {"savings": draw(st.sampled_from([0, 5_000, 12_000, 20_000]))} + return members, children, benunit, household + + +def situation(families, year, benunit_inputs=None): + people, benunits, households = {}, {}, {} + for i, (members, children, benunit, household) in enumerate(families): + names = [] + for j, inputs in enumerate(members): + name = f"a{i}_{j}" + people[name] = {k: {year: v} for k, v in inputs.items()} + people[name]["is_claimant_or_partner"] = {year: True} + names.append(name) + for j, inputs in enumerate(children): + name = f"c{i}_{j}" + people[name] = {k: {year: v} for k, v in inputs.items()} + people[name]["is_claimant_or_partner"] = {year: False} + names.append(name) + benunits[f"b{i}"] = { + "members": names, + **{k: {year: v} for k, v in benunit.items()}, + **{k: {year: v} for k, v in (benunit_inputs or {}).items()}, + } + households[f"h{i}"] = { + "members": names, + **{k: {year: v} for k, v in household.items()}, + } + return {"people": people, "benunits": benunits, "households": households} + + +def calculate(drawn, year, order=OUTPUTS, benunit_inputs=None): + sim = Simulation(situation=situation(drawn, year, benunit_inputs)) + return {variable: sim.calculate(variable, year) for variable in order} + + +def schedule_iia_amount(drawn, year): + """Schedule IIA amounts from the parameters, for children who are not + disabled: para 9(1)(a) for each child, and para 10's higher amount for + the eldest if born before 6 April 2017. With the default six months + since the last birthday, the model puts the birth of a child aged a in + year y on 6 April of y - a, so before 6 April 2017 exactly when + y - a < 2017.""" + from policyengine_uk import CountryTaxBenefitSystem + + child = ( + CountryTaxBenefitSystem() + .parameters(year) + .gov.dwp.pension_credit.guarantee_credit.child + ) + amounts = [] + for _, children, _, _ in drawn: + ages = [c["age"] for c in children] + if not ages: + amounts.append(0) + continue + eldest_first = year - max(ages) < 2017 + weekly = len(ages) * child.addition + if eldest_first: + weekly += child.first.addition - child.addition + amounts.append(weekly * 52) + return np.array(amounts) + + +SETTINGS = settings( + max_examples=20, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +FAMILIES = st.lists(families(), min_size=1, max_size=6) + +# The YAML households (child_amount_tax_credit_award.yaml), run on every +# invocation. Pension Credit earnings are net of SPC Regs Sch VI para 5, and +# the CTC family element is entered at its statutory £545. The last two pay +# rent, so the order and differential checks reach Housing Benefit; they +# enter the Housing Benefit claim, which the model otherwise infers from +# reported benefits across the whole simulation. +_GRANDPARENT = { + "age": 70, + "employment_income": 2_600, + "weekly_hours": 20, + "state_pension": 4_000, + "savings_interest_income": 300, +} +_INPUTS = {"would_claim_uc": False, "pension_credit_earnings": 2_080} +_RENTED = {"savings": 18_000, "rent": 12_000, "tenure_type": "RENT_FROM_COUNCIL"} +YAML_FAMILIES = [ + # CTC and WTC with Pension Credit: passported, no child amount. + ( + [ + { + **_GRANDPARENT, + "child_tax_credit_reported": 1, + "working_tax_credit_reported": 1, + }, + {"age": 70, "state_pension": 4_000}, + ], + [{"age": 10}], + {**_INPUTS, "CTC_family_element": 545}, + {"savings": 10_000}, + ), + # No tax credit claim: the child amount. + ( + [_GRANDPARENT, {"age": 70, "state_pension": 4_000}], + [{"age": 10}], + _INPUTS, + {"savings": 10_000}, + ), + # WTC alone. + ( + [ + {**_GRANDPARENT, "working_tax_credit_reported": 1}, + {"age": 70, "state_pension": 4_000}, + ], + [{"age": 10}], + _INPUTS, + {"savings": 10_000}, + ), + # A WTC award at a nil rate. + ( + [ + { + **_GRANDPARENT, + "employment_income": 3_500, + "state_pension": 9_000, + "working_tax_credit_reported": 1, + }, + {"age": 70, "state_pension": 8_000}, + ], + [{"age": 10}], + {**_INPUTS, "pension_credit_earnings": 2_980}, + {"savings": 10_000}, + ), + # A CTC award ends the Guarantee Credit and its Housing Benefit passport. + ( + [ + {"age": 80, "state_pension": 9_000, "child_tax_credit_reported": 1}, + {"age": 80, "state_pension": 9_000}, + ], + [{"age": 10}], + {"would_claim_uc": False, "would_claim_housing_benefit": True}, + _RENTED, + ), + # The same household with no tax credit claim keeps the passport. + ( + [{"age": 80, "state_pension": 9_000}, {"age": 80, "state_pension": 9_000}], + [{"age": 10}], + {"would_claim_uc": False, "would_claim_housing_benefit": True}, + _RENTED, + ), +] + + +@SETTINGS +@given(FAMILIES, st.sampled_from(TAX_CREDIT_YEARS)) +@example(YAML_FAMILIES, 2024) +def test_award_removes_the_child_amount_and_nothing_else(drawn, year): + conditions = [ + "would_claim_CTC", + "is_CTC_eligible", + "would_claim_WTC", + "is_WTC_eligible", + ] + on = calculate(drawn, year, order=OUTPUTS + conditions) + # 1. The award is a claim plus the conditions of entitlement. + expected_award = (on["would_claim_CTC"] & on["is_CTC_eligible"]) | ( + on["would_claim_WTC"] & on["is_WTC_eligible"] + ) + assert (on["has_tax_credit_award"] == expected_award).all(), drawn + # 2. Differential, and the Schedule IIA amount from the parameters. + without = calculate(drawn, year, benunit_inputs={"has_tax_credit_award": False}) + expected = np.where( + on["has_tax_credit_award"], 0, without["child_minimum_guarantee_addition"] + ) + assert np.allclose(on["child_minimum_guarantee_addition"], expected), drawn + assert np.allclose( + without["child_minimum_guarantee_addition"], + schedule_iia_amount(drawn, year), + atol=0.01, + ), drawn + # 5. Removing the child amount never raises Pension Credit plus tax + # credits, and changes nothing without an award. + total_on = sum(on[v] for v in SUPPORT) + total_without = sum(without[v] for v in SUPPORT) + assert (total_on <= total_without + 0.01).all(), drawn + no_award = ~on["has_tax_credit_award"] + for variable in SUPPORT + ["tax_credits_applicable_income"]: + assert np.allclose(on[variable][no_award], without[variable][no_award]), ( + variable, + drawn, + ) + + +@SETTINGS +@given(FAMILIES, st.sampled_from(NO_AWARD_YEARS)) +def test_no_award_after_tax_credits_ended(drawn, year): + on = calculate(drawn, year) + assert not on["has_tax_credit_award"].any() + assert np.allclose( + on["child_minimum_guarantee_addition"], + schedule_iia_amount(drawn, year), + atol=0.01, + ), drawn + + +@SETTINGS +@given(FAMILIES, st.sampled_from(BEFORE_SCHEDULE_IIA_YEARS)) +def test_no_child_amount_before_schedule_iia(drawn, year): + on = calculate(drawn, year) + assert (on["child_minimum_guarantee_addition"] == 0).all(), drawn + + +@SETTINGS +@given(FAMILIES, st.sampled_from(TAX_CREDIT_YEARS + NO_AWARD_YEARS)) +@example(YAML_FAMILIES, 2024) +def test_results_do_not_depend_on_request_order(drawn, year): + forward = calculate(drawn, year) + backward = calculate(drawn, year, order=list(reversed(OUTPUTS))) + household_first = calculate(drawn, year, order=["household_net_income"] + OUTPUTS) + for variable in OUTPUTS: + assert np.allclose(forward[variable], backward[variable]), (variable, drawn) + assert np.allclose(forward[variable], household_first[variable]), ( + variable, + drawn, + ) + + +def test_yaml_families_reach_every_case(): + """The pinned examples are not vacuous: they hit each case of the rule.""" + year = 2024 + order = OUTPUTS + ["guarantee_credit", "savings_credit"] + on = calculate(YAML_FAMILIES, year, order=order) + without = calculate( + YAML_FAMILIES, + year, + order=order, + benunit_inputs={"has_tax_credit_award": False}, + ) + assert on["has_tax_credit_award"].tolist() == [ + True, + False, + True, + True, + True, + False, + ] + # The rule removes the child amount from each awarded family. + assert (without["child_minimum_guarantee_addition"] > 0).all() + assert np.allclose( + on["child_minimum_guarantee_addition"], + [0, 3_993.08, 0, 0, 0, 3_993.08], + ) + # Pension Credit, CTC and WTC are all paid to the first family. + for variable in SUPPORT: + assert on[variable][0] > 0, variable + # The nil-rate award: no WTC, no Pension Credit, no child amount. + assert on["working_tax_credit"][3] == 0 + assert on["pension_credit"][3] == 0 + assert without["pension_credit"][3] > 0 + # The fifth family: the award leaves savings credit but no Guarantee + # Credit, so no Housing Benefit passport; the sixth keeps both. + assert on["guarantee_credit"][4] == 0 < without["guarantee_credit"][4] + assert 0 < on["savings_credit"][4] < without["savings_credit"][4] + assert on["housing_benefit"][4] == 0 < without["housing_benefit"][4] + assert on["guarantee_credit"][5] > 0 + assert on["housing_benefit"][5] > 0 diff --git a/policyengine_uk/tests/test_tax_credits_pension_credit_passport_properties.py b/policyengine_uk/tests/test_tax_credits_pension_credit_passport_properties.py index 85764ec416..95cbe06ea7 100644 --- a/policyengine_uk/tests/test_tax_credits_pension_credit_passport_properties.py +++ b/policyengine_uk/tests/test_tax_credits_pension_credit_passport_properties.py @@ -147,8 +147,8 @@ def calculate(sim, year, order=OUTPUTS): # The YAML cases, run on every invocation: random draws seldom raise CTC or # hit the third row. Pension Credit earnings are net of SPC Regs Sch VI -# para 5; the CTC family element and the Sch IIA child amount are entered at -# their statutory values (see the YAML file). +# para 5; the CTC family element is entered at its statutory value (see the +# YAML file). _PENSIONER = { "age": 70, "employment_income": 2_600, @@ -186,7 +186,6 @@ def calculate(sim, year, order=OUTPUTS): [{"age": 10}], { "pension_credit_earnings": 2_480, - "child_minimum_guarantee_addition": 0, "CTC_family_element": 545, }, {"savings": 10_000}, diff --git a/policyengine_uk/variables/gov/dwp/has_tax_credit_award.py b/policyengine_uk/variables/gov/dwp/has_tax_credit_award.py new file mode 100644 index 0000000000..911e11f21c --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/has_tax_credit_award.py @@ -0,0 +1,40 @@ +from policyengine_uk.model_api import * + + +class has_tax_credit_award(Variable): + value_type = bool + entity = BenUnit + label = "has a tax credit award" + documentation = ( + "Whether the family is awarded child tax credit or working tax credit " + "for the year, at any rate. Entitlement depends on a claim (TCA 2002 " + "s.3(1)), which a couple makes jointly (s.3(3)(a)), so an award is " + "both members'. On a claim HMRC decide whether to make an award and " + "at what rate (s.14(1)), and may award a tax credit at a nil rate " + "(s.14(3)). So the award follows the claim and the conditions of " + "entitlement other than the income test, not a positive amount. The " + "model reads the claim and those conditions from reported awards: " + "is_CTC_eligible and is_WTC_eligible need a positive reported " + "amount, so an award reported as nil, or one treated as continuing " + "during renewal (SPC Regs 2002 reg 6(12)), is not inferred; enter " + "this variable for one. Tax credits ended on 5 April 2025, so there " + "are no awards after that." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/ukpga/2002/21/section/3", + "https://www.legislation.gov.uk/ukpga/2002/21/section/14", + ) + + def formula(benunit, period, parameters): + if not parameters(period).gov.dwp.tax_credits.active: + return benunit.filled_array(False) + # Neither condition reads Pension Credit, which reads this award + # (child_minimum_guarantee_addition), so no cycle arises. + child_tax_credit = benunit("would_claim_CTC", period) & benunit( + "is_CTC_eligible", period + ) + working_tax_credit = benunit("would_claim_WTC", period) & benunit( + "is_WTC_eligible", period + ) + return child_tax_credit | working_tax_credit diff --git a/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/child_minimum_guarantee_addition.py b/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/child_minimum_guarantee_addition.py index 456d065394..9cc98e916a 100644 --- a/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/child_minimum_guarantee_addition.py +++ b/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/child_minimum_guarantee_addition.py @@ -8,9 +8,17 @@ class child_minimum_guarantee_addition(Variable): definition_period = YEAR value_type = float unit = GBP - reference = "https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA" + reference = ( + "https://www.legislation.gov.uk/uksi/2002/1792/regulation/6", + "https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA", + ) def formula(benunit, period, parameters): + gc = parameters(period).gov.dwp.pension_credit.guarantee_credit + # SPC Regs 2002 reg 6(6)(d) and Schedule IIA apply from 1 February + # 2019 (SI 2018/676 reg 2). + if not gc.child.in_effect: + return benunit.empty_array() person = benunit.members is_child_or_qualifying_young_person = person( "is_child_or_qualifying_young_person_for_pension_credit", period @@ -26,7 +34,6 @@ def formula(benunit, period, parameters): ) + 1 ) - gc = parameters(period).gov.dwp.pension_credit.guarantee_credit first_child_born_before_cutoff = (child_index == 1) & ( date_of_birth < gc.child.first.born_before ) @@ -56,4 +63,16 @@ def formula(benunit, period, parameters): ) * WEEKS_IN_YEAR ) - return benunit.sum(per_child_amount) + # Reg 6(6)(d) applies Schedule IIA "except where paragraph (11) + # applies", which is "the case of a person who is awarded, or who is + # treated as having an award of, a tax credit" (reg 6(11)): child tax + # credit or working tax credit (reg 6(17)), so a working tax credit + # award alone, which need not include anything for the child, also + # removes the amount. Reg 6(12) and (13) treat last year's award as + # continuing from the start of a tax year until it is renewed or + # finalised, and reg 6(14) to (16) end the amount when an award is + # made late. The model has no renewal, finalisation or revision + # history, so neither is modelled; a deemed award can be entered as + # has_tax_credit_award. + has_tax_credit_award = benunit("has_tax_credit_award", period) + return benunit.sum(per_child_amount) * ~has_tax_credit_award