From f1ffe6aa7db48291d6ef4c5005a2f273ff24809f Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff <35577657+nikhilwoodruff@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:35:04 +0100 Subject: [PATCH] Add consumer and capital incidence of employer NI (#969) * Fix Automatically apportion remaining employer NI response to consumption #968 * Versioning * Fix parameter labels * Add government balance/tax/spend variables --- CHANGELOG.md | 7 + changelog.yaml | 5 + .../employer_ni/capital_incidence.yaml | 6 + .../employer_ni/consumer_incidence.yaml | 6 + .../employer_ni/employee_incidence.yaml | 2 +- .../employer_ni_fixed_employer_cost_change.py | 196 +++++++++++++++++- policyengine_uk/variables/gov/gov_balance.py | 12 ++ policyengine_uk/variables/gov/gov_spending.py | 46 ++++ policyengine_uk/variables/gov/gov_tax.py | 36 ++++ policyengine_uk/variables/gov/hmrc/tax.py | 2 + .../household/demographic/household.py | 1 + .../variables/household/income/income.py | 1 - setup.py | 2 +- 13 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/capital_incidence.yaml create mode 100644 policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/consumer_incidence.yaml create mode 100644 policyengine_uk/variables/gov/gov_balance.py create mode 100644 policyengine_uk/variables/gov/gov_spending.py create mode 100644 policyengine_uk/variables/gov/gov_tax.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e219086f..3d44da30e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.7.0] - 2024-10-21 10:09:01 + +### Added + +- Automatic allocation of post-employee-incidence employee to households (consumers/capital). + ## [2.6.0] - 2024-10-19 19:58:09 ### Fixed @@ -1518,6 +1524,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +[2.7.0]: https://github.com/PolicyEngine/openfisca-uk/compare/2.6.0...2.7.0 [2.6.0]: https://github.com/PolicyEngine/openfisca-uk/compare/2.5.0...2.6.0 [2.5.0]: https://github.com/PolicyEngine/openfisca-uk/compare/2.4.0...2.5.0 [2.4.0]: https://github.com/PolicyEngine/openfisca-uk/compare/2.3.0...2.4.0 diff --git a/changelog.yaml b/changelog.yaml index 795f08abc..be3fad4ed 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -1274,3 +1274,8 @@ fixed: - Bug in budget change reforms. date: 2024-10-19 19:58:09 +- bump: minor + changes: + added: + - Automatic allocation of post-employee-incidence employee to households (consumers/capital). + date: 2024-10-21 10:09:01 diff --git a/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/capital_incidence.yaml b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/capital_incidence.yaml new file mode 100644 index 000000000..edb1a2701 --- /dev/null +++ b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/capital_incidence.yaml @@ -0,0 +1,6 @@ +description: Fraction of (remaining after employee incidence) employer NI that is borne by capital. +values: + 2010-01-01: 0.5 +metadata: + label: Employer NI capital incidence + unit: /1 diff --git a/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/consumer_incidence.yaml b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/consumer_incidence.yaml new file mode 100644 index 000000000..765298c71 --- /dev/null +++ b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/consumer_incidence.yaml @@ -0,0 +1,6 @@ +description: Fraction of (remaining after employee incidence) employer NI that is borne by consumers. +values: + 2010-01-01: 0.5 +metadata: + label: Employer NI consumer incidence + unit: /1 diff --git a/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/employee_incidence.yaml b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/employee_incidence.yaml index e31153a34..b0aaf7efe 100644 --- a/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/employee_incidence.yaml +++ b/policyengine_uk/parameters/gov/contrib/policyengine/employer_ni/employee_incidence.yaml @@ -1,4 +1,4 @@ -description: Fraction of employer NI that is borne by employees. The remaining is passed onto prices. +description: Fraction of employer NI that is borne by employees. values: 2010-01-01: 1 metadata: diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py index 53d57560c..784842b83 100644 --- a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py @@ -9,8 +9,200 @@ class employer_cost(Variable): unit = GBP def formula(person, period, parameters): - return person("employment_income", period) + person( - "ni_employer", period + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + return ( + person("employment_income", period) + + person("ni_employer", period) + + person("employer_pension_contributions", period) + + benefits + ) + + +class baseline_employer_cost(Variable): + label = "baseline employer cost" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + prior_employment_income = person( + "employment_income_before_lsr", period + ) + employment_income_behavioral_response = person( + "employment_income_behavioral_response", period + ) + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + employer_pension_contributions = person( + "employer_pension_contributions", period + ) + ni_class_1_income = ( + prior_employment_income + + employment_income_behavioral_response + + benefits + + employer_pension_contributions + ) + + # Calculate baseline employer cost + baseline_parameters = parameters(period).baseline + baseline_class_1 = ( + baseline_parameters.gov.hmrc.national_insurance.class_1 + ) + r_b = baseline_class_1.rates.employer + t_b = baseline_class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR + p_b = ( + baseline_parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions + ) + pen_con_subtracted_b = employer_pension_contributions + if p_b: + pen_con_subtracted_b = employer_pension_contributions + else: + pen_con_subtracted_b = 0 + + baseline_employer_ni = r_b * max_( + 0, ni_class_1_income - pen_con_subtracted_b - t_b + ) + return ni_class_1_income + baseline_employer_ni + + +class adjusted_employer_cost(Variable): + label = "employer cost" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + employment_income = person("employment_income", period) + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + employer_pension_contributions = person( + "employer_pension_contributions", period + ) + ni_class_1_income = ( + employment_income + benefits + employer_pension_contributions + ) + + # Calculate employer cost + parameters = parameters(period) + class_1 = parameters.gov.hmrc.national_insurance.class_1 + r_r = class_1.rates.employer + t_r = class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR + p_r = ( + parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions + ) + pen_con_subtracted_r = employer_pension_contributions + if p_r: + pen_con_subtracted_r = employer_pension_contributions + else: + pen_con_subtracted_r = 0 + + employer_ni = r_r * max_( + 0, ni_class_1_income - pen_con_subtracted_r - t_r + ) + return ni_class_1_income + employer_ni + + +class employer_ni_response_consumer_incidence(Variable): + label = "price response to employer NI reform" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + consumer_incidence = parameters( + period + ).gov.contrib.policyengine.employer_ni.consumer_incidence + if consumer_incidence == 0: + return 0 + + if not hasattr(person.simulation, "dataset"): + # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. + return 0 + + person_weight = person("person_weight", period) + baseline_employer_cost = person("baseline_employer_cost", period) + employer_cost = person("adjusted_employer_cost", period) + change_in_employer_cost = employer_cost - baseline_employer_cost + amount_paid_by_employers = ( + person_weight * change_in_employer_cost + ).sum() + + consumption = ( + person.household("consumption", period) + / person.household.nb_persons() + ) + total_consumption = (consumption * person_weight).sum() + share_of_total_consumption = consumption / total_consumption + + return ( + amount_paid_by_employers + * share_of_total_consumption + * consumer_incidence + ) + + +class employer_ni_response_capital_incidence(Variable): + label = "capital response to employer NI reform" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + capital_incidence = parameters( + period + ).gov.contrib.policyengine.employer_ni.capital_incidence + if capital_incidence == 0: + return 0 + + if not hasattr(person.simulation, "dataset"): + # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. + return 0 + + person_weight = person("person_weight", period) + baseline_employer_cost = person("baseline_employer_cost", period) + employer_cost = person("adjusted_employer_cost", period) + change_in_employer_cost = employer_cost - baseline_employer_cost + amount_paid_by_employers = ( + person_weight * change_in_employer_cost + ).sum() + + wealth = ( + person.household("corporate_wealth", period) + / person.household.nb_persons() + ) + total_wealth = (wealth * person_weight).sum() + share_of_total_wealth = wealth / total_wealth + + return ( + amount_paid_by_employers + * share_of_total_wealth + * capital_incidence ) diff --git a/policyengine_uk/variables/gov/gov_balance.py b/policyengine_uk/variables/gov/gov_balance.py new file mode 100644 index 000000000..03cc04494 --- /dev/null +++ b/policyengine_uk/variables/gov/gov_balance.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class gov_balance(Variable): + label = "government balance" + documentation = "Government deficit impact in respect of this household." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = ["gov_tax"] + subtracts = ["gov_spending"] diff --git a/policyengine_uk/variables/gov/gov_spending.py b/policyengine_uk/variables/gov/gov_spending.py new file mode 100644 index 000000000..27e86fb95 --- /dev/null +++ b/policyengine_uk/variables/gov/gov_spending.py @@ -0,0 +1,46 @@ +from policyengine_uk.model_api import * + + +class gov_spending(Variable): + label = "government spending" + documentation = "Government spending impact in respect of this household." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = [ + "child_benefit", + "esa_income", + "esa_contrib", + "housing_benefit", + "income_support", + "jsa_income", + "jsa_contrib", + "pension_credit", + "universal_credit", + "working_tax_credit", + "child_tax_credit", + "attendance_allowance", + "afcs", + "bsp", + "carers_allowance", + "dla", + "iidb", + "incapacity_benefit", + "jsa_contrib", + "pip", + "sda", + "state_pension", + "maternity_allowance", + "statutory_sick_pay", + "statutory_maternity_pay", + "ssmg", + "basic_income", + "epg_subsidy", + "cost_of_living_support_payment", + "energy_bills_rebate", + "winter_fuel_allowance", + "nhs_budget_change", + "education_budget_change", + "other_public_spending_budget_change", + ] diff --git a/policyengine_uk/variables/gov/gov_tax.py b/policyengine_uk/variables/gov/gov_tax.py new file mode 100644 index 000000000..20908642f --- /dev/null +++ b/policyengine_uk/variables/gov/gov_tax.py @@ -0,0 +1,36 @@ +from policyengine_uk.model_api import * + + +class gov_tax(Variable): + label = "government tax revenue" + documentation = ( + "Government tax revenue impact in respect of this household." + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = [ + "expected_sdlt", + "expected_ltt", + "expected_lbtt", + "corporate_sdlt", + "business_rates", + "council_tax", + "domestic_rates", + "fuel_duty", + "tv_licence", + "wealth_tax", + "non_primary_residence_wealth_tax", + "income_tax", + "national_insurance", + "LVT", + "carbon_tax", + "vat_change", + "capital_gains_tax", + "private_school_vat", + "corporate_incident_tax_revenue_change", + "consumer_incident_tax_revenue_change", + "high_income_incident_tax_change", + "ni_employer", + ] diff --git a/policyengine_uk/variables/gov/hmrc/tax.py b/policyengine_uk/variables/gov/hmrc/tax.py index c239dd0ce..3df50c968 100644 --- a/policyengine_uk/variables/gov/hmrc/tax.py +++ b/policyengine_uk/variables/gov/hmrc/tax.py @@ -40,6 +40,8 @@ class household_tax(Variable): "corporate_incident_tax_revenue_change", "consumer_incident_tax_revenue_change", "high_income_incident_tax_change", + "employer_ni_response_capital_incidence", + "employer_ni_response_consumer_incidence", ] def formula(household, period, parameters): diff --git a/policyengine_uk/variables/household/demographic/household.py b/policyengine_uk/variables/household/demographic/household.py index c3cb38167..ffbfe4b7a 100644 --- a/policyengine_uk/variables/household/demographic/household.py +++ b/policyengine_uk/variables/household/demographic/household.py @@ -37,6 +37,7 @@ class household_weight(Variable): label = "Weight factor for the household" definition_period = YEAR uprating = "gov.ons.population" + default_value = 1 class Country(Enum): diff --git a/policyengine_uk/variables/household/income/income.py b/policyengine_uk/variables/household/income/income.py index 53c1b420d..d7ff291a5 100644 --- a/policyengine_uk/variables/household/income/income.py +++ b/policyengine_uk/variables/household/income/income.py @@ -165,7 +165,6 @@ class hbai_household_net_income(Variable): "statutory_maternity_pay", "ssmg", "basic_income", - "epg_subsidy", "cost_of_living_support_payment", "winter_fuel_allowance", ] diff --git a/setup.py b/setup.py index d7fa35af4..63ade09df 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name="PolicyEngine-UK", - version="2.6.0", + version="2.7.0", author="PolicyEngine", author_email="nikhil@policyengine.org", classifiers=[