From 6996a676706707053b6007ff4973359c615ff885 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sun, 28 Jun 2026 11:36:26 +0200 Subject: [PATCH] fix(extract): don't let the day-clamp fallback break reservation resync (#1288) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This branch added a clamp-to-nearest-day fallback to resolveDayIdFromTime so an imported booking whose exact date has no day row still lands on a day. After rebasing onto dev, that collided with #1288's resyncReservationDays, which relies on the original "null when no exact day" semantics to leave a booking whose date now falls outside the range untouched — instead it snapped to an edge day (TRIP-SVC-019 failed: expected day_id kept, got the clamped one). Make clampToNearest an opt-in parameter (default true, preserving the import behaviour for create/update) and have resyncReservationDays pass false, so out-of-range bookings keep their day_id. Full server suite green (4082). --- server/src/services/reservationService.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server/src/services/reservationService.ts b/server/src/services/reservationService.ts index 846dda08..2e2b05fb 100644 --- a/server/src/services/reservationService.ts +++ b/server/src/services/reservationService.ts @@ -49,6 +49,7 @@ function loadEndpoints(reservationId: number): ReservationEndpoint[] { function resolveDayIdFromTime( tripId: string | number, time: string | null | undefined, + clampToNearest = true, ): number | null { if (!time) return null; const datePart = time.slice(0, 10); @@ -57,8 +58,11 @@ function resolveDayIdFromTime( .prepare('SELECT id FROM days WHERE trip_id = ? AND date = ? LIMIT 1') .get(tripId, datePart) as { id: number } | undefined; if (exact) return exact.id; - // Fallback: clamp to the nearest day in the trip so a booking whose exact date - // has no day row (or sits just outside the span) still lands on a day. + // Fallback: clamp to the nearest day in the trip so an imported booking whose + // exact date has no day row (or sits just outside the span) still lands on a day. + // Skipped by callers (e.g. resyncReservationDays) that must leave a booking whose + // date now falls outside the range untouched instead of snapping it to an edge day. + if (!clampToNearest) return null; const nearest = db .prepare('SELECT id FROM days WHERE trip_id = ? ORDER BY ABS(JULIANDAY(date) - JULIANDAY(?)) ASC, date ASC LIMIT 1') .get(tripId, datePart) as { id: number } | undefined; @@ -82,10 +86,10 @@ export function resyncReservationDays(tripId: string | number): void { }[]; const update = db.prepare('UPDATE reservations SET day_id = ?, end_day_id = ? WHERE id = ?'); for (const r of rows) { - const newDayId = resolveDayIdFromTime(tripId, r.reservation_time); + const newDayId = resolveDayIdFromTime(tripId, r.reservation_time, false); if (newDayId == null) continue; const newEndDayId = r.reservation_end_time - ? (resolveDayIdFromTime(tripId, r.reservation_end_time) ?? r.end_day_id) + ? (resolveDayIdFromTime(tripId, r.reservation_end_time, false) ?? r.end_day_id) : r.end_day_id; if (newDayId !== r.day_id || newEndDayId !== r.end_day_id) { update.run(newDayId, newEndDayId, r.id);