diff --git a/backend/src/routes/directors-forecast.js b/backend/src/routes/directors-forecast.js index 66c73e80..cf6b901e 100644 --- a/backend/src/routes/directors-forecast.js +++ b/backend/src/routes/directors-forecast.js @@ -104,8 +104,10 @@ export async function directorsForecastRoutes(fastify) { const actualDry = isPast ? (rev.dry?.otb ?? null) : null const actualWet = isPast ? (rev.wet?.otb ?? null) : null - // User overrides - const pickupRooms = ovr.pickup_rooms != null ? parseInt(ovr.pickup_rooms) : 0 + // User overrides — once a day is past and has an actual, a leftover + // pickup override from before the day arrived is stale and shouldn't count. + const pickupApplies = !(isPast && actualAccomm != null) + const pickupRooms = pickupApplies && ovr.pickup_rooms != null ? parseInt(ovr.pickup_rooms) : 0 const dayRate = ovr.pickup_avg_rate != null ? parseFloat(ovr.pickup_avg_rate) : rate // Forecast values @@ -209,7 +211,7 @@ export async function directorsForecastRoutes(fastify) { '29–end': { accomm: 0, dry: 0, wet: 0, rooms: 0 }, } - let totalAvailable = 0, totalOtbRooms = 0, totalOtbNetRev = 0, totalPickup = 0 + let totalAvailable = 0, totalOtbRooms = 0, totalOtbNetRev = 0, totalPickup = 0, totalPickupRevenue = 0 let totalAccomm = 0, totalDry = 0, totalWet = 0 let totalBudgetAccomm = 0, totalBudgetDry = 0, totalBudgetWet = 0 let totalLyAccomm = 0, totalLyDry = 0, totalLyWet = 0, totalLyRooms = 0 @@ -224,14 +226,19 @@ export async function directorsForecastRoutes(fastify) { const available = rm.available_rooms ?? 0 const otbRooms = rm.otb_rooms ?? 0 const otbNetRev = rev.accom?.otb ?? 0 - const pickupRooms = ovr.pickup_rooms != null ? parseInt(ovr.pickup_rooms) : 0 - const dayRate = ovr.pickup_avg_rate != null ? parseFloat(ovr.pickup_avg_rate) : sessionRate - const totalRooms = otbRooms + pickupRooms const actualAccomm = isPast ? (rev.accom?.otb ?? null) : null const actualDry = isPast ? (rev.dry?.otb ?? null) : null const actualWet = isPast ? (rev.wet?.otb ?? null) : null + // Once a day is past and has an actual, it's realised — any leftover + // pickup override from before the day arrived is stale and must not + // still count as "probable" pickup (it never contributed to fAccomm below). + const pickupApplies = !(isPast && actualAccomm != null) + const pickupRooms = pickupApplies && ovr.pickup_rooms != null ? parseInt(ovr.pickup_rooms) : 0 + const dayRate = ovr.pickup_avg_rate != null ? parseFloat(ovr.pickup_avg_rate) : sessionRate + const totalRooms = otbRooms + pickupRooms + const fAccomm = (isPast && actualAccomm != null) ? actualAccomm : otbNetRev + pickupRooms * dayRate const fDry = ovr.forecast_dry != null ? parseFloat(ovr.forecast_dry) : ovr.dry_override != null ? parseFloat(ovr.dry_override) @@ -244,6 +251,7 @@ export async function directorsForecastRoutes(fastify) { totalOtbRooms += otbRooms totalOtbNetRev += otbNetRev totalPickup += pickupRooms + totalPickupRevenue += pickupRooms * dayRate totalAccomm += fAccomm totalDry += fDry totalWet += fWet @@ -312,7 +320,7 @@ export async function directorsForecastRoutes(fastify) { arr: totalOtbRooms > 0 ? totalOtbNetRev / totalOtbRooms : null, revpar: totalAvailable > 0 ? totalOtbNetRev / totalAvailable : null, }, - pickup: { rooms: totalPickup, accomm: totalPickup * sessionRate, avg_rate: sessionRate }, + pickup: { rooms: totalPickup, accomm: totalPickupRevenue, avg_rate: totalPickup > 0 ? totalPickupRevenue / totalPickup : sessionRate }, weekly: Object.entries(weekBands).map(([label, v]) => ({ label, ...v })), snapshots: snapshotsRes.rows, utilities,