From bed0b134dce588ccc62c9429ebd598362c41b076 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Tue, 28 Jul 2026 14:04:49 +0000 Subject: [PATCH] Fix estimate silently dropping the gap between last reading and today remainingDays was computed as days-from-today to period-end, while actual-to-date consumption only ever reflects data up to the meter's last real reading. With sparse readings the last reading sits days before today, so that gap was counted in neither the actual nor the projected total. Project from the last reading's date instead. Co-Authored-By: Claude Sonnet 5 --- backend/src/lib/cost-calc.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/src/lib/cost-calc.js b/backend/src/lib/cost-calc.js index e2a5277..d367542 100644 --- a/backend/src/lib/cost-calc.js +++ b/backend/src/lib/cost-calc.js @@ -350,10 +350,17 @@ export async function getMeterEstimateForPeriod(meterId, periodStart, periodEnd, const today = toISODate(new Date()) const asOfDate = today < periodEnd ? today : periodEnd const daysInPeriod = daysInclusive(periodStart, periodEnd) - const remainingDays = Math.max(daysBetween(asOfDate, periodEnd), 0) const { daily_rate } = await getTrailingDailyRate(meterId, windowDays) - const { consumption: actualToDate, has_data } = await getPeriodConsumption(meterId, periodStart, asOfDate) + const { consumption: actualToDate, has_data, last: lastReading } = await getPeriodConsumption(meterId, periodStart, asOfDate) + + // Project from the meter's last actual reading, not from "today" — a meter + // read only every couple of weeks will almost always have its last reading + // sitting days before today, and projecting only from today would silently + // drop that gap from both the actual and projected totals. + const lastKnownDate = lastReading ? toISODate(lastReading.reading_date) : periodStart + const projectionFrom = lastKnownDate > periodStart ? lastKnownDate : periodStart + const remainingDays = Math.max(daysBetween(projectionFrom, periodEnd), 0) let projectedConsumption = null if (daily_rate != null) {