diff --git a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py index b2fa1e63..7a473041 100644 --- a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py +++ b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py @@ -368,36 +368,51 @@ def _fuel_cost_gbp_per_kwh( # RdSAP energy_tariff enum (per datatypes/epc/domain/epc_codes.csv): # 1 = dual (off-peak / Economy-7) # 2 = Single (standard tariff) -# 3 = Unknown (treated as Single — verified against -# Elmhurst assessor software with one -# real property; SAP assessor behavior is -# to default Unknown tariffs to Single) +# 3 = Unknown (Elmhurst-on-gas-property test says Single; +# corpus signal for electric dwellings says +# off-peak — see _is_off_peak_meter) # 4 = dual (24 hour) (off-peak / 24h heating) # 5 = off-peak 18 hour (off-peak) # # Different from the SAP-Schema enum which is 1=standard, 2=off-peak. # Our corpus is RdSAP so we use RdSAP codes. -_RDSAP_OFF_PEAK_METER_CODES: Final[frozenset[int]] = frozenset({1, 4, 5}) +_RDSAP_DEFINITELY_OFF_PEAK: Final[frozenset[int]] = frozenset({1, 4, 5}) +_RDSAP_UNKNOWN_METER: Final[frozenset[int]] = frozenset({3}) -def _is_off_peak_meter(meter_type: object) -> bool: - """True when the cert's `sap_energy_source.meter_type` lodges an - off-peak / dual-rate tariff per the RdSAP enum (codes 1, 4, 5). - Unknown (3) defaults to standard since that's the cert software's - behaviour. Trusts whatever meter_type the cert lodges regardless of - whether the heating system makes that tariff "natural" — per user - guidance the cert's lodged meter_type is the source of truth.""" +def _is_off_peak_meter(meter_type: object, *, fuel_is_electric: bool) -> bool: + """Whether the dwelling bills the given end-use (fuel_is_electric) at + the off-peak rate. RdSAP codes 1/4/5 are explicit off-peak. Code 3 + (Unknown) defers to the fuel: electric end-uses on Unknown meters + typically come from E7-eligible dwellings whose tariff the assessor + couldn't pin down, so we apply off-peak. Non-electric end-uses on + Unknown meters are unaffected. Per user guidance + Elmhurst test on + a single gas-heated property, code 2 (Single) is always standard.""" if meter_type is None: return False + code: Optional[int] if isinstance(meter_type, int): - return meter_type in _RDSAP_OFF_PEAK_METER_CODES - if isinstance(meter_type, str): - stripped = meter_type.strip().lower() - if stripped in {"dual", "off-peak", "1", "4", "5"}: - return True - if stripped in {"dual (24 hour)", "off-peak 18 hour"}: - return True + code = meter_type + elif isinstance(meter_type, str): + s = meter_type.strip().lower() + if s in {"single", "standard", "2"}: + return False + if s in {"dual", "1"}: + code = 1 + elif s in {"unknown", "3", ""}: + code = 3 + elif s in {"dual (24 hour)", "4"}: + code = 4 + elif s in {"off-peak 18 hour", "5"}: + code = 5 + else: + return False + else: return False + if code in _RDSAP_DEFINITELY_OFF_PEAK: + return True + if code in _RDSAP_UNKNOWN_METER and fuel_is_electric: + return True return False @@ -435,7 +450,7 @@ def _space_heating_fuel_cost_gbp_per_kwh( on an off-peak tariff (meter_type != standard) AND the main fuel is electricity, bill at the off-peak rate instead. Trusts the cert's meter_type rather than inferring tariff from heating code.""" - if _is_electric_main(main) and _is_off_peak_meter(meter_type): + if _is_electric_main(main) and _is_off_peak_meter(meter_type, fuel_is_electric=True): return prices.e7_low_rate_p_per_kwh * _PENCE_TO_GBP return _fuel_cost_gbp_per_kwh(main, prices) @@ -450,8 +465,8 @@ def _hot_water_fuel_cost_gbp_per_kwh( dwelling is on an off-peak tariff AND the water-heating fuel is electricity (immersion etc.), bill HW at the off-peak rate too — the cert assessor treats the immersion as running on the timer.""" - is_off_peak = _is_off_peak_meter(meter_type) - if is_off_peak and _is_electric_water(water_heating_fuel): + water_electric = _is_electric_water(water_heating_fuel) + if water_electric and _is_off_peak_meter(meter_type, fuel_is_electric=True): return prices.e7_low_rate_p_per_kwh * _PENCE_TO_GBP if water_heating_fuel is not None: return prices.unit_price_p_per_kwh(water_heating_fuel) * _PENCE_TO_GBP