From ced6287baac2033b09dd72894f36f76cc26007d2 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 3 Jun 2026 17:19:23 +0000 Subject: [PATCH] refactor(billing): relocate Bill Derivation to domain/billing/ (cross-stage) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bill / EnergyBreakdown / BillDerivation / sap_fuel were under domain/property_baseline/ only because Baseline was built first. The Modelling stage now needs them too, so move them (and their tests) to a neutral domain/billing/ — Fuel/FuelRates already live in the shared domain/fuel_rates/. Avoids a modelling -> property_baseline cross-stage import and a package name that wrongly implies ownership (ADR-0011, ADR-0014 amendment). Pure git mv + import rewrite across 10 files; 40 billing/baseline/repo tests pass, pyright strict clean. CONTEXT.md Bill Derivation location updated. Co-Authored-By: Claude Opus 4.8 --- CONTEXT.md | 2 +- domain/billing/__init__.py | 0 domain/{property_baseline => billing}/bill.py | 2 +- domain/{property_baseline => billing}/bill_derivation.py | 2 +- domain/{property_baseline => billing}/sap_fuel.py | 0 domain/property_baseline/property_baseline_performance.py | 2 +- .../postgres/property_baseline_performance_table.py | 2 +- orchestration/property_baseline_orchestrator.py | 4 ++-- tests/domain/billing/__init__.py | 0 .../{property_baseline => billing}/test_bill_derivation.py | 4 ++-- .../{property_baseline => billing}/test_energy_breakdown.py | 2 +- tests/domain/{property_baseline => billing}/test_sap_fuel.py | 2 +- tests/orchestration/test_property_baseline_orchestrator.py | 2 +- .../test_property_baseline_postgres_repository.py | 2 +- 14 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 domain/billing/__init__.py rename domain/{property_baseline => billing}/bill.py (98%) rename domain/{property_baseline => billing}/bill_derivation.py (98%) rename domain/{property_baseline => billing}/sap_fuel.py (100%) create mode 100644 tests/domain/billing/__init__.py rename tests/domain/{property_baseline => billing}/test_bill_derivation.py (95%) rename tests/domain/{property_baseline => billing}/test_energy_breakdown.py (98%) rename tests/domain/{property_baseline => billing}/test_sap_fuel.py (96%) diff --git a/CONTEXT.md b/CONTEXT.md index 9cf31602..36ae6d4c 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -118,7 +118,7 @@ The process that translates an Optimised Package into cert-field changes and pro _Avoid_: measure overrides (rejected during ADR-0009 grill — phantom mid-layer), package applier, retrofit simulator **Bill Derivation**: -The deterministic process that derives a Property's annual energy **bill**, composed into per-end-use sections (heating, hot water, lighting, appliances, cooking, pumps/fans, …) plus a **total**, by pricing **SAP10 Calculation**'s delivered kWh per end use at **current Fuel Rates** — each end use billed at its fuel's rate, rolled up per fuel for **standing charges** (metered fuels only — gas/electricity; oil/LPG/solid have none) minus **SEG** export credit on PV. Implemented by `BillDerivation` in `domain/property_baseline/` (deterministic, ADR-0006). Reads Fuel Rates from a committed static snapshot via `FuelRatesRepository` (no live ETL yet). **Distinct from the calculator's `total_fuel_cost_gbp`**, which is the SAP-rating notional cost at RdSAP Table 32 standardised prices (~half the real electricity price) — not what the household pays. Raises on a fuel it has no rate for (e.g. house coal, heat network). ADR-0014. +The deterministic process that derives a Property's annual energy **bill**, composed into per-end-use sections (heating, hot water, lighting, appliances, cooking, pumps/fans, …) plus a **total**, by pricing **SAP10 Calculation**'s delivered kWh per end use at **current Fuel Rates** — each end use billed at its fuel's rate, rolled up per fuel for **standing charges** (metered fuels only — gas/electricity; oil/LPG/solid have none) minus **SEG** export credit on PV. Implemented by `BillDerivation` in `domain/billing/` (a cross-stage concern — the Baseline stage derives the current bill, the Modelling stage re-runs it on the post-package end-state for post-retrofit bills; deterministic, ADR-0006). Reads Fuel Rates from a committed static snapshot via `FuelRatesRepository` (no live ETL yet). **Distinct from the calculator's `total_fuel_cost_gbp`**, which is the SAP-rating notional cost at RdSAP Table 32 standardised prices (~half the real electricity price) — not what the household pays. Raises on a fuel it has no rate for (e.g. house coal, heat network). ADR-0014. _Avoid_: EPC Energy Derivation (renamed), EpcEnergyDerivationService (no "service" suffix), kWh prediction, baseline kWh, energy estimation **UCL Correction**: diff --git a/domain/billing/__init__.py b/domain/billing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/domain/property_baseline/bill.py b/domain/billing/bill.py similarity index 98% rename from domain/property_baseline/bill.py rename to domain/billing/bill.py index 110aa237..0e06cf27 100644 --- a/domain/property_baseline/bill.py +++ b/domain/billing/bill.py @@ -6,7 +6,7 @@ from enum import Enum from typing import Optional, TYPE_CHECKING from domain.fuel_rates.fuel import Fuel -from domain.property_baseline.sap_fuel import sap_code_to_fuel +from domain.billing.sap_fuel import sap_code_to_fuel if TYPE_CHECKING: from domain.sap10_calculator.calculator import SapResult diff --git a/domain/property_baseline/bill_derivation.py b/domain/billing/bill_derivation.py similarity index 98% rename from domain/property_baseline/bill_derivation.py rename to domain/billing/bill_derivation.py index 2aceeeb3..c1a09c64 100644 --- a/domain/property_baseline/bill_derivation.py +++ b/domain/billing/bill_derivation.py @@ -5,7 +5,7 @@ from typing import Final from domain.fuel_rates.fuel import Fuel from domain.fuel_rates.fuel_rates import FuelRates -from domain.property_baseline.bill import ( +from domain.billing.bill import ( Bill, BillSection, BillSectionCost, diff --git a/domain/property_baseline/sap_fuel.py b/domain/billing/sap_fuel.py similarity index 100% rename from domain/property_baseline/sap_fuel.py rename to domain/billing/sap_fuel.py diff --git a/domain/property_baseline/property_baseline_performance.py b/domain/property_baseline/property_baseline_performance.py index 3951611d..6fee9858 100644 --- a/domain/property_baseline/property_baseline_performance.py +++ b/domain/property_baseline/property_baseline_performance.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import Optional -from domain.property_baseline.bill import Bill +from domain.billing.bill import Bill from domain.property_baseline.performance import Performance from domain.property_baseline.rebaseliner import RebaselineReason diff --git a/infrastructure/postgres/property_baseline_performance_table.py b/infrastructure/postgres/property_baseline_performance_table.py index 908534c0..1327f63b 100644 --- a/infrastructure/postgres/property_baseline_performance_table.py +++ b/infrastructure/postgres/property_baseline_performance_table.py @@ -5,7 +5,7 @@ from typing import ClassVar, Optional, cast from sqlmodel import Field, SQLModel from datatypes.epc.domain.epc import Epc -from domain.property_baseline.bill import Bill, BillSection, BillSectionCost +from domain.billing.bill import Bill, BillSection, BillSectionCost from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance from domain.property_baseline.performance import Performance from domain.property_baseline.rebaseliner import RebaselineReason diff --git a/orchestration/property_baseline_orchestrator.py b/orchestration/property_baseline_orchestrator.py index faeaad92..6c749e36 100644 --- a/orchestration/property_baseline_orchestrator.py +++ b/orchestration/property_baseline_orchestrator.py @@ -6,8 +6,8 @@ from datatypes.epc.domain.epc_property_data import ( EpcPropertyData, RenewableHeatIncentive, ) -from domain.property_baseline.bill import EnergyBreakdown -from domain.property_baseline.bill_derivation import BillDerivation +from domain.billing.bill import EnergyBreakdown +from domain.billing.bill_derivation import BillDerivation from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance from domain.property_baseline.performance import lodged_performance from domain.property_baseline.rebaseliner import Rebaseliner diff --git a/tests/domain/billing/__init__.py b/tests/domain/billing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/domain/property_baseline/test_bill_derivation.py b/tests/domain/billing/test_bill_derivation.py similarity index 95% rename from tests/domain/property_baseline/test_bill_derivation.py rename to tests/domain/billing/test_bill_derivation.py index 73239d0f..cce045ee 100644 --- a/tests/domain/property_baseline/test_bill_derivation.py +++ b/tests/domain/billing/test_bill_derivation.py @@ -4,8 +4,8 @@ import pytest from domain.fuel_rates.fuel import Fuel, UnpricedFuel from domain.fuel_rates.fuel_rates import FuelRate, FuelRates -from domain.property_baseline.bill import BillSection, EnergyBreakdown, EnergyLine -from domain.property_baseline.bill_derivation import BillDerivation +from domain.billing.bill import BillSection, EnergyBreakdown, EnergyLine +from domain.billing.bill_derivation import BillDerivation def _rates() -> FuelRates: diff --git a/tests/domain/property_baseline/test_energy_breakdown.py b/tests/domain/billing/test_energy_breakdown.py similarity index 98% rename from tests/domain/property_baseline/test_energy_breakdown.py rename to tests/domain/billing/test_energy_breakdown.py index ffe7ffb0..4c64da29 100644 --- a/tests/domain/property_baseline/test_energy_breakdown.py +++ b/tests/domain/billing/test_energy_breakdown.py @@ -3,7 +3,7 @@ from __future__ import annotations import pytest from domain.fuel_rates.fuel import Fuel -from domain.property_baseline.bill import BillSection, EnergyBreakdown +from domain.billing.bill import BillSection, EnergyBreakdown from domain.sap10_calculator.calculator import SapResult diff --git a/tests/domain/property_baseline/test_sap_fuel.py b/tests/domain/billing/test_sap_fuel.py similarity index 96% rename from tests/domain/property_baseline/test_sap_fuel.py rename to tests/domain/billing/test_sap_fuel.py index dacdb075..ae9dd28f 100644 --- a/tests/domain/property_baseline/test_sap_fuel.py +++ b/tests/domain/billing/test_sap_fuel.py @@ -3,7 +3,7 @@ from __future__ import annotations import pytest from domain.fuel_rates.fuel import Fuel -from domain.property_baseline.sap_fuel import sap_code_to_fuel +from domain.billing.sap_fuel import sap_code_to_fuel from domain.sap10_calculator.exceptions import UnmappedSapCode diff --git a/tests/orchestration/test_property_baseline_orchestrator.py b/tests/orchestration/test_property_baseline_orchestrator.py index 1e0f5ec2..9183a8b3 100644 --- a/tests/orchestration/test_property_baseline_orchestrator.py +++ b/tests/orchestration/test_property_baseline_orchestrator.py @@ -10,7 +10,7 @@ from datatypes.epc.domain.epc_property_data import ( RenewableHeatIncentive, ) from domain.fuel_rates.fuel import Fuel -from domain.property_baseline.bill import BillSection +from domain.billing.bill import BillSection from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance from domain.property_baseline.performance import Performance from domain.property_baseline.rebaseliner import ( diff --git a/tests/repositories/property_baseline/test_property_baseline_postgres_repository.py b/tests/repositories/property_baseline/test_property_baseline_postgres_repository.py index a46a65f9..de6de8f4 100644 --- a/tests/repositories/property_baseline/test_property_baseline_postgres_repository.py +++ b/tests/repositories/property_baseline/test_property_baseline_postgres_repository.py @@ -4,7 +4,7 @@ from sqlalchemy import Engine from sqlmodel import Session from datatypes.epc.domain.epc import Epc -from domain.property_baseline.bill import Bill, BillSection, BillSectionCost +from domain.billing.bill import Bill, BillSection, BillSectionCost from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance from domain.property_baseline.performance import Performance from repositories.property_baseline.property_baseline_postgres_repository import (