diff --git a/docs/docs-new b/docs/docs-new deleted file mode 100644 index d82bff801..000000000 --- a/docs/docs-new +++ /dev/null @@ -1,1138 +0,0 @@ -sumptions: - -The customer has finalized the contract signing procedure with Marketing Team; -The customer requested a customs clearing service (Global Logistics service); -The customer decided to proceed with the shipment operation - -Global Logistics workflow Unimodal Export - US 01: Operation Handover & Contract Verification(if the customer select freight transport with custom clearance) - -As a Global Logistics Staff, -I want to evaluate verified contract validity in a secure, read-only interface -So that I can seamlessly transition a validated business agreement into active execution without exposing agreed financial rates or core contract terms to unapproved structural modifications. -Acceptance Criteria -AC 1: Read-Only Contract Viewer -When an operation is initiated from the customer, signed contracts with the Marketing team will be visible to the Global Logistics team, the system must display the agreed contract details (eg: validity dates, service scopes). -The system must enforce a strict Read-Only / Deny Edit permission set for all Global Logistics roles on these contractual parameters. -US02: Initial Station Assignment & Communication Routing - -As a Global Logistics Customs staff; -I want the system to automatically parse the origin departure station ( origin station), and route the file directly to the appropriate localized global logistics team, -So that a dedicated staff can be assigned; -Acceptance Criteria -AC 1: Station Dropdown Selection -The system must present a mandatory station dropdown populated with valid EDR terminals (e.g., Sebeta, Endode, Dire Dawa, Djibouti Marine Port) to define the operational point of contact. -AC 2: Location-Based Routing Engine -When the customer triggers an operational action, the system must parse the departure location value and route the task directly to the queue of that specific regional station's department. -AC 3: Resource Assignment Control -The system must provide local Station managers with a user assignment dropdown, allowing them to bind a specific staff user ID to the unique MSRN record, updating the status to Staff Assigned. -US 03- Consignment Documentation Submission -As a customer, -I want the system to dynamically request and collect the necessary consignment documentation from the customer for export shipments. -So that the assigned staff can validate compliance and initiate the clearance process seamlessly. -AND -As a Global Logistics Djibouti Staff; -I want to retrieve the necessary documents for the system -So that Release Order can be recovered from the carrier -Acceptance Criteria -AC 1: Export Document Slots -If the shipment type is selected as Export, the customer portal must display mandatory upload fields for: -Booking Confirmation -Shipping Instructions -Verified Gross Mass (VGM) -Railway Bill (Train Waybill) -Important Export documents for clearance:Commercial Invoice, Packing List, Bank Permit, export License, and Delegation Letter/Power of Attorney -Export Commodity-Specific Integration Logic -Coffee: Requires Quality Certificate and Vertical Integration Document (both issued by the Ethiopian Coffee and Tea Authority). -Teff / Beans: Requires Trade Integration Letter and Permit Letter (Ministry of Agriculture). -Depending on the Items exported Supporting government regulatory body documents may be required. -Regional Cross-Border Operations Loop (For export process only) -The system must provide a dedicated, secure upload slot for the Djibouti Global Logistics Team to attach the collected ocean carrier Release Order. -Uploading this document must automatically update permissions, making it visible to the Ethiopian Global Logistics team and the customer to proceed with the customs clearance process. -AC 2: Notification Engine -Upon successful document upload by the customer, the system must trigger an automated status alert to the designated Global Logistics staff to begin the document review process. -US04: Document Compliance Management & Border Clearance Validation -As the assigned Customs documentation compliance staff; -I want to validate compliance of commercial files and commodity specific regulatory certificates; -So that I can approve national customs standards are fully met before proceeding with the entry process. -But this should allow me to revert any query back to the customer on a specific document so that an amendment can be made. -Acceptance Criteria -AC 1: Compliance Verification Workflow -When a customer submits their documents, the shipment status must automatically change to "Pending Document Review", and the assigned staff must receive a system notification. -The document compliance management staff must have a dedicated workspace to view, download, and check the validity of each uploaded file against customs rules and regulations. -The system must require the staff to explicitly mark each individual document as either "Approved" or "Queried" before they can move the overall shipment to the next stage of the entry process. -The system must allow document compliance staff to select a specific document slot (e.g., Commercial Invoice) and launch a query dialog box. -AC 2: Query & Correction Loop (Reversion Process) - If any document fails compliance checks, the staff must be able to flag that specific document and input a detailed, mandatory free-text reason explaining the error or required correction. -Upon submitting a query, the system must change the shipment status to "Action Required: Documentation Amendment" and instantly push a notification to the customer via the Customer Portal. -The Customer Portal must unlock only the specific queried document slots for re-upload, keeping previously approved files locked to prevent accidental changes. -When the customer resubmits the corrected documents, the system must log a new version history of the file and alert the compliance staff to reevaluate. -AC 3: Gatekeeping Logic -The system must strictly block the staff from proceeding to the customs entry creation until 100% of the mandatory and commodity specific documents are marked as "Approved". -US 05: Customs Clearance Document Injection- in Ethiopia side -As a Global logistics staff, -Once the Customs document verification is finalized and export customs entry process is conducted on the external customs portal, -I want the system to allow a slot where Customs entry documents are attached and also visible to the customer on the respective portal. -Acceptance Criteria -The system must provide dedicated slots for the final processed operational Customs forms: EX3 (Export Document), EX8 (Export Transit Document), Export Release, and T1 (Transport Document) -When the EX3 and EX8 Documents are uploaded, Declared milestone should automatically triggered on the system -When the export release and Transport documents (T1) are uploaded, the milestone Export Customs Clearance Process Finalized must automatically transition to completion. -US 06: Payment settlement and Wagon allocation request- Operational in parallel with US05 -As a Global Logistics Staff; -I want to Request for Wagon to load the export shipment on behalf of the customer -So that the customer can settle the payment and proceed with loading the shipment on train -Acceptance Criteria -The system must allow the Global Logistics team to request for Wagon to load the shipment to the operations team, the status will be ‘Wagon Requested’ -Operations team will validate the documents and Proceed to generating PNR for payment and status will show Pending payment -Once Payment is settled in by the customer the status will change to Payment settled -Once the status shows payment settled, Operations team will proceed with wagon allocation and status will change to Wagon allocated -US07: Cargo Acceptance and Loading on Train -As an Operation Staff , -I want to cross check and validate the correct shipment has arrived in the warehouse (container number, quantity if shipment is bulk , truck plate number ) -Accepting criteria -The warehouse and terminal team should trigger time that the cargo arrived and received in the system ‘cargo arrived’ -The operation team should trigger cargo ready for loading and loaded milestones in the system ‘ready for loading’ and once the consignment is ‘loaded’ -The Global Logistics team must upload the transport document T1 as shown in US05. -Each milestone should also reflect on the Global Logistics window and customer portal for each specific order. -US08: Train Depart to Djibouti -As Port and terminal staff; -I want to confirm the train number and wagon number at departure (Marshalling document) -So that the status of the shipment can be changed from loaded to departed. -Accepting criteria -The system shall use the train number and wagon number from wagon allocation stage and update the milestone ‘Departed’ -The system shall notify the departure time, Estimated arrival time and send a notification to Global Logistics Ethiopia and Djibouti team, Marketing and Business Development team and the customer. -The system shall generate a load list that contains wagon Number, Train number, customer name and ID, container number. Here the load list should match which container is loaded on which wagon number and should be visible to all departments for downloading and internal use. -US 09 : Inter-Regional cargo transfer and handover- Djibouti GL - -As a Djibouti Global Logistics Staff, -Upon train departure from Ethiopia, the task for cargo handling and offloading to the container yard should automatically be under the custody of GL Djibouti Team. - -Acceptance Criteria -AC 1: Operational Phase Handoff Trigger -When the milestone Departed is triggered from the Ethiopian side, the system must automatically execute a role transfer, setting the primary owner to Global Logistics Djibouti and assigning a read-only copy of the data packet to the Ethiopian origin office. -AC 2: Asymmetric Milestone Notifications -Upon train departure execution, the system must distribute tailored data packets automatically: -Global Logistics Ethiopia: Updates Actual time of Departure (ATD) -Global Logistics Djibouti: Receives Actual time of Arrival (ATA) -AC 3: Process Finalization stage -Global Logistics Djibouti: After retrieving the T1 and the documents from the shipment and in collaboration with the Djibouti customs clearing agent, Gatepass will be processed and the status of the shipment will show Gatpass granted -Global Logistics Djibouti: Updates the shipment is offloaded at Container yard in Djibouti. ‘Offloaded’. So that the Ethiopian GL and customer can see the arrival time. -Finance Management Sub-Module -US10: Post-Transit Miscellaneous Cost Settlement & Finance Cycle Closure -As a Corporate Finance, -I want to insert delayed third-party maritime bills into the active payables ledger after cargo delivery -So that I can lock the file from further adjustments, accurately calculate final profits, and conclude the operational billing lifecycle. -Acceptance Criteria -AC 1: Post-Operational AP Ingestion -The system must allow users to add additional rows to the Accounts Payable (AP) ledger for third-party charges (e.g., Djibouti Port Handling, Subcontracted Service Invoices) even while the invoice lifecycle status sits at Temporarily Closed. -AC 2: Permanent Accounting Closure Trigger -Once all outstanding third-party invoices from the Djibouti operations are entered and matched, the user must be able to switch the billing cycle state to Permanently Closed. -This action locks the financial record against any further adjustments or cost line additions. -Export Documents -No -Customer Uploading documents -Global Logistics Uploading Documents -Remark -1 -Booking Confirmation* -Release Order - - -2 -Verified Gross Mass (VGM)* -Export declaration documents - - -3 -Shipping Instruction (SI)* -Export Release - - -4 -Train waybill* -T1 (Transport document) - - -5 -Commercial Invoice* -Full in Interchange* - - -6 -Packing List* -Final Declaration* - - -7 -Bank Permit* - - - - -8 -Sales Contract* - - - - -9 -Power of Attorney (POA)* - - - - -10 -Export License* - - - - - -*Mandatory documents - - - -Milestone updates for Export Shipments -No -Important Milestones -Responsible departments -Milestones triggered by document upload -Reamark -1 -Export Documents Uploaded -Customer -No - - -2 -Pending Document Review -Global Logistics-ET -Yes -Once all documents are uploaded from customer side - - -3 -Documents Approved -Global Logistics-ET -No - - -4 -Release Order Secured -Global Logistics-Djibout -Yes -Once Uploaded by the Djibouti team -5 -Under Customs Clearance -Global Logistics- ET -No - - -6 -Declared -Global Logistics-ET -Yes -Once the export clearance documents are uploaded -7 -Export Released -Global Logistics-ET -No - - -8 -Wagon Requested -Global Logistics-ET -No - - -9 -Pending Payment - - - - - - -10 -Payment settled -Customer -Yes -When the PNR due amount is settled -11 -Wagon allocated -Operations department -No - - -12 -Cargo arrived -Port and terminal -No - - -13 -Ready for loading -Port and terminal -No - - -14 -Loaded -Port and terminal -No - - -15 -Departed to Djibouti -Port and terminal -No - - -16 -Arrived at Djibouti -Global Logistics-Djibouti -No - - -17 -Gatepass granted -Global Logistics-Djibouti -No - - -18 -offloaded - - -Yes -When Full-in interchange is uploaded - - -Global Logistics workflow Unimodal Import -US 01: Operation Handover & Contract Verification (if the customer select freight transport with custom clearance) - -As a Global Logistics Staff, -I want to evaluate verified contract validity in a secure, read-only interface -So that I can seamlessly transition a validated business agreement into active execution without exposing agreed financial rates or core contract terms to unapproved structural modifications. -Acceptance Criteria -AC 1: Read-Only Contract Viewer -When an operation is initiated from the customer, signed contracts with the Marketing team will be visible to the Global Logistics team, the system must display the agreed contract details (eg: validity dates, service scopes). -The system must enforce a strict Read-Only / Deny Edit permission set for all Global Logistics roles on these contractual parameters. -US 02- Consignment Documentation Submission- Import -As a Customer, -I want the system to dynamically request and collect the necessary consignment documentation from the customer for import shipment processing. -So that the Global Logistics staff can validate the cross border compliance and initiate the clearance process seamlessly. -Acceptance Criteria -AC 1: Import Document Logic (OBL vs. SWB) -The system must evaluate the Bill of Lading (B/L) type: -Original Bill of Lading (OBL): The system must display a notification prompting the customer to physically submit two (2) endorsed hard copies to the Djibouti Global Logistics Djibouti office. And upload an endorsed copy of the OBL. (Shipping address will be provided) -If Sea Waybill (SWB): The system must activate a softcopy attachment slot allowing the customer to upload the endorsed digital B/L, alongside a free-text notes field for communication with the Djibouti team. -Import Framework: Commercial Invoice, Packing List, Certificate of Origin (COO), Bill of Lading (OBL/SWB), Bank Permit/Franco Valuta, Import License, and Delegation Letter/Power of Attorney. -Depending on the import shipment type, regulatory documents may be requested so there should be a slot where customers can upload additional documents as required. -AC 2: Notification Engine -Upon successful document upload by the customer, the system must trigger an automated status alert to the designated Global Logistics staff to begin the document review process. -US03: Document Compliance Management & Border Clearance Validation -As a Customs documentation compliance staff, -I want to mandate and validate compliance of commercial files and commodity specific regulatory certificates through organized secure upload fields, -So that I can verify customs standards are fully met prior to proceeding with the entry process. -But this should allow me to revert any query back to the customer on the specific documents so that an amendment can be made. -Acceptance Criteria -AC 1: Compliance Verification Workflow -When a customer submits their documents, the shipment status must automatically change to "Pending Compliance Review", and the assigned staff must receive a system notification. -The compliance management staff must have a dedicated workspace to view, download, and check the validity of each uploaded file against customs rules and regulations. -The system must require the staff to explicitly mark each individual document as either "Approved" or "Queried" before they can move the overall shipment to the next stage of the entry process. -The system must allow users to select a specific document slot (e.g., Commercial Invoice) and launch a query dialog box. - -AC 2: Query & Correction Loop (Reversion Process) - If any document fails compliance checks, the staff must be able to flag that specific document and input a detailed, mandatory free-text reason explaining the error or required correction. -Upon submitting a query, the system must change the shipment status to "Action Required: Documentation Amendment" and instantly push a notification to the customer via the Customer Portal. -The Customer Portal must unlock only the specific queried document slots for re-upload and amendment, keeping previously approved files locked to prevent accidental changes. -When the customer resubmits the corrected documents, the system must log a new version history of the file and alert the compliance staff to reevaluate. -AC 3: Gatekeeping Logic -The system must strictly block the staff from proceeding to the customs entry creation or wagon allocation phase until 100% of the mandatory and commodity specific documents are marked as "Approved". -US 04: Customs Clearance Document Injection- in Ethiopia side -As a Global logistics staff, -Once the Customs document verification and entry process is finalized on the external customs portal, -I want the system to allow a slot where Customs entry documents are attached and also visible to the customer on the portal. -Acceptance Criteria -The system must provide dedicated slots for the final processed operational forms: IM4 (Permanent Import Document), IM5 (Temporary Import Document), -If there are any duties and taxes to be settled, The Global Logistics team will send the amount, Declaration Serial number and automatic notification to the customer will be sent to the customer. Here there should be a spot where the Customer can upload the payment slip from its portal and same will reflect and notify the Global Logistics team ‘Duties and taxes settled’ -There should be a slot where the Transit permit system screenshot can be uploaded in JPG format. -US 05: Customs Clearance Document Injection- in Djibouti side -As a Global Logistics Djibouti staff; -I want to update the Delivery order processing and port invoices are paid -So that further port handling is processed. -Acceptance Criteria -The Djibouti Global Logistics team receive Delivery Order DO once the invoice is paid by the Djibouti clearing Agent, here they will update ‘DO Received’ -US 06: Payment settlement and Wagon allocation request- Operational in parallel with US05 -As a Global Logistics Staff; -I want to Request for Wagon to load the import shipment on behalf of the customer -So that the customer can settle the payment and proceed with loading the shipment on train -Acceptance Criteria -The system must allow the Global Logistics team to request for Wagon to load the shipment to the operations team, the status will be ‘Wagon Requested’ -Operations team will validate the documents and Proceed to generating PNR for payment and status will show Pending payment -Once Payment is settled in by the customer the status will change to Payment settled -Once the status shows payment settled, Operations team will proceed with wagon allocation and status will change to Wagon allocated -Then update ‘Gate pass permission granted’for import shipment at container(DMP/ DCT) yard. -US07: Cargo Acceptance and Loading on Train -As an Djibouti Global Logistics staff , -I want to cross check and validate the correct shipment has arrived for loading at port -AND -I want to include any exception management if applicable to the cargo. -Accepting criteria -AC1: Exception Management -AC 1.1: Condition Incident Form Factor -The system must provide an emergency incident logging component within the active milestone interface. -AC 1.2: Standard Anomaly Taxonomy -The inspector must select from a predefined incident classification dropdown: Seal is Broken, Container Opened, Container Damaged, or Fluid Leaking/Coming Out. -A mandatory free-text narrative field for a detailed description must be filled out alongside a mandatory upload slot for photo verification (supported format: .jpg). -AC 1.3: Real-Time Border-to-Capital Notification Pipeline -Upon saving an incident record at a border or port station, the system must trigger a high-priority alert to the Global Logistics Team Ethiopia, attaching the text description and photos directly to their operational dashboard. At the same time the customer will receive a notification on its portal ‘damage reported’ - -AC 2: Shipment Loading Management -The Djibouti Global Logistics team should trigger cargo ready for loading and loaded milestones in the system ‘ready for loading’ and once the consignment is ‘loaded’ -The Global Logistics team must upload the transport documents T1 before train departure. -Each milestone should also reflect on the Global Logistics window and customer portal for each specific order. - - - -US08 : Train Depart For Import -As a Djibouti Global Logistics staff, -I want to confirm the train number, wagon number and container number at departure -So that, status of the shipment is changed from loaded status to departed status -And -As Ethiopian Global Logistics team and Operations staff, -I want the system to notify us about the Train Departure from Djibouti -So that final import clearance preparation can commence -Accepting criteria -The system shall use the train number and wagon number from wagon allocation stage and update the milestone ‘Departed from Djibouti’ from Djibouti Global Logistics side -The system shall notify the departure time, Estimated arrival time and send a notification to Operations and Global Logistics Ethiopia, Marketing and Business Development team and the customer. -The system shall generate a load list (Marshaling) that contains wagon Number, Train number, customer name and ID, container number. Here the load list should match which container is loaded on which wagon number and should be visible to all departments for downloading and internal use. -US09 : Inter-Regional cargo transfer and handover - -As a Djibouti Global Logistics Ethiopia, -Upon train departure from Djibouti, the task for cargo handling and offloading to the container yard should automatically be under the custody of Operations and GL Ethiopia team. -Acceptance Criteria -AC 1: Operational Phase Handoff Trigger -When the milestone Departed is triggered from the Djibouti side, the system must automatically execute an administrative role transfer, setting the primary owner to Global Logistics Ethiopia - -AC 2: Asymmetric Milestone Notifications -Upon train departure execution, the system must distribute tailored data packets automatically: -Global Logistics Djibouti: Updates Actual time of Departure (ATD) -Customer Portal: Receives status notification ‘Departed’ -Global Logistics Ethiopia: Receives Actual time of Departure (ATD) and Expected Time of Arrival (ETA) -US10: Cargo Acceptance and offloading - Import -As an port and terminal Staff , -I want to cross check and validate the correct shipment has arrived through the Marshalling document -And -As a Global Logistics Ethiopia Staff, -I want the system to notify me when the train arrives at Destination -So that further shipment clearance can proceed. -Accepting criteria -The port and terminal team should trigger time that the cargo arrived and received in the system ‘Train arrived’ -The operation team should trigger cargo offloaded milestones ‘Offloaded’ here offloading should state exact Yard number and Zone of the container location. (Warehousing Concept) -Each milestone should also reflect on the customer portal for each specific order. -US 11: Global Logistics Customs Clearance and Documentation finalization- IMPORT -As a Global Logistics Ethiopia staff, -I want to immediately process the closure of the T1 transport document and secure the Import Release Permit from Customs, -So that we can finalize the shipment’s status, arrange for cargo dispatch from port, and ensure the customer is notified of the final release milestone. -Acceptance Criteria: -Trigger: The system must confirm that the train has arrived at the Ethiopian terminal yard and that the containers have been offloaded. -T1 Closure: The Global Logistics system must reconcile the offloading confirmation against the T1 document, submitting a closure request to the Customs Authority. - Risk Assessment: Customs risk criteria (Green, Yellow, Blue, Red) must be assigned, defining the level of physical or documentary examination required. Here the system needs to notify the customer on ‘risk assigned’. GREEN, YELLOW, RED -Release Submission: Once the T1 is closed and Customs Process is finalized based on the assigned Risk, the system must trigger ‘Import Release Permit granted’ -Customer Notification: Upon permit issuance, an automated notification must be sent to the customer via the Customer Portal, updating their shipment status to "Import Release Granted." -When the above documents are uploaded, the milestone Import Customs Clearance Process Finalized must automatically transition to ‘Import process Completed’ -US 12: Automated Storage & Demurrage Fee Progressions -As port and terminal staff, -I want the system to systematically monitor container dwell times and dynamically apply incremental demurrage tariffs from day three onward, -So that I can automate auxiliary revenue collection and hold terminal space release until all outstanding storage balances are fully settled. -Acceptance Criteria -AC 1: Dwell-Time Calculation Engine -The system must automatically measure container dwell time: -Dwell Days = Exit Date —- Cargo Offloading Date -AC 2: Step-Up Demurrage Tariff Automation -The system must process billing logic using a stepped progressive calculation model: -Example: -Days 1 to 3: Free Storage Period (ETB 0.00). -Day 4: ETB 2,500per day per container. -Day 5: ETB 3,200 per day per container. -Day 6: ETB 4,000 per day per container. -Day 7: ETB 5,000 per day per container. - -AC 3: Financial Module Push Block -The Calculated storage and demurrage amount should automatically be calculated and the Due amount should be Notified to the Customer using a Unique serial Number for settlement ‘Storage and storage invoice raised’ status should be triggered automatically on the customer portal. -The total accrued cost must be sent as a mandatory charge item line to the Finance Module, and the system must block the release of the electronic exit note until the status of the storage invoice shows as Paid. -AC 4: Exit Note Trigger -Upon the completion of storage invoice settlement and status changed to Paid, generation of the final terminal exit note shall proceed. -Finance Management Module -US13: Post-Transit Miscellaneous Cost Settlement & Cycle Closure -As a Corporate Finance team, -I want to insert delayed third-party maritime bills into the active payables ledger after cargo delivery and explicitly trigger a "Permanently Closed" file validation, -So that I can lock the file from further adjustments, accurately calculate final profits, and conclude the operational billing lifecycle. -Acceptance Criteria -AC 1: Post-Operational AP Ingestion -The system must allow users to add additional rows to the Accounts Payable (AP) ledger for third-party charges (e.g., Djibouti Port Handling, Subcontracted Service Invoices) even while the invoice lifecycle status sits at Temporarily Closed. -AC 2: Permanent Accounting Closure Trigger -Once all outstanding third-party invoices from the Djibouti operations are entered and matched, the user must be able to switch the billing cycle state to Permanently Closed. -This action locks the financial record against any further adjustments or cost line additions. - -Import Documents -No -Customer Uploading documents -Global Logistics Uploading Documents -Remark -1 -Commercial Invoice* -Import Declaration Document* - - -2 -Packing List* -Transit permitted screenshot* - - -3 -Certificate of Origin (COO) -Delivery Order - - -4 -Bank Permit/Franco Valuta* -Damage report photos -Damage report photos are not mandatory only if there is any exceptions took place -5 -Bill of Lading/SWB* -T1 Transport documents - - -6 -Power of Attorney (POA)* -Import release - - -7 -Import License* -Full out Interchange* - - -8 -Other Regulatory documents as per the imported shipment -Empty in Interchange* - - -9 -Duty and tax payment slips* -Final Declaration* - - -10 -Storage and Demurrage payment slips* - - - - - -*Mandatory documents - - - - - - -Milestone updates for Export Shipments -No -Important Milestones -Responsible departments -Milestones triggered by document upload -Reamark -1 -Import Documents Uploaded -Customer -No - - -2 -Pending Document Review -Global Logistics-ET -Yes -Once all documents are uploaded from customer side - - -3 -Documents Approved -Global Logistics-ET -No - - -4 -Under Customs Clearance -Global Logistics- ET -No - - -5 -Declared -Global Logistics-ET -Yes -Once the export clearance documents are uploaded -6 -Duty and Taxes advised -Global Logistics-ET -No - - -7 -Duty and tax paid -Customer -Yes -When the customer uploads the payment slip -8 -DO Collected -Global Logistics-Djibouti -Yes -When the GL team uploads the DO on the system -9 -Wagon allocation requested -Global Logistics-Ethiopia -No - - -10 -Payment settled -Customer -Yes -When the PNR due amount is settled -11 -Wagon allocated -Operations department -No - - -12 -Gatepass granted -Global Logistics-Djibouti -No - - -13 -Ready for loading -Global Logistics-Djibouti -No - - -14 -Loaded -Global Logistics-Djibouti -No - - -15 -Departed from Djibouti -Global Logistics-Djibouti -No - - -16 -Arrived at Port in Ethiopia -Port and terminal -No - - -18 -offloaded -Port and terminal -No - - -19 -T1 Closed -Global Logistics- Ethiopia -No - - -20 -Customs Risk Assigned -Global Logistics- Ethiopia -No - - -21 -Import Release Permitted -Global Logistics- Ethiopia -No - - -22 -Storage and Demurrage charges settled -Customer -Yes -When the customer uploads the payment slip from the customer portal -23 -Port Exit issued -Global Logistics- Ethiopia -No - - - - - - - - - - - - -Multimodal Transport Global Logistics User Story IMPORT -US 01: Importer Service Request Placement - -As a customer I want to submit a digital Freight Proforma Request with complete cargo and supplier details -I want to seamlessly select my freight direction Import, choose between Modes of transport (Road, Sea, Air) to combine with Rail transport, define precise cargo type (Bulk/ Break bulk or Container..), and upload foundational corporate documentation through a single portal. Additional informations like Port of Loading (POL), Port of Destination (POD), buyer or Seller name, Cargo readiness date (for import shipments) -So that the MTO team can review the operational requirements and initiate the booking process with an error-free Multimodal Logistics operation tailored to my specific supply chain needs. -Acceptance Criteria -AC 1: Direction and Service Selection UI -Direction Choice: The system must automatically set Import as the Service Type. -Routing Information: Origin and destination of shipments (Country and port eg: CNNGB, CN designates China and NBG represents Ningbo port) -The system must render a dynamic selection grid/dropdown allowing the customer to select combinations of Road, Train, Sea, and Air. -AC 2: Information intake section -The form must enforce strict character and format validation on the following mandatory input fields: - -Importer Name & Importer Physical Address (Alphanumeric text strings)/Consignee. -Supplier/Seller Name & Seller Physical Address (Alphanumeric text strings). -Container Type (Dropdown: 20ft Standard, 40ft Standard, 40ft High-Cube, 20ft/40ft Reefer etc.). -Number of containers (Numeric positive integers only). -Readiness Date (Calendar date picker tool; must be equal to or greater than the current system date). -Port of Loading or Port of Destination (Dropdown field dynamically mapped to global UN/LOCODE standards). - -By Clicking the Submit Request button must automatically validate all fields, display real-time inline errors for missing data, change the shipment status to MTO Service Requested`, and timestamp the event in the system log. - -US 02: Order Intake by Global Logistics MTO (EDR) - -As Global Logistics Staff, I want to receive the filled information via a notification from the Customer portal -So that I can validate the requests compliance and decide to accept the service request or not -Acceptance Criteria -AC 1: Order amendment, Acceptance or Rejection -Once the order actually pops up on the Global Logistics interface, the staff can request for amendment on the information to the customer, accept or reject the service request and every acceptance or rejection will be communicated back to the customer. -Milestone Assumptions -Service Request amendment requested→ An amendment request will be sent to the customer with a note on it. -Service request Rejected → Rejection reasoning will be provided to the customer, reasons may arise due to vessel call at POL, Service unavailability at POD. -Service Request Accepted → Acceptance notification will be sent to the customer to proceed with the operation - - -US 03: Cargo Agent Space Booking & Allocation (This step will be done via email) -As a Global Logistics Staff, I want to transfer orders to the designated Freight Forwarding Agent at POL -So that the system can change the shipment state to Supplier contacted on the customer portal. -As a designated Freight forwarding agent, I want to check the cargo readiness date, Export formalities are done, Empty equipment availability and stuffing of cargo. Once all these formalities are done; Origin Export formalities finalized milestone will be updated on the customer portal. -Acceptance Criteria -AC 1:Notification and update -The global Logistics staff must have log communication records with the importer, confirming the liner shipping space availability, and input the carrier details and booking reference number. -US 04: Cargo Receipt & Loaded on Board (This step will be done via email) -As a designated Freight forwarding agent, I want to confirm the shipment is transferred to the shipping lines for loading. This will trigger, Ready for loading at Origin milestone will be updated on the customer portal. -Once the Vessel Sails from Origin, the Freight forwarding Agent will update Shipped on Board milestone will be updated on the customer portal and In transit status will automatically follow - - - -Acceptance Criteria -AC 1:Notification and update -The global Logistics staff must have log communication records with the importer, confirming the Ready for Loading Milestone once the cargo is transferred to the shipping lines at Origin and Shipped status once the shipment departs from origin port. - - - -US 05: Bill of Lading (B/L) Generation (This step will be done via email) - -As a designated Freight forwarding agent, once the shipping lines receive the physical cargo, the system must expose two secure document upload slots -Accepting Criteria -Master Bill of Lading (OBL Slot): After finalizing the communication between the shipping lines and designated freight forwarder/booking agent, Final approved OBL will be uploaded. -House Bill of Lading (HBL Slot): As per the request from the importer, the designated freight forwarding agent will upload the final approved HBL document on the portal. -Milestones Updated: Transport Documents Uploaded - - - - -US 06: Cargo arrive at Djibouti Port -As a Global Logistics Staff, I want to confirm the shipment arrival at Djibouti -So that the Customer can be notified and make the necessary documents ready for clearance at the destination port for clearance process -Accepting Criteria -There should be a slot where the Global logistics staff notifies the customer as a primary notification for the shipment arrival for the shipment process -US 07: Inland transportation to Inbound port - -As a Global Logistics staff, -I want to electronically submit a rail wagon and train allocation request to the Operations Team, receive an automated schedule assignment, log loading milestones, and track the final dispatch, -So that we can seamlessly transition cleared multimodal containers from terminal yards to their final destination stations without manual coordination delays. -Accepting Criteria -Wagon & Train Allocation Request (Global Logistics Workspace) -AC1 Submitting an Allocation Request -Customer/Company Name (Read-only, auto-populated from contract). -Target Loading Date (Calendar date picker). -Container Count & Configuration (Auto-extracted from shipping manifest; e.g., 2 × 40ft HC). -Origin Node & Destination Station (Dropdown menu select options, example: Negad to GMP). -And clicking Submit Request must instantly transfer the data payload to the Operations Team's active scheduling queue, changing the global status flag to Allocation Pending. - - -Schedule & Wagon Assignment Matrix (Operations Workspace) -AC 2: Operations Team Fulfills Request & Assigns Schedule -Given an incoming request is sitting in the Allocation Pending queue, -When an Operations user opens the shipment, -Then the system must provide resource-matching fields to input the following assets: -Assigned Train Number (Alphanumeric text string field). -Assigned Wagon Numbers (Multi-select list matching the container count from US-OI-01). -Scheduled Departure Date & Time (Timestamp inputs). -Estimated Arrival Date & Time (Timestamp inputs). -And when the Operations user clicks Confirm Allocation, the system must automatically update the transport file, push the finalized rail schedule to both the Global Logistics dashboard and the Customer Portal, and advance the status to Scheduled / Ready to Load for Inbound transit. - -Physical Loading Confirmation & Manifest Extraction -AC 3: Updating Loading Milestone -Given a shipment holds a status of Scheduled / Ready to Load, -When the yard loading team or Djibouti Global Logistics team completes container positioning onto the assigned wagons and clicks Confirm Loading Completion -Then the system must change the shipment status to Loaded - -AC4: Departure & Dispatch Automation Loop -Scenario: Train Departure Notification -Given the shipment milestone is set to Loaded, -When the terminal yard master updates the system status to Departed (or when integrated terminal tracking sensors flag train departure), -Then the system must flip the global status to In Transit - Rail Leg. -And the backend must immediately fire a cross-border notification array (containing the digital transit manifest packet as a PDF attachment) to the receiving Destination Station team so they can prepare inbound logistics, security, and customs processing. - - -US08: Cargo Acceptance and offloading Station at Destination - -As Operations/Port and terminal staff, -I want to cross check and validate the correct shipment has arrived through the Marshalling document -And -As a Global Logistics Ethiopia Staff, -I want the system to notify me when the train arrives at the destination station to process T1 closure with Customs. -Acceptance Criteria -The warehouse and terminal team should trigger time that the cargo arrived and received in the system Train arrived -The operation team should trigger cargo ready for offloading and offloaded milestones should be triggered in the system Offloaded at Container Yard here offloading should state exact Yard number and Zone of the container location. -Each milestone should also reflect on the customer portal for each specific order. -US 09: Payment Settlement and Import Process Initiation - -As Finance Department, -I want the system to automatically generate an automated payment invoice based on the active customer contract and dynamically flip the shipment milestone status to "Paid" upon payment verification, -So that we can eliminate credit risk, secure operating cash flow, and programmatically authorize Global Logistics, Operations and Port and terminal team to proceed with the necessary processes. - -Acceptance Criteria -Pre-Conditions for System Execution -After the container is placed at the designated warehouse, The shipment file must be in an initial state of Pending Invoice Generation. -AC1: Automated Invoice Generation -Scenario: Compiling Contract Data into a Digital Billing Invoice -Given a shipment file is in a Pending Invoice Generation state, -When a Finance user navigates to the "Billing & Invoice Module" and clicks Generate Invoice, -Then the system's calculation engine must execute a read-only query against the active contract to pull: -Base freight rates matched to the selected route corridor. -Container metrics (Total Count multiplied by tariff ). -Valid ancillary terminal or customs clearance fees (if Value-Added Service is selected). -And the system must output an immutable digital invoice document containing a unique, bank-integrated alphanumeric tracking serial structured as: [CustomerName]-[DueAmount]-[UniqueUUID]. -And the shipment status must instantly switch to Pending Payment, locking down all downstream operational queues like clearance and shipment exit from terminal. - - -AC2: Bank-API Integration & Payment Settlement -Receiving Payment Token and Unlocking System Gates -Given a shipment holds a status of Pending Payment, -When the customer settles the due amount and the system receives a secure, cryptographic transaction token from an integrated banking API (e.g., Commercial Bank of Ethiopia, Telebirr) matching the unique invoice serial number, -Then the system must immediately process a real-time transaction confirmation match. -And the platform must prevent manual overrides or bypassing of this payment validation gate unless authorized by a Super-User financial security role. - -US-FIN-03: Dynamic Milestone Triggering ("Paid") -Scenario: Updating the Global State and Notification Broadcast -Given the backend banking verification token evaluates to SUCCESS, -When the system logs the incoming transaction, -Then the system must automatically flip the shipment milestone status to Paid. -Below User Story 09 is Applicable only if the Customer Proceeds with Global Logistics Services -US 10: Customs Clearance Document Injection- Ethiopia side -As a Global logistics staff, -Once the Customs document verification and entry process is finalized on the external customs portal, -I want the system to allow a slot where Customs entry documents are attached and also visible to the customer on the portal. -Acceptance Criteria -The system must provide dedicated slots for the final processed operational forms: IM4 (Permanent Import Document), IM5 (Temporary Import Document) for the declared shipment -If there are any duties and taxes to be settled, The Global Logistics team will send the amount, Declaration Serial number and automatic notification to the customer will be sent to the customer. Here there should be a spot where the Customer can upload the payment slip from its portal and same will reflect and notify the Global Logistics team Duties and taxes settled - Risk Assessment: Customs risk criteria (Green, Yellow, Blue, Red) must be assigned, defining the level of physical or documentary examination required. Here the system needs to notify the customer on Risk assigned. GREEN, YELLOW, RED -Release Submission: Once the T1 is closed and Customs Process is finalized based on the assigned Risk, the system must trigger Import Release Permitted -Customer Notification: Upon permit issuance, an automated notification must be sent to the customer via the Customer Portal, updating their shipment status to "Import Release Granted." -When the above documents are uploaded, the milestone Import Customs Clearance Process Finalized must automatically transition to Import process Completed -US 10: Automated Storage & Demurrage Fee Progressions -As a Finance team, -I want the system to systematically monitor container dwell times and dynamically apply incremental demurrage tariffs from day six onward, -So that I can automate auxiliary revenue collection and hold terminal space release until all outstanding storage balances are fully settled. -Acceptance Criteria -AC 1: Dwell-Time Calculation Engine -The system must automatically measure container dwell time: -Dwell Days=Exit Date - Cargo Offloading Date -AC 2: Step-Up Demurrage Tariff Automation -The system must process billing logic using a stepped progressive calculation model: -Example: -Days 1 to 5: Free Storage Period (ETB 0.00). -Day 6: ETB 2,500per day per container. -Day 7: ETB 3,200 per day per container. -Day 8: ETB 4,000 per day per container. -Day 9: ETB 5,000 per day per container. -AC 3: Financial Module Push Block -The Calculated storage and demurrage amount should automatically be calculated and the Due amount should be Notified to the Customer using a Unique serial Number for settlement Storage and Demurrage invoice raised status should be triggered automatically on the customer portal. -The total accrued cost must be sent as a mandatory charge item line to the Finance Module, and the system must block the release of the electronic exit note until the status of the storage invoice shows as Paid. -US 11: Last Mile Transport processing- EDR processing the Last mile -As a Global Logistics staff, -I want to electronically submit a truck assignment request to the Truck & Machinery Department and, upon fulfillment, automatically compile the assigned asset/driver data into a standardized Truck Waybill, -So that we can efficiently coordinate physical last-mile deliveries, eliminate manual data entry errors. -Acceptance Criteria -Pre-Conditions for System Execution -The cargo must be physically present and the import customs clearance process should be finalized. -AC1: Inter-Departmental Last-Mile Truck Request -Scenario: Submitting a Request to the Truck & Machinery Department -Given a shipment file is ready for last-mile delivery to the customer's doorstep, -When a Global Logistics user opens the "Last-Mile Module" and clicks Request Fleet Allocation, -Then the system must auto-populate a routing task packet containing the following read-only fields: -Client Name & Client ID (Extracted from the master order profile). -Required Truck Type / Capacity (Based on container configurations or weight specifications). -Place of Unloading (The final delivery destination address provided by the customer and phone number of contact person). -Container Type (Eg: 1x40’ HC or 1x20 DV) -And clicking submit, it must change the local leg status to Pending Truck Request and route the task directly into the Truck & Machinery Department's active workspace queue. - - - -AC2: Asset Assignment & Data Injection (Truck & Machinery Dept.) -Fulfilling the Truck Request -Given a request sits in the Truck Request Pending queue, -When a user from the Truck & Machinery Department opens the task, -Then the system allows them to select and assign active, compliant resources from the database by filling out the following mandatory fields: -Driver Name (Dropdown or text validation matching registered, available operators). Which will auto populate the attached truck details and plate number with the driver -Truck Type (Dropdown: e.g., Casoni, truck trailer, highbed, low-bed etc.). -Driver Phone Number (Alphanumeric/Numeric text field linked to the driver profile). -And clicking Submit Fleet Assignment must update the vehicle monitoring engine state to Truck Assigned and hand control back to the Global Logistics team. -AC3: Automated Truck Waybill Compilation -Generating the Final Waybill Documents -Given the Truck & Machinery Department has completed the truck assignment, -When the system reassigns the task to the Global Logistics team dashboard, -Then the system must unlock a Generate Truck Waybill action engine. -And clicking this button must instantly compile a non-editable, standardized digital transport document incorporating the following multi-departmental data streams: -From the Client Profile: Client Name and Client ID. -From the Logistics Order: Place of Unloading. -From the Truck & Machinery Allocation: Driver Name, Driver Phone Number, and Truck Plate Number. -And the document must be saved to the database in PDF format, update the leg status to Waybill Generated, and enable a print/download utility for the gate security and driver. -And Dispatched Milestone will be triggered afterwards. - -The below User Story is applicable if the Customer does not require last mile service from EDR -US 12: Last Mile Transport processing- Customer processing the Last mile -As a Customer/Importer, -I want to directly input my designated third-party transport vehicle details, container matching fields, and operator data into the Customer Portal, generate a duplicate set of Freight Orders, and track the physical loading and proof of delivery (POD) signing by port operators and Customer. -So that I can seamlessly manage my own last-mile haulage assets and legally clear my containerized cargo from the terminal yard. -Acceptance Criteria -Pre-Conditions for System Execution -The cargo must be physically present and the import customs clearance process should be finalized. -AC1: Customer Portal Asset Ingestion Matrix -Customer Inputs External Truck and Driver Profiles -Given a shipment file satisfies all pre-conditions and sits in the Ready for loading status, -When the Customer logs into the portal, opens the shipment file, and fill in the transport details; -Then the system must provide a user interface requiring the customer to fill out the following mandatory parameters: -Truck Plate Number (Alphanumeric entry text field). -Driver Name (Alphanumeric entry text field). -Truck Type (Dropdown selection matrix: e.g., Flatbed, Container Chassis, Lowboy). -Container Number to Load (Alphanumeric format validation text box matching ISO container standards). -And clicking the Verify & Submit Assignment button must check that none of the inputs are empty strings, freeze editing capabilities for the customer, and advance the shipment state to Truck Assigned. - - - -AC 2: Automated Twin Freight Order Compilation -Printing Duplicate Freight Orders from the Customer Portal -Given a shipment holds a status of Truck Assigned, -When the customer clicks the Generate Freight Order Copies button, -Then the document generation engine must automatically compile a downloadable, print-ready PDF document containing all details gathered in AC1 alongside the Client Name and unique Client ID. -And the engine must render the document in a dual-copy format with watermark ("Copy 1: Port Operations Copy" and "Copy 2: Gate Security & Carrier Copy"). -And the gate and security team will check for the physical truck arrival and the port and terminal team will trigger Truck Arrived milestone status. - -AC 3: Port Yard Loading & Operations Validation Loop -Port and terminal staff Team Processes -Given the customer's third-party truck has physically arrived at the port gate holding the printed Freight Order documents, -When the internal EDR port and terminal staff team opens the matching file via the internal system terminal, -Then the system must force the operator to verify that the arriving physical vehicle's plate number, driver identity, and container matches the data locked by the customer. -And upon physical reachstacker positioning of the box, the operator clicks Loaded on truck, loading timestamp will be captured to the system log. -AC4: Digital POD Signing & Port Dispatch -Completing Handover and Archiving the File -Given the container has been physically loaded to the assigned truck, -When the authorized EDR Port and terminal staff checks the signed paper copy -Then the system must open a slot where a signed POD is uploaded on the system. -And saving this file must automatically mark the shipment milestone as Dispatched from Port / Completed. - -Milestone triggers for Multimodal Operation -No -Important Milestones -Responsible departments -Milestones triggered by document upload -Reamark -1 -MTO Service Requested -Customer -No - - -2 -Service Requested Accepted -Global Logistics-ET -No - - -3 -Supplier/Shipper Contacted -Global Logistics-ET -No - - -4 -Origin Export Formalities finalized -Global Logistics-ET -No - - -5 -Ready for Loading -Global Logistics-ET -No - - -6 -Shipped on Board/Loaded -Global Logistics-ET -No - - -7 -B/L (transport documents) uploaded -Global Logistics-ET -No - - -8 -In transit -Global Logistics-ET -No - - -9 -Ship arrived at Port -Global Logistics-Djibouti -No - - -10 -Scheduled / Ready to Load for Inbound transit -Global Logistics-Djibouti -No - - -11 -Loaded on train -Global Logistics -Djibouti -No - - -12 -In transit to destination port -Global Logistics -Djibouti -No - - -13 -Train Arrived -Port and terminal Department -No - - -14 -Offloaded at Container Yard -Port and terminal department -No - - -15 -Pending payment -Finance department -No -Multimodal transport service payment -16 -Paid -System Generated -Yes -When the Unique serial number for payment is settled the system will autofill date and time -17 -Commercial Documents received -Global Logistics-ET -NO -Only if the customer decided to proceed with GL customs clearance services -18 -Import Customs entry processed -Global Logistics-ET -No - - -19 -Duty and taxes notified -Global Logistics-ET -No - - -20 -Duty and taxes Paid -Customer /Bank system generated -Yes -When the Unique serial number for payment is settled the system will autofill date and time -21 -Customs Risk Assigned -Global Logistics-ET -No - - -22 -Import Release Permitted -Global Logistics-ET -No - - -23 -Storage and Demurrage Invoice Raised -Finance - - - - -24 -Storage and Demurrage Invoice Paid -Customer /Bank system generated -Yes -When the Unique serial number for payment is settled the system will autofill date and time -25 -Last Mile truck requested -Global Logistics-ET -No -Only if the customer requests for last mile service -26 -Truck Assigned -Truck and Machinary -No - - -27 -Truck Arrived -Port and terminal -No - - -28 -Truck loaded -Port and terminal -No - - -29 -Truck waybill generated -Global Logistics-ET -Yes -When the truck waybill is printed, it will automatically capture the date and time -30 -Dispatched -Port and Terminal -No - - - - - diff --git a/docs/freight-platform/SYSTEM-FLOW.md b/docs/freight-platform/SYSTEM-FLOW.md deleted file mode 100644 index f80039ee1..000000000 --- a/docs/freight-platform/SYSTEM-FLOW.md +++ /dev/null @@ -1,542 +0,0 @@ -# EDR Freight — System Flow (API) - -How the freight API drives a shipment end to end: - -**Onboarding → Contract → Booking → Clearance → Operation → Schedule → Delivery** - -This document tracks the **API logic only** — exact statuses, transitions, branches, and endpoints as the `edr-freight-api` implements them. Read arrows (`→`) as "then". **IF** blocks are the branches. - -> Two facts shape everything below: -> 1. **Clearance is per-booking, not pre-booking** (except the one-time-customs special case). The contract agrees terms; each booking carries its own customs document loop. -> 2. **Who creates the booking depends on customs.** No customs → the customer. Customs (Path B) → Global Logistics (GL) on the customer's behalf. - ---- - -## 1) Onboarding - -> Register the company so a profile can transact. Only an **active profile** may create contracts or bookings. - -``` -Company → status PENDING -Each operation profile (importer | exporter | freight_forwarder) → status PENDING -Backoffice approves each profile → ACTIVE + reference (IM-00001 / EX-00001 / FF-00001) - → only an ACTIVE profile can create contracts / bookings -``` - -Documents required depend **only on nationality**: - -| Nationality | Company documents | -|-------------|-------------------| -| Ethiopian | TIN Certificate · Commercial License · National ID | -| Foreign | TIN Certificate · Investment License · National ID · Passport | - -Operation type adds one business-license card per profile — nothing else. - ---- - -## 2) Contract - -> Agree terms (route, cargo, price), run the approval chain, sign. Service: `ContractTransitionService`. - -### Setup choices that decide later paths - -``` -contractKind: ONE_TIME (one shipment per contract) - GENERAL (many shipments over a validity window) - -tradeDirection: IMPORT | EXPORT | DOMESTIC - -customsClearingEnabled: true → Path B (GL clears + books) — IMPORT/EXPORT - false → Path A (customer self-clears + books) — IMPORT/EXPORT - (DOMESTIC → no clearance at all) -``` - -### Status enum (`CONTRACT_STATUSES`) - -``` -DRAFT · SUBMITTED · PRICE_CHANGED_PENDING_CONFIRM · CHANGES_REQUESTED -PENDING_APPROVAL · APPROVED · APPROVED_PENDING_SIGNATURE · CONTRACT_READY -SIGNED_CUSTOMER · FULLY_EXECUTED · CONTRACT_ACTIVE -AWAITING_CLEARANCE_DOCUMENTS · CLEARANCE_UNDER_REVIEW · CLEARANCE_READY_FOR_BOOKING -ACTIVE_SHIPMENT_IN_PROGRESS · CONTRACT_CLOSED · EXPIRED -REJECTED · CANCELLED -RENEWAL_DRAFT · RENEWAL_SUBMITTED · RENEWAL_PENDING_APPROVAL · AMENDMENTS_PROPOSED · ARCHIVED -``` - -Clearance enum (`CONTRACT_CLEARANCE_STATUSES`): `NOT_APPLICABLE · AWAITING_DOCUMENTS · DOCUMENTS_UNDER_REVIEW · CLEARANCE_READY_FOR_BOOKING · SELF_CLEARED · ACTIVE_SHIPMENT_IN_PROGRESS` - -### Transition table - -| Method | Guard (allowed status) | Result | -|--------|------------------------|--------| -| `submit()` | DRAFT, CHANGES_REQUESTED | freeze rates → `SUBMITTED` | -| `confirmSubmit()` | PRICE_CHANGED_PENDING_CONFIRM | freeze rates → `SUBMITTED` | -| `staffAccept(validityDays)` | SUBMITTED | set validity window, build approval chain → `PENDING_APPROVAL` | -| `requestChanges()` | SUBMITTED | review note → `CHANGES_REQUESTED` | -| `reject()` | SUBMITTED, PENDING_APPROVAL | → `REJECTED` | -| `approveStep(stepId, role)` | PENDING_APPROVAL, APPROVED_PENDING_SIGNATURE | complete step in order; all done → `APPROVED` | -| `generateContract()` | APPROVED, APPROVED_PENDING_SIGNATURE | render PDF → `CONTRACT_READY` | -| `sign(CUSTOMER)` | CONTRACT_READY | apply signature → `SIGNED_CUSTOMER` → auto `counterSign()` | -| `counterSign(STAFF/DIRECTOR/CEO)` | SIGNED_CUSTOMER | **branch on path** ↓ | -| `renew()` | any | clone with `renewalOfId`, version++ → `RENEWAL_DRAFT` | - -**Approval chain** (`instantiateApprovalSteps`): `LINE_STAFF` → optional `DIRECTOR` → optional `CEO`. Director required when `freightType = BULK` OR `cargoType.requiresDirectorApproval`. - -### The counter-sign branch — THIS is where the model differs from "clearance first" - -``` -IF GENERAL + customsClearingEnabled (Path B): - NO contract-level clearance cycle. - status → CONTRACT_ACTIVE, clearanceStatus → NOT_APPLICABLE - → customer requests shipments; GL creates + clears each booking (per-booking) - -IF ONE_TIME + customs (Path A self-clear OR one-time-customs): - open a contract clearance cycle - status → AWAITING_CLEARANCE_DOCUMENTS, clearanceStatus → AWAITING_DOCUMENTS - → contract-level clearance loop (section 3), then booking - -IF DOMESTIC (no customs): - status → FULLY_EXECUTED (ONE_TIME) or CONTRACT_ACTIVE (GENERAL) - → customer books immediately (section 4) -``` - -> So contract-level clearance (section 3) only runs for the **one-time + customs** case. The common GENERAL-customs case goes straight to `CONTRACT_ACTIVE` and defers all clearance to the booking (section 5). - -### Side branches - -``` -staff requestChanges → CHANGES_REQUESTED → customer edits → submit → SUBMITTED -staff reject → REJECTED -GENERAL contract → renew → RENEWAL_DRAFT (clone of prior version) -``` - -### Endpoints (`contracts.controller.ts`) - -``` -POST /contracts/:id/submit submit() -POST /contracts/:id/confirm-submit confirmSubmit() -POST /contracts/:id/staff/accept staffAccept() -POST /contracts/:id/staff/request-changes requestChanges() -POST /contracts/:id/staff/reject reject() -POST /contracts/:id/approval-steps/:stepId/approve approveStep() -POST /contracts/:id/contract/generate generateContract() -GET /contracts/:id/contract/view view PDF/HTML -POST /contracts/:id/contract/sign sign() -POST /contracts/:id/renew renew() -GET /contracts/:id/capacity remaining drawdown (GENERAL) -POST /contracts/:id/bookings create booking under contract (section 4) -GET /contracts/list-summary list -GET /contracts/booking-requests/queue staff shipment-request queue -``` - ---- - -## 3) Contract-level clearance (ONE_TIME + customs only) - -> Runs only when the counter-sign branch opened a contract clearance cycle. -> Service: `ContractClearanceService`. Loop: **upload → review (approve/query) → re-upload → finalize.** - -### Who reviews - -``` -Path A (customsClearingEnabled = false) → OPERATIONS -Path B (customsClearingEnabled = true) → GLOBAL LOGISTICS Ethiopia (GL ET) -``` - -### Document states & loop - -``` -each document: PENDING → APPROVED (reviewer approves) - → QUERIED (reviewer demands re-upload, note required) - → contract back to AWAITING_CLEARANCE_DOCUMENTS - (only queried docs re-upload; approved stay) - -1. customer uploads all required docs → CLEARANCE_UNDER_REVIEW, each doc PENDING -2. reviewer goes doc by doc (approve / query) -3. customer re-uploads queried docs → back to step 2 -4. all required docs APPROVED → finalize -``` - -### Required-doc resolution (`contract-clearance.util.ts`) - -``` -input (customer uploads): - Path B: contract_clearance_{import|export}_{container|bulk} - Path A: contract_clearance_selfclear_{import|export}_{container|bulk} - DOMESTIC: null (no gate) - -output (GL uploads, container customs only): - contract_clearance_output_{import|export}_container - (bulk or non-customs → null) -``` - -### Finalize - -``` -opsFinalize() Path A — requires every required input doc APPROVED - → clearanceStatus = SELF_CLEARED - → contract status = CONTRACT_ACTIVE (GENERAL) | FULLY_EXECUTED (ONE_TIME) - → CUSTOMER creates booking - -finalize() Path B — requires every required input APPROVED + every required output uploaded - → clearanceStatus = CLEARANCE_READY_FOR_BOOKING - → GL creates booking -``` - -### Endpoints - -``` -GET /contracts/:id/clearance document grid -POST /contracts/:id/clearance/documents customer upload (multipart) -POST /contracts/:id/clearance/review GL approve | query -POST /contracts/:id/clearance/output-documents GL upload output docs -POST /contracts/:id/clearance/finalize GL finalize → CLEARANCE_READY_FOR_BOOKING -POST /contracts/:id/clearance/ops-review Ops approve | query -POST /contracts/:id/clearance/ops-finalize Ops finalize → SELF_CLEARED -GET /contracts/clearance/queue GL ET queue (customs contracts) -GET /contracts/clearance/ops-queue Operations queue (self-clear) -GET /contracts/clearance/history GL completed -GET /contracts/clearance/ops-history Ops completed -``` - ---- - -## 4) Booking — who creates it & the gate - -> Turn a ready contract into a shipment. Service: `ContractBookingService` (creation gate), `BookingTransitionService` (lifecycle). -> Endpoint: `POST /contracts/:id/bookings`. - -### The gate (`assertGate`) - -``` -Customs (Path B) — only GL ET (needs contracts.createBooking permission): - ONE_TIME : contract.clearanceStatus = CLEARANCE_READY_FOR_BOOKING - GENERAL : contract.status = CONTRACT_ACTIVE - booking starts in AWAITING_DOCUMENTS (per-booking clearance), createdByRole = GL_ET - -No customs (Path A / DOMESTIC) — customer or staff: - contract.status = FULLY_EXECUTED or CONTRACT_ACTIVE - booking starts in OPERATION_REQUEST_PENDING (no clearance gate), - createdByRole = CUSTOMER | STAFF -``` - -So per-booking clearance applies to **every customs booking** — both the GENERAL drawdown and the one-time case (whose contract cycle already ran). Path A / domestic bookings skip straight to the operation request. - -### Booking status enum (`BOOKING_STATUSES`) - -``` -DRAFT · SUBMITTED · PRICE_CHANGED_PENDING_CONFIRM · CHANGES_REQUESTED -PENDING_APPROVAL · APPROVED_PENDING_SIGNATURE · APPROVED · CONTRACT_READY -SIGNED_CUSTOMER · FULLY_EXECUTED -AWAITING_DOCUMENTS · DOCUMENTS_UNDER_REVIEW · CLEARANCE_READY -OPERATION_REQUEST_PENDING · OPERATION_CHANGES_REQUESTED -SELECTED_FOR_BATCH · ROAD_DISPATCH_PENDING · PAID · IN_TRANSIT · COMPLETED -REJECTED · CANCELLED · EXPIRED · PENDING_CONSOLIDATION · CONSOLIDATED -``` - -Payment (`paymentStatus`): `PENDING → PNR_GENERATED → VERIFICATION_IN_PROGRESS → PAID` (or `FAILED`). -Scheduling (`schedulingStatus`): `NOT_SCHEDULED · HOLDING · ELIGIBLE · SCHEDULED · DISPATCHED`. - -### Customer-self-booking lifecycle (Path A / domestic — same approval shape as a contract) - -``` -DRAFT - → generate-price → submit - price unchanged → SUBMITTED - price changed → PRICE_CHANGED_PENDING_CONFIRM → confirm-submit → SUBMITTED - → acceptIntake (staff) → PENDING_APPROVAL (validity window + approval steps) - → approveStep ×N (LINE_STAFF → DIRECTOR → CEO) → APPROVED (auto-generates contract) - → CONTRACT_READY → customerSign → SIGNED_CUSTOMER - → marketingApprove → FULLY_EXECUTED (sets fullyExecutedAt, lockedAt) - → (then operation request, section 5) -``` - -Side branches: `requestChanges → CHANGES_REQUESTED`; `staffReject / reject → REJECTED`; `cancel → CANCELLED` (DRAFT…CONTRACT_READY). - -### Transition methods (`BookingTransitionService`) - -``` -submit · confirmSubmit · requestChanges · acceptIntake · staffReject · reject -approveStep · rejectStep · generateContract · customerSign · marketingApprove -governmentExpedite (govt fast-path → PAID) -requestOperation · proceedToOperation · reviewOperationRequest -submitClearanceDocuments · reviewDocument · uploadClearanceOutputDocuments · finalizeClearance -startTransit · complete · cancel · requestConsolidation · removeConsolidation -``` - ---- - -## 5) Per-booking clearance + operation request - -> The customs booking's own document loop, then everyone funnels into the operation request that puts the shipment in the schedule pool. - -### Per-booking clearance (customs bookings only) - -``` -GL creates booking → AWAITING_DOCUMENTS - submitClearanceDocuments (customer upload) → DOCUMENTS_UNDER_REVIEW - reviewDocument (GL) → doc APPROVED | QUERIED (note) - uploadClearanceOutputDocuments (GL, customs output) - finalizeClearance requires 100% required inputs APPROVED + required outputs present - → CLEARANCE_READY - proceedToOperation → OPERATION_REQUEST_PENDING -``` - -inputCode / outputCode resolve from `(tradeDirection, freightType, customsClearingEnabled)` — same scheme as the contract loop. Doc review status: `PENDING · APPROVED · QUERIED`. - -### Operation request (all paths) - -``` -requestOperation() guard CLEARANCE_READY | OPERATION_CHANGES_REQUESTED, valid schedule day - → OPERATION_REQUEST_PENDING - (Path A / domestic bookings begin life here directly) - -reviewOperationRequest(decision): - ACCEPT → acceptOperationRequest(): - train service → FULLY_EXECUTED + enqueue batch fill (origin, dest, day) - truck service → ROAD_DISPATCH_PENDING (skips train pool, section 7) - REQUEST_CHANGES → OPERATION_CHANGES_REQUESTED (note; customer resubmits) -``` - -### Clearance / operation endpoints (`bookings.controller.ts`) - -``` -GET /bookings/:id/clearance docs grid + GL review state -POST /bookings/:id/clearance/documents submitClearanceDocuments → DOCUMENTS_UNDER_REVIEW -POST /bookings/:id/clearance/review reviewClearanceDocument (approve | query) -POST /bookings/:id/clearance/output-documents GL upload output -POST /bookings/:id/clearance/finalize finalizeClearance → CLEARANCE_READY -POST /bookings/:id/clearance/proceed proceedToOperation → OPERATION_REQUEST_PENDING -POST /bookings/:id/operation/review reviewOperationRequest (ACCEPT | REQUEST_CHANGES) -``` - -Booking lifecycle endpoints (selected): - -``` -POST /bookings · PATCH /bookings/:id · DELETE /bookings/:id (DRAFT only) -POST /bookings/:id/generate-price · /submit · /confirm-submit · /reject -POST /bookings/:id/staff/accept · /staff/request-changes · /staff/reject -POST /bookings/:id/approval-steps/:stepId/approve · /reject -POST /bookings/:id/contract/generate · /contract/sign · GET /contract/view -POST /bookings/:id/marketing/approve → FULLY_EXECUTED -POST /bookings/:id/government-expedite govt → PAID -POST /bookings/:id/operations/start-transit → IN_TRANSIT -POST /bookings/:id/operations/complete → COMPLETED -POST /bookings/:id/cancel → CANCELLED -GET /bookings/my customer payable list -GET /bookings/queues/:queue intake | approval | signatures | marketing | finance -GET /bookings/:id/tracking shipment tracking -POST /bookings/:id/consolidation · DELETE · GET pair partial-wagon bookings -``` - ---- - -## 6) GL Phase 2 — post-booking milestones (customs / Path B) - -> Once GL creates a customs booking, the system seeds the **post-booking milestone timeline**. -> Services: `ClearanceMilestoneService` (seed/advance), `GlOperationsService` (structured actions). -> `seedPostBookingMilestones(booking)` runs at GL booking creation. Each milestone: `PENDING → COMPLETED` (or `SKIPPED`). - -### Milestone owners - -`ET` (GL Ethiopia) · `DJ` (GL Djibouti) · `OPS` (Operations) · `CUST` (Customer) - -### Import timeline (catalog order, post-booking segment) - -``` -WAGON_REQUESTED → FREIGHT_PAYMENT_SETTLED → WAGON_ALLOCATED → GATEPASS_GRANTED -→ READY_FOR_LOADING → LOADED → DEPARTED_FROM_DJIBOUTI [HANDOFF ET↔DJ] -→ ARRIVED_ETHIOPIA → OFFLOADED → T1_CLOSED → RISK_ASSIGNED -→ IMPORT_RELEASE_GRANTED → IMPORT_PROCESS_COMPLETED -→ STORAGE_INVOICE_RAISED → EXIT_NOTE_GENERATED -``` - -(Pre-booking import milestones — `IMPORT_DOCS_UPLOADED · PENDING_DOCUMENT_REVIEW · DOCUMENTS_APPROVED · UNDER_CUSTOMS_CLEARANCE · DECLARED · DUTY_TAXES_ADVISED · DUTY_TAX_PAID · DO_COLLECTED` — track the clearance loop and end at `DO_COLLECTED`.) - -### Export timeline (post-booking segment) - -``` -WAGON_REQUESTED → FREIGHT_PAYMENT_PENDING → FREIGHT_PAYMENT_SETTLED → WAGON_ALLOCATED -→ CARGO_ARRIVED → READY_FOR_LOADING → LOADED → DEPARTED_TO_DJIBOUTI [HANDOFF] -→ ARRIVED_AT_DJIBOUTI → GATEPASS_GRANTED → OFFLOADED -``` - -(Pre-booking export: `EXPORT_DOCS_UPLOADED · PENDING_DOCUMENT_REVIEW · DOCUMENTS_APPROVED · RELEASE_ORDER_SECURED · UNDER_CUSTOMS_CLEARANCE · DECLARED · EXPORT_RELEASED`.) - -### Advance logic - -``` -completeForBooking(bookingId, code, userId?, note?) mark COMPLETED + triggeredAt/By -completeForContract(contractId, code, …) pre-booking contract milestones -completeByDocTrigger(scope, code) auto-complete from a doc upload -onHandoff(bookingId, code) fires on DEPARTED_* (notification reserved) -``` - -### Structured GL actions (`GlOperationsService` + `gl-operations.dto.ts`) - -``` -assignRisk riskLevel = GREEN | YELLOW | RED → completes RISK_ASSIGNED -adviseDuty {amount, currency, declarationSerial?} → completes DUTY_TAXES_ADVISED - customer uploads slip → DUTY_TAX_PAID -assignStation {stationYardId, staffId?} sets glStationYardId/glAssignedStaffId (GL US-02) -reportIncident incidentType = SEAL_BROKEN | CONTAINER_OPENED | CONTAINER_DAMAGED | FLUID_LEAKING - + description + photos → ClearanceIncident -uploadDocuments / uploadDutySlip → doc-triggered milestone auto-complete -``` - -### Doc-triggered milestones (`DOC_CODE_TO_MILESTONE`) - -| Upload (code) | Completes | Owner | -|---------------|-----------|-------| -| `delivery_order` | DO_COLLECTED | GL DJ | -| `release_order` | RELEASE_ORDER_SECURED | GL DJ | -| `t1_transport_document` | T1_CLOSED | GL ET | -| `import_release` | IMPORT_RELEASE_GRANTED | GL ET | -| `full_in_interchange` | OFFLOADED | GL DJ | -| `final_declaration` | IMPORT_PROCESS_COMPLETED | GL ET | -| `duty_tax_receipt` | DUTY_TAX_PAID | **Customer** | -| `incident_photo` | (logging only, no milestone) | — | - -### Endpoints - -``` -POST /contracts/bookings/:bookingId/milestones/:code/complete manual complete -POST /contracts/:id/milestones/:code/complete pre-booking contract milestone -POST /contracts/bookings/:bookingId/risk assignRisk -POST /contracts/bookings/:bookingId/duty adviseDuty -POST /contracts/bookings/:bookingId/station-assign assignStation -POST /contracts/bookings/:bookingId/documents GL doc upload (DO/RO/T1/…) -POST /contracts/bookings/:bookingId/duty-slip customer duty slip -GET /contracts/bookings/:bookingId/incidents list -POST /contracts/bookings/:bookingId/incidents reportIncident -GET /contracts/bookings/:bookingId/milestones timeline -``` - -> Still manual-only (no fee engine): `STORAGE_INVOICE_RAISED`, `EXIT_NOTE_GENERATED`. Out of scope: demurrage auto-calc, finance AP closure, multimodal, truck waybill/POD. - ---- - -## 7) Schedule — demand batching (`booking-batch.service.ts`) - -> Day-level pooling. Customer picks a **day**; the batch engine assigns the actual **train** later. -> Cron groups bookings by `(origin yard, destination yard, day)`. EAT timezone, 3-hour windows (00–03 … 21–24). - -### Pool states (`boardState`, read-only view) - -``` -READY FULLY_EXECUTED + fullyExecutedAt, no train link yet -SELECTED_FOR_BATCH picked by fill, in pay window (trainScheduleId set, paymentDeadline set) -ALLOCATED linked to train via TrainScheduleBooking, PAID (or govt) -WAITING PAID but not yet linked (staff-reconciled) -EXPIRED failed to pay in window -PENDING_CONTRACT any other non-terminal state -``` - -### The cron cycle (every 3h; prod `0 */3 * * *`) - -``` -1. Fill distribute (route, dest, day) pool across OPEN schedules by priorityScore - pick earliest train; fit bookings (govt preempts commercial) - commercial → reserve: SELECTED_FOR_BATCH + paymentDeadline = now + 1h - govt → allocate: PAID, SCHEDULED -2. Settle 1h after window closes — allocate paid reservations, - expire unpaid (→ EXPIRED, unpin train), top up from waiting list -3. Reconcile link orphaned PAID bookings to a schedule -4. Allocate auto-assign wagon slots to allocated bookings -``` - -`reserve()` → SELECTED_FOR_BATCH. `allocate()` → create `TrainScheduleBooking`, PAID, schedulingStatus SCHEDULED. `expire()` → trainScheduleId null, EXPIRED, schedulingStatus ELIGIBLE (back in pool). - -### Payment → transit - -``` -SELECTED_FOR_BATCH / AWAITING_PAYMENT → pay (Telebirr) → PAID -PAID → startTransit → IN_TRANSIT → complete → COMPLETED -``` - -### Wagon math - -``` -wagonsRequired = ⌈ Σ over containers (qty × wagonsPerUnit) ⌉ -``` - -### Key endpoints (`train-scheduling.controller.ts`) - -``` -GET /train-scheduling/available-days days with OPEN departures -GET /train-scheduling/available-days-for-cargo capacity-aware bookable days -GET /train-scheduling/bookable-schedules OPEN same-route schedules -GET /train-scheduling/batch-board monitoring board (states + counts) -GET /train-scheduling/eligible-bookings PAID/FULLY_EXECUTED ready to allocate -POST /train-scheduling/{container|bulk}/schedules create schedule -POST /train-scheduling/schedules/:id/assign-bookings -POST /train-scheduling/schedules/:id/run-batch staff manual fill -POST /train-scheduling/schedules/:id/run-allocation staff manual wagon allocation -POST /train-scheduling/schedules/:id/finalize · /dispatch -PATCH /train-scheduling/schedules/:id/booking-window OPEN | CLOSE -POST /train-scheduling/bookings/:bookingId/mark-paid · /expire · /move-schedule -GET /train-scheduling/schedules/:id/checkpoints · POST /checkpoints tracking events -``` - ---- - -## 8) Delivery / last mile - -``` -Road service: reviewOperationRequest ACCEPT → ROAD_DISPATCH_PENDING (skips train pool) - billed by KM, dispatched by truck (First-Mile operations) - -First/last mile: pickup + delivery addresses captured at booking; equipment return WITH | WITHOUT - last-mile: PAYMENT_PENDING → READY_TO_TRANSIT → IN_TRANSIT → RECEIVED_TO_PORT -``` - ---- - -## The whole thing on one page - -``` -ONBOARD - profile (importer/exporter/FF) approved → ACTIVE → can transact - -CONTRACT (ContractTransitionService) - submit → staffAccept → approveStep×N (LINE_STAFF→DIRECTOR→CEO) → CONTRACT_READY - → sign(CUSTOMER) → counterSign → BRANCH: - GENERAL + customs → CONTRACT_ACTIVE (NOT_APPLICABLE) — clearance deferred to booking - ONE_TIME + customs → AWAITING_CLEARANCE_DOCUMENTS — contract clearance cycle (section 3) - DOMESTIC / no customs → FULLY_EXECUTED | CONTRACT_ACTIVE — book now - -BOOKING (POST /contracts/:id/bookings, gate in ContractBookingService) - customs (Path B) → only GL → starts AWAITING_DOCUMENTS - no customs (Path A/domestic) → customer/staff → starts OPERATION_REQUEST_PENDING - -PER-BOOKING CLEARANCE (customs only, BookingTransitionService) - upload → review(approve/query) → finalize → CLEARANCE_READY → proceed → OPERATION_REQUEST_PENDING - -OPERATION REQUEST (all paths) - requestOperation → OPERATION_REQUEST_PENDING - reviewOperationRequest ACCEPT → FULLY_EXECUTED (train) | ROAD_DISPATCH_PENDING (truck) - -GL PHASE 2 (customs, post-booking) — ClearanceMilestoneService + GlOperationsService - milestone timeline: wagon → pay → allocate → load → depart [handoff] - → arrive → offload → T1 close → risk → release → complete - GL actions: risk (G/Y/R) · duty advise · station assign · DO/RO/T1 upload · incident - customer: watch read-only · upload duty slip · pay freight - -SCHEDULE (booking-batch.service.ts, 3h EAT cron) - fill (route,dest,day) → SELECTED_FOR_BATCH (+1h pay) → pay → PAID/ALLOCATED - → IN_TRANSIT → COMPLETED (road → ROAD_DISPATCH_PENDING → truck) -``` - ---- - -### Code map - -| Area | Key files (`apps/edr-freight-api/src/modules/…`) | -|------|---------------------------------------------------| -| Contract state machine | `contracts/contract-transition.service.ts`, `contracts/entities/contract.entity.ts`, `contracts/contracts.controller.ts` | -| Contract clearance | `contracts/contract-clearance.service.ts`, `contracts/contract-clearance.util.ts` | -| Booking gate | `contracts/contract-booking.service.ts` | -| Booking lifecycle + per-booking clearance | `bookings/booking-transition.service.ts`, `bookings/entities/booking.entity.ts`, `bookings/bookings.controller.ts` | -| GL Phase 2 | `contracts/clearance-milestone.service.ts`, `contracts/clearance-milestone.catalog.ts`, `contracts/gl-operations.service.ts`, `contracts/dto/gl-operations.dto.ts`, `contracts/entities/clearance-incident.entity.ts` | -| Schedule / batch engine | `train-scheduling/booking-batch.service.ts`, `train-scheduling/train-scheduling.service.ts`, `train-scheduling/train-scheduling.controller.ts` | -``` diff --git a/docs/new-doc.md b/docs/new-doc.md deleted file mode 100644 index b7a09f46b..000000000 --- a/docs/new-doc.md +++ /dev/null @@ -1,1872 +0,0 @@ -# Contract–Booking Separation & Global Logistics Design - -**Version:** 1.1 -**Date:** 2026-06-26 -**Revision:** Customs-clearance-first path — GL owns booking creation; customer pays only. -**Scope:** Contracts, bookings, scheduling, allocation, and Global Logistics (GL) workflows -**References:** -- [ITLMS Operation Workflow V2.pdf](./ITLMS%20Operation%20Workflow%20V2.pdf) -- [ITMLS Customer and Marketing User Stories V3.0 (2).pdf](./ITMLS%20Customer%20and%20Marketing%20User%20Stories%20V3.0%20(2).pdf) -- [GlobaL Logistics- Unimodal Import and Export and Multimodal Import V02.docx.pdf](./GlobaL%20Logistics-%20Unimodal%20Import%20and%20Export%20and%20Multimodal%20Import%20V02.docx.pdf) -- Codebase: `edr-platform/apps/edr-freight-api`, `edr-platform/apps/edr-freight-web` - ---- - -## Table of Contents - -1. [Executive Summary](#1-executive-summary) -2. [Design Principles](#2-design-principles) -3. [Current Architecture](#3-current-architecture) -4. [Target Architecture](#4-target-architecture) -5. [Database Schema](#5-database-schema) -6. [Status Machines](#6-status-machines) -7. [Contract Wizard Specification](#7-contract-wizard-specification) -8. [Booking Creation Specification](#8-booking-creation-specification) -9. [Pricing Model](#9-pricing-model) -10. [Renewal and Expiry Rules](#10-renewal-and-expiry-rules) -11. [Global Logistics — Import Unimodal](#11-global-logistics--import-unimodal) -12. [Global Logistics — Export Unimodal](#12-global-logistics--export-unimodal) -13. [Customs Clearance Path — GL-Owned Execution](#13-customs-clearance-path--gl-owned-execution) -14. [Gap Analysis Matrix](#14-gap-analysis-matrix) -15. [API Endpoint Mapping](#15-api-endpoint-mapping) -16. [Frontend Route & Component Mapping](#16-frontend-route--component-mapping) -17. [Migration Plan](#17-migration-plan) -18. [Out of Scope / Future Work](#18-out-of-scope--future-work) -19. [Open Items for Sign-Off](#19-open-items-for-sign-off) - ---- - -## 1. Executive Summary - -The EDR freight platform currently **collapses the legal contract and the operational shipment into a single `freight.bookings` row**. The customer wizard collects container quantities, weights, and binding shipment details at intake; clearance documents, contract signatures, batch scheduling, and payment all attach to that same row. - -Stakeholder requirements and the reference PDFs describe a **two-phase lifecycle**: - -1. **Contract phase** — customer selects operation type, contract kind (general / one-time), service, cargo *scope* (20ft/40ft sizes or bulk commodity — **no quantities**), routes, hazard/reefer flags, estimated date, intake documents, and **unit-rate pricing**. Marketing approves, both parties sign. No booking exists yet. -2. **Execution phase** — behaviour splits by whether the contract includes **customs clearance (Global Logistics service)**: - - **Path A — Transport only (no customs):** After contract is fully executed, the **customer** creates a shipment booking (schedule date, quantities, container numbers, VGM, hazard/reefer counts), then enters batch → payment → allocation → transit as today. - - **Path B — Transport with customs clearance:** After contract is fully executed, the **customer uploads clearance documents to GL** (no booking yet). Ethiopian GL reviews and approves customer docs, uploads GL output documents, and runs the clearance workflow. **GL Ethiopia exclusively creates the booking** — entering schedule date, quantities, container numbers, VGM, bulk ton/item counts, and all other shipment details. The **customer does not use the booking wizard**. From booking creation onward, batch selection, wagon allocation, milestones, and transit proceed as today; the **only customer action in the operational pipeline is payment** (freight PNR, and duty/tax slips when advised during clearance). - -This document specifies a **minimal-change evolution**: introduce a first-class `freight.contracts` table by extracting contract-phase columns from `bookings`, add `contract_id` to bookings, and preserve all existing scheduling, payment, and allocation mechanics. - -**Confirmed stakeholder decisions:** -- **Customs clearance path (Path B):** Clearance documents are collected **after contract signing and before any booking row exists**. GL owns booking creation and data entry; customer pays only. -- **Transport-only path (Path A):** Customer creates bookings under the contract as normal. -- Each shipment cycle under a customs contract follows: sign contract → customer clearance docs → GL approval → GL creates booking → customer pays → ops pipeline. -- One-time contracts allow **one active booking at a time**; if payment expires the booking expires but the contract stays valid for a new clearance cycle + GL re-booking until contract validity ends. -- General contracts allow **multiple shipment cycles** over the validity window (each cycle: clearance docs → GL booking → payment) with no quantity pool caps at contract level. - ---- - -## 2. Design Principles - -| Principle | Rationale | -|-----------|-----------| -| **Extract, don't rewrite** | Keep `payments`, `train_schedules`, `wagon_booking_allocations`, and batch engine untouched; link via `bookings.contract_id`. | -| **Unit rates at contract, totals at booking** | Contract pricing shows per-container / per-ton / per-item rates; booking computes actual totals from entered quantities. | -| **Scope vs. execution** | Contract defines *what is allowed* (sizes, commodities, routes, flags); booking defines *what is shipped* (qty, container numbers, dates). | -| **Pre-booking clearance (customs path)** | For contracts with customs clearance, customer uploads clearance docs **after signing, before booking exists** — stored on `contract_id` via `contract_document_review`. | -| **GL-owned booking (customs path)** | When `customs_clearing_enabled = true`, only Ethiopian GL creates bookings and enters all shipment fields; customer portal has no booking wizard. | -| **Customer pays only (customs path)** | After GL creates the booking and it reaches the payment gate, the customer pays freight PNR (and duty/tax slips during clearance when advised). All other steps are GL/Ops. | -| **Contract intake docs separate** | Wizard step 6 documents (commercial framework / onboarding attachments) attach to `contract_id` at submission — distinct from post-sign clearance docs. | -| **Preserve approval hierarchy** | Line Staff → Director → CEO routing from US-06 moves to `contract_approval_steps`; booking operations review stays on booking. | -| **Dual GL teams** | Ethiopian GL (`edr_gl_ethiopia`) and Djibouti GL (`edr_gl_djibouti`) with region-scoped queues and milestone ownership. | -| **Backward-compatible payments** | Payment webhooks continue using `ref_id = booking.id`; no payment microservice change. | - ---- - -## 3. Current Architecture - -### 3.1 Data Model (Today) - -All entities live in PostgreSQL schema **`freight`**. - -```mermaid -erDiagram - companies ||--o{ bookings : owns - bookings ||--o{ booking_container : has_qty_at_intake - bookings ||--o{ booking_contract_signatures : signs - bookings ||--o{ booking_document_review : clearance - bookings ||--o{ booking_approval_step : approves - bookings ||--o{ booking_rate_snapshot : rates - bookings ||--o{ payments : pays - bookings ||--o| train_schedule_bookings : scheduled - bookings ||--o{ booking_orders : general_contract_drawdown - booking_orders ||--o| bookings : spawns_child_ONE_TIME - bookings ||--o{ contract_route_lines : multi_route_qty_pools -``` - -**Key observation:** `booking_type = GENERAL_CONTRACT` bookings are contracts; `booking_orders` spawns child `ONE_TIME` bookings for each drawdown. One-time bookings are simultaneously the contract and the shipment. - -### 3.2 Current API Modules - -| Module | Path | Role | -|--------|------|------| -| Bookings | `src/modules/bookings/` | Create, price, submit, approve, sign, clearance, operation review | -| Booking Orders | `src/modules/booking-orders/` | General contract drawdown (`POST /booking-orders`) | -| Payment | `src/modules/payment/` | PNR, webhook finalization | -| Train Scheduling | `src/modules/train-scheduling/` | Batch pool, wagon allocation | -| Contracts (PDF) | `src/contracts/` | Handlebars templates, PDF generation — operates on booking today | - -### 3.3 Current Booking API Endpoints - -Base path: `/bookings` - -| Method | Path | Phase | -|--------|------|-------| -| POST | `/` | Create (contract + shipment combined) | -| POST | `/:id/generate-price` | Pricing | -| POST | `/:id/submit` | Submit for approval | -| POST | `/:id/staff/accept` | Staff intake + contract validity | -| POST | `/:id/approval-steps/:stepId/approve` | Approval chain | -| POST | `/:id/contract/generate` | Generate contract PDF | -| POST | `/:id/contract/sign` | Customer / staff signature | -| POST | `/:id/clearance/*` | GL document gate | -| POST | `/:id/clearance/proceed` | Customer picks shipment day | -| POST | `/:id/operation/review` | Operations accept | -| POST | `/:id/payment/pay` | Payment | -| GET | `/queues/:queue` | Staff queues | - -Booking orders: `POST /booking-orders`, `GET /booking-orders/contract/:id/pool` - -### 3.4 Current Frontend Flow - -**Portal** (`edr-freight-web/portal/`): - -| Route | Component | Purpose | -|-------|-----------|---------| -| `/bookings/new` | `NewBookingPage.tsx` | 7-step wizard (operation → contract type → service → cargo → route → docs → review) | -| `/bookings/:id` | `BookingDetailPage` | Lifecycle actions | -| `/bookings/:id/contract` | `BookingContractPage` | Sign contract | -| `/contracts` | `ContractsList.tsx` | Lists `GENERAL_CONTRACT` bookings | -| `/contracts/:id` | `ContractDetailPage` | Pool + `PlaceOrderDialog` drawdown | - -**Backoffice** (`edr-freight-web/backoffice/`): - -| Route | Component | Purpose | -|-------|-----------|---------| -| `/dashboard/booking-requests` | `BookingRequestsPage` | Marketing approval | -| `/dashboard/clearance` | `DocumentClearanceListPage` | Single GL queue | - -### 3.5 Current Lifecycle (One-Time Booking) - -```mermaid -flowchart TD - A[DRAFT wizard with qty and weight] --> B[generate-price total amount] - B --> C[SUBMITTED] - C --> D[PENDING_APPROVAL staff accept sets validity] - D --> E[Approval chain APPROVED] - E --> F[CONTRACT_READY generate PDF] - F --> G[SIGNED_CUSTOMER] - G --> H[Staff counter-sign FULLY_EXECUTED or AWAITING_DOCUMENTS] - H --> I{Customs service?} - I -->|Yes IMPORT/EXPORT| J[Clearance on booking] - I -->|No DOMESTIC| K[Operation request] - J --> K[OPERATION_REQUEST_PENDING pick day] - K --> L[Ops review FULLY_EXECUTED] - L --> M[Batch pool SELECTED_FOR_BATCH] - M --> N[Payment PAID] - N --> O[Wagon allocation IN_TRANSIT] - O --> P[COMPLETED] -``` - -**General contract today:** Same through signing → `CONTRACT_ACTIVE` → `PlaceOrderDialog` spawns child booking via `booking_orders` → child enters clearance/batch pipeline. Quantity pools tracked in `contract_route_lines.quantity` and `booking_container.quantity`. - -### 3.6 Current Clearance Model - -- Utility: `clearance.util.ts` resolves setting codes from `trade_direction`, `freight_type`, `includesCustoms`. -- Review rows: `booking_document_review` keyed by `(booking_id, setting_code, file_key)`. -- Single IAM role: `edr_global_logistics` with `bookings:clearance_view`, `bookings:review_documents`, `bookings:upload_clearance_output`, `bookings:finalize_clearance`. -- Three booking statuses for clearance gate: `AWAITING_DOCUMENTS` → `DOCUMENTS_UNDER_REVIEW` → `CLEARANCE_READY`. - ---- - -## 4. Target Architecture - -### 4.1 Conceptual Split - -```mermaid -flowchart TD - subgraph contractPhase [Contract Phase] - C[contracts] - CR[contract_routes] - CCS[contract_cargo_scope] - CRS[contract_rate_snapshots] - CAS[contract_approval_steps] - CSIG[contract_signatures] - CDOC[contract intake files] - end - - subgraph bookingPhase [Booking Phase per Shipment] - B[bookings] - BC[booking_container + container_units] - BDR[booking_document_review] - BRS[booking_rate_snapshots] - PAY[payments] - TSB[train_schedule_bookings] - WBA[wagon_booking_allocations] - end - - C --> CR - C --> CCS - C --> CRS - C --> CAS - C --> CSIG - C --> CDOC - C --> B - B --> BC - B --> BDR - B --> BRS - B --> PAY - B --> TSB - B --> WBA -``` - -### 4.2 Target Lifecycle — Dual Execution Paths - -```mermaid -flowchart TD - subgraph contract [Contract Lifecycle — both paths] - C1[DRAFT contract wizard unit rates only] - C1 --> C2[SUBMITTED approval sign] - C2 --> C3[FULLY_EXECUTED or CONTRACT_ACTIVE] - end - - C3 --> Fork{Customs clearance service?} - - subgraph pathA [Path A — Transport only] - A1[Customer creates booking wizard] - A1 --> A2[Operation request batch] - A2 --> A3[Customer pays] - A3 --> A4[Allocation transit COMPLETED] - end - - subgraph pathB [Path B — Transport with customs clearance] - B1[Customer uploads clearance docs on contract] - B1 --> B2[GL ET reviews approves customer docs] - B2 --> B3[GL ET and GL DJ upload output docs milestones] - B3 --> B4[GL ET creates booking all shipment data] - B4 --> B5[Batch pool customer pays freight only] - B5 --> B6[GL Ops milestones allocation transit] - B6 --> B7[COMPLETED or EXPIRED payment] - end - - Fork -->|No| A1 - Fork -->|Yes| B1 - - B7 -->|ONE_TIME or GENERAL next cycle| B1 - A4 -->|GENERAL next shipment| A1 -``` - -**Path B summary (customs clearance contracts):** - -| Step | Actor | Action | -|------|-------|--------| -| 1 | Customer | Sign contract (Marketing counter-sign → `FULLY_EXECUTED` / `CONTRACT_ACTIVE`) | -| 2 | Customer | Upload clearance documents on **contract** (BL, invoice, license, etc.) — **no booking row yet** | -| 3 | GL Ethiopia | Review, approve, or query each document | -| 4 | GL Ethiopia / Djibouti | Upload customs output docs, advance clearance milestones | -| 5 | GL Ethiopia | **Create booking** — enter route, binding schedule date, container qty/numbers/VGM, bulk ton/item count, hazard/reefer counts | -| 6 | System / Ops | Batch selection, PNR generation | -| 7 | **Customer** | **Pay freight** (and duty/tax slips when advised during step 4) | -| 8 | GL / Ops | Wagon allocation, loading, departure, arrival, remaining milestones | -| 9 | Customer | Track shipment; no further data entry unless queried docs | - -**Path A summary (transport-only contracts):** Unchanged from prior design — customer uses booking wizard after contract sign; customer pays at batch gate. - -### 4.2.1 Shipment Cycles on General Contracts (Customs) - -For `contract_kind = GENERAL` with customs clearance, each new shipment repeats the Path B clearance cycle on the same contract: - -``` -CONTRACT_ACTIVE - → AWAITING_CLEARANCE_DOCUMENTS (customer uploads for this shipment) - → CLEARANCE_UNDER_REVIEW (GL ET reviews) - → CLEARANCE_READY_FOR_BOOKING (GL cleared to create booking) - → [GL creates booking] (contract may show ACTIVE_BOOKING_IN_PROGRESS) - → [booking completes / expires] - → CONTRACT_ACTIVE (ready for next shipment cycle) -``` - -Use optional `contract_clearance_cycles` (see §5.16) to distinguish multiple clearance rounds on one contract. - -### 4.3 What Stays Unchanged - -- `freight.payments` — `ref_id` remains booking UUID -- `freight.train_schedules`, `train_schedule_bookings` -- `freight.wagon_booking_allocations`, `wagon_allocation_container_items`, `wagon_allocation_bulk_loads` -- `BookingBatchService` — day-level pool, payment window, `EXPIRED` on booking -- `RuleEngineService` — rates, modifiers, weight limits (applied at booking with actual qty) -- `file_upload_settings` seeder pattern — extend with phased GL codes -- Contract PDF templates in `src/contracts/templates/` — resolver reads `contracts` instead of `bookings` - -### 4.4 What Is Deprecated (Phased) - -| Current | Replacement | -|---------|-------------| -| `bookings.booking_type = GENERAL_CONTRACT` | `contracts.contract_kind` | -| `booking_orders` ledger | Direct `bookings.contract_id` FK | -| `contract_route_lines.quantity` | Removed — routes only | -| `general-contract.service.getQuantityLines()` pool math | Validity window + optional soft limits only | -| `POST /bookings` for contract creation | `POST /contracts` | -| Contract fields on `bookings` | Migrated to `contracts` | - ---- - -## 5. Database Schema - -### 5.1 Entity Relationship (Target) - -```mermaid -erDiagram - companies ||--o{ contracts : owns - contracts ||--o{ contract_routes : has - contracts ||--o{ contract_cargo_scope : defines_scope - contracts ||--o{ contract_rate_snapshots : unit_rates - contracts ||--o{ contract_approval_steps : has - contracts ||--o{ contract_signatures : has - contracts ||--o{ contract_review_notes : has - contracts ||--o{ contract_document_review : pre_booking_clearance - contracts ||--o{ contract_clearance_cycles : shipment_cycles - contracts ||--o| contracts : renewal_of - contracts ||--o{ bookings : spawns - bookings ||--o{ booking_container : has - bookings ||--o{ booking_container_units : container_numbers - bookings ||--o{ booking_document_review : clearance - bookings ||--o{ booking_rate_snapshot : computed_total - bookings ||--o{ clearance_milestones : gl_tracking - bookings ||--o{ payments : pays - bookings ||--o| train_schedule_bookings : scheduled - yards ||--o{ contract_routes : origin_dest - service_types ||--o{ contracts : defines - cargo_types ||--o{ contract_cargo_scope : commodity -``` - ---- - -### 5.2 `freight.contracts` (NEW) - -Primary legal/commercial agreement. Replaces the contract-phase portion of `bookings`. - -```sql -CREATE TABLE freight.contracts ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - reference VARCHAR(64) NOT NULL UNIQUE, -- CTR-2026-00001 - - -- Ownership - company_id UUID REFERENCES freight.companies(id), - company_profile_id UUID REFERENCES freight.company_profiles(id), - is_government BOOLEAN NOT NULL DEFAULT FALSE, - government_institution VARCHAR(255), - - -- Classification - contract_kind VARCHAR(20) NOT NULL, -- ONE_TIME | GENERAL - renewal_of_id UUID REFERENCES freight.contracts(id), - trade_direction VARCHAR(10) NOT NULL, -- IMPORT | EXPORT | DOMESTIC - freight_type VARCHAR(20) NOT NULL, -- CONTAINER | BULK - - -- Service & commercial - service_type_id UUID NOT NULL REFERENCES freight.service_types(id), - payment_currency VARCHAR(5) NOT NULL, -- ETB | USD - customs_clearing_enabled BOOLEAN NOT NULL DEFAULT FALSE, - customs_clearing_agent VARCHAR(200), - equipment_return VARCHAR(20), -- with_return | without_return - - -- First / last mile (copied from booking.entity.ts) - first_mile_pickup_address TEXT, - first_mile_pickup_lat NUMERIC(10,7), - first_mile_pickup_lng NUMERIC(10,7), - last_mile_delivery_address TEXT, - last_mile_delivery_lat NUMERIC(10,7), - last_mile_delivery_lng NUMERIC(10,7), - - -- Cargo flags at contract level (NOT derived from container type) - is_hazardous BOOLEAN NOT NULL DEFAULT FALSE, - is_reefer BOOLEAN NOT NULL DEFAULT FALSE, - - -- Dates - estimated_shipment_date TIMESTAMPTZ, -- non-binding estimate from wizard - contract_validity_days INT, - contract_valid_from TIMESTAMPTZ, - contract_valid_until TIMESTAMPTZ, - expires_at TIMESTAMPTZ, -- GENERAL ordering window end - - -- Workflow - status VARCHAR(40) NOT NULL DEFAULT 'DRAFT', - - -- Pricing (unit rates only at contract phase) - pricing_breakdown JSONB, -- displayMode: UNIT_RATES - pricing_display_mode VARCHAR(20) DEFAULT 'UNIT_RATES', - - -- Contract document generation - contract_type VARCHAR(20), -- SPOT etc. - contract_template_key VARCHAR(128), - contract_generated_at TIMESTAMPTZ, - contract_summary TEXT, - version_number INT NOT NULL DEFAULT 1, - financial_terms JSONB, - - -- Signature timestamps (denormalized for queries) - approved_by_staff_id UUID, - approved_by_staff_at TIMESTAMPTZ, - signed_by_director_id UUID, - signed_by_director_at TIMESTAMPTZ, - signed_by_ceo_id UUID, - signed_by_ceo_at TIMESTAMPTZ, - customer_signed_at TIMESTAMPTZ, - fully_executed_at TIMESTAMPTZ, - locked_at TIMESTAMPTZ, - - -- Audit - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - deleted_at TIMESTAMPTZ -); - -CREATE INDEX idx_contracts_company ON freight.contracts(company_id); -CREATE INDEX idx_contracts_status ON freight.contracts(status); -CREATE INDEX idx_contracts_kind ON freight.contracts(contract_kind); -CREATE INDEX idx_contracts_valid_until ON freight.contracts(contract_valid_until); -``` - -**Columns migrated FROM `bookings`:** `contract_validity_days`, `contract_valid_from`, `contract_valid_until`, `contract_type`, `contract_template_key`, `contract_generated_at`, `contract_summary`, `version_number`, `financial_terms`, signature timestamp columns, `expires_at` (GENERAL only), `is_hazardous`, `is_reefer`, service/mile/customs fields, `estimated_shipment_date`. - -**Columns NOT on contracts:** `scheduled_date`, `payment_status`, `pnr_code`, `train_schedule_id`, `scheduling_status`, `priority_score`, `total_amount` (booking computed total). - -**Columns ADDED for customs path on contracts:** - -| Column | Type | Purpose | -|--------|------|---------| -| `clearance_status` | varchar | Pre-booking clearance gate: `NOT_APPLICABLE`, `AWAITING_DOCUMENTS`, `DOCUMENTS_UNDER_REVIEW`, `CLEARANCE_READY_FOR_BOOKING`, `ACTIVE_SHIPMENT_IN_PROGRESS` | -| `clearance_cycle_number` | int | Incremented per shipment cycle on GENERAL contracts | - ---- - -### 5.3 `freight.contract_routes` (EVOLVE `contract_route_lines`) - -Defines allowed origin/destination pairs. **No quantity.** - -```sql -CREATE TABLE freight.contract_routes ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - origin_yard_id UUID NOT NULL REFERENCES freight.yards(id), - destination_yard_id UUID NOT NULL REFERENCES freight.yards(id), - km NUMERIC(10,2), -- road billing distance; null for rail-only - sort_order SMALLINT NOT NULL DEFAULT 0, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - UNIQUE (contract_id, origin_yard_id, destination_yard_id) -); - -CREATE INDEX idx_contract_routes_contract ON freight.contract_routes(contract_id); -``` - -**Migration:** `ALTER TABLE freight.contract_route_lines RENAME TO contract_routes; ALTER ... RENAME COLUMN contract_booking_id TO contract_id; DROP COLUMN quantity; DROP COLUMN container_type_id;` - -**Rules:** -- `ONE_TIME`: exactly 1 route row (enforce via application or CHECK) -- `GENERAL`: 1..N route rows - ---- - -### 5.4 `freight.contract_cargo_scope` (NEW) - -Defines what cargo sizes/types are in scope without quantities. - -```sql -CREATE TABLE freight.contract_cargo_scope ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - - -- Container scope: one row per enabled size - container_size VARCHAR(10), -- '20ft' | '40ft'; NULL for bulk - - -- Bulk scope - cargo_type_id UUID REFERENCES freight.cargo_types(id), - cargo_free_text VARCHAR(200), - - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - -- Container: unique per size per contract - CONSTRAINT uq_contract_container_size - UNIQUE NULLS NOT DISTINCT (contract_id, container_size) -); - -CREATE INDEX idx_contract_cargo_scope_contract ON freight.contract_cargo_scope(contract_id); -``` - -**Validation rules:** -- `freight_type = CONTAINER`: at least one row with `container_size IN ('20ft','40ft')`; `cargo_type_id` optional (commodity label for contract PDF) -- `freight_type = BULK`: exactly one row with `cargo_type_id` required; `container_size` must be NULL -- No `quantity`, `vgm`, or detailed `container_type_id` (no "20ft Reefer" — reefer is `contracts.is_reefer` boolean) - ---- - -### 5.5 `freight.contract_signatures` (RENAME `booking_contract_signatures`) - -```sql -CREATE TABLE freight.contract_signatures ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - role VARCHAR(20) NOT NULL, -- CUSTOMER | STAFF | DIRECTOR | CEO - signer_display_name VARCHAR(255) NOT NULL, - signature_file_id UUID REFERENCES freight.files(id), - consent_text TEXT, - signed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); - -CREATE INDEX idx_contract_signatures_contract ON freight.contract_signatures(contract_id); -``` - ---- - -### 5.6 `freight.contract_approval_steps` (CLONE pattern from `booking_approval_step`) - -Same structure as `booking_approval_step` but FK → `contract_id`. Instantiated at staff accept from `approval_rules` based on cargo classification (US-06: Standard Container → Line Staff + Director; Bulk → Directors + CEO). - ---- - -### 5.7 `freight.contract_rate_snapshots` (CLONE from `booking_rate_snapshot`) - -Frozen **unit rates** at contract submit time. One row per rate line with `unit_of_measure` (`per_container`, `per_ton`, `per_item`, `per_km`). - -```sql -CREATE TABLE freight.contract_rate_snapshots ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - rate_id UUID REFERENCES freight.rates(id), - rate_code VARCHAR(64) NOT NULL, - description VARCHAR(255), - unit_price NUMERIC(14,2) NOT NULL, - unit_of_measure VARCHAR(32) NOT NULL, - currency VARCHAR(5) NOT NULL, - container_size VARCHAR(10), -- 20ft | 40ft when applicable - is_surcharge BOOLEAN DEFAULT FALSE, - conditional_on VARCHAR(32), -- is_hazardous | is_reefer - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); -``` - ---- - -### 5.8 `freight.contract_review_notes` (CLONE from `booking_review_note`) - -Staff/customer notes during contract approval (`CHANGES_REQUESTED`, rejection reasons). - ---- - -### 5.9 `freight.bookings` (MODIFY — shipment only) - -```sql -ALTER TABLE freight.bookings - ADD COLUMN contract_id UUID REFERENCES freight.contracts(id), - ADD COLUMN contract_route_id UUID REFERENCES freight.contract_routes(id), - ADD COLUMN created_by_role VARCHAR(20) DEFAULT 'CUSTOMER', -- CUSTOMER | GL_ET | STAFF - ADD COLUMN created_by_user_id UUID; - --- Partial unique: one active booking per ONE_TIME contract -CREATE UNIQUE INDEX uq_one_active_booking_per_one_time_contract - ON freight.bookings (contract_id) - WHERE status NOT IN ('EXPIRED', 'CANCELLED', 'COMPLETED', 'REJECTED') - AND contract_id IN ( - SELECT id FROM freight.contracts WHERE contract_kind = 'ONE_TIME' - ); -``` - -**Keep on bookings:** `reference`, `scheduled_date`, `status`, `scheduling_status`, `train_schedule_id`, `payment_status`, `pnr_code`, `total_amount`, `adjusted_total_amount`, `priority_score`, `payment_deadline`, `selected_for_batch_at`, `hold_started_at`, `hold_expires_at`, `wagons_required`, `consolidation_partner_id`, operational timestamps. - -**Remove from bookings (after migration):** `booking_type`, `contract_validity_*`, `contract_template_key`, `contract_generated_at`, `contract_summary`, `previous_contract_id`, `expires_at`, `estimated_shipment_date`, `is_hazardous`, `is_reefer` (move to contract; booking stores counts), primary `origin_yard_id`/`destination_yard_id` (use `contract_route_id` or denormalize at booking create). - -**Denormalize for performance:** Copy `origin_yard_id`, `destination_yard_id`, `trade_direction`, `freight_type` onto booking at creation from contract + selected route. - ---- - -### 5.10 `freight.booking_container` (ENRICH) - -Existing table extended for per-unit detail at booking time. - -```sql -ALTER TABLE freight.booking_container - ADD COLUMN container_size VARCHAR(10), -- 20ft | 40ft - ADD COLUMN hazardous_quantity SMALLINT DEFAULT 0, - ADD COLUMN reefer_quantity SMALLINT DEFAULT 0; -``` - -**New child table for individual container numbers:** - -```sql -CREATE TABLE freight.booking_container_units ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - booking_container_id UUID NOT NULL REFERENCES freight.booking_container(id) ON DELETE CASCADE, - container_number VARCHAR(64) NOT NULL, - seal_number VARCHAR(64), - vgm_tons NUMERIC(10,3) NOT NULL, - is_hazardous BOOLEAN DEFAULT FALSE, - is_reefer BOOLEAN DEFAULT FALSE, - sort_order SMALLINT NOT NULL DEFAULT 0, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - UNIQUE (booking_container_id, container_number) -); -``` - -**Bulk bookings:** use existing `cargo_total_weight_vgm` on booking for tons, or new `item_count` column when `cargo_types.unit_of_measure = PER_ITEM`. Add `hazardous_quantity` / bulk-specific counts on booking row or `booking_bulk_lines` if multiple bulk lines needed. - ---- - -### 5.11 `freight.booking_document_review` (KEEP — per booking clearance) - -No structural change. Add optional denormalized column: - -```sql -ALTER TABLE freight.booking_document_review - ADD COLUMN contract_id UUID REFERENCES freight.contracts(id); -``` - -Populated at insert from `bookings.contract_id` for GL read-only contract viewer queries. - ---- - -### 5.12 `freight.clearance_milestones` (NEW — GL gap) - -Tracks the 18–23 milestones from GL PDF that are not represented by the current 3 clearance statuses. - -```sql -CREATE TABLE freight.clearance_milestones ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - booking_id UUID NOT NULL REFERENCES freight.bookings(id) ON DELETE CASCADE, - milestone_code VARCHAR(64) NOT NULL, - milestone_label VARCHAR(255) NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- PENDING | COMPLETED | SKIPPED - owner_region VARCHAR(5), -- ET | DJ | OPS | CUST - triggered_by_doc BOOLEAN DEFAULT FALSE, - triggered_at TIMESTAMPTZ, - triggered_by_user_id UUID, - notes TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - UNIQUE (booking_id, milestone_code) -); - -CREATE INDEX idx_clearance_milestones_booking ON freight.clearance_milestones(booking_id); -CREATE INDEX idx_clearance_milestones_region ON freight.clearance_milestones(owner_region, status); -``` - ---- - -### 5.13 `freight.clearance_document_phases` (NEW — phased upload slots) - -Extends `file_upload_settings` with phase metadata for GL document matrix. - -```sql -CREATE TYPE freight.clearance_doc_phase AS ENUM ( - 'CUSTOMER_INTAKE', -- customer uploads before GL review - 'GL_ET_REVIEW', -- ET GL internal - 'GL_DJ_COLLECTION', -- DJ GL collects RO, DO - 'GL_ET_OUTPUT', -- IM4, EX3, EX8, T1 uploaded by ET GL - 'CUSTOMER_DUTY', -- duty/tax payment slips - 'GL_ET_POST_CLEARANCE', -- import release, T1 closure - 'GL_DJ_LOADING', -- gatepass, loading docs - 'POST_TRANSIT' -- demurrage slips, final declaration -); - --- Extend file_upload_fields with: --- phase clearance_doc_phase NOT NULL --- owner_region VARCHAR(5) -- ET | DJ | CUST --- trade_direction VARCHAR(10) --- triggers_milestone_code VARCHAR(64) -``` - ---- - -### 5.14 Files / MinIO Resource Tagging - -| Resource | Entity | Examples | -|----------|--------|----------| -| `contracts` | Contract PDF, intake docs, **post-sign clearance docs (Path B)** | Commercial invoice at wizard; BL, packing list after sign | -| `bookings` | Post-booking ops docs, copied GL outputs | VGM on booking row, IM4 linked after GL creates booking | -| `contract_signatures` | Signature images | PNG from signature pad | - ---- - -### 5.15 IAM Roles (GL Split) - -| Role code | Permissions | -|-----------|-------------| -| `edr_gl_ethiopia` | View contract, **review pre-booking clearance docs on contract**, upload IM4/IM5/EX3/EX8/T1, **create booking with full shipment data**, request wagon, assign station staff, post-booking ET milestones | -| `edr_gl_djibouti` | View contract + booking (read-only rates), upload Release Order / DO / gatepass, loading milestones, damage reports, handoff triggers | -| `edr_global_logistics` | **Deprecated** — split into ET/DJ; keep temporarily with union permissions for migration | - -Station routing (GL US-02): on booking create by GL, set `bookings.gl_station_yard_id` from contract route origin yard; queue filters by station assignment. - ---- - -### 5.16 Pre-Booking Clearance (Path B — customs contracts) - -When `customs_clearing_enabled = true`, clearance documents and review happen **on the contract before any booking exists**. - -#### `freight.contract_document_review` - -Same structure as `booking_document_review`, keyed on `contract_id` (and optionally `clearance_cycle_id`): - -```sql -CREATE TABLE freight.contract_document_review ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - clearance_cycle_id UUID REFERENCES freight.contract_clearance_cycles(id), - setting_code VARCHAR(128) NOT NULL, - file_key VARCHAR(128) NOT NULL, - file_record_id UUID, - status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- PENDING | APPROVED | QUERIED - note TEXT, - uploaded_by_role VARCHAR(20) NOT NULL DEFAULT 'CUSTOMER', -- CUSTOMER | GL_ET | GL_DJ - reviewed_by_staff_id UUID, - reviewed_at TIMESTAMPTZ, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - - UNIQUE (contract_id, clearance_cycle_id, setting_code, file_key) -); - -CREATE INDEX idx_contract_doc_review_contract ON freight.contract_document_review(contract_id); -CREATE INDEX idx_contract_doc_review_status ON freight.contract_document_review(status); -``` - -Customer uploads attach here after contract sign. GL approves/queries here. When all required docs are `APPROVED` and GL output docs for the pre-booking phase are uploaded, contract moves to `CLEARANCE_READY_FOR_BOOKING` — GL may then create the booking. - -#### `freight.contract_clearance_cycles` (GENERAL multi-shipment) - -```sql -CREATE TABLE freight.contract_clearance_cycles ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE, - cycle_number INT NOT NULL, - status VARCHAR(40) NOT NULL DEFAULT 'AWAITING_DOCUMENTS', - booking_id UUID REFERENCES freight.bookings(id), -- set when GL creates booking - started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - clearance_ready_at TIMESTAMPTZ, - completed_at TIMESTAMPTZ, - - UNIQUE (contract_id, cycle_number) -); -``` - -ONE_TIME contracts use a single implicit cycle (cycle_number = 1, no separate row required — or always row 1). - -#### `freight.clearance_milestones` — split ownership - -| Phase | Attached to | When | -|-------|-------------|------| -| Pre-booking milestones (docs uploaded, declared, DO collected, etc. before wagon request) | `contract_id` + `clearance_cycle_id` | Before booking exists | -| Post-booking milestones (wagon allocated, loaded, departed, offloaded, etc.) | `booking_id` | After GL creates booking | - -Add nullable `contract_id` and `clearance_cycle_id` to `clearance_milestones` alongside `booking_id`. - ---- - -## 6. Status Machines - -### 6.1 Contract Statuses - -``` -DRAFT - → SUBMITTED - → PRICE_CHANGED_PENDING_CONFIRM (if rates changed on resubmit) - → CHANGES_REQUESTED - → PENDING_APPROVAL (after staff accept + validity window set) - → APPROVED - → APPROVED_PENDING_SIGNATURE - → CONTRACT_READY (PDF generated) - → SIGNED_CUSTOMER - → FULLY_EXECUTED (ONE_TIME, transport-only: customer may book) - → CONTRACT_ACTIVE (GENERAL, transport-only: customer may book) - -- Path B only (customs_clearing_enabled = true), after counter-sign: - → AWAITING_CLEARANCE_DOCUMENTS (customer uploads clearance docs — no booking yet) - → CLEARANCE_UNDER_REVIEW (GL ET reviews customer docs) - → CLEARANCE_READY_FOR_BOOKING (GL cleared to create booking; output docs in place) - → ACTIVE_SHIPMENT_IN_PROGRESS (GL created booking; cycle in ops pipeline) - → CONTRACT_CLOSED (GENERAL: validity ended or manually closed) - → EXPIRED (contract_valid_until passed) - → REJECTED - → CANCELLED -``` - -For **transport-only** contracts (`customs_clearing_enabled = false`), counter-sign goes directly to `FULLY_EXECUTED` / `CONTRACT_ACTIVE` with `clearance_status = NOT_APPLICABLE`. - -For **customs** contracts, counter-sign goes to `AWAITING_CLEARANCE_DOCUMENTS` with `clearance_status = AWAITING_DOCUMENTS`. - -**Renewal branch:** - -``` -RENEWAL_DRAFT - → RENEWAL_SUBMITTED - → RENEWAL_PENDING_APPROVAL - → AMENDMENTS_PROPOSED (staff changed price/terms) - → (customer Accept) → CONTRACT_READY - → (customer Reject) → ARCHIVED -``` - -### 6.2 Contract Transition Table - -| From | Action | Actor | To | -|------|--------|-------|-----| -| DRAFT | Submit | Customer | SUBMITTED | -| SUBMITTED | Accept | Line Staff | PENDING_APPROVAL | -| SUBMITTED | Request changes | Line Staff | CHANGES_REQUESTED | -| SUBMITTED | Reject | Line Staff | REJECTED | -| PENDING_APPROVAL | Approve step | Line Staff / Director / CEO | APPROVED (when all steps done) | -| APPROVED | Generate contract | System | CONTRACT_READY | -| CONTRACT_READY | Sign | Customer | SIGNED_CUSTOMER | -| SIGNED_CUSTOMER | Counter-sign | Staff/Director/CEO | FULLY_EXECUTED / CONTRACT_ACTIVE (transport-only) **or** AWAITING_CLEARANCE_DOCUMENTS (customs) | -| AWAITING_CLEARANCE_DOCUMENTS | Upload all required docs | Customer | CLEARANCE_UNDER_REVIEW (auto when complete) | -| CLEARANCE_UNDER_REVIEW | Approve all docs | GL ET | CLEARANCE_READY_FOR_BOOKING | -| CLEARANCE_UNDER_REVIEW | Query doc | GL ET | AWAITING_CLEARANCE_DOCUMENTS (customer re-upload) | -| CLEARANCE_READY_FOR_BOOKING | Create booking | GL ET | ACTIVE_SHIPMENT_IN_PROGRESS | -| ACTIVE_SHIPMENT_IN_PROGRESS | Booking completes/expired | System | CONTRACT_ACTIVE / FULLY_EXECUTED (ready for next cycle) | -| CONTRACT_ACTIVE | Validity ends | System cron | CONTRACT_CLOSED or EXPIRED | -| FULLY_EXECUTED | Validity ends | System cron | EXPIRED | - -**Contract carries pre-booking clearance statuses (Path B).** Batch and freight payment statuses remain on `bookings` only. - -### 6.3 Booking Statuses (Shipment Pipeline) - -Bookings are **only created after** contract signing. For Path B (customs), GL creates the booking only after contract clearance reaches `CLEARANCE_READY_FOR_BOOKING`. - -**Path A (transport-only)** — customer creates booking; may start at `OPERATION_REQUEST_PENDING` or `DRAFT`: - -``` -[Customer creates booking] - → OPERATION_REQUEST_PENDING (customer picked binding scheduled_date) - → OPERATION_CHANGES_REQUESTED - → OPERATION_PRICE_PENDING_CONFIRM - → FULLY_EXECUTED (enters batch holding pool) - → SELECTED_FOR_BATCH - → EXPIRED (payment window — contract unaffected) - → PNR_GENERATED / PAID - → IN_TRANSIT - → COMPLETED - → CANCELLED / REJECTED - → ROAD_DISPATCH_PENDING (road services) -``` - -**Path B (customs — GL creates booking)** — booking skips pre-booking doc gates; GL has already entered schedule date and cargo details at creation: - -``` -[GL ET creates booking with full shipment data] - → FULLY_EXECUTED or OPERATION_REQUEST_PENDING (based on whether ops review required) - → SELECTED_FOR_BATCH - → EXPIRED (payment window — customer action: pay only) - → PNR_GENERATED / PAID (customer pays freight) - → IN_TRANSIT (GL/Ops milestones continue) - → COMPLETED -``` - -Post-booking GL milestones (wagon allocated, loaded, departed, offloaded, T1 closed, etc.) attach to `booking_id` via `clearance_milestones` as today. - -**Gate to create booking (Path A — customer):** - -```sql -contract.status IN ('FULLY_EXECUTED', 'CONTRACT_ACTIVE') -AND contract.customs_clearing_enabled = false -AND contract.contract_valid_until > NOW() -AND (contract_kind = 'GENERAL' - OR NOT EXISTS active booking for this contract) -``` - -**Gate to create booking (Path B — GL only):** - -```sql -contract.clearance_status = 'CLEARANCE_READY_FOR_BOOKING' -AND contract.customs_clearing_enabled = true -AND contract.contract_valid_until > NOW() -AND (contract_kind = 'GENERAL' - OR NOT EXISTS active booking for this contract) -AND caller.role = 'edr_gl_ethiopia' -``` - -Where `active booking` = status NOT IN (`EXPIRED`, `CANCELLED`, `COMPLETED`, `REJECTED`). - -### 6.4 Booking Creation Rules - -| Path | Who creates booking | UI | Initial booking status | -|------|---------------------|-----|------------------------| -| **A — Transport only** | Customer | Portal booking wizard (§8.1) | `OPERATION_REQUEST_PENDING` or `DRAFT` | -| **B — Customs clearance** | **GL Ethiopia only** | Backoffice GL booking form (§8.2) | `FULLY_EXECUTED` or `OPERATION_REQUEST_PENDING` (schedule date + cargo already filled by GL) | - -**Customer portal (Path B):** After contract sign, show **Upload Clearance Documents** on contract detail — not a booking wizard. After GL creates booking, customer sees shipment on `/bookings/:id` with **Pay** as primary action when batch-selected. - -**No customer confirmation step** before GL booking goes live — GL enters authoritative shipment data; customer is notified when booking is created and when payment is due. - ---- - -## 7. Contract Wizard Specification - -**Route:** `/contracts/new` -**Replaces:** `/bookings/new` for contract creation - -### Step 0 — Operation Type - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `operationType` | enum | Yes | `import`, `export`, `intercity`, `import_ff`, `export_ff` | -| | | | Gated by company profile types (existing logic from `step0-operation-type.tsx`) | - -Maps to `contracts.trade_direction`: import/export → IMPORT/EXPORT; intercity → DOMESTIC. - ---- - -### Step 1 — Contract Type - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `contractKind` | enum | Yes | `one_time` → ONE_TIME, `general_contract` → GENERAL | -| `renewalMode` | enum | No | `new` \| `renewal` | -| `renewalOfReference` | combobox | If renewal | Search prior contracts; pre-fill service, routes, cargo scope | - -Maps to `contracts.contract_kind`, `contracts.renewal_of_id`. - ---- - -### Step 2 — Service Type & Currency - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `serviceTypeId` | select | Yes | From reference data; `canBeBookedAlone` | -| `paymentCurrency` | enum | Yes | `USD` \| `ETB` | -| `firstMile.*` | toggle + map | If service includes first mile | Address, lat, lng | -| `lastMile.*` | toggle + map | If service includes last mile | Address, lat, lng | -| `equipmentReturn` | enum | If last mile | `with_return` \| `without_return` | -| `customsClearingAgent` | text | Optional | When customs not bundled | - -Auto-set `customs_clearing_enabled` from `serviceType.includesCustoms`. - ---- - -### Step 3 — Cargo Scope (NO quantities) - -**Container freight:** - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `enabledContainerSizes` | checkbox[] | Yes, min 1 | `20ft`, `40ft` — creates `contract_cargo_scope` rows | -| `cargoCommodityId` | select | Optional | Commodity label for contract PDF (Coffee, etc.) | - -**Do NOT collect:** quantity, VGM, container type detail (dry/reefer/high-cube), shipping line. - -**Bulk freight:** - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `cargoTypePath` | cascader | Yes | Bulk group → commodity (Coffee, Fertilizer, …) | -| `cargoFreeText` | text | If "Others" | | - -**Do NOT collect:** tonnage, item count, weight. - -**Flags (both):** - -| Field | Type | Notes | -|-------|------|-------| -| `isHazardous` | toggle | Sets `contracts.is_hazardous`; surcharge shown as unit rate | -| `isRefrigerated` | toggle | Sets `contracts.is_reefer`; bulk + container | - ---- - -### Step 4 — Route & Estimated Date - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `originYardId` | select | Yes | Filtered by operation type | -| `destinationYardId` | select | Yes | | -| `extraRoutes[]` | repeater | GENERAL only | Additional origin/destination pairs → `contract_routes` | -| `estimatedShipmentDate` | date | Yes | Non-binding; maps to `contracts.estimated_shipment_date` | - -**Do NOT collect:** `scheduledDate` (binding date is at booking step). - ---- - -### Step 5 — Contract Intake Documents - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `documents` | file map | Per setting | Attach to `contract_id` via FilesService `resource=contracts` | - -Setting codes (new or repurpose): -- `contract_intake_documents_import_container` -- `contract_intake_documents_export_bulk` -- etc. (mirror clearance pattern by direction × freight × customs) - -These are **framework / commercial documents** at contract submission — distinct from **post-sign clearance documents** (BL, import license, etc.) uploaded on `/contracts/:id/clearance` after signing (Path B). - ---- - -### Step 6 — Review & Submit - -| Field | Type | Notes | -|-------|------|-------| -| `notes` | textarea | Optional special instructions | -| Pricing panel | read-only | **Unit rates only** — see §9 | -| Actions | buttons | Save draft, Generate price, Submit | - -**Post-submit:** existing approval + contract sign flow on `/contracts/:id/contract`. - -**Post-sign (Path B only):** Customer is routed to `/contracts/:id/clearance` to upload clearance documents — not to a booking wizard. - ---- - -## 8. Booking Creation Specification - -Booking creation is **split by execution path** (see §4.2). Only **Path A (transport-only)** uses the customer portal booking wizard. **Path B (customs clearance)** uses a **GL-only backoffice form** — the customer never enters shipment data. - ---- - -### 8.1 Customer Booking Wizard (Path A — transport only) - -**Route:** `/contracts/:contractId/bookings/new` -**Actor:** Customer -**Preconditions:** -- `contract.customs_clearing_enabled = false` -- `contract.status IN ('FULLY_EXECUTED', 'CONTRACT_ACTIVE')` -- Contract validity not expired -- ONE_TIME: no other active booking - -#### Step 1 — Route Selection - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `contractRouteId` | select | If GENERAL multi-route | From `contract_routes`; ONE_TIME auto-selected | - -Denormalize origin/destination onto booking. - -#### Step 2 — Schedule Date - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `scheduledDate` | date | Yes | Binding day; validated against open train departures | - -#### Step 3 — Cargo Details - -**Container** (for each enabled size from `contract_cargo_scope`): - -| Field | Type | Required | -|-------|------|----------| -| `containers[].size` | 20ft/40ft | Yes | -| `containers[].quantity` | int ≥ 1 | Yes | -| `containers[].units[].containerNumber` | text | Yes, one per container | -| `containers[].units[].sealNumber` | text | Optional | -| `containers[].units[].vgmTons` | decimal | Yes, per unit | -| `containers[].hazardousQuantity` | int | If `contract.is_hazardous` | -| `containers[].reeferQuantity` | int | If `contract.is_reefer` | - -**Bulk:** - -| Field | Type | Required | -|-------|------|----------| -| `cargoWeightTons` OR `itemCount` | decimal/int | Yes | -| `hazardousQuantity` | int | If hazardous | - -#### Step 4 — Review & Submit - -- Compute total from contract unit rates × quantities -- Submit → `OPERATION_REQUEST_PENDING` → ops review → batch → **customer pays** - ---- - -### 8.2 GL Booking Form (Path B — customs clearance) - -**Route:** `/dashboard/contracts/:contractId/create-booking` (backoffice) -**Actor:** `edr_gl_ethiopia` **only** -**Preconditions:** -- `contract.customs_clearing_enabled = true` -- `contract.clearance_status = 'CLEARANCE_READY_FOR_BOOKING'` -- Contract validity not expired; ONE_TIME has no active booking - -GL enters **all shipment fields** that the customer would enter in §8.1: - -| Field | Type | Required | Notes | -|-------|------|----------|-------| -| `contractRouteId` | select | If GENERAL multi-route | | -| `scheduledDate` | date | Yes | Binding schedule day | -| `containers[].size` | 20ft/40ft | If container contract | From contract scope | -| `containers[].quantity` | int | Yes | | -| `containers[].units[].containerNumber` | text | Yes | GL enters on behalf of customer | -| `containers[].units[].sealNumber` | text | Optional | | -| `containers[].units[].vgmTons` | decimal | Yes | | -| `containers[].hazardousQuantity` | int | If hazardous | | -| `containers[].reeferQuantity` | int | If reefer | | -| `cargoWeightTons` / `itemCount` | decimal/int | If bulk | Ton or item per cargo type | -| `notes` | text | Optional | Internal GL notes | - -**On submit:** -1. Create `bookings` row with `created_by_role = 'GL_ET'`, `created_by_user_id = gl staff` -2. Compute `total_amount` from contract unit rates × GL-entered quantities -3. Set booking status → `FULLY_EXECUTED` or `OPERATION_REQUEST_PENDING` -4. Set contract → `ACTIVE_SHIPMENT_IN_PROGRESS`; link `contract_clearance_cycles.booking_id` -5. Notify customer: "Your shipment has been booked by Global Logistics — you will be notified when payment is due" -6. Continue post-booking pipeline (batch, milestones) — **customer action = pay only** - -**Customer portal:** No booking wizard. Contract detail shows clearance upload; after GL books, `/bookings/:id` shows read-only shipment summary + Pay button. - ---- - -### 8.3 Customer Post-Sign Clearance Upload (Path B) - -**Route:** `/contracts/:id/clearance` (portal) -**Actor:** Customer -**When:** After contract counter-sign → `AWAITING_CLEARANCE_DOCUMENTS` - -| Field | Type | Notes | -|-------|------|-------| -| Clearance documents | file map | Per `contract_clearance_*` file-upload setting (import/export × container/bulk) | -| Ad-hoc documents | name + file | Optional additional regulatory docs | - -Documents stored in `contract_document_review` — **not** on a booking row (booking does not exist yet). - -After all required uploads: contract auto-transitions to `CLEARANCE_UNDER_REVIEW`; GL ET queue receives the contract. - ---- - -## 9. Pricing Model - -### 9.1 Contract Phase — Unit Rate Display - -Example UI (matches stakeholder requirement): - -``` -Pricing Schedule (estimated — final amount calculated at booking) - - 20ft container ...................... 2,000 ETB / container - 40ft container ........................ 40,000 ETB / container - Hazardous surcharge ................. 3,000 ETB / container (if enabled) - Reefer surcharge .................... 5,000 ETB / container (if enabled) - Coffee (bulk) ....................... 1,500 ETB / ton - - No total amount shown — quantities unknown at contract stage. -``` - -**JSON storage (`contracts.pricing_breakdown`):** - -```json -{ - "displayMode": "UNIT_RATES", - "currency": "ETB", - "lineItems": [ - { - "code": "CONTAINER_20FT", - "label": "20ft container", - "unit": "per_container", - "unitPrice": 2000, - "containerSize": "20ft" - }, - { - "code": "CONTAINER_40FT", - "label": "40ft container", - "unit": "per_container", - "unitPrice": 40000, - "containerSize": "40ft" - }, - { - "code": "HAZARD_SURCHARGE", - "label": "Hazardous surcharge", - "unit": "per_container", - "unitPrice": 3000, - "conditionalOn": "is_hazardous" - }, - { - "code": "REEFER_SURCHARGE", - "label": "Reefer surcharge", - "unit": "per_container", - "unitPrice": 5000, - "conditionalOn": "is_reefer" - }, - { - "code": "BULK_COFFEE", - "label": "Coffee", - "unit": "per_ton", - "unitPrice": 1500, - "cargoTypeCode": "COFFEE" - } - ] -} -``` - -Contract PDF (US-07) embeds unit rate schedule, not totals. - -### 9.2 Booking Phase — Total Calculation - -``` -booking_total = - Σ (container_qty[size] × unit_rate[size]) - + Σ (hazardous_qty × hazard_unit_rate) - + Σ (reefer_qty × reefer_unit_rate) - + bulk_qty × bulk_unit_rate - + first_mile_km × per_km_rate (if applicable) - + last_mile_km × per_km_rate (if applicable) - + overweight surcharges (when VGM entered per US-11) -``` - -Algorithm reuses `BookingPricingService.computePriceForBooking()` but inputs come from booking quantities + frozen `contract_rate_snapshots` instead of contract-time qty. - -Store result in `bookings.total_amount` and `booking_rate_snapshot` rows. - -### 9.3 Quotation Approval (US-04) - -At contract submit, customer sees unit-rate quotation → **Approve Quotation** → advances to approval/signing. Reject → `REJECTED` / archived. - -This replaces the current total-amount confirmation modal in `NewBookingPage.tsx`. - ---- - -## 10. Renewal and Expiry Rules - -### 10.1 Contract Validity Expiry - -| Event | Contract behavior | Booking behavior | -|-------|-------------------|------------------| -| `contract_valid_until` passed | Status → `EXPIRED` or `CONTRACT_CLOSED`; no new bookings | In-flight bookings continue to completion | -| Customer requests renewal | `RENEWAL_DRAFT` linked via `renewal_of_id` | N/A | -| Staff approves without changes | → `CONTRACT_READY` → sign | N/A | -| Staff proposes amendments | → `AMENDMENTS_PROPOSED`; customer accept/reject | N/A | - -Renewal UI: extend current `step1-contract-type.tsx` renewal combobox to search `contracts` table. - -### 10.2 Payment Window Expiry (Booking Only) - -| Event | Contract | Booking | -|-------|----------|---------| -| Payment deadline passes (`BookingBatchService`) | **Unchanged** — stays `FULLY_EXECUTED` or `CONTRACT_ACTIVE` | Status → `EXPIRED`; wagons released | -| Customer re-books (Path A) | Same contract | Customer creates new booking | -| Customer re-ships (Path B) | Returns to `AWAITING_CLEARANCE_DOCUMENTS` for new cycle | GL creates new booking after clearance | - -**UX Path A:** Contract detail shows "Create new booking" when prior booking expired. - -**UX Path B:** Contract detail shows "Upload clearance documents" for next shipment cycle; customer never sees a booking creation form. - -### 10.3 General Contract Ordering Window - -`contracts.expires_at` (ordering window, from `general_contract_period` setting) is separate from `contract_valid_until` (legal validity). Both must be open for new bookings. - -### 10.4 ONE_TIME Single Active Booking Rule - -Enforced by partial unique index (§5.9). Terminal statuses free the slot: - -- `EXPIRED` (payment) -- `CANCELLED` -- `COMPLETED` -- `REJECTED` - ---- - -## 11. Global Logistics — Import Unimodal - -### 11.1 Actors - -| Actor | Role code | Primary responsibilities | -|-------|-----------|-------------------------| -| Customer | Portal user | **Path B:** Upload clearance docs on contract after sign; pay freight PNR and duty/tax slips when advised. **Does not create bookings or enter shipment data.** | -| GL Ethiopia | `edr_gl_ethiopia` | Review pre-booking clearance docs on contract; upload IM4/IM5/EX3/EX8/T1; **create booking with full shipment data**; wagon request; post-booking milestones | -| GL Djibouti | `edr_gl_djibouti` | DO collection, gatepass, loading milestones, damage reports, departure | -| Operations | `edr_operations` | Wagon allocation, train dispatch, marshalling | -| Port/Terminal | `edr_terminal` | Arrival, offload, yard assignment | - -### 11.2 Import Document Matrix - -| # | Customer uploads | GL Ethiopia uploads | GL Djibouti uploads | Phase | -|---|------------------|--------------------|--------------------|-------| -| 1 | Commercial Invoice* | | | `contract_id` — CUSTOMER_INTAKE (post-sign) | -| 2 | Packing List* | | | `contract_id` | -| 3 | Certificate of Origin* | | | `contract_id` | -| 4 | Bank Permit / Franco Valuta* | | | `contract_id` | -| 5 | Bill of Lading / SWB* | | | `contract_id` | -| 6 | Power of Attorney* | | | `contract_id` | -| 7 | Import License* | | | `contract_id` | -| 8 | Other regulatory docs | | | `contract_id` | -| 9 | | Import Declaration (IM4/IM5)* | | GL_ET_OUTPUT | -| 10 | | Transit permit screenshot | | GL_ET_OUTPUT | -| 11 | Duty/tax payment slip* | | | CUSTOMER_DUTY | -| 12 | | | Delivery Order | GL_DJ_COLLECTION | -| 13 | | | T1 transport document | GL_DJ_LOADING | -| 14 | | Import release* | | GL_ET_POST_CLEARANCE | -| 15 | | | Full out Interchange | GL_DJ_LOADING | -| 16 | | | Damage report photos | GL_DJ_LOADING (conditional) | -| 17 | Storage/demurrage payment slip | | | POST_TRANSIT | - -*Mandatory per GL PDF Import Documents table. - -**Document ownership (Path B):** -- Rows 1–8, 11, 17 (customer uploads): attach to **`contract_id`** via `contract_document_review` **before booking exists** -- Rows 9–10, 14 (GL ET output): attach to **`contract_id`** during pre-booking clearance, copied/referenced on `booking_id` when GL creates booking -- Rows 12–13, 15–16 (GL DJ): may attach to `contract_id` pre-booking or `booking_id` post-booking depending on milestone timing (see §5.16) -- After GL creates booking, post-booking milestones (wagon+, rows 9–23 in §11.3) track on **`booking_id`** - -Contract terms visible read-only to GL throughout; customer pays freight at milestone 10 only. - -### 11.3 Import Milestone Sequence - -| # | Milestone | Owner | Triggered by doc upload? | -|---|-----------|-------|--------------------------| -| 1 | Import Documents Uploaded | Customer | No | -| 2 | Pending Document Review | GL-ET | Yes (all customer docs uploaded) | -| 3 | Documents Approved | GL-ET | No | -| 4 | Under Customs Clearance | GL-ET | No | -| 5 | Declared | GL-ET | Yes (IM4/IM5 uploaded) | -| 6 | Duty and Taxes Advised | GL-ET | No | -| 7 | Duty and Tax Paid | Customer | Yes (payment slip) | -| 8 | DO Collected | GL-DJ | Yes (DO uploaded) | -| 9 | Wagon Allocation Requested | GL-ET | No | -| 10 | Payment Settled (freight) | Customer | Yes (PNR paid) | -| 11 | Wagon Allocated | Operations | No | -| 12 | Gatepass Granted | GL-DJ | No | -| 13 | Ready for Loading | GL-DJ | No | -| 14 | Loaded | GL-DJ | No | -| 15 | Departed from Djibouti | GL-DJ | No | -| 16 | Arrived at Port in Ethiopia | Port/Terminal | No | -| 17 | Offloaded | Port/Terminal | No | -| 18 | T1 Closed | GL-ET | No | -| 19 | Risk Assigned (GREEN/YELLOW/RED) | GL-ET | No | -| 20 | Import Release Granted | GL-ET | Yes (release doc) | -| 21 | Import Process Completed | GL-ET | Yes | -| 22 | Storage Invoice Raised | System | No (demurrage engine) | -| 23 | Exit Note Generated | Port | Yes (storage paid) | - -Map to `clearance_milestones.milestone_code` enum constants. - -**Milestone phase split (Path B):** - -| Milestones | Phase | Attached to | -|------------|-------|-------------| -| 1–8 | Pre-booking clearance | `contract_id` — **before booking exists** | -| 8a | **GL creates booking** (schedule, qty, container numbers entered by GL ET) | `booking_id` created | -| 9–23 | Post-booking operations | `booking_id` — batch, payment (customer), allocation, transit | - -Customer actions in this table: **#1 upload**, **#7 duty slip**, **#10 pay freight**, **#17 storage slip**. All other steps are GL/Ops/System. - -### 11.4 Import Sequence Diagram (Path B — customs clearance) - -```mermaid -sequenceDiagram - participant Cust as Customer - participant CTR as Contract - participant BK as Booking - participant GLET as GL_Ethiopia - participant GLDJ as GL_Djibouti - participant Ops as Operations - - Cust->>CTR: Sign contract - Cust->>CTR: Upload clearance docs on contract - CTR->>GLET: CLEARANCE_UNDER_REVIEW - GLET->>CTR: Approve or query per document - GLET->>CTR: Upload IM4 IM5 output docs - GLET->>Cust: Duty and taxes advised - Cust->>CTR: Upload duty payment slip - GLDJ->>CTR: Upload DO collected - Note over GLET,CTR: CLEARANCE_READY_FOR_BOOKING - GLET->>BK: GL creates booking schedule qty container numbers - GLET->>Ops: Wagon allocation requested - Ops->>Cust: PNR pending payment - Cust->>BK: Pay freight only - Ops->>BK: Wagon allocated - GLDJ->>BK: Gatepass loading loaded - GLDJ->>BK: Departed from Djibouti - Ops->>BK: Arrived offloaded Ethiopia - GLET->>BK: T1 closed import release granted -``` - -### 11.5 ET ↔ DJ Handoff (Import US-09) - -When milestone `DEPARTED_FROM_DJIBOUTI` completes: -- Primary owner → GL Ethiopia + Operations (read-only copy for GL Djibouti) -- Notifications: ATD to all; ETA to GL-ET and customer - ---- - -## 12. Global Logistics — Export Unimodal - -### 12.1 Export Document Matrix - -| # | Customer uploads | GL Ethiopia uploads | GL Djibouti uploads | -|---|------------------|--------------------|--------------------| -| 1 | Booking Confirmation* | | | -| 2 | Verified Gross Mass (VGM)* | | | -| 3 | Shipping Instructions* | | | -| 4 | Train Waybill* | | | -| 5 | Commercial Invoice* | | | -| 6 | Packing List* | | | -| 7 | Bank Permit* | | | -| 8 | Sales Contract* | | | -| 9 | Power of Attorney* | | | -| 10 | Export License* | | | -| 11 | | Export declaration docs | | -| 12 | | Export Release | | -| 13 | | T1 transport document | | -| 14 | | | Release Order* | -| 15 | | | Full in Interchange | -| 16 | | Final Declaration | | - -Commodity-specific (Coffee, Teff, etc.): additional regulatory docs per GL PDF US-03. - -**Path B (customs export):** Customer uploads rows 1–10 on **`contract_id`** after contract sign (same pre-booking pattern as import §11.2). GL ET creates booking after `CLEARANCE_READY_FOR_BOOKING`. VGM and container numbers are entered by **GL ET at booking creation**, not by the customer at contract sign. - -### 12.2 Export Milestone Sequence - -| # | Milestone | Owner | Doc triggered? | -|---|-----------|-------|----------------| -| 1 | Export Documents Uploaded | Customer | No | -| 2 | Pending Document Review | GL-ET | Yes | -| 3 | Documents Approved | GL-ET | No | -| 4 | Release Order Secured | GL-DJ | Yes (RO upload) | -| 5 | Under Customs Clearance | GL-ET | No | -| 6 | Declared | GL-ET | Yes (EX3/EX8) | -| 7 | Export Released | GL-ET | No | -| 8 | Wagon Requested | GL-ET | No | -| 9 | Pending Payment | Customer | No | -| 10 | Payment Settled | Customer | Yes | -| 11 | Wagon Allocated | Operations | No | -| 12 | Cargo Arrived | Port/Terminal | No | -| 13 | Ready for Loading | Port/Terminal | No | -| 14 | Loaded | Port/Terminal | No | -| 15 | Departed to Djibouti | Port/Terminal | No | -| 16 | Arrived at Djibouti | GL-DJ | No | -| 17 | Gatepass Granted | GL-DJ | No | -| 18 | Offloaded | GL-DJ | Yes (Full-in Interchange) | - -### 12.3 Export ET ↔ DJ Handoff (US-09 Export) - -When `DEPARTED_TO_DJIBOUTI` from Ethiopia: -- Primary owner → GL Djibouti -- GL Ethiopia receives read-only copy + ATD notification - ---- - -## 13. Customs Clearance Path — GL-Owned Execution - -**Applies when:** `customs_clearing_enabled = true` (transport with Global Logistics / customs clearance service). - -This is the **default execution path** for import, export, and intercity contracts that bundle customs clearance. The customer **never** creates a booking or enters shipment quantities, container numbers, or schedule dates. - -### 13.1 End-to-End Flow - -```mermaid -flowchart TD - A[Contract signed FULLY_EXECUTED or CONTRACT_ACTIVE] --> B[Customer uploads clearance docs on contract] - B --> C[CLEARANCE_UNDER_REVIEW] - C --> D[GL ET approves or queries each document] - D --> E[GL ET and GL DJ upload output docs milestones] - E --> F[CLEARANCE_READY_FOR_BOOKING] - F --> G[GL ET creates booking — enters ALL shipment data] - G --> H[Batch pool PNR generated] - H --> I[Customer pays freight] - I --> J[GL Ops milestones wagon allocation transit] - J --> K[COMPLETED or EXPIRED payment] - K -->|GENERAL next shipment| B -``` - -### 13.2 Customer Responsibilities (Path B only) - -| Action | When | Where | -|--------|------|-------| -| Sign contract | After Marketing approval | `/contracts/:id/contract` | -| Upload clearance documents | After contract counter-sign | `/contracts/:id/clearance` | -| Re-upload queried documents | When GL queries a specific doc | `/contracts/:id/clearance` | -| Upload duty/tax payment slip | When GL advises amount | `/contracts/:id/clearance` | -| **Pay freight PNR** | When booking selected for batch | `/bookings/:id` — **Pay** button | -| Upload storage/demurrage slip | If applicable post-transit | `/bookings/:id` | -| Track shipment | Read-only | `/bookings/:id`, `/tracking` | - -**Customer does NOT:** create booking, pick schedule date, enter container numbers, enter quantities, enter VGM, or confirm GL-entered booking data. - -### 13.3 GL Ethiopia Responsibilities - -| Action | When | -|--------|------| -| Review/approve/query customer clearance docs | `CLEARANCE_UNDER_REVIEW` on contract | -| Upload IM4/IM5/EX3/EX8/T1 and other ET output docs | During pre-booking clearance | -| Request wagon / advance clearance milestones | Before or after booking create per import/export matrix | -| **Create booking** with route, `scheduledDate`, container qty/numbers/VGM, bulk ton/item count, hazard/reefer counts | When `CLEARANCE_READY_FOR_BOOKING` | -| Continue post-booking ET milestones | After booking exists | - -### 13.4 API (Path B) - -``` -# Customer — post-sign clearance on contract (no booking yet) -GET /contracts/:id/clearance -POST /contracts/:id/clearance/documents -POST /contracts/:id/clearance/adhoc-documents - -# GL ET — review pre-booking clearance -GET /contracts/clearance/queue?region=ET -POST /contracts/:id/clearance/review { fileKey, status, note } -POST /contracts/:id/clearance/output-documents -POST /contracts/:id/clearance/finalize → CLEARANCE_READY_FOR_BOOKING - -# GL ET — create booking (exclusive; replaces customer POST) -POST /contracts/:contractId/bookings - Authorization: edr_gl_ethiopia - Body: { contractRouteId, scheduledDate, containers[], bulkLines[], notes } - Response: booking { id, reference, totalAmount, status } - Side effects: - - contract.status → ACTIVE_SHIPMENT_IN_PROGRESS - - contract_clearance_cycles.booking_id set - - customer notified (email + in-app) - -# Customer — payment only (unchanged) -POST /bookings/:id/payment/pay -``` - -**Removed:** `POST /bookings/:id/customer/confirm-gl-booking` — GL booking is authoritative; no customer confirmation gate. - -### 13.5 Field Parity - -GL booking form (§8.2) collects the same shipment fields the customer would enter in Path A (§8.1). GL staff source container numbers and VGM from physical documents and port data — not from customer portal entry. - ---- - -## 14. Gap Analysis Matrix - -| # | Topic | PDF / Stakeholder requirement | Current implementation | Target design | Gap severity | -|---|-------|------------------------------|------------------------|---------------|--------------| -| 1 | Contract vs booking separation | US-03, US-07, US-08; ITLMS §1 "Upon contract signature, invite booking" | Single `bookings` row | `contracts` + `bookings` | **Critical** | -| 2 | Contract cargo = scope only | US-03 Case 2: no weight/qty at contract; 20ft/40ft only | Wizard step 3 collects qty, VGM | `contract_cargo_scope` | **Critical** | -| 3 | Unit-rate contract pricing | US-04; stakeholder step 8 | Total amount from qty at intake | `UNIT_RATES` display mode | **Critical** | -| 4 | Hazard/reefer at contract | US-03 Case 3: billing flags at contract | Route step on booking; reefer from container type | `contracts.is_hazardous/is_reefer`; counts at booking | **High** | -| 5 | Binding vs estimated date | US-03 one-time: departure at contract; stakeholder: estimate only at contract | `estimatedShipmentDate` + `scheduledDate` both on booking | Estimate on contract; binding on booking | **High** | -| 6 | Contract intake docs | Stakeholder step 7 → contract | Wizard docs + draft booking docs mixed | `resource=contracts` intake settings | **High** | -| 7 | Pre-booking clearance (Path B) | Stakeholder: sign → upload docs → GL approves → GL books | Clearance on booking after booking create | Customer docs on `contract_id` before booking; GL creates booking | **Critical** | -| 8 | General contract qty pool | Stakeholder: no qty at contract | `contract_route_lines.quantity` + pool math | Remove quantity; validity-only | **Critical** | -| 9 | ONE_TIME re-book after payment expiry | Stakeholder requirement | EXPIRED on booking; no contract link for re-book | `contract_id` FK + partial unique index | **High** | -| 10 | Contract renewal/amendment | US-03 renewal paths | `previous_contract_id` on booking | `contracts.renewal_of_id` + amendment statuses | **High** | -| 11 | GL ET vs DJ split | GL PDF throughout | Single `edr_global_logistics` role | `edr_gl_ethiopia`, `edr_gl_djibouti` | **Critical** | -| 12 | GL milestones | 18–23 milestones per direction | 3 clearance statuses | `clearance_milestones` table | **Critical** | -| 13 | Phased document uploads | GL PDF: before pay / after pay / after data | Flat clearance doc list | `clearance_document_phases` | **High** | -| 14 | Station routing | GL US-02 | Not implemented | `gl_station_yard_id` + queue routing | **High** | -| 15 | GL-owned booking (Path B) | Stakeholder + GL US-06: GL enters all shipment data; customer pays only | Customer-only booking creation | GL ET exclusive `POST /contracts/:id/bookings`; no customer booking wizard | **Critical** | -| 16 | Djibouti Release Order | Export milestone 4; unlocks ET clearance | Single `clearance_output_*` code | DJ upload slot + milestone trigger | **High** | -| 17 | Duty/tax payment slip | Import milestone 7 | Not modeled | CUSTOMER_DUTY phase slot | **Medium** | -| 18 | Damage/exception reporting | Import US-07 AC1 | Not implemented | Incident form on GL-DJ loading | **Medium** | -| 19 | Demurrage auto-calc | GL US-12; ITLMS §8 | Not in freight-api | Future — finance module | **Low** (out of scope) | -| 20 | Multimodal GL | GL PDF multimodal section | Not implemented | Future phase | **Low** (out of scope) | -| 21 | MSRN reference prefix | GL V01: MSRNI/MSRNE | Booking reference format differs | Optional: align reference prefixes | **Low** | -| 22 | Approval hierarchy on contract | US-06 container vs bulk routing | On booking today | Move to `contract_approval_steps` | **Medium** | -| 23 | Container numbers at booking | ITLMS §1; stakeholder booking step | `container_number` nullable on line; not per-unit | `booking_container_units` | **High** | -| 24 | Domestic/intercity clearance | No customs gate | `clearance.util` returns null for DOMESTIC | Unchanged | **None** | -| 25 | Batch/scheduling/allocation | ITLMS §1 priority, wagon calc | Implemented on booking | Unchanged — operates on booking after GL/customer creates it | **None** | -| 26 | Customer payment-only in customs path | Stakeholder: customer pays; GL does rest | Customer enters all data + pays | Customer: upload docs + pay PNR/duty only | **Critical** | - ---- - -## 15. API Endpoint Mapping - -### 15.1 New Contract Endpoints - -| Method | Path | Replaces | Notes | -|--------|------|----------|-------| -| POST | `/contracts` | `POST /bookings` | Multipart; creates contract + routes + cargo scope | -| PATCH | `/contracts/:id` | `PATCH /bookings/:id` | Customer editable in DRAFT, CHANGES_REQUESTED | -| GET | `/contracts` | `GET /bookings?bookingType=GENERAL_CONTRACT` | List/filter | -| GET | `/contracts/my` | Portal scoped list | | -| GET | `/contracts/:id` | Contract detail | Includes routes, cargo scope, unit rates | -| POST | `/contracts/:id/documents` | `POST /bookings/:id/documents` | Intake docs | -| POST | `/contracts/:id/generate-price` | Same on booking | Returns unit rates | -| POST | `/contracts/:id/submit` | Same on booking | Freezes contract_rate_snapshots | -| POST | `/contracts/:id/confirm-submit` | Same | Price change confirm | -| POST | `/contracts/:id/staff/accept` | Same | Sets validity window | -| POST | `/contracts/:id/staff/request-changes` | Same | | -| POST | `/contracts/:id/staff/reject` | Same | | -| POST | `/contracts/:id/approval-steps/:stepId/approve` | Same | | -| POST | `/contracts/:id/contract/generate` | Same | PDF from contract | -| GET | `/contracts/:id/contract/view` | Same | | -| POST | `/contracts/:id/contract/sign` | Same | | -| POST | `/contracts/:id/renew` | Partial via previousContractRef | Renewal workflow | -| POST | `/contracts/:id/renew/accept-amendments` | New | Customer accepts | -| POST | `/contracts/:id/renew/reject-amendments` | New | Customer rejects | - -### 15.2 Booking-under-Contract Endpoints - -| Method | Path | Actor | Notes | -|--------|------|-------|-------| -| POST | `/contracts/:contractId/bookings` | **Customer** (Path A only) | `customs_clearing_enabled = false` | -| POST | `/contracts/:contractId/bookings` | **GL ET** (Path B only) | `clearance_status = CLEARANCE_READY_FOR_BOOKING` | -| GET | `/contracts/:contractId/bookings` | Customer / staff | List shipments under contract | - -### 15.2.1 Contract Pre-Booking Clearance Endpoints (Path B) - -| Method | Path | Actor | Notes | -|--------|------|-------|-------| -| GET | `/contracts/:id/clearance` | Customer / GL ET | Document grid on contract (no booking) | -| POST | `/contracts/:id/clearance/documents` | Customer | Upload clearance docs after sign | -| POST | `/contracts/:id/clearance/review` | GL ET | Approve/query per `fileKey` | -| POST | `/contracts/:id/clearance/output-documents` | GL ET / GL DJ | GL output uploads pre-booking | -| POST | `/contracts/:id/clearance/finalize` | GL ET | → `CLEARANCE_READY_FOR_BOOKING` | -| GET | `/contracts/clearance/queue` | GL ET | Contracts awaiting review (not bookings) | - -### 15.3 Unchanged Booking Endpoints (operate on shipment) - -All clearance, operation, payment, batch, transit endpoints remain on `/bookings/:id/*`: - -- `/bookings/:id/clearance/*` -- `/bookings/:id/clearance/proceed` -- `/bookings/:id/operation/review` -- `/bookings/:id/payment/pay` -- `/bookings/:id/operations/start-transit` -- `/bookings/:id/operations/complete` - -### 15.4 New GL Endpoints - -| Method | Path | Actor | -|--------|------|-------| -| GET | `/contracts/clearance/queue?region=ET` | GL ET — **contracts** awaiting doc review | -| GET | `/bookings/clearance/queue?region=ET\|DJ` | GL ET/DJ — post-booking milestone queue | -| POST | `/bookings/:id/clearance/milestones/:code/complete` | GL/Ops/Terminal | -| POST | `/bookings/:id/clearance/incidents` | GL-DJ damage report | -| POST | `/bookings/:id/gl/station-assign` | GL station manager | -| GET | `/contracts/:id/read-only` | GL read-only contract view | - -### 15.5 Deprecated Endpoints (Phase 4) - -| Endpoint | Replacement | -|----------|-------------| -| `POST /bookings` (contract creation) | `POST /contracts` | -| `POST /booking-orders` | `POST /contracts/:id/bookings` | -| `GET /booking-orders/contract/:id/pool` | Removed — no qty pool | - ---- - -## 16. Frontend Route & Component Mapping - -### 16.1 Portal Routes (Target) - -| Current | Target | Action | -|---------|--------|--------| -| `/bookings/new` | `/contracts/new` | New contract wizard (§7) | -| `/bookings/:id` (contract phase) | `/contracts/:id` | Contract detail + sign | -| `/bookings/:id/contract` | `/contracts/:id/contract` | Move `BookingContractPage` → `ContractPage` | -| `/contracts` | `/contracts` | Query `contracts` table (both kinds) | -| `/contracts/:id` | `/contracts/:id` | Show unit rates, validity, booking list | -| `PlaceOrderDialog` | `/contracts/:id/bookings/new` | Path A only — customer booking wizard (§8.1) | -| New | `/contracts/:id/clearance` | Path B — customer uploads clearance docs after sign | -| `/contracts/:id` | `/contracts/:id` | Path B: clearance CTA; Path A: "New booking" CTA | -| `/bookings/:id` (shipment) | `/bookings/:id` | Shipment detail — **Pay** (Path B primary action), track | -| `/bookings` | `/bookings` | My shipments list | - -### 16.2 Portal Component Changes - -| File | Change | -|------|--------| -| `new-booking-form/schema.ts` | Split into `new-contract-form/schema.ts` + `new-shipment-form/schema.ts` | -| `step5-cargo-details.tsx` | Contract: sizes/commodity only; Booking: qty + container numbers | -| `step4-route.tsx` | Contract: estimated date only; Booking: binding scheduled date | -| `step-documents.tsx` | Contract: intake docs → contract API | -| `NewBookingPage.tsx` | Rename/refactor → `NewContractPage.tsx` | -| New | `NewShipmentPage.tsx` under contract | -| `ContractDetailPage.tsx` | Path A: "New booking" CTA; Path B: "Upload clearance documents" + read-only booking list after GL books | -| New | `ContractClearanceFlow.tsx` | Path B customer clearance upload on contract (mirrors `ClearanceFlow` but targets `contract_id`) | -| `ClearanceFlow.tsx` | Path A post-booking only; Path B uses `ContractClearanceFlow` until booking exists | -| `BookingDetailPage/constants.ts` | Split `PROGRESS_STAGES` into contract vs booking | - -### 16.3 Backoffice Routes (Target) - -| Current | Target | -|---------|--------| -| `/dashboard/booking-requests` | `/dashboard/contract-requests` (contract approval queue) | -| `/dashboard/booking-requests/:id` | `/dashboard/contract-requests/:id` | -| `/dashboard/clearance` | `/dashboard/clearance/ethiopia` + `/dashboard/clearance/djibouti` (post-booking) | -| *(new)* | `/dashboard/contracts/clearance` — GL ET queue for contracts in `CLEARANCE_UNDER_REVIEW` | -| *(new)* | `/dashboard/contracts/:id/create-booking` — GL ET booking form (§8.2); **exclusive Path B booking creation** | -| *(new)* | `/dashboard/bookings/:id/milestones` — GL post-booking milestone panel | - -### 16.4 Backoffice Component Changes - -| File | Change | -|------|--------| -| `DocumentClearanceListPage.tsx` | Filter by `region=ET\|DJ`; show milestone progress | -| `ClearanceReviewSection.tsx` | Phase-grouped document tabs | -| New | `ClearanceMilestoneTimeline.tsx` | -| New | `ContractClearanceReviewSection.tsx` | GL review of docs on contract (pre-booking) | -| New | `GlCreateBookingForm.tsx` | GL ET enters schedule, qty, container numbers, bulk data (§8.2) | -| `BookingRequestsPage.tsx` | → `ContractRequestsPage.tsx` | - ---- - -## 17. Migration Plan - -### Phase 1 — Additive Schema (no breaking changes) - -1. Create `contracts`, `contract_routes`, `contract_cargo_scope`, `contract_signatures`, `contract_approval_steps`, `contract_rate_snapshots`, `contract_review_notes`, **`contract_document_review`**, **`contract_clearance_cycles`**. -2. Create `clearance_milestones`, extend `file_upload_fields` with phase columns. -3. Add `bookings.contract_id`, `bookings.contract_route_id`, `booking_container_units`. -4. Dual-write: new contract wizard writes both `contracts` and legacy `bookings` row (feature flag `CONTRACT_SPLIT_DUAL_WRITE=true`). -5. Backfill: for each `booking_type = GENERAL_CONTRACT` booking, insert `contracts` row and link existing child bookings. - -**Rollback:** Drop new tables; null `bookings.contract_id`. - -### Phase 2 — UI Switch - -1. Deploy contract wizard at `/contracts/new`; redirect `/bookings/new` → `/contracts/new`. -2. Deploy booking wizard at `/contracts/:id/bookings/new`. -3. Marketing queue reads from `contracts`. -4. Portal contract list reads `contracts` table. - -**Rollback:** Feature flag revert to legacy wizard. - -### Phase 3 — Read Switch + GL - -1. GL queues split ET/DJ; milestone panel live. -2. Contract PDF generator reads `contracts`. -3. Stop dual-write; `POST /bookings` rejects contract creation (returns 410 with redirect hint). -4. Payment, batch, allocation verified on booking-with-contract_id in staging. - -### Phase 4 — Cleanup - -1. Drop `bookings.booking_type`, `previous_contract_id`, contract columns migrated to `contracts`. -2. Drop `booking_orders` table (after data migration to direct bookings). -3. Drop `quantity` from `contract_routes`. -4. Remove `edr_global_logistics` union role. -5. Remove dual-write flag and legacy code paths. - -### Data Migration Script Outline - -```sql --- For each booking WHERE booking_type = 'GENERAL_CONTRACT': -INSERT INTO freight.contracts (...) SELECT ... FROM freight.bookings; -UPDATE freight.bookings SET contract_id = ... WHERE id = general_contract_id; -UPDATE freight.bookings SET contract_id = ... WHERE id IN (child booking ids via booking_orders); - --- For each ONE_TIME booking that went through full contract flow: -INSERT INTO freight.contracts (...); -INSERT INTO freight.bookings (contract_id, ...) -- new shipment row from operational fields; --- Or: same row gets contract_id pointing to newly inserted contract parent. -``` - -Exact backfill strategy depends on whether historical ONE_TIME rows should split into contract + booking parent-child or coalesce (recommend: insert contract from row; same row becomes shipment with `contract_id` set). - ---- - -## 18. Out of Scope / Future Work - -| Item | Source | Notes | -|------|--------|-------| -| Demurrage auto-calculation | GL US-12, ITLMS §8 | Requires dwell-time engine + finance billing module | -| Storage invoice / exit note block | GL US-12 AC3-4 | Terminal module integration | -| Multimodal GL (sea/air legs) | GL PDF multimodal section | Email-handled steps (US 03–05) need separate design | -| Finance AP closure / Permanently Closed | GL US-10, US-13 | Corporate finance module | -| MSRN reference prefix (MSRNI/MSRNE) | GL V01 AC4 | Cosmetic alignment with booking reference generator | -| Wagon type selection (NW7, PW2, etc.) | ITLMS §1 | Operations scheduling enhancement | -| 3-hour holding period aggregation | ITLMS §1 | Batch engine enhancement | -| Priority scoring syntax | ITLMS §1 | Already partially in `priority_score`; full matrix TBD | -| Ministry of Trade TIN lookup | US-01 | Onboarding module | -| Grievance/ticketing | US-09 | Separate module | -| Marketing analytics dashboard | US-10 | Separate module | - ---- - -## 19. Open Items for Sign-Off - -| # | Item | Options | Recommendation | -|---|------|---------|----------------| -| 1 | Historical ONE_TIME backfill | Split row vs. insert contract sibling | Insert contract; same booking row gets `contract_id` | -| 2 | Contract reference prefix | CTR-, CON-, keep booking ref | `CTR-YYYY-NNNNN` | -| 3 | GL booking customer confirm | Required vs. informational | **Not required** — GL booking is authoritative; customer notified + pays when due | -| 4 | GENERAL contract soft cap | Unlimited bookings vs. optional max | Unlimited within validity (per stakeholder) | -| 5 | Contract intake doc setting codes | Reuse onboarding settings vs. new | New `contract_intake_*` codes | -| 6 | Intercity contract flow | Same split vs. simplified | Same split; skip clearance gate | -| 7 | Feature flag duration | Dual-write period | 2 sprints minimum | -| 8 | Bulk hazard count semantics | Per-ton vs. per-shipment | Per booking line matching container pattern | - ---- - -## Appendix A — Contract Status Enum (Implementation) - -```typescript -export const CONTRACT_STATUSES = [ - 'DRAFT', - 'SUBMITTED', - 'PRICE_CHANGED_PENDING_CONFIRM', - 'CHANGES_REQUESTED', - 'PENDING_APPROVAL', - 'APPROVED', - 'APPROVED_PENDING_SIGNATURE', - 'CONTRACT_READY', - 'SIGNED_CUSTOMER', - 'FULLY_EXECUTED', // transport-only ONE_TIME - 'CONTRACT_ACTIVE', // transport-only GENERAL - // Path B — customs clearance (pre-booking) - 'AWAITING_CLEARANCE_DOCUMENTS', - 'CLEARANCE_UNDER_REVIEW', - 'CLEARANCE_READY_FOR_BOOKING', - 'ACTIVE_SHIPMENT_IN_PROGRESS', - 'CONTRACT_CLOSED', - 'EXPIRED', - 'REJECTED', - 'CANCELLED', - // Renewal - 'RENEWAL_DRAFT', - 'RENEWAL_SUBMITTED', - 'RENEWAL_PENDING_APPROVAL', - 'AMENDMENTS_PROPOSED', - 'ARCHIVED', -] as const; -``` - -## Appendix B — Clearance Milestone Codes (Import) - -```typescript -export const IMPORT_MILESTONES = [ - 'IMPORT_DOCS_UPLOADED', - 'PENDING_DOCUMENT_REVIEW', - 'DOCUMENTS_APPROVED', - 'UNDER_CUSTOMS_CLEARANCE', - 'DECLARED', - 'DUTY_TAXES_ADVISED', - 'DUTY_TAX_PAID', - 'DO_COLLECTED', - 'WAGON_REQUESTED', - 'FREIGHT_PAYMENT_SETTLED', - 'WAGON_ALLOCATED', - 'GATEPASS_GRANTED', - 'READY_FOR_LOADING', - 'LOADED', - 'DEPARTED_FROM_DJIBOUTI', - 'ARRIVED_ETHIOPIA', - 'OFFLOADED', - 'T1_CLOSED', - 'RISK_ASSIGNED', - 'IMPORT_RELEASE_GRANTED', - 'IMPORT_PROCESS_COMPLETED', - 'STORAGE_INVOICE_RAISED', - 'EXIT_NOTE_GENERATED', -] as const; -``` - -## Appendix C — Clearance Milestone Codes (Export) - -```typescript -export const EXPORT_MILESTONES = [ - 'EXPORT_DOCS_UPLOADED', - 'PENDING_DOCUMENT_REVIEW', - 'DOCUMENTS_APPROVED', - 'RELEASE_ORDER_SECURED', - 'UNDER_CUSTOMS_CLEARANCE', - 'DECLARED', - 'EXPORT_RELEASED', - 'WAGON_REQUESTED', - 'FREIGHT_PAYMENT_PENDING', - 'FREIGHT_PAYMENT_SETTLED', - 'WAGON_ALLOCATED', - 'CARGO_ARRIVED', - 'READY_FOR_LOADING', - 'LOADED', - 'DEPARTED_TO_DJIBOUTI', - 'ARRIVED_AT_DJIBOUTI', - 'GATEPASS_GRANTED', - 'OFFLOADED', -] as const; -``` - ---- - -*End of document.*