mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Persist no Lodged Performance for a predicted Property 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7ecb7eebd1
commit
5d63118d5a
1 changed files with 75 additions and 1 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
@ -244,6 +244,80 @@ def test_run_raises_when_no_rhi_and_no_scored_result() -> None:
|
||||||
assert uow.commits == 0
|
assert uow.commits == 0
|
||||||
|
|
||||||
|
|
||||||
|
class _PredictedRebaseliner(Rebaseliner):
|
||||||
|
"""Scores a predicted picture into a fixed Effective Performance, ignoring the
|
||||||
|
(absent) lodged half — a predicted Property always has ``physical_state_changed``,
|
||||||
|
so the calculator output IS the Effective Performance (ADR-0039)."""
|
||||||
|
|
||||||
|
def __init__(self, effective: Performance, result: SapResult) -> None:
|
||||||
|
self._effective = effective
|
||||||
|
self._result = result
|
||||||
|
|
||||||
|
def rebaseline(
|
||||||
|
self,
|
||||||
|
property_id: int,
|
||||||
|
effective_epc: EpcPropertyData,
|
||||||
|
lodged: Optional[Performance],
|
||||||
|
*,
|
||||||
|
physical_state_changed: bool = False,
|
||||||
|
) -> RebaselineResult:
|
||||||
|
return RebaselineResult(
|
||||||
|
effective=self._effective,
|
||||||
|
reason="physical_state_changed",
|
||||||
|
sap_result=self._result,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _predicted_property() -> Property:
|
||||||
|
"""A predicted Property (ADR-0031): no lodged EPC, only a neighbour-synthesised
|
||||||
|
predicted EPC whose recorded performance fields are a *different dwelling's* —
|
||||||
|
the phantom Lodged Performance this fix must not propagate. Mirrors the #1361
|
||||||
|
exemplar (uprn 34003067 / property 742072), predicted-only."""
|
||||||
|
predicted = object.__new__(EpcPropertyData)
|
||||||
|
predicted.energy_rating_current = 14 # phantom: copied from a neighbour template
|
||||||
|
predicted.current_energy_efficiency_band = Epc.G
|
||||||
|
predicted.co2_emissions_current = 5.0
|
||||||
|
predicted.energy_consumption_current = 400
|
||||||
|
predicted.sap_version = 10.2
|
||||||
|
predicted.renewable_heat_incentive = None
|
||||||
|
return Property(
|
||||||
|
identity=PropertyIdentity(
|
||||||
|
portfolio_id=1, postcode="A0 0AA", address="1 Some Street", uprn=34003067
|
||||||
|
),
|
||||||
|
epc=None,
|
||||||
|
predicted_epc=predicted,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_persists_no_lodged_performance_for_a_predicted_property() -> None:
|
||||||
|
# Arrange — a predicted Property (no lodged cert); the rebaseliner scores its
|
||||||
|
# predicted picture into a known Effective Performance.
|
||||||
|
property_baseline_repo = FakePropertyBaselineRepo()
|
||||||
|
uow = FakeUnitOfWork(
|
||||||
|
property=FakePropertyRepo({742072: _predicted_property()}),
|
||||||
|
property_baseline=property_baseline_repo,
|
||||||
|
)
|
||||||
|
effective = Performance(
|
||||||
|
sap_score=57, epc_band=Epc.D, co2_emissions=1.5, primary_energy_intensity=160
|
||||||
|
)
|
||||||
|
orchestrator = PropertyBaselineOrchestrator(
|
||||||
|
unit_of_work=lambda: uow,
|
||||||
|
rebaseliner=_PredictedRebaseliner(
|
||||||
|
effective, _sap_result_with_heating(space_kwh=4200.0, water_kwh=1600.0)
|
||||||
|
),
|
||||||
|
fuel_rates=FuelRatesStaticFileRepository(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
orchestrator.run([742072])
|
||||||
|
|
||||||
|
# Assert — no lodged comparator was manufactured from the neighbour-synthesised
|
||||||
|
# EPC's recorded fields; the Effective half is still persisted.
|
||||||
|
(baseline, _) = property_baseline_repo.saved[0]
|
||||||
|
assert baseline.lodged is None
|
||||||
|
assert baseline.effective == effective
|
||||||
|
|
||||||
|
|
||||||
def test_run_derives_and_persists_a_bill_when_the_rebaseliner_scores() -> None:
|
def test_run_derives_and_persists_a_bill_when_the_rebaseliner_scores() -> None:
|
||||||
# Arrange — a rebaseliner that hands back a SapResult with lighting energy,
|
# Arrange — a rebaseliner that hands back a SapResult with lighting energy,
|
||||||
# so the orchestrator prices it into a Bill at the committed snapshot.
|
# so the orchestrator prices it into a Bill at the committed snapshot.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue