Model/repositories/plan/plan_repository.py
Khalim Conn-Kowlessar 5c982beba4 A new default plan demotes the prior default across scenarios 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:42:47 +00:00

65 lines
2.3 KiB
Python

from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from domain.modelling.plan import Plan
@dataclass(frozen=True)
class PlanSaveRequest:
"""Bundles the five fields the plan repository needs to persist one Plan.
Mirrors ``EpcSaveRequest`` in shape — used by ``PlanRepository.save_batch()``
to accumulate write intent before the batch is flushed in one Unit of Work."""
plan: Plan
property_id: int
scenario_id: int
portfolio_id: int
is_default: bool
class PlanRepository(ABC):
"""Persists a Plan (and its Plan Measures) for a Property + Scenario.
A re-run INSERTs a fresh Plan and keeps the prior one as history rather than
deleting it. When the new Plan is the default, prior default Plans for the
same (Property, Scenario) are demoted to `is_default=False`, so the current
Plan is the one with `is_default=True` (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. Keeps prior Plans for
``(property_id, scenario_id)`` as history; when ``is_default`` is True,
demotes the property's prior default Plan — whatever its Scenario — to
``is_default=False`` (one default Plan per property)."""
...
@abstractmethod
def save_batch(self, requests: list[PlanSaveRequest]) -> list[int]:
"""Persist a batch of Plans in three statements regardless of batch size.
Returns one plan id per request in input order. Fires a single demote
UPDATE only when at least one request has ``is_default=True``. Keeps
prior Plans as history (ADR-0017)."""
...
@abstractmethod
def property_ids_with_default_plans(self, property_ids: list[int]) -> set[int]:
"""The subset of ``property_ids`` that already have a default Plan.
A property outside this set is receiving its first Plan, which becomes
its default whatever the Scenario says (the legacy ``p.is_new`` rule —
readers need one default Plan per property)."""
...