initial commit
This commit is contained in:
60
hameter/cost.py
Normal file
60
hameter/cost.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Cost calculation for meter rate components."""
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
|
||||
from hameter.config import RateComponent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CostResult:
|
||||
"""Result of a cost calculation for a single reading delta."""
|
||||
|
||||
delta: float
|
||||
per_unit_cost: float
|
||||
component_costs: list[dict]
|
||||
total_incremental_cost: float
|
||||
|
||||
|
||||
def calculate_incremental_cost(
|
||||
delta: float,
|
||||
cost_factors: list[RateComponent],
|
||||
) -> CostResult:
|
||||
"""Calculate the incremental cost for a usage delta.
|
||||
|
||||
Only per_unit rate components contribute to incremental cost.
|
||||
Fixed charges are NOT included — they are handled separately
|
||||
via the billing period reset / manual add workflow.
|
||||
|
||||
Args:
|
||||
delta: Usage delta in calibrated units (e.g., kWh).
|
||||
cost_factors: List of rate components from meter config.
|
||||
|
||||
Returns:
|
||||
CostResult with per-component breakdown and total.
|
||||
"""
|
||||
component_costs = []
|
||||
per_unit_total = 0.0
|
||||
|
||||
for cf in cost_factors:
|
||||
if cf.type == "per_unit":
|
||||
cost = round(delta * cf.rate, 4)
|
||||
per_unit_total += cost
|
||||
else:
|
||||
cost = 0.0
|
||||
|
||||
component_costs.append({
|
||||
"name": cf.name,
|
||||
"rate": cf.rate,
|
||||
"type": cf.type,
|
||||
"cost": cost,
|
||||
})
|
||||
|
||||
return CostResult(
|
||||
delta=delta,
|
||||
per_unit_cost=round(per_unit_total, 4),
|
||||
component_costs=component_costs,
|
||||
total_incremental_cost=round(per_unit_total, 4),
|
||||
)
|
||||
Reference in New Issue
Block a user