Skip to content
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

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open

Tax free childcare #1004

wants to merge 39 commits into from

Conversation

vahid-ahmadi
Copy link
Collaborator

@vahid-ahmadi vahid-ahmadi commented Jan 10, 2025

Overview

This PR implements the Tax-Free Childcare (TFC) scheme calculation in PolicyEngine UK. The scheme provides government contributions towards childcare costs for eligible families. (Documentation link)

Key Features

  • Annual contribution of up to £2,000 per standard child and £4,000 per disabled child
  • Government contributes £2 for every £8 parents deposit (20% support)
  • Income threshold of £100,000 per partner
  • Compatible with 15/30 hours of free childcare programs

Fixes #1002

@MaxGhenis
Copy link
Collaborator

@PavelMakarchuk could you please meet with @vahid-ahmadi on this? Probably easier than text review for getting the parameters and vectorization in for the first time.

Copy link
Collaborator

@PavelMakarchuk PavelMakarchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come initial comments but will meet to discuss in detail

Comment on lines 33 to 35
basic_age_condition = (age < 12)
age_under_17 = (age < 17)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to parameterize values such as "12" and "17" - in general we try to avoid hard coding any values that could be adjusted through a parameter reform.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

# Combine conditions
eligible = basic_age_condition | (age_under_17 & is_disabled)

return benunit.any(eligible)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is a person level variable we do not need to add the benunit.any condition

we want to return basic_age_condition | (age_under_17 & is_disabled)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a class

Use underscores when naming files instead of "-"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


class childcare_work_condition(Variable):
value_type = bool
entity = Person
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator

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 folder
standard.yaml : value: 12
disability.yaml : value: 17
image

description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits.
metadata:
unit: years
name: childcare_age_limits
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name: childcare_age_limits

@@ -0,0 +1,14 @@
description: Age thresholds for tax-free childcare eligibility, including standard and disability-related limits.
metadata:
unit: years
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unit: years
unit: year

metadata:
unit: years
name: childcare_age_limits
label: Age limits for tax-free childcare eligibility
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label: Age limits for tax-free childcare eligibility
label: Age limits for tax-free childcare eligibility
period: year

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split this into three files:
folder: contributions

Comment on lines 29 to 31
gc = parameters(period).gov.dwp.pension_credit.guarantee_credit
standard_disability_benefits = gc.child.disability.eligibility
severe_disability_benefits = gc.child.disability.severe.eligibility
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines 43 to 47
eligible = basic_age_condition | (
age_under_disability_limit & is_disabled
)

return eligible
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eligible = basic_age_condition | (
age_under_disability_limit & is_disabled
)
return eligible
return basic_age_condition | (
age_under_disability_limit & is_disabled
)

],
)

yearly_eligible_income = total_income - investment_income
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
yearly_eligible_income = total_income - investment_income
yearly_eligible_income = max_(total_income - investment_income, 0)

If the value cant be negative

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can consolidate the adult and young adult files as follows:

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the .py file we can use it then as

age = person("age", period)
file_name.calc(age)

Comment on lines 42 to 48
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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of if, else statements lets use where conditions

Copy link
Collaborator

Choose a reason for hiding this comment

The 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:
in a new incompatibilities_childcare_eligible.yaml file:

- name: Ineligible due to all credits being 0
  period: 2025
  input:
    working_tax_credit: 0
    child_tax_credit: 0
    universal_credit: 0
  output:
    incompatibilities_childcare_eligible: false

- name: Eligible due to universal credit 
  period: 2025
  input:
    working_tax_credit: 0
    child_tax_credit: 0
    universal_credit: 1
  output:
    incompatibilities_childcare_eligible: true

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To run:
policyengine-core test ./policyengine_uk/tests/policy/baseline.....

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the specific name of this program? Lets preface all variable class names with that

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 7 to 8
label = "Child age eligibility requirements"
documentation = "Whether this person meets the age and disability requirements for eligibility"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be program specific in label and description

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

from policyengine_uk.model_api import *


class child_age_eligible(Variable):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class child_age_eligible(Variable):
class tax_free_childcare_child_age_eligible(Variable):

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use in all variable classes and file names should match the class name

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

).astype(bool)

# Combine conditions using logical AND
is_eligible = np.logical_and.reduce(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets make a separate tax_free_childcare_eligible.py

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just make this for the BenefitUnit? Not sure if we need to examine each individual person here if all of the credits are computed for the benefit unit

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar question for all person level vars

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand, both parents can claim tax-free childcare. So, I think we need to check for each person here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a statutory reference for this case here, just to confirm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tax free childcare
3 participants