"""A Plan derives its Valuation Uplift from its band jump (ADR-0018). The uplift is plan-conditional — it needs the Plan's baseline -> post band jump and its cost — so the Plan derives it, given one external input: the Property's current market value (mostly absent, so the £ forms are usually None).""" from __future__ import annotations from domain.modelling.plan import Plan from domain.modelling.scoring.package_scorer import Score from infrastructure.postgres.modelling import PlanModel def _plan(*, current_market_value: float | None) -> Plan: # Baseline SAP 57.4 rounds to band D; post 70.0 rounds to band C. baseline = Score( sap_continuous=57.4, co2_kg_per_yr=3000.0, primary_energy_kwh_per_yr=300.0 ) post = Score( sap_continuous=70.0, co2_kg_per_yr=2100.0, primary_energy_kwh_per_yr=240.0 ) return Plan( measures=(), baseline=baseline, post_retrofit=post, current_market_value=current_market_value, ) def test_plan_derives_pound_uplift_from_a_current_market_value() -> None: # Arrange — a £200k property modelled D -> C. plan: Plan = _plan(current_market_value=200_000.0) # Act uplift = plan.valuation # Assert — D->C average 2.5% of £200k = £5,000 uplift, £205,000 post-retrofit. assert uplift.average_value is not None assert abs(uplift.average_value - 5_000.0) <= 1e-6 assert uplift.post_retrofit_value is not None assert abs(uplift.post_retrofit_value - 205_000.0) <= 1e-6 def test_plan_model_persists_the_valuation_pound_forms() -> None: # Arrange plan: Plan = _plan(current_market_value=200_000.0) # Act model: PlanModel = PlanModel.from_domain( plan, property_id=1, scenario_id=7, portfolio_id=1, is_default=True ) # Assert assert model.valuation_increase_lower_bound is not None assert abs(model.valuation_increase_lower_bound - 4_000.0) <= 1e-6 assert model.valuation_increase_average is not None assert abs(model.valuation_increase_average - 5_000.0) <= 1e-6 assert model.valuation_post_retrofit is not None assert abs(model.valuation_post_retrofit - 205_000.0) <= 1e-6 def test_plan_model_leaves_valuation_null_without_a_market_value() -> None: # Arrange — no current market value (the common case at scale). plan: Plan = _plan(current_market_value=None) # Act model: PlanModel = PlanModel.from_domain( plan, property_id=1, scenario_id=7, portfolio_id=1, is_default=True ) # Assert — the percentage is still derivable, but the £ columns stay NULL. assert model.valuation_increase_average is None assert model.valuation_post_retrofit is None assert abs(plan.valuation.average_pct - 0.025) <= 1e-9