"""Recommendation and Measure Option — the Modelling stage's proposal types. A Recommendation is a labelled group of mutually-exclusive Measure Options for one target surface; the Optimiser selects at most one. The target itself is encoded entirely in each Option's Simulation Overlay (which addresses building parts, windows, or systems), so this type stays stable as new surfaces land. Impact is never stored here — it is cascade-conditional (ADR-0016). See CONTEXT.md. """ from dataclasses import dataclass from typing import Optional from domain.modelling.simulation import EpcSimulation @dataclass(frozen=True) class Cost: """A Measure Option's cost: a single fully-loaded total (labour + VAT + preliminaries + margin rolled in) plus a separately-carried per-Measure-Type contingency rate.""" total: float contingency_rate: float @dataclass(frozen=True) class MeasureOption: """One mutually-exclusive way to treat a Recommendation's surface.""" measure_type: str description: str overlay: EpcSimulation cost: Optional[Cost] = None # The catalogue id of the Product this Option installs (Product.id), carried # through to the persisted Plan Measure's ``material_id``. None when priced # from a catalogue with no ids. material_id: Optional[int] = None @dataclass(frozen=True) class Recommendation: """A target surface and the mutually-exclusive Measure Options that treat it. `surface` is a human label for display/grouping; the actual target is in each Option's overlay.""" surface: str options: tuple[MeasureOption, ...]