From 96a4dd2e7fdb841facbd160d92a450b03ca144ac Mon Sep 17 00:00:00 2001 From: Marshal Date: Thu, 6 Aug 2026 22:34:53 +0000 Subject: [PATCH] exclude self from container clash --- .../src/modules/contracts/contract-booking.service.ts | 4 ++++ .../src/modules/contracts/contracts.controller.ts | 5 ++++- .../src/components/contracts/GlCreateBookingForm.tsx | 2 +- .../backoffice/src/services/contracts.service.ts | 10 +++++++++- .../portal/src/pages/contracts/NewShipmentPage.tsx | 7 ++++++- apps/edr-freight-web/portal/src/services/api.ts | 10 +++++++--- .../portal/src/services/contracts.service.ts | 7 ++++++- 7 files changed, 37 insertions(+), 8 deletions(-) diff --git a/apps/edr-freight-api/src/modules/contracts/contract-booking.service.ts b/apps/edr-freight-api/src/modules/contracts/contract-booking.service.ts index 1c152d795..1147c018b 100644 --- a/apps/edr-freight-api/src/modules/contracts/contract-booking.service.ts +++ b/apps/edr-freight-api/src/modules/contracts/contract-booking.service.ts @@ -1882,6 +1882,9 @@ export class ContractBookingService { async validateShipment( contractId: string, dto: CreateBookingUnderContractDto, + // Completion/resubmit preview: the booking being completed must not clash + // with its own persisted containers. + excludeBookingId?: string, ): Promise<{ overweightLines: Array<{ containerTypeCode: string; @@ -2043,6 +2046,7 @@ export class ContractBookingService { originYardId: route?.originYardId, destinationYardId: route?.destinationYardId, }, + excludeBookingId, ); containerClashErrors = clashes.map( (c) => diff --git a/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts b/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts index ec61fdd4a..706d3cdee 100644 --- a/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts +++ b/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts @@ -1124,8 +1124,11 @@ export class ContractsController { validateShipment( @Param('id', ParseUUIDPipe) id: string, @Body() dto: CreateBookingUnderContractDto, + // Completion/resubmit preview: exclude this booking's own persisted + // containers from the same-train clash check. + @Query('bookingId') bookingId?: string, ) { - return this.contractBookingService.validateShipment(id, dto); + return this.contractBookingService.validateShipment(id, dto, bookingId); } @Get(':id/capacity') diff --git a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx index 52f7ee40d..42ecbce61 100644 --- a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx +++ b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx @@ -961,7 +961,7 @@ export default function GlCreateBookingForm() { // modal falls back to the contract unit-rate estimate while it loads. const validateShipmentMutation = useMutation({ mutationFn: (dto: Freight.CreateBookingUnderContractDto) => - contractsService.validateShipment(id ?? "", dto), + contractsService.validateShipment(id ?? "", dto, completeBookingId), }); const validation = validateShipmentMutation.data ?? null; diff --git a/apps/edr-freight-web/backoffice/src/services/contracts.service.ts b/apps/edr-freight-web/backoffice/src/services/contracts.service.ts index c0a397d7f..37971cb96 100644 --- a/apps/edr-freight-web/backoffice/src/services/contracts.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/contracts.service.ts @@ -673,8 +673,16 @@ export const contractsService = { validateShipment: ( id: string, payload: Freight.CreateBookingUnderContractDto, + // Completion/resubmit preview: exclude this booking's own containers from + // the same-train clash check. + excludeBookingId?: string, ) => - postContract(C.VALIDATE_SHIPMENT(id), payload), + postContract( + excludeBookingId + ? `${C.VALIDATE_SHIPMENT(id)}?bookingId=${excludeBookingId}` + : C.VALIDATE_SHIPMENT(id), + payload, + ), /** Remaining bookable quantity per cargo line (GENERAL draw-down cap). */ getCapacity: async (id: string): Promise => { diff --git a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx index aab5d7de1..80fcabc8b 100644 --- a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx @@ -483,7 +483,12 @@ function NewShipmentBookingForm({ // price modal opens so re-reviewing after an edit re-checks. const validateMutation = useMutation({ mutationFn: (dto: Freight.CreateBookingUnderContractDto) => - api.contracts.validateShipment.call({ id: contractId, dto }), + api.contracts.validateShipment.call({ + id: contractId, + dto, + // Resubmit preview must not clash with this booking's own containers. + excludeBookingId: completeBookingId, + }), }); function buildDto( diff --git a/apps/edr-freight-web/portal/src/services/api.ts b/apps/edr-freight-web/portal/src/services/api.ts index 7546fedbc..3d00a5f16 100644 --- a/apps/edr-freight-web/portal/src/services/api.ts +++ b/apps/edr-freight-web/portal/src/services/api.ts @@ -610,10 +610,14 @@ export const api = { ), validateShipment: endpoint< - { id: string; dto: Freight.CreateBookingUnderContractDto }, + { + id: string; + dto: Freight.CreateBookingUnderContractDto; + excludeBookingId?: string; + }, ShipmentValidation - >("contracts", "validateShipment", ({ id, dto }) => - contractsService.validateShipment(id, dto), + >("contracts", "validateShipment", ({ id, dto, excludeBookingId }) => + contractsService.validateShipment(id, dto, excludeBookingId), ), getContractMilestones: endpoint< diff --git a/apps/edr-freight-web/portal/src/services/contracts.service.ts b/apps/edr-freight-web/portal/src/services/contracts.service.ts index 2ce09d4cc..3842296ef 100644 --- a/apps/edr-freight-web/portal/src/services/contracts.service.ts +++ b/apps/edr-freight-web/portal/src/services/contracts.service.ts @@ -400,8 +400,13 @@ export const contractsService = { validateShipment: async ( id: string, dto: Freight.CreateBookingUnderContractDto, + // Completion/resubmit: exclude this booking's own containers from the + // same-train clash check. + excludeBookingId?: string, ): Promise => { - const { data } = await client.post(C.VALIDATE_SHIPMENT(id), dto); + const { data } = await client.post(C.VALIDATE_SHIPMENT(id), dto, { + params: excludeBookingId ? { bookingId: excludeBookingId } : undefined, + }); return data.data ?? data; },