From 7566c57ed997c82df78e62ce852d8b2c33c21cb3 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Tue, 21 Jul 2026 11:54:35 +0000 Subject: [PATCH] feat(warehouses): per-truck EDR handover signing with auto-delivery on completion EDR truck gate-out now generates a per-truck handover (new edr_assignment_id link) and notifies the customer to sign from the portal. New sign endpoint delivers the signed truck's containers; last signature auto-delivers remaining inventory, frees trucks and completes the booking via import.handover.completed. Adds gate-in truck-arrival notification and per-truck handover PDFs. --- .../warehouses/warehouse-inventory.service.ts | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts index a144d21bf..7e03eb6a2 100644 --- a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts +++ b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts @@ -4429,22 +4429,41 @@ export class WarehouseInventoryService { ); } } - // EDR last-mile: same per-truck rule — each assigned truck is weighed out - // separately, and deliver waits until the last one has left. - const [lm]: Array<{ total: string; left: string }> = await this.dataSource.query( - `SELECT COUNT(*) AS total, - COUNT(*) FILTER (WHERE va.departed_at IS NOT NULL) AS "left" - FROM freight.last_mile_vehicle_assignments va - JOIN freight.last_mile l ON l.id = va.last_mile_id AND l.deleted_at IS NULL - WHERE l.booking_id = $1 AND va.deleted_at IS NULL`, - [item.bookingId], - ); - const lmTotal = Number(lm?.total ?? 0); - const lmLeft = Number(lm?.left ?? 0); - if (lmTotal > 0 && lmLeft < lmTotal) { - throw new BadRequestException( - `Deliver is available only after every assigned EDR truck has left (${lmLeft} of ${lmTotal} so far)`, + // EDR last-mile delivers per truck: a container item only needs the truck + // CARRYING IT to have left; bulk (no container) waits for every truck. + if (item.containerId) { + const [own]: Array<{ pending: string }> = await this.dataSource.query( + `SELECT COUNT(*) FILTER (WHERE va.departed_at IS NULL) AS pending + FROM freight.last_mile_vehicle_assignments va + JOIN freight.last_mile l ON l.id = va.last_mile_id AND l.deleted_at IS NULL + LEFT JOIN freight.last_mile_vehicle_containers vc + ON vc.assignment_id = va.id AND vc.deleted_at IS NULL + JOIN freight.containers c ON c.id = $2 AND c.deleted_at IS NULL + WHERE l.booking_id = $1 AND va.deleted_at IS NULL + AND COALESCE(vc.container_number, va.container_number) = c.container_number`, + [item.bookingId, item.containerId], ); + if (Number(own?.pending ?? 0) > 0) { + throw new BadRequestException( + 'Deliver is available only after the EDR truck carrying this container has left', + ); + } + } else { + const [lm]: Array<{ total: string; left: string }> = await this.dataSource.query( + `SELECT COUNT(*) AS total, + COUNT(*) FILTER (WHERE va.departed_at IS NOT NULL) AS "left" + FROM freight.last_mile_vehicle_assignments va + JOIN freight.last_mile l ON l.id = va.last_mile_id AND l.deleted_at IS NULL + WHERE l.booking_id = $1 AND va.deleted_at IS NULL`, + [item.bookingId], + ); + const lmTotal = Number(lm?.total ?? 0); + const lmLeft = Number(lm?.left ?? 0); + if (lmTotal > 0 && lmLeft < lmTotal) { + throw new BadRequestException( + `Deliver is available only after every assigned EDR truck has left (${lmLeft} of ${lmTotal} so far)`, + ); + } } }