Resolve HHR-control ticks on manual-entry non-HHR storage heaters to 2402 🟩

#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 <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-16 16:21:03 +00:00
parent 1587db61f2
commit ea4b7938e7

View file

@ -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]: