diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 3d3915b9b..239ab9115 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -1913,6 +1913,10 @@ class EpcPropertyDataMapper: insulated_door_count=schema.insulated_door_count, draughtproofed_door_count=None, percent_draughtproofed=schema.percent_draughtproofed, + # 20.0.0 lodges insulated_door_u_value but the mapper dropped it, + # unlike 21.0.1. heat_transmission reads it as the measured door + # U-value override; without it every door falls to the RdSAP default. + insulated_door_u_value=schema.insulated_door_u_value, # ADR-0027: 20.0.0 has no flue/fan/vent counts (calculator defaults # them via RdSAP Table 5), but sheltered_sides must come from # built_form — else the calculator assumes mid-terrace (2) for all. @@ -2187,6 +2191,10 @@ class EpcPropertyDataMapper: schema.mechanical_ventilation_index_number ), mechanical_vent_duct_type=schema.mechanical_vent_duct_type, + # 21.0.0 lodges insulated_door_u_value but the mapper dropped it, + # unlike 21.0.1. heat_transmission reads it as the measured door + # U-value override; without it every door falls to the RdSAP default. + insulated_door_u_value=schema.insulated_door_u_value, led_fixed_lighting_bulbs_count=schema.led_fixed_lighting_bulbs_count or 0, cfl_fixed_lighting_bulbs_count=schema.cfl_fixed_lighting_bulbs_count or 0, incandescent_fixed_lighting_bulbs_count=schema.incandescent_fixed_lighting_bulbs_count, diff --git a/tests/datatypes/epc/domain/test_mapper_insulated_door_u_value.py b/tests/datatypes/epc/domain/test_mapper_insulated_door_u_value.py new file mode 100644 index 000000000..80b33aa4c --- /dev/null +++ b/tests/datatypes/epc/domain/test_mapper_insulated_door_u_value.py @@ -0,0 +1,49 @@ +"""Regression: the 20.0.0 and 21.0.0 mappers must carry the lodged +insulated_door_u_value, like the 21.0.1 path. + +PRD #1385 phase 2. Both dropped `insulated_door_u_value` (top-level Optional +float). heat_transmission reads it as the measured door U-value override +(`cert_to_inputs` → `heat_transmission`), so dropping it forced every door onto +the RdSAP default U-value. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from datatypes.epc.domain.mapper import EpcPropertyDataMapper + +# 21.0.0 sample lodges insulated_door_u_value=3. +_FIXTURE_21_0_0 = Path("backend/epc_api/json_samples/RdSAP-Schema-21.0.0/epc.json") +# 20.0.0 certs don't lodge it in the corpus (latent) — inject to prove the path. +_FIXTURE_20_0_0 = Path("tests/fixtures/epc_prediction/PE71NT/cert-460c2d58495c.json") + + +def test_21_0_0_carries_insulated_door_u_value() -> None: + data: dict[str, Any] = json.loads(_FIXTURE_21_0_0.read_text()) + assert data["schema_type"] == "RdSAP-Schema-21.0.0" + assert data["insulated_door_u_value"] == 3 + + epc = EpcPropertyDataMapper.from_api_response(data) + assert epc.insulated_door_u_value == 3 + + +def test_20_0_0_carries_injected_insulated_door_u_value() -> None: + data: dict[str, Any] = json.loads(_FIXTURE_20_0_0.read_text()) + assert data["schema_type"] == "RdSAP-Schema-20.0.0" + data["insulated_door_u_value"] = 1.5 + + epc = EpcPropertyDataMapper.from_api_response(data) + # Was hardcoded away (None); now carried through. + assert epc.insulated_door_u_value == 1.5 + + +def test_20_0_0_absent_insulated_door_u_value_maps_to_none() -> None: + # The real cert lodges no value → None (no phantom U-value). + data: dict[str, Any] = json.loads(_FIXTURE_20_0_0.read_text()) + assert data.get("insulated_door_u_value") is None + + epc = EpcPropertyDataMapper.from_api_response(data) + assert epc.insulated_door_u_value is None