diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 2f2bd00b..1658383f 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -2150,7 +2150,10 @@ class EpcPropertyDataMapper: door_count=schema.door_count, habitable_rooms_count=schema.habitable_room_count, heated_rooms_count=schema.heated_room_count, - wet_rooms_count=schema.wet_rooms_count, + # 21.0.1 lodges wet_rooms_count as Optional; None violates the + # epc_property NOT-NULL column (and the calc's `> 0` check). Coalesce + # to 0 like every other mapper (RdSAP "not lodged" → calc minimum 1). + wet_rooms_count=schema.wet_rooms_count or 0, extensions_count=schema.extensions_count, open_chimneys_count=schema.open_chimneys_count, insulated_door_count=schema.insulated_door_count, diff --git a/datatypes/epc/domain/tests/test_from_rdsap_schema.py b/datatypes/epc/domain/tests/test_from_rdsap_schema.py index e0c6b1ba..023f7c60 100644 --- a/datatypes/epc/domain/tests/test_from_rdsap_schema.py +++ b/datatypes/epc/domain/tests/test_from_rdsap_schema.py @@ -379,6 +379,22 @@ class TestFromRdSapSchema21_0_1: # --- general --- + def test_omitted_wet_rooms_count_defaults_to_zero_not_none(self) -> None: + # 21.0.1 lodges wet_rooms_count as Optional, so a cert that omits it + # mapped to EpcPropertyData.wet_rooms_count=None. That None then + # violated the epc_property.wet_rooms_count NOT-NULL constraint when a + # predicted EPC (which deep-copies a comparable template) was persisted, + # and would crash the calc's `wet_rooms_count > 0` check. 37 modelling_e2e + # properties failed on the 2026-06-23 run for this reason. Default to 0, + # matching every other mapper (RdSAP 0 → calc's Table-?? minimum 1 fan). + data = load("21_0_1.json") + data.pop("wet_rooms_count", None) + schema = from_dict(RdSapSchema21_0_1, data) + + result = EpcPropertyDataMapper.from_rdsap_schema_21_0_1(schema) + + assert result.wet_rooms_count == 0 + def test_uprn(self, result: EpcPropertyData) -> None: assert result.uprn == 12457