diff --git a/datatypes/epc/domain/tests/test_from_site_notes.py b/datatypes/epc/domain/tests/test_from_site_notes.py index 7a91425ea..135179038 100644 --- a/datatypes/epc/domain/tests/test_from_site_notes.py +++ b/datatypes/epc/domain/tests/test_from_site_notes.py @@ -2485,7 +2485,10 @@ class TestPasHubMainHeatingControlCoding: # mapper against drifting out of sync with the calculator's coverage. from datatypes.epc.domain.mapper import ( _PASHUB_HEAT_NETWORK_CONTROL_TO_SAP10, # pyright: ignore[reportPrivateUsage] + _PASHUB_HEAT_PUMP_CONTROL_TO_SAP10, # pyright: ignore[reportPrivateUsage] _PASHUB_MAIN_HEATING_CONTROL_TO_SAP10, # pyright: ignore[reportPrivateUsage] + _PASHUB_ROOM_HEATER_CONTROL_TO_SAP10, # pyright: ignore[reportPrivateUsage] + _PASHUB_STORAGE_HEATER_CONTROL_TO_SAP10, # pyright: ignore[reportPrivateUsage] ) from domain.sap10_calculator.rdsap.cert_to_inputs import ( _CONTROL_TYPE_BY_CODE, # pyright: ignore[reportPrivateUsage] @@ -2493,7 +2496,10 @@ class TestPasHubMainHeatingControlCoding: mapped_codes = { *_PASHUB_MAIN_HEATING_CONTROL_TO_SAP10.values(), + *_PASHUB_HEAT_PUMP_CONTROL_TO_SAP10.values(), *_PASHUB_HEAT_NETWORK_CONTROL_TO_SAP10.values(), + *_PASHUB_STORAGE_HEATER_CONTROL_TO_SAP10.values(), + *_PASHUB_ROOM_HEATER_CONTROL_TO_SAP10.values(), } unknown = {c for c in mapped_codes if c not in _CONTROL_TYPE_BY_CODE} @@ -2576,6 +2582,52 @@ class TestPasHubMainHeatingControlCoding: with pytest.raises(UnmappedPasHubLabel, match="Heat pump"): EpcPropertyDataMapper.from_site_notes(survey) + def test_heat_pump_control_maps_to_group2_code(self) -> None: + # Arrange — a Table 4e Group 2 heat pump (`system_type = "Heat pump + # with radiators or underfloor heating"`). Its control label carries + # the Group 2 (22xx) code, not the boiler 2106: "Programmer, room + # thermostat and TRVs" → 2210, control type 2 (PCDB + # `heat_pump_controls`). + from domain.sap10_calculator.rdsap.cert_to_inputs import ( + _control_type, # pyright: ignore[reportPrivateUsage] + ) + + raw = load("pashub_rdsap_site_notes_example1.json") + mh = raw["heating_and_hot_water"]["main_heating"] + mh["system_type"] = "Heat pump with radiators or underfloor heating" + mh["fuel"] = "Electricity" + mh["controls"] = "Programmer, room thermostat and TRVs" + survey = from_dict(PasHubRdSapSiteNotes, raw) + + # Act + main = EpcPropertyDataMapper.from_site_notes( + survey + ).sap_heating.main_heating_details[0] + + # Assert — the Group 2 code, its spec control type, and Table 4a + # category 4: the calculator's Appendix N3.6/N3.7 heat-pump + # efficiency path gates on `main_heating_category == 4`, so without + # it the lodged PCDB heat pump is billed as 100%-efficient direct + # electric (−47 SAP on the two LRHA WAVE 3 heat-pump fixtures). + assert main.main_heating_control == 2210 + assert _control_type(main) == 2 + assert main.main_heating_category == 4 + + def test_heat_pump_unmapped_control_label_strict_raises(self) -> None: + # A heat-pump control label the Group 2 lookup does not cover + # strict-raises (naming label + system), never silently mis-rating. + raw = load("pashub_rdsap_site_notes_example1.json") + mh = raw["heating_and_hot_water"]["main_heating"] + mh["system_type"] = "Heat pump with radiators or underfloor heating" + mh["fuel"] = "Electricity" + mh["controls"] = "Some novel control" + survey = from_dict(PasHubRdSapSiteNotes, raw) + + with pytest.raises( + UnmappedPasHubLabel, match="Heat pump with radiators" + ): + EpcPropertyDataMapper.from_site_notes(survey) + def test_heat_network_control_maps_to_group3_code(self) -> None: # Arrange — a Table 4e Group 3 heat network (`system_type = # "Community heating system"`). Its control label carries a Group 3