From 5cf5b674201a2dcb1ce32d160d788c20a067be1f Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 26 Jun 2026 10:37:36 +0000 Subject: [PATCH] =?UTF-8?q?Carry=20donor's=20display=20heating=20+=20contr?= =?UTF-8?q?ol=20into=20predicted=20EPC=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _apply_heating_donor copies the donor's calc sap_heating but leaves the display rows (main_heating, main_heating_controls) on the structural template — incoherent, and 'Heating Control: Unknown' when the template lodged no control (predicted property 721167, ADR-0029 follow-up). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../epc_prediction/test_epc_prediction.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/domain/epc_prediction/test_epc_prediction.py b/tests/domain/epc_prediction/test_epc_prediction.py index 6cbb3de4..4b0ff149 100644 --- a/tests/domain/epc_prediction/test_epc_prediction.py +++ b/tests/domain/epc_prediction/test_epc_prediction.py @@ -10,6 +10,7 @@ from typing import Optional, Union from datatypes.epc.domain.epc_property_data import ( BuildingPartIdentifier, + EnergyElement, EpcPropertyData, MainHeatingDetail, SapBuildingPart, @@ -51,6 +52,8 @@ def _epc( has_hot_water_cylinder: bool = True, solar_water_heating: bool = False, meter_type: str = "2", + main_heating_label: str = "Boiler and radiators, mains gas", + main_heating_controls_label: Optional[str] = None, ) -> EpcPropertyData: epc: EpcPropertyData = object.__new__(EpcPropertyData) epc.property_type = "2" @@ -87,6 +90,22 @@ def _epc( heating.cylinder_insulation_type = 1 heating.secondary_heating_type = None epc.sap_heating = heating + epc.main_heating = [ + EnergyElement( + description=main_heating_label, + energy_efficiency_rating=4, + environmental_efficiency_rating=4, + ) + ] + epc.main_heating_controls = ( + EnergyElement( + description=main_heating_controls_label, + energy_efficiency_rating=4, + environmental_efficiency_rating=4, + ) + if main_heating_controls_label is not None + else None + ) epc.has_hot_water_cylinder = has_hot_water_cylinder epc.solar_water_heating = solar_water_heating energy: SapEnergySource = object.__new__(SapEnergySource) @@ -637,3 +656,32 @@ def test_heating_donor_carries_the_donors_off_peak_meter() -> None: donor = _epc(meter_type="Dual", main_fuel_type=29) # the cohort's heating EpcPrediction._apply_heating_donor(predicted, _cohort(donor)) assert predicted.sap_energy_source.meter_type == "Dual" + + +def test_heating_donor_carries_the_donors_display_heating_and_control() -> None: + # The displayed heating panel (Main Heating + Heating Control rows) describes + # the same system as the calc cluster, so it must travel with the donor — not + # be left on the size-representative structural template. Two failures + # otherwise: (1) the displayed heating is incoherent with the donated calc + # system, and (2) "Heating Control: Unknown" whenever the template lodged no + # control row (the donor's is dropped). Predicted property 721167 (ADR-0029 + # follow-up): the template carried no main_heating_controls, so its passport + # showed Heating Control = Unknown despite a coherent gas-boiler donor. + predicted = _epc( + main_heating_label="Room heaters, electric", + main_heating_controls_label=None, # template lodged no control + ) + donor = _epc( + main_fuel_type=29, + main_heating_label="Boiler and radiators, mains gas", + main_heating_controls_label="Programmer, room thermostat and TRVs", + ) + + EpcPrediction._apply_heating_donor(predicted, _cohort(donor)) + + assert predicted.main_heating[0].description == "Boiler and radiators, mains gas" + assert predicted.main_heating_controls is not None + assert ( + predicted.main_heating_controls.description + == "Programmer, room thermostat and TRVs" + )