mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Expand half of the recommendation_materials retirement (ADR-0017). A Plan Measure installs a single Product, so thread its catalogue id end to end — Product.id -> MeasureOption.material_id -> PlanMeasure.material_id -> recommendation.material_id — replacing the per-material BOM child table with one nullable column on the row. ProductPostgresRepository reads the id from MaterialRow; the four fabric generators set it on their Option; the orchestrator carries it onto the Plan Measure; the mirror declares + maps the column. Optional throughout (the JSON stopgap catalogue carries no ids -> NULL). The multi-measure integration test now pins each persisted measure's material_id to its seeded MaterialRow id. Migration spec (live column must be added before this deploys; contraction is the owner's next step) in docs/migrations/recommendation-material-id.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
22 lines
820 B
Python
22 lines
820 B
Python
"""Product — a catalogue entry a Measure Option installs.
|
|
|
|
Carries the data needed to price an Option: a fully-loaded unit cost and the
|
|
per-Measure-Type contingency rate carried alongside it (CONTEXT.md). The
|
|
catalogue is equipment-dominated (heat pumps, glazing, PV) — hence "Product",
|
|
not "material". Read via a `ProductRepository`.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Product:
|
|
measure_type: str
|
|
unit_cost_per_m2: float
|
|
contingency_rate: float
|
|
# The catalogue row id, threaded onto the persisted Plan Measure as
|
|
# ``recommendation.material_id`` (the single-material reference that replaces
|
|
# the retired ``recommendation_materials`` BOM). Optional: the JSON
|
|
# stopgap catalogue carries no ids.
|
|
id: Optional[int] = None
|