diff --git a/client/src/components/Planner/ReservationModal.test.tsx b/client/src/components/Planner/ReservationModal.test.tsx
index b9d6a7922..a051bfb7c 100644
--- a/client/src/components/Planner/ReservationModal.test.tsx
+++ b/client/src/components/Planner/ReservationModal.test.tsx
@@ -1,4 +1,4 @@
-// FE-PLANNER-RESMODAL-001 to FE-PLANNER-RESMODAL-105
+// FE-PLANNER-RESMODAL-001 to FE-PLANNER-RESMODAL-106
import { render, screen, waitFor, fireEvent, within, act } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
@@ -1409,6 +1409,38 @@ describe('ReservationModal', () => {
expect(onSave.mock.calls[0][0].create_budget_entry).toEqual({ total_price: 50, category: 'accommodation' });
});
+ it('FE-PLANNER-RESMODAL-106: an import with source files but no upload handler still saves and opens the expense editor', async () => {
+ // Without onFileUpload the pending source files cannot go anywhere; the save
+ // used to call the missing handler, reject, and never reach the expense step.
+ budgetEnabled();
+ const onSave = vi.fn().mockResolvedValue({ id: 84 });
+ const onOpenExpense = vi.fn();
+ const prefill = hotelPrefill({
+ type: 'restaurant',
+ metadata: {},
+ _accommodation: undefined,
+ _sourceFiles: [new File(['pdf'], 'voucher.pdf', { type: 'application/pdf' })],
+ });
+ render(
+
+ );
+
+ await userEvent.click(screen.getByRole('button', { name: /Create expense/i }));
+ await waitFor(() => expect(onSave).toHaveBeenCalled());
+ await waitFor(() =>
+ expect(onOpenExpense).toHaveBeenCalledWith(
+ expect.objectContaining({ prefill: expect.objectContaining({ reservationId: 84 }) })
+ )
+ );
+ });
+
// ── File error paths ────────────────────────────────────────────────────────
it('FE-PLANNER-RESMODAL-077: a cancelled file dialog changes nothing', () => {
diff --git a/client/src/components/Planner/bookingFormModel.test.ts b/client/src/components/Planner/bookingFormModel.test.ts
index c6be97fc7..014d5c6b0 100644
--- a/client/src/components/Planner/bookingFormModel.test.ts
+++ b/client/src/components/Planner/bookingFormModel.test.ts
@@ -1,4 +1,4 @@
-// FE-PLANNER-BOOKINGFORM-001 to -007: the travellers, picked files, attached files
+// FE-PLANNER-BOOKINGFORM-001 to -008: the travellers, picked files, attached files
// and expense requests of the transport and booking forms, desktop and phone alike.
import { typeToCostCategory } from '@trek/shared';
import { describe, expect, it, vi } from 'vitest';
@@ -60,6 +60,10 @@ describe('bookingFormModel', () => {
expect(upload).not.toHaveBeenCalled();
});
+ it('FE-PLANNER-BOOKINGFORM-008: without an upload handler the picked files are skipped and the save is not rejected', async () => {
+ await expect(uploadBookingFiles(undefined, 42, [new File(['a'], 'a.pdf')], 'Hotel stay')).resolves.toBeUndefined();
+ });
+
it('FE-PLANNER-BOOKINGFORM-005: an expense wish opens the editor for the saved booking, nothing else does', () => {
const booking = { title: 'Hotel Adlon', type: 'hotel' };
const item = buildBudgetItem({ id: 7 });
diff --git a/client/src/components/Planner/bookingFormModel.ts b/client/src/components/Planner/bookingFormModel.ts
index eab9b82b2..71a952afe 100644
--- a/client/src/components/Planner/bookingFormModel.ts
+++ b/client/src/components/Planner/bookingFormModel.ts
@@ -34,13 +34,16 @@ export function travelersChanged(reservation: Pick | n
* the other, each described with the booking's title. Callers run it after both a
* create and an edit: the form only holds files the user just picked, so nothing is
* uploaded twice, and skipping the edit dropped them without a word (#2534).
+ * Without an upload handler (a dialog opened with no file support) there is
+ * nowhere to send them, so nothing is uploaded and the save carries on.
*/
export async function uploadBookingFiles(
- upload: (fd: FormData) => Promise,
+ upload: ((fd: FormData) => Promise) | undefined,
savedId: number,
files: File[],
description: string
): Promise {
+ if (!upload) return;
for (const file of files) {
const fd = new FormData();
fd.append('file', file);