Monthly forecast: repeat last full actual week to end of month instead of single 7-day lookback
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 <noreply@anthropic.com>
This commit is contained in:
parent
84f5d38b10
commit
9e7f4030d2
1 changed files with 8 additions and 5 deletions
|
|
@ -21,6 +21,12 @@ function fmtDisplay(dateStr: string): string {
|
||||||
function budgetColour(pct: number): string {
|
function budgetColour(pct: number): string {
|
||||||
return pct <= 100 ? 'var(--app-primary)' : pct <= 110 ? '#b45309' : '#dc2626'
|
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<string, { cost: number }>, 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']
|
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++) {
|
for (let day = 1; day <= dim; day++) {
|
||||||
const dateStr = `${monthStr}-${String(day).padStart(2, '0')}`
|
const dateStr = `${monthStr}-${String(day).padStart(2, '0')}`
|
||||||
if (dateStr <= yesterdayStr) continue
|
if (dateStr <= yesterdayStr) continue
|
||||||
const priorStr = fmt(addDays(new Date(dateStr + 'T00:00:00'), -7))
|
forecastRem += repeatingPriorCost(dep.days, dateStr, yesterdayStr)
|
||||||
const priorCost = dep.days[priorStr]?.cost
|
|
||||||
if (priorCost != null) forecastRem += priorCost
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,8 +215,7 @@ export default function Monthly() {
|
||||||
if (dateStr <= cutoff) {
|
if (dateStr <= cutoff) {
|
||||||
deptCost += srcDep?.days[dateStr]?.cost ?? 0
|
deptCost += srcDep?.days[dateStr]?.cost ?? 0
|
||||||
} else {
|
} else {
|
||||||
const priorStr = fmt(addDays(new Date(dateStr + 'T00:00:00'), -7))
|
deptCost += repeatingPriorCost(srcDep?.days ?? {}, dateStr, cutoff)
|
||||||
deptCost += srcDep?.days[priorStr]?.cost ?? 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry[dep.department_name] = deptCost
|
entry[dep.department_name] = deptCost
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue