Pin end-to-end off-peak day/night bill regression 🟩

An Economy-7 storage dwelling now prices heating at the 0.20-day/0.80-
night blend through cert -> calculator -> bill, instead of raising
UnpricedFuel and aborting the modelling_e2e batch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-24 18:03:16 +00:00
parent 81d5429b60
commit 1da86a6366

View file

@ -0,0 +1,77 @@
"""End-to-end regression for the Off-Peak Meter day/night bill split (ADR-0014
2026-06-24 amendment).
Drives a real off-peak (Economy-7) storage-heating cert through the whole chain
`cert_to_inputs` `calculate_sap_from_inputs` `EnergyBreakdown.from_sap_result`
`BillDerivation.derive` against the committed Fuel Rates snapshot. Before the
amendment this raised `UnpricedFuel` ("no rate for fuel ELECTRICITY_OFF_PEAK"),
which aborted the `modelling_e2e` batch (e.g. property 717572). It must now price
the dwelling day/night instead of crashing.
"""
from __future__ import annotations
from dataclasses import replace
from domain.sap10_ml.tests._fixtures import (
make_building_part,
make_minimal_sap10_epc,
make_sap_heating,
)
from datatypes.epc.domain.epc_property_data import EpcPropertyData, MainHeatingDetail
from domain.sap10_calculator.calculator import calculate_sap_from_inputs
from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs
from domain.fuel_rates.fuel import Fuel
from domain.billing.bill import BillSection, EnergyBreakdown
from domain.billing.bill_derivation import BillDerivation
from repositories.fuel_rates.fuel_rates_static_file_repository import (
FuelRatesStaticFileRepository,
)
_TYPICAL_TFA_M2 = 60.0
def _off_peak_storage_epc() -> EpcPropertyData:
# Integrated storage heaters (SAP code 408, Table 12a Grid 1 high-rate
# fraction 0.20) on a Dual / Economy-7 (7-hour) meter.
main = MainHeatingDetail(
has_fghrs=False,
main_fuel_type=29, # electricity
heat_emitter_type=1,
emitter_temperature=1,
main_heating_control=2401,
main_heating_category=7, # electric storage heaters
sap_main_heating_code=408,
)
base = make_minimal_sap10_epc(
total_floor_area_m2=_TYPICAL_TFA_M2,
habitable_rooms_count=4,
country_code="ENG",
sap_building_parts=[make_building_part(construction_age_band="E")],
sap_heating=make_sap_heating(main_heating_details=[main]),
)
return replace(
base,
sap_energy_source=replace(base.sap_energy_source, meter_type="1"),
)
def test_off_peak_storage_dwelling_bills_heating_day_night_not_crashes() -> None:
# Arrange — score the off-peak cert and price it at the committed snapshot.
result = calculate_sap_from_inputs(cert_to_inputs(_off_peak_storage_epc()))
rates = FuelRatesStaticFileRepository().get_current()
# Act — the previously-crashing path: off-peak electricity reaches the bill.
breakdown = EnergyBreakdown.from_sap_result(result)
bill = BillDerivation(rates).derive(breakdown)
# Assert — heating bills on the off-peak carrier at the 0.20-day/0.80-night
# blend (0.20×29.73 + 0.80×13.89 = 17.058 p/kWh), not a crash and not the
# flat standard rate (24.67 p) or pure night rate (13.89 p).
heating = bill.sections[BillSection.HEATING]
assert heating.kwh > 0.0
implied_rate_p = heating.cost_gbp / heating.kwh * 100.0
assert abs(implied_rate_p - 17.058) <= 1e-6
# The off-peak meter contributes one standing charge (56.99 p/day).
assert any(line.fuel is Fuel.ELECTRICITY_OFF_PEAK for line in breakdown.lines)
assert bill.total_gbp > 0.0