mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
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:
parent
81d5429b60
commit
1da86a6366
1 changed files with 77 additions and 0 deletions
77
tests/domain/billing/test_off_peak_bill_integration.py
Normal file
77
tests/domain/billing/test_off_peak_bill_integration.py
Normal 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
|
||||||
Loading…
Add table
Reference in a new issue