diff --git a/domain/epc/property_overlays/main_heating_system_overlay.py b/domain/epc/property_overlays/main_heating_system_overlay.py index 12d695b18..904a774bf 100644 --- a/domain/epc/property_overlays/main_heating_system_overlay.py +++ b/domain/epc/property_overlays/main_heating_system_overlay.py @@ -45,6 +45,10 @@ _OFF_PEAK_METER = "Dual" # meter bleed through and bill the new gas/direct-acting system on an Economy-7 # split (the mirror of the storage→Dual drag, ADR-0035). _SINGLE_RATE_METER = "Single" +# Meter-agnostic codes: the archetype cannot determine the tariff, so the overlay +# defers `meter_type` to the cert (`_meter_for` → None). Screed underfloor (424) is +# "standard or off peak" — 81% of the corpus lodge off-peak (ADR-0046). +_METER_AGNOSTIC_CODES = frozenset({424}) # Electric room heaters (SAP Table 4a 691). They don't *require* off-peak the way # storage/CPSU do, so they're absent from the calculator's §12 @@ -228,11 +232,19 @@ _MAIN_HEATING_CODES: dict[str, int] = { } -def _meter_for(code: int) -> str: +def _meter_for(code: int) -> Optional[str]: """The coherent meter a heating code implies: an off-peak ("Dual") meter for the §12 off-peak systems and all-electric room-heater dwellings (`_ASSUMED_DUAL_METER_CODES`), an explicit single-rate ("Single") meter for - every other system. Always set — never left to bleed.""" + every other system — set explicitly so it never bleeds. + + Exception: a **meter-agnostic** code (`_METER_AGNOSTIC_CODES` — screed + underfloor 424, "standard or off peak") returns ``None`` so the overlay leaves + `meter_type` unset and the cert's lodged tariff stands. The archetype genuinely + cannot know the tariff, and unlike a switched-off storage system it is not a + re-metering, so deferring is faithful, not a bleed (ADR-0046).""" + if code in _METER_AGNOSTIC_CODES: + return None return _OFF_PEAK_METER if code in _ASSUMED_DUAL_METER_CODES else _SINGLE_RATE_METER diff --git a/tests/domain/epc/test_main_heating_system_overlay.py b/tests/domain/epc/test_main_heating_system_overlay.py index c477bcee2..9f83c9b3f 100644 --- a/tests/domain/epc/test_main_heating_system_overlay.py +++ b/tests/domain/epc/test_main_heating_system_overlay.py @@ -13,6 +13,7 @@ from domain.epc.property_overlays.main_fuel_overlay import fuel_overlay_for from domain.epc.property_overlays.main_heating_system_overlay import ( _ASSUMED_DUAL_METER_CODES, _MAIN_HEATING_CODES, + _METER_AGNOSTIC_CODES, main_heating_overlay_for, ) from domain.epc.property_overlays.water_heating_overlay import ( @@ -367,11 +368,18 @@ def test_off_peak_archetypes_drag_dual_others_drag_single() -> None: # the calculator's single off-peak classification, so any archetype whose # code implies off-peak MUST synthesise a Dual meter and every other code # MUST synthesise a Single meter — a system switch can never silently leave - # the previous system's meter in place. + # the previous system's meter in place. The one exception is a meter-agnostic + # code (screed underfloor, tariff-ambiguous), which defers to the cert's + # lodged meter (None) rather than assert one (ADR-0046). for value, code in _MAIN_HEATING_CODES.items(): simulation = main_heating_overlay_for(value, 0) assert simulation is not None and simulation.heating is not None - expected = "Dual" if code in _ASSUMED_DUAL_METER_CODES else "Single" + if code in _METER_AGNOSTIC_CODES: + expected = None + elif code in _ASSUMED_DUAL_METER_CODES: + expected = "Dual" + else: + expected = "Single" assert simulation.heating.meter_type == expected, value