fix(reservations): a booking read on its own carries its day slots, so an edit no longer drops them

The create and update answers, the reservation:updated broadcast and the MCP
reservation tools all hand back the single booking read, and that read never
loaded reservation_day_positions. The day list puts a booking moved on a day by
that slot before its time, and the client replaces the booking with the answer,
so a booking moved on a day jumped back to its time after every edit until the
next reload. The read now attaches day_positions in the shape the trip list
already sends.

Refs #2631
This commit is contained in:
Maurice
2026-10-12 12:32:24 +02:00
parent c2dd66986c
commit eaab7dc4e4
12 changed files with 108 additions and 7 deletions
@@ -5,6 +5,7 @@
import { db as testDb } from '../../../src/db/database';
import { BudgetItems } from '../../../src/db/entities/BudgetItems.entity';
import { DayAccommodations } from '../../../src/db/entities/DayAccommodations.entity';
import { ReservationDayPositions } from '../../../src/db/entities/ReservationDayPositions.entity';
import { ReservationTravelers } from '../../../src/db/entities/ReservationTravelers.entity';
import { Reservations } from '../../../src/db/entities/Reservations.entity';
import { Trips } from '../../../src/db/entities/Trips.entity';
@@ -293,6 +294,26 @@ describe('Tool: update_reservation', () => {
expect(events.indexOf('budget:updated')).toBeLessThan(events.indexOf('reservation:updated'));
});
// #2631: the day planner puts a booking moved on a day by its day slot, and the
// edit's answer and broadcast replace the booking in every open list.
it('keeps the day slots in the answer and the broadcast', async () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id);
const day = createDay(testDb, trip.id);
const reservation = createReservation(testDb, trip.id, { title: 'Bus', type: 'bus' });
await insertRow(orm, ReservationDayPositions, { reservation: reservation.id, day: day.id, position: 2.5 });
await withHarness(user.id, async (h) => {
const result = await h.client.callTool({
name: 'update_reservation',
arguments: { tripId: trip.id, reservationId: reservation.id, notes: 'aisle' },
});
type Answer = { reservation: { day_positions: Record<string, number> | null } };
expect((parseToolResult(result) as Answer).reservation.day_positions).toEqual({ [day.id]: 2.5 });
const sent = broadcastMock.mock.calls.find((c) => c[1] === 'reservation:updated')?.[2] as Answer;
expect(sent.reservation.day_positions).toEqual({ [day.id]: 2.5 });
});
});
it('returns error for reservation not found', async () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id);