Repository navigation
Model the company car and car fuel benefits, with Class 1A NICs - #2750
Conversation
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 <noreply@anthropic.com>
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles. Checks failing on both base and head (1)
Counted-once checkEach of 91 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
Review of #2750 at
|
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 <noreply@anthropic.com>
|
Thanks, Anthony. A point-by-point reply follows; the changes are in the commit after Bugs
Design points
Checks against bad values
Smaller pointsAll done:
Test gapsAdded tests for:
CIThe YAML job's tests passed (5,277 in 18 minutes). The process then stayed alive for 11 minutes after the summary, until the 45-minute limit. Main's release YAML job shows the same tail of about 12 minutes, and Optional follow-upsI haven't opened these. Vans, optional remuneration arrangements, pool and shared cars, classic cars, cars without a CO2 figure or registered before 1998, part-year fuel and other benefits in kind are listed under "Not modelled" on the docs page. 🤖 Generated with Claude Code |
Second pass on #2750 at
|
| Point | Status | What was checked |
|---|---|---|
| Student loans count the benefits in kind | Fixed | student_loan_repayment and plan_2_interest_rate use max_(net_income − benefits_in_kind, 0). SI 2009/470 reg. 29(4)(e) excludes "amounts chargeable to tax under Chapters 5, 6, 7 or 10 of Part 3 of the 2003 Act (benefits in kind)", so the basis holds. |
| HB and CTR deduct only part of the tax | Fixed within the proportional split | variables/gov/dwp/housing_benefit/applicable_income/_net_earnings.py adds benefits_in_kind to both the earnings side of the tax share and the base, but not to the earnings. The remaining gap to UC's figure comes from the proportional method, as you say. |
| Class 1A incidence | Kept static on purpose; documented | This is consistent with scoring the rise as the OBR does. It is documented in docs/book/assumptions/employer-ni-incidence.md, on the company car page and in ni_class_1a, and it is pinned by tests. Passing Class 1A through is tracked in #2752. |
| SPI EPB double count | Documented | Noted on company_car_list_price and in the docs page's data section. |
| A list price without a CO2 figure | Partly addressed, reasonably | There is no statutory rate for an unknown figure, so documenting that the two inputs must be filled together is the right call. utils/company_car.py now gives ELECTRIC cars a figure of 0. A car whose figure rounds to 0 takes neither the diesel supplement nor a fuel benefit. |
| Uprating indices | Done | obr.consumer_price_index is declared on company_car_list_price, company_car_capital_contributions and company_car_private_use_payments, and the three are listed in data/uprating_indices.yaml. |
| The £30,200 multiplier against the 2030-31 rates | Explained | The multiplier is the routine CPI uprating, which the Budget lists but doesn't score. The 2030-31 rates are the scored measure. That distinction holds up. |
| CO2 rounding mismatch | Fixed | Both benefits read the one co2_emissions_figure. |
| Upper bounds | Reasonably declined | There has been no statutory price cap since 2011-12, so plausibility checks belong with data validation. |
| Smaller points | Done | has_company_car is used as defined_for on the appropriate percentage and both benefits. Also: from_april_2020, the share_of_year_available helper, the electric_range_co2_limit parameter, and file-level references in graduated_base_rate.yaml. |
| Test gaps | Tests added | Per your reply. They were not run for this pass. |
| Optional follow-ups | Listed under "Not modelled" | Fine as they're optional and low priority. |
Other readers of net_income and adjusted_net_income were also re-checked for any that now pick up the benefit wrongly. Tax-Free Childcare and the extended childcare entitlement use adjusted net income only for the £100,000 upper limit, where the benefit belongs. Their minimum-income tests read separate earnings variables.
New in this pass
-
The student loan fix can over-subtract when allowable employment deductions exceed pay (low priority, rare).
taxable_employment_incomeismax_(0, taxable_earnings + benefits + benefits_in_kind − deductions). When deductions exceed pay, part of the benefit is absorbed by the floor and never reachesnet_income. Subtracting the wholebenefits_in_kindthen takes off more than was added.- Example: pay of £10,000, £15,000 of allowable deductions, a £9,000 benefit and a £30,000 private pension.
taxable_employment_incomeis £4,000 with the car and £0 without. Sonet_income − benefits_in_kindis £25,000, where repayment income excluding the benefit is £30,000. - Fix: subtract only the part of the benefit that reached net income, for example
taxable_employment_incomeminus the same sum computed withoutbenefits_in_kind.
-
Test / YAMLwas cancelled at the 45-minute limit again on this head.- It ran from 17:01:33 to 17:47:03 UTC, and the step "Run the YAML tests" was cancelled. The other test jobs and "Aggregate impact" were still running when checked.
- Your diagnosis (the tests finish, then the process stays alive) and the other branch hitting the same limit point to the workflow rather than this PR. Still, the PR's overall result will fail until that job completes, so it may be worth a separate look at what keeps the process alive after the summary.
🤖 Generated with Claude Code
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
|
Thanks, Anthony. Both points are now in.
🤖 Generated with Claude Code |
For PolicyEngine/uk-autumn-budget-dashboard-2026#316: Budget 2026 raises company car tax by one percentage point in every band from 2030-31, and policyengine-uk 2.165.0 (PolicyEngine/policyengine-uk#2750) models the company car and car fuel benefits. hmrc-benefits-in-kind-july-2026 carries 544 facts from HMRC's Tables 1 to 4 workbook (15 July 2026): Table 1's recipients, taxable value, average taxable value, and Income Tax and Class 1A NIC liabilities, 2011-12 to 2024-25; Tables 2A, 2C and 2E's car benefit recipients by CO2 band and by fuel type and the average CO2 emissions of company cars over the same years; and Tables 3A, 3B and 4's 2024-25 recipients, taxable value and average taxable value by range of total income and by region. Markers emit no fact, never a zero; each sheet's rounding is quoted; 2024-25 is provisional and payrolled cars are under-reported from 2016-17, on every record set's notes. HMRC's averages are ported as printed, since the rounded cells do not reproduce them. Tables 3A, 3B and 4 are grossed from the SPI cases matched to the P11D and RTI records (survey_aggregate); their 'All' rows print Table 1's totals and are not ported again. Re-pin the merged bundle: +544 facts, one source table and one geography (HMRC's unknown recipient region). The merged-bundle test needs `chronicle publish-raw` for the new artifact first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #2749
What and why
Budget 2026 raises company car tax for 2030-31: every appropriate percentage goes up one point, to a maximum of 40% (Table 6.1 row 86: +£310m in 2030-31, +£345m in 2031-32). The model could not score it, because nothing in it computed a company car benefit (PolicyEngine/uk-autumn-budget-dashboard-2026#316). This PR adds the company car and car fuel benefits in kind from 2020-21, the Class 1A NICs employers pay on them, and a scenario that applies the Budget 2026 rise.
Inputs (on the person). They default to no car, so existing simulations don't change. The
car_*vehicle excise duty inputs describe the person's own car and are untouched.company_car_list_pricecompany_car_capital_contributionscompany_car_co2_emissionscompany_car_electric_rangecompany_car_fuel_typeCarFuelType;DIESEL_NON_RDE2takes the supplement (s. 141);ELECTRICgives a CO2 figure of 0company_car_registered_from_6_april_2020company_car_availabilitycompany_car_private_use_paymentscompany_car_private_fuel_providedThe list price, capital contributions and payments for private use are uprated by CPI when a dataset is projected.
Variables
has_company_car(a positive list price).company_car_appropriate_percentage(s. 139, s. 139A, s. 141); zero without a company car.company_car_benefit, following s. 121's steps: price less capital contributions up to £5,000, times the appropriate percentage, reduced for days unavailable, less payments for private use, never below zero.car_fuel_benefit: the appropriate percentage of the s. 150 multiplier, reduced for days unavailable. It is nil for a car that cannot emit CO2 (s. 149(4)), and capital contributions and private-use payments don't reduce it.benefits_in_kind, the sum of the two.ni_class_1a: the Class 1A percentage ofbenefits_in_kind(SSCBA 1992 s. 10).Where the benefits go
taxable_employment_incomeaddsbenefits_in_kindas its own term. The benefits therefore reach income tax (at Scottish rates for Scottish taxpayers) and adjusted net income: the personal allowance taper, HICBC, and the £100,000 limits of Tax-Free Childcare and the extended childcare entitlement. They also count as relevant UK earnings for the pension relief limit (HMRC PTM044100).student_loan_repaymentandplan_2_interest_ratetake net income less the benefits in kind it holds. Self Assessment excludes Chapter 6 car benefits (SI 2009/470 reg. 29(4)(e)), PAYE deducts from Class 1 earnings (reg. 41), and Plan 2 interest income is the repayment income (reg. 21A(11)). Net income holds all of the benefits unless allowable deductions exceed pay, so the helper (utils/student_loans.py) subtracts the smaller oftaxable_employment_incomeandbenefits_in_kind.employment_incomeandemployment_benefits. Employee Class 1 NICs are unchanged, and so are the gross earnings of UC (UC Regs 2013 reg. 55(2)(a)), HB (HB Regs 2006 reg. 35(2)(a)) and CTR.uc_income_tax_on_earningsdeducts all of it.person_net_earnings) deduct earnings' share of the person's tax, and now count the benefits with pay in that share and in the base, though not in the earnings.ni_employerandtotal_national_insuranceaddni_class_1a, so Class 1A reachesgov_tax. The fiscal partition listsni_class_1aas a component of employer NICs.Employer NI incidence (
contrib/policyengine/employer_ni/).baseline_employer_costandadjusted_employer_costrecompute secondary Class 1 only.employer_costreadni_employer, so with Class 1A inni_employerit would have stopped agreeing with them for company car holders. It now readsni_class_1_employer. That leaves every current value unchanged, and it leaves out the Class 1A as well as the benefit it is charged on. Consequences:docs/book/assumptions/employer-ni-incidence.mdand on the company car page, and Pass Class 1A NICs through the employer NI incidence #2752 covers passing Class 1A through.2030-31. The repository's future-date convention (
docs/book/policy/future-date-announcements.md) would put Budget 2026's announced 2030-31 rates in the baseline, as #2712 did for Budget 2026's cash ISA limit. This PR keeps current law in the baseline instead, because the Autumn Budget dashboard needs a pre-Budget 2030-31 baseline with the rise as its reform; see point 1 below. Current law: Finance Act 2025 s. 6 sets 2029-30's rates "for the tax year 2029-30 and subsequent tax years", so 2030-31 has 9% for 0 g/km, 19% for 1-50 g/km and a 39% maximum. The rise is thecompany_car_tax_rise_2030scenario (policyengine_uk/scenarios/company_car_tax_rise_2030.py). ItsCOMPANY_CAR_TAX_RISE_2030dict gives the same changes asparameter_changeskeyed2030-04-06.2100-12-31, for the dashboard. Moving the rise into the baseline instead would mean adding a 2030-04-06 row to five parameter files with the scenario's values.Parameter sources
Every value was read verbatim from legislation.gov.uk and checked against HMRC's published tables.
HMRC checks: 480 Appendix 2 (2020-21 to 2026-27, NEDC and WLTP columns), HMRC's "CO2 emissions tables of rates" (2027-28), and the 2025-28 and 2028-30 policy papers.
Points for review
maximumfor s. 139(3)(b) and s. 141, 37% to 2027-28.registered_from_6_april_2020parameter node with anin_effectflag. HMRC 480 Appendix 1 Examples 6 and 7 test both schedules.ni_class_1areads the secondary percentage in other years, so reforms to it carry through, as s. 10(5) requires.employment_income: policyengine-uk-data's SPI records add EPB (expenses payments and benefits in kind) to pay, so filling these inputs there means taking the car benefits out of EPB. Both are documented on the inputs and on the docs page, for the Microcosm work in Company car tax +1 point (2030-31): needs a company car benefit in policyengine-uk and company cars in Microcosm uk-autumn-budget-dashboard-2026#316. The money inputs are uprated by CPI, standing in for a new-car price index.ni_employeris net of it (split from #1918) #2459) is not modelled; when it is, it must not reduce Class 1A.CI
This PR raises the
Test / YAMLjob's limit in.github/workflows/test-suite.yamlfrom 45 to 75 minutes, the Python shards' limit. Here it was cancelled at 45 minutes twice after every test had passed: pytest's summary came at 34.5 and 27.5 minutes, and the process stayed alive until the limit. The memory cause is #2741, and #2742 releases each YAML case's simulation; the higher limit holds until then.Test plan
policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/benefits_in_kind/company_car_benefit.yaml:car_fuel_benefit.yaml: HMRC 480 para 13.6 (£24,600 x 36% = £8,856), made good, zero emission, unavailability, 2027-28, a 1-50 g/km hybrid, the s. 139A rates.benefits_in_kind_interactions.yaml:ni_employer;national_insurance/class_1a/ni_class_1a.yaml: 13.8%, 14.53%, 15%; an under-21 earner's Class 1A;total_national_insurance.policyengine_uk/tests/test_company_car_benefit.py:COMPANY_CAR_TAX_RISE_2030asreform=matches the scenario in 2030, 2031 and 2035;gov_taxrises by the income tax plus Class 1A for a student loan borrower;baseline_employer_costandadjusted_employer_coststill equalemployer_costfor a company car holder;adjusted_net_income.yaml.test_fiscal_partition.py,test_employer_ni_fixed_cost_properties.py,test_employer_ni_zero_rate_consumers.py,test_housing_benefit_earnings_disregard_properties.py,test_income_support_earnings_disregard_properties.py,test_student_loan_existing_holder_projection.py,test_student_loan_plan_uprating.py,test_projection_horizon.py,test_parameter_descriptions.py,test_program_registry.py,code_health/test_uprating_declarations.py.make formatchangelog.d/2749.added.mdaxiom: needed: rulespec-uk has no module for ITEPA 2003 ss. 121-152 or SSCBA 1992 s. 10; a dispatch-ready pe-parity issue still has to be opened.
🤖 Generated with Claude Code