diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 7e3add21..06771a7e 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3319,14 +3319,28 @@ def _synthesise_reduced_field_windows( ] +# ADR-0028: multiple_glazing_type "ND" (Not Defined) → the DG-modal +# default (cascade code 2 → daylight g_L 0.80), as for 18.0/19.0/17.x. +_RDSAP20_ND_GLAZING_TYPE: int = 2 + + def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]: """ADR-0027/0028 seam: 20.0.0 glazed_type codes 1-8+ND are identical to 21.0.1's, so route multiple_glazing_type through the verified cascade (fixes - code 1 "DG pre-2002" read as single), then call the shared core.""" + code 1 "DG pre-2002" read as single), then call the shared core. The "ND" + string (no cascade mapping) falls back to the DG-modal default, mirroring + `_synthesise_18_0_sap_windows` — without this the cascade raised + UnmappedApiCode on every ND-glazed 20.0.0 cert.""" + mgt = schema.multiple_glazing_type + glazing_type = ( + _api_cascade_glazing_type(mgt) + if isinstance(mgt, int) + else _RDSAP20_ND_GLAZING_TYPE + ) return _synthesise_reduced_field_windows( schema.glazed_area, schema.total_floor_area, - _api_cascade_glazing_type(schema.multiple_glazing_type), + glazing_type, ) diff --git a/datatypes/epc/schema/rdsap_schema_20_0_0.py b/datatypes/epc/schema/rdsap_schema_20_0_0.py index dbd5feef..9a4b78f2 100644 --- a/datatypes/epc/schema/rdsap_schema_20_0_0.py +++ b/datatypes/epc/schema/rdsap_schema_20_0_0.py @@ -267,7 +267,9 @@ class RdSapSchema20_0_0: energy_rating_current: int lighting_cost_current: float main_heating_controls: List[EnergyElement] - multiple_glazing_type: int + # "ND" (Not Defined) appears in the corpus alongside the 1-8 integer + # codes — type as Union to match the data (mirrors RdSAP-Schema-18.0). + multiple_glazing_type: Union[int, str] open_fireplaces_count: int heating_cost_potential: float hot_water_cost_current: float