M8-R17: allocate thermal standing costs by service
This commit is contained in:
@@ -3002,6 +3002,11 @@ def test_m8_thermal_today_summary_ends_at_frozen_now(energy_db) -> None:
|
||||
"heating": Decimal("0"), "hot_water_heating": Decimal("1.25"),
|
||||
"hot_water": Decimal("2.75"), "hot_water_tax": Decimal("9.99"),
|
||||
},
|
||||
"fixed_breakdown": {
|
||||
"heating_network": Decimal("0"), "metering": Decimal("0"),
|
||||
"delivery_set": Decimal("0"), "hot_water_network": Decimal("0"),
|
||||
"other": Decimal("0"),
|
||||
},
|
||||
}
|
||||
|
||||
def summarize_spy(_session: Session, start: datetime, end: datetime, *, now: datetime) -> dict:
|
||||
@@ -3023,5 +3028,53 @@ def test_m8_thermal_today_summary_ends_at_frozen_now(energy_db) -> None:
|
||||
assert entity.value_getter(session) == Decimal("0")
|
||||
hot_water_total = next(item.entity for item in build_catalog(session)
|
||||
if item.entity.key.endswith(".hot_water_total_today"))
|
||||
assert hot_water_total.value_getter(session) == Decimal("4.00")
|
||||
assert hot_water_total.value_getter(session) == Decimal("13.99")
|
||||
assert captured["end"] == now
|
||||
|
||||
|
||||
@pytest.mark.parametrize("suffix", ("total", "today"))
|
||||
def test_m8_thermal_totals_allocate_standing_costs_and_match_all_in(energy_db, suffix: str) -> None:
|
||||
"""Heating and hot water each receive their assigned costs, without overlap."""
|
||||
from app.integrations.expose import build_catalog
|
||||
from app.services import timezone as tz
|
||||
|
||||
now = datetime(2026, 1, 15, 10, tzinfo=timezone.utc)
|
||||
summary = {
|
||||
"period_count": 2,
|
||||
"fixed_cost": Decimal("35"),
|
||||
"total_cost": Decimal("54"),
|
||||
"breakdown": {
|
||||
"heating": Decimal("10"), "hot_water_heating": Decimal("2"),
|
||||
"hot_water": Decimal("3"), "hot_water_tax": Decimal("4"),
|
||||
},
|
||||
"fixed_breakdown": {
|
||||
"heating_network": Decimal("5"), "metering": Decimal("6"),
|
||||
"delivery_set": Decimal("7"), "hot_water_network": Decimal("9"),
|
||||
"other": Decimal("8"),
|
||||
},
|
||||
}
|
||||
|
||||
with Session(energy_db) as session:
|
||||
_make_thermal_source_and_meter(session, "heating", now)
|
||||
_make_thermal_source_and_meter(session, "hot_water", now)
|
||||
session.commit()
|
||||
with (
|
||||
patch("app.integrations.expose._utc_now", return_value=now),
|
||||
patch.object(tz, "local_tz", return_value=ZoneInfo("Europe/Amsterdam")),
|
||||
patch.object(tz, "local_now", return_value=now.astimezone(ZoneInfo("Europe/Amsterdam"))),
|
||||
patch("app.services.meter_cost.summarize", return_value=summary),
|
||||
):
|
||||
entities = {item.entity.key.rsplit(".", 1)[-1]: item.entity
|
||||
for item in build_catalog(session) if item.entity.key.startswith("thermal_cost.")}
|
||||
heating = entities[f"heating_{suffix}"].value_getter(session)
|
||||
hot_water = entities[f"hot_water_total_{suffix}"].value_getter(session)
|
||||
|
||||
assert heating == Decimal("36") # 10 + heating_network + metering + delivery_set + other
|
||||
assert hot_water == Decimal("18") # 2 + 3 + 4 + hot_water_network
|
||||
assert entities[f"fixed_{suffix}"].value_getter(session) == Decimal("35")
|
||||
assert entities[f"all_in_{suffix}"].value_getter(session) == Decimal("54")
|
||||
assert heating + hot_water == entities[f"all_in_{suffix}"].value_getter(session)
|
||||
# Component entities deliberately remain variable-only.
|
||||
assert entities[f"hot_water_heating_{suffix}"].value_getter(session) == Decimal("2")
|
||||
assert entities[f"water_{suffix}"].value_getter(session) == Decimal("3")
|
||||
assert entities[f"water_tax_{suffix}"].value_getter(session) == Decimal("4")
|
||||
|
||||
Reference in New Issue
Block a user