Exclude sub-meters from category subtotals in Estimates/Reports/internal API

Water Softener is configured as a submeter (parent_meter_id) of Main Hotel
Water, but estimates.js, reports.js, and internal.js's cost + estimate
routes summed every active meter flatly regardless of parent/child, double-
counting the softener's consumption on top of the main meter's own reading.

Sub-meter rows are still shown individually (useful for diagnostics), just
excluded from the category subtotal/total sums, with a 'submeter' badge on
the row explaining why. Fixes the water figures on Estimates, Reports, and
downstream consumers of internal.js (Directors report, Weekly Actuals).
This commit is contained in:
jtricerolph 2026-08-17 09:56:02 +00:00
parent d6a787e244
commit d223592aec
6 changed files with 72 additions and 24 deletions

View file

@ -8,8 +8,12 @@ function extrasFor(m: { ccl_cost_pence: number; rab_levy_cost_pence: number; met
return m.ccl_cost_pence + m.rab_levy_cost_pence + m.metering_cost_pence + m.other_charges_cost_pence
}
// Sub-meters (parent_meter_id set) read a subset of their parent's own
// reading (e.g. the water softener sits inline on the main supply) — their
// row is still shown for visibility, but excluding them here avoids the
// subtotal double-counting water/energy that's already in the parent's figure.
function sumMeterCosts(rows: MeterCostRow[]) {
return rows.reduce((acc, r) => ({
return rows.filter(r => !r.parent_meter_id).reduce((acc, r) => ({
consumption: acc.consumption + (r.consumption || 0),
usage_cost_pence: acc.usage_cost_pence + r.usage_cost_pence,
standing_cost_pence: acc.standing_cost_pence + r.standing_cost_pence,
@ -97,6 +101,11 @@ export default function Reports() {
<tr key={m.meter_id}>
<td>
{m.meter_name}
{m.parent_meter_id && (
<span className="badge" style={{ marginLeft: 6 }} title="Sub-meter — reads a subset of its parent meter, excluded from the subtotal below to avoid double-counting">
submeter
</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