mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 14:21:46 +00:00
fix: delete linked budget item when accommodation or reservation is deleted (#933)
Deleting an accommodation or reservation now removes any budget item
linked via reservation_id, preventing orphan entries in the Budget page.
Also fixes a pre-existing payload-shape bug where budget:deleted was
broadcast with {id} instead of {itemId}, breaking live updates for
collaborators when a reservation price was cleared.
Tests added: ACCOM-006, RESV-009b, BUDGET-004b.
This commit is contained in:
@@ -189,6 +189,25 @@ describe('Delete budget item', () => {
|
||||
.set('Cookie', authCookie(user.id));
|
||||
expect(list.body.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('BUDGET-004b — DELETE budget item does NOT delete its linked reservation', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const reservation = createReservation(testDb, trip.id, { title: 'Hotel Booking', type: 'hotel' });
|
||||
|
||||
const result = testDb.prepare(
|
||||
'INSERT INTO budget_items (trip_id, name, category, total_price, reservation_id) VALUES (?, ?, ?, ?, ?)'
|
||||
).run(trip.id, 'Hotel Cost', 'Accommodation', 250, reservation.id);
|
||||
const itemId = result.lastInsertRowid as number;
|
||||
|
||||
const del = await request(app)
|
||||
.delete(`/api/trips/${trip.id}/budget/${itemId}`)
|
||||
.set('Cookie', authCookie(user.id));
|
||||
expect(del.status).toBe(200);
|
||||
|
||||
const reservationAfter = testDb.prepare('SELECT id FROM reservations WHERE id = ?').get(reservation.id);
|
||||
expect(reservationAfter).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -502,4 +502,46 @@ describe('Accommodations', () => {
|
||||
).get(reservationBefore.id);
|
||||
expect(reservationAfter).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ACCOM-006 — DELETE accommodation also removes its linked budget item (issue #933)', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id, { title: 'Hotel Budget Trip' });
|
||||
const day1 = createDay(testDb, trip.id, { date: '2026-11-01' });
|
||||
const day2 = createDay(testDb, trip.id, { date: '2026-11-03' });
|
||||
const place = createPlace(testDb, trip.id, { name: 'Grand Hotel' });
|
||||
|
||||
// Create a hotel reservation that creates an accommodation and a linked budget item
|
||||
const createRes = await request(app)
|
||||
.post(`/api/trips/${trip.id}/reservations`)
|
||||
.set('Cookie', authCookie(user.id))
|
||||
.send({
|
||||
title: 'Grand Hotel Stay',
|
||||
type: 'hotel',
|
||||
day_id: day1.id,
|
||||
create_accommodation: { place_id: place.id, start_day_id: day1.id, end_day_id: day2.id },
|
||||
create_budget_entry: { total_price: 450, category: 'Accommodation' },
|
||||
});
|
||||
expect(createRes.status).toBe(201);
|
||||
|
||||
const accommodationId = testDb.prepare(
|
||||
'SELECT id FROM day_accommodations WHERE trip_id = ?'
|
||||
).get(trip.id) as any;
|
||||
expect(accommodationId).toBeDefined();
|
||||
|
||||
const budgetBefore = testDb.prepare(
|
||||
'SELECT id FROM budget_items WHERE trip_id = ?'
|
||||
).get(trip.id);
|
||||
expect(budgetBefore).toBeDefined();
|
||||
|
||||
// Delete via the accommodation endpoint (the primary bug path)
|
||||
const delRes = await request(app)
|
||||
.delete(`/api/trips/${trip.id}/accommodations/${accommodationId.id}`)
|
||||
.set('Cookie', authCookie(user.id));
|
||||
expect(delRes.status).toBe(200);
|
||||
|
||||
const budgetAfter = testDb.prepare(
|
||||
'SELECT id FROM budget_items WHERE trip_id = ?'
|
||||
).get(trip.id);
|
||||
expect(budgetAfter).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -452,4 +452,41 @@ describe('Reservation accommodation delete', () => {
|
||||
).get(accom.id);
|
||||
expect(accomAfter).toBeUndefined();
|
||||
});
|
||||
|
||||
it('RESV-009b — DELETE reservation linked to accommodation also removes its linked budget item (issue #933)', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const day1 = createDay(testDb, trip.id, { date: '2025-08-01' });
|
||||
const day2 = createDay(testDb, trip.id, { date: '2025-08-03' });
|
||||
const place = createPlace(testDb, trip.id, { name: 'Seaside Resort' });
|
||||
|
||||
const createRes = await request(app)
|
||||
.post(`/api/trips/${trip.id}/reservations`)
|
||||
.set('Cookie', authCookie(user.id))
|
||||
.send({
|
||||
title: 'Seaside Resort Stay',
|
||||
type: 'hotel',
|
||||
day_id: day1.id,
|
||||
create_accommodation: { place_id: place.id, start_day_id: day1.id, end_day_id: day2.id },
|
||||
create_budget_entry: { total_price: 320, category: 'Accommodation' },
|
||||
});
|
||||
expect(createRes.status).toBe(201);
|
||||
const reservationId = createRes.body.reservation.id;
|
||||
|
||||
const budgetBefore = testDb.prepare(
|
||||
'SELECT id FROM budget_items WHERE trip_id = ? AND reservation_id = ?'
|
||||
).get(trip.id, reservationId);
|
||||
expect(budgetBefore).toBeDefined();
|
||||
|
||||
// Delete via the reservation endpoint
|
||||
const delRes = await request(app)
|
||||
.delete(`/api/trips/${trip.id}/reservations/${reservationId}`)
|
||||
.set('Cookie', authCookie(user.id));
|
||||
expect(delRes.status).toBe(200);
|
||||
|
||||
const budgetAfter = testDb.prepare(
|
||||
'SELECT id FROM budget_items WHERE trip_id = ?'
|
||||
).get(trip.id);
|
||||
expect(budgetAfter).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user