// Backend port of wages/frontend/src/lib/forecast.ts's forecastDayCost — keep both in sync. const MAX_HOPS = 6 // 6 * 7 = 42 days back, comfortably within the 35-day actuals sync window function addDaysStr(dateStr, n) { const d = new Date(dateStr + 'T00:00:00') d.setDate(d.getDate() + n) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` } // Tiered forecast cost for a single department + date: actual (if present) → published // rota → draft rota (if includeUnpublished) → same-weekday recurring fallback (7-day hops). export function forecastDayCost(dateStr, actualDays, scheduledDays, includeUnpublished) { let probe = dateStr for (let hop = 0; hop <= MAX_HOPS; hop++) { const actual = actualDays?.[probe] if (actual != null) return { cost: actual.cost, tier: 'actual' } const sched = scheduledDays?.[probe] const hasPublished = (sched?.published_shift_count ?? 0) > 0 const hasUnpublished = (sched?.unpublished_shift_count ?? 0) > 0 if (hasPublished || (includeUnpublished && hasUnpublished)) { const cost = (sched?.published_cost ?? 0) + (includeUnpublished ? (sched?.unpublished_cost ?? 0) : 0) return { cost, tier: hasPublished ? 'rota-published' : 'rota-draft' } } probe = addDaysStr(probe, -7) } return { cost: 0, tier: 'none' } }