From 9e7f4030d2d2f38d49c87f3f3e07b9d73f444d00 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Thu, 23 Jul 2026 17:19:10 +0000 Subject: [PATCH] Monthly forecast: repeat last full actual week to end of month instead of single 7-day lookback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an edge case where days more than 7 days past the actual cutoff had no valid prior-week source day (itself a forecast day), silently contributing £0. Now walks back in 7-day steps until landing on an actual day, so the last complete actual week tiles forward for the rest of the month. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/pages/Monthly.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Monthly.tsx b/frontend/src/pages/Monthly.tsx index 9d15e93..97c7e24 100644 --- a/frontend/src/pages/Monthly.tsx +++ b/frontend/src/pages/Monthly.tsx @@ -21,6 +21,12 @@ function fmtDisplay(dateStr: string): string { function budgetColour(pct: number): string { return pct <= 100 ? 'var(--app-primary)' : pct <= 110 ? '#b45309' : '#dc2626' } +// Baseline forecast: repeat the last full actual week's pattern forward to end of month +function repeatingPriorCost(days: Record, dateStr: string, cutoffStr: string): number { + let probe = dateStr + while (probe > cutoffStr) probe = fmt(addDays(new Date(probe + 'T00:00:00'), -7)) + return days[probe]?.cost ?? 0 +} const DEPT_COLORS = ['#065f46','#059669','#0891b2','#7c3aed','#c2410c','#b45309','#0f766e','#4338ca','#be185d','#15803d'] @@ -154,9 +160,7 @@ export default function Monthly() { for (let day = 1; day <= dim; day++) { const dateStr = `${monthStr}-${String(day).padStart(2, '0')}` if (dateStr <= yesterdayStr) continue - const priorStr = fmt(addDays(new Date(dateStr + 'T00:00:00'), -7)) - const priorCost = dep.days[priorStr]?.cost - if (priorCost != null) forecastRem += priorCost + forecastRem += repeatingPriorCost(dep.days, dateStr, yesterdayStr) } } @@ -211,8 +215,7 @@ export default function Monthly() { if (dateStr <= cutoff) { deptCost += srcDep?.days[dateStr]?.cost ?? 0 } else { - const priorStr = fmt(addDays(new Date(dateStr + 'T00:00:00'), -7)) - deptCost += srcDep?.days[priorStr]?.cost ?? 0 + deptCost += repeatingPriorCost(srcDep?.days ?? {}, dateStr, cutoff) } } entry[dep.department_name] = deptCost