Split cost/estimate across mid-period tariff changes
Rate changes are often scheduled ahead of time or confirmed by finance after the fact and backdated — assign-tariff already supported any effective_from date, but cost-calc previously priced a whole period at a single tariff (whichever was in force on the period-end date), silently mispricing the days on the other side of a change. getTariffSegments() finds every tariff assignment overlapping a date range; getMeterCostForPeriod/getMeterEstimateForPeriod now split consumption pro-rata by day count across segments and price each against its own tariff, summing to the period total. Estimates project the remaining days across a future-scheduled change the same way, rather than assuming today's tariff holds for the rest of the period. Also consolidates internal.js's cost/estimate routes (previously their own duplicate calc) onto the same shared functions reports.js and estimates.js use, so the Directors' report can't drift from the in-app numbers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
4fc5230d79
commit
7ab048931f
5 changed files with 151 additions and 63 deletions
|
|
@ -76,7 +76,15 @@ export default function Reports() {
|
|||
<tbody>
|
||||
{report.meters.map(m => (
|
||||
<tr key={m.meter_id}>
|
||||
<td>{m.meter_name}{!m.has_data && <span className="badge badge-outline" style={{ marginLeft: 6 }}>no data</span>}</td>
|
||||
<td>
|
||||
{m.meter_name}
|
||||
{!m.has_data && <span className="badge badge-outline" style={{ marginLeft: 6 }}>no data</span>}
|
||||
{m.rate_changed_mid_period && (
|
||||
<span className="badge badge-tou" style={{ marginLeft: 6 }} title={m.segments.map(s => `${s.tariff_name}: ${s.seg_start} – ${s.seg_end}`).join(', ')}>
|
||||
rate changed
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td>{m.category_name}</td>
|
||||
<td className="num">{formatUnits(m.consumption, m.unit_label)}</td>
|
||||
<td className="num">{formatMoney(m.usage_cost_pence)}</td>
|
||||
|
|
|
|||
|
|
@ -118,6 +118,20 @@ export interface Reading {
|
|||
warning?: string | null
|
||||
}
|
||||
|
||||
export interface CostSegment {
|
||||
tariff_id: number
|
||||
tariff_name: string
|
||||
seg_start: string
|
||||
seg_end: string
|
||||
days: number
|
||||
consumption: number | null
|
||||
usage_cost_pence: number
|
||||
standing_cost_pence: number
|
||||
ccl_cost_pence: number
|
||||
vat_pence: number
|
||||
total_pence: number
|
||||
}
|
||||
|
||||
export interface CostBreakdown {
|
||||
usage_cost_pence: number
|
||||
standing_cost_pence: number
|
||||
|
|
@ -126,6 +140,8 @@ export interface CostBreakdown {
|
|||
vat_pence: number
|
||||
total_pence: number
|
||||
split: Record<string, { share: number; rate: number; cost_pence: number }> | null
|
||||
segments: CostSegment[]
|
||||
rate_changed_mid_period: boolean
|
||||
}
|
||||
|
||||
export interface MeterCostRow extends CostBreakdown {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue