Map the default plan's selected measures onto the export row 🟥

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-13 23:29:26 +00:00
parent 14763638e2
commit 5dab7beae5
2 changed files with 96 additions and 1 deletions

View file

@ -22,3 +22,6 @@ class MaterialRow(SQLModel, table=True):
cost_unit: Optional[str] = Field(default=None)
description: Optional[str] = Field(default=None)
is_active: bool = Field(default=True)
# Whether a solar_pv Option's product bundle includes a battery; the Scenario
# Export reads it to pivot solar-with-battery to its own column (ADR-0065).
includes_battery: bool = Field(default=False)

View file

@ -8,7 +8,11 @@ from sqlalchemy import Engine
from sqlmodel import Session
from datatypes.epc.domain.epc import Epc
from infrastructure.postgres.modelling import PlanModel # registers `plan`
from infrastructure.postgres.modelling import ( # registers `plan` + `recommendation`
PlanModel,
RecommendationModel,
)
from infrastructure.postgres.product_table import MaterialRow # registers `material`
from infrastructure.postgres.property_table import PropertyRow # registers `property`
from repositories.scenario_export.scenario_export_postgres_repository import (
ScenarioExportPostgresRepository,
@ -91,3 +95,91 @@ def test_excludes_requested_properties_without_a_default_plan_under_the_scenario
# assert — only #1 (a default Plan under scenario 5) survives (model A).
assert [r.property_id for r in rows] == [1]
def test_maps_the_default_plans_selected_measures_with_battery_and_exclusions(
db_engine: Engine,
) -> None:
# arrange — a default Plan with two selected measures (one solar_pv whose
# material carries a battery), plus a non-default and an already-installed
# recommendation that must be excluded.
with Session(db_engine) as session:
session.add(PropertyRow(id=1, portfolio_id=100, landlord_property_id="LP1"))
session.add(
PlanModel(
id=10, portfolio_id=100, property_id=1, scenario_id=5, is_default=True
)
)
session.add(MaterialRow(id=1, type="loft_insulation", includes_battery=False))
session.add(MaterialRow(id=2, type="solar_pv", includes_battery=True))
session.add(
RecommendationModel(
plan_id=10,
property_id=1,
type="loft_insulation",
measure_type="loft_insulation",
description="Loft insulation",
estimated_cost=1200.0,
sap_points=3.0,
co2_equivalent_savings=0.4,
kwh_savings=900.0,
energy_cost_savings=120.0,
material_id=1,
default=True,
already_installed=False,
)
)
session.add(
RecommendationModel(
plan_id=10,
property_id=1,
type="solar_pv",
measure_type="solar_pv",
description="Solar PV with battery",
estimated_cost=6000.0,
sap_points=5.0,
material_id=2,
default=True,
already_installed=False,
)
)
session.add(
RecommendationModel(
plan_id=10,
property_id=1,
type="double_glazing",
measure_type="double_glazing",
description="Double glazing (not selected)",
estimated_cost=4000.0,
default=False,
already_installed=False,
)
)
session.add(
RecommendationModel(
plan_id=10,
property_id=1,
type="cavity_wall_insulation",
measure_type="cavity_wall_insulation",
description="CWI (already installed)",
estimated_cost=800.0,
default=True,
already_installed=True,
)
)
session.commit()
# act
with Session(db_engine) as session:
rows = ScenarioExportPostgresRepository(session).rows_for(
portfolio_id=100, scenario_id=5, property_ids=[1]
)
# assert — only the two selected measures map through, the battery flag is
# resolved from the material, and savings are carried per measure.
by_type = {m.measure_type: m for m in rows[0].measures}
assert set(by_type) == {"loft_insulation", "solar_pv"}
assert by_type["loft_insulation"].estimated_cost == 1200.0
assert by_type["loft_insulation"].kwh_savings == 900.0
assert by_type["loft_insulation"].includes_battery is False
assert by_type["solar_pv"].includes_battery is True