diff --git a/apps/edr-freight-api/src/modules/first-mile/first-mile.service.ts b/apps/edr-freight-api/src/modules/first-mile/first-mile.service.ts index 7cdf6398b..5c12d94ee 100644 --- a/apps/edr-freight-api/src/modules/first-mile/first-mile.service.ts +++ b/apps/edr-freight-api/src/modules/first-mile/first-mile.service.ts @@ -208,6 +208,7 @@ export class FirstMileService { originYard: true, destinationYard: true, cargoType: true, + bookingContainers: { containerType: true, units: true }, }, vehicle: true, vehicleAssignments: { vehicle: true }, @@ -255,6 +256,7 @@ export class FirstMileService { originYard: true, destinationYard: true, cargoType: true, + bookingContainers: { containerType: true, units: true }, }, vehicle: true, vehicleAssignments: { vehicle: true }, diff --git a/apps/edr-freight-api/src/modules/last-mile/last-mile.service.ts b/apps/edr-freight-api/src/modules/last-mile/last-mile.service.ts index a42ce289a..22f25a6aa 100644 --- a/apps/edr-freight-api/src/modules/last-mile/last-mile.service.ts +++ b/apps/edr-freight-api/src/modules/last-mile/last-mile.service.ts @@ -170,7 +170,7 @@ export class LastMileService { const [data, total] = await this.lastMileRepository.findAndCount({ where, relations: { - booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true, bookingContainers: { containerType: true } }, + booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true, bookingContainers: { containerType: true, units: true } }, vehicle: true, vehicleAssignments: { vehicle: true }, }, @@ -195,7 +195,7 @@ export class LastMileService { async findById(id: string): Promise { const record = await this.lastMileRepository.findById(id, { relations: { - booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true, bookingContainers: { containerType: true } }, + booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true, bookingContainers: { containerType: true, units: true } }, vehicle: true, vehicleAssignments: { vehicle: true }, }, diff --git a/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx b/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx index c3dac97d8..56d84470b 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx @@ -116,11 +116,25 @@ const vehicleLabel = (record: FirstMileRecord) => { const isAssigned = (record: FirstMileRecord) => Boolean(record.vehicleId) || Boolean(record.vehicleAssignments?.length); -/** Container numbers on a booking, in line order (skips lines without one). */ -const bookingContainerNumbers = (record: FirstMileRecord): string[] => - (record.booking?.bookingContainers ?? []) - .map((c) => c.containerNumber) - .filter((n): n is string => Boolean(n)); +/** Real per-physical-container numbers on a booking, in order. Prefers each + * line's `units` (the actual numbers) over the line-level number (often a + * "TBD-…" placeholder). One entry per physical container, for per-truck prefill. */ +const bookingContainerNumbers = (record: FirstMileRecord): string[] => { + const out: string[] = []; + const real = (n?: string | null): n is string => + Boolean(n) && !/^TBD/i.test(n!.trim()); + for (const c of record.booking?.bookingContainers ?? []) { + const units = [...(c.units ?? [])].sort( + (a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0), + ); + if (units.length) { + for (const u of units) if (real(u.containerNumber)) out.push(u.containerNumber!); + } else if (real(c.containerNumber)) { + out.push(c.containerNumber!); + } + } + return out; +}; /** Progress stages of the first-mile pickup workflow, for the detail stepper. */ const computeFirstMileSteps = (record: FirstMileRecord): LastMileStepState[] => { diff --git a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx index d7545fd7b..a43790346 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx @@ -128,11 +128,25 @@ const requiredVehicles = (record: LastMileRecord) => { return n > 0 ? Math.ceil(n / CONTAINERS_PER_VEHICLE) : 0; }; -/** Container numbers on a booking, in line order (skips lines without one). */ -const bookingContainerNumbers = (record: LastMileRecord): string[] => - (record.booking?.bookingContainers ?? []) - .map((c) => c.containerNumber) - .filter((n): n is string => Boolean(n)); +/** Real per-physical-container numbers on a booking, in order. Prefers each + * line's `units` (the actual numbers) over the line-level number (often a + * "TBD-…" placeholder). One entry per physical container, for per-truck prefill. */ +const bookingContainerNumbers = (record: LastMileRecord): string[] => { + const out: string[] = []; + const real = (n?: string | null): n is string => + Boolean(n) && !/^TBD/i.test(n!.trim()); + for (const c of record.booking?.bookingContainers ?? []) { + const units = [...(c.units ?? [])].sort( + (a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0), + ); + if (units.length) { + for (const u of units) if (real(u.containerNumber)) out.push(u.containerNumber!); + } else if (real(c.containerNumber)) { + out.push(c.containerNumber!); + } + } + return out; +}; /** Container badges for a booking: the container number when known, else the * type × quantity. */ diff --git a/apps/edr-freight-web/backoffice/src/services/first-mile.service.ts b/apps/edr-freight-web/backoffice/src/services/first-mile.service.ts index ab5dfb7d9..6fc80a282 100644 --- a/apps/edr-freight-web/backoffice/src/services/first-mile.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/first-mile.service.ts @@ -30,6 +30,9 @@ export interface FirstMileBooking { containerNumber?: string | null; containerSize?: string | null; containerType?: { id: string; name?: string; label?: string; code?: string } | null; + /** Physical containers under this line — their real numbers (line-level + * containerNumber is often a TBD placeholder). */ + units?: Array<{ id: string; containerNumber?: string | null; sortOrder?: number }>; }>; } diff --git a/apps/edr-freight-web/backoffice/src/services/last-mile.service.ts b/apps/edr-freight-web/backoffice/src/services/last-mile.service.ts index 2f8a1cec5..88e4bb9c0 100644 --- a/apps/edr-freight-web/backoffice/src/services/last-mile.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/last-mile.service.ts @@ -30,6 +30,9 @@ export interface LastMileBooking { containerNumber?: string | null; containerSize?: string | null; containerType?: { id: string; name?: string; label?: string; code?: string } | null; + /** Physical containers under this line — their real numbers (line-level + * containerNumber is often a TBD placeholder). */ + units?: Array<{ id: string; containerNumber?: string | null; sortOrder?: number }>; }>; }