From 11b1399b5cfce4f1aab9f43ad8bf6ef9efbec3f1 Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 21 Sep 2026 20:54:23 +0200 Subject: [PATCH] fix(pdf): list a day the way the plan lists it, without the booked night's stop Booking a night writes the hotel onto its check-in day as a stop, and since 4.3.1 that stop heads its day. The day plan hides it and shows the booking as the day's accommodation block, but the desktop toolbar hands the export the store's own assignments, filter and all, so the PDF printed the hotel place above a flight that left in the morning. The export now drops that stop before it merges the day, as the list does. The mobile export already received the filtered set and was right all along. Closes #2434 --- client/src/components/PDF/TripPDF.test.ts | 18 ++++++++++++++++++ client/src/components/PDF/TripPDF.tsx | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/client/src/components/PDF/TripPDF.test.ts b/client/src/components/PDF/TripPDF.test.ts index 35e9acba0..60a9e4921 100644 --- a/client/src/components/PDF/TripPDF.test.ts +++ b/client/src/components/PDF/TripPDF.test.ts @@ -253,6 +253,24 @@ describe('downloadTripPDF', () => { expect(iframe!.srcdoc).toContain('Air Italia · AI123 · CDG → FCO') }) + it('FE-COMP-TRIPPDF-013f: the stop a booked night wrote is not listed, so a morning flight prints before the hotel (#2434)', async () => { + // The desktop toolbar hands the export the store's assignments, hidden stop and + // all, and that stop heads its check-in day. The day plan hides it and shows the + // booking as the accommodation block, and the print lists what the plan lists. + const hotelPlace = { id: 101, name: 'Hotel Hafen Hamburg', address: 'Seewartenstr. 9', place_time: null } as any + const hotelStop = { id: 201, day_id: 10, place_id: 101, order_index: 0, accommodation_id: 30, place: hotelPlace } + const flight = { ...transportReservation, title: 'Morning flight', reservation_time: '2025-06-01T08:00:00' } + await downloadTripPDF({ + ...richArgs, + assignments: { '10': [hotelStop, { ...assignmentForDay, order_index: 1 }] } as any, + reservations: [flight], + }) + const html = getIframe()!.srcdoc + expect(html).not.toContain('Seewartenstr. 9') + expect(html.indexOf('Morning flight')).toBeLessThan(html.indexOf('Colosseum')) + expect(html.indexOf('Morning flight')).toBeGreaterThan(-1) + }) + it('FE-COMP-TRIPPDF-013c: a flight that lands the same day shows both times (#1310)', async () => { const sameDay = { ...transportReservation, reservation_end_time: '2025-06-01T16:45:00' } await downloadTripPDF({ ...richArgs, reservations: [sameDay] }) diff --git a/client/src/components/PDF/TripPDF.tsx b/client/src/components/PDF/TripPDF.tsx index e1c480eb4..30bad18fe 100644 --- a/client/src/components/PDF/TripPDF.tsx +++ b/client/src/components/PDF/TripPDF.tsx @@ -335,7 +335,13 @@ export async function downloadTripPDF({ trip, days, places, assignments = {}, ca } // Build day HTML const daysHtml = sorted.map((day, di) => { - const assigned = (assignments[String(day.id)] || []).slice() + // Without the stop a booked night wrote onto its check-in day: the day plan hides + // it (the day already carries the booking as its accommodation block) and the + // export lists what the plan lists. The desktop toolbar hands the export the + // store's own assignments, filter and all, and the stop sits at the head of its + // day, so the hotel printed above a morning flight (#2434). + const assigned = (assignments[String(day.id)] || []) + .filter((a: any) => a.accommodation_id == null) .sort((a: any, b: any) => (a.order_index ?? 0) - (b.order_index ?? 0)) const notes = (dayNotes || []).filter(n => n.day_id === day.id).slice() .sort((a: any, b: any) => (a.sort_order ?? 0) - (b.sort_order ?? 0))