AI Insights: a daily Claude-generated wage cost briefing covering month-to-date pace vs budget, prior-month/prior-year comparison, rota-vs-actual variance by department, a rota-informed forecast to month-end, employee-level anomalies, and wage cost as a % of revenue. Runs on a configurable daily schedule or on demand, gated by a manual 5-minute rate limit and a daily token budget. Uses the Anthropic key configured centrally in Portal → Settings → Integrations. Also includes the rota-vs-repeat-pattern forecast method (published/ draft rota tiers with same-weekday fallback) already built into the Weekly/Monthly views, and adds a .gitignore for node_modules/dist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
29 lines
1.3 KiB
JavaScript
29 lines
1.3 KiB
JavaScript
// 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' }
|
|
}
|