Skip to content

Commit

Permalink
Add consumer and capital incidence of employer NI (#969)
Browse files Browse the repository at this point in the history
* Fix Automatically apportion remaining employer NI response to consumption #968

* Versioning

* Fix parameter labels

* Add government balance/tax/spend variables
  • Loading branch information
nikhilwoodruff authored Oct 21, 2024
1 parent 03b1c8b commit f1ffe6a
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 5 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)


Expand Down
12 changes: 12 additions & 0 deletions policyengine_uk/variables/gov/gov_balance.py
Original file line number Diff line number Diff line change
@@ -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"]
46 changes: 46 additions & 0 deletions policyengine_uk/variables/gov/gov_spending.py
Original file line number Diff line number Diff line change
@@ -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",
]
36 changes: 36 additions & 0 deletions policyengine_uk/variables/gov/gov_tax.py
Original file line number Diff line number Diff line change
@@ -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",
]
2 changes: 2 additions & 0 deletions policyengine_uk/variables/gov/hmrc/tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 0 additions & 1 deletion policyengine_uk/variables/household/income/income.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

setup(
name="PolicyEngine-UK",
version="2.6.0",
version="2.7.0",
author="PolicyEngine",
author_email="nikhil@policyengine.org",
classifiers=[
Expand Down

0 comments on commit f1ffe6a

Please sign in to comment.