From adf96a0d6ef15ea205138550bc4feb0a7aec38e0 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Fri, 10 Jan 2025 19:32:50 +0000 Subject: [PATCH 01/38] Implement tax free childcare --- .../childcare_age_child_condition.py | 39 +++++++++++ .../conditions/childcare_income_condition.py | 64 +++++++++++++++++++ .../childcare_incompatibilities_condition.py | 31 +++++++++ .../conditions/childcare_work_condition.py | 50 +++++++++++++++ .../tax-free-childcare-benefits.py | 33 ++++++++++ 5 files changed, 217 insertions(+) create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py new file mode 100644 index 00000000..95e8c041 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -0,0 +1,39 @@ +from policyengine_uk.model_api import * + + +class child_age_eligible(Variable): + value_type = bool + entity = Person + label = "Child age eligibility requirements" + documentation = "Whether this person meets the age and disability requirements for eligibility" + definition_period = YEAR + + def formula(person, period, parameters): + """ + Calculate age eligibility based on age and disability conditions. + + Returns: + bool: True if eligible (under 12, or under 17 with disability), False otherwise + """ + # Get the benefit unit the person belongs to + benunit = person.benunit + + # Get person's characteristics + age = person("age", period) + + # Check disability conditions + gc = parameters(period).gov.dwp.pension_credit.guarantee_credit + standard_disability_benefits = gc.child.disability.eligibility + severe_disability_benefits = gc.child.disability.severe.eligibility + + is_disabled = (add(person, period, standard_disability_benefits) | + add(person, period, severe_disability_benefits)) > 0 + + # Check age conditions + basic_age_condition = (age < 12) + age_under_17 = (age < 17) + + # Combine conditions + eligible = basic_age_condition | (age_under_17 & is_disabled) + + return benunit.any(eligible) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py new file mode 100644 index 00000000..493085e4 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -0,0 +1,64 @@ +from policyengine_uk.model_api import * + + +class meets_income_requirements(Variable): + value_type = bool + entity = Person + label = "Income requirements and calculations" + documentation = "Whether this person meets the income requirements for tax-free childcare based on age and income thresholds" + definition_period = YEAR + + def formula(person, period): + """ + Calculate if a person meets income requirements based on their age and income. + + Returns: + bool: True if they meet the income conditions for their age group + """ + # Get person's characteristics + age = person("age", period) + + # Calculate eligible income + total_income = person("total_income", period) + # Extract investment incomes to subtract + investment_income = add( + person, + period, + [ + "private_pension_income", + "savings_interest_income", + "dividend_income", + "property_income", + ] + ) + + yearly_eligible_income = total_income - investment_income + + # Income thresholds by age group + quarterly_income = yearly_eligible_income / 4 + + # Age >= 21 + meets_adult_condition = ( + (age >= 21) & + (quarterly_income >= 2379) + ) + + # Age 18-20 + meets_young_adult_condition = ( + (age >= 18) & + (age <= 20) & + (quarterly_income >= 1788) + ) + + # Age < 18 + meets_youth_condition = ( + (age < 18) & + (quarterly_income >= 1331) + ) + + # Combine all conditions + return ( + meets_adult_condition | + meets_young_adult_condition | + meets_youth_condition + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py new file mode 100644 index 00000000..22bac4f5 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class incompatibilities_childcare_eligible(Variable): + value_type = bool + entity = Person + label = "Tax-Free Childcare Exclusions" + documentation = "Whether the person's benefit unit meets the incompatibility conditions for tax-free childcare (not receiving WTC, CTC, or UC)" + definition_period = YEAR + + def formula(person, period, parameters): + """ + Calculate eligibility based on incompatible benefits. + + Returns: + bool: True if eligible (no incompatible benefits received), False if receiving any incompatible benefits + """ + # Get the benefit unit the person belongs to + benunit = person.benunit + + # Check if receiving any of the mutually exclusive benefits + has_wtc = benunit("working_tax_credit", period) > 0 + has_ctc = benunit("child_tax_credit", period) > 0 + has_uc = benunit("universal_credit", period) > 0 + + # Returns True when person's benefit unit does NOT receive any of these benefits + return ~( + has_wtc | + has_ctc | + has_uc + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py new file mode 100644 index 00000000..5f009cc2 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * + +class childcare_work_condition(Variable): + value_type = bool + entity = Person + label = "Work conditions for tax-free childcare" + documentation = "Whether the person/couple meets work requirements for tax-free childcare" + definition_period = YEAR + def formula(person, period, parameters): + """ + Calculate if person meets work conditions for: + - Single working adult + - Couple where either both work or one works and other has disability/incapacity + """ + benunit = person.benunit + is_adult = person("is_adult", period) + + # Basic work status + in_work = person("in_work", period) + + # Get disability/incapacity conditions like we did in childcare age eligibility + gc = parameters(period).gov.dwp.pension_credit.guarantee_credit + standard_disability_benefits = gc.child.disability.eligibility + severe_disability_benefits = gc.child.disability.severe.eligibility + + is_disabled = (add(person, period, standard_disability_benefits) | + add(person, period, severe_disability_benefits)) > 0 + + has_incapacity = person("incapacity_benefit", period) > 0 + + # Build conditions + # Single adult conditions + is_single = benunit.sum(is_adult) == 1 + single_working = is_single & in_work + + # Couple conditions + is_couple = benunit.sum(is_adult) == 2 + partner_in_work = in_work + partner_has_condition = (is_disabled | has_incapacity) + + couple_both_working = is_couple & in_work & partner_in_work + is_partner_working_with_disabled_person = is_couple & partner_in_work & (is_disabled | has_incapacity) + is_person_working_with_disabled_partner = is_couple & in_work & partner_has_condition + + return ( + single_working | + couple_both_working | + is_person_working_with_disabled_partner | + is_partner_working_with_disabled_person + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py new file mode 100644 index 00000000..259b7b19 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py @@ -0,0 +1,33 @@ +from policyengine_uk.model_api import * + + +def formula(benunit, period, parameters, parents_contribution): + # Define tax-free childcare parameters + p = { + "standard_child": { + "yearly_max": 2000 + }, + "disabled_child": { + "yearly_max": 4000 + }, + "government_contribution": 2/8 + } + + # Check eligibility conditions + meets_age_condition = benunit("child_age_eligible", period) + meets_income_condition = benunit.any(benunit.members("meets_income_requirements", period)) + is_eligible = meets_age_condition & meets_income_condition & benunit("incompatibilities_childcare_eligible", period) + + # Determine the maximum eligible childcare cost for a single child + max_amount = 0 + for child in benunit.members("is_child", period): + if is_eligible[child]: + if child("is_disabled", period): + max_amount = p["disabled_child"]["yearly_max"] # Only consider disabled child's max + else: + max_amount = p["standard_child"]["yearly_max"] # Only consider standard child's max + + # Calculate the government contribution + government_contribution = min(parents_contribution * p["government_contribution"], max_amount) + + return government_contribution From ee24d612fc476a1bb39128f2f20e9fe88c7e3a06 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:27:55 +0000 Subject: [PATCH 02/38] Parameterize values --- .../tax_free_childcare/age_limits.yaml | 16 ++++++ .../contribution_parameters.yaml | 20 +++++++ .../tax_free_childcare/income_thresholds.yaml | 23 ++++++++ .../childcare_age_child_condition.py | 41 ++++++++------ .../conditions/childcare_income_condition.py | 29 +++++----- .../childcare_incompatibilities_condition.py | 10 +--- .../conditions/childcare_work_condition.py | 50 ++++++++++------- .../tax-free-childcare-benefits.py | 33 ----------- .../tax_free_childcare_benefits.py | 56 +++++++++++++++++++ 9 files changed, 187 insertions(+), 91 deletions(-) create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml delete mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py create mode 100644 policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml new file mode 100644 index 00000000..d520a059 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml @@ -0,0 +1,16 @@ +description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. +values: + 2025-01-01: + standard_age_limit: 12 + disability_age_limit: 17 + metadata: + reference: + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +metadata: + unit: years + name: childcare_age_limits + label: Age limits for tax-free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml new file mode 100644 index 00000000..ea1b8b32 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml @@ -0,0 +1,20 @@ +description: Parameters for tax-free childcare government contribution calculations +values: + 2025-01-01: + contribution_rates: + standard_child: + yearly_max: 2000 + disabled_child: + yearly_max: 4000 + government_contribution_ratio: 0.25 # 2/8 expressed as decimal + metadata: + reference: + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +metadata: + unit: currency-GBP + name: contribution_parameters + label: Tax-free childcare contribution parameters + reference: + - title: Childcare Payments Act 2014 + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml new file mode 100644 index 00000000..aa5df701 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -0,0 +1,23 @@ +description: Income thresholds for tax-free childcare eligibility by age group +values: + 2025-01-01: + adult: + min_age: 21 + quarterly_income: 2379 + young_adult: + min_age: 18 + max_age: 20 + quarterly_income: 1788 + youth: + quarterly_income: 1331 + metadata: + reference: + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +metadata: + unit: currency-GBP + name: income_thresholds + label: Income thresholds for tax-free childcare eligibility + reference: + - title: Childcare Payments Act 2014 + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 95e8c041..6aa5d7b9 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,35 +5,44 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = "Whether this person meets the age and disability requirements for eligibility" + documentation = ( + "Whether this person meets the age and disability requirements for eligibility" + ) definition_period = YEAR def formula(person, period, parameters): """ Calculate age eligibility based on age and disability conditions. - + Returns: - bool: True if eligible (under 12, or under 17 with disability), False otherwise + bool: True if eligible (under standard age limit, or under disability age limit with disability), False otherwise """ # Get the benefit unit the person belongs to benunit = person.benunit - + # Get person's characteristics age = person("age", period) - + + # Get age thresholds from parameters + age_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits + standard_age_limit = age_limits.standard_age_limit + disability_age_limit = age_limits.disability_age_limit + # Check disability conditions gc = parameters(period).gov.dwp.pension_credit.guarantee_credit standard_disability_benefits = gc.child.disability.eligibility severe_disability_benefits = gc.child.disability.severe.eligibility - - is_disabled = (add(person, period, standard_disability_benefits) | - add(person, period, severe_disability_benefits)) > 0 - - # Check age conditions - basic_age_condition = (age < 12) - age_under_17 = (age < 17) - + + is_disabled = ( + add(person, period, standard_disability_benefits) + | add(person, period, severe_disability_benefits) + ) > 0 + + # Check age conditions using parameterized values + basic_age_condition = age < standard_age_limit + age_under_disability_limit = age < disability_age_limit + # Combine conditions - eligible = basic_age_condition | (age_under_17 & is_disabled) - - return benunit.any(eligible) \ No newline at end of file + eligible = basic_age_condition | (age_under_disability_limit & is_disabled) + + return eligible \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 493085e4..b0c8195a 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -8,10 +8,10 @@ class meets_income_requirements(Variable): documentation = "Whether this person meets the income requirements for tax-free childcare based on age and income thresholds" definition_period = YEAR - def formula(person, period): + def formula(person, period, parameters): """ Calculate if a person meets income requirements based on their age and income. - + Returns: bool: True if they meet the income conditions for their age group """ @@ -29,36 +29,35 @@ def formula(person, period): "savings_interest_income", "dividend_income", "property_income", - ] + ], ) - + yearly_eligible_income = total_income - investment_income - # Income thresholds by age group + # Get income thresholds from parameters + income_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds quarterly_income = yearly_eligible_income / 4 # Age >= 21 meets_adult_condition = ( - (age >= 21) & - (quarterly_income >= 2379) + (age >= income_limits.adult.min_age) & + (quarterly_income >= income_limits.adult.quarterly_income) ) # Age 18-20 meets_young_adult_condition = ( - (age >= 18) & - (age <= 20) & - (quarterly_income >= 1788) + (age >= income_limits.young_adult.min_age) & + (age <= income_limits.young_adult.max_age) & + (quarterly_income >= income_limits.young_adult.quarterly_income) ) # Age < 18 meets_youth_condition = ( - (age < 18) & - (quarterly_income >= 1331) + (age < income_limits.young_adult.min_age) & + (quarterly_income >= income_limits.youth.quarterly_income) ) # Combine all conditions return ( - meets_adult_condition | - meets_young_adult_condition | - meets_youth_condition + meets_adult_condition | meets_young_adult_condition | meets_youth_condition ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py index 22bac4f5..8a89213f 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py @@ -11,21 +11,17 @@ class incompatibilities_childcare_eligible(Variable): def formula(person, period, parameters): """ Calculate eligibility based on incompatible benefits. - + Returns: bool: True if eligible (no incompatible benefits received), False if receiving any incompatible benefits """ # Get the benefit unit the person belongs to benunit = person.benunit - + # Check if receiving any of the mutually exclusive benefits has_wtc = benunit("working_tax_credit", period) > 0 has_ctc = benunit("child_tax_credit", period) > 0 has_uc = benunit("universal_credit", period) > 0 # Returns True when person's benefit unit does NOT receive any of these benefits - return ~( - has_wtc | - has_ctc | - has_uc - ) \ No newline at end of file + return ~(has_wtc | has_ctc | has_uc) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py index 5f009cc2..93e278a1 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -1,11 +1,15 @@ -from policyengine_uk.model_api import * +from policyengine_uk.model_api import * + class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = "Whether the person/couple meets work requirements for tax-free childcare" + documentation = ( + "Whether the person/couple meets work requirements for tax-free childcare" + ) definition_period = YEAR + def formula(person, period, parameters): """ Calculate if person meets work conditions for: @@ -14,37 +18,43 @@ def formula(person, period, parameters): """ benunit = person.benunit is_adult = person("is_adult", period) - + # Basic work status in_work = person("in_work", period) - + # Get disability/incapacity conditions like we did in childcare age eligibility gc = parameters(period).gov.dwp.pension_credit.guarantee_credit standard_disability_benefits = gc.child.disability.eligibility severe_disability_benefits = gc.child.disability.severe.eligibility - - is_disabled = (add(person, period, standard_disability_benefits) | - add(person, period, severe_disability_benefits)) > 0 - + + is_disabled = ( + add(person, period, standard_disability_benefits) + | add(person, period, severe_disability_benefits) + ) > 0 + has_incapacity = person("incapacity_benefit", period) > 0 - + # Build conditions # Single adult conditions is_single = benunit.sum(is_adult) == 1 single_working = is_single & in_work - + # Couple conditions is_couple = benunit.sum(is_adult) == 2 partner_in_work = in_work - partner_has_condition = (is_disabled | has_incapacity) - + partner_has_condition = is_disabled | has_incapacity + couple_both_working = is_couple & in_work & partner_in_work - is_partner_working_with_disabled_person = is_couple & partner_in_work & (is_disabled | has_incapacity) - is_person_working_with_disabled_partner = is_couple & in_work & partner_has_condition - + is_partner_working_with_disabled_person = ( + is_couple & partner_in_work & (is_disabled | has_incapacity) + ) + is_person_working_with_disabled_partner = ( + is_couple & in_work & partner_has_condition + ) + return ( - single_working | - couple_both_working | - is_person_working_with_disabled_partner | - is_partner_working_with_disabled_person - ) \ No newline at end of file + single_working + | couple_both_working + | is_person_working_with_disabled_partner + | is_partner_working_with_disabled_person + ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py deleted file mode 100644 index 259b7b19..00000000 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax-free-childcare-benefits.py +++ /dev/null @@ -1,33 +0,0 @@ -from policyengine_uk.model_api import * - - -def formula(benunit, period, parameters, parents_contribution): - # Define tax-free childcare parameters - p = { - "standard_child": { - "yearly_max": 2000 - }, - "disabled_child": { - "yearly_max": 4000 - }, - "government_contribution": 2/8 - } - - # Check eligibility conditions - meets_age_condition = benunit("child_age_eligible", period) - meets_income_condition = benunit.any(benunit.members("meets_income_requirements", period)) - is_eligible = meets_age_condition & meets_income_condition & benunit("incompatibilities_childcare_eligible", period) - - # Determine the maximum eligible childcare cost for a single child - max_amount = 0 - for child in benunit.members("is_child", period): - if is_eligible[child]: - if child("is_disabled", period): - max_amount = p["disabled_child"]["yearly_max"] # Only consider disabled child's max - else: - max_amount = p["standard_child"]["yearly_max"] # Only consider standard child's max - - # Calculate the government contribution - government_contribution = min(parents_contribution * p["government_contribution"], max_amount) - - return government_contribution diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py new file mode 100644 index 00000000..79ec1cdc --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -0,0 +1,56 @@ +from policyengine_uk.model_api import * + + +class tax_free_childcare(Variable): + value_type = float + entity = BenUnit + label = "Tax-free childcare government contribution" + documentation = "The amount of government contribution provided through the tax-free childcare scheme" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + """ + Calculate the government contribution for tax-free childcare. + + Args: + benunit: The benefit unit + period: The time period + parameters: Policy parameters + + Returns: + float: The calculated government contribution + """ + # Get parents contribution + parents_contribution = benunit("childcare_cost", period) + + # Get parameters from the parameter tree + p = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters + + # Check eligibility conditions + meets_age_condition = benunit("child_age_eligible", period) + meets_income_condition = benunit.any( + benunit.members("meets_income_requirements", period) + ) + is_eligible = ( + meets_age_condition + & meets_income_condition + & benunit("incompatibilities_childcare_eligible", period) + ) + + # Determine the maximum eligible childcare cost for a single child + max_amount = 0 + for child in benunit.members("is_child", period): + if is_eligible[child]: + if child("is_disabled", period): + max_amount = p.contribution_rates.disabled_child.yearly_max + else: + max_amount = p.contribution_rates.standard_child.yearly_max + + # Calculate the government contribution + government_contribution = min( + parents_contribution * p.contribution_rates.government_contribution_ratio, + max_amount + ) + + return where(is_eligible, government_contribution, 0) \ No newline at end of file From 12d24be7866385dc0c6808ccd8d77748c109036b Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:30:56 +0000 Subject: [PATCH 03/38] Reformatting to pass the tests --- .../childcare_age_child_condition.py | 6 +++-- .../conditions/childcare_income_condition.py | 22 +++++++++---------- .../tax_free_childcare_benefits.py | 18 ++++++++------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 6aa5d7b9..65a134aa 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -24,7 +24,9 @@ def formula(person, period, parameters): age = person("age", period) # Get age thresholds from parameters - age_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits + age_limits = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits standard_age_limit = age_limits.standard_age_limit disability_age_limit = age_limits.disability_age_limit @@ -45,4 +47,4 @@ def formula(person, period, parameters): # Combine conditions eligible = basic_age_condition | (age_under_disability_limit & is_disabled) - return eligible \ No newline at end of file + return eligible diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index b0c8195a..2f91e7c8 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -35,29 +35,29 @@ def formula(person, period, parameters): yearly_eligible_income = total_income - investment_income # Get income thresholds from parameters - income_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds + income_limits = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds quarterly_income = yearly_eligible_income / 4 # Age >= 21 - meets_adult_condition = ( - (age >= income_limits.adult.min_age) & - (quarterly_income >= income_limits.adult.quarterly_income) + meets_adult_condition = (age >= income_limits.adult.min_age) & ( + quarterly_income >= income_limits.adult.quarterly_income ) # Age 18-20 meets_young_adult_condition = ( - (age >= income_limits.young_adult.min_age) & - (age <= income_limits.young_adult.max_age) & - (quarterly_income >= income_limits.young_adult.quarterly_income) + (age >= income_limits.young_adult.min_age) + & (age <= income_limits.young_adult.max_age) + & (quarterly_income >= income_limits.young_adult.quarterly_income) ) # Age < 18 - meets_youth_condition = ( - (age < income_limits.young_adult.min_age) & - (quarterly_income >= income_limits.youth.quarterly_income) + meets_youth_condition = (age < income_limits.young_adult.min_age) & ( + quarterly_income >= income_limits.youth.quarterly_income ) # Combine all conditions return ( meets_adult_condition | meets_young_adult_condition | meets_youth_condition - ) \ No newline at end of file + ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 79ec1cdc..0f8a3de5 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -8,25 +8,27 @@ class tax_free_childcare(Variable): documentation = "The amount of government contribution provided through the tax-free childcare scheme" definition_period = YEAR unit = GBP - + def formula(benunit, period, parameters): """ Calculate the government contribution for tax-free childcare. - + Args: benunit: The benefit unit period: The time period parameters: Policy parameters - + Returns: float: The calculated government contribution """ # Get parents contribution parents_contribution = benunit("childcare_cost", period) - + # Get parameters from the parameter tree - p = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters - + p = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters + # Check eligibility conditions meets_age_condition = benunit("child_age_eligible", period) meets_income_condition = benunit.any( @@ -50,7 +52,7 @@ def formula(benunit, period, parameters): # Calculate the government contribution government_contribution = min( parents_contribution * p.contribution_rates.government_contribution_ratio, - max_amount + max_amount, ) - return where(is_eligible, government_contribution, 0) \ No newline at end of file + return where(is_eligible, government_contribution, 0) From 235fd08b3a69e65fbf7eae48c175204da303e882 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:41:00 +0000 Subject: [PATCH 04/38] Reformatting with black --- .../conditions/childcare_age_child_condition.py | 13 +++---------- .../conditions/childcare_income_condition.py | 11 +++-------- .../conditions/childcare_work_condition.py | 13 ++++--------- .../tax_free_childcare_benefits.py | 9 +++------ 4 files changed, 13 insertions(+), 33 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 65a134aa..8ec5346b 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,9 +5,7 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = ( - "Whether this person meets the age and disability requirements for eligibility" - ) + documentation = "Whether this person meets the age and disability requirements for eligibility" definition_period = YEAR def formula(person, period, parameters): @@ -17,16 +15,11 @@ def formula(person, period, parameters): Returns: bool: True if eligible (under standard age limit, or under disability age limit with disability), False otherwise """ - # Get the benefit unit the person belongs to - benunit = person.benunit - # Get person's characteristics age = person("age", period) # Get age thresholds from parameters - age_limits = parameters( - period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits + age_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits standard_age_limit = age_limits.standard_age_limit disability_age_limit = age_limits.disability_age_limit @@ -47,4 +40,4 @@ def formula(person, period, parameters): # Combine conditions eligible = basic_age_condition | (age_under_disability_limit & is_disabled) - return eligible + return eligible \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 2f91e7c8..7e1dcca2 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -9,8 +9,7 @@ class meets_income_requirements(Variable): definition_period = YEAR def formula(person, period, parameters): - """ - Calculate if a person meets income requirements based on their age and income. + """Calculate if a person meets income requirements based on their age and income. Returns: bool: True if they meet the income conditions for their age group @@ -35,9 +34,7 @@ def formula(person, period, parameters): yearly_eligible_income = total_income - investment_income # Get income thresholds from parameters - income_limits = parameters( - period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds + income_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds quarterly_income = yearly_eligible_income / 4 # Age >= 21 @@ -58,6 +55,4 @@ def formula(person, period, parameters): ) # Combine all conditions - return ( - meets_adult_condition | meets_young_adult_condition | meets_youth_condition - ) + return meets_adult_condition | meets_young_adult_condition | meets_youth_condition \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py index 93e278a1..0d3ad034 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,14 +5,11 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = ( - "Whether the person/couple meets work requirements for tax-free childcare" - ) + documentation = "Whether the person/couple meets work requirements for tax-free childcare" definition_period = YEAR def formula(person, period, parameters): - """ - Calculate if person meets work conditions for: + """Calculate if person meets work conditions for: - Single working adult - Couple where either both work or one works and other has disability/incapacity """ @@ -48,13 +45,11 @@ def formula(person, period, parameters): is_partner_working_with_disabled_person = ( is_couple & partner_in_work & (is_disabled | has_incapacity) ) - is_person_working_with_disabled_partner = ( - is_couple & in_work & partner_has_condition - ) + is_person_working_with_disabled_partner = is_couple & in_work & partner_has_condition return ( single_working | couple_both_working | is_person_working_with_disabled_partner | is_partner_working_with_disabled_person - ) + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 0f8a3de5..b277be76 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -10,8 +10,7 @@ class tax_free_childcare(Variable): unit = GBP def formula(benunit, period, parameters): - """ - Calculate the government contribution for tax-free childcare. + """Calculate the government contribution for tax-free childcare. Args: benunit: The benefit unit @@ -25,9 +24,7 @@ def formula(benunit, period, parameters): parents_contribution = benunit("childcare_cost", period) # Get parameters from the parameter tree - p = parameters( - period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters + p = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters # Check eligibility conditions meets_age_condition = benunit("child_age_eligible", period) @@ -55,4 +52,4 @@ def formula(benunit, period, parameters): max_amount, ) - return where(is_eligible, government_contribution, 0) + return where(is_eligible, government_contribution, 0) \ No newline at end of file From b3b1967fff5ea82e609bdb367e84cada29dcc3ea Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:44:21 +0000 Subject: [PATCH 05/38] Reformatting with black --- .../conditions/childcare_age_child_condition.py | 8 ++++++-- .../conditions/childcare_income_condition.py | 11 ++++++++--- .../conditions/childcare_work_condition.py | 8 ++++++-- .../tax_free_childcare/tax_free_childcare_benefits.py | 7 +++++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 8ec5346b..123b8166 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,7 +5,9 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = "Whether this person meets the age and disability requirements for eligibility" + documentation = ( + "Whether this person meets the age and disability requirements for eligibility" + ) definition_period = YEAR def formula(person, period, parameters): @@ -19,7 +21,9 @@ def formula(person, period, parameters): age = person("age", period) # Get age thresholds from parameters - age_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits + age_limits = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits standard_age_limit = age_limits.standard_age_limit disability_age_limit = age_limits.disability_age_limit diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 7e1dcca2..c257da78 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -9,7 +9,8 @@ class meets_income_requirements(Variable): definition_period = YEAR def formula(person, period, parameters): - """Calculate if a person meets income requirements based on their age and income. + """ + Calculate if a person meets income requirements based on their age and income. Returns: bool: True if they meet the income conditions for their age group @@ -34,7 +35,9 @@ def formula(person, period, parameters): yearly_eligible_income = total_income - investment_income # Get income thresholds from parameters - income_limits = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds + income_limits = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds quarterly_income = yearly_eligible_income / 4 # Age >= 21 @@ -55,4 +58,6 @@ def formula(person, period, parameters): ) # Combine all conditions - return meets_adult_condition | meets_young_adult_condition | meets_youth_condition \ No newline at end of file + return ( + meets_adult_condition | meets_young_adult_condition | meets_youth_condition + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py index 0d3ad034..e7c9e2ec 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,7 +5,9 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = "Whether the person/couple meets work requirements for tax-free childcare" + documentation = ( + "Whether the person/couple meets work requirements for tax-free childcare" + ) definition_period = YEAR def formula(person, period, parameters): @@ -45,7 +47,9 @@ def formula(person, period, parameters): is_partner_working_with_disabled_person = ( is_couple & partner_in_work & (is_disabled | has_incapacity) ) - is_person_working_with_disabled_partner = is_couple & in_work & partner_has_condition + is_person_working_with_disabled_partner = ( + is_couple & in_work & partner_has_condition + ) return ( single_working diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index b277be76..45e1cf90 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -10,7 +10,8 @@ class tax_free_childcare(Variable): unit = GBP def formula(benunit, period, parameters): - """Calculate the government contribution for tax-free childcare. + """ + Calculate the government contribution for tax-free childcare. Args: benunit: The benefit unit @@ -24,7 +25,9 @@ def formula(benunit, period, parameters): parents_contribution = benunit("childcare_cost", period) # Get parameters from the parameter tree - p = parameters(period).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters + p = parameters( + period + ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters # Check eligibility conditions meets_age_condition = benunit("child_age_eligible", period) From 55ce6c342877189db61550fa33cc192c84ebd640 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:46:04 +0000 Subject: [PATCH 06/38] Format code with black --- .../conditions/childcare_age_child_condition.py | 2 +- .../tax_free_childcare/conditions/childcare_income_condition.py | 2 +- .../tax_free_childcare/conditions/childcare_work_condition.py | 2 +- .../gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 123b8166..e2f5affb 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -44,4 +44,4 @@ def formula(person, period, parameters): # Combine conditions eligible = basic_age_condition | (age_under_disability_limit & is_disabled) - return eligible \ No newline at end of file + return eligible diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index c257da78..2f91e7c8 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -60,4 +60,4 @@ def formula(person, period, parameters): # Combine all conditions return ( meets_adult_condition | meets_young_adult_condition | meets_youth_condition - ) \ No newline at end of file + ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py index e7c9e2ec..dc60017f 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -56,4 +56,4 @@ def formula(person, period, parameters): | couple_both_working | is_person_working_with_disabled_partner | is_partner_working_with_disabled_person - ) \ No newline at end of file + ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 45e1cf90..0f8a3de5 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -55,4 +55,4 @@ def formula(benunit, period, parameters): max_amount, ) - return where(is_eligible, government_contribution, 0) \ No newline at end of file + return where(is_eligible, government_contribution, 0) From 9952ed9d3c5e2a9eecdc7c605300b1e25c483579 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 13:59:06 +0000 Subject: [PATCH 07/38] Format code with black using 79-char line length --- .../conditions/childcare_age_child_condition.py | 8 ++++---- .../conditions/childcare_income_condition.py | 4 +++- .../conditions/childcare_work_condition.py | 4 +--- .../tax_free_childcare/tax_free_childcare_benefits.py | 3 ++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index e2f5affb..ad0359e3 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,9 +5,7 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = ( - "Whether this person meets the age and disability requirements for eligibility" - ) + documentation = "Whether this person meets the age and disability requirements for eligibility" definition_period = YEAR def formula(person, period, parameters): @@ -42,6 +40,8 @@ def formula(person, period, parameters): age_under_disability_limit = age < disability_age_limit # Combine conditions - eligible = basic_age_condition | (age_under_disability_limit & is_disabled) + eligible = basic_age_condition | ( + age_under_disability_limit & is_disabled + ) return eligible diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 2f91e7c8..1fbe2c54 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -59,5 +59,7 @@ def formula(person, period, parameters): # Combine all conditions return ( - meets_adult_condition | meets_young_adult_condition | meets_youth_condition + meets_adult_condition + | meets_young_adult_condition + | meets_youth_condition ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py index dc60017f..eb28187d 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,9 +5,7 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = ( - "Whether the person/couple meets work requirements for tax-free childcare" - ) + documentation = "Whether the person/couple meets work requirements for tax-free childcare" definition_period = YEAR def formula(person, period, parameters): diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 0f8a3de5..76757c26 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -51,7 +51,8 @@ def formula(benunit, period, parameters): # Calculate the government contribution government_contribution = min( - parents_contribution * p.contribution_rates.government_contribution_ratio, + parents_contribution + * p.contribution_rates.government_contribution_ratio, max_amount, ) From d6b22e521bd54e4ed00d2503f71f461e7ef9a7ae Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 14:07:59 +0000 Subject: [PATCH 08/38] Fix YAML structure --- .../tax_free_childcare/age_limits.yaml | 7 +++--- .../contribution_parameters.yaml | 19 +++++++-------- .../tax_free_childcare/income_thresholds.yaml | 23 ++++++++++--------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml index d520a059..a461d442 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml @@ -1,8 +1,9 @@ description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. values: 2025-01-01: - standard_age_limit: 12 - disability_age_limit: 17 + value: + standard_age_limit: 12 + disability_age_limit: 17 metadata: reference: - title: Tax-Free Childcare Guidance @@ -13,4 +14,4 @@ metadata: label: Age limits for tax-free childcare eligibility reference: - title: Childcare Act - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml index ea1b8b32..38761298 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml @@ -1,12 +1,13 @@ -description: Parameters for tax-free childcare government contribution calculations +description: Parameters for tax-free childcare government contribution calculations. values: 2025-01-01: - contribution_rates: - standard_child: - yearly_max: 2000 - disabled_child: - yearly_max: 4000 - government_contribution_ratio: 0.25 # 2/8 expressed as decimal + value: + contribution_rates: + standard_child: + yearly_max: 2000 + disabled_child: + yearly_max: 4000 + government_contribution_ratio: 0.25 # 2/8 expressed as decimal metadata: reference: - title: Tax-Free Childcare Guidance @@ -16,5 +17,5 @@ metadata: name: contribution_parameters label: Tax-free childcare contribution parameters reference: - - title: Childcare Payments Act 2014 - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml index aa5df701..4b022fa2 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -1,15 +1,16 @@ -description: Income thresholds for tax-free childcare eligibility by age group +description: Income thresholds for tax-free childcare eligibility by age group. values: 2025-01-01: - adult: - min_age: 21 - quarterly_income: 2379 - young_adult: - min_age: 18 - max_age: 20 - quarterly_income: 1788 - youth: - quarterly_income: 1331 + value: + adult: + min_age: 21 + quarterly_income: 2379 + young_adult: + min_age: 18 + max_age: 20 + quarterly_income: 1788 + youth: + quarterly_income: 1331 metadata: reference: - title: Tax-Free Childcare Guidance @@ -20,4 +21,4 @@ metadata: label: Income thresholds for tax-free childcare eligibility reference: - title: Childcare Payments Act 2014 - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 \ No newline at end of file + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 From b06a47c3863997afa0d07c50e3512f3ee74e80c9 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 14:10:33 +0000 Subject: [PATCH 09/38] Fix age condition YAML file --- .../childcare_subsidies/tax_free_childcare/age_limits.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml index a461d442..5677388d 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml @@ -1,9 +1,8 @@ description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. values: 2025-01-01: - value: - standard_age_limit: 12 - disability_age_limit: 17 + - standard_age_limit: 12 + - disability_age_limit: 17 metadata: reference: - title: Tax-Free Childcare Guidance From 90eb8238a88375f78d20d02d109805913110a240 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 14:15:38 +0000 Subject: [PATCH 10/38] Fix age condition YAML file --- .../tax_free_childcare/age_limits.yaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml index 5677388d..f228fe1a 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml @@ -1,12 +1,4 @@ description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. -values: - 2025-01-01: - - standard_age_limit: 12 - - disability_age_limit: 17 - metadata: - reference: - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d metadata: unit: years name: childcare_age_limits @@ -14,3 +6,9 @@ metadata: reference: - title: Childcare Act href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2025-01-01: + - standard_age_limit: 12 + - disability_age_limit: 17 \ No newline at end of file From 0c871a5288c69657340e352106bbbba3219690e3 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 14:19:50 +0000 Subject: [PATCH 11/38] Fix other YAML files --- .../contribution_parameters.yaml | 35 +++++++----- .../tax_free_childcare/income_thresholds.yaml | 54 +++++++++++++------ 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml index 38761298..01308a15 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml @@ -1,17 +1,4 @@ description: Parameters for tax-free childcare government contribution calculations. -values: - 2025-01-01: - value: - contribution_rates: - standard_child: - yearly_max: 2000 - disabled_child: - yearly_max: 4000 - government_contribution_ratio: 0.25 # 2/8 expressed as decimal - metadata: - reference: - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d metadata: unit: currency-GBP name: contribution_parameters @@ -19,3 +6,25 @@ metadata: reference: - title: Childcare Payments Act href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +standard_child: + yearly_max: + values: + 2025-01-01: 2000 + metadata: + unit: currency-GBP + +disabled_child: + yearly_max: + values: + 2025-01-01: 4000 + metadata: + unit: currency-GBP + +government_contribution_ratio: + values: + 2025-01-01: 0.25 # 2/8 expressed as decimal + metadata: + unit: /1 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml index 4b022fa2..1be95381 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -1,20 +1,4 @@ description: Income thresholds for tax-free childcare eligibility by age group. -values: - 2025-01-01: - value: - adult: - min_age: 21 - quarterly_income: 2379 - young_adult: - min_age: 18 - max_age: 20 - quarterly_income: 1788 - youth: - quarterly_income: 1331 - metadata: - reference: - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d metadata: unit: currency-GBP name: income_thresholds @@ -22,3 +6,41 @@ metadata: reference: - title: Childcare Payments Act 2014 href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +adult: + quarterly_income: + values: + 2025-01-01: 2379 + metadata: + unit: currency-GBP + min_age: + values: + 2025-01-01: 21 + metadata: + unit: years + +young_adult: + quarterly_income: + values: + 2025-01-01: 1788 + metadata: + unit: currency-GBP + min_age: + values: + 2025-01-01: 18 + metadata: + unit: years + max_age: + values: + 2025-01-01: 20 + metadata: + unit: years + +youth: + quarterly_income: + values: + 2025-01-01: 1331 + metadata: + unit: currency-GBP \ No newline at end of file From b28b5f8474f8ce9535422a79f590ddbcd725f950 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 18:02:03 +0000 Subject: [PATCH 12/38] Edit parameterization --- .../tax_free_childcare/age/disability.yaml | 12 +++++ .../tax_free_childcare/age/standard.yaml | 12 +++++ .../tax_free_childcare/age_limits.yaml | 14 ------ .../contribution/disabled_child.yaml | 12 +++++ .../government_contribution_ratio.yaml | 12 +++++ .../contribution/standard_child.yaml | 12 +++++ .../contribution_parameters.yaml | 30 ------------ .../tax_free_childcare/income_thresholds.yaml | 46 ------------------- .../income_thresholds/adult.yaml | 22 +++++++++ .../income_thresholds/young_adult.yaml | 29 ++++++++++++ .../income_thresholds/youth.yaml | 15 ++++++ .../childcare_age_child_condition.py | 16 +++---- .../conditions/childcare_income_condition.py | 18 ++++---- .../tax_free_childcare_benefits.py | 18 ++------ 14 files changed, 146 insertions(+), 122 deletions(-) create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml delete mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml delete mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml delete mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml new file mode 100644 index 00000000..5158b87b --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for tax-free childcare eligibility - disability-related limit +metadata: + unit: year + period: year + label: Disability age limit for tax-free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2025-01-01: 17 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml new file mode 100644 index 00000000..e8a7f194 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for tax-free childcare eligibility - standard limit +metadata: + unit: year + period: year + label: Standard age limit for tax-free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2025-01-01: 12 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml deleted file mode 100644 index f228fe1a..00000000 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age_limits.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. -metadata: - unit: years - name: childcare_age_limits - label: Age limits for tax-free childcare eligibility - reference: - - title: Childcare Act - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d -values: - 2025-01-01: - - standard_age_limit: 12 - - disability_age_limit: 17 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml new file mode 100644 index 00000000..1ead8dd1 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml @@ -0,0 +1,12 @@ +description: Maximum yearly contribution limit for disabled child tax-free childcare +metadata: + unit: currency-GBP + period: year + label: Disabled child yearly maximum contribution + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2015-01-01: 4000 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml new file mode 100644 index 00000000..7330c773 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml @@ -0,0 +1,12 @@ +description: Government contribution ratio for tax-free childcare +metadata: + unit: /1 + period: year + label: Government contribution ratio for tax-free childcare + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2015-01-01: 0.25 # 2/8 expressed as decimal \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml new file mode 100644 index 00000000..a350a0bb --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml @@ -0,0 +1,12 @@ +description: Maximum yearly contribution limit for standard child tax-free childcare +metadata: + unit: currency-GBP + period: year + label: Standard child yearly maximum contribution + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2015-01-01: 2000 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml deleted file mode 100644 index 01308a15..00000000 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution_parameters.yaml +++ /dev/null @@ -1,30 +0,0 @@ -description: Parameters for tax-free childcare government contribution calculations. -metadata: - unit: currency-GBP - name: contribution_parameters - label: Tax-free childcare contribution parameters - reference: - - title: Childcare Payments Act - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d - -standard_child: - yearly_max: - values: - 2025-01-01: 2000 - metadata: - unit: currency-GBP - -disabled_child: - yearly_max: - values: - 2025-01-01: 4000 - metadata: - unit: currency-GBP - -government_contribution_ratio: - values: - 2025-01-01: 0.25 # 2/8 expressed as decimal - metadata: - unit: /1 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml deleted file mode 100644 index 1be95381..00000000 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml +++ /dev/null @@ -1,46 +0,0 @@ -description: Income thresholds for tax-free childcare eligibility by age group. -metadata: - unit: currency-GBP - name: income_thresholds - label: Income thresholds for tax-free childcare eligibility - reference: - - title: Childcare Payments Act 2014 - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d - -adult: - quarterly_income: - values: - 2025-01-01: 2379 - metadata: - unit: currency-GBP - min_age: - values: - 2025-01-01: 21 - metadata: - unit: years - -young_adult: - quarterly_income: - values: - 2025-01-01: 1788 - metadata: - unit: currency-GBP - min_age: - values: - 2025-01-01: 18 - metadata: - unit: years - max_age: - values: - 2025-01-01: 20 - metadata: - unit: years - -youth: - quarterly_income: - values: - 2025-01-01: 1331 - metadata: - unit: currency-GBP \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml new file mode 100644 index 00000000..5089229d --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml @@ -0,0 +1,22 @@ +description: Income thresholds for tax-free childcare eligibility - adult category +metadata: + period: year + label: Adult income thresholds for tax-free childcare eligibility + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +quarterly_income: + values: + 2015-01-01: 2379 + metadata: + unit: currency-GBP + +min_age: + values: + 2015-01-01: 21 + metadata: + unit: year + period: year \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml new file mode 100644 index 00000000..4e82458f --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml @@ -0,0 +1,29 @@ +description: Income thresholds for tax-free childcare eligibility - young adult category +metadata: + period: year + label: Young adult income thresholds for tax-free childcare eligibility + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +quarterly_income: + values: + 2015-01-01: 1788 + metadata: + unit: currency-GBP + +min_age: + values: + 2015-01-01: 18 + metadata: + unit: year + period: year + +max_age: + values: + 2015-01-01: 20 + metadata: + unit: year + period: year \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml new file mode 100644 index 00000000..339627ff --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml @@ -0,0 +1,15 @@ +description: Income thresholds for tax-free childcare eligibility - youth category +metadata: + period: year + label: Youth income thresholds for tax-free childcare eligibility + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +quarterly_income: + values: + 2015-01-01: 1331 + metadata: + unit: currency-GBP \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index ad0359e3..f55e438e 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -21,14 +21,14 @@ def formula(person, period, parameters): # Get age thresholds from parameters age_limits = parameters( period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.age_limits - standard_age_limit = age_limits.standard_age_limit - disability_age_limit = age_limits.disability_age_limit + ).gov.hmrc.childcare_subsidies.tax_free_childcare.age + standard_age_limit = age_limits.standard.values + disability_age_limit = age_limits.disability.values # Check disability conditions - gc = parameters(period).gov.dwp.pension_credit.guarantee_credit - standard_disability_benefits = gc.child.disability.eligibility - severe_disability_benefits = gc.child.disability.severe.eligibility + gc = parameters(period).gov.dwp.pension_credit.guarantee_credit.child.disability + standard_disability_benefits = gc.eligibility + severe_disability_benefits = gc.severe.eligibility is_disabled = ( add(person, period, standard_disability_benefits) @@ -40,8 +40,6 @@ def formula(person, period, parameters): age_under_disability_limit = age < disability_age_limit # Combine conditions - eligible = basic_age_condition | ( + return basic_age_condition | ( age_under_disability_limit & is_disabled ) - - return eligible diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 1fbe2c54..04bf9da2 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -32,7 +32,7 @@ def formula(person, period, parameters): ], ) - yearly_eligible_income = total_income - investment_income + yearly_eligible_income = max_(total_income - investment_income, 0) # Get income thresholds from parameters income_limits = parameters( @@ -41,20 +41,20 @@ def formula(person, period, parameters): quarterly_income = yearly_eligible_income / 4 # Age >= 21 - meets_adult_condition = (age >= income_limits.adult.min_age) & ( - quarterly_income >= income_limits.adult.quarterly_income + meets_adult_condition = (age >= income_limits.adult.min_age.values) & ( + quarterly_income >= income_limits.adult.quarterly_income.values ) # Age 18-20 meets_young_adult_condition = ( - (age >= income_limits.young_adult.min_age) - & (age <= income_limits.young_adult.max_age) - & (quarterly_income >= income_limits.young_adult.quarterly_income) + (age >= income_limits.young_adult.min_age.values) + & (age <= income_limits.young_adult.max_age.values) + & (quarterly_income >= income_limits.young_adult.quarterly_income.values) ) # Age < 18 - meets_youth_condition = (age < income_limits.young_adult.min_age) & ( - quarterly_income >= income_limits.youth.quarterly_income + meets_youth_condition = (age < income_limits.young_adult.min_age.values) & ( + quarterly_income >= income_limits.youth.quarterly_income.values ) # Combine all conditions @@ -62,4 +62,4 @@ def formula(person, period, parameters): meets_adult_condition | meets_young_adult_condition | meets_youth_condition - ) + ) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 76757c26..c27f3e5c 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -21,13 +21,10 @@ def formula(benunit, period, parameters): Returns: float: The calculated government contribution """ - # Get parents contribution - parents_contribution = benunit("childcare_cost", period) - # Get parameters from the parameter tree p = parameters( period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution_parameters + ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution # Check eligibility conditions meets_age_condition = benunit("child_age_eligible", period) @@ -45,15 +42,8 @@ def formula(benunit, period, parameters): for child in benunit.members("is_child", period): if is_eligible[child]: if child("is_disabled", period): - max_amount = p.contribution_rates.disabled_child.yearly_max + max_amount = p.disabled_child.values else: - max_amount = p.contribution_rates.standard_child.yearly_max - - # Calculate the government contribution - government_contribution = min( - parents_contribution - * p.contribution_rates.government_contribution_ratio, - max_amount, - ) + max_amount = p.standard_child.values - return where(is_eligible, government_contribution, 0) + return where(is_eligible, max_amount, 0) \ No newline at end of file From ff1f45678595464accb608fac9af51fd96d4263f Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 18:04:45 +0000 Subject: [PATCH 13/38] Fix formatting --- .../conditions/childcare_age_child_condition.py | 8 ++++---- .../conditions/childcare_income_condition.py | 13 ++++++++----- .../tax_free_childcare_benefits.py | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index f55e438e..827f14f6 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -26,7 +26,9 @@ def formula(person, period, parameters): disability_age_limit = age_limits.disability.values # Check disability conditions - gc = parameters(period).gov.dwp.pension_credit.guarantee_credit.child.disability + gc = parameters( + period + ).gov.dwp.pension_credit.guarantee_credit.child.disability standard_disability_benefits = gc.eligibility severe_disability_benefits = gc.severe.eligibility @@ -40,6 +42,4 @@ def formula(person, period, parameters): age_under_disability_limit = age < disability_age_limit # Combine conditions - return basic_age_condition | ( - age_under_disability_limit & is_disabled - ) + return basic_age_condition | (age_under_disability_limit & is_disabled) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 04bf9da2..ac4bfc08 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -49,17 +49,20 @@ def formula(person, period, parameters): meets_young_adult_condition = ( (age >= income_limits.young_adult.min_age.values) & (age <= income_limits.young_adult.max_age.values) - & (quarterly_income >= income_limits.young_adult.quarterly_income.values) + & ( + quarterly_income + >= income_limits.young_adult.quarterly_income.values + ) ) # Age < 18 - meets_youth_condition = (age < income_limits.young_adult.min_age.values) & ( - quarterly_income >= income_limits.youth.quarterly_income.values - ) + meets_youth_condition = ( + age < income_limits.young_adult.min_age.values + ) & (quarterly_income >= income_limits.youth.quarterly_income.values) # Combine all conditions return ( meets_adult_condition | meets_young_adult_condition | meets_youth_condition - ) \ No newline at end of file + ) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index c27f3e5c..91ef3b4e 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -46,4 +46,4 @@ def formula(benunit, period, parameters): else: max_amount = p.standard_child.values - return where(is_eligible, max_amount, 0) \ No newline at end of file + return where(is_eligible, max_amount, 0) From d3ad6853f3e3aeb4a196d24de8cd049803e432cd Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 18:17:21 +0000 Subject: [PATCH 14/38] Edit age in YAML files --- .../childcare_subsidies/tax_free_childcare/age/disability.yaml | 2 +- .../childcare_subsidies/tax_free_childcare/age/standard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml index 5158b87b..5ee1fcc9 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml @@ -9,4 +9,4 @@ metadata: - title: Tax-Free Childcare Guidance href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d values: - 2025-01-01: 17 \ No newline at end of file + 2015-01-01: 17 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml index e8a7f194..649dffd5 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml @@ -9,4 +9,4 @@ metadata: - title: Tax-Free Childcare Guidance href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d values: - 2025-01-01: 12 \ No newline at end of file + 2015-01-01: 12 \ No newline at end of file From 5c6d359cd1942a194b00a831147625274f1bcd01 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 18:59:38 +0000 Subject: [PATCH 15/38] Replace if with where --- .../adult.yaml => income_thresholds.yaml} | 29 +++++++-------- .../income_thresholds/young_adult.yaml | 29 --------------- .../income_thresholds/youth.yaml | 15 -------- .../childcare_age_child_condition.py | 7 ++-- .../conditions/childcare_income_condition.py | 36 ++++--------------- .../tax_free_childcare_benefits.py | 18 ++++++---- 6 files changed, 36 insertions(+), 98 deletions(-) rename policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/{income_thresholds/adult.yaml => income_thresholds.yaml} (55%) delete mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml delete mode 100644 policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml similarity index 55% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml rename to policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml index 5089229d..343605a0 100644 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/adult.yaml +++ b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -1,22 +1,23 @@ -description: Income thresholds for tax-free childcare eligibility - adult category +description: Income thresholds for tax-free childcare eligibility metadata: period: year - label: Adult income thresholds for tax-free childcare eligibility + label: Income thresholds for tax-free childcare eligibility reference: - title: Childcare Payments Act href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - title: Tax-Free Childcare Guidance href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d -quarterly_income: - values: - 2015-01-01: 2379 - metadata: - unit: currency-GBP - -min_age: - values: - 2015-01-01: 21 - metadata: - unit: year - period: year \ No newline at end of file +brackets: + - threshold: + 2015-01-01: 0 + amount: + 2015-01-01: 0 + - threshold: + 2015-01-01: 18 + amount: + 2015-01-01: 1_788 + - threshold: + 2015-01-01: 21 + amount: + 2015-01-01: 2_379 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml deleted file mode 100644 index 4e82458f..00000000 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/young_adult.yaml +++ /dev/null @@ -1,29 +0,0 @@ -description: Income thresholds for tax-free childcare eligibility - young adult category -metadata: - period: year - label: Young adult income thresholds for tax-free childcare eligibility - reference: - - title: Childcare Payments Act - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d - -quarterly_income: - values: - 2015-01-01: 1788 - metadata: - unit: currency-GBP - -min_age: - values: - 2015-01-01: 18 - metadata: - unit: year - period: year - -max_age: - values: - 2015-01-01: 20 - metadata: - unit: year - period: year \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml b/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml deleted file mode 100644 index 339627ff..00000000 --- a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds/youth.yaml +++ /dev/null @@ -1,15 +0,0 @@ -description: Income thresholds for tax-free childcare eligibility - youth category -metadata: - period: year - label: Youth income thresholds for tax-free childcare eligibility - reference: - - title: Childcare Payments Act - href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 - - title: Tax-Free Childcare Guidance - href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d - -quarterly_income: - values: - 2015-01-01: 1331 - metadata: - unit: currency-GBP \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 827f14f6..a8d89ba2 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -1,6 +1,5 @@ from policyengine_uk.model_api import * - class child_age_eligible(Variable): value_type = bool entity = Person @@ -22,8 +21,8 @@ def formula(person, period, parameters): age_limits = parameters( period ).gov.hmrc.childcare_subsidies.tax_free_childcare.age - standard_age_limit = age_limits.standard.values - disability_age_limit = age_limits.disability.values + standard_age_limit = age_limits.standard + disability_age_limit = age_limits.disability # Check disability conditions gc = parameters( @@ -42,4 +41,4 @@ def formula(person, period, parameters): age_under_disability_limit = age < disability_age_limit # Combine conditions - return basic_age_condition | (age_under_disability_limit & is_disabled) + return basic_age_condition | (age_under_disability_limit & is_disabled) \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index ac4bfc08..22bec712 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -33,36 +33,14 @@ def formula(person, period, parameters): ) yearly_eligible_income = max_(total_income - investment_income, 0) + quarterly_income = yearly_eligible_income / 4 - # Get income thresholds from parameters + # Get required income threshold based on age income_limits = parameters( period ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds - quarterly_income = yearly_eligible_income / 4 - - # Age >= 21 - meets_adult_condition = (age >= income_limits.adult.min_age.values) & ( - quarterly_income >= income_limits.adult.quarterly_income.values - ) - - # Age 18-20 - meets_young_adult_condition = ( - (age >= income_limits.young_adult.min_age.values) - & (age <= income_limits.young_adult.max_age.values) - & ( - quarterly_income - >= income_limits.young_adult.quarterly_income.values - ) - ) - - # Age < 18 - meets_youth_condition = ( - age < income_limits.young_adult.min_age.values - ) & (quarterly_income >= income_limits.youth.quarterly_income.values) - - # Combine all conditions - return ( - meets_adult_condition - | meets_young_adult_condition - | meets_youth_condition - ) + + required_threshold = income_limits.calc(age) + + # Compare quarterly income to required threshold + return quarterly_income >= required_threshold \ No newline at end of file diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 91ef3b4e..39438d46 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -40,10 +40,14 @@ def formula(benunit, period, parameters): # Determine the maximum eligible childcare cost for a single child max_amount = 0 for child in benunit.members("is_child", period): - if is_eligible[child]: - if child("is_disabled", period): - max_amount = p.disabled_child.values - else: - max_amount = p.standard_child.values - - return where(is_eligible, max_amount, 0) + max_amount = where( + is_eligible[child], + where( + child("is_disabled", period), + p.disabled_child.values, + p.standard_child.values + ), + max_amount + ) + + return where(is_eligible, max_amount, 0) \ No newline at end of file From e0221f822e5ed035898a3e45a286450eb40934e0 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 19:03:07 +0000 Subject: [PATCH 16/38] Format with black --- .../conditions/childcare_age_child_condition.py | 7 ++++--- .../conditions/childcare_income_condition.py | 6 +++--- .../hmrc/tax_free_childcare/tax_free_childcare_benefits.py | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index a8d89ba2..af1c8318 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -1,5 +1,6 @@ from policyengine_uk.model_api import * + class child_age_eligible(Variable): value_type = bool entity = Person @@ -21,8 +22,8 @@ def formula(person, period, parameters): age_limits = parameters( period ).gov.hmrc.childcare_subsidies.tax_free_childcare.age - standard_age_limit = age_limits.standard - disability_age_limit = age_limits.disability + standard_age_limit = age_limits.standard + disability_age_limit = age_limits.disability # Check disability conditions gc = parameters( @@ -41,4 +42,4 @@ def formula(person, period, parameters): age_under_disability_limit = age < disability_age_limit # Combine conditions - return basic_age_condition | (age_under_disability_limit & is_disabled) \ No newline at end of file + return basic_age_condition | (age_under_disability_limit & is_disabled) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py index 22bec712..7db63d7b 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py @@ -39,8 +39,8 @@ def formula(person, period, parameters): income_limits = parameters( period ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds - + required_threshold = income_limits.calc(age) - + # Compare quarterly income to required threshold - return quarterly_income >= required_threshold \ No newline at end of file + return quarterly_income >= required_threshold diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 39438d46..70e0963c 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -45,9 +45,9 @@ def formula(benunit, period, parameters): where( child("is_disabled", period), p.disabled_child.values, - p.standard_child.values + p.standard_child.values, ), - max_amount + max_amount, ) - return where(is_eligible, max_amount, 0) \ No newline at end of file + return where(is_eligible, max_amount, 0) From fc83a039fe53a03dcaac4f2e592254771b955d99 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 19:23:12 +0000 Subject: [PATCH 17/38] Edit for test passing --- .../conditions/childcare_age_child_condition.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index af1c8318..24d6de0d 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -1,6 +1,5 @@ from policyengine_uk.model_api import * - class child_age_eligible(Variable): value_type = bool entity = Person @@ -32,14 +31,15 @@ def formula(person, period, parameters): standard_disability_benefits = gc.eligibility severe_disability_benefits = gc.severe.eligibility - is_disabled = ( - add(person, period, standard_disability_benefits) - | add(person, period, severe_disability_benefits) - ) > 0 + # Convert to boolean arrays before combining + standard_benefits = add(person, period, standard_disability_benefits).astype(bool) + severe_benefits = add(person, period, severe_disability_benefits).astype(bool) + is_disabled = (standard_benefits | severe_benefits) # Check age conditions using parameterized values basic_age_condition = age < standard_age_limit age_under_disability_limit = age < disability_age_limit - # Combine conditions - return basic_age_condition | (age_under_disability_limit & is_disabled) + # Convert to boolean before final combination + return (basic_age_condition.astype(bool) | + (age_under_disability_limit.astype(bool) & is_disabled)) \ No newline at end of file From 4421154c06ab5a61432cab517740b41e16263561 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 19:25:30 +0000 Subject: [PATCH 18/38] Edit format with black --- .../conditions/childcare_age_child_condition.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py index 24d6de0d..6a73108b 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -1,5 +1,6 @@ from policyengine_uk.model_api import * + class child_age_eligible(Variable): value_type = bool entity = Person @@ -32,14 +33,19 @@ def formula(person, period, parameters): severe_disability_benefits = gc.severe.eligibility # Convert to boolean arrays before combining - standard_benefits = add(person, period, standard_disability_benefits).astype(bool) - severe_benefits = add(person, period, severe_disability_benefits).astype(bool) - is_disabled = (standard_benefits | severe_benefits) + standard_benefits = add( + person, period, standard_disability_benefits + ).astype(bool) + severe_benefits = add( + person, period, severe_disability_benefits + ).astype(bool) + is_disabled = standard_benefits | severe_benefits # Check age conditions using parameterized values basic_age_condition = age < standard_age_limit age_under_disability_limit = age < disability_age_limit # Convert to boolean before final combination - return (basic_age_condition.astype(bool) | - (age_under_disability_limit.astype(bool) & is_disabled)) \ No newline at end of file + return basic_age_condition.astype(bool) | ( + age_under_disability_limit.astype(bool) & is_disabled + ) From b4e8d1aee3f6ad7c4e7141c4baa52d51af2c1db2 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 19:37:03 +0000 Subject: [PATCH 19/38] Edit format of benefit py file --- .../tax_free_childcare_benefits.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 70e0963c..0f3293a0 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -26,15 +26,19 @@ def formula(benunit, period, parameters): period ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution - # Check eligibility conditions - meets_age_condition = benunit("child_age_eligible", period) + # Check eligibility conditions with explicit type conversion + meets_age_condition = benunit("child_age_eligible", period).astype( + bool + ) meets_income_condition = benunit.any( benunit.members("meets_income_requirements", period) - ) + ).astype(bool) + childcare_eligible = benunit( + "incompatibilities_childcare_eligible", period + ).astype(bool) + is_eligible = ( - meets_age_condition - & meets_income_condition - & benunit("incompatibilities_childcare_eligible", period) + meets_age_condition & meets_income_condition & childcare_eligible ) # Determine the maximum eligible childcare cost for a single child From cf7f6e68e702b3c808455006c8469a2aa2152b0c Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Mon, 13 Jan 2025 19:47:52 +0000 Subject: [PATCH 20/38] Edit child as not being boolean --- .../tax_free_childcare_benefits.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 0f3293a0..d13f3a40 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -43,13 +43,19 @@ def formula(benunit, period, parameters): # Determine the maximum eligible childcare cost for a single child max_amount = 0 - for child in benunit.members("is_child", period): + child_mask = benunit.members("is_child", period) + for member in benunit.members: + disabled = benunit.members("is_disabled", period)[member] max_amount = where( - is_eligible[child], + child_mask[member], where( - child("is_disabled", period), - p.disabled_child.values, - p.standard_child.values, + is_eligible, + where( + disabled, + p.disabled_child.values, + p.standard_child.values, + ), + max_amount, ), max_amount, ) From d4ff3b2605a2d1639d9e84d27637e5787e154079 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 13:58:27 +0000 Subject: [PATCH 21/38] Change if to where --- .../tax_free_childcare_benefits.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index d13f3a40..46998079 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -41,23 +41,21 @@ def formula(benunit, period, parameters): meets_age_condition & meets_income_condition & childcare_eligible ) - # Determine the maximum eligible childcare cost for a single child - max_amount = 0 + # Calculate maximum eligible childcare cost using vectorized operations child_mask = benunit.members("is_child", period) - for member in benunit.members: - disabled = benunit.members("is_disabled", period)[member] - max_amount = where( - child_mask[member], + disabled = benunit.members("is_disabled", period) + max_amount = where( + child_mask, + where( + is_eligible, where( - is_eligible, - where( - disabled, - p.disabled_child.values, - p.standard_child.values, - ), - max_amount, + disabled, + p.disabled_child.values, + p.standard_child.values, ), - max_amount, - ) + 0, + ), + 0, + ).max(axis=0) return where(is_eligible, max_amount, 0) From 0058402c03b26c63bf69f08da050864c6be7bcbf Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 14:17:37 +0000 Subject: [PATCH 22/38] Change disability definition --- .../tax_free_childcare_benefits.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index 46998079..f9ab7cd7 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -22,10 +22,14 @@ def formula(benunit, period, parameters): float: The calculated government contribution """ # Get parameters from the parameter tree - p = parameters( + p_tfc = parameters( period ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution + p_disabled = parameters( + period + ).gov.dwp.universal_credit.elements.child.disabled + # Check eligibility conditions with explicit type conversion meets_age_condition = benunit("child_age_eligible", period).astype( bool @@ -43,19 +47,15 @@ def formula(benunit, period, parameters): # Calculate maximum eligible childcare cost using vectorized operations child_mask = benunit.members("is_child", period) - disabled = benunit.members("is_disabled", period) max_amount = where( - child_mask, + child_mask + * p_disabled.amount, # Use the disabled parameter amount + where(is_eligible, p_tfc.disabled_child.values, 0), where( - is_eligible, - where( - disabled, - p.disabled_child.values, - p.standard_child.values, - ), + child_mask, + where(is_eligible, p_tfc.standard_child.values, 0), 0, ), - 0, ).max(axis=0) return where(is_eligible, max_amount, 0) From 37c7228c5d9e5d2d7ca0eda982be27079c59f721 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 14:25:56 +0000 Subject: [PATCH 23/38] Solve .values problem --- .../gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index f9ab7cd7..c16a518a 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -49,7 +49,7 @@ def formula(benunit, period, parameters): child_mask = benunit.members("is_child", period) max_amount = where( child_mask - * p_disabled.amount, # Use the disabled parameter amount + * (p_disabled.amount > 0), # Check if disabled amount exists where(is_eligible, p_tfc.disabled_child.values, 0), where( child_mask, From af96f3a9ccac52712517f1311d19f5e1c935c80c Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 14:36:25 +0000 Subject: [PATCH 24/38] Edit .values error --- .../tax_free_childcare_benefits.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index c16a518a..ef6829b9 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -47,15 +47,20 @@ def formula(benunit, period, parameters): # Calculate maximum eligible childcare cost using vectorized operations child_mask = benunit.members("is_child", period) + disabled = child_mask * (p_disabled.amount > 0) + max_amount = where( - child_mask - * (p_disabled.amount > 0), # Check if disabled amount exists - where(is_eligible, p_tfc.disabled_child.values, 0), + child_mask, where( - child_mask, - where(is_eligible, p_tfc.standard_child.values, 0), + is_eligible, + where( + disabled, + p_tfc.disabled_child, + p_tfc.standard_child, + ), 0, ), + 0, ).max(axis=0) return where(is_eligible, max_amount, 0) From e96bc2e393c08232c879d2ec8f03ab922f831cbd Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 14:43:54 +0000 Subject: [PATCH 25/38] Edit vectorization problem --- .../tax_free_childcare_benefits.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py index ef6829b9..b92d3f66 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py @@ -26,9 +26,7 @@ def formula(benunit, period, parameters): period ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution - p_disabled = parameters( - period - ).gov.dwp.universal_credit.elements.child.disabled + p_disabled = parameters(period).gov.dwp.universal_credit.elements.child # Check eligibility conditions with explicit type conversion meets_age_condition = benunit("child_age_eligible", period).astype( @@ -46,15 +44,13 @@ def formula(benunit, period, parameters): ) # Calculate maximum eligible childcare cost using vectorized operations - child_mask = benunit.members("is_child", period) - disabled = child_mask * (p_disabled.amount > 0) - + # Just check child and disability status directly in where conditions max_amount = where( - child_mask, + benunit.members("is_child", period), where( is_eligible, where( - disabled, + p_disabled.amount > 0, p_tfc.disabled_child, p_tfc.standard_child, ), From 153c2f64ee8e99739ba62df687cf609cc4413907 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 15:15:38 +0000 Subject: [PATCH 26/38] Change folder from hmrc to dwp --- .../childcare_subsidies/tax_free_childcare/age/disability.yaml | 0 .../childcare_subsidies/tax_free_childcare/age/standard.yaml | 0 .../tax_free_childcare/contribution/disabled_child.yaml | 0 .../contribution/government_contribution_ratio.yaml | 0 .../tax_free_childcare/contribution/standard_child.yaml | 0 .../tax_free_childcare/income_thresholds.yaml | 0 .../conditions/childcare_age_child_condition.py | 2 +- .../tax_free_childcare/conditions/childcare_income_condition.py | 2 +- .../conditions/childcare_incompatibilities_condition.py | 0 .../tax_free_childcare/conditions/childcare_work_condition.py | 0 .../tax_free_childcare/tax_free_childcare_benefits.py | 2 +- 11 files changed, 3 insertions(+), 3 deletions(-) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/age/disability.yaml (100%) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/age/standard.yaml (100%) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml (100%) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml (100%) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml (100%) rename policyengine_uk/parameters/gov/{hmrc => dwp}/childcare_subsidies/tax_free_childcare/income_thresholds.yaml (100%) rename policyengine_uk/variables/gov/{hmrc => dwp}/tax_free_childcare/conditions/childcare_age_child_condition.py (96%) rename policyengine_uk/variables/gov/{hmrc => dwp}/tax_free_childcare/conditions/childcare_income_condition.py (95%) rename policyengine_uk/variables/gov/{hmrc => dwp}/tax_free_childcare/conditions/childcare_incompatibilities_condition.py (100%) rename policyengine_uk/variables/gov/{hmrc => dwp}/tax_free_childcare/conditions/childcare_work_condition.py (100%) rename policyengine_uk/variables/gov/{hmrc => dwp}/tax_free_childcare/tax_free_childcare_benefits.py (96%) diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/age/disability.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/disability.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/age/disability.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/age/standard.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/age/standard.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/age/standard.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/disabled_child.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/government_contribution_ratio.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/contribution/standard_child.yaml diff --git a/policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml similarity index 100% rename from policyengine_uk/parameters/gov/hmrc/childcare_subsidies/tax_free_childcare/income_thresholds.yaml rename to policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py similarity index 96% rename from policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py rename to policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py index 6a73108b..88fece09 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -21,7 +21,7 @@ def formula(person, period, parameters): # Get age thresholds from parameters age_limits = parameters( period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.age + ).gov.dwp.childcare_subsidies.tax_free_childcare.age standard_age_limit = age_limits.standard disability_age_limit = age_limits.disability diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py similarity index 95% rename from policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py rename to policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py index 7db63d7b..fd2a6770 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py @@ -38,7 +38,7 @@ def formula(person, period, parameters): # Get required income threshold based on age income_limits = parameters( period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.income_thresholds + ).gov.dwp.childcare_subsidies.tax_free_childcare.income_thresholds required_threshold = income_limits.calc(age) diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py similarity index 100% rename from policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_incompatibilities_condition.py rename to policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py similarity index 100% rename from policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/childcare_work_condition.py rename to policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py similarity index 96% rename from policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py rename to policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py index b92d3f66..20ab4f2a 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py @@ -24,7 +24,7 @@ def formula(benunit, period, parameters): # Get parameters from the parameter tree p_tfc = parameters( period - ).gov.hmrc.childcare_subsidies.tax_free_childcare.contribution + ).gov.dwp.childcare_subsidies.tax_free_childcare.contribution p_disabled = parameters(period).gov.dwp.universal_credit.elements.child From c9fbd0c735aa14902a3639e86188bd58b6cbf9f0 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 15:30:55 +0000 Subject: [PATCH 27/38] Use is_disabled_for_benefits variable --- .../tax_free_childcare_benefits.py | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py index 20ab4f2a..3c9f14b1 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py @@ -21,42 +21,36 @@ def formula(benunit, period, parameters): Returns: float: The calculated government contribution """ - # Get parameters from the parameter tree + # Get parameters p_tfc = parameters( period ).gov.dwp.childcare_subsidies.tax_free_childcare.contribution - p_disabled = parameters(period).gov.dwp.universal_credit.elements.child - - # Check eligibility conditions with explicit type conversion - meets_age_condition = benunit("child_age_eligible", period).astype( - bool - ) + # Check eligibility conditions + meets_age_condition = benunit("child_age_eligible", period) meets_income_condition = benunit.any( benunit.members("meets_income_requirements", period) - ).astype(bool) + ) childcare_eligible = benunit( "incompatibilities_childcare_eligible", period - ).astype(bool) + ) is_eligible = ( meets_age_condition & meets_income_condition & childcare_eligible ) - # Calculate maximum eligible childcare cost using vectorized operations - # Just check child and disability status directly in where conditions - max_amount = where( - benunit.members("is_child", period), - where( - is_eligible, - where( - p_disabled.amount > 0, - p_tfc.disabled_child, - p_tfc.standard_child, - ), - 0, - ), + # Calculate per-child amounts at the person level + is_child = benunit.members("is_child", period) + is_disabled = benunit.members("is_disabled_for_benefits", period) + + child_amounts = where( + is_child, + where(is_disabled, p_tfc.disabled_child, p_tfc.standard_child), 0, - ).max(axis=0) + ) + + # Reduce to benefit unit level by taking maximum + max_amount = benunit.max(child_amounts) + # Apply final eligibility check return where(is_eligible, max_amount, 0) From a2f73d202407fab32f1772f62c66a92622586a09 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 15:34:32 +0000 Subject: [PATCH 28/38] Add .astype(bool) to fix error --- .../tax_free_childcare/tax_free_childcare_benefits.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py index 3c9f14b1..c9e96abd 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py @@ -26,14 +26,16 @@ def formula(benunit, period, parameters): period ).gov.dwp.childcare_subsidies.tax_free_childcare.contribution - # Check eligibility conditions - meets_age_condition = benunit("child_age_eligible", period) + # Check eligibility conditions with explicit type conversion + meets_age_condition = benunit("child_age_eligible", period).astype( + bool + ) meets_income_condition = benunit.any( benunit.members("meets_income_requirements", period) - ) + ).astype(bool) childcare_eligible = benunit( "incompatibilities_childcare_eligible", period - ) + ).astype(bool) is_eligible = ( meets_age_condition & meets_income_condition & childcare_eligible From a0618773a2a16ec1923269a77bd9e7d24827b21f Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 15:44:41 +0000 Subject: [PATCH 29/38] Fix bitwise AND operation error --- .../dwp/tax_free_childcare/tax_free_childcare_benefits.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py index c9e96abd..fddc54a1 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py @@ -37,8 +37,9 @@ def formula(benunit, period, parameters): "incompatibilities_childcare_eligible", period ).astype(bool) - is_eligible = ( - meets_age_condition & meets_income_condition & childcare_eligible + # Combine conditions using logical AND + is_eligible = np.logical_and.reduce( + [meets_age_condition, meets_income_condition, childcare_eligible] ) # Calculate per-child amounts at the person level From a9ca8a76b6a1db4f83b859955c2d752574ffde9e Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 15:50:26 +0000 Subject: [PATCH 30/38] Add .astype(bool) to conditions --- .../childcare_age_child_condition.py | 11 ++++--- .../conditions/childcare_income_condition.py | 4 +-- .../childcare_incompatibilities_condition.py | 8 ++--- .../conditions/childcare_work_condition.py | 32 +++++++++++-------- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py index 88fece09..d4156e11 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -39,13 +39,14 @@ def formula(person, period, parameters): severe_benefits = add( person, period, severe_disability_benefits ).astype(bool) - is_disabled = standard_benefits | severe_benefits + is_disabled = (standard_benefits | severe_benefits).astype(bool) # Check age conditions using parameterized values - basic_age_condition = age < standard_age_limit - age_under_disability_limit = age < disability_age_limit + basic_age_condition = (age < standard_age_limit).astype(bool) + age_under_disability_limit = (age < disability_age_limit).astype(bool) # Convert to boolean before final combination - return basic_age_condition.astype(bool) | ( - age_under_disability_limit.astype(bool) & is_disabled + combined_condition = (age_under_disability_limit & is_disabled).astype( + bool ) + return (basic_age_condition | combined_condition).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py index fd2a6770..1b0427a0 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py @@ -42,5 +42,5 @@ def formula(person, period, parameters): required_threshold = income_limits.calc(age) - # Compare quarterly income to required threshold - return quarterly_income >= required_threshold + # Compare quarterly income to required threshold and convert to boolean + return (quarterly_income >= required_threshold).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py index 8a89213f..60f235f9 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py @@ -19,9 +19,9 @@ def formula(person, period, parameters): benunit = person.benunit # Check if receiving any of the mutually exclusive benefits - has_wtc = benunit("working_tax_credit", period) > 0 - has_ctc = benunit("child_tax_credit", period) > 0 - has_uc = benunit("universal_credit", period) > 0 + has_wtc = (benunit("working_tax_credit", period) > 0).astype(bool) + has_ctc = (benunit("child_tax_credit", period) > 0).astype(bool) + has_uc = (benunit("universal_credit", period) > 0).astype(bool) # Returns True when person's benefit unit does NOT receive any of these benefits - return ~(has_wtc | has_ctc | has_uc) + return ~(has_wtc | has_ctc | has_uc).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py index eb28187d..d7f8bd5a 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py @@ -14,10 +14,10 @@ def formula(person, period, parameters): - Couple where either both work or one works and other has disability/incapacity """ benunit = person.benunit - is_adult = person("is_adult", period) + is_adult = person("is_adult", period).astype(bool) # Basic work status - in_work = person("in_work", period) + in_work = person("in_work", period).astype(bool) # Get disability/incapacity conditions like we did in childcare age eligibility gc = parameters(period).gov.dwp.pension_credit.guarantee_credit @@ -25,33 +25,37 @@ def formula(person, period, parameters): severe_disability_benefits = gc.child.disability.severe.eligibility is_disabled = ( - add(person, period, standard_disability_benefits) - | add(person, period, severe_disability_benefits) - ) > 0 + (add(person, period, standard_disability_benefits) > 0) + | (add(person, period, severe_disability_benefits) > 0) + ).astype(bool) - has_incapacity = person("incapacity_benefit", period) > 0 + has_incapacity = (person("incapacity_benefit", period) > 0).astype( + bool + ) # Build conditions # Single adult conditions - is_single = benunit.sum(is_adult) == 1 - single_working = is_single & in_work + is_single = (benunit.sum(is_adult) == 1).astype(bool) + single_working = (is_single & in_work).astype(bool) # Couple conditions - is_couple = benunit.sum(is_adult) == 2 + is_couple = (benunit.sum(is_adult) == 2).astype(bool) partner_in_work = in_work - partner_has_condition = is_disabled | has_incapacity + partner_has_condition = (is_disabled | has_incapacity).astype(bool) - couple_both_working = is_couple & in_work & partner_in_work + couple_both_working = (is_couple & in_work & partner_in_work).astype( + bool + ) is_partner_working_with_disabled_person = ( is_couple & partner_in_work & (is_disabled | has_incapacity) - ) + ).astype(bool) is_person_working_with_disabled_partner = ( is_couple & in_work & partner_has_condition - ) + ).astype(bool) return ( single_working | couple_both_working | is_person_working_with_disabled_partner | is_partner_working_with_disabled_person - ) + ).astype(bool) From c7c6762c96ef2641c30e2948ec7413aad47ebcf8 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 18:46:57 +0000 Subject: [PATCH 31/38] Add unit tests --- .../tax_free_childcare/income_thresholds.yaml | 2 +- .../childcare_age_child_condition.yaml | 31 ++++ .../childcare_income_condition.yaml | 114 ++++++++++++ .../childcare_work_condition.yaml | 163 ++++++++++++++++++ .../incompatibilities_childcare_eligible.yaml | 38 ++++ .../tax_free_childcare_benefits.yaml | 109 ++++++++++++ .../childcare_age_child_condition.py | 27 +-- .../conditions/childcare_work_condition.py | 30 ++-- 8 files changed, 474 insertions(+), 40 deletions(-) create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml index 343605a0..0b76070a 100644 --- a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -12,7 +12,7 @@ brackets: - threshold: 2015-01-01: 0 amount: - 2015-01-01: 0 + 2015-01-01: 1_331 - threshold: 2015-01-01: 18 amount: diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml new file mode 100644 index 00000000..6f397992 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml @@ -0,0 +1,31 @@ +- name: Under standard age limit - eligible + period: 2025 + input: + age: 10 + is_disabled_for_benefits: false + output: + child_age_eligible: true + +- name: Over standard age limit and not disabled - ineligible + period: 2025 + input: + age: 12 + is_disabled_for_benefits: false + output: + child_age_eligible: false + +- name: Over standard age but under disability limit and disabled - eligible + period: 2025 + input: + age: 15 + is_disabled_for_benefits: true + output: + child_age_eligible: true + +- name: Over disability age limit and disabled - ineligible + period: 2025 + input: + age: 17 + is_disabled_for_benefits: true + output: + child_age_eligible: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml new file mode 100644 index 00000000..d50889af --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml @@ -0,0 +1,114 @@ +# Tests for age under 18 bracket (threshold: £1,331 quarterly) +- name: Under threshold for age 15 with low income and no other income sources - ineligible + period: 2025 + input: + age: 15 + total_income: 5000 + private_pension_income: 0 + savings_interest_income: 500 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: At threshold for age 15 - eligible + period: 2025 + input: + age: 15 + total_income: 6324 + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 200 + output: + meets_income_requirements: true + +# Tests for age 18-20 bracket (threshold: £1,788 quarterly) +- name: Under threshold for age 19 - ineligible + period: 2025 + input: + age: 19 + total_income: 7000 # £1,750 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: At threshold for age 19 - eligible + period: 2025 + input: + age: 18 + total_income: 7152 # £1,788 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true + +# Tests for age 21+ bracket (threshold: £2,379 quarterly) +- name: Under threshold for age 22 - ineligible + period: 2025 + input: + age: 22 + total_income: 9000 # £2,250 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: age 18 - eligible + period: 2025 + input: + age: 18 + total_income: 9516 # £2,379 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true + +# Tests with investment income +- name: Above threshold but with investment income making eligible income below threshold - ineligible + period: 2025 + input: + age: 22 + total_income: 12000 + private_pension_income: 3000 + savings_interest_income: 500 + dividend_income: 1000 + property_income: 500 + # Net eligible income: 7000 (£1,750 quarterly) + output: + meets_income_requirements: false + +- name: Edge case with investment income exactly at threshold - eligible + period: 2025 + input: + age: 15 + total_income: 7324 + private_pension_income: 2000 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + # Net eligible income: 5324 (£1,331 quarterly) + output: + meets_income_requirements: true + +# Test age boundary cases +- name: Age boundary test at 18 - using higher threshold + period: 2025 + input: + age: 18 + total_income: 7152 + private_pension_income: 0 + savings_interest_income: 100 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml new file mode 100644 index 00000000..0b5ca14c --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml @@ -0,0 +1,163 @@ +- name: Couple both working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple neither working - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: false + +- name: Couple one working one with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple one working one with incapacity - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple one working without qualifying condition - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: false + +- name: Single working adult - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: true + +- name: Single non-working adult - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: false + +- name: Couple both with disability one working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Single working adult with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: true + +- name: Single disabled non-working adult - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml new file mode 100644 index 00000000..b4b6e366 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml @@ -0,0 +1,38 @@ +- name: Eligible - does NOT receive any of these benefits + period: 2025 + input: + working_tax_credit: 0 + child_tax_credit: 0 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: True + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 0 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: False + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 1 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: False + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 1 + universal_credit: 1 + output: + incompatibilities_childcare_eligible: False \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml new file mode 100644 index 00000000..4f949a60 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml @@ -0,0 +1,109 @@ +- name: Eligible family with standard child + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 2000 + +- name: Eligible family with disabled child + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 4000 + +- name: Ineligible due to not being a child + period: 2025 + input: + meets_income_requirements: true + is_child: false + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to age condition + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: false + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to income requirements + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to incompatibilities + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 + +- name: Disabled child but fails income requirements + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Disabled child but fails age condition + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: false + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Disabled child but fails incompatibilities + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 + +- name: Fails all eligibility conditions + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: false + child_age_eligible: false + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 \ No newline at end of file diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py index d4156e11..17cc7d63 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,7 +5,9 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = "Whether this person meets the age and disability requirements for eligibility" + documentation = ( + "Whether this person meets the age and disability requirements for eligibility" + ) definition_period = YEAR def formula(person, period, parameters): @@ -25,28 +27,13 @@ def formula(person, period, parameters): standard_age_limit = age_limits.standard disability_age_limit = age_limits.disability - # Check disability conditions - gc = parameters( - period - ).gov.dwp.pension_credit.guarantee_credit.child.disability - standard_disability_benefits = gc.eligibility - severe_disability_benefits = gc.severe.eligibility - - # Convert to boolean arrays before combining - standard_benefits = add( - person, period, standard_disability_benefits - ).astype(bool) - severe_benefits = add( - person, period, severe_disability_benefits - ).astype(bool) - is_disabled = (standard_benefits | severe_benefits).astype(bool) + # Check disability status + is_disabled = person("is_disabled_for_benefits", period) # Check age conditions using parameterized values basic_age_condition = (age < standard_age_limit).astype(bool) age_under_disability_limit = (age < disability_age_limit).astype(bool) - # Convert to boolean before final combination - combined_condition = (age_under_disability_limit & is_disabled).astype( - bool - ) + # Combine conditions + combined_condition = (age_under_disability_limit & is_disabled).astype(bool) return (basic_age_condition | combined_condition).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py index d7f8bd5a..bdf3922d 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,7 +5,9 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = "Whether the person/couple meets work requirements for tax-free childcare" + documentation = ( + "Whether the person/couple meets work requirements for tax-free childcare" + ) definition_period = YEAR def formula(person, period, parameters): @@ -29,9 +31,8 @@ def formula(person, period, parameters): | (add(person, period, severe_disability_benefits) > 0) ).astype(bool) - has_incapacity = (person("incapacity_benefit", period) > 0).astype( - bool - ) + has_incapacity = (person("incapacity_benefit", period) > 0).astype(bool) + has_condition = (is_disabled | has_incapacity).astype(bool) # Build conditions # Single adult conditions @@ -40,22 +41,13 @@ def formula(person, period, parameters): # Couple conditions is_couple = (benunit.sum(is_adult) == 2).astype(bool) - partner_in_work = in_work - partner_has_condition = (is_disabled | has_incapacity).astype(bool) - - couple_both_working = (is_couple & in_work & partner_in_work).astype( - bool - ) - is_partner_working_with_disabled_person = ( - is_couple & partner_in_work & (is_disabled | has_incapacity) - ).astype(bool) - is_person_working_with_disabled_partner = ( - is_couple & in_work & partner_has_condition + benunit_has_condition = benunit.any(has_condition) + benunit_has_worker = benunit.any(in_work) + couple_both_working = (is_couple & benunit.all(in_work)).astype(bool) + couple_one_working_one_disabled = ( + is_couple & benunit_has_worker & benunit_has_condition ).astype(bool) return ( - single_working - | couple_both_working - | is_person_working_with_disabled_partner - | is_partner_working_with_disabled_person + single_working | couple_both_working | couple_one_working_one_disabled ).astype(bool) From 59e1b78b4ff292dcbf9488de111d51a42387c5d1 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 18:50:23 +0000 Subject: [PATCH 32/38] Reformat with black --- .../conditions/childcare_age_child_condition.py | 8 ++++---- .../conditions/childcare_work_condition.py | 12 +++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py index 17cc7d63..29dd7894 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,9 +5,7 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = ( - "Whether this person meets the age and disability requirements for eligibility" - ) + documentation = "Whether this person meets the age and disability requirements for eligibility" definition_period = YEAR def formula(person, period, parameters): @@ -35,5 +33,7 @@ def formula(person, period, parameters): age_under_disability_limit = (age < disability_age_limit).astype(bool) # Combine conditions - combined_condition = (age_under_disability_limit & is_disabled).astype(bool) + combined_condition = (age_under_disability_limit & is_disabled).astype( + bool + ) return (basic_age_condition | combined_condition).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py index bdf3922d..44cceec6 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,9 +5,7 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = ( - "Whether the person/couple meets work requirements for tax-free childcare" - ) + documentation = "Whether the person/couple meets work requirements for tax-free childcare" definition_period = YEAR def formula(person, period, parameters): @@ -31,7 +29,9 @@ def formula(person, period, parameters): | (add(person, period, severe_disability_benefits) > 0) ).astype(bool) - has_incapacity = (person("incapacity_benefit", period) > 0).astype(bool) + has_incapacity = (person("incapacity_benefit", period) > 0).astype( + bool + ) has_condition = (is_disabled | has_incapacity).astype(bool) # Build conditions @@ -49,5 +49,7 @@ def formula(person, period, parameters): ).astype(bool) return ( - single_working | couple_both_working | couple_one_working_one_disabled + single_working + | couple_both_working + | couple_one_working_one_disabled ).astype(bool) From cebaace89cbd280a180d49779902e86b14624649 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 15 Jan 2025 14:35:35 +0000 Subject: [PATCH 33/38] Fix the folders --- .../conditions/childcare_age_child_condition.py | 0 .../tax_free_childcare/conditions/childcare_income_condition.py | 0 .../conditions/childcare_incompatibilities_condition.py | 0 .../tax_free_childcare/conditions/childcare_work_condition.py | 0 .../tax_free_childcare/tax_free_childcare_benefits.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_age_child_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_income_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_incompatibilities_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_work_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/tax_free_childcare_benefits.py (100%) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py From c1a5cf08e27f5026f89e8d8f83682fe357f276a7 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 15 Jan 2025 14:53:33 +0000 Subject: [PATCH 34/38] Fix the folders --- .../conditions/childcare_age_child_condition.py | 0 .../tax_free_childcare/conditions/childcare_income_condition.py | 0 .../conditions/childcare_incompatibilities_condition.py | 0 .../tax_free_childcare/conditions/childcare_work_condition.py | 0 .../tax_free_childcare/tax_free_childcare_benefits.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_age_child_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_income_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_incompatibilities_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/conditions/childcare_work_condition.py (100%) rename policyengine_uk/variables/gov/dwp/{ => childcare_subsidies}/tax_free_childcare/tax_free_childcare_benefits.py (100%) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_income_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_incompatibilities_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py similarity index 100% rename from policyengine_uk/variables/gov/dwp/tax_free_childcare/tax_free_childcare_benefits.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py From 4a5cad5f41bdacd519ef07f57299b0fab11ce4a7 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 15 Jan 2025 18:35:09 +0000 Subject: [PATCH 35/38] Implement free childcare program --- .../age/lower_limit_3_years.yaml | 12 ++ .../age/lower_limit_9_months.yaml | 12 ++ .../age/upper_limit_3_years.yaml | 12 ++ .../age/upper_limit_4_years.yaml | 12 ++ .../free_childcare/income_thresholds.yaml | 23 +++ .../free_childcare/max_income_thresholds.yaml | 12 ++ .../free_childcare/15_hours_eligible.yaml | 41 +++++ .../free_childcare/30_hours_eligible.yaml | 48 ++++++ .../free_childcare/income_eligible.yaml | 119 +++++++++++++ .../free_childcare/program_eligible.yaml | 89 ++++++++++ .../free_childcare/work_eligible.yaml | 163 ++++++++++++++++++ .../age_child_condition_15_hours.py | 32 ++++ .../age_child_condition_30_hours.py | 32 ++++ .../conditions/income_condition.py | 52 ++++++ .../conditions/work_condition.py | 45 +++++ .../free_childcare/free_childcare_eligible.py | 46 +++++ 16 files changed, 750 insertions(+) create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_3_years.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_9_months.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_3_years.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_4_years.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/income_thresholds.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/max_income_thresholds.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/15_hours_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/30_hours_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/program_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/work_eligible.yaml create mode 100644 policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py create mode 100644 policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py create mode 100644 policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py create mode 100644 policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py create mode 100644 policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_3_years.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_3_years.yaml new file mode 100644 index 00000000..c3497e46 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_3_years.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for free childcare eligibility +metadata: + unit: year + period: year + label: Age limit for free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/uksi/2017/1160/pdfs/uksiem_20171160_en.pdf + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/check-eligible-free-childcare-if-youre-working?step-by-step-nav=f517cd57-3c18-4bb9-aa8b-1b907e279bf9 +values: + 2015-01-01: 3 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_9_months.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_9_months.yaml new file mode 100644 index 00000000..9be87351 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/lower_limit_9_months.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for free childcare eligibility +metadata: + unit: year + period: year + label: Age limit for free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/uksi/2017/1160/pdfs/uksiem_20171160_en.pdf + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/check-eligible-free-childcare-if-youre-working?step-by-step-nav=f517cd57-3c18-4bb9-aa8b-1b907e279bf9 +values: + 2015-01-01: 0.75 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_3_years.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_3_years.yaml new file mode 100644 index 00000000..c3497e46 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_3_years.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for free childcare eligibility +metadata: + unit: year + period: year + label: Age limit for free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/uksi/2017/1160/pdfs/uksiem_20171160_en.pdf + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/check-eligible-free-childcare-if-youre-working?step-by-step-nav=f517cd57-3c18-4bb9-aa8b-1b907e279bf9 +values: + 2015-01-01: 3 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_4_years.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_4_years.yaml new file mode 100644 index 00000000..138a3361 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/age/upper_limit_4_years.yaml @@ -0,0 +1,12 @@ +description: Age thresholds for free childcare eligibility +metadata: + unit: year + period: year + label: Age limit for free childcare eligibility + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/uksi/2017/1160/pdfs/uksiem_20171160_en.pdf + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/check-eligible-free-childcare-if-youre-working?step-by-step-nav=f517cd57-3c18-4bb9-aa8b-1b907e279bf9 +values: + 2015-01-01: 4 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/income_thresholds.yaml new file mode 100644 index 00000000..0b76070a --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/income_thresholds.yaml @@ -0,0 +1,23 @@ +description: Income thresholds for tax-free childcare eligibility +metadata: + period: year + label: Income thresholds for tax-free childcare eligibility + reference: + - title: Childcare Payments Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d + +brackets: + - threshold: + 2015-01-01: 0 + amount: + 2015-01-01: 1_331 + - threshold: + 2015-01-01: 18 + amount: + 2015-01-01: 1_788 + - threshold: + 2015-01-01: 21 + amount: + 2015-01-01: 2_379 \ No newline at end of file diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/max_income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/max_income_thresholds.yaml new file mode 100644 index 00000000..aae9ddbe --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/free_childcare/max_income_thresholds.yaml @@ -0,0 +1,12 @@ +description: max income threshold +metadata: + unit: year + period: year + label: max income threshold + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/uksi/2017/1160/pdfs/uksiem_20171160_en.pdf + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/check-eligible-free-childcare-if-youre-working?step-by-step-nav=f517cd57-3c18-4bb9-aa8b-1b907e279bf9 +values: + 2015-01-01: 100_000 \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/15_hours_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/15_hours_eligible.yaml new file mode 100644 index 00000000..fa786311 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/15_hours_eligible.yaml @@ -0,0 +1,41 @@ +- name: Child too young - ineligible + period: 2025 + input: + age: 0.5 # 6 months + output: + free_childcare_15_hours: false + +- name: Child exactly at minimum age - eligible + period: 2025 + input: + age: 0.75 # 9 months + output: + free_childcare_15_hours: true + +- name: Child in middle of eligible range - eligible + period: 2025 + input: + age: 1.5 # 18 months + output: + free_childcare_15_hours: true + +- name: Child at maximum age - eligible + period: 2025 + input: + age: 2.0 # 24 months + output: + free_childcare_15_hours: true + +- name: eligible + period: 2025 + input: + age: 2.1 # 25 months + output: + free_childcare_15_hours: true + +- name: eligible + period: 2025 + input: + age: 3.1 # 25 months + output: + free_childcare_15_hours: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/30_hours_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/30_hours_eligible.yaml new file mode 100644 index 00000000..2a548854 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/30_hours_eligible.yaml @@ -0,0 +1,48 @@ +- name: Child too young - ineligible + period: 2025 + input: + age: 2.99 # Just under 3 years + output: + free_childcare_30_hours: false + +- name: Child exactly at minimum age - eligible + period: 2025 + input: + age: 3.0 # Exactly 3 years + output: + free_childcare_30_hours: true + +- name: Child in early eligible range - eligible + period: 2025 + input: + age: 3.2 # 3 years and ~2.5 months + output: + free_childcare_30_hours: true + +- name: Child in middle of eligible range - eligible + period: 2025 + input: + age: 3.5 # 3.5 years + output: + free_childcare_30_hours: true + +- name: Child in late eligible range - eligible + period: 2025 + input: + age: 3.8 # 3 years and ~10 months + output: + free_childcare_30_hours: true + +- name: Child at maximum age - eligible + period: 2025 + input: + age: 4.0 # Exactly 4 years + output: + free_childcare_30_hours: true + +- name: Child too old - ineligible + period: 2025 + input: + age: 4.1 # Just over 4 years + output: + free_childcare_30_hours: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml new file mode 100644 index 00000000..128bca03 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml @@ -0,0 +1,119 @@ +- name: Under 18 meeting yearly threshold + period: 2025 + input: + age: 17 + total_income: 5400 # Above £1,331 * 4 = £5,324 yearly threshold + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: true + +- name: Just turning 18 with yearly threshold + period: 2025 + input: + age: 18 + total_income: 7200 # Above £1,788 * 4 = £7,152 yearly threshold + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: true + +- name: Age 20 meeting 18-20 yearly threshold + period: 2025 + input: + age: 20 + total_income: 22000 + private_pension_income: 10 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: true + +- name: Age 21+ meeting higher yearly threshold + period: 2025 + input: + age: 25 + total_income: 29600 # Above £2,379 * 4 = £9,516 yearly threshold + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: true + +- name: Below yearly threshold after investment deductions + period: 2025 + input: + age: 30 + total_income: 12000 + private_pension_income: 2000 + savings_interest_income: 1000 + dividend_income: 500 # Leaving £8,500 yearly < £9,516 threshold + property_income: 0 + output: + income_requirements: false + +- name: Exactly at 21+ yearly threshold after deductions + period: 2025 + input: + age: 30 + total_income: 12516 + private_pension_income: 2000 # Leaving exactly £9,516 yearly + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: true + +- name: Under 18 below yearly threshold + period: 2025 + input: + age: 16 + total_income: 5000 # Below £5,324 yearly threshold + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: false + +- name: Age 18-20 with investment reducing below yearly threshold + period: 2025 + input: + age: 19 + total_income: 8000 + private_pension_income: 0 + savings_interest_income: 1000 # Leaving £7,000 < £7,152 yearly threshold + dividend_income: 0 + property_income: 0 + output: + income_requirements: false + +- name: All investment income + period: 2025 + input: + age: 25 + total_income: 15000 + private_pension_income: 5000 + savings_interest_income: 4000 + dividend_income: 3000 + property_income: 3000 # All income is investment = £0 eligible + output: + income_requirements: false + +- name: At maximum threshold + period: 2025 + input: + age: 35 + total_income: 1000000 # At assumed maximum threshold + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + income_requirements: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/program_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/program_eligible.yaml new file mode 100644 index 00000000..9fe00d41 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/program_eligible.yaml @@ -0,0 +1,89 @@ +- name: Family eligible for 15 hours - all conditions met + period: 2025 + input: + free_childcare_15_hours: true + income_requirements: true + work_eligibility_childcare: true + output: + free_childcare_15_hours_eligibility: true + +- name: Family eligible for 30 hours - all conditions met + period: 2025 + input: + free_childcare_30_hours: true + income_requirements: true + work_eligibility_childcare: true + output: + free_childcare_30_hours_eligibility: true + +- name: Family fails age test for 15 hours + period: 2025 + input: + free_childcare_15_hours: false + income_requirements: true + work_eligibility_childcare: true + output: + free_childcare_15_hours_eligibility: false + +- name: Family fails age test for 30 hours + period: 2025 + input: + free_childcare_30_hours: false + income_requirements: true + work_eligibility_childcare: true + output: + free_childcare_30_hours_eligibility: false + +- name: Family fails income test for 15 hours + period: 2025 + input: + free_childcare_15_hours: true + income_requirements: false + work_eligibility_childcare: true + output: + free_childcare_15_hours_eligibility: false + +- name: Family fails income test for 30 hours + period: 2025 + input: + free_childcare_30_hours: true + income_requirements: false + work_eligibility_childcare: true + output: + free_childcare_30_hours_eligibility: false + +- name: Family fails work test for 15 hours + period: 2025 + input: + free_childcare_15_hours: true + income_requirements: true + work_eligibility_childcare: false + output: + free_childcare_15_hours_eligibility: false + +- name: Family fails work test for 30 hours + period: 2025 + input: + free_childcare_30_hours: true + income_requirements: true + work_eligibility_childcare: false + output: + free_childcare_30_hours_eligibility: false + +- name: Family fails all tests for 15 hours + period: 2025 + input: + free_childcare_15_hours: false + income_requirements: false + work_eligibility_childcare: false + output: + free_childcare_15_hours_eligibility: false + +- name: Family fails all tests for 30 hours + period: 2025 + input: + free_childcare_30_hours: false + income_requirements: false + work_eligibility_childcare: false + output: + free_childcare_30_hours_eligibility: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/work_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/work_eligible.yaml new file mode 100644 index 00000000..843bf2bf --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/work_eligible.yaml @@ -0,0 +1,163 @@ +- name: Couple both working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: true + +- name: Couple neither working - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: false + +- name: Couple one working one with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: true + +- name: Couple one working one with incapacity - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: true + +- name: Couple one working without qualifying condition - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: false + +- name: Single working adult - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + work_eligibility_childcare: true + +- name: Single non-working adult - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + work_eligibility_childcare: false + +- name: Couple both with disability one working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + work_eligibility_childcare: true + +- name: Single working adult with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + work_eligibility_childcare: true + +- name: Single disabled non-working adult - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + work_eligibility_childcare: true \ No newline at end of file diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py new file mode 100644 index 00000000..f718dd2a --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py @@ -0,0 +1,32 @@ +from policyengine_uk.model_api import * + + +class free_childcare_15_hours(Variable): + value_type = bool + entity = Person + label = "Free childcare 15 hours eligibility" + documentation = "Whether this child meets the age requirements for 15 hours of free childcare" + definition_period = YEAR # Evaluate eligibility on a yearly basis + + def formula(person, period, parameters): + """ + Determine if a child is eligible for 15 hours of free childcare based on age. + + Returns: + bool: True if the child is eligible for 15 hours, False otherwise. + """ + # Get the child's age + age = person("age", period) + + # Retrieve age thresholds for 15 hours eligibility + age_limits = parameters( + period + ).gov.dwp.childcare_subsidies.free_childcare.age + lower_limit_9_months = age_limits.lower_limit_9_months + upper_limit_3_years = age_limits.upper_limit_3_years + + # Check if the child is eligible for 15 hours (between 3 and 4 years old) + eligible_15_hours = (age >= lower_limit_9_months) & (age <= upper_limit_3_years) + + # Return the eligibility status + return eligible_15_hours.astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py new file mode 100644 index 00000000..d515726d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py @@ -0,0 +1,32 @@ +from policyengine_uk.model_api import * + + +class free_childcare_30_hours(Variable): + value_type = bool + entity = Person + label = "Free childcare 30 hours eligibility" + documentation = "Whether this child meets the age requirements for 30 hours of free childcare" + definition_period = YEAR # Evaluate eligibility on a yearly basis + + def formula(person, period, parameters): + """ + Determine if a child is eligible for 30 hours of free childcare based on age. + + Returns: + bool: True if the child is eligible for 30 hours, False otherwise. + """ + # Get the child's age + age = person("age", period) + + # Retrieve age thresholds for 30 hours eligibility + age_limits = parameters( + period + ).gov.dwp.childcare_subsidies.free_childcare.age + lower_limit_3_years = age_limits.lower_limit_3_years + upper_limit_4_years = age_limits.upper_limit_4_years + + # Check if the child is eligible for 30 hours (between 3 and 4 years old) + eligible_30_hours = (age >= lower_limit_3_years) & (age <= upper_limit_4_years) + + # Return the eligibility status + return eligible_30_hours.astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py new file mode 100644 index 00000000..e3f6d505 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py @@ -0,0 +1,52 @@ +from policyengine_uk.model_api import * + + +class income_requirements(Variable): + value_type = bool + entity = Person + label = "Income requirements and calculations" + documentation = "Whether this person meets the income requirements for free childcare based on age and income thresholds" + definition_period = YEAR + + def formula(person, period, parameters): + """ + Calculate if a person meets income requirements based on their age and income. + Returns: + bool: True if they meet the income conditions for their age group, and do not exceed the max threshold. + """ + # Get person's characteristics + age = person("age", period) + + # Calculate eligible income + total_income = person("total_income", period) + # Extract investment incomes to subtract + investment_income = add( + person, + period, + [ + "private_pension_income", + "savings_interest_income", + "dividend_income", + "property_income", + ], + ) + + yearly_eligible_income = max_(total_income - investment_income, 0) + # quarterly_income = yearly_eligible_income / 4 + + # Get required income threshold based on age + income_limits = parameters( + period + ).gov.dwp.childcare_subsidies.free_childcare.income_thresholds + + required_threshold = income_limits.calc(age) * 4 + + # Get max income threshold to check if they exceed it + max_income_threshold = parameters( + period + ).gov.dwp.childcare_subsidies.free_childcare.max_income_thresholds + + # Check if they meet the conditions: + # 1. Quarterly income is above the required threshold + # 2. Yearly income is below the max threshold + return ((yearly_eligible_income > required_threshold) & (yearly_eligible_income < max_income_threshold)).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py new file mode 100644 index 00000000..824fc5bb --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py @@ -0,0 +1,45 @@ +from policyengine_uk.model_api import * + + +class work_eligibility_childcare(Variable): + value_type = bool + entity = Person + label = "Alternative Eligibility for Free Childcare" + documentation = "Eligibility maintained for a single parent working or receiving incapacity benefit, or a couple with one or both working." + definition_period = YEAR + + def formula(person, period, parameters): + """ + Calculate eligibility for free childcare based on the following conditions: + - Single parent: working or not working but receiving incapacity benefit + - Couple: both working or one working and one receiving incapacity benefit + """ + # Get the benefit unit the person belongs to + benunit = person.benunit + is_adult = person("is_adult", period).astype(bool) + + # Check work status (in_work) + in_work = person("in_work", period).astype(bool) + + # Check if the person is receiving incapacity benefit (simplified condition) + has_incapacity_benefit = (person("incapacity_benefit", period) > 0).astype(bool) + + # Build conditions for Single Parent + is_single = (benunit.sum(is_adult) == 1).astype(bool) + single_working = is_single & in_work + single_not_working_but_eligible = is_single & ~in_work & has_incapacity_benefit + + # Build conditions for Couples + is_couple = (benunit.sum(is_adult) == 2).astype(bool) + couple_both_working = is_couple & benunit.all(in_work) + couple_one_working_one_disabled = ( + is_couple & benunit.any(in_work) & benunit.any(has_incapacity_benefit) + ).astype(bool) + + # Return eligibility for single working, single not working but eligible, couple both working, or couple one working one receiving benefit + return ( + single_working + | single_not_working_but_eligible + | couple_both_working + | couple_one_working_one_disabled + ).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py new file mode 100644 index 00000000..c04ff876 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py @@ -0,0 +1,46 @@ +from policyengine_uk.model_api import * + +class free_childcare_15_hours_eligibility(Variable): + value_type = bool + entity = BenUnit + label = "15 hours free childcare full eligibility" + documentation = "Whether the benefit unit is eligible for 15 hours of free childcare" + definition_period = YEAR + + def formula(benunit, period, parameters): + """ + Calculate eligibility for 15 hours free childcare program. + """ + # Age eligibility + age_eligible = benunit.any(benunit.members("free_childcare_15_hours", period)) + + # Income requirements + meets_income = benunit.any(benunit.members("income_requirements", period)) + + # Alternative eligibility + alternative_eligible = benunit.any(benunit.members("work_eligibility_childcare", period)) + + return (age_eligible & meets_income & alternative_eligible).astype(bool) + + +class free_childcare_30_hours_eligibility(Variable): + value_type = bool + entity = BenUnit + label = "30 hours free childcare full eligibility" + documentation = "Whether the benefit unit is eligible for 30 hours of free childcare" + definition_period = YEAR + + def formula(benunit, period, parameters): + """ + Calculate eligibility for 30 hours free childcare program. + """ + # Age eligibility + age_eligible = benunit.any(benunit.members("free_childcare_30_hours", period)) + + # Income requirements + meets_income = benunit.any(benunit.members("income_requirements", period)) + + # Alternative eligibility + alternative_eligible = benunit.any(benunit.members("work_eligibility_childcare", period)) + + return (age_eligible & meets_income & alternative_eligible).astype(bool) \ No newline at end of file From 4432db277075e904c659abdcc7ffa28f1c27d36d Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 15 Jan 2025 18:40:56 +0000 Subject: [PATCH 36/38] Format with black --- .../age_child_condition_15_hours.py | 4 +- .../age_child_condition_30_hours.py | 4 +- .../conditions/income_condition.py | 7 ++- .../conditions/work_condition.py | 12 +++- .../free_childcare/free_childcare_eligible.py | 57 +++++++++++++------ 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py index f718dd2a..85105fb2 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_15_hours.py @@ -26,7 +26,9 @@ def formula(person, period, parameters): upper_limit_3_years = age_limits.upper_limit_3_years # Check if the child is eligible for 15 hours (between 3 and 4 years old) - eligible_15_hours = (age >= lower_limit_9_months) & (age <= upper_limit_3_years) + eligible_15_hours = (age >= lower_limit_9_months) & ( + age <= upper_limit_3_years + ) # Return the eligibility status return eligible_15_hours.astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py index d515726d..cb60348f 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/age_child_condition_30_hours.py @@ -26,7 +26,9 @@ def formula(person, period, parameters): upper_limit_4_years = age_limits.upper_limit_4_years # Check if the child is eligible for 30 hours (between 3 and 4 years old) - eligible_30_hours = (age >= lower_limit_3_years) & (age <= upper_limit_4_years) + eligible_30_hours = (age >= lower_limit_3_years) & ( + age <= upper_limit_4_years + ) # Return the eligibility status return eligible_30_hours.astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py index e3f6d505..9d3ad697 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/income_condition.py @@ -46,7 +46,10 @@ def formula(person, period, parameters): period ).gov.dwp.childcare_subsidies.free_childcare.max_income_thresholds - # Check if they meet the conditions: + # Check if they meet the conditions: # 1. Quarterly income is above the required threshold # 2. Yearly income is below the max threshold - return ((yearly_eligible_income > required_threshold) & (yearly_eligible_income < max_income_threshold)).astype(bool) + return ( + (yearly_eligible_income > required_threshold) + & (yearly_eligible_income < max_income_threshold) + ).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py index 824fc5bb..637c3f32 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/conditions/work_condition.py @@ -22,18 +22,24 @@ def formula(person, period, parameters): in_work = person("in_work", period).astype(bool) # Check if the person is receiving incapacity benefit (simplified condition) - has_incapacity_benefit = (person("incapacity_benefit", period) > 0).astype(bool) + has_incapacity_benefit = ( + person("incapacity_benefit", period) > 0 + ).astype(bool) # Build conditions for Single Parent is_single = (benunit.sum(is_adult) == 1).astype(bool) single_working = is_single & in_work - single_not_working_but_eligible = is_single & ~in_work & has_incapacity_benefit + single_not_working_but_eligible = ( + is_single & ~in_work & has_incapacity_benefit + ) # Build conditions for Couples is_couple = (benunit.sum(is_adult) == 2).astype(bool) couple_both_working = is_couple & benunit.all(in_work) couple_one_working_one_disabled = ( - is_couple & benunit.any(in_work) & benunit.any(has_incapacity_benefit) + is_couple + & benunit.any(in_work) + & benunit.any(has_incapacity_benefit) ).astype(bool) # Return eligibility for single working, single not working but eligible, couple both working, or couple one working one receiving benefit diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py index c04ff876..839ce4d3 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/free_childcare/free_childcare_eligible.py @@ -1,46 +1,67 @@ from policyengine_uk.model_api import * + class free_childcare_15_hours_eligibility(Variable): value_type = bool entity = BenUnit label = "15 hours free childcare full eligibility" - documentation = "Whether the benefit unit is eligible for 15 hours of free childcare" + documentation = ( + "Whether the benefit unit is eligible for 15 hours of free childcare" + ) definition_period = YEAR - + def formula(benunit, period, parameters): """ Calculate eligibility for 15 hours free childcare program. """ # Age eligibility - age_eligible = benunit.any(benunit.members("free_childcare_15_hours", period)) - + age_eligible = benunit.any( + benunit.members("free_childcare_15_hours", period) + ) + # Income requirements - meets_income = benunit.any(benunit.members("income_requirements", period)) - + meets_income = benunit.any( + benunit.members("income_requirements", period) + ) + # Alternative eligibility - alternative_eligible = benunit.any(benunit.members("work_eligibility_childcare", period)) - - return (age_eligible & meets_income & alternative_eligible).astype(bool) + alternative_eligible = benunit.any( + benunit.members("work_eligibility_childcare", period) + ) + + return (age_eligible & meets_income & alternative_eligible).astype( + bool + ) class free_childcare_30_hours_eligibility(Variable): value_type = bool entity = BenUnit label = "30 hours free childcare full eligibility" - documentation = "Whether the benefit unit is eligible for 30 hours of free childcare" + documentation = ( + "Whether the benefit unit is eligible for 30 hours of free childcare" + ) definition_period = YEAR - + def formula(benunit, period, parameters): """ Calculate eligibility for 30 hours free childcare program. """ # Age eligibility - age_eligible = benunit.any(benunit.members("free_childcare_30_hours", period)) - + age_eligible = benunit.any( + benunit.members("free_childcare_30_hours", period) + ) + # Income requirements - meets_income = benunit.any(benunit.members("income_requirements", period)) - + meets_income = benunit.any( + benunit.members("income_requirements", period) + ) + # Alternative eligibility - alternative_eligible = benunit.any(benunit.members("work_eligibility_childcare", period)) - - return (age_eligible & meets_income & alternative_eligible).astype(bool) \ No newline at end of file + alternative_eligible = benunit.any( + benunit.members("work_eligibility_childcare", period) + ) + + return (age_eligible & meets_income & alternative_eligible).astype( + bool + ) From c7c19987fd1e0ee6da16fcb59121c998e2eb029a Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 15 Jan 2025 18:43:14 +0000 Subject: [PATCH 37/38] Change tests --- .../free_childcare/income_eligible.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml index 128bca03..34260880 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/free_childcare/income_eligible.yaml @@ -58,17 +58,6 @@ output: income_requirements: false -- name: Exactly at 21+ yearly threshold after deductions - period: 2025 - input: - age: 30 - total_income: 12516 - private_pension_income: 2000 # Leaving exactly £9,516 yearly - savings_interest_income: 0 - dividend_income: 0 - property_income: 0 - output: - income_requirements: true - name: Under 18 below yearly threshold period: 2025 From 789f67630d8c4b0bb55313697cde2bc2571d1f19 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Fri, 17 Jan 2025 13:11:59 +0000 Subject: [PATCH 38/38] Edit format based on Pavel's comments --- .../max_income_thresholds.yaml | 12 ++ .../childcare_age_child_condition.yaml | 8 +- .../childcare_income_condition.yaml | 33 +++-- .../childcare_work_condition.yaml | 20 +-- .../incompatibilities_childcare_eligible.yaml | 8 +- .../tax_free_childcare_benefits.yaml | 126 ++++++++---------- ...tax_free_childcare_age_child_condition.py} | 4 +- ...=> tax_free_childcare_income_condition.py} | 15 ++- ..._childcare_incompatibilities_condition.py} | 2 +- ...y => tax_free_childcare_work_condition.py} | 2 +- .../tax_free_childcare_benefits.py | 66 ++++++--- 11 files changed, 172 insertions(+), 124 deletions(-) create mode 100644 policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/max_income_thresholds.yaml rename policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/{childcare_age_child_condition.py => tax_free_childcare_age_child_condition.py} (91%) rename policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/{childcare_income_condition.py => tax_free_childcare_income_condition.py} (73%) rename policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/{childcare_incompatibilities_condition.py => tax_free_childcare_incompatibilities_condition.py} (93%) rename policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/{childcare_work_condition.py => tax_free_childcare_work_condition.py} (97%) diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/max_income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/max_income_thresholds.yaml new file mode 100644 index 00000000..a7cbe850 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/max_income_thresholds.yaml @@ -0,0 +1,12 @@ +description: max income threshold for tax free childcare +metadata: + unit: year + period: year + label: max income threshold + reference: + - title: Childcare Act + href: https://www.legislation.gov.uk/ukdsi/2015/9780111127063 + - title: Tax-Free Childcare Guidance + href: https://www.gov.uk/tax-free-childcare?step-by-step-nav=d78aeaf6-1747-4d72-9619-f16efb4dd89d +values: + 2015-01-01: 100_000 \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml index 6f397992..c107430c 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml @@ -4,7 +4,7 @@ age: 10 is_disabled_for_benefits: false output: - child_age_eligible: true + tax_free_childcare_child_age_eligible: true - name: Over standard age limit and not disabled - ineligible period: 2025 @@ -12,7 +12,7 @@ age: 12 is_disabled_for_benefits: false output: - child_age_eligible: false + tax_free_childcare_child_age_eligible: false - name: Over standard age but under disability limit and disabled - eligible period: 2025 @@ -20,7 +20,7 @@ age: 15 is_disabled_for_benefits: true output: - child_age_eligible: true + tax_free_childcare_child_age_eligible: true - name: Over disability age limit and disabled - ineligible period: 2025 @@ -28,4 +28,4 @@ age: 17 is_disabled_for_benefits: true output: - child_age_eligible: false \ No newline at end of file + tax_free_childcare_child_age_eligible: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml index d50889af..ee5234e6 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml @@ -9,7 +9,7 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: false + tax_free_childcare_meets_income_requirements: false - name: At threshold for age 15 - eligible period: 2025 @@ -21,7 +21,7 @@ dividend_income: 0 property_income: 200 output: - meets_income_requirements: true + tax_free_childcare_meets_income_requirements: true # Tests for age 18-20 bracket (threshold: £1,788 quarterly) - name: Under threshold for age 19 - ineligible @@ -34,7 +34,7 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: false + tax_free_childcare_meets_income_requirements: false - name: At threshold for age 19 - eligible period: 2025 @@ -46,7 +46,7 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: true + tax_free_childcare_meets_income_requirements: true # Tests for age 21+ bracket (threshold: £2,379 quarterly) - name: Under threshold for age 22 - ineligible @@ -59,7 +59,7 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: false + tax_free_childcare_meets_income_requirements: false - name: age 18 - eligible period: 2025 @@ -71,7 +71,7 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: true + tax_free_childcare_meets_income_requirements: true # Tests with investment income - name: Above threshold but with investment income making eligible income below threshold - ineligible @@ -85,20 +85,20 @@ property_income: 500 # Net eligible income: 7000 (£1,750 quarterly) output: - meets_income_requirements: false + tax_free_childcare_meets_income_requirements: false - name: Edge case with investment income exactly at threshold - eligible period: 2025 input: age: 15 - total_income: 7324 + total_income: 7524 private_pension_income: 2000 savings_interest_income: 0 dividend_income: 0 property_income: 0 # Net eligible income: 5324 (£1,331 quarterly) output: - meets_income_requirements: true + tax_free_childcare_meets_income_requirements: true # Test age boundary cases - name: Age boundary test at 18 - using higher threshold @@ -111,4 +111,17 @@ dividend_income: 0 property_income: 0 output: - meets_income_requirements: true + tax_free_childcare_meets_income_requirements: true + +# Test age boundary cases +- name: over 100k + period: 2025 + input: + age: 18 + total_income: 117152 + private_pension_income: 0 + savings_interest_income: 100 + dividend_income: 0 + property_income: 0 + output: + tax_free_childcare_meets_income_requirements: false diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml index 0b5ca14c..d859e67a 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml @@ -14,7 +14,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Couple neither working - ineligible period: 2025 @@ -32,7 +32,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: false + tax_free_childcare_work_condition: false - name: Couple one working one with disability - eligible period: 2025 @@ -50,7 +50,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Couple one working one with incapacity - eligible period: 2025 @@ -68,7 +68,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Couple one working without qualifying condition - ineligible period: 2025 @@ -86,7 +86,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: false + tax_free_childcare_work_condition: false - name: Single working adult - eligible period: 2025 @@ -100,7 +100,7 @@ benunit: members: [person] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Single non-working adult - ineligible period: 2025 @@ -114,7 +114,7 @@ benunit: members: [person] output: - childcare_work_condition: false + tax_free_childcare_work_condition: false - name: Couple both with disability one working - eligible period: 2025 @@ -132,7 +132,7 @@ benunit: members: [person, spouse] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Single working adult with disability - eligible period: 2025 @@ -146,7 +146,7 @@ benunit: members: [person] output: - childcare_work_condition: true + tax_free_childcare_work_condition: true - name: Single disabled non-working adult - ineligible period: 2025 @@ -160,4 +160,4 @@ benunit: members: [person] output: - childcare_work_condition: false \ No newline at end of file + tax_free_childcare_work_condition: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml index b4b6e366..22ef616d 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml @@ -5,7 +5,7 @@ child_tax_credit: 0 universal_credit: 0 output: - incompatibilities_childcare_eligible: True + tax_free_childcare_incompatibilities_childcare_eligible: True - name: Non eligible - receives benefits @@ -15,7 +15,7 @@ child_tax_credit: 0 universal_credit: 0 output: - incompatibilities_childcare_eligible: False + tax_free_childcare_incompatibilities_childcare_eligible: False - name: Non eligible - receives benefits @@ -25,7 +25,7 @@ child_tax_credit: 1 universal_credit: 0 output: - incompatibilities_childcare_eligible: False + tax_free_childcare_incompatibilities_childcare_eligible: False - name: Non eligible - receives benefits @@ -35,4 +35,4 @@ child_tax_credit: 1 universal_credit: 1 output: - incompatibilities_childcare_eligible: False \ No newline at end of file + tax_free_childcare_incompatibilities_childcare_eligible: False \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml index 4f949a60..8afb6897 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml @@ -1,109 +1,87 @@ -- name: Eligible family with standard child +# Tests for tax_free_childcare_overall_eligible +- name: All conditions met period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: false - child_age_eligible: true - incompatibilities_childcare_eligible: true + tax_free_childcare_child_age_eligible: true + tax_free_childcare_meets_income_requirements: true + tax_free_childcare_incompatibilities_childcare_eligible: true + tax_free_childcare_work_condition: true output: - tax_free_childcare: 2000 + tax_free_childcare_overall_eligible: true -- name: Eligible family with disabled child +- name: Fails age condition only period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: true - child_age_eligible: true - incompatibilities_childcare_eligible: true + tax_free_childcare_child_age_eligible: false + tax_free_childcare_meets_income_requirements: true + tax_free_childcare_incompatibilities_childcare_eligible: true + tax_free_childcare_work_condition: true output: - tax_free_childcare: 4000 + tax_free_childcare_overall_eligible: false -- name: Ineligible due to not being a child +- name: Fails income condition only period: 2025 input: - meets_income_requirements: true - is_child: false - is_disabled_for_benefits: false - child_age_eligible: true - incompatibilities_childcare_eligible: true + tax_free_childcare_child_age_eligible: true + tax_free_childcare_meets_income_requirements: false + tax_free_childcare_incompatibilities_childcare_eligible: true + tax_free_childcare_work_condition: true output: - tax_free_childcare: 0 + tax_free_childcare_overall_eligible: false -- name: Ineligible due to age condition +- name: Fails work condition only period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: false - child_age_eligible: false - incompatibilities_childcare_eligible: true + tax_free_childcare_child_age_eligible: true + tax_free_childcare_meets_income_requirements: true + tax_free_childcare_incompatibilities_childcare_eligible: true + tax_free_childcare_work_condition: false output: - tax_free_childcare: 0 + tax_free_childcare_overall_eligible: false -- name: Ineligible due to income requirements +- name: Fails all conditions period: 2025 input: - meets_income_requirements: false - is_child: true - is_disabled_for_benefits: false - child_age_eligible: true - incompatibilities_childcare_eligible: true + tax_free_childcare_child_age_eligible: false + tax_free_childcare_meets_income_requirements: false + tax_free_childcare_incompatibilities_childcare_eligible: false + tax_free_childcare_work_condition: false output: - tax_free_childcare: 0 + tax_free_childcare_overall_eligible: false -- name: Ineligible due to incompatibilities +# Tests for tax_free_childcare +- name: Eligible standard child period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: false - child_age_eligible: true - incompatibilities_childcare_eligible: false + tax_free_childcare_overall_eligible: true + is_child: true + is_disabled_for_benefits: false output: - tax_free_childcare: 0 + tax_free_childcare: 2000 -- name: Disabled child but fails income requirements +- name: Eligible disabled child period: 2025 input: - meets_income_requirements: false - is_child: true - is_disabled_for_benefits: true - child_age_eligible: true - incompatibilities_childcare_eligible: true + tax_free_childcare_overall_eligible: true + is_child: true + is_disabled_for_benefits: true output: - tax_free_childcare: 0 + tax_free_childcare: 4000 -- name: Disabled child but fails age condition +- name: Not eligible due to not being child period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: true - child_age_eligible: false - incompatibilities_childcare_eligible: true + tax_free_childcare_overall_eligible: true + is_child: false + is_disabled_for_benefits: false output: - tax_free_childcare: 0 + tax_free_childcare: 0 -- name: Disabled child but fails incompatibilities +- name: Not eligible despite being disabled child period: 2025 input: - meets_income_requirements: true - is_child: true - is_disabled_for_benefits: true - child_age_eligible: true - incompatibilities_childcare_eligible: false + tax_free_childcare_overall_eligible: false + is_child: true + is_disabled_for_benefits: true output: - tax_free_childcare: 0 - -- name: Fails all eligibility conditions - period: 2025 - input: - meets_income_requirements: false - is_child: true - is_disabled_for_benefits: false - child_age_eligible: false - incompatibilities_childcare_eligible: false - output: - tax_free_childcare: 0 \ No newline at end of file + tax_free_childcare: 0 diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_age_child_condition.py similarity index 91% rename from policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_age_child_condition.py index 29dd7894..231b88ea 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_age_child_condition.py @@ -1,10 +1,10 @@ from policyengine_uk.model_api import * -class child_age_eligible(Variable): +class tax_free_childcare_child_age_eligible(Variable): value_type = bool entity = Person - label = "Child age eligibility requirements" + label = "Child age eligibility requirements for tax-free childcare" documentation = "Whether this person meets the age and disability requirements for eligibility" definition_period = YEAR diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_income_condition.py similarity index 73% rename from policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_income_condition.py index 1b0427a0..b9dfbaab 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_income_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_income_condition.py @@ -1,10 +1,10 @@ from policyengine_uk.model_api import * -class meets_income_requirements(Variable): +class tax_free_childcare_meets_income_requirements(Variable): value_type = bool entity = Person - label = "Income requirements and calculations" + label = "Income requirements and calculations for tax-free childcare" documentation = "Whether this person meets the income requirements for tax-free childcare based on age and income thresholds" definition_period = YEAR @@ -42,5 +42,14 @@ def formula(person, period, parameters): required_threshold = income_limits.calc(age) + max_income_threshold = parameters( + period + ).gov.dwp.childcare_subsidies.tax_free_childcare.max_income_thresholds + + max_income_threshold_quarterly = max_income_threshold / 4 + # Compare quarterly income to required threshold and convert to boolean - return (quarterly_income >= required_threshold).astype(bool) + return ( + (quarterly_income > required_threshold) + & (quarterly_income < max_income_threshold_quarterly) + ).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_incompatibilities_condition.py similarity index 93% rename from policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_incompatibilities_condition.py index 60f235f9..988ca775 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_incompatibilities_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_incompatibilities_condition.py @@ -1,7 +1,7 @@ from policyengine_uk.model_api import * -class incompatibilities_childcare_eligible(Variable): +class tax_free_childcare_incompatibilities_childcare_eligible(Variable): value_type = bool entity = Person label = "Tax-Free Childcare Exclusions" diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_work_condition.py similarity index 97% rename from policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py rename to policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_work_condition.py index 44cceec6..0c9e547a 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/conditions/tax_free_childcare_work_condition.py @@ -1,7 +1,7 @@ from policyengine_uk.model_api import * -class childcare_work_condition(Variable): +class tax_free_childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" diff --git a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py index fddc54a1..72563252 100644 --- a/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py +++ b/policyengine_uk/variables/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.py @@ -1,6 +1,55 @@ from policyengine_uk.model_api import * +class tax_free_childcare_overall_eligible(Variable): + value_type = bool + entity = BenUnit + label = "Overall eligibility for tax-free childcare" + documentation = ( + "Combined result of all tax-free childcare eligibility conditions" + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + """ + Combines all eligibility conditions for tax-free childcare using logical AND. + + Args: + benunit: The benefit unit + period: The time period + parameters: Policy parameters + + Returns: + bool: Whether all eligibility conditions are met + """ + meets_age_condition = benunit( + "tax_free_childcare_child_age_eligible", period + ).astype(bool) + + meets_income_condition = benunit.any( + benunit.members( + "tax_free_childcare_meets_income_requirements", period + ) + ).astype(bool) + + childcare_eligible = benunit( + "tax_free_childcare_incompatibilities_childcare_eligible", period + ).astype(bool) + + work_eligible = benunit( + "tax_free_childcare_work_condition", period + ).astype(bool) + + return np.logical_and.reduce( + [ + meets_age_condition, + meets_income_condition, + childcare_eligible, + work_eligible, + ] + ) + + class tax_free_childcare(Variable): value_type = float entity = BenUnit @@ -26,21 +75,8 @@ def formula(benunit, period, parameters): period ).gov.dwp.childcare_subsidies.tax_free_childcare.contribution - # Check eligibility conditions with explicit type conversion - meets_age_condition = benunit("child_age_eligible", period).astype( - bool - ) - meets_income_condition = benunit.any( - benunit.members("meets_income_requirements", period) - ).astype(bool) - childcare_eligible = benunit( - "incompatibilities_childcare_eligible", period - ).astype(bool) - - # Combine conditions using logical AND - is_eligible = np.logical_and.reduce( - [meets_age_condition, meets_income_condition, childcare_eligible] - ) + # Get eligibility from separate class + is_eligible = benunit("tax_free_childcare_overall_eligible", period) # Calculate per-child amounts at the person level is_child = benunit.members("is_child", period)