Widen actuals fetch window so the hop-back forecast fallback can reach history

Monthly.tsx and Dashboard.tsx only ever fetched actuals for the current
period (month-start..month-end, or a 7-day lookback for the week view),
but forecastDayCost's same-weekday fallback hops back up to 42 days
looking for a match. For any early-period date whose hop landed before
the fetch window started, that data was never in memory at all — it
silently fell through to the 'none' tier (£0) once published rota ran
out, rather than finding a real historical match.

ai-insights.js's gatherForecastData already fetched a proper 42-day
lookback; these two pages didn't. Widened both fetches to match, with
explicit >= period-start filters on the actual-sum calculations so the
extra history is only ever used by the hop fallback, never counted
twice into "actual".
This commit is contained in:
jtricerolph 2026-08-06 07:41:09 +00:00
parent 221a5598c0
commit 2ce51b036d
2 changed files with 19 additions and 6 deletions

View file

@ -63,6 +63,12 @@ export default function Monthly() {
const toStr = `${monthStr}-${String(dim).padStart(2, '0')}`
const isCurrentMonth = year === todayYear && month === todayMonth
// forecastDayCost hops back up to 42 days looking for a same-weekday actual — fetch that
// much extra history before the month start so early-month dates can actually find it,
// instead of falling through to the 'none' tier once rota runs out. actualMTD/etc. below
// still filter to >= fromStr, so this extra history is only ever used for the hop fallback.
const actualsFromStr = fmt(addDays(new Date(fromStr + 'T00:00:00'), -42))
// Prior year: always fetch full month (derive both MTD and full totals from one call)
const pyDim = daysInMonth(year - 1, month)
const pyFromStr = `${year - 1}-${String(month).padStart(2, '0')}-01`
@ -80,7 +86,7 @@ export default function Monthly() {
try {
// Net sales: full month — OTB/forecast for future dates, actuals for past dates
const [actRes, schedRes, salesRes, budRes, pyActRes, pmActRes] = await Promise.all([
getActuals(fromStr, toStr),
getActuals(actualsFromStr, toStr),
getScheduled(fromStr, toStr),
getNetSales(fromStr, toStr),
getBudgets(),
@ -136,7 +142,7 @@ export default function Monthly() {
} finally {
setLoading(false)
}
}, [fromStr, toStr, pyFromStr, pyToStr, pmFromStr, pmToStr, isCurrentMonth, yesterdayStr, year, month, pyDim])
}, [fromStr, toStr, actualsFromStr, pyFromStr, pyToStr, pmFromStr, pmToStr, isCurrentMonth, yesterdayStr, year, month, pyDim])
useEffect(() => { load() }, [load])