Fix F&B last-7-days fill to span month boundary

"Fill F&B from last 7 days" only drew from the current month's elapsed
days, so early in a month (e.g. the 3rd) it only had 1-2 actual days to
repeat instead of a true trailing 7-day window. Add a dedicated
last7-fnb endpoint that fetches a real 7-day window ending yesterday,
reaching back into the prior month when needed.
This commit is contained in:
jtricerolph 2026-08-03 09:31:08 +00:00
parent d1c50c77d0
commit 73d2c713d4
3 changed files with 51 additions and 5 deletions

View file

@ -57,9 +57,38 @@ async function fetchMonthFromApi(year, month, lastDayNum, dowAlign = true) {
return { revMap, roomsMap }
}
async function fetchLast7ActualsFnB() {
const today = new Date()
const end = new Date(today)
end.setDate(end.getDate() - 1)
const start = new Date(end)
start.setDate(start.getDate() - 6)
const startDate = start.toISOString().split('T')[0]
const revData = await fcFetch(`/forecast/revenue?start_date=${startDate}&days=7&type=all`)
return revData.data
.slice()
.sort((a, b) => a.date.localeCompare(b.date))
.map(d => ({
date: d.date,
actual_dry: d.dry?.otb ?? null,
actual_wet: d.wet?.otb ?? null,
forecast_dry: d.dry?.forecast ?? null,
forecast_wet: d.wet?.forecast ?? null,
}))
}
export async function directorsForecastRoutes(fastify) {
fastify.addHook('preHandler', requireAuth)
// GET /api/directors-forecast/last7-fnb — trailing 7 actual F&B days ending
// yesterday, spanning a month boundary. Used by "fill F&B from last 7 days",
// which otherwise only has the current month's elapsed days to draw from.
fastify.get('/api/directors-forecast/last7-fnb', async (request, reply) => {
const days = await fetchLast7ActualsFnB()
return { days }
})
// GET /api/directors-forecast/worksheet/:year/:month
fastify.get('/api/directors-forecast/worksheet/:year/:month', async (request, reply) => {
const year = parseInt(request.params.year)

View file

@ -52,6 +52,18 @@ export function dfGetReport(year: number, month: number, dowAlign = true): Promi
return request<ForecastReportData>(`/directors-forecast/report/${year}/${month}?dow_align=${dowAlign}`)
}
export type Last7FnBDay = {
date: string
actual_dry: number | null
actual_wet: number | null
forecast_dry: number | null
forecast_wet: number | null
}
export function dfGetLast7Fnb(): Promise<{ days: Last7FnBDay[] }> {
return request<{ days: Last7FnBDay[] }>('/directors-forecast/last7-fnb')
}
export function dfSaveSnapshot(year: number, month: number, data: object) {
return request(`/directors-forecast/report/${year}/${month}/snapshot`, {
method: 'POST', body: JSON.stringify(data),

View file

@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
import { useFrameViewport } from '../hooks/useFrameViewport'
import { Save, RefreshCw, Camera, Trash2, Printer, Repeat2 } from 'lucide-react'
import { useAuth } from '../components/AuthGate'
import { dfGetWorksheet, dfSaveWorksheet, dfGetReport, dfSaveSnapshot, dfDeleteSnapshot } from '../api'
import { dfGetWorksheet, dfSaveWorksheet, dfGetReport, dfSaveSnapshot, dfDeleteSnapshot, dfGetLast7Fnb } from '../api'
import type { WorksheetDay, WorksheetData, ForecastReportData, ForecastSnapshot } from '../types'
import { can, DOW_NAMES, MONTH_NAMES } from '../types'
@ -82,11 +82,16 @@ function WorksheetTab({ year, month, dowAlign }: { year: number; month: number;
setDirty(true)
}
function fillFnBFromLast7() {
async function fillFnBFromLast7() {
if (!data) return
const last7 = data.days
.filter(d => d.is_past && (d.actual_dry != null || d.actual_wet != null))
.slice(-7)
let last7
try {
const res = await dfGetLast7Fnb()
last7 = res.days.filter(d => d.actual_dry != null || d.actual_wet != null)
} catch {
setError('Failed to load last 7 days of actuals.')
return
}
if (!last7.length) return
setOverrides(prev => {
const next = { ...prev }