From cee3a4f429d12719da081b0364d6812fdf9f6883 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Thu, 23 Jul 2026 12:02:57 +0000 Subject: [PATCH] disputes only before acknowledgement, registered disputes immutable (view-only), cancel action removed end-to-end, and internal UUIDs removed from the document, detail view, and list. --- .../interchange-documents.controller.ts | 7 ---- .../interchange-documents.service.ts | 18 +++----- .../src/hooks/useInterchangeDocuments.ts | 7 ---- .../warehouses/InterchangeDocumentsPage.tsx | 41 ++++--------------- .../services/interchange-documents.service.ts | 2 - 5 files changed, 13 insertions(+), 62 deletions(-) diff --git a/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.controller.ts b/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.controller.ts index 03797f3b7..61a13744e 100644 --- a/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.controller.ts +++ b/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.controller.ts @@ -54,11 +54,4 @@ export class InterchangeDocumentsController { dispute(@Param('id', ParseUUIDPipe) id: string, @Body() dto: DisputeInterchangeDocumentDto) { return this.service.dispute(id, dto); } - - @Patch(':id/cancel') - @BookingStaff(FREIGHT_PERMS.interchangeDocuments.cancel) - @ApiOperation({ summary: 'Cancel a draft/generated interchange document' }) - cancel(@Param('id', ParseUUIDPipe) id: string) { - return this.service.cancel(id); - } } diff --git a/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.service.ts b/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.service.ts index f91e942c0..e5fb5dc4d 100644 --- a/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.service.ts +++ b/apps/edr-freight-api/src/modules/interchange-documents/interchange-documents.service.ts @@ -203,11 +203,12 @@ export class InterchangeDocumentsService { async dispute(id: string, dto: DisputeInterchangeDocumentDto): Promise { const document = await this.findOne(id); - // A dispute can only be raised on a live handover — a GENERATED or already - // ACKNOWLEDGED document. CANCELLED and already-DISPUTED are terminal here. - if (!['GENERATED', 'ACKNOWLEDGED'].includes(document.status)) { + // A dispute can only be raised BEFORE the handover is acknowledged — an + // acknowledged document is settled. DISPUTED itself is terminal and + // read-only: the registered dispute cannot be re-raised or overwritten. + if (document.status !== 'GENERATED') { throw new BadRequestException( - `Interchange document in ${document.status} status cannot be disputed (must be GENERATED or ACKNOWLEDGED)`, + `Interchange document in ${document.status} status cannot be disputed (must be GENERATED — an acknowledged handover is settled, a registered dispute is read-only)`, ); } await this.dataSource.getRepository(InterchangeDocument).update(id, { @@ -217,15 +218,6 @@ export class InterchangeDocumentsService { return this.findOne(id); } - async cancel(id: string): Promise { - const document = await this.findOne(id); - if (!['DRAFT', 'GENERATED'].includes(document.status)) { - throw new BadRequestException(`Interchange document ${document.status} cannot be cancelled`); - } - await this.dataSource.getRepository(InterchangeDocument).update(id, { status: 'CANCELLED' }); - return this.findOne(id); - } - private async getScheduleSnapshot(scheduleId: string): Promise { const [schedule] = await this.dataSource.query( `SELECT ts.id, diff --git a/apps/edr-freight-web/backoffice/src/hooks/useInterchangeDocuments.ts b/apps/edr-freight-web/backoffice/src/hooks/useInterchangeDocuments.ts index 67cf70f24..ba1f10b73 100644 --- a/apps/edr-freight-web/backoffice/src/hooks/useInterchangeDocuments.ts +++ b/apps/edr-freight-web/backoffice/src/hooks/useInterchangeDocuments.ts @@ -67,10 +67,3 @@ export function useDisputeInterchangeDocument() { }); } -export function useCancelInterchangeDocument() { - const onSuccess = useInterchangeInvalidation(); - return useMutation({ - mutationFn: (id: string) => interchangeDocumentsService.cancel(id), - onSuccess, - }); -} diff --git a/apps/edr-freight-web/backoffice/src/pages/warehouses/InterchangeDocumentsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/warehouses/InterchangeDocumentsPage.tsx index 4bd647106..04fe38970 100644 --- a/apps/edr-freight-web/backoffice/src/pages/warehouses/InterchangeDocumentsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/warehouses/InterchangeDocumentsPage.tsx @@ -12,7 +12,7 @@ import { Text, TextInput, } from '@mantine/core'; -import { CheckCircle2, Download, Eye, FileText, Printer, Search, XCircle } from 'lucide-react'; +import { CheckCircle2, Download, Eye, FileText, Printer, Search } from 'lucide-react'; import type { ReactNode } from 'react'; import { DataTable, type ColumnDef } from '@edr/ui-common'; @@ -21,7 +21,6 @@ import { PageContainer, PageHeader } from '@/components/page'; import { VisualEmptyState, formatDate, formatNumber } from '@/components/warehouses'; import { useAcknowledgeInterchangeDocument, - useCancelInterchangeDocument, useDisputeInterchangeDocument, useInterchangeDocument, useInterchangeDocuments, @@ -66,7 +65,7 @@ const buildPrintableInterchangeHtml = (document: InterchangeDocument) => { (item, index) => ` ${index + 1} - ${escapeHtml(item.bookingReference ?? item.bookingId?.slice(0, 8))} + ${escapeHtml(item.bookingReference)} ${escapeHtml(item.itemType)} ${escapeHtml(item.containerNumber)} ${escapeHtml(item.sealNumber)} @@ -118,7 +117,6 @@ const buildPrintableInterchangeHtml = (document: InterchangeDocument) => {
Direction
${escapeHtml(document.direction)}
Train No
${escapeHtml(document.trainNo)}
-
Schedule
${escapeHtml(document.scheduleId)}
Handover Location
${escapeHtml(document.handoverLocation)}
Handover From
${escapeHtml(document.handoverFrom)}
Handover To
${escapeHtml(document.handoverTo)}
@@ -191,7 +189,6 @@ function InterchangeDocumentDetail({ id }: { id: string }) { - @@ -232,7 +229,7 @@ function InterchangeDocumentDetail({ id }: { id: string }) { {items.map((item) => ( - {item.bookingReference ?? item.bookingId?.slice(0, 8) ?? '-'} + {item.bookingReference ?? '-'} {item.itemType} {item.containerNumber ?? '-'} {item.sealNumber ?? '-'} @@ -268,7 +265,6 @@ export default function InterchangeDocumentsPage() { const { data: documents = [], isLoading } = useInterchangeDocuments(filter); const acknowledge = useAcknowledgeInterchangeDocument(); const dispute = useDisputeInterchangeDocument(); - const cancel = useCancelInterchangeDocument(); const getPrintableDocument = async (interchangeDocument: InterchangeDocument) => { if (interchangeDocument.items?.length) return interchangeDocument; @@ -336,19 +332,7 @@ export default function InterchangeDocumentsPage() { ), }, { id: 'direction', header: 'Direction', cell: ({ row }) => row.original.direction }, - { - id: 'train', - header: 'Train No / Schedule', - cell: ({ row }) => ( - - {row.original.trainNo ?? '-'} - - {row.original.scheduleId?.slice(0, 8) ?? '-'} - - - ), - }, - { id: 'route', header: 'Route', cell: ({ row }) => row.original.routeId?.slice(0, 8) ?? '-' }, + { id: 'train', header: 'Train No', cell: ({ row }) => row.original.trainNo ?? '-' }, { id: 'handoverLocation', header: 'Handover Location', cell: ({ row }) => row.original.handoverLocation }, { id: 'handoverFrom', header: 'Handover From', cell: ({ row }) => row.original.handoverFrom }, { id: 'handoverTo', header: 'Handover To', cell: ({ row }) => row.original.handoverTo }, @@ -390,7 +374,7 @@ export default function InterchangeDocumentsPage() { > View - {doc.status !== 'ACKNOWLEDGED' && doc.status !== 'CANCELLED' ? ( + {doc.status === 'GENERATED' ? ( - ) : null} ); }, diff --git a/apps/edr-freight-web/backoffice/src/services/interchange-documents.service.ts b/apps/edr-freight-web/backoffice/src/services/interchange-documents.service.ts index 6e2fd3474..0996b053d 100644 --- a/apps/edr-freight-web/backoffice/src/services/interchange-documents.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/interchange-documents.service.ts @@ -28,6 +28,4 @@ export const interchangeDocumentsService = { apiClient.patch(URL_CONSTANTS.INTERCHANGE_DOCUMENTS.ACKNOWLEDGE(id), payload), dispute: (id: string, payload: { remarks: string }) => apiClient.patch(URL_CONSTANTS.INTERCHANGE_DOCUMENTS.DISPUTE(id), payload), - cancel: (id: string) => - apiClient.patch(URL_CONSTANTS.INTERCHANGE_DOCUMENTS.CANCEL(id), {}), };