-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tax free childcare #1004
base: master
Are you sure you want to change the base?
Tax free childcare #1004
Changes from 11 commits
adf96a0
ee24d61
12d24be
235fd08
b3b1967
55ce6c3
9952ed9
d6b22e5
b06a47c
90eb823
0c871a5
b28b5f8
ff1f456
d3ad685
5c6d359
e0221f8
fc83a03
4421154
b4e8d1a
cf7f6e6
d4ff3b2
0058402
37c7228
af96f3a
e96bc2e
153c2f6
c9fbd0c
a2f73d2
a061877
a9ca8a7
c7c6762
59e1b78
cebaace
c1a5cf0
49ec556
4a5cad5
4432db2
c7c1998
789f676
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,14 @@ | ||||||||
description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits. | ||||||||
metadata: | ||||||||
unit: years | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
name: childcare_age_limits | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
label: Age limits for tax-free childcare eligibility | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
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 |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. split this into three files: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
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 |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||||||||||||
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 standard age limit, or under disability age limit with disability), False otherwise | ||||||||||||||||||
""" | ||||||||||||||||||
# 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 | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
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_disability_limit & is_disabled | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
return eligible | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,65 @@ | ||||||
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, 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 | ||||||
""" | ||||||
# 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If the value cant be negative |
||||||
|
||||||
# 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 >= 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 < 18 | ||||||
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 | ||||||
) |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We then want to create unit tests to check the formulas Example test:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To run: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from policyengine_uk.model_api import * | ||
|
||
|
||
class childcare_work_condition(Variable): | ||
value_type = bool | ||
entity = Person | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar question for each of the files - do we want to compute this for each person or for the entire Benefit Unit? |
||
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 | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets split this file into to separate parameters:
make an
age_limit
folderstandard.yaml
: value: 12disability.yaml
: value: 17