diff --git a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py index a4fb4229..118ea9e9 100644 --- a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py +++ b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py @@ -514,6 +514,30 @@ def test_summary_0380_main_heating_category_is_heat_pump() -> None: assert main.main_heating_category == 4 +def test_summary_0380_filled_cavity_plus_external_insulation_routes_to_code_6() -> None: + # Arrange — cert 0380's Summary lodges main walls as + # `wall_type = "CA Cavity"` and `insulation = "FE Filled Cavity + + # External"` (a cavity wall with subsequent external-insulation + # upgrade). The cascade enum `wall_insulation_type=6` is + # "filled cavity + external insulation" (per + # `domain.sap10_ml.rdsap_uvalues` lines 120-131); without it the + # cascade defaults to the as-built routing and overstates walls + # heat loss by +58 W/K on cert 0380 (Summary 69.69 vs API 11.62 + # at HEAD before this slice). API path EPC for cert 0380 surfaces + # `wall_insulation_type=6` and is the ground-truth pin here. + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000899_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + + # Act + epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) + + # Assert + assert epc.sap_building_parts, "no building parts surfaced" + main = epc.sap_building_parts[0] + assert main.wall_construction == 4 # 4 = Cavity ('CA') + assert main.wall_insulation_type == 6 # 6 = filled cavity + external + + def test_summary_0380_full_chain_sap_matches_worksheet_pdf_exactly() -> None: # Arrange — cert 0380-2471-3250-2596-8761 (Summary_000899.pdf / # dr87-0001-000899.pdf) is the first heat-pump cert under per-cert diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 05af513f..12c60a00 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -2040,12 +2040,21 @@ _ELMHURST_WALL_CODE_TO_SAP10: Dict[str, int] = { # Elmhurst wall-insulation-type codes mapped to the SAP10 integer enum # documented at domain.sap10_ml.rdsap_uvalues.WALL_INSULATION_FILLED_CAVITY. +# Two-letter dual codes ("FE", "FI") encode a cavity-wall that received a +# subsequent external- or internal-insulation upgrade — these map to the +# SAP10 composite codes 6 and 7, which the cascade routes through the +# series-resistance composite-U calc rather than the filled-cavity table +# default. Cert 0380's `walls.insulation = "FE Filled Cavity + External"` +# is the cohort fixture pinning the FE branch. _ELMHURST_INSULATION_CODE_TO_SAP10: Dict[str, int] = { "E": 1, # External wall insulation "F": 2, # Filled cavity "I": 3, # Internal wall insulation "A": 4, # As built / assumed (default cascade) "N": 5, # None specified + "FE": 6, # Filled cavity + external insulation (cohort: cert 0380) + "FI": 7, # Filled cavity + internal insulation (no fixture yet — + # mirror of FE for the internal-upgrade variant) }