diff --git a/domain/epc/property_overlays/main_heating_system_overlay.py b/domain/epc/property_overlays/main_heating_system_overlay.py index 9782174e3..d0c498320 100644 --- a/domain/epc/property_overlays/main_heating_system_overlay.py +++ b/domain/epc/property_overlays/main_heating_system_overlay.py @@ -28,6 +28,7 @@ are left UNKNOWN until modelled. Unresolvable values produce no overlay. from __future__ import annotations +import logging from typing import Optional from domain.modelling.simulation import EpcSimulation, HeatingOverlay @@ -35,6 +36,8 @@ from domain.sap10_calculator.tables.table_12a import ( OFF_PEAK_IMPLYING_HEATING_CODES, ) +logger = logging.getLogger(__name__) + # Off-peak (Economy 7) meter. Electric storage / CPSU systems charge overnight at # the low rate and cannot run economically on a single-rate meter; "Dual" lets # the §12 dispatch resolve the specific tariff (storage 7-hour, CPSU 10-hour). @@ -351,13 +354,32 @@ def main_heating_overlay_for( return None if code in _GAS_BOILER_CODES: return EpcSimulation(heating=_gas_boiler_overlay(code)) + category = _category_for(code) + control = _control_for(code) + if category is None or control is None: + # A system-replacing override should stamp a complete, coherent companion + # set; an unmapped category or control leaves the field unset, so the + # effective cert inherits the REPLACED system's value (a silent mis-rating + # — e.g. a room heater keeping a storage heater's category/control). We do + # not yet have confident defaults for every archetype, so log-and-continue + # (matching `flag_fuel_mismatch`) to make the gap visible for review + # rather than raise mid-run. + logger.error( + "Landlord main_heating_system %r (SAP code %d) overlaid with an " + "incomplete companion set (category=%s, control=%s); the effective " + "cert may inherit the replaced system's value — known gap, review", + main_heating_value, + code, + category, + control, + ) return EpcSimulation( heating=HeatingOverlay( sap_main_heating_code=code, - main_heating_category=_category_for(code), + main_heating_category=category, main_fuel_type=_natural_fuel_for(code), meter_type=_meter_for(code), - main_heating_control=_control_for(code), + main_heating_control=control, # A landlord override describes the existing dwelling, so its assumed # off-peak meter must not downgrade a more-off-peak cert meter # (e.g. a 24-hour all-low tariff). Measures, which re-meter for real,