diff --git a/domain/epc/property_overrides/roof_type.py b/domain/epc/property_overrides/roof_type.py index 56ef9e8ed..bc3073f86 100644 --- a/domain/epc/property_overrides/roof_type.py +++ b/domain/epc/property_overrides/roof_type.py @@ -22,6 +22,28 @@ class RoofType(Enum): FLAT_NO_INSULATION = "Flat, no insulation" FLAT_NO_INSULATION_ASSUMED = "Flat, no insulation (assumed)" + # A flat roof with a known insulation depth. RdSAP 10 Table 16 col (1) + # ("joists at ceiling level and flat roof") scores flat roofs by thickness, so + # these carry the depth into the overlay (mirroring the PITCHED_LOFT_* ladder; + # no "loft" — a flat roof has none). ADR-0041. The value column is an FE-owned + # pgEnum, so these members are added to the Drizzle enum by the FE owner. + FLAT_12MM = "Flat, 12 mm insulation" + FLAT_25MM = "Flat, 25 mm insulation" + FLAT_50MM = "Flat, 50 mm insulation" + FLAT_75MM = "Flat, 75 mm insulation" + FLAT_100MM = "Flat, 100 mm insulation" + FLAT_125MM = "Flat, 125 mm insulation" + FLAT_150MM = "Flat, 150 mm insulation" + FLAT_175MM = "Flat, 175 mm insulation" + FLAT_200MM = "Flat, 200 mm insulation" + FLAT_225MM = "Flat, 225 mm insulation" + FLAT_250MM = "Flat, 250 mm insulation" + FLAT_270MM = "Flat, 270 mm insulation" + FLAT_300MM = "Flat, 300 mm insulation" + FLAT_350MM = "Flat, 350 mm insulation" + FLAT_400MM = "Flat, 400 mm insulation" + FLAT_400_PLUS = "Flat, 400+ mm insulation" + PITCHED_INSULATED = "Pitched, insulated" PITCHED_INSULATED_ASSUMED = "Pitched, insulated (assumed)" PITCHED_INSULATED_AT_RAFTERS = "Pitched, insulated at rafters" diff --git a/tests/domain/epc/test_roof_type_overlay.py b/tests/domain/epc/test_roof_type_overlay.py index 75aab9204..1c39ec9f7 100644 --- a/tests/domain/epc/test_roof_type_overlay.py +++ b/tests/domain/epc/test_roof_type_overlay.py @@ -11,6 +11,26 @@ import pytest from datatypes.epc.domain.epc_property_data import BuildingPartIdentifier from domain.epc.property_overlays.roof_type_overlay import roof_overlay_for +from domain.epc.property_overrides.roof_type import RoofType + +_FLAT_THICKNESS_MEMBERS: list[tuple[RoofType, int]] = [ + (RoofType.FLAT_12MM, 12), + (RoofType.FLAT_25MM, 25), + (RoofType.FLAT_50MM, 50), + (RoofType.FLAT_75MM, 75), + (RoofType.FLAT_100MM, 100), + (RoofType.FLAT_125MM, 125), + (RoofType.FLAT_150MM, 150), + (RoofType.FLAT_175MM, 175), + (RoofType.FLAT_200MM, 200), + (RoofType.FLAT_225MM, 225), + (RoofType.FLAT_250MM, 250), + (RoofType.FLAT_270MM, 270), + (RoofType.FLAT_300MM, 300), + (RoofType.FLAT_350MM, 350), + (RoofType.FLAT_400MM, 400), + (RoofType.FLAT_400_PLUS, 400), +] def test_pitched_loft_depth_maps_to_roof_insulation_thickness() -> None: @@ -56,6 +76,24 @@ def test_flat_roof_depth_maps_to_roof_insulation_thickness() -> None: assert overlay.roof_insulation_thickness == 150 +@pytest.mark.parametrize(("member", "expected_mm"), _FLAT_THICKNESS_MEMBERS) +def test_every_flat_thickness_member_resolves_to_its_depth( + member: RoofType, expected_mm: int +) -> None: + # Each FLAT_*MM taxonomy member must stay lock-step with the overlay: its value + # resolves to the depth it names (so a landlord-stored member scores via Table + # 16 col (1), not the age-band default). ADR-0041. + + # Act + simulation = roof_overlay_for(member.value, 0) + + # Assert + assert simulation is not None + overlay = simulation.building_parts[BuildingPartIdentifier.MAIN] + assert overlay.roof_construction_type == "Flat" + assert overlay.roof_insulation_thickness == expected_mm + + @pytest.mark.parametrize( "roof_type_value", [