diff --git a/datatypes/epc/domain/tests/test_from_site_notes.py b/datatypes/epc/domain/tests/test_from_site_notes.py index de38e214a..78eb89da0 100644 --- a/datatypes/epc/domain/tests/test_from_site_notes.py +++ b/datatypes/epc/domain/tests/test_from_site_notes.py @@ -240,6 +240,80 @@ class TestRoomInRoofMapping: assert by_kind["flat_ceiling"] == 100 assert by_kind["gable_wall"] is None + # A Simplified-approach lodgement ("Roof room type: RR type 2", details + # NOT known): gables + common walls only, no slopes/flat ceilings. + # Verbatim from fixture 499538003156 (78 North Road). + _RIR_SIMPLIFIED = { + "age_range": "C: 1930 - 1949", + "floor_area_m2": 25.43, + "roof_room_type": "RR type 2 (With the Accessible areas of continuous common walls)", + "gables": [ + {"length_m": 5.82, "height_m": 2.72, "gable_type": "Party"}, + {"length_m": 5.82, "height_m": 2.72, "gable_type": "Party"}, + ], + "common_walls": [ + {"length_m": 4.37, "height_m": 2.34}, + {"length_m": 4.37, "height_m": 2.34}, + ], + } + + def test_simplified_common_walls_bill_as_common_wall(self) -> None: + """On the Simplified approach (a "Roof room type" is lodged), a + "common wall" is a §3.9.2 Type-2 concept billed as an external wall + at the main-wall U — SAP kind `common_wall`, spec area L × (0.25 + H) + (RdSAP 10 §3.9.2 / heat_transmission's Simplified-BP area rule) — NOT + a Detailed §3.10 `stud_wall` roof surface, which would drop to the + Table 18 col(4) "all elements uninsulated" default (2.30 W/m²K) over + ~20 m² on the no-loft-access North Road pair (deals + 499538003156/499627236560, over-rate ~5 SAP before this fix).""" + # Arrange + data = load("pashub_rdsap_site_notes_example1.json") + data["roof_space"]["main_building"]["rooms_in_roof"] = True + data["roof_space"]["main_building"]["room_in_roof"] = self._RIR_SIMPLIFIED + survey = from_dict(PasHubRdSapSiteNotes, data) + + # Act + rir = EpcPropertyDataMapper.from_site_notes(survey).sap_building_parts[ + 0 + ].sap_room_in_roof + + # Assert + assert rir is not None + surfaces = rir.detailed_surfaces + assert surfaces is not None + cw_area = round(4.37 * (0.25 + 2.34), 2) # L × (0.25 + H) + kinds_areas = [(s.kind, round(s.area_m2, 2)) for s in surfaces] + assert kinds_areas == [ + ("gable_wall", round(5.82 * 2.72, 2)), + ("gable_wall", round(5.82 * 2.72, 2)), + ("common_wall", cw_area), + ("common_wall", cw_area), + ] + + def test_detailed_common_walls_stay_stud_wall(self) -> None: + """Regression: a Detailed §3.10 lodgement (no "Roof room type", per- + surface slopes lodged) keeps common walls as `stud_wall` — pinned + alongside `test_room_in_roof_reaches_building_part`.""" + # Arrange — the Detailed fixture has no roof_room_type. + data = load("pashub_rdsap_site_notes_example1.json") + data["roof_space"]["main_building"]["rooms_in_roof"] = True + data["roof_space"]["main_building"]["room_in_roof"] = self._RIR + survey = from_dict(PasHubRdSapSiteNotes, data) + + # Act + rir = EpcPropertyDataMapper.from_site_notes(survey).sap_building_parts[ + 0 + ].sap_room_in_roof + + # Assert + assert rir is not None + assert rir.detailed_surfaces is not None + assert {s.kind for s in rir.detailed_surfaces if "wall" in s.kind} == { + "gable_wall_external", + "gable_wall", + "stud_wall", + } + def test_no_rooms_in_roof_stays_absent(self) -> None: # Arrange — the fixture as lodged (rooms_in_roof=false). survey = from_dict(