diff --git a/changelog.d/ctc-family-element-2017-cutoff.fixed.md b/changelog.d/ctc-family-element-2017-cutoff.fixed.md new file mode 100644 index 0000000000..b0c7a483a8 --- /dev/null +++ b/changelog.d/ctc-family-element-2017-cutoff.fixed.md @@ -0,0 +1 @@ +Fix the Child Tax Credit family element: hold it at £545 a year from 2003-04 (it was never uprated, but the model CPI-uprated it from 2016, giving £721.57 in 2024-25), pay it only to families with a child or qualifying young person born before 6 April 2017 (Tax Credits Act 2002 s.9(2)(a), CTC Regs 2002 reg 7(2)(a)), and add the £545 baby addition for a child under one before 6 April 2011 (reg 7(3)(a) as made). diff --git a/docs/book/programs/gov/dwp/tax-credits.md b/docs/book/programs/gov/dwp/tax-credits.md index 284dd56994..4c28e9b8b3 100644 --- a/docs/book/programs/gov/dwp/tax-credits.md +++ b/docs/book/programs/gov/dwp/tax-credits.md @@ -59,7 +59,7 @@ benunit worked. PolicyEngine computes it via `ctc_entitlement`, gated by | Element | Parameter | |---------|-----------| -| Family | `family_element.yaml` (extinct from 2017 for new claims) | +| Family | `family_element.yaml`: £545 a year from 2003-04 to 2024-25, never uprated. Until 2010-11 it was £1,090 with a child under one (`family_element_baby_addition.yaml`). From 2017-18 it is included only where a child or qualifying young person was born before 6 April 2017 (`eligibility/family_element_born_before.yaml`; Tax Credits Act 2002 s.9(2)(a), CTC Regs 2002 reg 7(2)(a)) | | Per qualifying child | `child_element.yaml` | | Disabled child addition | `dis_child_element.yaml` | | Severely disabled child addition | `severe_dis_child_element.yaml` | diff --git a/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element.yaml b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element.yaml index 1031f19d2b..83a916beb8 100644 --- a/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element.yaml +++ b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element.yaml @@ -1,11 +1,29 @@ description: Child Tax Credit family element. values: - 2016-04-06: - value: 545 + # £545 a year from the start of tax credits on 6 April 2003 until they ended + # on 5 April 2025: CTC Regs 2002 reg 7(3)(b) as made ("in any other case"), + # then reg 7(3) as substituted by SI 2011/1035 reg 2(2) from 6 April 2011. + # Until 5 April 2011, reg 7(3)(a) set £1,090 where a child was under one + # (family_element_baby_addition). Apart from that 2011 substitution, the Tax + # Credits Up-rating Regulations from 2004 to 2024 amended only paragraphs (4) + # and (5), and the point-in-time texts of reg 7 for 2003-04, 2010-11, + # 2011-12, 2016-17, 2017-18 and 2024-25 all give £545. The amount was never + # uprated and is not projected. + 2003-04-06: 545 metadata: period: year unit: currency-GBP - uprating: gov.benefit_uprating_cpi + label: CTC family element reference: - - https://www.gov.uk/government/publications/rates-and-allowances-tax-credits-child-benefit-and-guardians-allowance/tax-credits-child-benefit-and-guardians-allowance - - https://www.iser.essex.ac.uk/files/projects/UKMOD/EUROMOD_country_report.pdf#page=27 + - title: Tax Credits Act 2002 s. 9(2)(a) and (3) + href: https://www.legislation.gov.uk/ukpga/2002/21/section/9 + - title: The Child Tax Credit Regulations 2002 (SI 2002/2007) reg. 7(3), as made + href: https://www.legislation.gov.uk/uksi/2002/2007/regulation/7/made + - title: The Tax Credits Up-rating Regulations 2011 (SI 2011/1035) reg. 2(2), substituting reg. 7(3) + href: https://www.legislation.gov.uk/uksi/2011/1035/regulation/2/made + - title: The Child Tax Credit Regulations 2002 reg. 7(3), 2024-25 version + href: https://www.legislation.gov.uk/uksi/2002/2007/regulation/7/2024-04-06 + - title: "Tax credits, Child Benefit and Guardian's Allowance: rates and allowances (HMRC, gov.uk)" + href: https://www.gov.uk/government/publications/rates-and-allowances-tax-credits-child-benefit-and-guardians-allowance/tax-credits-child-benefit-and-guardians-allowance + - title: UKMOD country report (EUROMOD) + href: https://www.iser.essex.ac.uk/files/projects/UKMOD/EUROMOD_country_report.pdf#page=27 diff --git a/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element_baby_addition.yaml b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element_baby_addition.yaml new file mode 100644 index 0000000000..d2becb05e3 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/elements/family_element_baby_addition.yaml @@ -0,0 +1,19 @@ +description: Addition to the Child Tax Credit family element where any child is under the age of one year. +values: + # CTC Regs 2002 reg 7(3) as made: the family element was £1,090 "in a case + # where any of the children referred to in paragraph (2)(b) above is under the + # age of one year" and £545 "in any other case", so the addition is £545. + # SI 2011/1035 reg 2(2) replaced reg 7(3) with a single £545 from 6 April 2011. + 2003-04-06: 545 + 2011-04-06: 0 +metadata: + period: year + unit: currency-GBP + label: CTC family element baby addition + reference: + - title: The Child Tax Credit Regulations 2002 (SI 2002/2007) reg. 7(3)(a), as made + href: https://www.legislation.gov.uk/uksi/2002/2007/regulation/7/made + - title: The Child Tax Credit Regulations 2002 reg. 7(3), 2010-11 version + href: https://www.legislation.gov.uk/uksi/2002/2007/regulation/7/2010-04-06 + - title: The Tax Credits Up-rating Regulations 2011 (SI 2011/1035) reg. 2(2), substituting reg. 7(3) + href: https://www.legislation.gov.uk/uksi/2011/1035/regulation/2/made diff --git a/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/eligibility/family_element_born_before.yaml b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/eligibility/family_element_born_before.yaml new file mode 100644 index 0000000000..b9e3fd68f2 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/eligibility/family_element_born_before.yaml @@ -0,0 +1,21 @@ +description: The Child Tax Credit family element is included only where the claimant is responsible for a child or qualifying young person born before this date, written as a YYYYMMDD number. +values: + # In force from 6 April 2017: Tax Credits Act 2002 s.9(2)(a) as amended by the + # Welfare Reform and Work Act 2016 s.13(2)(a), and CTC Regs 2002 reg 7(2)(a) as + # amended by SI 2017/387 reg 4(b). Every child or qualifying young person in an + # earlier tax year was born before this date, so it changes nothing before + # 2017-18. + 0001-01-01: 20170406 +metadata: + unit: date + period: year + label: CTC family element child date of birth cutoff + reference: + - title: Tax Credits Act 2002 s. 9(2)(a) + href: https://www.legislation.gov.uk/ukpga/2002/21/section/9 + - title: Welfare Reform and Work Act 2016 s. 13(2)(a) + href: https://www.legislation.gov.uk/ukpga/2016/7/section/13 + - title: The Child Tax Credit Regulations 2002 (SI 2002/2007) reg. 7(2)(a) + href: https://www.legislation.gov.uk/uksi/2002/2007/regulation/7 + - title: The Child Tax Credit (Amendment) Regulations 2017 (SI 2017/387) reg. 4(b) + href: https://www.legislation.gov.uk/uksi/2017/387/regulation/4/made diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits/ctc_family_element.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits/ctc_family_element.yaml new file mode 100644 index 0000000000..5ba49e3119 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/tax_credits/ctc_family_element.yaml @@ -0,0 +1,456 @@ +# Child Tax Credit family element: Tax Credits Act 2002 s.9(2)(a) and +# CTC Regs 2002 (SI 2002/2007) reg 7(2)(a) and (3). +# +# Expected values are worked from the law, not from the model: +# - reg 7(3): the family element is £545 in every tax year from 2003-04 to +# 2024-25 (as made: £545, or £1,090 where any child is under one; from +# 6 April 2011, SI 2011/1035 reg 2(2): £545). +# - reg 7(2)(a), from 6 April 2017 (SI 2017/387 reg 4(b)): included only where +# the claimant is responsible for a child or qualifying young person born +# before 6 April 2017. The model knows birth years only (period less age), so +# a child counts as born before the cutoff when their birth year is 2016 or +# earlier. +# - 2024-25 award, CTC only: individual element £3,455 per child +# (reg 7(4)(c), SI 2024/247 reg 3(a)); threshold £19,995 and taper 41% +# (SI 2002/2008 reg 8(3) steps 4 and 5, SI 2024/247 reg 4(4)). For income of +# £25,000 the reduction is 41% x (25,000 - 19,995) = £2,052.05. +# - Award amounts follow the model's annual arithmetic: maximum rate less the +# reduction. Reg 8(3) steps 1 and 2 take each element as a daily rate rounded +# up to the penny, which the model does not, so a full-year statutory award is +# a few pounds higher (e.g. £1,952.00 rather than £1,947.95 for the first +# case). The element amounts and the family element condition are exact. +# +# Every family claims CTC (reported) and not Universal Credit, has no one in +# work for 16 hours (so no WTC), and has no disabled child. + +- name: 2024-25, one child born 2014 - family element of 545 included + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + child: + age: 10 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 545 + CTC_maximum_rate: 4_000 # 3,455 + 545 + child_tax_credit: 1_947.95 # 4,000 - 2,052.05 + +- name: 2024-25, only child born 2019 - no family element + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + child: + age: 5 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 0 + CTC_maximum_rate: 3_455 + child_tax_credit: 1_402.95 # 3,455 - 2,052.05 + +- name: 2024-25, children born 2015 and 2019 - one child born before the cutoff is enough, and there is one family element + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + older_child: + age: 9 + younger_child: + age: 5 + benunits: + benunit: + members: [parent, older_child, younger_child] + would_claim_uc: false + output: + CTC_family_element: 545 + CTC_maximum_rate: 7_455 # 2 x 3,455 + 545 + child_tax_credit: 5_402.95 # 7,455 - 2,052.05 + +- name: 2024-25, three children born 2018, 2020 and 2022 - no family element, and the child limit gives two individual elements + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + child_1: + age: 6 + child_2: + age: 4 + child_3: + age: 2 + benunits: + benunit: + members: [parent, child_1, child_2, child_3] + would_claim_uc: false + output: + CTC_family_element: 0 + CTC_child_element: 6_910 # 2 x 3,455 (reg 7(2A)) + CTC_maximum_rate: 6_910 + child_tax_credit: 4_857.95 # 6,910 - 2,052.05 + +- name: 2024-25, a qualifying young person born 2006 counts as born before the cutoff + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 45 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + young_person: + age: 18 + current_education: UPPER_SECONDARY + child: + age: 2 + benunits: + benunit: + members: [parent, young_person, child] + would_claim_uc: false + output: + is_child_or_qualifying_young_person_for_child_tax_credit: [false, true, true] + CTC_family_element: 545 + CTC_maximum_rate: 7_455 # 2 x 3,455 + 545 + child_tax_credit: 5_402.95 + +- name: 2024-25, an older sibling aged 20 is not a qualifying young person, so does not bring the family element + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 45 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + adult_child: + age: 20 + current_education: UPPER_SECONDARY + child: + age: 4 + benunits: + benunit: + members: [parent, adult_child, child] + would_claim_uc: false + output: + is_child_or_qualifying_young_person_for_child_tax_credit: [false, false, true] + CTC_family_element: 0 + CTC_maximum_rate: 3_455 + child_tax_credit: 1_402.95 + +- name: 2024-25, joint claim with a child born 2012 - family element included + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent_1: + age: 35 + employment_income: 15_000 + child_tax_credit_reported: 1_000 + parent_2: + age: 33 + employment_income: 10_000 + child: + age: 12 + benunits: + benunit: + members: [parent_1, parent_2, child] + would_claim_uc: false + output: + CTC_family_element: 545 + child_tax_credit: 1_947.95 # 3,455 + 545 - 41% x (25,000 - 19,995) + +- name: 2024-25, a child aged 7 with an entered birth year of 2016 is born before the cutoff + period: 2024 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + child: + age: 7 + birth_year: 2016 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 545 + +- name: 2024-25, a child aged 7 with no birth year has birth year 2017 (period less age), so is not born before the cutoff + period: 2024 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + child: + age: 7 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 0 + +- name: 2024-25, a birth year entered for one child only - the other child's birth year is still the period less age + period: 2024 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + child_1: + age: 6 + birth_year: 2018 + child_2: + age: 3 + benunits: + benunit: + members: [parent, child_1, child_2] + would_claim_uc: false + output: + CTC_family_element: 0 # birth years 2018 and 2021 + +- name: 2024-25, a birth year entered for the younger child only - the older child (period less age, 2014) still brings the family element + period: 2024 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + older_child: + age: 10 + younger_child: + age: 5 + birth_year: 2019 + benunits: + benunit: + members: [parent, older_child, younger_child] + would_claim_uc: false + output: + CTC_family_element: 545 + +- name: 2024-25, two children born before the cutoff - still one family element + period: 2024 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + child_1: + age: 12 + child_2: + age: 10 + benunits: + benunit: + members: [parent, child_1, child_2] + would_claim_uc: false + output: + CTC_family_element: 545 + CTC_maximum_rate: 7_455 # 2 x 3,455 + 545 + child_tax_credit: 5_402.95 + +- name: 2023-24, the family element is 545, not uprated + period: 2023 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + child: + age: 10 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 545 + +- name: 2017-18, the first year of the cutoff - only child born in 2017-18, no family element + period: 2017 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + child: + age: 0 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + CTC_family_element: 0 + +- name: 2017-18, a newborn with a sibling born 2014 - family element included + period: 2017 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + older_child: + age: 3 + baby: + age: 0 + benunits: + benunit: + members: [parent, older_child, baby] + would_claim_uc: false + output: + CTC_family_element: 545 + +- name: 2016-17, a child under one - 545, as the baby addition ended in 2011 and the cutoff starts in 2017 + period: 2016 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child_tax_credit_reported: 1_000 + baby: + age: 0 + benunits: + benunit: + members: [parent, baby] + would_claim_uc: false + output: + CTC_family_element: 545 + +# The individual element and income thresholds start in April 2016 in the +# parameter files, and the model backdates every parameter's earliest value to +# 1 January 2015, so they have no values before 2015. These cases enter CTC +# eligibility and test the family element alone. PE-UK converts parameters to +# fiscal years for 2015 to 2040 only; an earlier YEAR period reads them at +# 1 January, which falls in the previous tax year. The periods below are chosen +# so that both tax years have the same law. + +- name: 2012 (read at 1 January 2012, in 2011-12), a child under one - 545 (reg 7(3) as substituted by SI 2011/1035) + period: 2012 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + baby: + age: 0 + benunits: + benunit: + members: [parent, baby] + is_CTC_eligible: true + output: + CTC_family_element: 545 + +- name: 2010 (read at 1 January 2010, in 2009-10), a child under one - 1,090 (reg 7(3)(a) as made) + period: 2010 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + older_child: + age: 4 + baby: + age: 0 + benunits: + benunit: + members: [parent, older_child, baby] + is_CTC_eligible: true + output: + CTC_family_element: 1_090 + +- name: 2010 (read at 1 January 2010, in 2009-10), youngest child aged one - 545 (reg 7(3)(b) as made) + period: 2010 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child: + age: 1 + benunits: + benunit: + members: [parent, child] + is_CTC_eligible: true + output: + CTC_family_element: 545 + +- name: 2004 (read at 1 January 2004, in 2003-04, the first year of tax credits) - 545 + period: 2004 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child: + age: 5 + benunits: + benunit: + members: [parent, child] + is_CTC_eligible: true + output: + CTC_family_element: 545 + +- name: 2025-26, tax credits have ended - no Child Tax Credit + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + employment_income: 25_000 + child_tax_credit_reported: 1_000 + child: + age: 11 + benunits: + benunit: + members: [parent, child] + would_claim_uc: false + output: + child_tax_credit: 0 + +- name: 2026-27, the family element amount is not projected after tax credits ended + period: 2026 + absolute_error_margin: 0 + input: + people: + parent: + age: 35 + child: + age: 12 + benunits: + benunit: + members: [parent, child] + is_CTC_eligible: true + output: + CTC_family_element: 545 diff --git a/policyengine_uk/tests/test_ctc_family_element_properties.py b/policyengine_uk/tests/test_ctc_family_element_properties.py new file mode 100644 index 0000000000..60f4cb2726 --- /dev/null +++ b/policyengine_uk/tests/test_ctc_family_element_properties.py @@ -0,0 +1,258 @@ +"""Property-based tests for the Child Tax Credit family element. + +Law: Tax Credits Act 2002 s.9(2)(a); Child Tax Credit Regulations 2002 +(SI 2002/2007) reg 7(2)(a) and (3). + +- reg 7(3) set the family element at £545 from 6 April 2003, or £1,090 where + any child was under one; from 6 April 2011 (SI 2011/1035 reg 2(2)) it is + £545. No Up-rating Regulations changed it before tax credits ended on + 5 April 2025. +- reg 7(2)(a), from 6 April 2017 (SI 2017/387 reg 4(b)): the family element is + included only where the claimant is responsible for a child or qualifying + young person born before 6 April 2017. + +Invariants: + +1. The parameter is £545 on every day from 6 April 2003 to 2040, and with the + baby addition never exceeds £1,090, nor £545 from 6 April 2011. In every + converted fiscal year (2015 to 2040) the YEAR value is £545. +2. For any population of families and any tax year from 2015-16 to 2024-25: + the family element is 0 or £545 (one per family, never per child), and it + is £545 exactly when the family is eligible for Child Tax Credit and + counts a child or qualifying young person born in 2016 or earlier. The + oracle takes birth years from the generated ages and entered birth years, + and the model's list of counted children; it does not call the formula or + read birth_year (which is 0 for anyone left out when a situation enters it + for some people). +3. Metamorphic: listing a family's members in a different order does not + change the family element. +4. Metamorphic: from 2017-18, adding a newborn (born after 6 April 2017) + never changes the family element. +5. Before 2011-12 (2004 to 2010, read at 1 January), the family element is + £1,090 where an eligible family counts a child under one, otherwise £545. +""" + +import datetime + +import numpy as np +import pytest +from hypothesis import HealthCheck, example, given, settings +from hypothesis import strategies as st + +from policyengine_uk import CountryTaxBenefitSystem, Simulation + +FAMILY_ELEMENT = 545 +BABY_FAMILY_ELEMENT = 1_090 +PROPERTY_SETTINGS = settings( + max_examples=25, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +@pytest.fixture(scope="module") +def system(): + return CountryTaxBenefitSystem() + + +def test_family_element_is_545_every_day_from_2003(system): + elements = system.parameters.gov.dwp.tax_credits.child_tax_credit.elements + day = datetime.date(2003, 4, 6) + while day <= datetime.date(2040, 12, 31): + instant = day.isoformat() + family = elements.family_element(instant) + baby = elements.family_element_baby_addition(instant) + assert family == FAMILY_ELEMENT, instant + assert family + baby <= BABY_FAMILY_ELEMENT, instant + if day >= datetime.date(2011, 4, 6): + assert family + baby == FAMILY_ELEMENT, instant + else: + assert family + baby == BABY_FAMILY_ELEMENT, instant + day += datetime.timedelta(days=1) + + +@pytest.mark.parametrize("year", range(2015, 2041)) +def test_family_element_is_545_in_every_converted_fiscal_year(system, year): + elements = system.parameters(str(year)).gov.dwp.tax_credits.child_tax_credit + assert elements.elements.family_element == FAMILY_ELEMENT + assert elements.elements.family_element_baby_addition == 0 + assert elements.eligibility.family_element_born_before == 20170406 + + +# Ages 0 to 21 cover children, qualifying young persons (16 to 19 in +# non-advanced education) and dependants who no longer count. +member = st.fixed_dictionaries( + { + "age": st.integers(0, 21), + "in_education": st.booleans(), + # None: birth year is the period less age. Otherwise an entered birth + # year consistent with the age (a birthday later in the year). + "birth_year_offset": st.sampled_from([None, None, 0, 1]), + } +) +family = st.fixed_dictionaries( + { + "adults": st.integers(1, 2), + "children": st.lists(member, min_size=0, max_size=4), + "reports_ctc": st.booleans(), + "would_claim_uc": st.booleans(), + } +) +population = st.lists(family, min_size=1, max_size=8) + + +def _situation(families, year, reverse=False, add_newborn=False, eligible=None): + people, benunits, households = {}, {}, {} + for i, unit in enumerate(families): + names = [] + for j in range(unit["adults"]): + name = f"a{i}_{j}" + adult = {"age": {year: 35 + j}} + if j == 0 and unit["reports_ctc"]: + adult["child_tax_credit_reported"] = {year: 1_000} + people[name] = adult + names.append(name) + children = list(unit["children"]) + if add_newborn: + children.append( + {"age": 0, "in_education": False, "birth_year_offset": None} + ) + for k, child in enumerate(children): + name = f"c{i}_{k}" + person = {"age": {year: child["age"]}} + if child["in_education"]: + person["current_education"] = {year: "UPPER_SECONDARY"} + if child["birth_year_offset"] is not None: + person["birth_year"] = { + year: year - child["age"] - child["birth_year_offset"] + } + people[name] = person + names.append(name) + if reverse: + names = names[::-1] + benunit = {"members": names} + if eligible is None: + benunit["would_claim_uc"] = {year: unit["would_claim_uc"]} + else: + benunit["is_CTC_eligible"] = {year: eligible} + benunits[f"b{i}"] = benunit + households[f"h{i}"] = {"members": names} + return {"people": people, "benunits": benunits, "households": households} + + +def _calculate(families, year, **kwargs): + simulation = Simulation(situation=_situation(families, year, **kwargs)) + return { + variable: simulation.calculate(variable, year) + for variable in [ + "CTC_family_element", + "is_CTC_eligible", + "is_child_or_qualifying_young_person_for_child_tax_credit", + "birth_year", + "age", + ] + } + + +def _oracle_people(families, year, add_newborn=False): + """Each person's family index and birth year, in situation order, from the + generated data alone.""" + benunit, birth_year = [], [] + for i, unit in enumerate(families): + for j in range(unit["adults"]): + benunit.append(i) + birth_year.append(year - 35 - j) + children = list(unit["children"]) + if add_newborn: + children.append({"age": 0, "birth_year_offset": None}) + for child in children: + benunit.append(i) + birth_year.append(year - child["age"] - (child["birth_year_offset"] or 0)) + return np.array(benunit), np.array(birth_year) + + +def _any_by_family(families, benunit, mask): + result = np.zeros(len(families), dtype=bool) + np.logical_or.at(result, benunit, mask) + return result + + +def _child(age, birth_year_offset=None): + return {"age": age, "in_education": False, "birth_year_offset": birth_year_offset} + + +# Birth years entered for some people only: the others are left at 0. +PARTIAL_BIRTH_YEARS = [ + # Entered 2018 for one child; the sibling's birth year is 2021. + { + "adults": 1, + "children": [_child(6, 0), _child(3)], + "reports_ctc": True, + "would_claim_uc": False, + }, + # Entered 2019 for the younger child; the older child's is 2014. + { + "adults": 2, + "children": [_child(10), _child(5, 0)], + "reports_ctc": True, + "would_claim_uc": False, + }, +] + + +@PROPERTY_SETTINGS +@given(population, st.integers(2015, 2024)) +@example(PARTIAL_BIRTH_YEARS, 2024) +def test_family_element_is_one_flat_amount_for_a_child_born_before_the_cutoff( + families, year +): + result = _calculate(families, year) + family_element = result["CTC_family_element"] + assert np.all(np.isin(family_element, [0, FAMILY_ELEMENT])) + + # Oracle: a child or qualifying young person the model counts, born in + # 2016 or earlier (birth year entered, or the period less age). + counted = result["is_child_or_qualifying_young_person_for_child_tax_credit"] + benunit, birth_year = _oracle_people(families, year) + has_qualifying_child = _any_by_family( + families, benunit, counted & (birth_year <= 2016) + ) + expected = np.where( + result["is_CTC_eligible"] & has_qualifying_child, FAMILY_ELEMENT, 0 + ) + np.testing.assert_array_equal(family_element, expected) + + +@PROPERTY_SETTINGS +@given(population, st.integers(2015, 2024)) +def test_family_element_does_not_depend_on_member_order(families, year): + forward = _calculate(families, year)["CTC_family_element"] + reverse = _calculate(families, year, reverse=True)["CTC_family_element"] + np.testing.assert_array_equal(forward, reverse) + + +@PROPERTY_SETTINGS +@given(population, st.integers(2017, 2024)) +def test_a_newborn_after_the_cutoff_never_changes_the_family_element(families, year): + # Hold eligibility fixed: a newborn can make a family with no other child + # eligible, and the family element must still be nil. + without = _calculate(families, year, eligible=True)["CTC_family_element"] + with_newborn = _calculate(families, year, add_newborn=True, eligible=True)[ + "CTC_family_element" + ] + np.testing.assert_array_equal(without, with_newborn) + + +@PROPERTY_SETTINGS +@given(population, st.integers(2004, 2010)) +def test_baby_rate_before_2011(families, year): + result = _calculate(families, year, eligible=True) + counted = result["is_child_or_qualifying_young_person_for_child_tax_credit"] + benunit, _ = _oracle_people(families, year) + has_child = _any_by_family(families, benunit, counted) + has_baby = _any_by_family(families, benunit, counted & (result["age"] < 1)) + expected = np.where( + has_child, np.where(has_baby, BABY_FAMILY_ELEMENT, FAMILY_ELEMENT), 0 + ) + np.testing.assert_array_equal(result["CTC_family_element"], expected) diff --git a/policyengine_uk/variables/gov/dwp/CTC_family_element.py b/policyengine_uk/variables/gov/dwp/CTC_family_element.py index 6d4b74d0ad..577c0135cd 100644 --- a/policyengine_uk/variables/gov/dwp/CTC_family_element.py +++ b/policyengine_uk/variables/gov/dwp/CTC_family_element.py @@ -6,11 +6,40 @@ class CTC_family_element(Variable): entity = BenUnit label = "CTC entitlement in the Family Element" definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" + reference = [ + "https://www.legislation.gov.uk/ukpga/2002/21/section/9", + "https://www.legislation.gov.uk/uksi/2002/2007/regulation/7", + ] unit = GBP defined_for = "is_CTC_eligible" def formula(benunit, period, parameters): - return parameters( - period - ).gov.dwp.tax_credits.child_tax_credit.elements.family_element + ctc = parameters(period).gov.dwp.tax_credits.child_tax_credit + person = benunit.members + child_or_qualifying_young_person = person( + "is_child_or_qualifying_young_person_for_child_tax_credit", period + ) + age = person("age", period) + # Tax Credits Act 2002 s.9(2)(a) and CTC Regs 2002 reg 7(2)(a), from + # 6 April 2017: the family element is included only where the claimant + # is responsible for a child or qualifying young person born before + # 6 April 2017. Ages give a birth year only (birth_year is the period + # less age), so each birthday is taken as 6 April of that year. Against + # the 6 April 2017 cutoff that means a birth year of 2016 or earlier, + # as the child limit reads it (is_CTC_child_limit_exempt). + birth_year = person("birth_year", period) + # A situation that enters birth_year for some people leaves everyone + # else at 0; use the period less age for them. + from_age = (period.start.year - age).astype(int) + birth_year = where(birth_year > 0, birth_year, from_age) + birth_date = birth_year * 10_000 + 406 + born_before_cutoff = birth_date < ctc.eligibility.family_element_born_before + included = benunit.any(child_or_qualifying_young_person & born_before_cutoff) + # CTC Regs 2002 reg 7(3)(a) as made, until 5 April 2011: a higher + # family element where any child is under the age of one year. + has_baby = benunit.any(child_or_qualifying_young_person & (age < 1)) + amount = ( + ctc.elements.family_element + + has_baby * ctc.elements.family_element_baby_addition + ) + return included * amount