"""Characterisation of the FE-facing portfolio aggregation (`aggregate_portfolio_recommendations`): it sums a Scenario's **default** Recommendations and writes the totals onto the Scenario row. This pins the `recommendation.plan_id` linkage the m2m retirement introduced (ADR-0017 amendment): the aggregation joins Recommendation → Plan on `recommendation.plan_id`, so only measures carrying the right `plan_id` (and `default = True`) are summed. """ from __future__ import annotations from sqlmodel import Session from backend.app.db.functions.portfolio_functions import ( aggregate_portfolio_recommendations, ) from backend.app.db.models.recommendations import ( PlanModel, Recommendation, ScenarioModel, ) from domain.modelling.portfolio_goal import PortfolioGoal from tests.utilities.floats import assert_float_matches def _rec( *, plan_id: int, default: bool, cost: float, kwh: float, gbp: float, co2: float ) -> Recommendation: return Recommendation( property_id=10, plan_id=plan_id, type="cavity_wall_insulation", measure_type="cavity_wall_insulation", description="Cavity wall insulation", estimated_cost=cost, kwh_savings=kwh, energy_cost_savings=gbp, co2_equivalent_savings=co2, total_work_hours=4.0, default=default, already_installed=False, ) def test_aggregation_sums_default_measures_linked_by_plan_id( db_session: Session, ) -> None: # Arrange — one Scenario + Plan, two default measures (summed) plus a # non-default one (excluded), all linked by recommendation.plan_id. db_session.add( ScenarioModel( id=7, portfolio_id=1, goal=PortfolioGoal.INCREASING_EPC, goal_value="C", is_default=True, ) ) db_session.add( PlanModel(id=100, portfolio_id=1, property_id=10, scenario_id=7, is_default=True) ) db_session.add_all( [ _rec(plan_id=100, default=True, cost=1000.0, kwh=500.0, gbp=120.0, co2=0.5), _rec(plan_id=100, default=True, cost=500.0, kwh=300.0, gbp=80.0, co2=0.2), # excluded: not default _rec(plan_id=100, default=False, cost=9.0, kwh=9.0, gbp=9.0, co2=9.0), ] ) db_session.commit() # Act aggregate_portfolio_recommendations( db_session, portfolio_id=1, scenario_id=7, total_valuation_increase=2500.0, labour_days=3.0, aggregated_data={}, ) db_session.commit() # Assert — the default measures' sums land on the Scenario row scenario = db_session.query(ScenarioModel).filter_by(id=7).one() assert scenario.cost is not None assert_float_matches(scenario.cost, 1500.0) # 1000 + 500 assert scenario.energy_savings is not None assert_float_matches(scenario.energy_savings, 800.0) # Σ kwh_savings assert scenario.energy_cost_savings is not None assert_float_matches(scenario.energy_cost_savings, 200.0) # 120 + 80 assert scenario.co2_equivalent_savings is not None assert_float_matches(scenario.co2_equivalent_savings, 0.7) # 0.5 + 0.2 assert scenario.total_work_hours is not None assert_float_matches(scenario.total_work_hours, 8.0) # 4 + 4 assert scenario.property_valuation_increase == 2500.0 assert scenario.labour_days == 3.0