From 46bbcfd4eac4add827bc4f65ef588190502cd776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:46:35 +0000 Subject: [PATCH 1/4] Model the company car and car fuel benefits, with Class 1A NICs Add person inputs for a company car, the ITEPA 2003 s. 139 appropriate percentages from 2020-21 (with the s. 139A rates for cars first registered from 6 April 2020 in 2020-21 and 2021-22), the s. 141 diesel supplement, the s. 132 contribution cap and the s. 150 car fuel multiplier, and calculate the company car and car fuel benefits. Tax them through taxable_employment_income, outside employment_income and employment_benefits, so Class 1 NICs and the UC, HB and CTR earnings tests leave them out. Charge Class 1A NICs on them in ni_employer. employer_cost now reads secondary Class 1 directly, as the employer NI incidence variables do. The 2030-31 baseline keeps 2029-30's rates; the company_car_tax_rise_2030 scenario applies Budget 2026's one-point rise. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2749.added.md | 1 + .../programs/gov/hmrc/company-car-benefit.md | 111 ++++++ policyengine_uk/data/fiscal_partition.yaml | 1 + .../income_tax/benefits_in_kind/README.md | 1 + .../benefits_in_kind/car_fuel/README.md | 1 + .../benefits_in_kind/car_fuel/multiplier.yaml | 59 +++ .../benefits_in_kind/company_car/README.md | 1 + .../appropriate_percentage/README.md | 1 + .../appropriate_percentage/co2_band.yaml | 78 ++++ .../diesel_supplement.yaml | 12 + .../electric_range.yaml | 81 +++++ .../graduated/README.md | 1 + .../graduated/base_rate.yaml | 37 ++ .../graduated/increment.yaml | 10 + .../graduated/step.yaml | 10 + .../graduated/threshold.yaml | 12 + .../appropriate_percentage/maximum.yaml | 45 +++ .../registered_from_6_april_2020/README.md | 1 + .../co2_band.yaml | 43 +++ .../electric_range.yaml | 43 +++ .../graduated_base_rate.yaml | 18 + .../in_effect.yaml | 16 + .../appropriate_percentage/zero_emission.yaml | 62 ++++ .../company_car/capital_contribution_cap.yaml | 10 + .../national_insurance/class_1a/README.md | 1 + .../class_1a/percentage_2022_23/README.md | 1 + .../percentage_2022_23/in_effect.yaml | 12 + .../class_1a/percentage_2022_23/rate.yaml | 10 + policyengine_uk/programs.yaml | 4 +- policyengine_uk/scenarios/__init__.py | 1 + .../scenarios/company_car_tax_rise_2030.py | 44 +++ .../benefits_in_kind_interactions.yaml | 104 ++++++ .../benefits_in_kind/car_fuel_benefit.yaml | 102 ++++++ .../benefits_in_kind/company_car_benefit.yaml | 318 ++++++++++++++++ .../class_1a/ni_class_1a.yaml | 98 +++++ .../tests/test_company_car_benefit.py | 341 ++++++++++++++++++ .../policyengine/employer_ni/employer_cost.py | 10 +- .../bases/taxable_employment_income.py | 6 +- .../benefits_in_kind/benefits_in_kind.py | 39 ++ .../benefits_in_kind/car_fuel_benefit.py | 47 +++ .../company_car_appropriate_percentage.py | 88 +++++ .../benefits_in_kind/company_car_benefit.py | 54 +++ .../class_1a/ni_class_1a.py | 38 ++ .../hmrc/national_insurance/ni_employer.py | 5 + .../total_national_insurance.py | 2 +- .../marginal_tax_rate_wrt_employer_cost.py | 4 +- .../company_car/company_car_availability.py | 28 ++ .../company_car_capital_contributions.py | 20 + .../company_car/company_car_co2_emissions.py | 29 ++ .../company_car/company_car_electric_range.py | 20 + .../company_car/company_car_fuel_type.py | 20 + .../company_car/company_car_list_price.py | 28 ++ .../company_car_private_fuel_provided.py | 26 ++ .../company_car_private_use_payments.py | 20 + ...ompany_car_registered_from_6_april_2020.py | 24 ++ 55 files changed, 2192 insertions(+), 7 deletions(-) create mode 100644 changelog.d/2749.added.md create mode 100644 docs/book/programs/gov/hmrc/company-car-benefit.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/co2_band.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/diesel_supplement.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/base_rate.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/increment.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/step.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/threshold.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/maximum.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/co2_band.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/electric_range.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/in_effect.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/zero_emission.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/capital_contribution_cap.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/README.md create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/in_effect.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/rate.yaml create mode 100644 policyengine_uk/scenarios/company_car_tax_rise_2030.py create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1a/ni_class_1a.yaml create mode 100644 policyengine_uk/tests/test_company_car_benefit.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py create mode 100644 policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_availability.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_capital_contributions.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_co2_emissions.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_electric_range.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_fuel_type.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_list_price.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_private_fuel_provided.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_private_use_payments.py create mode 100644 policyengine_uk/variables/input/company_car/company_car_registered_from_6_april_2020.py diff --git a/changelog.d/2749.added.md b/changelog.d/2749.added.md new file mode 100644 index 0000000000..46db1b72b0 --- /dev/null +++ b/changelog.d/2749.added.md @@ -0,0 +1 @@ +Added the company car and car fuel benefits in kind from 2020-21 (ITEPA 2003 ss. 120 to 152), with the s. 139 appropriate percentages to 2029-30, person inputs for the company car, and Class 1A NICs on both benefits in `ni_employer` and `total_national_insurance`; the benefits are taxed through `taxable_employment_income` and kept out of Class 1 and means-tested earnings, and the `company_car_tax_rise_2030` scenario applies Budget 2026's one-point rise for 2030-31. diff --git a/docs/book/programs/gov/hmrc/company-car-benefit.md b/docs/book/programs/gov/hmrc/company-car-benefit.md new file mode 100644 index 0000000000..f69baafff7 --- /dev/null +++ b/docs/book/programs/gov/hmrc/company-car-benefit.md @@ -0,0 +1,111 @@ +# Company car and car fuel benefits + +A car an employer makes available for an employee's private use is a +benefit in kind: its cash equivalent is taxed as employment income +([ITEPA 2003 s. 120][s120]), and so is fuel provided for its private use +([s. 149][s149]). The employer pays Class 1A National Insurance on both +([SSCBA 1992 s. 10][sscba10]). The model calculates both benefits from +2020-21. + +## Inputs + +Each person can have one company car. The inputs default to no car, so a +simulation that sets none of them is unchanged. + +| Input | Meaning | +| --- | --- | +| `company_car_list_price` | List price of the car and its accessories; zero means no company car | +| `company_car_capital_contributions` | Capital sums the employee has contributed, this year and earlier | +| `company_car_co2_emissions` | CO2 emissions figure in g/km (0 for a zero-emission car) | +| `company_car_electric_range` | Electric range figure in miles | +| `company_car_fuel_type` | Fuel type (the `CarFuelType` enum also used for vehicle excise duty) | +| `company_car_registered_from_6_april_2020` | First registered on or after 6 April 2020 (matters in 2020-21 and 2021-22 only) | +| `company_car_availability` | Share of the tax year the car is available (default 1) | +| `company_car_private_use_payments` | Payments for private use | +| `company_car_private_fuel_provided` | Fuel provided for private use and not made good in full | + +The `car_*` inputs describe the person's own car for vehicle excise duty +and play no part here. + +## Calculation + +`company_car_appropriate_percentage` applies [s. 139][s139]: a rate for +0 g/km, rates for 1 to 50 g/km by electric range (one rate from 2028-29), +a rate for each band from 51-54 to 70-74 g/km (65-74 g/km from 2028-29), +and from 75 g/km a base rate plus one point for each full 5 g/km, up to the +maximum. A diesel car not meeting the Euro 6d (RDE2) standard adds 4 points, +within the maximum ([s. 141][s141]). In 2020-21 and 2021-22 a car first +registered on or after 6 April 2020 takes the lower rates of s. 139A +(Finance Act 2020 ss. 9 and 10). + +`company_car_benefit` follows the steps of [s. 121][s121]: the price, less +capital contributions up to £5,000, times the appropriate percentage, +reduced in proportion to the days the car is unavailable, less payments for +private use, and never below zero. `car_fuel_benefit` is the appropriate +percentage of the car fuel benefit charge multiplier ([s. 150][s150]), +reduced in proportion to the days the car is unavailable. It is nil for a +car that cannot emit CO2. `benefits_in_kind` adds the two. + +## Where the benefits go + +- **Income tax.** `taxable_employment_income` adds `benefits_in_kind`, so the + benefits reach income tax and adjusted net income: the personal allowance + taper, the High Income Child Benefit Charge, and the £100,000 limits of + Tax-Free Childcare and the extended childcare entitlement. They also reach + the other uses of net income, such as student loan repayments. +- **Not earnings for Class 1 or the means tests.** The benefits are not in + `employment_income` or `employment_benefits`, so employee Class 1 NICs and + the earnings of Universal Credit ([UC Regulations 2013 reg. 55(2)(a)][uc55]), + Housing Benefit (HB Regulations 2006 reg. 35(2)(a)) and Council Tax + Reduction leave them out. Those tests still deduct the income tax on them, + which is tax paid in respect of the employment (reg. 55(5)(b)) and which + PAYE collects from pay. The Childcare Offer for Wales's gross income lists + its sources one by one, and benefits in kind are not among them. +- **Employer National Insurance.** `ni_class_1a` is the Class 1A percentage + of `benefits_in_kind`, and `ni_employer` adds it to secondary Class 1, so it + reaches `gov_tax`. The Class 1A percentage is the secondary percentage for + the year, except 14.53% in 2022-23. `employer_cost`, and the employer NI + incidence variables that recompute it, cover pay and secondary Class 1 + only: they leave out benefits in kind and the Class 1A on them, so an + employer NI reform's fixed-cost pay response does not offset a change in + Class 1A. + +## 2030-31 + +Finance Act 2025 s. 6 sets the rates for 2029-30 and subsequent tax years, +so the baseline keeps them for 2030-31: 9% for 0 g/km, 19% for 1 to 50 g/km +and a 39% maximum. Budget 2026 raises every rate for 2030-31 by one point, to +a maximum of 40%, to be legislated in Finance Bill 2026-27. The +`company_car_tax_rise_2030` scenario applies that rise: + +```python +from policyengine_uk import Simulation +from policyengine_uk.scenarios import company_car_tax_rise_2030 + +reformed = Simulation(situation=situation, scenario=company_car_tax_rise_2030) +``` + +`policyengine_uk.scenarios.company_car_tax_rise_2030.COMPANY_CAR_TAX_RISE_2030` +holds the same changes as a parameter-changes dictionary. + +## Not modelled + +- Vans and van fuel (ITEPA 2003 s. 154 onwards). +- Optional remuneration arrangements (s. 120A), shared cars (s. 148) and + pool cars (s. 167). +- Classic cars (s. 147), cars without a CO2 emissions figure (s. 140) and + cars first registered before 1 January 1998 (s. 142). +- Fuel provided for only part of the year (s. 152(2)). +- Other benefits in kind. + +The survey data does not fill these inputs yet, and a dataset projected to +later years holds them at the data year's values. + +[s120]: https://www.legislation.gov.uk/ukpga/2003/1/section/120 +[s121]: https://www.legislation.gov.uk/ukpga/2003/1/section/121 +[s139]: https://www.legislation.gov.uk/ukpga/2003/1/section/139 +[s141]: https://www.legislation.gov.uk/ukpga/2003/1/section/141 +[s149]: https://www.legislation.gov.uk/ukpga/2003/1/section/149 +[s150]: https://www.legislation.gov.uk/ukpga/2003/1/section/150 +[sscba10]: https://www.legislation.gov.uk/ukpga/1992/4/section/10 +[uc55]: https://www.legislation.gov.uk/uksi/2013/376/regulation/55 diff --git a/policyengine_uk/data/fiscal_partition.yaml b/policyengine_uk/data/fiscal_partition.yaml index f242470b4a..1a9744700e 100644 --- a/policyengine_uk/data/fiscal_partition.yaml +++ b/policyengine_uk/data/fiscal_partition.yaml @@ -743,6 +743,7 @@ exclusions: - {variable: employer_ni_fixed_employer_cost_change, kind: change_measure, head: nics_employer, note: "The wage change from an employer NI reform; employment_income already adds it."} - {variable: salary_sacrifice_broad_base_haircut, kind: change_measure, head: nics_employer, note: "A wage change employment_income already adds."} - {variable: ni_class_1_employer, kind: component, head: nics_employer} + - {variable: ni_class_1a, kind: component, head: nics_employer} - {variable: salary_sacrifice_pension_ni_employee, kind: measure_within, head: nics_individual, note: "The excess sacrifice is added back to ni_class_1_income, so Class 1 already charges it."} - {variable: salary_sacrifice_pension_ni_employer, kind: measure_within, head: nics_employer, note: "As above, for employer Class 1."} - {variable: tax, kind: composite, spans: [income_tax, nics_individual]} diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/README.md new file mode 100644 index 0000000000..b8ccc9c907 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/README.md @@ -0,0 +1 @@ +# Benefits in kind diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/README.md new file mode 100644 index 0000000000..624e1ad42a --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/README.md @@ -0,0 +1 @@ +# Car fuel diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml new file mode 100644 index 0000000000..a4ecc604e9 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml @@ -0,0 +1,59 @@ +description: Amount to which the company car's appropriate percentage is applied to give the cash equivalent of fuel provided for its private use (the car fuel benefit charge multiplier). +# The Treasury sets this by order under ITEPA 2003 s. 170(5); no statute +# indexes it. The 2027-28 figure is announced: Budget 2026 OOTLAR paragraph +# 2.6 gives £30,200 (September CPI uprating), and the Treasury order is not +# yet made. From 2028-29 the values are a CPI projection, as the government +# has uprated the figure by September CPI each year; none is announced. +values: + 2020-04-06: + value: 24_500 + metadata: + reference: + - title: The Van Benefit and Car and Van Fuel Benefit Order 2020 (SI 2020/199) art. 2 + href: https://www.legislation.gov.uk/uksi/2020/199/article/2/made + 2021-04-06: + value: 24_600 + metadata: + reference: + - title: The Van Benefit and Car and Van Fuel Benefit Order 2021 (SI 2021/248) art. 2 + href: https://www.legislation.gov.uk/uksi/2021/248/article/2/made + 2022-04-06: + value: 25_300 + metadata: + reference: + - title: The Van Benefit and Car and Van Fuel Benefit (No. 2) Order 2021 (SI 2021/1422) art. 2 + href: https://www.legislation.gov.uk/uksi/2021/1422/article/2/made + 2023-04-06: + value: 27_800 + metadata: + reference: + - title: The Van Benefit and Car and Fuel Benefit Order 2022 (SI 2022/1288) art. 2 (tax year 2023-24 and subsequent tax years) + href: https://www.legislation.gov.uk/uksi/2022/1288/article/2/made + 2025-04-06: + value: 28_200 + metadata: + reference: + - title: The Van Benefit and Car and Van Fuel Benefit Order 2024 (SI 2024/1349) art. 2(2) + href: https://www.legislation.gov.uk/uksi/2024/1349/article/2/made + 2026-04-06: + value: 29_200 + metadata: + reference: + - title: The Van Benefit and Car and Van Fuel Benefit Order 2025 (SI 2025/1254) art. 2(2) + href: https://www.legislation.gov.uk/uksi/2025/1254/article/2/made + 2027-04-06: + value: 30_200 + metadata: + reference: + - title: Budget 2026, overview of tax legislation and rates, paragraph 2.6 + href: https://www.gov.uk/government/publications/budget-2026-overview-of-tax-legislation-and-rates-ootlar/budget-2026-overview-of-tax-legislation-and-rates-ootlar +metadata: + unit: currency-GBP + period: year + label: Car fuel benefit charge multiplier + uprating: gov.economic_assumptions.indices.obr.consumer_price_index + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 150(1) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/150 + - title: Income Tax (Earnings and Pensions) Act 2003 s. 170(5) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/170 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/README.md new file mode 100644 index 0000000000..b335b878d6 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/README.md @@ -0,0 +1 @@ +# Company car diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/README.md new file mode 100644 index 0000000000..36786a1ddb --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/README.md @@ -0,0 +1 @@ +# Appropriate percentage diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/co2_band.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/co2_band.yaml new file mode 100644 index 0000000000..88c601b42b --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/co2_band.yaml @@ -0,0 +1,78 @@ +description: Appropriate percentage for a company car with a CO2 emissions figure of 51 to 74 g/km, by that figure rounded down to a whole number. +# Finance Act 2025 s. 5 merges the 65-69 and 70-74 bands into one 65-74 band +# from 2028-29, so the last two brackets have the same amount from then. +# For 2020-21 and 2021-22 these are the rates for cars first registered +# before 6 April 2020; registered_from_6_april_2020 holds the s. 139A rates. +# The tax year 2030-31 keeps 2029-30's rates. Budget 2026 announced one +# point more in every band; the company_car_tax_rise_2030 scenario applies it. +metadata: + type: single_amount + threshold_unit: g/km + amount_unit: /1 + period: year + label: Company car appropriate percentage by CO2 emissions, 51 to 74 g/km + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(1) and (2)(a) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: Finance (No. 2) Act 2017 s. 2(2) (tax years 2020-21 to 2024-25) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted + - title: Finance Act 2023 s. 11 (tax years 2025-26, 2026-27 and 2027-28) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + - title: Finance Act 2025 s. 5(2) (tax year 2028-29) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/5/enacted + - title: Finance Act 2025 s. 6(2) (tax year 2029-30 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/6/enacted + - title: "HMRC 480 Appendix 2: appropriate percentages for 2020-21 to 2026-27" + href: https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 +brackets: + # 51 to 54 g/km. + - threshold: + 2020-04-06: 51 + amount: + 2020-04-06: 0.15 + 2025-04-06: 0.16 + 2026-04-06: 0.17 + 2027-04-06: 0.18 + 2028-04-06: 0.19 + 2029-04-06: 0.20 + # 55 to 59 g/km. + - threshold: + 2020-04-06: 55 + amount: + 2020-04-06: 0.16 + 2025-04-06: 0.17 + 2026-04-06: 0.18 + 2027-04-06: 0.19 + 2028-04-06: 0.20 + 2029-04-06: 0.21 + # 60 to 64 g/km. + - threshold: + 2020-04-06: 60 + amount: + 2020-04-06: 0.17 + 2025-04-06: 0.18 + 2026-04-06: 0.19 + 2027-04-06: 0.20 + 2028-04-06: 0.21 + 2029-04-06: 0.22 + # 65 to 69 g/km. + - threshold: + 2020-04-06: 65 + amount: + 2020-04-06: 0.18 + 2025-04-06: 0.19 + 2026-04-06: 0.20 + 2027-04-06: 0.21 + 2028-04-06: 0.22 + 2029-04-06: 0.23 + # 70 to 74 g/km. In 2027-28 Finance Act 2023 s. 11(7) keeps it at 21%, + # level with 65-69 g/km and with the 75 g/km rate. + - threshold: + 2020-04-06: 70 + amount: + 2020-04-06: 0.19 + 2025-04-06: 0.20 + 2026-04-06: 0.21 + 2027-04-06: 0.21 + 2028-04-06: 0.22 + 2029-04-06: 0.23 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/diesel_supplement.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/diesel_supplement.yaml new file mode 100644 index 0000000000..04bf928a30 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/diesel_supplement.yaml @@ -0,0 +1,12 @@ +description: Percentage points added to the appropriate percentage of a company car propelled solely by diesel that does not meet the Euro 6d (RDE2) emissions standard, up to the maximum appropriate percentage. +values: + 2020-04-06: 0.04 +metadata: + unit: /1 + period: year + label: Company car diesel supplement + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 141 + href: https://www.legislation.gov.uk/ukpga/2003/1/section/141 + - title: Finance Act 2018 s. 9(3)(b) (4 percentage points from 2018-19) + href: https://www.legislation.gov.uk/ukpga/2018/3/section/9/enacted diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range.yaml new file mode 100644 index 0000000000..8958993a9b --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range.yaml @@ -0,0 +1,81 @@ +description: Appropriate percentage for a company car with a CO2 emissions figure of 1 to 50 g/km, by its electric range figure in miles (the distance it can be driven in electric mode without recharging), rounded up to a whole number. +# Finance Act 2025 s. 5 replaces the electric range bands with one rate for +# 1 to 50 g/km from 2028-29 (18%, then 19% from 2029-30 under s. 6), so +# every bracket has the same amount from then. s. 5(10) and (11) omit the +# electric range figure from s. 139 from 2028-29. +# For 2020-21 and 2021-22 these are the rates for cars first registered +# before 6 April 2020; registered_from_6_april_2020 holds the s. 139A rates. +# The tax year 2030-31 keeps 2029-30's 19%. Budget 2026 announced 20%; the +# company_car_tax_rise_2030 scenario applies it. +metadata: + type: single_amount + threshold_unit: mile + amount_unit: /1 + period: year + label: Company car appropriate percentage by electric range + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(1) and (2)(b) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: Finance (No. 2) Act 2017 s. 2(2) (tax years 2020-21 to 2024-25) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted + - title: Finance Act 2020 s. 8(4)(a) (electric range figure rounded up, from 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/8/enacted + - title: Finance Act 2023 s. 11 (tax years 2025-26, 2026-27 and 2027-28) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + - title: Finance Act 2025 s. 5(2) (tax year 2028-29) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/5/enacted + - title: Finance Act 2025 s. 6(2) (tax year 2029-30 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/6/enacted + - title: "HMRC 480 Appendix 2: appropriate percentages for 2020-21 to 2026-27" + href: https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 +brackets: + # Electric range figure of less than 30 miles. + - threshold: + 2020-04-06: 0 + amount: + 2020-04-06: 0.14 + 2025-04-06: 0.15 + 2026-04-06: 0.16 + 2027-04-06: 0.17 + 2028-04-06: 0.18 + 2029-04-06: 0.19 + # 30 to 39 miles. + - threshold: + 2020-04-06: 30 + amount: + 2020-04-06: 0.12 + 2025-04-06: 0.13 + 2026-04-06: 0.14 + 2027-04-06: 0.15 + 2028-04-06: 0.18 + 2029-04-06: 0.19 + # 40 to 69 miles. + - threshold: + 2020-04-06: 40 + amount: + 2020-04-06: 0.08 + 2025-04-06: 0.09 + 2026-04-06: 0.10 + 2027-04-06: 0.11 + 2028-04-06: 0.18 + 2029-04-06: 0.19 + # 70 to 129 miles. + - threshold: + 2020-04-06: 70 + amount: + 2020-04-06: 0.05 + 2025-04-06: 0.06 + 2026-04-06: 0.07 + 2027-04-06: 0.08 + 2028-04-06: 0.18 + 2029-04-06: 0.19 + # 130 miles or more. + - threshold: + 2020-04-06: 130 + amount: + 2020-04-06: 0.02 + 2025-04-06: 0.03 + 2026-04-06: 0.04 + 2027-04-06: 0.05 + 2028-04-06: 0.18 + 2029-04-06: 0.19 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/README.md new file mode 100644 index 0000000000..e8e7b6d294 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/README.md @@ -0,0 +1 @@ +# Cars emitting 75 g/km or more diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/base_rate.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/base_rate.yaml new file mode 100644 index 0000000000..aee4bc3084 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/base_rate.yaml @@ -0,0 +1,37 @@ +description: Appropriate percentage for a company car with a CO2 emissions figure at the graduated threshold of 75 g/km, before the rise for each step of emissions above it. +# For 2020-21 and 2021-22 this is the rate for cars first registered before +# 6 April 2020; registered_from_6_april_2020 holds the s. 139A rate. +# The tax year 2030-31 keeps 2029-30's 23%. Budget 2026 announced 24%; the +# company_car_tax_rise_2030 scenario applies it. +values: + 2020-04-06: + value: 0.2 + metadata: + reference: + - title: Finance (No. 2) Act 2017 s. 2(2), substituting ITEPA 2003 s. 139(3)(a) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted + 2025-04-06: + value: 0.21 + metadata: + reference: + - title: Finance Act 2023 s. 11(3) and (4) (tax year 2025-26 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + 2028-04-06: + value: 0.22 + metadata: + reference: + - title: Finance Act 2025 s. 5(3)(a) and (9) (tax year 2028-29) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/5/enacted + 2029-04-06: + value: 0.23 + metadata: + reference: + - title: Finance Act 2025 s. 6(3)(a) and (9) (tax year 2029-30 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/6/enacted +metadata: + unit: /1 + period: year + label: Company car graduated appropriate percentage base rate + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(3)(a) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/increment.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/increment.yaml new file mode 100644 index 0000000000..b998a32305 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/increment.yaml @@ -0,0 +1,10 @@ +description: Rise in a company car's appropriate percentage for each full step of CO2 emissions above the graduated threshold. +values: + 2020-04-06: 0.01 +metadata: + unit: /1 + period: year + label: Company car graduated appropriate percentage increment + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(3)(a) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/step.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/step.yaml new file mode 100644 index 0000000000..be1c636caa --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/step.yaml @@ -0,0 +1,10 @@ +description: Width of each step of CO2 emissions above the graduated threshold that raises a company car's appropriate percentage; the emissions figure is rounded down to a multiple of it. +values: + 2020-04-06: 5 +metadata: + unit: g/km + period: year + label: Company car graduated appropriate percentage step + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(3)(a) and (4) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/threshold.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/threshold.yaml new file mode 100644 index 0000000000..c81d52b811 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/graduated/threshold.yaml @@ -0,0 +1,12 @@ +description: CO2 emissions figure from which a company car's appropriate percentage is the base rate plus a rise for each step of emissions above it, rather than a rate from the table for lower emissions. +values: + 2020-04-06: 75 +metadata: + unit: g/km + period: year + label: Company car graduated appropriate percentage threshold + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(1) and (3) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: Finance (No. 2) Act 2017 s. 2(2) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/maximum.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/maximum.yaml new file mode 100644 index 0000000000..506217b3c5 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/maximum.yaml @@ -0,0 +1,45 @@ +description: Maximum appropriate percentage for a company car, which caps both the graduated rate and a diesel car's rate after its supplement. +# Two provisions set this figure: s. 139(3)(b) for the graduated rate and +# s. 141(2) step 3(b) for a diesel car. They have moved together, so the +# model holds one figure. For s. 141, legislation.gov.uk prints 35%: Finance +# Act 2015 s. 9 substituted 37% for the tax year 2015-16 only, when Finance +# Act 2014 s. 24(11) was to omit s. 141 from 2016-17, and Finance Act 2016 +# s. 10 then kept s. 141 without restating the cap. Finance Act 2025 s. 5(6) +# substitutes 38% "for '37%'" in step 3, and HMRC 480 (paragraph 12.29, note +# 2) applies the 37% maximum to diesel cars from 2015-16, so the model uses +# 37% for both to 2027-28. +# The tax year 2030-31 keeps 2029-30's 39%. Budget 2026 announced 40%, for +# s. 139(3)(b) and s. 141 alike; the company_car_tax_rise_2030 scenario +# applies it. +values: + 2020-04-06: + value: 0.37 + metadata: + reference: + - title: Finance (No. 2) Act 2017 s. 2(2), substituting ITEPA 2003 s. 139(3)(b) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted + - title: Finance Act 2015 s. 9 (ITEPA 2003 s. 141(2) step 3, 37% for 2015-16) + href: https://www.legislation.gov.uk/ukpga/2015/11/section/9/enacted + - title: "HMRC 480 Chapter 12, paragraph 12.29, note 2: overall maximum of 37% from 2015-16" + href: https://www.gov.uk/guidance/how-to-work-out-the-benefit-of-a-company-car-480-chapter-12 + 2028-04-06: + value: 0.38 + metadata: + reference: + - title: Finance Act 2025 s. 5(3)(b), (6) and (9) (tax year 2028-29) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/5/enacted + 2029-04-06: + value: 0.39 + metadata: + reference: + - title: Finance Act 2025 s. 6(3)(b), (6) and (9) (tax year 2029-30 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/6/enacted +metadata: + unit: /1 + period: year + label: Company car maximum appropriate percentage + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(3)(b) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: Income Tax (Earnings and Pensions) Act 2003 s. 141(2), step 3(b) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/141 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/README.md b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/README.md new file mode 100644 index 0000000000..ac269a7dc9 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/README.md @@ -0,0 +1 @@ +# Cars first registered on or after 6 April 2020 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/co2_band.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/co2_band.yaml new file mode 100644 index 0000000000..262c37cd43 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/co2_band.yaml @@ -0,0 +1,43 @@ +description: Appropriate percentage under ITEPA 2003 s. 139A for a company car first registered on or after 6 April 2020 with a CO2 emissions figure of 51 to 74 g/km, while s. 139A is in effect. +metadata: + type: single_amount + threshold_unit: g/km + amount_unit: /1 + period: year + label: Appropriate percentage by CO2 emissions, cars first registered from 6 April 2020 + reference: + - title: Finance Act 2020 s. 9(3) (tax year 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + - title: Finance Act 2020 s. 10(3) (tax year 2021-22) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted +brackets: + # 51 to 54 g/km. + - threshold: + 2020-04-06: 51 + amount: + 2020-04-06: 0.13 + 2021-04-06: 0.14 + # 55 to 59 g/km. + - threshold: + 2020-04-06: 55 + amount: + 2020-04-06: 0.14 + 2021-04-06: 0.15 + # 60 to 64 g/km. + - threshold: + 2020-04-06: 60 + amount: + 2020-04-06: 0.15 + 2021-04-06: 0.16 + # 65 to 69 g/km. + - threshold: + 2020-04-06: 65 + amount: + 2020-04-06: 0.16 + 2021-04-06: 0.17 + # 70 to 74 g/km. + - threshold: + 2020-04-06: 70 + amount: + 2020-04-06: 0.17 + 2021-04-06: 0.18 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/electric_range.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/electric_range.yaml new file mode 100644 index 0000000000..77b922480f --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/electric_range.yaml @@ -0,0 +1,43 @@ +description: Appropriate percentage under ITEPA 2003 s. 139A for a company car first registered on or after 6 April 2020 with a CO2 emissions figure of 1 to 50 g/km, by its electric range figure in miles, while s. 139A is in effect. +metadata: + type: single_amount + threshold_unit: mile + amount_unit: /1 + period: year + label: Appropriate percentage by electric range, cars first registered from 6 April 2020 + reference: + - title: Finance Act 2020 s. 9(3) (tax year 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + - title: Finance Act 2020 s. 10(3) (tax year 2021-22) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted +brackets: + # Electric range figure of less than 30 miles. + - threshold: + 2020-04-06: 0 + amount: + 2020-04-06: 0.12 + 2021-04-06: 0.13 + # 30 to 39 miles. + - threshold: + 2020-04-06: 30 + amount: + 2020-04-06: 0.1 + 2021-04-06: 0.11 + # 40 to 69 miles. + - threshold: + 2020-04-06: 40 + amount: + 2020-04-06: 0.06 + 2021-04-06: 0.07 + # 70 to 129 miles. + - threshold: + 2020-04-06: 70 + amount: + 2020-04-06: 0.03 + 2021-04-06: 0.04 + # 130 miles or more. + - threshold: + 2020-04-06: 130 + amount: + 2020-04-06: 0 + 2021-04-06: 0.01 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml new file mode 100644 index 0000000000..6f8c9305d3 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml @@ -0,0 +1,18 @@ +description: Appropriate percentage under ITEPA 2003 s. 139A for a company car first registered on or after 6 April 2020 with a CO2 emissions figure of 75 g/km, before the rise for each step of emissions above it, while s. 139A is in effect. The maximum appropriate percentage is unchanged. +values: + 2020-04-06: + value: 0.18 + metadata: + reference: + - title: Finance Act 2020 s. 9(3) (tax year 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + 2021-04-06: + value: 0.19 + metadata: + reference: + - title: Finance Act 2020 s. 10(3) (tax year 2021-22) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted +metadata: + unit: /1 + period: year + label: Graduated appropriate percentage base rate, cars first registered from 6 April 2020 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/in_effect.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/in_effect.yaml new file mode 100644 index 0000000000..c9df564da0 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/in_effect.yaml @@ -0,0 +1,16 @@ +description: Whether ITEPA 2003 s. 139A gives a company car first registered on or after 6 April 2020 its own, lower appropriate percentages. Finance Act 2020 inserted s. 139A for 2020-21 (s. 9(3)) and again for 2021-22 (s. 10(3)); from 2022-23 every car takes the s. 139 rates. +values: + 0000-01-01: false + 2020-04-06: true + 2022-04-06: false +metadata: + unit: bool + period: year + label: Separate appropriate percentages for cars first registered from 6 April 2020 + reference: + - title: Finance Act 2020 s. 9(3) (tax year 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + - title: Finance Act 2020 s. 10(3) (tax year 2021-22) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted + - title: "HMRC 480 Appendix 2: NEDC and WLTP columns for 2020-21 and 2021-22" + href: https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/zero_emission.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/zero_emission.yaml new file mode 100644 index 0000000000..b63c4517ce --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/zero_emission.yaml @@ -0,0 +1,62 @@ +description: Appropriate percentage for a company car with a CO2 emissions figure of 0, the share of the car's price taxed each year as its benefit. +# The tax year 2030-31 keeps 2029-30's 9%. Budget 2026 announced 10% for +# 2030-31, to be legislated in Finance Bill 2026-27; the +# company_car_tax_rise_2030 scenario applies it. +values: + 2020-04-06: + value: 0 + metadata: + reference: + - title: Finance Act 2020 s. 9(2)(a) (tax year 2020-21) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + 2021-04-06: + value: 0.01 + metadata: + reference: + - title: Finance Act 2020 s. 10(2)(a) (tax year 2021-22) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted + 2022-04-06: + value: 0.02 + metadata: + reference: + - title: Finance (No. 2) Act 2017 s. 2(2), substituting ITEPA 2003 s. 139(1) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted + 2025-04-06: + value: 0.03 + metadata: + reference: + - title: Finance Act 2023 s. 11(1) and (2) (tax year 2025-26) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + 2026-04-06: + value: 0.04 + metadata: + reference: + - title: Finance Act 2023 s. 11(5) and (6) (tax year 2026-27) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + 2027-04-06: + value: 0.05 + metadata: + reference: + - title: Finance Act 2023 s. 11(7) and (8) (tax year 2027-28) + href: https://www.legislation.gov.uk/ukpga/2023/1/section/11/enacted + 2028-04-06: + value: 0.07 + metadata: + reference: + - title: Finance Act 2025 s. 5(2) and (9) (tax year 2028-29) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/5/enacted + 2029-04-06: + value: 0.09 + metadata: + reference: + - title: Finance Act 2025 s. 6(2) and (9) (tax year 2029-30 and subsequent tax years) + href: https://www.legislation.gov.uk/ukpga/2025/8/section/6/enacted +metadata: + unit: /1 + period: year + label: Zero-emission company car appropriate percentage + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(1) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: "HMRC 480 Appendix 2: appropriate percentages for 2020-21 to 2026-27" + href: https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/capital_contribution_cap.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/capital_contribution_cap.yaml new file mode 100644 index 0000000000..5f531821a8 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/capital_contribution_cap.yaml @@ -0,0 +1,10 @@ +description: Maximum deduction from a company car's price for capital sums the employee has contributed, in the year and earlier years, to the car and its accessories. +values: + 2003-04-06: 5_000 +metadata: + unit: currency-GBP + period: year + label: Company car capital contribution cap + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 132(3)(b) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/132 diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/README.md b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/README.md new file mode 100644 index 0000000000..30f1be9de4 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/README.md @@ -0,0 +1 @@ +# Class 1A diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/README.md b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/README.md new file mode 100644 index 0000000000..bc41cad93b --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/README.md @@ -0,0 +1 @@ +# Class 1A percentage for 2022-23 diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/in_effect.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/in_effect.yaml new file mode 100644 index 0000000000..c2f3aa3403 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/in_effect.yaml @@ -0,0 +1,12 @@ +description: Whether the Class 1A percentage is the fixed rate the Health and Social Care Levy (Repeal) Act 2022 sets for 2022-23, rather than the secondary Class 1 percentage it equals in other tax years. +values: + 0000-01-01: false + 2022-04-06: true + 2023-04-06: false +metadata: + unit: bool + period: year + label: Fixed Class 1A percentage for 2022-23 + reference: + - title: Health and Social Care Levy (Repeal) Act 2022 Schedule paragraph 1 + href: https://www.legislation.gov.uk/ukpga/2022/43/schedule/paragraph/1/enacted diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/rate.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/rate.yaml new file mode 100644 index 0000000000..113eb1cda8 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1a/percentage_2022_23/rate.yaml @@ -0,0 +1,10 @@ +description: Class 1A percentage for 2022-23, which applies to the whole year's benefits in kind although the secondary Class 1 percentage fell from 15.05% to 13.8% on 6 November 2022. +values: + 2022-04-06: 0.1453 +metadata: + unit: /1 + period: year + label: Class 1A percentage for 2022-23 + reference: + - title: Health and Social Care Levy (Repeal) Act 2022 Schedule paragraph 1(2) + href: https://www.legislation.gov.uk/ukpga/2022/43/schedule/paragraph/1/enacted diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 734a227b56..ed22040b61 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -50,7 +50,7 @@ programs: variable: income_tax parameter_prefix: gov.hmrc.income_tax verified_start_year: 2019 - notes: Includes Scottish rates via devolved tax bands, the high income child benefit charge and, from 2025-26, the winter fuel payment charge + notes: Includes Scottish rates via devolved tax bands, the high income child benefit charge, from 2025-26 the winter fuel payment charge, and from 2020-21 the company car and car fuel benefits in kind (no other benefits in kind, and no vans) - id: national_insurance name: National Insurance @@ -62,7 +62,7 @@ programs: variable: national_insurance parameter_prefix: gov.hmrc.national_insurance verified_start_year: 2019 - notes: Classes 1, 2, 3, and 4 + notes: Classes 1, 2, 3, and 4, and Class 1A on the company car and car fuel benefits - id: capital_gains_tax name: Capital gains tax diff --git a/policyengine_uk/scenarios/__init__.py b/policyengine_uk/scenarios/__init__.py index 4be5973b03..e206fa2af7 100644 --- a/policyengine_uk/scenarios/__init__.py +++ b/policyengine_uk/scenarios/__init__.py @@ -1,4 +1,5 @@ from .abolish_benefit_cap import abolish_benefit_cap +from .company_car_tax_rise_2030 import company_car_tax_rise_2030 from .no_economic_assumptions import no_economic_assumptions from .pip_reform import reform_pip_phase_in from .reindex_benefit_cap import reindex_benefit_cap diff --git a/policyengine_uk/scenarios/company_car_tax_rise_2030.py b/policyengine_uk/scenarios/company_car_tax_rise_2030.py new file mode 100644 index 0000000000..cc1679e3d0 --- /dev/null +++ b/policyengine_uk/scenarios/company_car_tax_rise_2030.py @@ -0,0 +1,44 @@ +"""Budget 2026's company car tax rates for 2030-31. + +Budget 2026 sets every company car appropriate percentage for 2030-31 one +point above 2029-30's, zero-emission cars included, and raises the maximum +from 39% to 40% (ITEPA 2003 s. 139(1), (3)(a) and (3)(b), and the diesel +cap in s. 141(2) step 3). Finance Bill 2026-27 is to legislate it, so the +baseline keeps 2029-30's rates for 2030-31 and this scenario applies the +rise from 6 April 2030. Table 6.1 of Budget 2026 (row 86) costs it at ++£310m in 2030-31 and +£345m in 2031-32. + +Sources: Budget 2026, overview of tax legislation and rates, paragraph 1.25; +HMRC, "Taxation of company cars: the appropriate percentage for tax years +2030 to 2031 and 2031 to 2032" (28 October 2026). +""" + +from policyengine_uk.model_api import Scenario + +APPROPRIATE_PERCENTAGE = ( + "gov.hmrc.income_tax.benefits_in_kind.company_car.appropriate_percentage" +) +FROM_2030_31 = "2030-04-06.2100-12-31" + +# Each rate is 2029-30's (Finance Act 2025 s. 6) plus one point. +COMPANY_CAR_TAX_RISE_2030 = { + f"{APPROPRIATE_PERCENTAGE}.zero_emission": {FROM_2030_31: 0.10}, + # 1 to 50 g/km, one rate whatever the electric range. + **{ + f"{APPROPRIATE_PERCENTAGE}.electric_range[{bracket}].amount": { + FROM_2030_31: 0.20 + } + for bracket in range(5) + }, + # 51-54, 55-59, 60-64, 65-69 and 70-74 g/km. + f"{APPROPRIATE_PERCENTAGE}.co2_band[0].amount": {FROM_2030_31: 0.21}, + f"{APPROPRIATE_PERCENTAGE}.co2_band[1].amount": {FROM_2030_31: 0.22}, + f"{APPROPRIATE_PERCENTAGE}.co2_band[2].amount": {FROM_2030_31: 0.23}, + f"{APPROPRIATE_PERCENTAGE}.co2_band[3].amount": {FROM_2030_31: 0.24}, + f"{APPROPRIATE_PERCENTAGE}.co2_band[4].amount": {FROM_2030_31: 0.24}, + # 75 g/km, and one point more for each 5 g/km above it. + f"{APPROPRIATE_PERCENTAGE}.graduated.base_rate": {FROM_2030_31: 0.24}, + f"{APPROPRIATE_PERCENTAGE}.maximum": {FROM_2030_31: 0.40}, +} + +company_car_tax_rise_2030 = Scenario(parameter_changes=COMPANY_CAR_TAX_RISE_2030) diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml new file mode 100644 index 0000000000..23c8f4ff8c --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml @@ -0,0 +1,104 @@ +# Where the company car benefit goes. A person paid £20,000 in 2026-27 has +# a zero-emission company car with a list price of £75,000, so a benefit of +# £75,000 x 4% = £3,000. The benefit is employment income for income tax +# (ITEPA 2003 s. 120) and so counts in adjusted net income. It is not +# earnings for employee Class 1 NICs, nor for Universal Credit (UC +# Regulations 2013 reg. 55(2)(a)), Housing Benefit (HB Regulations 2006 +# reg. 35(2)(a)) or Council Tax Reduction. Those earnings tests do deduct the +# income tax on it, which is tax paid in respect of the employment (UC reg. +# 55(5)(b)); PAYE collects it from the person's pay. + +- name: Without the company car + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + output: + taxable_employment_income: 20_000 + adjusted_net_income: 20_000 + # (£20,000 - £12,570) x 20% = £1,486. + income_tax: 1_486 + # (£20,000 - £12,570) x 8% = £594.40. + ni_class_1_employee: 594.4 + uc_income_tax_on_earnings: 1_486 + # £20,000 - £1,486 - £594.40 = £17,919.60. + uc_individual_earned_income_before_mif: 17_919.6 + housing_benefit_net_earnings: 17_919.6 + council_tax_reduction_net_earnings: 17_919.6 + # (£20,000 - £5,000) x 15% = £2,250. + ni_employer: 2_250 + +- name: With the company car + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + company_car_list_price: 75_000 + company_car_co2_emissions: 0 + company_car_fuel_type: ELECTRIC + output: + company_car_benefit: 3_000 + # Income tax and adjusted net income include the benefit. + taxable_employment_income: 23_000 + adjusted_net_income: 23_000 + # (£23,000 - £12,570) x 20% = £2,086. + income_tax: 2_086 + # Pay, statutory pay and Class 1 earnings leave the benefit out. + employment_income: 20_000 + employment_benefits: 0 + ni_class_1_income: 20_000 + ni_class_1_employee: 594.4 + # The earnings tests leave the benefit out and deduct the income tax on + # it: £20,000 - £2,086 - £594.40 = £17,319.60. + uc_income_tax_on_earnings: 2_086 + uc_individual_earned_income_before_mif: 17_319.6 + housing_benefit_net_earnings: 17_319.6 + council_tax_reduction_net_earnings: 17_319.6 + # Class 1A of £3,000 x 15% = £450 on top of secondary Class 1. + ni_class_1a: 450 + ni_employer: 2_700 + +- name: The benefit reaches the personal allowance taper + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 100_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # 120 g/km is 30%: a £12,000 benefit takes adjusted net income to + # £112,000, so the allowance falls by £12,000 / 2 to £6,570 + # (Income Tax Act 2007 s. 35). + adjusted_net_income: 112_000 + personal_allowance: 6_570 + +- name: The benefit counts towards the High Income Child Benefit Charge + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 58_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + child: + age: 5 + benunits: + benunit: + members: [parent, child] + would_claim_child_benefit: true + households: + household: + members: [parent, child] + output: + # Adjusted net income of £70,000 is above the £60,000 to £80,000 taper + # midpoint, so the charge is half the Child Benefit + # (Income Tax (Earnings and Pensions) Act 2003 s. 681C). + adjusted_net_income: [70_000, 0] + # Child Benefit of £27.05 a week for 52 weeks is £1,406.60, rounded + # down to £1,406; half of it is £703 (s. 681C(3)). + CB_HITC: [703, 0] diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml new file mode 100644 index 0000000000..47e19a0bf2 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml @@ -0,0 +1,102 @@ +# The car fuel benefit (ITEPA 2003 ss. 149 to 152). HMRC 480 Chapter 13 +# gives the worked example for 2021-22: +# https://www.gov.uk/guidance/taxable-fuel-provided-for-company-cars-and-vans-480-chapter-13 + +- name: HMRC 480 paragraph 13.6, a petrol car first registered on or after 6 April 2020, in 2021-22 + period: 2021 + absolute_error_margin: 0.01 + input: + company_car_list_price: 30_000 + company_car_co2_emissions: 160 + company_car_registered_from_6_april_2020: true + company_car_private_fuel_provided: true + output: + # The appropriate percentage for the car is 36%, and the 2021-22 car + # fuel benefit charge is £24,600 x 36% = £8,856. + company_car_appropriate_percentage: 0.36 + car_fuel_benefit: 8_856 + +- name: Diesel car not meeting RDE2 with private fuel, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 15_000 + company_car_co2_emissions: 156 + company_car_fuel_type: DIESEL_NON_RDE2 + company_car_private_fuel_provided: true + output: + # The car's 37% of the £29,200 multiplier (SI 2025/1254): £10,804. + car_fuel_benefit: 10_804 + +- name: 2027-28 uses the multiplier announced at Budget 2026 + period: 2027 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_private_fuel_provided: true + output: + # 120 g/km is 30% in 2027-28 (HMRC future rates table). OOTLAR + # paragraph 2.6 gives a £30,200 multiplier: £30,200 x 30% = £9,060. + car_fuel_benefit: 9_060 + +- name: No fuel benefit when private fuel is made good in full + period: 2026 + absolute_error_margin: 0 + input: + company_car_list_price: 15_000 + company_car_co2_emissions: 156 + company_car_private_fuel_provided: false + output: + # s. 151, condition A. + car_fuel_benefit: 0 + +- name: No fuel benefit for a car that cannot emit CO2 + period: 2026 + absolute_error_margin: 0 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 0 + company_car_fuel_type: ELECTRIC + company_car_private_fuel_provided: true + output: + # s. 149(4): energy for such a car is not fuel. + car_fuel_benefit: 0 + +- name: Fuel benefit is reduced for days the car is unavailable + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 15_000 + company_car_co2_emissions: 156 + company_car_fuel_type: DIESEL_NON_RDE2 + company_car_private_fuel_provided: true + company_car_availability: 0.5 + output: + # s. 152(1): £10,804 x 0.5 = £5,402. + car_fuel_benefit: 5_402 + +- name: Payments for private use and capital contributions do not reduce the fuel benefit + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 15_000 + company_car_capital_contributions: 5_000 + company_car_co2_emissions: 156 + company_car_fuel_type: DIESEL_NON_RDE2 + company_car_private_fuel_provided: true + company_car_private_use_payments: 10_000 + output: + # HMRC 480 paragraph 13.1: a fuel benefit arises even where these + # reduce the car benefit to nil. + company_car_benefit: 0 + car_fuel_benefit: 10_804 + +- name: No fuel benefit without a company car + period: 2026 + absolute_error_margin: 0 + input: + company_car_co2_emissions: 120 + company_car_private_fuel_provided: true + output: + car_fuel_benefit: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml new file mode 100644 index 0000000000..e0229224d3 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml @@ -0,0 +1,318 @@ +# The company car benefit (ITEPA 2003 s. 121) on HMRC's worked examples. +# HMRC 480 Appendix 1 (company car benefit examples) gives Examples 6 and 7 +# for 2020-21. Examples 3 to 5 are for 2018-19, before the model's first +# year, so their cars are valued here with the appropriate percentages in +# HMRC 480 Appendix 2's 2026-27 table: +# https://www.gov.uk/guidance/company-car-benefit-examples-480-appendix-1 +# https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 + +- name: No company car gives no benefit + period: 2026 + absolute_error_margin: 0 + input: + employment_income: 40_000 + output: + company_car_benefit: 0 + car_fuel_benefit: 0 + benefits_in_kind: 0 + taxable_employment_income: 40_000 + +- name: HMRC 480 Appendix 1 Example 6, a hybrid first registered before 6 April 2020, in 2020-21 + period: 2020 + absolute_error_margin: 0.01 + input: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 77 + output: + # First registered on 22 March 2020, so the NEDC figures: 1-50 g/km + # with an electric range of 70-129 miles is 5%. £32,000 x 5% = £1,600. + company_car_appropriate_percentage: 0.05 + company_car_benefit: 1_600 + +- name: HMRC 480 Appendix 1 Example 7, a hybrid first registered on 6 April 2020, in 2020-21 + period: 2020 + absolute_error_margin: 0.01 + input: + company_car_list_price: 22_000 + company_car_co2_emissions: 50 + company_car_electric_range: 65 + company_car_registered_from_6_april_2020: true + output: + # WLTP figures, so the s. 139A rates: 40-69 miles is 6%. + # £22,000 x 6% = £1,320. + company_car_appropriate_percentage: 0.06 + company_car_benefit: 1_320 + +- name: The s. 139A rates end after 2021-22 + period: 2022 + absolute_error_margin: 0.01 + input: + company_car_list_price: 22_000 + company_car_co2_emissions: 50 + company_car_electric_range: 65 + company_car_registered_from_6_april_2020: true + output: + # HMRC 480 Appendix 2, 2022-23: 40-69 miles is 8% for NEDC and WLTP + # cars alike. £22,000 x 8% = £1,760. + company_car_appropriate_percentage: 0.08 + company_car_benefit: 1_760 + +- name: Petrol car, HMRC 480 Appendix 1 Example 5's car, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 18_000 + company_car_co2_emissions: 104 + output: + # 104 rounds down to 100 g/km: 26% (Appendix 2, 2026-27). + # £18,000 x 26% = £4,680. + company_car_appropriate_percentage: 0.26 + company_car_benefit: 4_680 + +- name: Petrol car at the maximum, HMRC 480 Appendix 1 Example 3's car, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 15_000 + company_car_co2_emissions: 173 + output: + # 173 rounds down to 170 g/km: 37% (Appendix 2, 2026-27, 170 and + # above). £15,000 x 37% = £5,550. + company_car_appropriate_percentage: 0.37 + company_car_benefit: 5_550 + +- name: Diesel car not meeting RDE2 takes the 4-point supplement, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 18_000 + company_car_co2_emissions: 104 + company_car_fuel_type: DIESEL_NON_RDE2 + output: + # 26% for 100 g/km plus the 4-point diesel supplement (HMRC 480 + # paragraph 12.29). £18,000 x 30% = £5,400. + company_car_appropriate_percentage: 0.3 + company_car_benefit: 5_400 + +- name: Diesel car meeting RDE2 takes no supplement, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 18_000 + company_car_co2_emissions: 104 + company_car_fuel_type: DIESEL_RDE2 + output: + company_car_appropriate_percentage: 0.26 + company_car_benefit: 4_680 + +- name: Diesel supplement stops at the maximum, HMRC 480 Appendix 1 Example 4's car, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 15_000 + company_car_co2_emissions: 156 + company_car_fuel_type: DIESEL_NON_RDE2 + output: + # 156 rounds down to 155 g/km: 37% (Appendix 2, 2026-27). Adding 4 + # points would pass the 37% maximum (paragraph 12.29, note 2). + # £15,000 x 37% = £5,550. + company_car_appropriate_percentage: 0.37 + company_car_benefit: 5_550 + +- name: 1-50 g/km cars in each electric range band, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + range_130: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 130 + range_77: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 77 + range_65: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 65 + range_35: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 35 + range_20: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 20 + range_29_point_2: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 29.2 + benunits: + benunit: + members: [range_130, range_77, range_65, range_35, range_20, range_29_point_2] + households: + household: + members: [range_130, range_77, range_65, range_35, range_20, range_29_point_2] + output: + # Appendix 2, 2026-27: 130 and above 4%, 70-129 7%, 40-69 10%, + # 30-39 14%, less than 30 16%. 29.2 miles rounds up to 30 (s. 139(2)(b)). + company_car_appropriate_percentage: [0.04, 0.07, 0.1, 0.14, 0.16, 0.14] + company_car_benefit: [1_280, 2_240, 3_200, 4_480, 5_120, 4_480] + +- name: 1-50 g/km cars take one rate from 2028-29 whatever their electric range + period: 2029 + absolute_error_margin: 0.01 + input: + people: + range_130: + company_car_list_price: 32_000 + company_car_co2_emissions: 50 + company_car_electric_range: 130 + range_20: + company_car_list_price: 32_000 + company_car_co2_emissions: 1 + company_car_electric_range: 20 + benunits: + benunit: + members: [range_130, range_20] + households: + household: + members: [range_130, range_20] + output: + # Finance Act 2025 s. 6(2): 19% for 1-50 g/km in 2029-30. + company_car_appropriate_percentage: [0.19, 0.19] + company_car_benefit: [6_080, 6_080] + +- name: Zero-emission car in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 0 + company_car_fuel_type: ELECTRIC + output: + # Appendix 2, 2026-27: 4%. £40,000 x 4% = £1,600. + company_car_appropriate_percentage: 0.04 + company_car_benefit: 1_600 + +- name: 2029-30 rates, the baseline for 2030-31 + period: 2029 + absolute_error_margin: 0.01 + input: + people: + electric: + company_car_list_price: 40_000 + company_car_co2_emissions: 0 + band_65_74: + company_car_list_price: 40_000 + company_car_co2_emissions: 74 + band_75: + company_car_list_price: 40_000 + company_car_co2_emissions: 79 + high: + company_car_list_price: 40_000 + company_car_co2_emissions: 200 + benunits: + benunit: + members: [electric, band_65_74, band_75, high] + households: + household: + members: [electric, band_65_74, band_75, high] + output: + # Finance Act 2025 s. 6: 9% for 0 g/km, 23% for 65-74 g/km, 23% at + # 75-79 g/km, and a 39% maximum. + company_car_appropriate_percentage: [0.09, 0.23, 0.23, 0.39] + company_car_benefit: [3_600, 9_200, 9_200, 15_600] + +- name: 2030-31 keeps 2029-30's rates in the baseline + period: 2030 + absolute_error_margin: 0.01 + input: + people: + electric: + company_car_list_price: 40_000 + company_car_co2_emissions: 0 + hybrid: + company_car_list_price: 40_000 + company_car_co2_emissions: 30 + company_car_electric_range: 40 + high: + company_car_list_price: 40_000 + company_car_co2_emissions: 200 + benunits: + benunit: + members: [electric, hybrid, high] + households: + household: + members: [electric, hybrid, high] + output: + company_car_appropriate_percentage: [0.09, 0.19, 0.39] + +- name: Capital contributions above £5,000 are capped + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_capital_contributions: 7_000 + company_car_co2_emissions: 120 + output: + # s. 132(3): the deduction is the lesser of the contributions and + # £5,000. 120 g/km is 30% (Appendix 2, 2026-27). + # (£40,000 - £5,000) x 30% = £10,500. + company_car_benefit: 10_500 + +- name: Capital contributions below the cap are deducted in full + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_capital_contributions: 3_000 + company_car_co2_emissions: 120 + output: + # (£40,000 - £3,000) x 30% = £11,100. + company_car_benefit: 11_100 + +- name: Car available for part of the year + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_availability: 0.5 + output: + # s. 143(3) deducts A x U / Y: £12,000 x 0.5 = £6,000. + company_car_benefit: 6_000 + +- name: Payments for private use come off after the reduction for unavailability + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_availability: 0.5 + company_car_private_use_payments: 1_200 + output: + # Steps 7 then 8 of s. 121(1): £12,000 x 0.5 - £1,200 = £4,800. + company_car_benefit: 4_800 + +- name: Payments for private use above the benefit reduce it to nil + period: 2026 + absolute_error_margin: 0 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_private_use_payments: 13_000 + output: + # s. 144(2). + company_car_benefit: 0 + +- name: No benefit before 2020-21, the model's first year + period: 2019 + absolute_error_margin: 0 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + company_car_benefit: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1a/ni_class_1a.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1a/ni_class_1a.yaml new file mode 100644 index 0000000000..0e452d55a5 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1a/ni_class_1a.yaml @@ -0,0 +1,98 @@ +# Class 1A contributions on benefits in kind (SSCBA 1992 s. 10). The Class +# 1A percentage is the secondary Class 1 percentage for the year (s. 10(5)): +# 13.8% to 2024-25 and 15% from 2025-26, except 14.53% for 2022-23 (Health +# and Social Care Levy (Repeal) Act 2022 Schedule paragraph 1(2)). Each case +# has a petrol car with a list price of £40,000 emitting 120 g/km, whose +# appropriate percentage is 29% to 2024-25 and 30% from 2025-26 (HMRC 480 +# Appendix 2), so its benefit is £11,600, then £12,000. + +- name: Class 1A at 13.8% in 2021-22 + period: 2021 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # £11,600 x 13.8% = £1,600.80. + ni_class_1a: 1_600.8 + +- name: Class 1A at the 2022-23 rate of 14.53%, not the secondary percentage at 30 April + period: 2022 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # £11,600 x 14.53% = £1,685.48. + ni_class_1a: 1_685.48 + +- name: Class 1A back at 13.8% in 2023-24 + period: 2023 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + ni_class_1a: 1_600.8 + +- name: Class 1A at 15% from 2025-26 + period: 2025 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # £12,000 x 15% = £1,800. + ni_class_1a: 1_800 + +- name: Class 1A on the car and car fuel benefits together + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_private_fuel_provided: true + output: + # Car £12,000 plus fuel £29,200 x 30% = £8,760: £20,760 x 15% = £3,114. + benefits_in_kind: 20_760 + ni_class_1a: 3_114 + +- name: Class 1A reaches employer NI alongside secondary Class 1 + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 40_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # Secondary Class 1: (£40,000 - £5,000) x 15% = £5,250, on pay only. + ni_class_1_employer: 5_250 + ni_class_1a: 1_800 + ni_employer: 7_050 + +- name: Class 1A is due for an earner under 21, whose pay is zero-rated for secondary Class 1 + period: 2026 + absolute_error_margin: 0.01 + input: + age: 20 + employment_income: 30_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + ni_class_1_employer: 0 + ni_class_1a: 1_800 + ni_employer: 1_800 + +- name: Total National Insurance includes Class 1A + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 40_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # Employee Class 1: (£40,000 - £12,570) x 8% = £2,194.40, on pay only. + ni_class_1_employee: 2_194.4 + total_national_insurance: 2_194.4 + 7_050 diff --git a/policyengine_uk/tests/test_company_car_benefit.py b/policyengine_uk/tests/test_company_car_benefit.py new file mode 100644 index 0000000000..32b40dc98e --- /dev/null +++ b/policyengine_uk/tests/test_company_car_benefit.py @@ -0,0 +1,341 @@ +"""The company car benefit against HMRC's published rates, and its reforms. + +1. Published rates: for every tax year from 2020-21 to 2029-30, the + appropriate percentage at both ends of each band equals the rate HMRC + publishes for it: HMRC 480 Appendix 2 for 2020-21 to 2026-27 (its + 2022-23 table holds to 2024-25), HMRC's future rates table for 2027-28, + and the tables in Finance Act 2025 ss. 5 and 6 for 2028-29 and 2029-30. + In 2020-21 and 2021-22 cars first registered before 6 April 2020 take + the NEDC column and later cars the WLTP column (ITEPA 2003 s. 139A). + A diesel car not meeting RDE2 adds 4 points, up to the maximum. +2. Budget 2026: the company_car_tax_rise_2030 scenario raises every + appropriate percentage for 2030-31 by one point, to a maximum of 40%. It + changes a company car holder's income tax and their employer's Class 1A, + nothing else, and nobody else's tax; 2029-30 is unchanged. +3. Employer NI: Class 1A follows the secondary percentage (SSCBA 1992 + s. 10(5)), so an employer rate reform moves it. The fixed-employer-cost + response covers secondary Class 1 only, and baseline_employer_cost and + adjusted_employer_cost still equal each simulation's employer_cost for a + company car holder, as they do for everyone else. + +Comparisons allow float32 rounding. +""" + +import numpy as np +import pytest + +from policyengine_uk import Simulation +from policyengine_uk.scenarios import company_car_tax_rise_2030 + +pytestmark = pytest.mark.usefixtures("cloned_uk_tax_benefit_system") + +# Each table, in percentage points: the rate for 0 g/km; for 1 to 50 g/km by +# electric range (130 miles or more, 70-129, 40-69, 30-39, under 30); for +# 51-54, 55-59, 60-64, 65-69 and 70-74 g/km; and for each 5 g/km band from +# 75-79 to 165-169 g/km, then 170 g/km and above. +NEDC_2020 = ( + 0, + [2, 5, 8, 12, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37], +) +WLTP_2020 = ( + 0, + [0, 3, 6, 10, 12], + [13, 14, 15, 16, 17], + [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37], +) +NEDC_2021 = ( + 1, + [2, 5, 8, 12, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37], +) +WLTP_2021 = ( + 1, + [1, 4, 7, 11, 13], + [14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37], +) +ALL_CARS_2022 = ( + 2, + [2, 5, 8, 12, 14], + [15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37], +) +ALL_CARS_2025 = ( + 3, + [3, 6, 9, 13, 15], + [16, 17, 18, 19, 20], + [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37, 37], +) +ALL_CARS_2026 = ( + 4, + [4, 7, 10, 14, 16], + [17, 18, 19, 20, 21], + [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37, 37], +) +ALL_CARS_2027 = ( + 5, + [5, 8, 11, 15, 17], + [18, 19, 20, 21, 21], + [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37, 37], +) +# Finance Act 2025 ss. 5 and 6: one rate for 1 to 50 g/km, a 65-74 g/km +# band, and from 75 g/km 22% (then 23%) plus one point for each 5 g/km, up +# to 38% (then 39%). +ALL_CARS_2028 = ( + 7, + [18] * 5, + [19, 20, 21, 22, 22], + [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 38, 38, 38], +) +ALL_CARS_2029 = ( + 9, + [19] * 5, + [20, 21, 22, 23, 23], + [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 39, 39, 39], +) +# Budget 2026 for 2030-31: one point above 2029-30 in every band, up to 40%. +BUDGET_2026_2030 = ( + 10, + [20] * 5, + [21, 22, 23, 24, 24], + [24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 40, 40, 40], +) + +# (year, first registered on or after 6 April 2020, table) +PUBLISHED = [ + (2020, False, NEDC_2020), + (2020, True, WLTP_2020), + (2021, False, NEDC_2021), + (2021, True, WLTP_2021), + (2022, False, ALL_CARS_2022), + (2022, True, ALL_CARS_2022), + (2023, True, ALL_CARS_2022), + (2024, False, ALL_CARS_2022), + (2025, False, ALL_CARS_2025), + (2026, True, ALL_CARS_2026), + (2027, False, ALL_CARS_2027), + (2028, False, ALL_CARS_2028), + (2029, False, ALL_CARS_2029), +] + +RANGE_BANDS = [(130, 300), (70, 129), (40, 69), (30, 39), (0, 29)] +CO2_BANDS = [(51, 54), (55, 59), (60, 64), (65, 69), (70, 74)] +GRADUATED_BANDS = [(low, low + 4) for low in range(75, 170, 5)] + [(170, 250)] + + +def band_points(table): + """(CO2 g/km, electric range in miles, expected rate) at each band's ends.""" + zero, by_range, by_co2, graduated = table + points = [(0, 0, zero)] + for (low, high), rate in zip(RANGE_BANDS, by_range): + for co2 in (1, 50): + points += [(co2, low, rate), (co2, high, rate)] + for bands, rates in ((CO2_BANDS, by_co2), (GRADUATED_BANDS, graduated)): + for (low, high), rate in zip(bands, rates): + points += [(low, 0, rate), (high, 0, rate)] + return points + + +def situation(year, cars): + """One person per car, each with the given company car inputs.""" + people = { + f"person_{i}": { + "age": {year: 40}, + "employment_income": {year: 40_000}, + **{name: {year: value} for name, value in car.items()}, + } + for i, car in enumerate(cars) + } + return { + "people": people, + "benunits": {f"benunit_{name}": {"members": [name]} for name in people}, + "households": {f"household_{name}": {"members": [name]} for name in people}, + } + + +def values(simulation, variable, year): + return np.asarray(simulation.calculate(variable, year), dtype=float) + + +def appropriate_percentages(year, cars, **kwargs): + simulation = Simulation(situation=situation(year, cars), **kwargs) + return values(simulation, "company_car_appropriate_percentage", year) + + +def cars_at(points, registered, fuel_type="PETROL"): + return [ + { + "company_car_list_price": 30_000, + "company_car_co2_emissions": co2, + "company_car_electric_range": electric_range, + "company_car_registered_from_6_april_2020": registered, + "company_car_fuel_type": fuel_type, + } + for co2, electric_range, _ in points + ] + + +@pytest.mark.parametrize("year, registered, table", PUBLISHED) +def test_appropriate_percentages_match_published_rates(year, registered, table): + points = band_points(table) + actual = appropriate_percentages(year, cars_at(points, registered)) + expected = np.array([rate for *_, rate in points]) / 100 + np.testing.assert_allclose(actual, expected, atol=1e-6) + + +@pytest.mark.parametrize("year, registered, table", PUBLISHED) +def test_diesel_supplement_adds_four_points_up_to_the_maximum(year, registered, table): + # HMRC 480 paragraph 12.29: a diesel car not meeting RDE2 adds 4 points, + # subject to the overall maximum, which is the table's highest rate. + points = [point for point in band_points(table) if point[0] > 0] + maximum = max(table[3]) + actual = appropriate_percentages( + year, cars_at(points, registered, "DIESEL_NON_RDE2") + ) + expected = np.minimum(np.array([rate for *_, rate in points]) + 4, maximum) / 100 + np.testing.assert_allclose(actual, expected, atol=1e-6) + + +def test_figures_are_rounded_as_section_139_rounds_them(): + # s. 139(2): CO2 down to a whole number, electric range up; s. 139(4): + # from 75 g/km, CO2 down to a multiple of 5. 2026-27 rates. + cars = [ + { + "company_car_list_price": 30_000, + "company_car_co2_emissions": co2, + "company_car_electric_range": electric_range, + } + for co2, electric_range in [(50.9, 130), (49, 29.1), (74.9, 0), (79.9, 0)] + ] + actual = appropriate_percentages(2026, cars) + np.testing.assert_allclose(actual, [0.04, 0.14, 0.21, 0.21], atol=1e-6) + + +def test_2030_31_rise_sets_the_budget_2026_rates(): + points = band_points(BUDGET_2026_2030) + cars = cars_at(points, registered=False) + baseline = appropriate_percentages(2030, cars) + reformed = appropriate_percentages(2030, cars, scenario=company_car_tax_rise_2030) + expected = np.array([rate for *_, rate in points]) / 100 + np.testing.assert_allclose(reformed, expected, atol=1e-6) + # The baseline keeps 2029-30's rates. + expected_baseline = np.array([rate for *_, rate in band_points(ALL_CARS_2029)]) + np.testing.assert_allclose(baseline, expected_baseline / 100, atol=1e-6) + + +# A company car holder with private fuel, and an employee without a car. +HOLDER = { + "company_car_list_price": 40_000, + "company_car_co2_emissions": 120, + "company_car_private_fuel_provided": True, +} +TAX_AND_INCOME = [ + "income_tax", + "ni_class_1a", + "ni_class_1_employer", + "ni_class_1_employee", + "employment_income", + "employer_ni_fixed_employer_cost_change", + "uc_individual_earned_income_before_mif", +] + + +@pytest.mark.parametrize("year", [2029, 2030, 2031]) +def test_2030_31_rise_changes_only_holders_income_tax_and_class_1a(year): + cars = [HOLDER, {}] + baseline = Simulation(situation=situation(year, cars)) + reformed = Simulation( + situation=situation(year, cars), scenario=company_car_tax_rise_2030 + ) + change = { + variable: values(reformed, variable, year) - values(baseline, variable, year) + for variable in TAX_AND_INCOME + ["benefits_in_kind", "gov_tax"] + } + benefit_change = change["benefits_in_kind"][0] + if year == 2029: + # The rise starts on 6 April 2030. + for variable, difference in change.items(): + np.testing.assert_allclose(difference, 0, atol=1e-2, err_msg=variable) + return + # One point on the car (£40,000) and on the fuel multiplier. + multiplier = baseline.tax_benefit_system.parameters( + f"{year}-01-01" + ).gov.hmrc.income_tax.benefits_in_kind.car_fuel.multiplier + assert benefit_change == pytest.approx(0.01 * (40_000 + multiplier), abs=0.05) + # Pay of £40,000 plus about £23,000 of car and fuel benefits is in the + # higher rate band, and below the personal allowance taper. + assert change["income_tax"][0] == pytest.approx(0.4 * benefit_change, abs=0.05) + assert change["ni_class_1a"][0] == pytest.approx(0.15 * benefit_change, abs=0.05) + assert change["gov_tax"][0] == pytest.approx( + change["income_tax"][0] + change["ni_class_1a"][0], abs=0.05 + ) + # UC earnings lose the extra income tax and nothing else. + assert change["uc_individual_earned_income_before_mif"][0] == pytest.approx( + -change["income_tax"][0], abs=0.05 + ) + for variable in [ + "ni_class_1_employer", + "ni_class_1_employee", + "employment_income", + "employer_ni_fixed_employer_cost_change", + ]: + np.testing.assert_allclose(change[variable][0], 0, atol=1e-2, err_msg=variable) + # Nothing changes for the employee without a car. + for variable, difference in change.items(): + np.testing.assert_allclose(difference[1], 0, atol=1e-2, err_msg=variable) + + +def test_a_company_car_raises_gov_tax_by_its_income_tax_and_class_1a(): + cars = [HOLDER] + with_car = Simulation(situation=situation(2026, cars)) + without_car = Simulation(situation=situation(2026, [{}])) + + def change(variable): + return (values(with_car, variable, 2026) - values(without_car, variable, 2026))[ + 0 + ] + + assert change("ni_class_1a") > 0 + assert change("gov_tax") == pytest.approx( + change("income_tax") + change("ni_class_1a"), abs=0.05 + ) + # The benefit is not cash: household income falls by the tax on it. + assert change("household_net_income") == pytest.approx( + -change("income_tax"), abs=0.05 + ) + + +EMPLOYER_RATE = "gov.hmrc.national_insurance.class_1.employer_scale[1].rate" + + +def test_class_1a_follows_an_employer_rate_reform_outside_the_fixed_cost_response(): + reform = {EMPLOYER_RATE: {"2026-04-06.2100-12-31": 0.16}} + cars = [HOLDER, {}] + baseline = Simulation(situation=situation(2026, cars)) + reformed = Simulation(situation=situation(2026, cars), reform=reform) + benefits = values(baseline, "benefits_in_kind", 2026) + # SSCBA 1992 s. 10(5): the Class 1A percentage is the secondary + # percentage for the year. + np.testing.assert_allclose( + values(reformed, "ni_class_1a", 2026), 0.16 * benefits, rtol=1e-6 + ) + # The pay response holds employer cost before Class 1A, so the holder's + # pay moves exactly as the other employee's does. + pay_change = values(reformed, "employer_ni_fixed_employer_cost_change", 2026) + assert pay_change[0] < 0 + assert pay_change[0] == pytest.approx(pay_change[1], abs=0.01) + # Agreement: the incidence variables' costs equal employer_cost in each + # simulation, for the holder too. + np.testing.assert_allclose( + values(reformed, "baseline_employer_cost", 2026), + values(reformed.baseline, "employer_cost", 2026), + atol=0.01, + ) + np.testing.assert_allclose( + values(reformed, "adjusted_employer_cost", 2026), + values(reformed, "employer_cost", 2026), + atol=0.01, + ) diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py index 98ca7b95ea..659dbacdf6 100644 --- a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py @@ -3,6 +3,14 @@ class employer_cost(Variable): label = "employer cost" + documentation = ( + "Pay, statutory sick, maternity and paternity pay, employer pension " + "contributions and the secondary Class 1 contributions on them. " + "Benefits in kind and the Class 1A contributions on them are left " + "out, as baseline_employer_cost and adjusted_employer_cost leave " + "them out, so a fixed-employer-cost response to an employer NI " + "reform holds this cost." + ) entity = Person definition_period = YEAR value_type = float @@ -20,7 +28,7 @@ def formula(person, period, parameters): ) return ( person("employment_income", period) - + person("ni_employer", period) + + person("ni_class_1_employer", period) + person("employer_pension_contributions", period) + benefits ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/bases/taxable_employment_income.py b/policyengine_uk/variables/gov/hmrc/income_tax/bases/taxable_employment_income.py index cb82e8af7f..44e95a173d 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/bases/taxable_employment_income.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/bases/taxable_employment_income.py @@ -16,4 +16,8 @@ def formula(person, period, parameters): taxable_earnings = person("employment_income", period) deductions = add(person, period, ["employment_deductions"]) benefits = person("employment_benefits", period) - return max_(0, taxable_earnings + benefits - deductions) + # Benefits code earnings (ITEPA 2003 s. 63), held apart from pay and + # statutory pay because Class 1 NICs and the means tests' earnings + # leave them out. + benefits_in_kind = person("benefits_in_kind", period) + return max_(0, taxable_earnings + benefits + benefits_in_kind - deductions) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py new file mode 100644 index 0000000000..a643126617 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py @@ -0,0 +1,39 @@ +from policyengine_uk.model_api import * + + +class benefits_in_kind(Variable): + value_type = float + entity = Person + label = "Taxable benefits in kind" + documentation = ( + "Benefits the person's employment provides that the benefits code " + "(ITEPA 2003 s. 63) treats as earnings: the company car and car " + "fuel benefits. Income tax charges them through " + "taxable_employment_income, and employers pay Class 1A NICs on them. " + "They are not earnings for Class 1 NICs, nor for the earnings tests " + "of Universal Credit (UC Regulations 2013 reg. 55(2)(a)), Housing " + "Benefit (HB Regulations 2006 reg. 35(2)(a)) and Council Tax " + "Reduction, so they are kept out of employment_income and " + "employment_benefits. Other benefits in kind and vans are not " + "modelled." + ) + definition_period = YEAR + unit = GBP + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 63 (the benefits code)", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/63", + ), + dict( + title="Universal Credit Regulations 2013 reg. 55(2)(a)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/55", + ), + dict( + title="Housing Benefit Regulations 2006 reg. 35(2)(a)", + href="https://www.legislation.gov.uk/uksi/2006/213/regulation/35", + ), + ] + adds = [ + "company_car_benefit", + "car_fuel_benefit", + ] diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py new file mode 100644 index 0000000000..120bb6872a --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py @@ -0,0 +1,47 @@ +from policyengine_uk.model_api import * + + +class car_fuel_benefit(Variable): + value_type = float + entity = Person + label = "Car fuel benefit" + documentation = ( + "The cash equivalent of fuel provided for private use of the " + "person's company car, treated as earnings from their employment " + "(ITEPA 2003 s. 149): the car's appropriate percentage of the car " + "fuel benefit charge multiplier (s. 150), reduced in proportion to " + "the days the car is unavailable (s. 152(1)). It is nil where the " + "person makes good the whole cost of private fuel (s. 151), and for " + "a car that cannot emit CO2, whose energy is not fuel (s. 149(4)). " + "Payments for private use and capital contributions do not reduce " + "it. Calculated from 2020-21." + ) + definition_period = YEAR + unit = GBP + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 149", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/149", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 150", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/150", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 152", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/152", + ), + dict( + title="HMRC 480 Chapter 13: taxable fuel provided for company cars and vans", + href="https://www.gov.uk/guidance/taxable-fuel-provided-for-company-cars-and-vans-480-chapter-13", + ), + ] + + def formula_2020(person, period, parameters): + p = parameters(period).gov.hmrc.income_tax.benefits_in_kind.car_fuel + has_company_car = person("company_car_list_price", period) > 0 + fuel_provided = person("company_car_private_fuel_provided", period) + can_emit_co2 = person("company_car_co2_emissions", period) > 0 + full_year = p.multiplier * person("company_car_appropriate_percentage", period) + availability = min_(max_(person("company_car_availability", period), 0), 1) + return has_company_car * fuel_provided * can_emit_co2 * full_year * availability diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py new file mode 100644 index 0000000000..2f5ff20568 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py @@ -0,0 +1,88 @@ +from policyengine_uk.model_api import * + + +class company_car_appropriate_percentage(Variable): + value_type = float + entity = Person + label = "Appropriate percentage of the person's company car" + documentation = ( + "The share of the company car's price taxed each year as its " + "benefit, which the car fuel benefit also uses (ITEPA 2003 s. 139). " + "A car emitting 0 g/km takes the zero-emission rate, one emitting 1 " + "to 50 g/km a rate by its electric range, and one emitting 51 to " + "74 g/km a rate from the table. From 75 g/km the rate is the base " + "rate plus one point for each full 5 g/km above 75, up to the " + "maximum. A diesel car not meeting the Euro 6d (RDE2) standard adds " + "the diesel supplement, within the maximum (s. 141). In 2020-21 and " + "2021-22 a car first registered on or after 6 April 2020 takes the " + "s. 139A rates. Cars without a CO2 emissions figure (s. 140) and " + "cars first registered before 1 January 1998 (s. 142) are not " + "modelled. Calculated from 2020-21." + ) + definition_period = YEAR + unit = "/1" + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 139", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/139", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 141", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/141", + ), + dict( + title="Finance Act 2020 ss. 9 and 10 (s. 139A for 2020-21 and 2021-22)", + href="https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted", + ), + ] + + def formula_2020(person, period, parameters): + p = parameters( + period + ).gov.hmrc.income_tax.benefits_in_kind.company_car.appropriate_percentage + # s. 139(2): the CO2 emissions figure is rounded down to a whole + # number and the electric range figure up. + co2 = np.floor(max_(person("company_car_co2_emissions", period), 0)) + electric_range = np.ceil(max_(person("company_car_electric_range", period), 0)) + recent = p.registered_from_6_april_2020 + uses_recent_rates = recent.in_effect & person( + "company_car_registered_from_6_april_2020", period + ) + electric_range_rate = where( + uses_recent_rates, + recent.electric_range.calc(electric_range), + p.electric_range.calc(electric_range), + ) + co2_band_rate = where( + uses_recent_rates, + recent.co2_band.calc(co2), + p.co2_band.calc(co2), + ) + # s. 139(3) and (4): from the threshold, the base rate plus the + # increment for each full step above it, up to the maximum. + graduated = p.graduated + base_rate = where( + uses_recent_rates, recent.graduated_base_rate, graduated.base_rate + ) + steps = np.floor((co2 - graduated.threshold) / graduated.step) + graduated_rate = min_( + base_rate + graduated.increment * max_(steps, 0), p.maximum + ) + # The 1-50 g/km row of the s. 139(1) table ends where the first + # 51-74 g/km band begins. Finance Act 2020 ss. 9(2)(a) and 10(2)(a) + # give the s. 139 table the s. 139A rate for 0 g/km, so both + # schedules use the one zero-emission rate. + rate = select( + [ + co2 == 0, + co2 < p.co2_band.thresholds[0], + co2 < graduated.threshold, + ], + [p.zero_emission, electric_range_rate, co2_band_rate], + default=graduated_rate, + ) + # s. 141: a diesel car not meeting the Euro 6d standard adds the + # supplement, up to the maximum. + fuel_type = person("company_car_fuel_type", period) + diesel = fuel_type == fuel_type.possible_values.DIESEL_NON_RDE2 + return where(diesel, min_(rate + p.diesel_supplement, p.maximum), rate) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py new file mode 100644 index 0000000000..2f96848090 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py @@ -0,0 +1,54 @@ +from policyengine_uk.model_api import * + + +class company_car_benefit(Variable): + value_type = float + entity = Person + label = "Company car benefit" + documentation = ( + "The cash equivalent of the benefit of the person's company car, " + "treated as earnings from their employment (ITEPA 2003 s. 120), " + "following the steps of s. 121(1): the price of the car and its " + "accessories, less capital contributions up to the cap (s. 132), " + "times the appropriate percentage, reduced in proportion to the " + "days the car is unavailable (s. 143), less payments for private " + "use, and nil if those payments are at least that amount (s. 144). " + "Not modelled: optional remuneration arrangements (s. 120A), " + "classic cars (s. 147), shared and pool cars (ss. 148 and 167), and " + "vans (s. 154 onwards). Calculated from 2020-21." + ) + definition_period = YEAR + unit = GBP + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 120", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/120", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 121", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/121", + ), + dict( + title="HMRC 480 Chapter 12: how to work out the benefit of a company car", + href="https://www.gov.uk/guidance/how-to-work-out-the-benefit-of-a-company-car-480-chapter-12", + ), + ] + + def formula_2020(person, period, parameters): + p = parameters(period).gov.hmrc.income_tax.benefits_in_kind.company_car + # Steps 1 and 2: the price of the car and its accessories. + price = max_(person("company_car_list_price", period), 0) + # Step 3: capital contributions, up to the cap (s. 132(3)). + contributions = min_( + max_(person("company_car_capital_contributions", period), 0), + p.capital_contribution_cap, + ) + interim_sum = max_(price - contributions, 0) + # Steps 5 and 6: the appropriate percentage of the interim sum. + full_year = interim_sum * person("company_car_appropriate_percentage", period) + # Step 7: s. 143(3) deducts A x U / Y for the days unavailable. + availability = min_(max_(person("company_car_availability", period), 0), 1) + provisional_sum = full_year * availability + # Step 8: payments for private use (s. 144). + payments = max_(person("company_car_private_use_payments", period), 0) + return max_(provisional_sum - payments, 0) diff --git a/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py b/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py new file mode 100644 index 0000000000..785674cc06 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py @@ -0,0 +1,38 @@ +from policyengine_uk.model_api import * + + +class ni_class_1a(Variable): + value_type = float + entity = Person + label = "NI Class 1A contributions" + documentation = ( + "Class 1A contributions the employer pays on the person's taxable " + "benefits in kind, which Class 1 leaves out of earnings (SSCBA 1992 " + "s. 10(1) and (4)). The Class 1A percentage equals the secondary " + "Class 1 percentage for the year (s. 10(5)), except in 2022-23, when " + "the Health and Social Care Levy (Repeal) Act 2022 fixes it at " + "14.53%. Unlike secondary Class 1, it has no threshold and no zero " + "rate for earners under 21 or apprentices under 25, and the " + "Employment Allowance cannot be set against it." + ) + definition_period = YEAR + unit = GBP + reference = [ + dict( + title="Social Security Contributions and Benefits Act 1992 s. 10", + href="https://www.legislation.gov.uk/ukpga/1992/4/section/10", + ), + dict( + title="Health and Social Care Levy (Repeal) Act 2022 Schedule paragraph 1", + href="https://www.legislation.gov.uk/ukpga/2022/43/schedule/paragraph/1/enacted", + ), + ] + + def formula(person, period, parameters): + ni = parameters(period).gov.hmrc.national_insurance + fixed = ni.class_1a.percentage_2022_23 + if fixed.in_effect: + rate = fixed.rate + else: + rate = ni.class_1.employer_scale.components[1].rate + return rate * person("benefits_in_kind", period) diff --git a/policyengine_uk/variables/gov/hmrc/national_insurance/ni_employer.py b/policyengine_uk/variables/gov/hmrc/national_insurance/ni_employer.py index 063dc1e014..073b87e60e 100644 --- a/policyengine_uk/variables/gov/hmrc/national_insurance/ni_employer.py +++ b/policyengine_uk/variables/gov/hmrc/national_insurance/ni_employer.py @@ -5,8 +5,13 @@ class ni_employer(Variable): value_type = float entity = Person label = "employer-side National Insurance" + documentation = ( + "Secondary Class 1 contributions on the person's earnings and Class " + "1A contributions on their benefits in kind." + ) definition_period = YEAR unit = GBP adds = [ "ni_class_1_employer", + "ni_class_1a", ] diff --git a/policyengine_uk/variables/gov/hmrc/national_insurance/total_national_insurance.py b/policyengine_uk/variables/gov/hmrc/national_insurance/total_national_insurance.py index e972224314..8e28743a1d 100644 --- a/policyengine_uk/variables/gov/hmrc/national_insurance/total_national_insurance.py +++ b/policyengine_uk/variables/gov/hmrc/national_insurance/total_national_insurance.py @@ -10,5 +10,5 @@ class total_national_insurance(Variable): unit = GBP adds = [ "national_insurance", - "ni_class_1_employer", + "ni_employer", ] diff --git a/policyengine_uk/variables/household/marginal_tax_rate_wrt_employer_cost.py b/policyengine_uk/variables/household/marginal_tax_rate_wrt_employer_cost.py index 5be93cbc6a..dda3336e72 100644 --- a/policyengine_uk/variables/household/marginal_tax_rate_wrt_employer_cost.py +++ b/policyengine_uk/variables/household/marginal_tax_rate_wrt_employer_cost.py @@ -12,8 +12,8 @@ class marginal_tax_rate_wrt_employer_cost(Variable): label = "Marginal tax rate with respect to employer cost" documentation = ( "Percent of a marginal increase in the employer's cost of employment " - "(gross pay plus employer NI, employer pension contributions and " - "statutory sick/maternity/paternity pay) that does not flow to " + "(gross pay plus secondary Class 1 NI, employer pension contributions " + "and statutory sick/maternity/paternity pay) that does not flow to " "household net income. Gross pay rises by " "gov.simulation.marginal_tax_rate_delta, or 0.1% of employment income " "where that is larger, so that float32 rounding stays negligible at any " diff --git a/policyengine_uk/variables/input/company_car/company_car_availability.py b/policyengine_uk/variables/input/company_car/company_car_availability.py new file mode 100644 index 0000000000..f49cd2d09b --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_availability.py @@ -0,0 +1,28 @@ +from policyengine_uk.model_api import * + + +class company_car_availability(Variable): + value_type = float + entity = Person + label = "Share of the tax year the person's company car is available" + documentation = ( + "The share of the tax year's days on which the company car is " + "available to the person: 1 less the share of days on which it is " + "unavailable, which are days before it is first available, after it " + "is last available, and within a period of 30 days or more when it " + "is not available (ITEPA 2003 s. 143(2)). The company car and car " + "fuel benefits are reduced in proportion (ss. 143(3) and 152(1))." + ) + definition_period = YEAR + unit = "/1" + default_value = 1 + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 143", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/143", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 152", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/152", + ), + ] diff --git a/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py b/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py new file mode 100644 index 0000000000..ed73abe2d8 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class company_car_capital_contributions(Variable): + value_type = float + entity = Person + label = "Capital contributions to the person's company car" + documentation = ( + "Capital sums the person has contributed, in this and earlier tax " + "years, to the cost of their company car and its accessories. The " + "company car benefit deducts them up to the cap " + "(ITEPA 2003 s. 132(3))." + ) + definition_period = YEAR + unit = GBP + quantity_type = STOCK + reference = dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 132", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/132", + ) diff --git a/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py b/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py new file mode 100644 index 0000000000..23aa1dfbda --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py @@ -0,0 +1,29 @@ +from policyengine_uk.model_api import * + + +class company_car_co2_emissions(Variable): + value_type = float + entity = Person + label = "CO2 emissions figure of the person's company car" + documentation = ( + "The CO2 emissions figure of the person's company car in grams per " + "kilometre, as on its certificate of conformity: 0 for a car that " + "cannot emit CO2 by being driven. For a car first registered on or " + "after 6 April 2020 it is the WLTP figure (ITEPA 2003 s. 136(2B)). " + "The model treats every company car as having a CO2 emissions " + "figure; cars without one (s. 140) and cars first registered before " + "1 January 1998 (s. 142) are not modelled." + ) + definition_period = YEAR + unit = "g/km" + quantity_type = STOCK + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 136", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/136", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 139", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/139", + ), + ] diff --git a/policyengine_uk/variables/input/company_car/company_car_electric_range.py b/policyengine_uk/variables/input/company_car/company_car_electric_range.py new file mode 100644 index 0000000000..a8c8a0f696 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_electric_range.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class company_car_electric_range(Variable): + value_type = float + entity = Person + label = "Electric range figure of the person's company car" + documentation = ( + "The distance in miles the person's company car can be driven in " + "electric mode without recharging, as on its certificate of " + "conformity (ITEPA 2003 s. 139(5)). Only cars with a CO2 emissions " + "figure of 1 to 50 g/km use it, and only to 2027-28." + ) + definition_period = YEAR + unit = "mile" + quantity_type = STOCK + reference = dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 139", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/139", + ) diff --git a/policyengine_uk/variables/input/company_car/company_car_fuel_type.py b/policyengine_uk/variables/input/company_car/company_car_fuel_type.py new file mode 100644 index 0000000000..7398bbd5a9 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_fuel_type.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.input.vehicle.car_fuel_type import CarFuelType + + +class company_car_fuel_type(Variable): + value_type = Enum + possible_values = CarFuelType + default_value = CarFuelType.PETROL + entity = Person + label = "Fuel type of the person's company car" + documentation = ( + "A car propelled solely by diesel that does not meet the Euro 6d " + "(RDE2) emissions standard takes the diesel supplement (ITEPA 2003 " + "s. 141). A diesel hybrid is not propelled solely by diesel." + ) + definition_period = YEAR + reference = dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 141", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/141", + ) diff --git a/policyengine_uk/variables/input/company_car/company_car_list_price.py b/policyengine_uk/variables/input/company_car/company_car_list_price.py new file mode 100644 index 0000000000..3133970780 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_list_price.py @@ -0,0 +1,28 @@ +from policyengine_uk.model_api import * + + +class company_car_list_price(Variable): + value_type = float + entity = Person + label = "List price of the person's company car, with its accessories" + documentation = ( + "The list price of a car made available to the person by reason of " + "their employment and for their private use, on the day before its " + "first registration, plus the price of its qualifying accessories " + "(ITEPA 2003 s. 121(1) steps 1 and 2). Zero means the person has no " + "company car. This is not car_list_price, which describes the " + "person's own car for vehicle excise duty." + ) + definition_period = YEAR + unit = GBP + quantity_type = STOCK + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 121", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/121", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 123", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/123", + ), + ] diff --git a/policyengine_uk/variables/input/company_car/company_car_private_fuel_provided.py b/policyengine_uk/variables/input/company_car/company_car_private_fuel_provided.py new file mode 100644 index 0000000000..c75c31c2f6 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_private_fuel_provided.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * + + +class company_car_private_fuel_provided(Variable): + value_type = bool + entity = Person + label = "Fuel provided for private use of the person's company car" + documentation = ( + "Whether fuel is provided for the company car by reason of the " + "person's employment and its cost for private use is not made good " + "in full (ITEPA 2003 ss. 149 and 151). Electricity is not fuel " + "(s. 149(4)). Part-year provision of fuel (s. 152(2)) is not " + "modelled; the car fuel benefit is reduced only for days the car is " + "unavailable." + ) + definition_period = YEAR + reference = [ + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 149", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/149", + ), + dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 151", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/151", + ), + ] diff --git a/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py b/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py new file mode 100644 index 0000000000..049d140565 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class company_car_private_use_payments(Variable): + value_type = float + entity = Person + label = "Payments for private use of the person's company car" + documentation = ( + "Amounts the person is required to pay, and pays by 6 July after the " + "tax year, as a condition of the company car being available for " + "private use (ITEPA 2003 s. 144). Payments for fuel or insurance are " + "not payments for private use." + ) + definition_period = YEAR + unit = GBP + quantity_type = FLOW + reference = dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 144", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/144", + ) diff --git a/policyengine_uk/variables/input/company_car/company_car_registered_from_6_april_2020.py b/policyengine_uk/variables/input/company_car/company_car_registered_from_6_april_2020.py new file mode 100644 index 0000000000..7ba9c394e3 --- /dev/null +++ b/policyengine_uk/variables/input/company_car/company_car_registered_from_6_april_2020.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * + + +class company_car_registered_from_6_april_2020(Variable): + value_type = bool + entity = Person + label = "Company car first registered on or after 6 April 2020" + documentation = ( + "In 2020-21 and 2021-22 a car first registered on or after 6 April " + "2020 has lower appropriate percentages (ITEPA 2003 s. 139A, " + "inserted for each year by Finance Act 2020 ss. 9 and 10). From " + "2022-23 every car takes the same rates and this has no effect." + ) + definition_period = YEAR + reference = [ + dict( + title="Finance Act 2020 s. 9", + href="https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted", + ), + dict( + title="Finance Act 2020 s. 10", + href="https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted", + ), + ] From e1f7f70f6b5f848fda38ab77742d0c96b1a27be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:01:02 +0000 Subject: [PATCH 2/4] Address review: student loans, HB tax share, zero-emission and uprating Leave benefits in kind out of student loan repayment income and the Plan 2 interest rate (SI 2009/470 regs. 29(4)(e), 41 and 21A(11)). Count them with pay in the HB, CTR and IS share of income tax, though not in the earnings. Take an ELECTRIC car as 0 g/km, and give a car that rounds down to 0 g/km neither the diesel supplement nor a fuel benefit. Make the appropriate percentage and both benefits zero without a company car, give the 1-50 g/km limit its own parameter, and uprate the money inputs by CPI. Document that no household bears Class 1A, so the 2030-31 rise stays static (#2752 covers passing it through), what data must fill the inputs, and why the 2027-28 fuel multiplier is in the baseline. Add the tests the review listed. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2749.added.md | 2 +- .../book/assumptions/employer-ni-incidence.md | 13 ++ .../programs/gov/hmrc/company-car-benefit.md | 70 ++++++++--- policyengine_uk/data/uprating_indices.yaml | 5 + .../benefits_in_kind/car_fuel/multiplier.yaml | 9 +- .../electric_range_co2_limit.yaml | 12 ++ .../graduated_base_rate.yaml | 5 + .../benefits_in_kind_interactions.yaml | 114 ++++++++++++++++++ .../benefits_in_kind/car_fuel_benefit.yaml | 29 +++++ .../benefits_in_kind/company_car_benefit.yaml | 96 +++++++++++++-- .../tests/test_company_car_benefit.py | 110 +++++++++++++---- policyengine_uk/utils/company_car.py | 27 +++++ policyengine_uk/utils/pensions.py | 7 +- .../applicable_income/_net_earnings.py | 13 +- .../benefits_in_kind/benefits_in_kind.py | 6 +- .../benefits_in_kind/car_fuel_benefit.py | 13 +- .../company_car_appropriate_percentage.py | 51 ++++---- .../benefits_in_kind/company_car_benefit.py | 5 +- .../benefits_in_kind/has_company_car.py | 21 ++++ .../class_1a/ni_class_1a.py | 5 +- .../pensions/pension_contributions_relief.py | 3 +- .../student_loans/plan_2_interest_rate.py | 6 +- .../student_loans/student_loan_repayment.py | 8 +- .../company_car_capital_contributions.py | 1 + .../company_car/company_car_co2_emissions.py | 7 +- .../company_car/company_car_list_price.py | 9 +- .../company_car_private_use_payments.py | 1 + 27 files changed, 557 insertions(+), 91 deletions(-) create mode 100644 policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range_co2_limit.yaml create mode 100644 policyengine_uk/utils/company_car.py create mode 100644 policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/has_company_car.py diff --git a/changelog.d/2749.added.md b/changelog.d/2749.added.md index 46db1b72b0..0ef3999318 100644 --- a/changelog.d/2749.added.md +++ b/changelog.d/2749.added.md @@ -1 +1 @@ -Added the company car and car fuel benefits in kind from 2020-21 (ITEPA 2003 ss. 120 to 152), with the s. 139 appropriate percentages to 2029-30, person inputs for the company car, and Class 1A NICs on both benefits in `ni_employer` and `total_national_insurance`; the benefits are taxed through `taxable_employment_income` and kept out of Class 1 and means-tested earnings, and the `company_car_tax_rise_2030` scenario applies Budget 2026's one-point rise for 2030-31. +Added the company car and car fuel benefits in kind from 2020-21 (ITEPA 2003 ss. 120 to 152), with the s. 139 appropriate percentages to 2029-30, person inputs for the company car, and Class 1A NICs on both benefits in `ni_employer` and `total_national_insurance`; the benefits are taxed through `taxable_employment_income` and kept out of Class 1 earnings, means-tested earnings and student loan repayment income, and the `company_car_tax_rise_2030` scenario applies Budget 2026's one-point rise for 2030-31. diff --git a/docs/book/assumptions/employer-ni-incidence.md b/docs/book/assumptions/employer-ni-incidence.md index 78fadb32a6..01187806c3 100644 --- a/docs/book/assumptions/employer-ni-incidence.md +++ b/docs/book/assumptions/employer-ni-incidence.md @@ -62,6 +62,19 @@ The response amounts enter `household_tax`; those same variables are not added t and [capital](https://github.com/PolicyEngine/policyengine-uk/blob/b4dbaf57a4ebb1d1f0ba5f7a384795422828db75/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py) allocation formulas. +## Secondary Class 1 only + +All three channels cover secondary Class 1 contributions. `ni_employer` also +holds Class 1A contributions on benefits in kind (`ni_class_1a`), but +`employer_cost`, `baseline_employer_cost` and `adjusted_employer_cost` leave +them out, as they leave out the benefits themselves. No household bears +Class 1A or a change in it, whether the change comes from the benefits (as +with a rise in the company car appropriate percentages) or from the employer +rate, which Class 1A follows (SSCBA 1992 s. 10(5)). Such a reform raises +`gov_tax` by its Class 1A change on top of what households pay. Passing +Class 1A through these channels is +[#2752](https://github.com/PolicyEngine/policyengine-uk/issues/2752). + ## Measures and aggregation Report these quantities separately: diff --git a/docs/book/programs/gov/hmrc/company-car-benefit.md b/docs/book/programs/gov/hmrc/company-car-benefit.md index f69baafff7..cd17d0b8f3 100644 --- a/docs/book/programs/gov/hmrc/company-car-benefit.md +++ b/docs/book/programs/gov/hmrc/company-car-benefit.md @@ -16,7 +16,7 @@ simulation that sets none of them is unchanged. | --- | --- | | `company_car_list_price` | List price of the car and its accessories; zero means no company car | | `company_car_capital_contributions` | Capital sums the employee has contributed, this year and earlier | -| `company_car_co2_emissions` | CO2 emissions figure in g/km (0 for a zero-emission car) | +| `company_car_co2_emissions` | CO2 emissions figure in g/km (0 for a car that cannot emit CO2) | | `company_car_electric_range` | Electric range figure in miles | | `company_car_fuel_type` | Fuel type (the `CarFuelType` enum also used for vehicle excise duty) | | `company_car_registered_from_6_april_2020` | First registered on or after 6 April 2020 (matters in 2020-21 and 2021-22 only) | @@ -27,6 +27,21 @@ simulation that sets none of them is unchanged. The `car_*` inputs describe the person's own car for vehicle excise duty and play no part here. +Data that fills these inputs needs to know three things: + +- **Set the CO2 figure with the list price.** The model cannot tell a + missing figure from 0 g/km, so a car with a list price and no CO2 figure is + taxed at the zero-emission rate. A car of the `ELECTRIC` fuel type is taken + to emit 0 g/km whatever figure is entered. +- **Keep the benefits out of pay.** `employment_income` must not also hold + them, or they are taxed twice and wrongly bear employee NICs. The SPI's + EPB (expenses payments and benefits in kind) includes them, so a dataset + that adds EPB to pay, as policyengine-uk-data's SPI records do, must take + the car and car fuel benefits out of it. +- **Projected years.** The list price, capital contributions and payments + for private use grow with CPI when a dataset is projected to later years. + CPI stands in for a new-car price index, which the OBR does not forecast. + ## Calculation `company_car_appropriate_percentage` applies [s. 139][s139]: a rate for @@ -34,9 +49,11 @@ and play no part here. a rate for each band from 51-54 to 70-74 g/km (65-74 g/km from 2028-29), and from 75 g/km a base rate plus one point for each full 5 g/km, up to the maximum. A diesel car not meeting the Euro 6d (RDE2) standard adds 4 points, -within the maximum ([s. 141][s141]). In 2020-21 and 2021-22 a car first -registered on or after 6 April 2020 takes the lower rates of s. 139A -(Finance Act 2020 ss. 9 and 10). +within the maximum ([s. 141][s141]); a car that cannot emit CO2 takes no +supplement. In 2020-21 and 2021-22 a car first registered on or after +6 April 2020 takes the lower rates of s. 139A (Finance Act 2020 ss. 9 and +10). The appropriate percentage is zero for a person without a company car +(`has_company_car`). `company_car_benefit` follows the steps of [s. 121][s121]: the price, less capital contributions up to £5,000, times the appropriate percentage, @@ -49,26 +66,40 @@ car that cannot emit CO2. `benefits_in_kind` adds the two. ## Where the benefits go - **Income tax.** `taxable_employment_income` adds `benefits_in_kind`, so the - benefits reach income tax and adjusted net income: the personal allowance - taper, the High Income Child Benefit Charge, and the £100,000 limits of - Tax-Free Childcare and the extended childcare entitlement. They also reach - the other uses of net income, such as student loan repayments. + benefits reach income tax, at Scottish rates for a Scottish taxpayer, and + adjusted net income: the personal allowance taper, the High Income Child + Benefit Charge, and the £100,000 limits of Tax-Free Childcare and the + extended childcare entitlement. They count as relevant UK earnings for the + limit on pension tax relief (HMRC PTM044100). +- **Not student loan income.** Student loan repayments and the Plan 2 + interest rate leave the benefits out: Self Assessment excludes Chapter 6 + car benefits ([SI 2009/470 reg. 29(4)(e)][sl29]), and PAYE deducts + repayments from Class 1 earnings (reg. 41). - **Not earnings for Class 1 or the means tests.** The benefits are not in `employment_income` or `employment_benefits`, so employee Class 1 NICs and the earnings of Universal Credit ([UC Regulations 2013 reg. 55(2)(a)][uc55]), Housing Benefit (HB Regulations 2006 reg. 35(2)(a)) and Council Tax Reduction leave them out. Those tests still deduct the income tax on them, which is tax paid in respect of the employment (reg. 55(5)(b)) and which - PAYE collects from pay. The Childcare Offer for Wales's gross income lists - its sources one by one, and benefits in kind are not among them. + PAYE collects from pay. Universal Credit takes earnings as the lowest slice + of income, so it deducts all of that tax. Housing Benefit and Council Tax + Reduction deduct earnings' share of the person's tax, and count the + benefits with pay in that share; with other income, part of the tax on the + benefits falls on the other income, as part of the tax on pay does. The + Childcare Offer for Wales's gross income lists its sources one by one, and + benefits in kind are not among them. - **Employer National Insurance.** `ni_class_1a` is the Class 1A percentage of `benefits_in_kind`, and `ni_employer` adds it to secondary Class 1, so it reaches `gov_tax`. The Class 1A percentage is the secondary percentage for - the year, except 14.53% in 2022-23. `employer_cost`, and the employer NI + the year, except 14.53% in 2022-23. +- **No household bears Class 1A.** `employer_cost`, and the employer NI incidence variables that recompute it, cover pay and secondary Class 1 - only: they leave out benefits in kind and the Class 1A on them, so an - employer NI reform's fixed-cost pay response does not offset a change in - Class 1A. + only. So the fixed-cost pay response and the capital and consumer + channels leave out Class 1A and any change in it: it stays with employers + as revenue. A reform that changes Class 1A, such as the 2030-31 rise below + or a rise in the employer rate, raises `gov_tax` by more than it lowers + household incomes. Passing Class 1A through like secondary Class 1 is + [#2752](https://github.com/PolicyEngine/policyengine-uk/issues/2752). ## 2030-31 @@ -88,6 +119,12 @@ reformed = Simulation(situation=situation, scenario=company_car_tax_rise_2030) `policyengine_uk.scenarios.company_car_tax_rise_2030.COMPANY_CAR_TAX_RISE_2030` holds the same changes as a parameter-changes dictionary. +The car fuel benefit charge multiplier is in the baseline for 2027-28 at the +£30,200 Budget 2026 announced, before the Treasury order is made. That is the +annual CPI uprating, which the Budget does not score, so a pre-Budget +baseline has it too; the 2030-31 rise is a policy decision Budget 2026 scores +(Table 6.1, row 86). The fuel part of the rise depends on the multiplier. + ## Not modelled - Vans and van fuel (ITEPA 2003 s. 154 onwards). @@ -98,8 +135,8 @@ holds the same changes as a parameter-changes dictionary. - Fuel provided for only part of the year (s. 152(2)). - Other benefits in kind. -The survey data does not fill these inputs yet, and a dataset projected to -later years holds them at the data year's values. +The survey data does not fill these inputs yet, so a population run scores +the company car benefits at zero until a dataset does. [s120]: https://www.legislation.gov.uk/ukpga/2003/1/section/120 [s121]: https://www.legislation.gov.uk/ukpga/2003/1/section/121 @@ -108,4 +145,5 @@ later years holds them at the data year's values. [s149]: https://www.legislation.gov.uk/ukpga/2003/1/section/149 [s150]: https://www.legislation.gov.uk/ukpga/2003/1/section/150 [sscba10]: https://www.legislation.gov.uk/ukpga/1992/4/section/10 +[sl29]: https://www.legislation.gov.uk/uksi/2009/470/regulation/29 [uc55]: https://www.legislation.gov.uk/uksi/2013/376/regulation/55 diff --git a/policyengine_uk/data/uprating_indices.yaml b/policyengine_uk/data/uprating_indices.yaml index ee591c95de..5e28db57dd 100644 --- a/policyengine_uk/data/uprating_indices.yaml +++ b/policyengine_uk/data/uprating_indices.yaml @@ -46,6 +46,11 @@ gov.economic_assumptions.yoy_growth.obr.consumer_price_index: - childcare_expenses - clothing_and_footwear_consumption - communication_consumption +# Company car prices and the payments that go with them. CPI stands in for +# a new-car price index, which the OBR does not forecast. +- company_car_capital_contributions +- company_car_list_price +- company_car_private_use_payments - education_consumption - esa_contrib_reported - esa_income_reported diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml index a4ecc604e9..97e9c97cfa 100644 --- a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/car_fuel/multiplier.yaml @@ -2,8 +2,13 @@ description: Amount to which the company car's appropriate percentage is applied # The Treasury sets this by order under ITEPA 2003 s. 170(5); no statute # indexes it. The 2027-28 figure is announced: Budget 2026 OOTLAR paragraph # 2.6 gives £30,200 (September CPI uprating), and the Treasury order is not -# yet made. From 2028-29 the values are a CPI projection, as the government -# has uprated the figure by September CPI each year; none is announced. +# yet made. It is in the baseline although the Budget 2026 rise in the +# appropriate percentages for 2030-31 is not: that rise is a policy decision +# Budget 2026 scores (Table 6.1, row 86), while this is the annual CPI +# uprating, which the Budget lists among its other announcements and does +# not score, so a pre-Budget baseline has it too. From 2028-29 the values +# are a CPI projection, as the government has uprated the figure by +# September CPI each year; none is announced. values: 2020-04-06: value: 24_500 diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range_co2_limit.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range_co2_limit.yaml new file mode 100644 index 0000000000..88ed3e0c70 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/electric_range_co2_limit.yaml @@ -0,0 +1,12 @@ +description: Highest CO2 emissions figure, rounded down to a whole number, at which a company car's appropriate percentage is the rate for 1 to 50 g/km, set by its electric range to 2027-28 and a single rate from 2028-29. +values: + 2020-04-06: 50 +metadata: + unit: g/km + period: year + label: Company car CO2 limit for the 1 to 50 g/km band + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139(1) + href: https://www.legislation.gov.uk/ukpga/2003/1/section/139 + - title: Finance (No. 2) Act 2017 s. 2(2) + href: https://www.legislation.gov.uk/ukpga/2017/32/section/2/enacted diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml index 6f8c9305d3..3b3caacd81 100644 --- a/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/benefits_in_kind/company_car/appropriate_percentage/registered_from_6_april_2020/graduated_base_rate.yaml @@ -16,3 +16,8 @@ metadata: unit: /1 period: year label: Graduated appropriate percentage base rate, cars first registered from 6 April 2020 + reference: + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139A(b), inserted for 2020-21 by Finance Act 2020 s. 9(3) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/9/enacted + - title: Income Tax (Earnings and Pensions) Act 2003 s. 139A(b), inserted for 2021-22 by Finance Act 2020 s. 10(3) + href: https://www.legislation.gov.uk/ukpga/2020/14/section/10/enacted diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml index 23c8f4ff8c..0ffdf693c1 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml @@ -102,3 +102,117 @@ # Child Benefit of £27.05 a week for 52 weeks is £1,406.60, rounded # down to £1,406; half of it is £703 (s. 681C(3)). CB_HITC: [703, 0] + +- name: Student loan repayments leave the benefit out + period: 2026 + absolute_error_margin: 0.01 + input: + age: 30 + employment_income: 40_000 + student_loan_plan: PLAN_2 + company_car_list_price: 30_000 + company_car_co2_emissions: 120 + output: + # A £9,000 benefit (30% of £30,000) is in net income, but not in the + # income repayments are due on: Self Assessment leaves out Chapter 6 car + # benefits (SI 2009/470 reg. 29(4)(e)) and PAYE deducts repayments from + # Class 1 earnings (reg. 41). 9% of (£40,000 - £29,385) = £955.35. + net_income: 49_000 + student_loan_repayment: 955.35 + +- name: The pension relief limit counts the benefit as relevant UK earnings + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 10_000 + personal_pension_contributions: 14_000 + company_car_list_price: 20_000 + company_car_co2_emissions: 120 + output: + # Taxable benefits in kind are employment income chargeable to tax, so + # relevant UK earnings (Finance Act 2004 s. 189(2)(a); HMRC PTM044100): + # £10,000 + £6,000 = £16,000 is above the £14,000 contributed, so all of + # it is relieved. Without the car the limit would be £10,000. + pension_contributions_relief: 14_000 + +- name: A Scottish taxpayer pays Scottish rates on the benefit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person: + age: 40 + employment_income: 35_000 + company_car_list_price: 75_000 + company_car_co2_emissions: 0 + company_car_fuel_type: ELECTRIC + benunits: + benunit: + members: [person] + households: + household: + members: [person] + region: SCOTLAND + output: + # Taxable income of £35,000 + £3,000 - £12,570 = £25,430, all within + # the intermediate band (21%) above £16,956: 19% of £3,967, 20% of + # £12,989 and 21% of £8,474 = £5,131.07, of which 21% of the £3,000 + # benefit is £630. + income_tax: 5_131.07 + +- name: The benefit counts towards the £100,000 limits of Tax-Free Childcare and the extended entitlement + period: 2026 + absolute_error_margin: 0 + input: + age: 40 + employment_income: 95_000 + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + output: + # Adjusted net income of £95,000 + £12,000 is above £100,000 (SI + # 2015/448 reg. 15; SI 2022/1134 regs. 14(3)(c)(i) and 15(3)(b)(i)). + adjusted_net_income: 107_000 + tax_free_childcare_meets_income_requirements: false + extended_childcare_entitlement_meets_income_requirements: false + +- name: Earnings tests with other income, without the company car + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + private_pension_income: 20_000 + output: + # (£40,000 - £12,570) x 20% = £5,486. + income_tax: 5_486 + # UC takes earnings as the lowest slice: (£20,000 - £12,570) x 20%. + uc_income_tax_on_earnings: 1_486 + uc_individual_earned_income_before_mif: 17_919.6 + # HB and CTR take earnings' share of the tax: £5,486 x 20/40 = £2,743; + # £20,000 - £2,743 - £594.40 = £16,662.60. + housing_benefit_net_earnings: 16_662.6 + council_tax_reduction_net_earnings: 16_662.6 + +- name: Earnings tests with other income, with the company car + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + private_pension_income: 20_000 + company_car_list_price: 75_000 + company_car_co2_emissions: 0 + company_car_fuel_type: ELECTRIC + output: + # The £3,000 benefit adds £600 of tax: (£43,000 - £12,570) x 20%. + income_tax: 6_086 + # UC deducts all £600 from earnings, as the benefit is in the lowest + # slice with pay. + uc_income_tax_on_earnings: 2_086 + uc_individual_earned_income_before_mif: 17_319.6 + # HB and CTR count the benefit with pay in earnings' share of the tax, + # though not in the earnings: £6,086 x 23/43 = £3,255.30, so + # £20,000 - £3,255.30 - £594.40 = £16,150.30. + housing_benefit_net_earnings: 16_150.29 + council_tax_reduction_net_earnings: 16_150.29 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml index 47e19a0bf2..7c919c6207 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.yaml @@ -100,3 +100,32 @@ company_car_private_fuel_provided: true output: car_fuel_benefit: 0 + +- name: Fuel benefit for a 1-50 g/km hybrid, in 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 32_000 + company_car_co2_emissions: 40 + company_car_electric_range: 35 + company_car_private_fuel_provided: true + output: + # 30-39 miles is 14% (HMRC 480 Appendix 2, 2026-27): the petrol it + # burns is fuel, the electricity is not (s. 149(4)). + # £29,200 x 14% = £4,088. + car_fuel_benefit: 4_088 + +- name: Fuel benefit under the s. 139A rates, in 2021-22 + period: 2021 + absolute_error_margin: 0.01 + input: + company_car_list_price: 22_000 + company_car_co2_emissions: 50 + company_car_electric_range: 65 + company_car_registered_from_6_april_2020: true + company_car_private_fuel_provided: true + output: + # WLTP, 40-69 miles: 7% (HMRC 480 Appendix 2, 2021-22). + # £24,600 x 7% = £1,722. + company_car_appropriate_percentage: 0.07 + car_fuel_benefit: 1_722 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml index e0229224d3..83901092d5 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml @@ -1,8 +1,9 @@ -# The company car benefit (ITEPA 2003 s. 121) on HMRC's worked examples. -# HMRC 480 Appendix 1 (company car benefit examples) gives Examples 6 and 7 -# for 2020-21. Examples 3 to 5 are for 2018-19, before the model's first -# year, so their cars are valued here with the appropriate percentages in -# HMRC 480 Appendix 2's 2026-27 table: +# The company car benefit (ITEPA 2003 s. 121). HMRC 480 Appendix 1 (company +# car benefit examples) gives worked results for 2020-21 in Examples 6 and 7, +# tested here. Its Examples 3 to 5 are for 2018-19, before the model's first +# year, so the cases below that reuse their cars are not HMRC's worked +# results: they value those cars with the rates in HMRC 480 Appendix 2's +# 2026-27 table, and so test the transcribed rates. # https://www.gov.uk/guidance/company-car-benefit-examples-480-appendix-1 # https://www.gov.uk/guidance/company-car-benefit-the-appropriate-percentage-480-appendix-2 @@ -12,6 +13,8 @@ input: employment_income: 40_000 output: + has_company_car: false + company_car_appropriate_percentage: 0 company_car_benefit: 0 car_fuel_benefit: 0 benefits_in_kind: 0 @@ -58,7 +61,7 @@ company_car_appropriate_percentage: 0.08 company_car_benefit: 1_760 -- name: Petrol car, HMRC 480 Appendix 1 Example 5's car, in 2026-27 +- name: Petrol car at 100 g/km (the car in HMRC 480 Appendix 1 Example 5) at 2026-27 rates period: 2026 absolute_error_margin: 0.01 input: @@ -70,7 +73,7 @@ company_car_appropriate_percentage: 0.26 company_car_benefit: 4_680 -- name: Petrol car at the maximum, HMRC 480 Appendix 1 Example 3's car, in 2026-27 +- name: Petrol car at the maximum (the car in HMRC 480 Appendix 1 Example 3) at 2026-27 rates period: 2026 absolute_error_margin: 0.01 input: @@ -106,7 +109,7 @@ company_car_appropriate_percentage: 0.26 company_car_benefit: 4_680 -- name: Diesel supplement stops at the maximum, HMRC 480 Appendix 1 Example 4's car, in 2026-27 +- name: Diesel supplement stops at the maximum (the car in HMRC 480 Appendix 1 Example 4) at 2026-27 rates period: 2026 absolute_error_margin: 0.01 input: @@ -316,3 +319,80 @@ company_car_co2_emissions: 120 output: company_car_benefit: 0 + +- name: Out-of-range amounts are floored at zero and availability is kept between 0 and 1 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + negative_price: + company_car_list_price: -40_000 + company_car_co2_emissions: 120 + negative_amounts: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_capital_contributions: -5_000 + company_car_private_use_payments: -1_200 + availability_above_one: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_availability: 1.5 + availability_below_zero: + company_car_list_price: 40_000 + company_car_co2_emissions: 120 + company_car_availability: -0.5 + negative_figures: + company_car_list_price: 40_000 + company_car_co2_emissions: -10 + company_car_electric_range: -10 + benunits: + benunit: + members: [negative_price, negative_amounts, availability_above_one, availability_below_zero, negative_figures] + households: + household: + members: [negative_price, negative_amounts, availability_above_one, availability_below_zero, negative_figures] + output: + # A negative price is no car; negative contributions and payments are + # none; availability is capped at the whole year and floored at none. + # Negative CO2 and electric range figures are 0: the zero-emission rate. + has_company_car: [false, true, true, true, true] + company_car_appropriate_percentage: [0, 0.3, 0.3, 0.3, 0.04] + company_car_benefit: [0, 12_000, 12_000, 0, 1_600] + +- name: An electric car is zero-emission whatever CO2 figure is entered + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 150 + company_car_fuel_type: ELECTRIC + company_car_private_fuel_provided: true + output: + # As for vehicle excise duty, the ELECTRIC fuel type gives a CO2 figure + # of 0: 4% in 2026-27, and no car fuel benefit (s. 149(4)). + company_car_appropriate_percentage: 0.04 + company_car_benefit: 1_600 + car_fuel_benefit: 0 + +- name: A car at 0 g/km takes no diesel supplement + period: 2026 + absolute_error_margin: 0.01 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 0 + company_car_fuel_type: DIESEL_NON_RDE2 + output: + # A car that cannot emit CO2 is not propelled by diesel (s. 141(3)). + company_car_appropriate_percentage: 0.04 + company_car_benefit: 1_600 + +- name: A CO2 figure that rounds down to 0 is zero-emission for both benefits + period: 2026 + absolute_error_margin: 0 + input: + company_car_list_price: 40_000 + company_car_co2_emissions: 0.5 + company_car_private_fuel_provided: true + output: + company_car_appropriate_percentage: 0.04 + car_fuel_benefit: 0 diff --git a/policyengine_uk/tests/test_company_car_benefit.py b/policyengine_uk/tests/test_company_car_benefit.py index 32b40dc98e..8c4be71795 100644 --- a/policyengine_uk/tests/test_company_car_benefit.py +++ b/policyengine_uk/tests/test_company_car_benefit.py @@ -11,12 +11,17 @@ 2. Budget 2026: the company_car_tax_rise_2030 scenario raises every appropriate percentage for 2030-31 by one point, to a maximum of 40%. It changes a company car holder's income tax and their employer's Class 1A, - nothing else, and nobody else's tax; 2029-30 is unchanged. + nothing else (their student loan repayment included), and nobody else's + tax; 2029-30 is unchanged. COMPANY_CAR_TAX_RISE_2030 passed as a reform + dict gives the same results. 3. Employer NI: Class 1A follows the secondary percentage (SSCBA 1992 - s. 10(5)), so an employer rate reform moves it. The fixed-employer-cost - response covers secondary Class 1 only, and baseline_employer_cost and - adjusted_employer_cost still equal each simulation's employer_cost for a - company car holder, as they do for everyone else. + s. 10(5)), so an employer rate reform moves it. The employer NI + incidence covers secondary Class 1 only, so no household bears Class 1A: + the fixed-employer-cost pay response and the employer cost change that + the capital and consumer channels spread leave it out. And + baseline_employer_cost and adjusted_employer_cost still equal each + simulation's employer_cost for a company car holder, as they do for + everyone else. Comparisons allow float32 rounding. """ @@ -26,6 +31,9 @@ from policyengine_uk import Simulation from policyengine_uk.scenarios import company_car_tax_rise_2030 +from policyengine_uk.scenarios.company_car_tax_rise_2030 import ( + COMPANY_CAR_TAX_RISE_2030, +) pytestmark = pytest.mark.usefixtures("cloned_uk_tax_benefit_system") @@ -226,21 +234,27 @@ def test_2030_31_rise_sets_the_budget_2026_rates(): np.testing.assert_allclose(baseline, expected_baseline / 100, atol=1e-6) -# A company car holder with private fuel, and an employee without a car. +# A company car holder with private fuel and a Plan 2 student loan, and an +# employee without a car. HOLDER = { "company_car_list_price": 40_000, "company_car_co2_emissions": 120, "company_car_private_fuel_provided": True, + "student_loan_plan": "PLAN_2", } -TAX_AND_INCOME = [ - "income_tax", - "ni_class_1a", +UNCHANGED_FOR_HOLDER = [ "ni_class_1_employer", "ni_class_1_employee", "employment_income", "employer_ni_fixed_employer_cost_change", - "uc_individual_earned_income_before_mif", + "student_loan_repayment", + "plan_2_interest_rate", ] +TAX_AND_INCOME = [ + "income_tax", + "ni_class_1a", + "uc_individual_earned_income_before_mif", +] + UNCHANGED_FOR_HOLDER @pytest.mark.parametrize("year", [2029, 2030, 2031]) @@ -276,22 +290,20 @@ def test_2030_31_rise_changes_only_holders_income_tax_and_class_1a(year): assert change["uc_individual_earned_income_before_mif"][0] == pytest.approx( -change["income_tax"][0], abs=0.05 ) - for variable in [ - "ni_class_1_employer", - "ni_class_1_employee", - "employment_income", - "employer_ni_fixed_employer_cost_change", - ]: - np.testing.assert_allclose(change[variable][0], 0, atol=1e-2, err_msg=variable) + for variable in UNCHANGED_FOR_HOLDER: + np.testing.assert_allclose(change[variable][0], 0, atol=1e-6, err_msg=variable) # Nothing changes for the employee without a car. for variable, difference in change.items(): np.testing.assert_allclose(difference[1], 0, atol=1e-2, err_msg=variable) def test_a_company_car_raises_gov_tax_by_its_income_tax_and_class_1a(): - cars = [HOLDER] - with_car = Simulation(situation=situation(2026, cars)) - without_car = Simulation(situation=situation(2026, [{}])) + # The same employee, with the same student loan, without the car. + without_the_car = { + name: value for name, value in HOLDER.items() if not name.startswith("company") + } + with_car = Simulation(situation=situation(2026, [HOLDER])) + without_car = Simulation(situation=situation(2026, [without_the_car])) def change(variable): return (values(with_car, variable, 2026) - values(without_car, variable, 2026))[ @@ -299,6 +311,8 @@ def change(variable): ] assert change("ni_class_1a") > 0 + # Student loan repayments are in gov_tax but leave the benefit out. + assert change("student_loan_repayment") == 0 assert change("gov_tax") == pytest.approx( change("income_tax") + change("ni_class_1a"), abs=0.05 ) @@ -323,10 +337,15 @@ def test_class_1a_follows_an_employer_rate_reform_outside_the_fixed_cost_respons values(reformed, "ni_class_1a", 2026), 0.16 * benefits, rtol=1e-6 ) # The pay response holds employer cost before Class 1A, so the holder's - # pay moves exactly as the other employee's does. + # pay moves exactly as the other employee's does, and so does the cost + # change the capital and consumer channels would spread. pay_change = values(reformed, "employer_ni_fixed_employer_cost_change", 2026) assert pay_change[0] < 0 assert pay_change[0] == pytest.approx(pay_change[1], abs=0.01) + cost_change = values(reformed, "adjusted_employer_cost", 2026) - values( + reformed, "baseline_employer_cost", 2026 + ) + assert cost_change[0] == pytest.approx(cost_change[1], abs=0.01) # Agreement: the incidence variables' costs equal employer_cost in each # simulation, for the holder too. np.testing.assert_allclose( @@ -339,3 +358,52 @@ def test_class_1a_follows_an_employer_rate_reform_outside_the_fixed_cost_respons values(reformed, "employer_cost", 2026), atol=0.01, ) + + +@pytest.mark.parametrize("year", [2030, 2031, 2035]) +def test_the_parameter_changes_as_a_reform_dict_match_the_scenario(year): + cars = [HOLDER, {}] + from_scenario = Simulation( + situation=situation(year, cars), scenario=company_car_tax_rise_2030 + ) + from_dict = Simulation( + situation=situation(year, cars), reform=COMPANY_CAR_TAX_RISE_2030 + ) + for variable in ["company_car_appropriate_percentage", "income_tax", "gov_tax"]: + np.testing.assert_allclose( + values(from_dict, variable, year), + values(from_scenario, variable, year), + atol=1e-6, + err_msg=variable, + ) + + +INCIDENCE = "gov.contrib.policyengine.employer_ni" + + +def test_no_household_bears_the_class_1a_on_the_2030_31_rise(): + # With half of an employer NI change borne by employees, the rest would + # go to capital and consumers through adjusted_employer_cost less + # baseline_employer_cost. Neither covers Class 1A, so the rise's Class 1A + # moves no pay and leaves nothing for those channels to spread. + reform = { + **COMPANY_CAR_TAX_RISE_2030, + f"{INCIDENCE}.employee_incidence": {"2030-04-06.2100-12-31": 0.5}, + } + simulation = Simulation(situation=situation(2030, [HOLDER]), reform=reform) + class_1a_change = values(simulation, "ni_class_1a", 2030) - values( + simulation.baseline, "ni_class_1a", 2030 + ) + assert class_1a_change[0] > 0 + for variable in ["employer_ni_fixed_employer_cost_change", "employment_income"]: + np.testing.assert_allclose( + values(simulation, variable, 2030), + values(simulation.baseline, variable, 2030), + atol=1e-6, + err_msg=variable, + ) + np.testing.assert_allclose( + values(simulation, "adjusted_employer_cost", 2030), + values(simulation, "baseline_employer_cost", 2030), + atol=0.01, + ) diff --git a/policyengine_uk/utils/company_car.py b/policyengine_uk/utils/company_car.py new file mode 100644 index 0000000000..0bc876ffc3 --- /dev/null +++ b/policyengine_uk/utils/company_car.py @@ -0,0 +1,27 @@ +"""Inputs of the company car and car fuel benefits, as the formulas read them.""" + +from policyengine_core.model_api import max_, min_, where +import numpy as np + + +def co2_emissions_figure(person, period): + """The company car's CO2 emissions figure for ITEPA 2003 s. 139. + + Rounded down to a whole number (s. 139(2)(a)). A car of the ELECTRIC + fuel type (electric or other zero emission) is taken to have a figure of + 0 whatever CO2 figure is entered, as for vehicle excise duty, so a car is + zero-emission for both the appropriate percentage and the car fuel + benefit (s. 149(4)) exactly when this is 0. + """ + fuel_type = person("company_car_fuel_type", period) + co2 = np.floor(max_(person("company_car_co2_emissions", period), 0)) + return where(fuel_type == fuel_type.possible_values.ELECTRIC, 0, co2) + + +def share_of_year_available(person, period): + """The share of the tax year the company car is available, from 0 to 1. + + Both benefits are reduced in proportion to the days the car is + unavailable (ITEPA 2003 ss. 143(3) and 152(1)). + """ + return min_(max_(person("company_car_availability", period), 0), 1) diff --git a/policyengine_uk/utils/pensions.py b/policyengine_uk/utils/pensions.py index be14ef3e7c..ea9ef772a3 100644 --- a/policyengine_uk/utils/pensions.py +++ b/policyengine_uk/utils/pensions.py @@ -27,9 +27,10 @@ def relevant_uk_earnings(person, period, hmrc): """FA 2004 s.189(2) relevant UK earnings. Employment income chargeable to income tax is net taxable earnings - (ITEPA 2003 ss.9(2) and 11): pay and employer-paid taxable SSP, SMP and - SPP (employment_benefits), which the model holds separately from wages, - less the allowable expenses in s.327(3). s.327(5) also lists the deductions for pension contributions + (ITEPA 2003 ss.9(2) and 11): pay, employer-paid taxable SSP, SMP and + SPP (employment_benefits) and taxable benefits in kind (benefits_in_kind, + HMRC PTM044100), which the model holds separately from wages, less the + allowable expenses in s.327(3). s.327(5) also lists the deductions for pension contributions (FA 2004 ss.188-194), but those are the relief that these earnings limit, so they are not taken off; the limit would otherwise depend on itself. Trading income is the profit chargeable under ITTOIA 2005 Part 2, before diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/_net_earnings.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/_net_earnings.py index 993bfd0a45..82088a86be 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/_net_earnings.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/_net_earnings.py @@ -40,7 +40,10 @@ def person_net_earnings( The existing tax-attribution approximation uses earnings' share of an explicit raw income base including statutory pay, before losses, and tax before the Winter Fuel Payment charge. This is independent of the income - tax definition of total_income and its component list. Housing Benefit + tax definition of total_income and its component list. Benefits in kind + are not earnings (reg 35(2)(a)), but PAYE collects the tax on them from + the employment's pay, so they count with earnings in that share and in + the base, though not in the earnings themselves. Housing Benefit (housing_benefit_net_earnings), Council Tax Reduction (council_tax_reduction_net_earnings) and Income Support (income_support_net_earnings) use these deductions. @@ -73,10 +76,14 @@ def person_net_earnings( "taxable_miscellaneous_income", ] ) + # Benefits in kind are taxed with the pay but are not earnings. + benefits_in_kind = person("benefits_in_kind", period) + income_before_losses = income_before_losses + benefits_in_kind + taxed_with_earnings = earnings + benefits_in_kind earnings_share = np.divide( - earnings, + taxed_with_earnings, income_before_losses, - out=np.zeros_like(earnings), + out=np.zeros_like(taxed_with_earnings), where=income_before_losses > 0, ) # Tax before the winter fuel payment charge: the charge recovers a diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py index a643126617..355ce17718 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind.py @@ -14,8 +14,10 @@ class benefits_in_kind(Variable): "of Universal Credit (UC Regulations 2013 reg. 55(2)(a)), Housing " "Benefit (HB Regulations 2006 reg. 35(2)(a)) and Council Tax " "Reduction, so they are kept out of employment_income and " - "employment_benefits. Other benefits in kind and vans are not " - "modelled." + "employment_benefits; those tests do deduct the income tax on them. " + "Student loan repayments and the Plan 2 interest rate leave them " + "out too (SI 2009/470 regs. 29(4)(e) and 41). Other benefits in kind " + "and vans are not modelled." ) definition_period = YEAR unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py index 120bb6872a..285bc3723f 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/car_fuel_benefit.py @@ -1,4 +1,8 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.company_car import ( + co2_emissions_figure, + share_of_year_available, +) class car_fuel_benefit(Variable): @@ -18,6 +22,7 @@ class car_fuel_benefit(Variable): ) definition_period = YEAR unit = GBP + defined_for = "has_company_car" reference = [ dict( title="Income Tax (Earnings and Pensions) Act 2003 s. 149", @@ -39,9 +44,9 @@ class car_fuel_benefit(Variable): def formula_2020(person, period, parameters): p = parameters(period).gov.hmrc.income_tax.benefits_in_kind.car_fuel - has_company_car = person("company_car_list_price", period) > 0 fuel_provided = person("company_car_private_fuel_provided", period) - can_emit_co2 = person("company_car_co2_emissions", period) > 0 + # s. 149(4): the same test as the zero-emission rate in s. 139. + can_emit_co2 = co2_emissions_figure(person, period) > 0 full_year = p.multiplier * person("company_car_appropriate_percentage", period) - availability = min_(max_(person("company_car_availability", period), 0), 1) - return has_company_car * fuel_provided * can_emit_co2 * full_year * availability + availability = share_of_year_available(person, period) + return fuel_provided * can_emit_co2 * full_year * availability diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py index 2f5ff20568..bb9b07b21d 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_appropriate_percentage.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.company_car import co2_emissions_figure class company_car_appropriate_percentage(Variable): @@ -12,15 +13,18 @@ class company_car_appropriate_percentage(Variable): "to 50 g/km a rate by its electric range, and one emitting 51 to " "74 g/km a rate from the table. From 75 g/km the rate is the base " "rate plus one point for each full 5 g/km above 75, up to the " - "maximum. A diesel car not meeting the Euro 6d (RDE2) standard adds " - "the diesel supplement, within the maximum (s. 141). In 2020-21 and " + "maximum. A car of the ELECTRIC fuel type is taken to emit 0 g/km. " + "A diesel car not meeting the Euro 6d (RDE2) standard adds the " + "diesel supplement, within the maximum (s. 141). In 2020-21 and " "2021-22 a car first registered on or after 6 April 2020 takes the " - "s. 139A rates. Cars without a CO2 emissions figure (s. 140) and " - "cars first registered before 1 January 1998 (s. 142) are not " - "modelled. Calculated from 2020-21." + "s. 139A rates. Zero for a person without a company car. Cars " + "without a CO2 emissions figure (s. 140) and cars first registered " + "before 1 January 1998 (s. 142) are not modelled. Calculated from " + "2020-21." ) definition_period = YEAR unit = "/1" + defined_for = "has_company_car" reference = [ dict( title="Income Tax (Earnings and Pensions) Act 2003 s. 139", @@ -42,47 +46,52 @@ def formula_2020(person, period, parameters): ).gov.hmrc.income_tax.benefits_in_kind.company_car.appropriate_percentage # s. 139(2): the CO2 emissions figure is rounded down to a whole # number and the electric range figure up. - co2 = np.floor(max_(person("company_car_co2_emissions", period), 0)) + co2 = co2_emissions_figure(person, period) electric_range = np.ceil(max_(person("company_car_electric_range", period), 0)) - recent = p.registered_from_6_april_2020 - uses_recent_rates = recent.in_effect & person( + from_april_2020 = p.registered_from_6_april_2020 + takes_s139a_rates = from_april_2020.in_effect & person( "company_car_registered_from_6_april_2020", period ) electric_range_rate = where( - uses_recent_rates, - recent.electric_range.calc(electric_range), + takes_s139a_rates, + from_april_2020.electric_range.calc(electric_range), p.electric_range.calc(electric_range), ) co2_band_rate = where( - uses_recent_rates, - recent.co2_band.calc(co2), + takes_s139a_rates, + from_april_2020.co2_band.calc(co2), p.co2_band.calc(co2), ) # s. 139(3) and (4): from the threshold, the base rate plus the # increment for each full step above it, up to the maximum. graduated = p.graduated base_rate = where( - uses_recent_rates, recent.graduated_base_rate, graduated.base_rate + takes_s139a_rates, + from_april_2020.graduated_base_rate, + graduated.base_rate, ) steps = np.floor((co2 - graduated.threshold) / graduated.step) graduated_rate = min_( base_rate + graduated.increment * max_(steps, 0), p.maximum ) - # The 1-50 g/km row of the s. 139(1) table ends where the first - # 51-74 g/km band begins. Finance Act 2020 ss. 9(2)(a) and 10(2)(a) - # give the s. 139 table the s. 139A rate for 0 g/km, so both - # schedules use the one zero-emission rate. + # Finance Act 2020 ss. 9(2)(a) and 10(2)(a) give the s. 139 table the + # s. 139A rate for 0 g/km, so both schedules use the one + # zero-emission rate. + zero_emission = co2 == 0 rate = select( [ - co2 == 0, - co2 < p.co2_band.thresholds[0], + zero_emission, + co2 <= p.electric_range_co2_limit, co2 < graduated.threshold, ], [p.zero_emission, electric_range_rate, co2_band_rate], default=graduated_rate, ) # s. 141: a diesel car not meeting the Euro 6d standard adds the - # supplement, up to the maximum. + # supplement, up to the maximum. A car that cannot emit CO2 is not + # propelled by diesel (s. 141(3)). fuel_type = person("company_car_fuel_type", period) - diesel = fuel_type == fuel_type.possible_values.DIESEL_NON_RDE2 + diesel = (fuel_type == fuel_type.possible_values.DIESEL_NON_RDE2) & ~( + zero_emission + ) return where(diesel, min_(rate + p.diesel_supplement, p.maximum), rate) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py index 2f96848090..68663af5d7 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.company_car import share_of_year_available class company_car_benefit(Variable): @@ -19,6 +20,7 @@ class company_car_benefit(Variable): ) definition_period = YEAR unit = GBP + defined_for = "has_company_car" reference = [ dict( title="Income Tax (Earnings and Pensions) Act 2003 s. 120", @@ -47,8 +49,7 @@ def formula_2020(person, period, parameters): # Steps 5 and 6: the appropriate percentage of the interim sum. full_year = interim_sum * person("company_car_appropriate_percentage", period) # Step 7: s. 143(3) deducts A x U / Y for the days unavailable. - availability = min_(max_(person("company_car_availability", period), 0), 1) - provisional_sum = full_year * availability + provisional_sum = full_year * share_of_year_available(person, period) # Step 8: payments for private use (s. 144). payments = max_(person("company_car_private_use_payments", period), 0) return max_(provisional_sum - payments, 0) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/has_company_car.py b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/has_company_car.py new file mode 100644 index 0000000000..fa79a93491 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/benefits_in_kind/has_company_car.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class has_company_car(Variable): + value_type = bool + entity = Person + label = "Has a company car" + documentation = ( + "Whether the person has a company car available for private use, " + "which the model takes from a positive company_car_list_price. The " + "appropriate percentage and the company car and car fuel benefits " + "are zero without one." + ) + definition_period = YEAR + reference = dict( + title="Income Tax (Earnings and Pensions) Act 2003 s. 114", + href="https://www.legislation.gov.uk/ukpga/2003/1/section/114", + ) + + def formula(person, period, parameters): + return person("company_car_list_price", period) > 0 diff --git a/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py b/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py index 785674cc06..e66484baf7 100644 --- a/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py +++ b/policyengine_uk/variables/gov/hmrc/national_insurance/class_1a/ni_class_1a.py @@ -13,7 +13,10 @@ class ni_class_1a(Variable): "the Health and Social Care Levy (Repeal) Act 2022 fixes it at " "14.53%. Unlike secondary Class 1, it has no threshold and no zero " "rate for earners under 21 or apprentices under 25, and the " - "Employment Allowance cannot be set against it." + "Employment Allowance cannot be set against it. The employer NI " + "incidence variables cover secondary Class 1 only, so no household " + "bears Class 1A or a change in it: it stays with the employer as " + "revenue in gov_tax." ) definition_period = YEAR unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_relief.py b/policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_relief.py index 3ba60f7cd9..a048506d79 100644 --- a/policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_relief.py +++ b/policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_relief.py @@ -18,7 +18,8 @@ class pension_contributions_relief(Variable): "contributions as relief at source and employee contributions as a " "net pay arrangement. The annual allowance does not limit relief. " "Relevant UK earnings (s. 189(2)) are employment income, including " - "employer-paid taxable statutory pay, less allowable expenses but " + "employer-paid taxable statutory pay and taxable benefits in kind, " + "less allowable expenses but " "before pension deductions, and trading profits after capital " "allowances and the trading allowance but before losses. " "Contributions above it can be relieved here and separately attract " diff --git a/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py b/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py index ec5a2f2b5e..e69d4c3f66 100644 --- a/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py +++ b/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py @@ -16,7 +16,11 @@ class plan_2_interest_rate(Variable): reference = "https://www.legislation.gov.uk/uksi/2012/1309/regulation/10" def formula(person, period, parameters): - income = person("net_income", period) + # Interest income is the repayment income (SI 2009/470 reg. 21A(11)), + # which leaves out benefits in kind (regs. 29(4)(e) and 41). + income = max_( + person("net_income", period) - person("benefits_in_kind", period), 0 + ) p = parameters(period).gov.hmrc.student_loans rpi = parameters(period).gov.economic_assumptions.yoy_growth.obr.rpi diff --git a/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py b/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py index f086a65126..8ccab03a3a 100644 --- a/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py +++ b/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py @@ -19,7 +19,13 @@ class student_loan_repayment(Variable): def formula(person, period, parameters): plan = person("student_loan_plan", period) - income = person("net_income", period) + # Benefits in kind are not repayment income: Self Assessment leaves + # out the car benefits of ITEPA 2003 Part 3 Chapter 6 (SI 2009/470 + # reg. 29(4)(e)), and PAYE deducts repayments from Class 1 earnings + # only (reg. 41). + income = max_( + person("net_income", period) - person("benefits_in_kind", period), 0 + ) balance = max_(0, person("student_loan_balance", period)) p = parameters(period).gov.hmrc.student_loans diff --git a/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py b/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py index ed73abe2d8..bb0b1249a7 100644 --- a/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py +++ b/policyengine_uk/variables/input/company_car/company_car_capital_contributions.py @@ -13,6 +13,7 @@ class company_car_capital_contributions(Variable): ) definition_period = YEAR unit = GBP + uprating = "gov.economic_assumptions.indices.obr.consumer_price_index" quantity_type = STOCK reference = dict( title="Income Tax (Earnings and Pensions) Act 2003 s. 132", diff --git a/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py b/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py index 23aa1dfbda..f73a2b76ae 100644 --- a/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py +++ b/policyengine_uk/variables/input/company_car/company_car_co2_emissions.py @@ -8,8 +8,11 @@ class company_car_co2_emissions(Variable): documentation = ( "The CO2 emissions figure of the person's company car in grams per " "kilometre, as on its certificate of conformity: 0 for a car that " - "cannot emit CO2 by being driven. For a car first registered on or " - "after 6 April 2020 it is the WLTP figure (ITEPA 2003 s. 136(2B)). " + "cannot emit CO2 by being driven, and taken as 0 for a car of the " + "ELECTRIC fuel type. Set it whenever company_car_list_price is set: " + "the model cannot tell a missing figure from 0 g/km, and taxes both " + "at the zero-emission rate. For a car first registered on or after " + "6 April 2020 it is the WLTP figure (ITEPA 2003 s. 136(2B)). " "The model treats every company car as having a CO2 emissions " "figure; cars without one (s. 140) and cars first registered before " "1 January 1998 (s. 142) are not modelled." diff --git a/policyengine_uk/variables/input/company_car/company_car_list_price.py b/policyengine_uk/variables/input/company_car/company_car_list_price.py index 3133970780..2661d2a73f 100644 --- a/policyengine_uk/variables/input/company_car/company_car_list_price.py +++ b/policyengine_uk/variables/input/company_car/company_car_list_price.py @@ -10,11 +10,16 @@ class company_car_list_price(Variable): "their employment and for their private use, on the day before its " "first registration, plus the price of its qualifying accessories " "(ITEPA 2003 s. 121(1) steps 1 and 2). Zero means the person has no " - "company car. This is not car_list_price, which describes the " - "person's own car for vehicle excise duty." + "company car. Set company_car_co2_emissions with it: a CO2 figure of " + "0 means a car that cannot emit CO2. The benefits it gives must not " + "also be in employment_income, or they are taxed twice; a dataset " + "whose pay includes benefits in kind, as the SPI's EPB does, must take " + "the car and car fuel benefits out of it. This is not car_list_price, " + "which describes the person's own car for vehicle excise duty." ) definition_period = YEAR unit = GBP + uprating = "gov.economic_assumptions.indices.obr.consumer_price_index" quantity_type = STOCK reference = [ dict( diff --git a/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py b/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py index 049d140565..82b32647a9 100644 --- a/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py +++ b/policyengine_uk/variables/input/company_car/company_car_private_use_payments.py @@ -13,6 +13,7 @@ class company_car_private_use_payments(Variable): ) definition_period = YEAR unit = GBP + uprating = "gov.economic_assumptions.indices.obr.consumer_price_index" quantity_type = FLOW reference = dict( title="Income Tax (Earnings and Pensions) Act 2003 s. 144", From 3940406d702415eb3375b88cd78929de5e1d2b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:29:12 +0000 Subject: [PATCH 3/4] Take off student loan income only the benefits net income holds Where allowable deductions exceed pay, taxable employment income holds only part of the benefits in kind, so subtracting all of them took other income out of repayment income. Subtract the smaller of taxable employment income and the benefits, in one helper for the repayment and the Plan 2 interest rate. Co-Authored-By: Claude Opus 5.5 --- .../benefits_in_kind_interactions.yaml | 21 ++++++++++++++++ policyengine_uk/utils/student_loans.py | 24 +++++++++++++++++++ .../student_loans/plan_2_interest_rate.py | 5 ++-- .../student_loans/student_loan_repayment.py | 10 +++----- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 policyengine_uk/utils/student_loans.py diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml index 0ffdf693c1..8b9d782e1c 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/benefits_in_kind_interactions.yaml @@ -120,6 +120,27 @@ net_income: 49_000 student_loan_repayment: 955.35 +- name: Student loans leave out only the part of the benefit that deductions leave in income + period: 2026 + absolute_error_margin: 0.01 + input: + age: 30 + employment_income: 10_000 + employment_expenses: 15_000 + private_pension_income: 30_000 + student_loan_plan: PLAN_2 + company_car_list_price: 30_000 + company_car_co2_emissions: 120 + output: + # Allowable expenses of £15,000 come off pay and the £9,000 benefit + # together (ITEPA 2003 s. 11): taxable employment income is £4,000, and + # would be nil without the car. So net income of £34,000 holds £4,000 + # of the benefit, and repayment income is £30,000, the pension: + # 9% of (£30,000 - £29,385) = £55.35. + taxable_employment_income: 4_000 + net_income: 34_000 + student_loan_repayment: 55.35 + - name: The pension relief limit counts the benefit as relevant UK earnings period: 2026 absolute_error_margin: 0.01 diff --git a/policyengine_uk/utils/student_loans.py b/policyengine_uk/utils/student_loans.py new file mode 100644 index 0000000000..845850f0ac --- /dev/null +++ b/policyengine_uk/utils/student_loans.py @@ -0,0 +1,24 @@ +"""The income on which student loan repayments and interest are worked out.""" + +from policyengine_core.model_api import max_, min_ + + +def student_loan_income(person, period): + """Net income less the benefits in kind it holds. + + Benefits in kind are not repayment income: Self Assessment leaves out + the car benefits of ITEPA 2003 Part 3 Chapter 6 (SI 2009/470 reg. + 29(4)(e)), and PAYE deducts repayments from Class 1 earnings only + (reg. 41). Plan 2 interest income is the same income (reg. 21A(11)). + + Allowable deductions come off pay and benefits together (ITEPA 2003 + s. 11), so where they exceed pay, taxable employment income holds only + part of the benefits. Taxable employment income is the pay and benefits + less deductions, floored at zero, so it holds the smaller of itself and + the benefits: only that much comes off. + """ + benefits_in_net_income = min_( + person("taxable_employment_income", period), + person("benefits_in_kind", period), + ) + return max_(person("net_income", period) - benefits_in_net_income, 0) diff --git a/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py b/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py index e69d4c3f66..709ed627cd 100644 --- a/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py +++ b/policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.student_loans import student_loan_income import numpy as np @@ -18,9 +19,7 @@ class plan_2_interest_rate(Variable): def formula(person, period, parameters): # Interest income is the repayment income (SI 2009/470 reg. 21A(11)), # which leaves out benefits in kind (regs. 29(4)(e) and 41). - income = max_( - person("net_income", period) - person("benefits_in_kind", period), 0 - ) + income = student_loan_income(person, period) p = parameters(period).gov.hmrc.student_loans rpi = parameters(period).gov.economic_assumptions.yoy_growth.obr.rpi diff --git a/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py b/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py index 8ccab03a3a..cd24f7df64 100644 --- a/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py +++ b/policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.student_loans import student_loan_income from policyengine_uk.variables.gov.hmrc.student_loans.student_loan_plan import ( StudentLoanPlan, ) @@ -19,13 +20,8 @@ class student_loan_repayment(Variable): def formula(person, period, parameters): plan = person("student_loan_plan", period) - # Benefits in kind are not repayment income: Self Assessment leaves - # out the car benefits of ITEPA 2003 Part 3 Chapter 6 (SI 2009/470 - # reg. 29(4)(e)), and PAYE deducts repayments from Class 1 earnings - # only (reg. 41). - income = max_( - person("net_income", period) - person("benefits_in_kind", period), 0 - ) + # Net income less benefits in kind (SI 2009/470 regs. 29(4)(e), 41). + income = student_loan_income(person, period) balance = max_(0, person("student_loan_balance", period)) p = parameters(period).gov.hmrc.student_loans From 4cda1b96394972dfa5dfd60aaa8b17f9873c0950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:29:12 +0000 Subject: [PATCH 4/4] Raise the YAML test job's time limit to 75 minutes On 29 October the job was cancelled at 45 minutes after every test had passed, with the process still alive 11 and 18 minutes after pytest's summary. The runner most likely runs short of memory (#2741). 75 minutes matches the Python shards, which Result waits for anyway. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test-suite.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-suite.yaml b/.github/workflows/test-suite.yaml index 18b02696ec..0ea9cd70c6 100644 --- a/.github/workflows/test-suite.yaml +++ b/.github/workflows/test-suite.yaml @@ -84,9 +84,13 @@ jobs: # (two more were cancelled by newer pushes). In merge-queue run # 40541059060 the cancelled job failed Result once the shards had # finished, which took that pull request out of the queue. 45 minutes - # leaves room for a slow runner and still stops a hung job; Result - # waits about an hour for the shards anyway. - timeout-minutes: 45 + # was then not enough either: on 29 October jobs were cancelled at it + # after every test had passed, with the process still alive 11 and 18 + # minutes after pytest's summary (runs 40895005971 and 40917254004). + # The runner most likely runs short of memory, because every case's + # simulation is kept to the end (#2741). 75 minutes matches the Python + # shards, which Result waits for anyway, until the cases are released. + timeout-minutes: 75 steps: - name: Checkout repo uses: actions/checkout@v6