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 320bc8b72..59eba6982 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 @@ -1,6 +1,5 @@ import { BadRequestException, - ForbiddenException, Injectable, NotFoundException, } from '@nestjs/common'; @@ -192,18 +191,15 @@ export class ContractBookingService { */ private async assertGate(contract: Contract, isGlActor: boolean): Promise { if (contract.customsClearingEnabled) { - // Path B — GL Ethiopia only. - if (!isGlActor) { - throw new ForbiddenException( - 'Only Global Logistics Ethiopia can create bookings for customs-clearance contracts.', - ); - } + // Path B — the customer creates the booking once GL has finalized the + // pre-booking clearance (GL "create booking" was removed; clearance ends + // at finalize and hands the booking back to the customer). if (contract.clearanceStatus !== 'CLEARANCE_READY_FOR_BOOKING') { throw new BadRequestException( 'Contract clearance is not ready for booking yet.', ); } - return 'GL_ET'; + return isGlActor ? 'GL_ET' : 'CUSTOMER'; } // Path A — customer (or staff) once the contract is executed. diff --git a/apps/edr-freight-web/backoffice/src/components/contracts/ContractActionsToolbar.tsx b/apps/edr-freight-web/backoffice/src/components/contracts/ContractActionsToolbar.tsx index 9b6419240..d8f9f1269 100644 --- a/apps/edr-freight-web/backoffice/src/components/contracts/ContractActionsToolbar.tsx +++ b/apps/edr-freight-web/backoffice/src/components/contracts/ContractActionsToolbar.tsx @@ -12,7 +12,6 @@ import { Check, FileSignature, MessageSquareWarning, - PackagePlus, ShieldCheck, Sparkles, XCircle, @@ -21,8 +20,6 @@ import { import type { Freight } from "@edr/types"; import { SectionCard } from "@/components/bookings/detail/SectionCard"; -import { useAuth } from "@/auth/useAuth"; -import { canCreateContractBooking } from "@/lib/permissions"; import type { useContractMutations } from "@/hooks/contracts/useContracts"; type Mutations = ReturnType; @@ -49,7 +46,6 @@ export function ContractActionsToolbar({ onReviewClearance, }: ContractActionsToolbarProps) { const navigate = useNavigate(); - const { user } = useAuth(); const { status } = contract; const [acceptOpen, setAcceptOpen] = useState(false); @@ -85,10 +81,6 @@ export function ContractActionsToolbar({ const canViewContract = ["CONTRACT_READY", "SIGNED_CUSTOMER"].includes(status) && Boolean(contract.contractGeneratedAt); - const canCreateBooking = - status === "CLEARANCE_READY_FOR_BOOKING" && - contract.customsClearingEnabled && - canCreateContractBooking(user); // Show "Review clearance" while the contract is in the document-review phase. // Reviewer = GL (Path B / customs) or Operations (Path A / no customs). const canReviewClearance = @@ -174,23 +166,12 @@ export function ContractActionsToolbar({ )} - {canCreateBooking && ( - - )} + {/* GL "Create booking" removed for now — clearance ends at finalize and + the customer creates the booking in the portal. */} {!canAccept && !needsManualGenerate && !canViewContract && - !canCreateBooking && !canReviewClearance && ( No staff actions available for this status. Monitor until the diff --git a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx index 19e2c8cb9..63f011ea6 100644 --- a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx @@ -160,6 +160,7 @@ export default function ContractClearanceDetailPage() { contractId={id!} hideSummary selfClear={false} + readOnly={ready} /> diff --git a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceListPage.tsx b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceListPage.tsx index 304e6a69a..e9d83e20c 100644 --- a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceListPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceListPage.tsx @@ -145,7 +145,7 @@ function StatusBadge({ row }: { row: ClearanceRow }) { radius="sm" leftSection={} > - Ready — customer books + Clearance finalized ); diff --git a/apps/edr-freight-web/portal/src/pages/contracts/ContractClearancePanel.tsx b/apps/edr-freight-web/portal/src/pages/contracts/ContractClearancePanel.tsx index 42a0f799e..d4b530f5f 100644 --- a/apps/edr-freight-web/portal/src/pages/contracts/ContractClearancePanel.tsx +++ b/apps/edr-freight-web/portal/src/pages/contracts/ContractClearancePanel.tsx @@ -1,4 +1,5 @@ import { useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Alert, @@ -20,6 +21,7 @@ import { Download, Eye, FileText, + PackagePlus, Plus, Upload, } from "lucide-react"; @@ -85,6 +87,7 @@ export function ContractClearancePanel({ bare, }: ContractClearancePanelProps) { const queryClient = useQueryClient(); + const navigate = useNavigate(); const [pending, setPending] = useState>({}); const [adHoc, setAdHoc] = useState([]); const { view, viewer } = useFileViewer(); @@ -192,9 +195,8 @@ export function ContractClearancePanel({ )} {isReady ? ( } mb="md"> - {customsPath - ? "Your clearance documents are approved. Global Logistics will create your booking — you will be notified when payment is due." - : "Your clearance documents are approved. You can now create a shipment booking under this contract."} + Your clearance documents are approved. You can now create a shipment + booking under this contract. ) : isUnderReview ? ( } mb="md"> @@ -437,6 +439,20 @@ export function ContractClearancePanel({ )} + + {/* Clearance finalized → the customer creates the booking himself. */} + {isReady && status === "CLEARANCE_READY_FOR_BOOKING" && ( + + + + )} {viewer} ); diff --git a/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx b/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx index 34665bb4f..4185d3a18 100644 --- a/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx @@ -205,14 +205,21 @@ export default function ContractDetailPage() { const canSign = contract.status === "CONTRACT_READY"; const customsPath = contract.customsClearingEnabled; - // The customer creates the booking himself unless GL owns it (customs / Path B). - // Reached only once the contract is executed (after self-clearance on Path A). - const canBookShipment = - !customsPath && PATH_A_BOOKABLE.includes(contract.status); - // The customer uploads clearance documents on the contract while in a clearance - // status — Path B (customs, GL-reviewed) or Path A self-clearance (Operations). + // The customer creates the booking himself on BOTH paths: + // - Path A (no customs): once the contract is executed after self-clearance. + // - Path B (customs): once GL finalizes the pre-booking clearance + // (CLEARANCE_READY_FOR_BOOKING). GL "create booking" was removed. + const clearanceFinalized = + contract.clearanceStatus === "CLEARANCE_READY_FOR_BOOKING" || + contract.status === "CLEARANCE_READY_FOR_BOOKING"; + const canBookShipment = customsPath + ? clearanceFinalized + : PATH_A_BOOKABLE.includes(contract.status); + // The customer uploads clearance documents while in a clearance status — but + // once clearance is finalized the upload step is done; the action becomes + // "create booking" instead. const canUploadClearance = - CLEARANCE_UPLOAD_STATUSES.includes(contract.status); + CLEARANCE_UPLOAD_STATUSES.includes(contract.status) && !clearanceFinalized; return ( @@ -517,10 +524,10 @@ export default function ContractDetailPage() { {customsPath ? contract.status === "AWAITING_CLEARANCE_DOCUMENTS" - ? "Upload your clearance documents so Global Logistics can review them. After approval, Global Logistics creates your booking — you only pay the freight." + ? "Upload your clearance documents so Global Logistics can review them. Once they finalize the clearance you can create your shipment booking." : contract.status === "CLEARANCE_UNDER_REVIEW" ? "Global Logistics is reviewing your clearance documents. Re-upload any queried documents to proceed." - : "Your documents are cleared. Global Logistics will create your booking shortly — you will be notified when payment is due." + : "Your documents are cleared. You can now create a shipment booking under this contract." : contract.status === "AWAITING_CLEARANCE_DOCUMENTS" ? "This service does not include EDR customs clearance. Clear the cargo yourself and upload your clearance documents so the Operations team can review them before you book a shipment." : contract.status === "CLEARANCE_UNDER_REVIEW"