Fix missing GST on all AER-sourced usage rates and supply charges
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 35s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 35s
AER's Consumer Data Right unitPrice fields are GST-exclusive, confirmed by comparing a real, currently-effective EnergyAustralia contract's own GST-inclusive rates against this plan's cached AER data: usage rates and the supply charge all matched the cached value multiplied by exactly 1.1 (four independent figures). A real AGL bill confirmed the same convention separately (excl-GST line items, GST added back as one combined amount at the end). This understated every one of the ~700+ cached comparison plans' costs by ~10% relative to manually-entered current plans (which were correctly GST-inclusive, taken from real bills), biasing every "savings vs current" figure across the board. Feed-in/solar buy-back rates are the exception -- confirmed GST-free in that same contract's own terms -- so the multiplier applies only to usage rates and the supply charge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9e66a9a97d
commit
e4c41c3eb9
2 changed files with 29 additions and 8 deletions
|
|
@ -5,7 +5,7 @@ from datetime import datetime
|
|||
|
||||
import pytest
|
||||
|
||||
from app.costing.aer_mapper import map_aer_plan
|
||||
from app.costing.aer_mapper import GST_MULTIPLIER, map_aer_plan
|
||||
from app.costing.engine import IntervalReading, evaluate
|
||||
from app.costing.tariff_models import find_usage_coverage_gaps
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ def test_maps_real_agl_time_of_use_plan():
|
|||
assert plan.retailer == "AGL"
|
||||
assert "Energex" in plan.distributors
|
||||
assert len(plan.usage_components) == 3 # peak, off-peak, shoulder
|
||||
assert plan.supply_charge_c_per_day == pytest.approx(1.536 * 100)
|
||||
assert plan.supply_charge_c_per_day == pytest.approx(1.536 * 100 * GST_MULTIPLIER)
|
||||
|
||||
labels = {c.label for c in plan.usage_components}
|
||||
assert {"Time of Use Peak", "Time of Use Off Peak", "Time of Use Shoulder"} <= labels
|
||||
|
|
|
|||
|
|
@ -62,10 +62,27 @@ def _time_windows(entries: list[dict]) -> list[TimeWindow]:
|
|||
]
|
||||
|
||||
|
||||
# AER's Consumer Data Right "Get Generic Plan Detail" unitPrice fields are
|
||||
# GST-EXCLUSIVE -- confirmed by comparing a real, currently-effective
|
||||
# EnergyAustralia contract's own quoted GST-inclusive rates against this
|
||||
# plan's cached AER data: every usage rate and the supply charge matched
|
||||
# the cached value multiplied by exactly 1.1, precisely (four independent
|
||||
# figures, not a coincidence), and a real AGL bill separately confirmed
|
||||
# the same convention (its line items are excl-GST, with GST added back as
|
||||
# one combined amount at the end). Feed-in/solar buy-back rates are the
|
||||
# exception -- confirmed GST-free in that same EnergyAustralia contract's
|
||||
# own terms ("The rate at which we purchase your electricity does not
|
||||
# include GST") -- so this multiplier applies only to usage rates and the
|
||||
# supply charge, never to feed_in_tariffs/tiered_feed_in.
|
||||
GST_MULTIPLIER = 1.1
|
||||
|
||||
|
||||
def _rate_cents(rates: list[dict]) -> tuple[float, bool]:
|
||||
"""Returns (cents_per_kwh, is_tiered). Only the first tier is used for
|
||||
tiered/inclining-block rate structures -- callers should mark the plan
|
||||
not-modelable when is_tiered is True, since later tiers are ignored."""
|
||||
"""Returns (cents_per_kwh, is_tiered), GST-exclusive as given by AER --
|
||||
callers apply GST_MULTIPLIER themselves where it applies (usage rates,
|
||||
not feed-in). Only the first tier is used for tiered/inclining-block
|
||||
rate structures -- callers should mark the plan not-modelable when
|
||||
is_tiered is True, since later tiers are ignored."""
|
||||
return float(rates[0]["unitPrice"]) * 100, len(rates) > 1
|
||||
|
||||
|
||||
|
|
@ -84,13 +101,17 @@ def map_aer_plan(list_entry: dict, detail: dict) -> TariffPlan:
|
|||
for period in ec.get("tariffPeriod", []):
|
||||
season_months = _season_months(period.get("startDate", "01-01"), period.get("endDate", "12-31"))
|
||||
rate_type = period.get("rateBlockUType")
|
||||
supply_charges_c.append(float(period.get("dailySupplyCharge", 0) or 0) * 100)
|
||||
supply_charges_c.append(float(period.get("dailySupplyCharge", 0) or 0) * 100 * GST_MULTIPLIER)
|
||||
|
||||
if rate_type == "singleRate":
|
||||
block = period["singleRate"]
|
||||
rate_c, tiered = _rate_cents(block["rates"])
|
||||
usage_components.append(
|
||||
UsageComponent(label=block.get("displayName", "Usage"), rate_c_per_kwh=rate_c, season_months=season_months)
|
||||
UsageComponent(
|
||||
label=block.get("displayName", "Usage"),
|
||||
rate_c_per_kwh=rate_c * GST_MULTIPLIER,
|
||||
season_months=season_months,
|
||||
)
|
||||
)
|
||||
if tiered:
|
||||
modelable = False
|
||||
|
|
@ -101,7 +122,7 @@ def map_aer_plan(list_entry: dict, detail: dict) -> TariffPlan:
|
|||
usage_components.append(
|
||||
UsageComponent(
|
||||
label=tou_block.get("displayName", tou_block.get("type", "TOU")),
|
||||
rate_c_per_kwh=rate_c,
|
||||
rate_c_per_kwh=rate_c * GST_MULTIPLIER,
|
||||
time_windows=_time_windows(tou_block.get("timeOfUse", [])),
|
||||
season_months=season_months,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue