from __future__ import annotations from sqlmodel import Session, col, delete from domain.modelling.plan import Plan from infrastructure.postgres.modelling import PlanModel, RecommendationModel from repositories.plan.plan_repository import PlanRepository class PlanPostgresRepository(PlanRepository): """Maps a Plan and its Plan Measures onto the live ``plan`` / ``recommendation`` tables (ADR-0017). Does not commit — the Unit of Work owns the transaction (ADR-0012).""" def __init__(self, session: Session) -> None: self._session = session def save( self, plan: Plan, *, property_id: int, scenario_id: int, portfolio_id: int, is_default: bool, ) -> int: # Idempotent replace for (property_id, scenario_id): deleting the Plan # cascades to its recommendation rows via the plan_id FK (ON DELETE # CASCADE), so a re-run overwrites rather than duplicating (ADR-0012). self._session.exec( # type: ignore[call-overload] delete(PlanModel).where( col(PlanModel.property_id) == property_id, col(PlanModel.scenario_id) == scenario_id, ) ) plan_row = PlanModel.from_domain( plan, property_id=property_id, scenario_id=scenario_id, portfolio_id=portfolio_id, is_default=is_default, ) self._session.add(plan_row) self._session.flush() if plan_row.id is None: raise ValueError("plan row did not receive an id") for measure in plan.measures: self._session.add( RecommendationModel.from_domain( measure, property_id=property_id, plan_id=plan_row.id ) ) return plan_row.id