mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-19 17:03:02 +00:00
10 modelling_e2e properties failed with "unmapped SAP code in fuel_code: 10": the billing layer (`sap_code_to_fuel`) had no carrier for Table-32 code 10 (dual fuel, mineral + wood) and raised rather than guess one. SAP 10.2 treats dual fuel as its OWN fuel (its own Table-12 factors), so model it as its own billing carrier rather than collapsing onto wood or coal: - New `Fuel.DUAL_FUEL_MINERAL_AND_WOOD`. - `_CODE_TO_FUEL[10]` -> that carrier. - Fuel Rates snapshot prices it at 7.69 p/kWh — the midpoint of the COAL proxy (7.13) and WOOD_LOGS (8.25). This mirrors SAP's own construction: Table-32 dual fuel (3.99) ~= midpoint of house coal (3.67) and wood logs (4.23). Marked `derived` with a documented _note/_gap/_assumption (like the COAL and HEAT_NETWORK proxies), since there is no retail blend price. A dedicated carrier + rate (vs a one-line map to an existing carrier) keeps the fuel identity faithful to SAP and avoids mispricing dual fuel as pure wood/coal. Tests: code 10 -> DUAL_FUEL carrier; snapshot prices it at 7.69; grid-export codes (36/60) still raise (the genuine no-carrier case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.2 KiB
Python
60 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from domain.fuel_rates.fuel import Fuel
|
|
from domain.billing.sap_fuel import sap_code_to_fuel
|
|
from domain.sap10_calculator.exceptions import UnmappedSapCode
|
|
|
|
|
|
def test_mains_gas_code_maps_to_mains_gas() -> None:
|
|
# Arrange / Act / Assert — Table 32 code 1 is mains gas.
|
|
assert sap_code_to_fuel(1) == Fuel.MAINS_GAS
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("code", "fuel"),
|
|
[
|
|
(1, Fuel.MAINS_GAS),
|
|
(2, Fuel.LPG),
|
|
(4, Fuel.OIL),
|
|
(76, Fuel.OIL), # bioethanol — a liquid fuel row
|
|
(11, Fuel.COAL), # house coal
|
|
(15, Fuel.COAL), # anthracite
|
|
(12, Fuel.SMOKELESS),
|
|
(20, Fuel.WOOD_LOGS),
|
|
(23, Fuel.WOOD_PELLETS),
|
|
(10, Fuel.DUAL_FUEL_MINERAL_AND_WOOD), # dual fuel (mineral + wood)
|
|
(30, Fuel.ELECTRICITY), # standard tariff
|
|
(32, Fuel.ELECTRICITY_OFF_PEAK), # 7-hour tariff
|
|
(41, Fuel.HEAT_NETWORK), # heat from electric heat pump (community)
|
|
(50, Fuel.HEAT_NETWORK), # electricity for distribution pumping
|
|
],
|
|
)
|
|
def test_table_32_codes_map_to_their_billing_fuel(code: int, fuel: Fuel) -> None:
|
|
# Arrange / Act / Assert
|
|
assert sap_code_to_fuel(code) == fuel
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("api_code", "fuel"),
|
|
[
|
|
(26, Fuel.MAINS_GAS), # gov-API mains-gas enum -> Table 32 code 1
|
|
(0, Fuel.ELECTRICITY), # API "electricity" -> Table 32 code 30
|
|
(25, Fuel.HEAT_NETWORK), # API community heat -> Table 32 code 41
|
|
(14, Fuel.COAL), # API house coal -> Table 32 code 11
|
|
],
|
|
)
|
|
def test_raw_api_fuel_codes_normalize_before_mapping(api_code: int, fuel: Fuel) -> None:
|
|
# Arrange — the calculator may carry a raw gov-API fuel code (not yet a Table
|
|
# 32 code); sap_code_to_fuel normalizes via the calculator's own helper first.
|
|
# Act / Assert
|
|
assert sap_code_to_fuel(api_code) == fuel
|
|
|
|
|
|
def test_an_unmapped_code_raises_rather_than_guessing() -> None:
|
|
# Arrange — grid-export code 60 is not an end use's input fuel, so it has no
|
|
# billing carrier (code 10 dual fuel is now mapped — see the parametrized test).
|
|
# Act / Assert
|
|
with pytest.raises(UnmappedSapCode):
|
|
sap_code_to_fuel(60)
|