From ea4b7938e7baeaaca1a3b7aaa400370abfc10e4d Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 16 Jul 2026 16:21:03 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20HHR-control=20ticks=20on=20manual-ent?= =?UTF-8?q?ry=20non-HHR=20storage=20heaters=20to=202402=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1626: ground truth resolved per the issue's option 3. The accredited cert 0764-2894-7173-2720-5535 (62 Mayfair Road, UPRN 77066640 — the blocked Wythenshawe fixture's dwelling) lodges 402 + 2402 for the same heater description, and RdSAP 10 p.80 (item 7-3) requires a PCDB index for any HHR lodgement — absent one, the surveyed type field is authoritative and 2404 downgrades to 2402 (automatic charge control, preserving the tick's automatic-charging semantics). Applies only to the mapped non-HHR types (402/404); any other pairing still fails loud. LRHA fixture 505091686590 (Fan storage heater, same slip) computes 64.0 vs pre 65; LRHA 76→77 computed. Co-Authored-By: Claude Fable 5 --- datatypes/epc/domain/mapper.py | 54 ++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index b3e6783c9..dfe7a75d8 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -6493,11 +6493,9 @@ def _map_sap_heating( if community_sap_code is not None else _pashub_main_heating_system_code(main) ) - main_heating_control = _pashub_main_heating_control_code( - main.controls, main.system_type - ) - _pashub_guard_storage_control_coherence( - main_heating_control, sap_main_heating_code + main_heating_control = _pashub_resolve_storage_control_coherence( + _pashub_main_heating_control_code(main.controls, main.system_type), + sap_main_heating_code, ) return SapHeating( @@ -8166,23 +8164,41 @@ def _pashub_main_heating_system_code(main: PasHubMainHeating) -> Optional[int]: return None -def _pashub_guard_storage_control_coherence( +# The Manual-Entry (non-HHR) Table 4a storage codes the 2404→2402 coherence +# downgrade below applies to. Deliberately narrow: any other pairing keeps the +# fail-loud raise. +_PASHUB_NON_HHR_STORAGE_CODES: frozenset[int] = frozenset( + _PASHUB_STORAGE_HEATER_TYPE_TO_SAP10.values() +) + + +def _pashub_resolve_storage_control_coherence( main_heating_control: Union[int, str], sap_main_heating_code: Optional[int] -) -> None: +) -> Union[int, str]: """SAP 10.2 Table 4e footnote: the high-heat-retention storage control code - 2404 "applies to high heat retention storage heaters (code 409) only". A - survey pairing 2404 with a non-HHR storage type (402/404) is a genuine - surveyor contradiction — fail loud rather than rate a mismatched pair. The - converse (409 with a non-2404 charge control, e.g. 2402) is spec-legal and - passes through.""" + 2404 "applies to high heat retention storage heaters (code 409) only", and + RdSAP 10 p.80 (survey item 7-3) makes an HHR lodgement carry the PCDB index + of each heater — so a Manual Entry survey pairing 2404 with a non-HHR + storage type (402/404) can never be lodged as ticked. The surveyed type + field is authoritative and the control resolves to 2402 (automatic charge + control) — the pairing the accredited cert 0764-2894-7173-2720-5535 + (62 Mayfair Road, UPRN 77066640 — the blocked Wythenshawe fixture's own + dwelling) lodges for exactly this heater description, and the code that + preserves the tick's automatic-charging semantics (a manual-charge 2401 + would contradict it). Any other incoherent pairing still fails loud rather + than rating a mismatched pair. The converse (409 with a non-2404 charge + control, e.g. 2402) is spec-legal and passes through.""" is_hhr_control = main_heating_control == 2404 - if is_hhr_control and sap_main_heating_code != _PASHUB_STORAGE_HEATER_HHR_CODE: - raise UnmappedPasHubLabel( - "main heating control", - "'Controls for high heat retention storage heaters' (2404) lodged " - f"against non-HHR storage system code {sap_main_heating_code} " - "(Table 4e: 2404 applies to code 409 only)", - ) + if not is_hhr_control or sap_main_heating_code == _PASHUB_STORAGE_HEATER_HHR_CODE: + return main_heating_control + if sap_main_heating_code in _PASHUB_NON_HHR_STORAGE_CODES: + return 2402 + raise UnmappedPasHubLabel( + "main heating control", + "'Controls for high heat retention storage heaters' (2404) lodged " + f"against non-HHR storage system code {sap_main_heating_code} " + "(Table 4e: 2404 applies to code 409 only)", + ) def _pashub_heat_emitter_code(emitter_label: str) -> Union[int, str]: