Model/repositories/plan/plan_repository.py
Khalim Conn-Kowlessar d66f7eed84 feat(modelling): plan/recommendation SQLModel mirrors + PlanRepository (#1157)
Slice 3 of #1157. Persists a Plan and its Plan Measures to the live
plan / recommendation tables via SQLModel mirrors (ADR-0017).

- infrastructure/postgres/plan_table.py: PlanRow (`plan`) + RecommendationRow
  (`recommendation`) mirrors. RecommendationRow adds the new `plan_id` FK
  (ON DELETE CASCADE) linking each Plan Measure to its Plan, replacing the
  plan_recommendations m2m for new writes. from_domain mappers convert CO2
  kg → tonnes to match the live column contract and derive post_epc_rating
  from the rounded SAP. Only the impact + cost + identity columns the tracer
  fills are declared; energy/bill, U-value, valuation, labour, plan_type are
  left to later slices.
- PlanRepository port + PlanPostgresRepository.save(plan, *, property_id,
  scenario_id, portfolio_id, is_default) -> plan id. Idempotent replace:
  deleting the Plan cascades to its recommendation rows via plan_id, so a
  re-run overwrites (ADR-0012). No commit — the UoW owns the transaction.

2 tests (persist + idempotent re-run); pyright strict clean; 73 pass across
repositories/modelling/orchestration with no regressions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:51:02 +00:00

29 lines
865 B
Python

from __future__ import annotations
from abc import ABC, abstractmethod
from domain.modelling.plan import Plan
class PlanRepository(ABC):
"""Persists a Plan (and its Plan Measures) for a Property + Scenario.
One Plan per (Property, Scenario). The write is idempotent on re-run: it
replaces the existing Plan for that pair rather than duplicating (ADR-0012
/ ADR-0017). `portfolio_id` and `is_default` are supplied by the
orchestrator (the former from the trigger, the latter from the Scenario).
"""
@abstractmethod
def save(
self,
plan: Plan,
*,
property_id: int,
scenario_id: int,
portfolio_id: int,
is_default: bool,
) -> int:
"""Persist ``plan`` and return its Plan id, replacing any existing Plan
for ``(property_id, scenario_id)``."""
...