diff --git a/changelog.d/2166.fixed.md b/changelog.d/2166.fixed.md new file mode 100644 index 0000000000..f7872663f5 --- /dev/null +++ b/changelog.d/2166.fixed.md @@ -0,0 +1 @@ +Modelled the temporary zero rate of VAT on domestic electricity in Great Britain from 1 October 2026 to 31 March 2027 (SI 2026/987), with Northern Ireland and gas staying at 5%. VAT on domestic electricity and gas is now charged on the households' own bills (`domestic_energy_vat`), at a new nation-split `gov.hmrc.vat.domestic_electricity_rate` and the reduced rate, instead of on a flat share of total consumption grossed up to receipts. Energy-bill VAT is not grossed up by the survey coverage or the household share of VAT receipts. The ONS domestic fuel and power share of spending now comes out of the reduced-rate share, which leaves at most 0.01% of spending in the generic reduced-rate base in any year since 2006. The bills are taken net of the 5% VAT they were priced at (`gov.simulation.vat.energy_input_price_basis_rate`), so a reform to the reduced rate no longer changes the VAT-exclusive energy base. diff --git a/docs/engineering/vat-coverage.md b/docs/engineering/vat-coverage.md index 49c467898a..946d877607 100644 --- a/docs/engineering/vat-coverage.md +++ b/docs/engineering/vat-coverage.md @@ -56,8 +56,8 @@ the calculation below, whenever the dataset it should match changes. | Statutory rates | 20% standard, 5% reduced | `gov.hmrc.vat` | Model year 2024 is the 2024-25 fiscal year. All dataset totals are weighted -sums from `Microsimulation`, computed with policyengine-uk at this branch and -policyengine-core 3.33.2. +sums from `Microsimulation`, computed with policyengine-uk as of #2187 (before +energy bills were split out, see below) and policyengine-core 3.33.2. ### Formula @@ -146,11 +146,53 @@ For Enhanced FRS, download `enhanced_frs_2024_25.h5` from the model repository `policyengine/policyengine-uk-data-private` at the release tag (for example `revision="1.58.0"`). -## Interaction with an explicit energy VAT base +## Energy bills are not grossed up -A domestic fuel and power VAT base built from `electricity_consumption` and -`gas_consumption` (PolicyEngine/policyengine-uk#2166) changes what the survey -coverage applies to. If those energy inputs are already calibrated to an -aggregate, they should not be divided by the survey coverage again, and the -reconciled energy part of `reduced_rate_vat_consumption` should come out of the -base used to derive it. +VAT on domestic electricity and gas is charged on each household's own bills +(`domestic_energy_vat`, PolicyEngine/policyengine-uk#2166), and the ONS domestic +fuel and power share of spending comes out of the generic reduced-rate share. +Those bills are calibrated to national energy use (NEED), and households bear +VAT on them in full, so neither factor applies to them: + +``` +vat = (standard rate x full_rate_vat_consumption + + reduced rate x reduced_rate_vat_consumption) + / (survey_consumption_coverage x household_share_of_receipts) + + domestic_energy_vat +``` + +The electricity and gas inputs are priced at DESNZ Quarterly Energy Prices +average domestic prices paid, which include VAT at 5%, so `domestic_energy_vat` charges the statutory rates on the +input divided by 1.05. The divisor is the fixed price-basis parameter +`gov.simulation.vat.energy_input_price_basis_rate`, not the statutory reduced +rate, so a reform to a VAT rate leaves the VAT-exclusive base unchanged. + +Dividing energy VAT by the household share as well would put part of +government, charity and exempt-business VAT on electricity bills, and a zero +rate on domestic electricity would then cost 1/0.7 times the VAT households +actually pay on it. + +With energy split out, the coverage is derived against receipts net of energy +VAT: coverage = pre-scaling non-energy VAT / (0.7 x (receipts - energy VAT)). + +On the same inputs as the derivation above: + +| Dataset | Year | Pre-scaling non-energy VAT | Energy-bill VAT | OBR VAT receipts | Survey coverage | +|---|---|---|---|---|---| +| Microcosm UK 2024-25 (`f9d1922c`) | 2024-25 | £79.23bn | £1.99bn | £171.0bn (outturn) | 0.670 | +| Microcosm UK 2024-25 (`f9d1922c`) | 2025-26 | £82.51bn | £2.01bn | £180.2bn | 0.662 | +| Microcosm UK 2024-25 (`f9d1922c`) | 2026-27 | £84.73bn | £1.40bn | £187.7bn | 0.650 | +| Enhanced FRS 2024-25, uk-data 1.56.16 | 2024-25 | £109.39bn | £2.35bn | £171.0bn (outturn) | 0.927 | +| Enhanced FRS 2024-25, uk-data 1.58.0 | 2024-25 | £114.06bn | £2.44bn | £171.0bn (outturn) | 0.967 | + +The pre-scaling non-energy VAT is the standard-rated part alone, since the +generic reduced-rate base is empty once domestic fuel and power is taken out. +The Microcosm 2024-25 value rounds to the same 0.67 as the derivation above, +so the parameter is unchanged, and the bound above still holds (0.650 to 0.670 +by receipts, 0.688 by the ready reckoner). With it, total `vat` on Microcosm +is within 0.1% of 2024-25 receipts and 3.0% below in 2026-27; on Enhanced FRS +it is 38% (1.56.16) or 44% (1.58.0) above 2024-25 receipts. + +To reproduce, run the snippet above on this version and subtract +`sim.calculate("domestic_energy_vat", year).sum()` from the receipts before +dividing. diff --git a/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/great_britain.yaml b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/great_britain.yaml new file mode 100644 index 0000000000..3a066700c4 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/great_britain.yaml @@ -0,0 +1,26 @@ +description: VAT rate on supplies of domestic electricity in England, Wales and Scotland. It is the reduced rate except while the temporary zero rate applies. +values: + 1994-04-01: 0.08 + 1997-09-01: 0.05 + 2026-10-01: + value: 0 + metadata: + reference: + - title: The Value Added Tax (Supplies of Domestic Electricity) Order 2026 (SI 2026/987), article 3 + href: https://www.legislation.gov.uk/uksi/2026/987/contents/made + - title: HMRC Revenue and Customs Brief 10 (2026) + href: https://www.gov.uk/government/publications/revenue-and-customs-brief-10-2026-temporary-zero-rate-of-vat-for-domestic-electricity-in-great-britain/temporary-zero-rate-of-vat-for-domestic-electricity-in-great-britain + 2027-04-01: 0.05 +metadata: + unit: /1 + period: year + label: VAT rate on domestic electricity, Great Britain + reference: + - title: Value Added Tax Act 1994, Schedule 7A, Group 1 + href: https://www.legislation.gov.uk/ukpga/1994/23/schedule/7A + - title: The Value Added Tax (Supplies of Domestic Electricity) Order 2026 (SI 2026/987) + href: https://www.legislation.gov.uk/uksi/2026/987/contents/made + # Liability follows the date the electricity is consumed (Revenue and + # Customs Brief 10 (2026)). Day-weighting the fiscal year assumes equal + # daily use: FY 2026-27 has 183 days at 5% and 182 at 0%. + fiscal_year_blend: true diff --git a/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/index.yaml b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/index.yaml new file mode 100644 index 0000000000..ab54bb1738 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/index.yaml @@ -0,0 +1,3 @@ +description: VAT rates on supplies of domestic electricity, by nation. +metadata: + label: VAT rate on domestic electricity diff --git a/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/northern_ireland.yaml b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/northern_ireland.yaml new file mode 100644 index 0000000000..cbb38988ad --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/vat/domestic_electricity_rate/northern_ireland.yaml @@ -0,0 +1,14 @@ +description: VAT rate on supplies of domestic electricity in Northern Ireland. The temporary zero rate from 1 October 2026 does not apply there. +values: + 1994-04-01: 0.08 + 1997-09-01: 0.05 +metadata: + unit: /1 + period: year + label: VAT rate on domestic electricity, Northern Ireland + reference: + - title: Value Added Tax Act 1994, Schedule 7A, Group 1 + href: https://www.legislation.gov.uk/ukpga/1994/23/schedule/7A + - title: HMRC Revenue and Customs Brief 10 (2026) + href: https://www.gov.uk/government/publications/revenue-and-customs-brief-10-2026-temporary-zero-rate-of-vat-for-domestic-electricity-in-great-britain/temporary-zero-rate-of-vat-for-domestic-electricity-in-great-britain + fiscal_year_blend: true diff --git a/policyengine_uk/parameters/gov/hmrc/vat/domestic_fuel_and_power_share.yaml b/policyengine_uk/parameters/gov/hmrc/vat/domestic_fuel_and_power_share.yaml new file mode 100644 index 0000000000..dc3b70a4cb --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/vat/domestic_fuel_and_power_share.yaml @@ -0,0 +1,44 @@ +description: Share of household final consumption expenditure that goes on domestic fuel and power (electricity, gas and other fuels). It is subtracted from the reduced-rate share of consumption, because domestic electricity and gas are now taxed from their own bills. +values: + 2000-01-01: 0.0213 + 2001-01-01: 0.0203 + 2002-01-01: 0.0197 + 2003-01-01: 0.0189 + 2004-01-01: 0.0215 + 2005-01-01: 0.0219 + 2006-01-01: 0.0263 + 2007-01-01: 0.0259 + 2008-01-01: 0.0299 + 2009-01-01: 0.0305 + 2010-01-01: 0.0309 + 2011-01-01: 0.0291 + 2012-01-01: 0.0319 + 2013-01-01: 0.0323 + 2014-01-01: 0.0275 + 2015-01-01: 0.0274 + 2016-01-01: 0.0264 + 2017-01-01: 0.0249 + 2018-01-01: 0.0258 + 2019-01-01: 0.0264 + 2020-01-01: 0.0286 + 2021-01-01: 0.0290 + 2022-01-01: 0.0351 + 2023-01-01: 0.0363 + 2024-01-01: 0.0283 + 2025-01-01: 0.0279 +metadata: + unit: /1 + period: year + label: Domestic fuel and power share of household spending + reference: + - title: ONS Consumer trends, 04.5 Electricity, gas and other fuels, current prices, not seasonally adjusted (ADFX), release of 30 September 2026 + href: https://www.ons.gov.uk/economy/nationalaccounts/satelliteaccounts/timeseries/adfx/ct + - title: ONS, Household final consumption expenditure, domestic concept, current prices (ABQI) + href: https://www.ons.gov.uk/economy/nationalaccounts/satelliteaccounts/timeseries/abqi/ukea + - title: OBR, VAT ("Around 2.5 per cent of expenditure is taxed at this reduced rate") + href: https://obr.uk/forecasts-in-depth/tax-by-tax-spend-by-spend/vat/ + # ADFX / ABQI by calendar year. From 2006 the share is 2.49% or more, + # against the OBR's 2.5% reduced-rate share, so domestic fuel and power + # accounts for all of it to within rounding and the non-energy remainder + # (children's car seats, smoking-cessation products and similar) is too + # small to separate from published totals. diff --git a/policyengine_uk/parameters/gov/hmrc/vat/reduced_rate.yaml b/policyengine_uk/parameters/gov/hmrc/vat/reduced_rate.yaml index d807a25ca5..6db505553b 100644 --- a/policyengine_uk/parameters/gov/hmrc/vat/reduced_rate.yaml +++ b/policyengine_uk/parameters/gov/hmrc/vat/reduced_rate.yaml @@ -1,4 +1,4 @@ -description: A reduced rate of VAT applicable to select goods and services (domestic fuel and power). +description: A reduced rate of VAT applicable to select goods and services, including domestic gas and other fuels. Domestic electricity has its own rate (gov.hmrc.vat.domestic_electricity_rate), so a change to this rate does not reach electricity. values: 1994-04-01: value: 0.08 diff --git a/policyengine_uk/parameters/gov/simulation/vat/energy_input_price_basis_rate.yaml b/policyengine_uk/parameters/gov/simulation/vat/energy_input_price_basis_rate.yaml new file mode 100644 index 0000000000..a8dd1ce790 --- /dev/null +++ b/policyengine_uk/parameters/gov/simulation/vat/energy_input_price_basis_rate.yaml @@ -0,0 +1,21 @@ +description: VAT rate included in the prices used to value the electricity_consumption and gas_consumption inputs. Energy-bill VAT is charged on those inputs net of this rate, so the VAT-exclusive base stays fixed when a reform changes a statutory VAT rate. This describes the input data, not policy; change it only if the inputs are repriced on a different VAT basis. +metadata: + unit: /1 + period: year + economy: false + label: VAT rate included in energy input prices + derivation: >- + The default electricity and gas inputs are NEED kWh consumption valued at + DESNZ Quarterly Energy Prices average domestic prices paid (standard + electricity table 2.2.4 and gas table 2.3.4, all payment methods, 2024-25), + which include VAT at 5%. The VAT-exclusive base is therefore the input divided + by 1.05. Reading the statutory reduced rate here instead would make a + reform to that rate reprice the inputs (PolicyEngine/policyengine-uk#2189, + review finding C1). + reference: + - title: DESNZ, Annual domestic energy price statistics (Quarterly Energy Prices tables 2.2.4 and 2.3.4, prices include VAT) + href: https://www.gov.uk/government/statistical-data-sets/annual-domestic-energy-price-statistics + - title: VAT Act 1994, Schedule 7A, Group 1 (reduced rate on domestic fuel and power) + href: https://www.legislation.gov.uk/ukpga/1994/23/schedule/7A +values: + 2000-01-01: 0.05 diff --git a/policyengine_uk/parameters/gov/simulation/vat/survey_consumption_coverage.yaml b/policyengine_uk/parameters/gov/simulation/vat/survey_consumption_coverage.yaml index e5a16038db..0cad4f8287 100644 --- a/policyengine_uk/parameters/gov/simulation/vat/survey_consumption_coverage.yaml +++ b/policyengine_uk/parameters/gov/simulation/vat/survey_consumption_coverage.yaml @@ -7,19 +7,22 @@ metadata: Calibrated to the certified Microcosm UK 2024-25 national release only (policyengine/populace-uk-private, tag microcosm-uk-2024-25-national-20261002T230158Z-5c6b3f68, revision - f9d1922c). Pre-scaling VAT (standard rate times full_rate_vat_consumption - plus reduced rate times reduced_rate_vat_consumption) on that release is - 0.20 x £396.15bn + 0.05 x £25.02bn = £80.48bn in 2024. OBR VAT receipts - for 2024-25 (outturn, March 2026 EFO table 3.8, cash basis) are £170.994bn. - 80.48 / 170.994 = 0.471 for the product of the two factors; divided by the - household share of 0.7 gives 0.67. Across 2024-25 to 2026-27 the product is - 0.459 to 0.471, and the HMRC ready reckoner (£8.8bn for one point on the - standard rate in 2026-27) implies 0.481, so the coverage lies between 0.655 - and 0.688. On Enhanced FRS 2024-25 the same calculation gives a coverage of - 0.927 (policyengine-uk-data 1.56.16) or 0.967 (1.58.0), so this value puts - VAT on that dataset 38% or 44% above 2024-25 receipts. Re-derive when the - dataset it should match changes, with docs/engineering/vat-coverage.md, - which lists the inputs, file hashes and per-dataset results. + f9d1922c). VAT on household energy bills (domestic_energy_vat) is not + grossed up, so the coverage applies only to VAT on other recorded + consumption. On that release, pre-scaling VAT on that consumption is 0.20 x + £396.15bn = £79.23bn in 2024 (the generic reduced-rate base is empty once + domestic fuel and power is taken out), and VAT on energy bills is £1.99bn. + OBR VAT receipts for 2024-25 (outturn, March 2026 EFO table 3.8, cash + basis) are £170.994bn. Coverage = 79.23 / (0.7 x (170.994 - 1.99)) = 0.670. + Before energy bills were split out, the same calculation gave 0.672, so + the rounded value is unchanged. Across 2024-25 to 2026-27 the coverage is + 0.650 to 0.670, and the HMRC ready reckoner (£8.8bn for one point on the + standard rate in 2026-27) implies 0.688. On Enhanced FRS 2024-25 the same + calculation gives 0.927 (policyengine-uk-data 1.56.16) or 0.967 (1.58.0), + so this value puts total VAT on that dataset 38% or 44% above 2024-25 + receipts. Re-derive when the dataset it should match changes, with + docs/engineering/vat-coverage.md, which lists the inputs, file hashes and + per-dataset results. reference: - title: OBR, Economic and fiscal outlook March 2026, detailed forecast tables, receipts, table 3.8 href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/ diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/vat/domestic_energy_vat.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/vat/domestic_energy_vat.yaml new file mode 100644 index 0000000000..ed9b9da39f --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/vat/domestic_energy_vat.yaml @@ -0,0 +1,55 @@ +# Temporary GB zero rate on domestic electricity, 1 October 2026 to +# 31 March 2027 (SI 2026/987). £1,050 of bills carries £50 of VAT at 5%. +# FY 2026-27 has 183 days at 5% and 182 at 0%: 50 * 183 / 365 = 25.07. +- name: GB electricity in FY 2026-27 is zero-rated for 182 of 365 days + period: 2026 + absolute_error_margin: 0.01 + input: + region: WALES + electricity_consumption: 1_050 + gas_consumption: 0 + output: + domestic_energy_vat: 25.07 + +- name: Northern Ireland electricity stays at 5% + period: 2026 + absolute_error_margin: 0.01 + input: + region: NORTHERN_IRELAND + electricity_consumption: 1_050 + gas_consumption: 0 + output: + domestic_energy_vat: 50 + +- name: Gas is not affected by the electricity zero rate + period: 2026 + absolute_error_margin: 0.01 + input: + region: SCOTLAND + electricity_consumption: 0 + gas_consumption: 1_050 + output: + domestic_energy_vat: 50 + +- name: GB electricity is back at 5% in FY 2027-28 + period: 2027 + absolute_error_margin: 0.01 + input: + region: LONDON + electricity_consumption: 1_050 + gas_consumption: 0 + output: + domestic_energy_vat: 50 + +- name: Energy VAT adds to VAT without the coverage or household-share gross-up + period: 2025 + absolute_error_margin: 0.01 + input: + region: LONDON + consumption: 0 + electricity_consumption: 1_050 + gas_consumption: 1_050 + output: + reduced_rate_vat_consumption: 0 + vat: 100 + vat_change: 0 diff --git a/policyengine_uk/tests/test_domestic_electricity_vat.py b/policyengine_uk/tests/test_domestic_electricity_vat.py new file mode 100644 index 0000000000..8d91bf08e4 --- /dev/null +++ b/policyengine_uk/tests/test_domestic_electricity_vat.py @@ -0,0 +1,235 @@ +""" +Domestic electricity VAT: the temporary GB zero rate (SI 2026/987) and the +explicit energy VAT base (#2166). + +From 1 October 2026 to 31 March 2027 domestic electricity in England, Wales +and Scotland is zero-rated; Northern Ireland and gas stay at 5%. Model year +2026 runs 6 April 2026 to 5 April 2027, so with equal daily use it has 183 +days at 5% and 182 at 0%. +""" + +from pathlib import Path + +import pytest +from policyengine_core.parameters import ParameterNode +from policyengine_core.reforms import Reform + +import policyengine_uk +from policyengine_uk import Simulation +from policyengine_uk.model_api import Scenario +from policyengine_uk.system import system + +FY2026_SHARE_AT_5_PERCENT = 183 / 365 +GB_RATE = "gov.hmrc.vat.domestic_electricity_rate.great_britain" + + +def raw_parameters() -> ParameterNode: + """Parameters as written, before uprating and fiscal-year conversion.""" + directory = Path(policyengine_uk.__file__).parent / "parameters" + return ParameterNode(directory_path=str(directory)) + + +def household( + region="LONDON", + electricity=1_050, + gas=0, + consumption=20_000, + year=2026, +): + return { + "people": {"adult": {"age": {year: 45}}}, + "benunits": {"benunit": {"members": ["adult"]}}, + "households": { + "household": { + "members": ["adult"], + "region": {year: region}, + "consumption": {year: consumption}, + "electricity_consumption": {year: electricity}, + "gas_consumption": {year: gas}, + } + }, + } + + +def calc(variable, year=2026, scenario=None, **kwargs): + sim = Simulation(situation=household(year=year, **kwargs), scenario=scenario) + return float(sim.calculate(variable, year)[0]) + + +@pytest.mark.parametrize( + "date, rate", + [ + ("2026-09-30", 0.05), + ("2026-10-01", 0.0), + ("2027-03-31", 0.0), + ("2027-04-01", 0.05), + ], +) +def test_gb_rate_changes_on_both_statutory_dates(date, rate): + rates = raw_parameters().gov.hmrc.vat.domestic_electricity_rate + assert rates.great_britain(date) == pytest.approx(rate) + assert rates.northern_ireland(date) == pytest.approx(0.05) + + +def test_fy2026_gb_rate_is_day_weighted(): + rates = system.parameters.gov.hmrc.vat.domestic_electricity_rate + assert rates.great_britain("2025") == pytest.approx(0.05) + assert rates.great_britain("2026") == pytest.approx( + 0.05 * FY2026_SHARE_AT_5_PERCENT + ) + assert rates.great_britain("2027") == pytest.approx(0.05) + assert rates.northern_ireland("2026") == pytest.approx(0.05) + + +@pytest.mark.parametrize("region", ["LONDON", "WALES", "SCOTLAND"]) +def test_gb_electricity_vat_in_fy2026(region): + # £1,050 of bills includes £50 of VAT at 5%. + assert calc("domestic_energy_vat", region=region) == pytest.approx( + 50 * FY2026_SHARE_AT_5_PERCENT + ) + + +def test_northern_ireland_keeps_5_percent(): + assert calc("domestic_energy_vat", region="NORTHERN_IRELAND") == pytest.approx(50) + + +def test_gas_is_unchanged(): + assert calc("domestic_energy_vat", electricity=0, gas=1_050) == pytest.approx(50) + + +@pytest.mark.parametrize("year", [2025, 2027]) +def test_years_either_side_are_at_5_percent(year): + assert calc("domestic_energy_vat", year=year) == pytest.approx(50) + + +def test_vat_responds_to_electricity(): + low = calc("vat", electricity=1_200) + high = calc("vat", electricity=5_000) + expected = 3_800 / 1.05 * 0.05 * FY2026_SHARE_AT_5_PERCENT + # vat is float32, so the difference of two totals is good to about 1p. + assert high - low == pytest.approx(expected, abs=0.01) + + +def test_energy_vat_is_not_grossed_up_by_coverage(): + with_energy = calc("vat", electricity=1_050, gas=1_050, year=2025) + without_energy = calc("vat", electricity=0, gas=0, year=2025) + assert with_energy - without_energy == pytest.approx(100) + + +def test_domestic_fuel_and_power_leaves_the_generic_reduced_rate_base(): + # The ONS domestic fuel and power share of spending (2.79% in 2025) exceeds + # the OBR's 2.5% reduced-rate share, so nothing is left in the generic base. + assert calc("reduced_rate_vat_consumption", year=2025) == 0 + + +def test_non_energy_reduced_rate_base_is_kept(): + # A reduced-rate share above the domestic fuel and power share keeps the + # remainder in the generic base, at the reduced rate. + scenario = Scenario( + parameter_changes={"gov.hmrc.vat.reduced_rate_share": {"2025": 0.0379}} + ) + base = calc("reduced_rate_vat_consumption", year=2025, scenario=scenario) + assert base == pytest.approx(20_000 * (0.0379 - 0.0279)) + + +def test_no_reform_means_no_vat_change(): + assert calc("vat_change") == pytest.approx(0) + assert calc("baseline_domestic_energy_vat") == pytest.approx( + calc("domestic_energy_vat") + ) + + +def test_extending_the_zero_rate_to_fy2027(): + # Dated reforms are applied on the statutory timeline and day-weighted + # into the model year (#2167), so the zero rate must cover all of model + # year 2027: 6 April 2027 to 5 April 2028. A calendar-2027 window would + # leave 96 of 366 days at 5%. + reform = Reform.from_dict( + {GB_RATE: {"2027-04-06.2028-04-05": 0.0}}, country_id="uk" + ) + sim = Simulation(situation=household(year=2027), reform=reform) + assert sim.calculate("domestic_energy_vat", 2027)[0] == 0 + assert sim.calculate("baseline_domestic_energy_vat", 2027)[0] == pytest.approx(50) + assert sim.calculate("vat_change", 2027)[0] == pytest.approx(-50) + + +def test_reduced_rate_reform_reaches_gas_not_electricity(): + scenario = Scenario(parameter_changes={"gov.hmrc.vat.reduced_rate": {"2025": 0.0}}) + assert calc( + "domestic_energy_vat", year=2025, scenario=scenario, gas=1_050 + ) == pytest.approx(50) + + +# Price basis of the energy inputs (#2189 review, finding C1). The inputs are +# priced at DESNZ QEP average prices paid including 5% VAT, so the VAT-exclusive base +# is the bill / 1.05 whatever rate a reform sets. Previously the divisor was +# the baseline-tree reduced rate, which a Scenario reform also changed (#2188), +# so a 20% gas rate treated 5%-priced bills as if they included 20%. +HER_CASE = dict(region="LONDON", electricity=1_050, gas=1_050, year=2025) +GAS_RATE = "gov.hmrc.vat.reduced_rate" + + +def test_price_basis_rate_is_5_percent(): + p = system.parameters.gov.simulation.vat + for year in ("2024", "2025", "2026", "2027"): + assert p.energy_input_price_basis_rate(year) == pytest.approx(0.05) + + +def test_gas_rate_reform_scenario_and_reform_agree_at_250(): + # Model year 2025, London, £1,050 of electricity and £1,050 of gas, both + # priced including 5% VAT. VAT-exclusive base: 1,050 / 1.05 = £1,000 each. + # electricity: 1,000 x 5% = £50 + # gas: 1,000 x 20% = £200 + # total = £250 + # Before the fix the Scenario route divided by 1.20 instead: + # 1,050 / 1.20 x 5% + 1,050 / 1.20 x 20% = 43.75 + 175 = £218.75. + scenario = Scenario(parameter_changes={GAS_RATE: 0.20}) + reform = Reform.from_dict( + {GAS_RATE: {"2025-01-01.2100-12-31": 0.20}}, country_id="uk" + ) + via_scenario = Simulation(situation=household(**HER_CASE), scenario=scenario) + via_reform = Simulation(situation=household(**HER_CASE), reform=reform) + scenario_vat = float(via_scenario.calculate("domestic_energy_vat", 2025)[0]) + reform_vat = float(via_reform.calculate("domestic_energy_vat", 2025)[0]) + assert scenario_vat == pytest.approx(250, abs=0.01) + assert reform_vat == pytest.approx(250, abs=0.01) + assert scenario_vat == pytest.approx(reform_vat, abs=0.01) + # Total vat differs only through energy, so it matches across routes too. + assert float(via_scenario.calculate("vat", 2025)[0]) == pytest.approx( + float(via_reform.calculate("vat", 2025)[0]), abs=0.01 + ) + # Both routes leave the baseline tree alone (#2188): 1,000 x 5% x 2 = £100. + for sim in (via_scenario, via_reform): + assert float( + sim.calculate("baseline_domestic_energy_vat", 2025)[0] + ) == pytest.approx(100, abs=0.01) + + +def test_scalar_zero_gas_rate_leaves_electricity_vat_alone(): + # £1,050 of electricity only: 1,050 / 1.05 x 5% = £50. A scalar Scenario + # setting the reduced rate to 0 used to give 1,050 / 1.00 x 5% = £52.50. + scenario = Scenario(parameter_changes={GAS_RATE: 0.0}) + assert calc( + "domestic_energy_vat", year=2025, gas=0, scenario=scenario + ) == pytest.approx(50, abs=0.01) + + +@pytest.mark.parametrize( + "region, electricity_rate", + [ + # GB, model year 2026: 183 of 365 days at 5%, so 0.05 x 183 / 365. + ("LONDON", 0.05 * FY2026_SHARE_AT_5_PERCENT), + ("SCOTLAND", 0.05 * FY2026_SHARE_AT_5_PERCENT), + # Northern Ireland is outside the zero rate: 5% all year. + ("NORTHERN_IRELAND", 0.05), + ], +) +def test_baseline_fy2026_window_by_nation_with_gas(region, electricity_rate): + # £1,050 of electricity and £2,100 of gas, each VAT-exclusive at / 1.05: + # electricity: 1,000 x rate (GB 25.07, NI 50) + # gas: 2,000 x 5% = £100 in every nation + # so GB = 125.07 and NI = 150. + vat = calc("domestic_energy_vat", region=region, electricity=1_050, gas=2_100) + assert vat == pytest.approx(1_000 * electricity_rate + 100, abs=0.01) + gas_only = calc("domestic_energy_vat", region=region, electricity=0, gas=2_100) + assert gas_only == pytest.approx(100, abs=0.01) diff --git a/policyengine_uk/variables/gov/hmrc/baseline_vat.py b/policyengine_uk/variables/gov/hmrc/baseline_vat.py index 4c6f4589b4..164f372021 100644 --- a/policyengine_uk/variables/gov/hmrc/baseline_vat.py +++ b/policyengine_uk/variables/gov/hmrc/baseline_vat.py @@ -18,6 +18,10 @@ def formula(household, period, parameters): + reduced_rate_consumption * p.hmrc.vat.reduced_rate ) # Survey consumption under-records spending, and households bear only - # part of VAT liabilities, so household VAT is grossed up to receipts. + # part of VAT liabilities, so household VAT on recorded consumption is + # grossed up to receipts. VAT on the household's own energy bills is + # not grossed up: the bills are calibrated to national energy use and + # households bear that VAT in full. coverage = vat_grossing_factor(p.simulation) - return raw_vat / coverage + energy_vat = household("baseline_domestic_energy_vat", period) + return raw_vat / coverage + energy_vat diff --git a/policyengine_uk/variables/gov/hmrc/domestic_energy_vat.py b/policyengine_uk/variables/gov/hmrc/domestic_energy_vat.py new file mode 100644 index 0000000000..663b1ca3e6 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/domestic_energy_vat.py @@ -0,0 +1,79 @@ +from types import SimpleNamespace + +from policyengine_uk.model_api import * +from policyengine_uk.utils.parameters import fiscal_year_average + + +def _domestic_energy_vat(household, period, parameters, electricity_rate, gas_rate): + # electricity_consumption and gas_consumption are priced at DESNZ Quarterly + # Energy Prices average prices paid, which include VAT at 5%, so the VAT-exclusive base is the bill + # divided by 1.05 (5/105 of the bill is VAT). The divisor is the rate the + # inputs were priced at (gov.simulation.vat.energy_input_price_basis_rate), + # never the statutory rate being modelled, so the base stays fixed when a + # reform changes a VAT rate. The inputs are calibrated to NEED consumption, + # not grossed up to receipts, so neither the survey consumption coverage + # nor the household share of VAT receipts applies. + priced_rate = parameters(period).gov.simulation.vat.energy_input_price_basis_rate + country = household("country", period) + northern_ireland = country == country.possible_values.NORTHERN_IRELAND + rate = where( + northern_ireland, + electricity_rate.northern_ireland, + electricity_rate.great_britain, + ) + electricity = household("electricity_consumption", period) / (1 + priced_rate) + gas = household("gas_consumption", period) / (1 + priced_rate) + return electricity * rate + gas * gas_rate + + +class domestic_energy_vat(Variable): + label = "VAT on domestic electricity and gas" + documentation = ( + "VAT on the household's electricity and gas bills. Electricity is " + "charged at its own rate, which is zero in Great Britain from 1 October " + "2026 to 31 March 2027; gas is charged at the reduced rate." + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = [ + "https://www.legislation.gov.uk/ukpga/1994/23/schedule/7A", + "https://www.legislation.gov.uk/uksi/2026/987/contents/made", + ] + + def formula(household, period, parameters): + p = parameters(period) + vat = p.gov.hmrc.vat + return _domestic_energy_vat( + household, + period, + parameters, + vat.domestic_electricity_rate, + vat.reduced_rate, + ) + + +class baseline_domestic_energy_vat(Variable): + label = "baseline VAT on domestic electricity and gas" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + # The baseline parameter tree keeps its dated values (it is not + # converted to fiscal years), so the electricity rates are + # day-weighted here as the reform tree's are, or a simulation with no + # reform would show a VAT change in 2026-27. + rates = household.simulation.tax_benefit_system.parameters.baseline.gov + rates = rates.hmrc.vat.domestic_electricity_rate + year = period.start.year + electricity_rate = SimpleNamespace( + great_britain=fiscal_year_average(rates.great_britain, year), + northern_ireland=fiscal_year_average(rates.northern_ireland, year), + ) + reduced_rate = parameters(period).baseline.gov.hmrc.vat.reduced_rate + return _domestic_energy_vat( + household, period, parameters, electricity_rate, reduced_rate + ) diff --git a/policyengine_uk/variables/gov/hmrc/vat.py b/policyengine_uk/variables/gov/hmrc/vat.py index 70012df9c5..8a913749dd 100644 --- a/policyengine_uk/variables/gov/hmrc/vat.py +++ b/policyengine_uk/variables/gov/hmrc/vat.py @@ -18,6 +18,10 @@ def formula(household, period, parameters): + reduced_rate_consumption * p.hmrc.vat.reduced_rate ) # Survey consumption under-records spending, and households bear only - # part of VAT liabilities, so household VAT is grossed up to receipts. + # part of VAT liabilities, so household VAT on recorded consumption is + # grossed up to receipts. VAT on the household's own energy bills is + # not grossed up: the bills are calibrated to national energy use and + # households bear that VAT in full. coverage = vat_grossing_factor(p.simulation) - return raw_vat / coverage + energy_vat = household("domestic_energy_vat", period) + return raw_vat / coverage + energy_vat diff --git a/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py b/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py index a4e7b6b953..9719becd71 100644 --- a/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py +++ b/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py @@ -2,13 +2,16 @@ class reduced_rate_vat_consumption(Variable): - label = "consumption of VAT reduced-rated goods and services" + label = "consumption of VAT reduced-rated goods and services other than domestic fuel and power" entity = Household definition_period = YEAR value_type = float unit = "currency-GBP" def formula(household, period, parameters): + # Domestic electricity and gas are taxed from their own bills in + # domestic_energy_vat, so the domestic fuel and power share of spending + # comes out of the reduced-rate share here. vat = parameters(period).gov.hmrc.vat - consumption = household("consumption", period) - return consumption * vat.reduced_rate_share + share = max_(vat.reduced_rate_share - vat.domestic_fuel_and_power_share, 0) + return household("consumption", period) * share