mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
add booking request functionality for GENERAL customs contracts
- Create migration for booking_requests table with necessary fields and indexes. - Implement BookingRequestRepository for database operations related to booking requests. - Develop BookingRequestService to handle business logic for submitting, accepting, rejecting, and canceling booking requests. - Create DTOs for creating booking requests and reviewing them. - Define BookingRequest entity to map to the booking_requests table. - Add UI components for managing shipment requests, including detail and list pages. - Implement OperationDatePicker component for selecting available shipment days.
This commit is contained in:
@@ -1,354 +1,393 @@
|
||||
# EDR Freight — How the System Works (Step by Step)
|
||||
# EDR Freight — System Flow (API)
|
||||
|
||||
A plain-language walkthrough of the whole customer journey:
|
||||
How the freight API drives a shipment end to end:
|
||||
|
||||
**Onboarding → Contract → Clearance → Booking → Schedule → Delivery**
|
||||
**Onboarding → Contract → Booking → Clearance → Operation → Schedule → Delivery**
|
||||
|
||||
Every step shows its branches. Read the arrows (`→`) as "then". Read **IF** blocks as the different paths.
|
||||
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
|
||||
|
||||
> Goal: register the company so it can make bookings. The wizard has **9 steps** in this order.
|
||||
> Register the company so a profile can transact. Only an **active profile** may create contracts or bookings.
|
||||
|
||||
```
|
||||
1. Nationality 2. Role/Operation 3. Company info 4. Personnel (GM)
|
||||
5. Contact person 6. Verify phone (OTP) 7. Power of Attorney (optional)
|
||||
8. Documents 9. Business license per profile
|
||||
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
|
||||
```
|
||||
|
||||
### Step 1 — Pick nationality
|
||||
Documents required depend **only on nationality**:
|
||||
|
||||
```
|
||||
Foreign OR Ethiopian
|
||||
```
|
||||
(stored on the company: `nationality = "foreign" | "ethiopian"`)
|
||||
|
||||
### Step 2 — Pick operation type(s)
|
||||
|
||||
You may pick **more than one**. Each one becomes its own *profile* with its own approval + license.
|
||||
|
||||
```
|
||||
Importer OR Exporter OR Freight Forwarder
|
||||
(importer | exporter | freight_forwarder)
|
||||
```
|
||||
|
||||
### Steps 3–7 — Fill company + people
|
||||
|
||||
- **Company:** TIN (auto-looked-up from eTrade), name, email, phone, address (region/zone/woreda/kebele/house), VAT, FAN.
|
||||
- **Personnel:** General Manager name / email / phone.
|
||||
- **Contact person:** name / phone (+ optional position, email).
|
||||
- **Verify:** SMS OTP sent to the contact phone — must enter the 6-digit code.
|
||||
- **Power of Attorney:** all optional.
|
||||
|
||||
### Step 8 — Upload company documents → **THIS IS WHERE THE PATH SPLITS**
|
||||
|
||||
The required documents depend **only on nationality** (NOT on operation type).
|
||||
|
||||
```
|
||||
IF Ethiopian → upload:
|
||||
• TIN Certificate
|
||||
• Commercial License
|
||||
• National ID
|
||||
|
||||
IF Foreign → upload:
|
||||
• TIN Certificate
|
||||
• Investment License
|
||||
• National ID
|
||||
• Passport
|
||||
```
|
||||
|
||||
All are required (1 file each, pdf/jpg/png, ≤10 MB).
|
||||
|
||||
### Step 9 — Business license per profile
|
||||
|
||||
For **each** operation type you picked, upload that profile's business/trade license (1+ files each).
|
||||
|
||||
### After onboarding finishes
|
||||
|
||||
```
|
||||
Company status → "Pending" (backoffice must approve)
|
||||
Each profile status → "Pending"
|
||||
|
||||
Backoffice approves each profile one by one
|
||||
→ profile status = "active", gets a reference (e.g. IM-00001 / EX-00001)
|
||||
→ only then can that profile create contracts/bookings
|
||||
```
|
||||
|
||||
**Branch summary**
|
||||
|
||||
| Nationality | Company documents required |
|
||||
|-------------|----------------------------|
|
||||
| Nationality | Company documents |
|
||||
|-------------|-------------------|
|
||||
| Ethiopian | TIN Certificate · Commercial License · National ID |
|
||||
| Foreign | TIN Certificate · Investment License · National ID · Passport |
|
||||
|
||||
> Operation type changes **nothing** in the document set — only adds one business-license card per profile.
|
||||
Operation type adds one business-license card per profile — nothing else.
|
||||
|
||||
---
|
||||
|
||||
## 2) Contract
|
||||
|
||||
> Goal: agree the terms (route, cargo, price) and sign. Only an **active profile** can do this.
|
||||
> Agree terms (route, cargo, price), run the approval chain, sign. Service: `ContractTransitionService`.
|
||||
|
||||
### Create — the wizard (4 steps)
|
||||
### Setup choices that decide later paths
|
||||
|
||||
```
|
||||
Step 0 Setup operation direction (import/export/intercity),
|
||||
contract kind (ONE_TIME vs GENERAL),
|
||||
new vs renewal, service type, currency,
|
||||
first/last mile, customs-clearing on/off, equipment return
|
||||
Step 1 Cargo+Route container sizes OR bulk commodity, hazardous/reefer flags,
|
||||
origin & destination yard, extra routes (GENERAL only)
|
||||
Step 2 Documents required onboarding docs + any contract-specific uploads
|
||||
Step 3 Review check everything, see quotation, submit (or save draft)
|
||||
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)
|
||||
```
|
||||
|
||||
Two key choices made here decide later paths:
|
||||
### Status enum (`CONTRACT_STATUSES`)
|
||||
|
||||
```
|
||||
contract kind: ONE_TIME (one shipment at a time)
|
||||
GENERAL (ship many times over a validity window)
|
||||
|
||||
customs clearing: ENABLED → Path B (Global Logistics clears for you)
|
||||
DISABLED → Path A (you self-clear) — for IMPORT/EXPORT
|
||||
(DOMESTIC/intercity → no clearance at all)
|
||||
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
|
||||
```
|
||||
|
||||
### Status journey (happy path)
|
||||
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"
|
||||
|
||||
```
|
||||
DRAFT
|
||||
→ SUBMITTED (customer submits; prices frozen)
|
||||
→ PENDING_APPROVAL (staff accepts intake, sets validity window)
|
||||
→ APPROVED (approval chain signs: LINE_STAFF → DIRECTOR → CEO)
|
||||
→ CONTRACT_READY (staff generates the contract PDF)
|
||||
→ SIGNED_CUSTOMER (customer signs)
|
||||
→ counter-sign by staff/director/ceo … then it SPLITS ↓
|
||||
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)
|
||||
```
|
||||
|
||||
### The counter-sign split → which path?
|
||||
> 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
|
||||
|
||||
```
|
||||
IF customs clearing ENABLED (IMPORT/EXPORT) → PATH B
|
||||
status → AWAITING_CLEARANCE_DOCUMENTS
|
||||
|
||||
IF customs clearing DISABLED (IMPORT/EXPORT) → PATH A (self-clear)
|
||||
status → AWAITING_CLEARANCE_DOCUMENTS
|
||||
|
||||
IF DOMESTIC / intercity (no clearance) → NO CLEARANCE
|
||||
status → CONTRACT_ACTIVE (GENERAL) or FULLY_EXECUTED (ONE_TIME)
|
||||
→ customer can book a shipment right away (skip to section 4)
|
||||
staff requestChanges → CHANGES_REQUESTED → customer edits → submit → SUBMITTED
|
||||
staff reject → REJECTED
|
||||
GENERAL contract → renew → RENEWAL_DRAFT (clone of prior version)
|
||||
```
|
||||
|
||||
**Side branches at any review stage**
|
||||
### Endpoints (`contracts.controller.ts`)
|
||||
|
||||
```
|
||||
staff requests changes → CHANGES_REQUESTED → customer edits → SUBMITTED again
|
||||
staff rejects → REJECTED
|
||||
customer/staff cancels → CANCELLED
|
||||
GENERAL contract later → renew → RENEWAL_DRAFT (copies the old contract)
|
||||
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) Clearance
|
||||
## 3) Contract-level clearance (ONE_TIME + customs only)
|
||||
|
||||
> Only happens for IMPORT/EXPORT contracts. Two paths. The loop is the same idea:
|
||||
> **customer uploads → reviewer approves or queries → customer re-uploads → … → finalize.**
|
||||
> Runs only when the counter-sign branch opened a contract clearance cycle.
|
||||
> Service: `ContractClearanceService`. Loop: **upload → review (approve/query) → re-upload → finalize.**
|
||||
|
||||
### Who reviews?
|
||||
### Who reviews
|
||||
|
||||
```
|
||||
PATH A (self-clear, customs DISABLED) → reviewed by OPERATIONS team
|
||||
PATH B (customs, customs ENABLED) → reviewed by GLOBAL LOGISTICS (GL)
|
||||
Path A (customsClearingEnabled = false) → OPERATIONS
|
||||
Path B (customsClearingEnabled = true) → GLOBAL LOGISTICS Ethiopia (GL ET)
|
||||
```
|
||||
|
||||
### The status sub-states
|
||||
### Document states & loop
|
||||
|
||||
```
|
||||
AWAITING_CLEARANCE_DOCUMENTS customer must upload
|
||||
CLEARANCE_UNDER_REVIEW reviewer is checking
|
||||
CLEARANCE_READY_FOR_BOOKING (Path B) done — GL will make the booking
|
||||
SELF_CLEARED (Path A) done — customer will make the booking
|
||||
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
|
||||
```
|
||||
|
||||
### The review loop (both paths)
|
||||
### Required-doc resolution (`contract-clearance.util.ts`)
|
||||
|
||||
```
|
||||
1. Customer uploads all required documents
|
||||
→ status = CLEARANCE_UNDER_REVIEW
|
||||
→ each document = PENDING
|
||||
input (customer uploads):
|
||||
Path B: contract_clearance_{import|export}_{container|bulk}
|
||||
Path A: contract_clearance_selfclear_{import|export}_{container|bulk}
|
||||
DOMESTIC: null (no gate)
|
||||
|
||||
2. Reviewer goes document by document:
|
||||
APPROVE → that document = APPROVED
|
||||
QUERY → that document = QUERIED (note required)
|
||||
→ contract drops back to AWAITING_CLEARANCE_DOCUMENTS
|
||||
(only the queried doc needs re-uploading; approved ones stay)
|
||||
|
||||
3. Customer re-uploads the queried document → back to step 2
|
||||
|
||||
4. When ALL required documents are APPROVED → finalize (below)
|
||||
output (GL uploads, container customs only):
|
||||
contract_clearance_output_{import|export}_container
|
||||
(bulk or non-customs → null)
|
||||
```
|
||||
|
||||
### PATH A — self-clear (Operations)
|
||||
### Finalize
|
||||
|
||||
```
|
||||
documents the CUSTOMER uploads (examples):
|
||||
import: customs declaration (IM4/IM5), import release, duty/tax receipt,
|
||||
delivery order, supporting doc
|
||||
export: customs declaration (EX3/EX8), export release, transit (T1), supporting doc
|
||||
|
||||
no output documents in Path A.
|
||||
|
||||
Operations finalize (POST .../clearance/ops-finalize)
|
||||
requires: every required doc APPROVED
|
||||
opsFinalize() Path A — requires every required input doc APPROVED
|
||||
→ clearanceStatus = SELF_CLEARED
|
||||
→ contract status = CONTRACT_ACTIVE (GENERAL) or FULLY_EXECUTED (ONE_TIME)
|
||||
→ CUSTOMER creates the booking (section 4)
|
||||
```
|
||||
→ contract status = CONTRACT_ACTIVE (GENERAL) | FULLY_EXECUTED (ONE_TIME)
|
||||
→ CUSTOMER creates booking
|
||||
|
||||
### PATH B — customs (Global Logistics)
|
||||
|
||||
```
|
||||
documents the CUSTOMER uploads (examples):
|
||||
import container: commercial invoice, packing list, import license,
|
||||
certificate of origin, freight cost, bill of lading,
|
||||
VGM*, release order*
|
||||
export container: booking confirmation, invoice, packing list,
|
||||
shipping instruction, bank permit, export license,
|
||||
VGM letter*, railway bill, delegation letter
|
||||
(* = required)
|
||||
|
||||
then GL uploads OUTPUT documents (container only):
|
||||
import: IM4 (required), IM5 (optional), transit screenshot
|
||||
export: EX3 (required), EX8, export release, T1
|
||||
|
||||
GL finalize (POST .../clearance/finalize)
|
||||
requires: every required customer doc APPROVED
|
||||
AND every required output doc uploaded
|
||||
finalize() Path B — requires every required input APPROVED + every required output uploaded
|
||||
→ clearanceStatus = CLEARANCE_READY_FOR_BOOKING
|
||||
→ GL (not the customer) creates the booking (section 4)
|
||||
→ GL creates booking
|
||||
```
|
||||
|
||||
### Cycles (GENERAL contracts)
|
||||
### Endpoints
|
||||
|
||||
A **cycle** is one clearance round. ONE_TIME contracts have a single cycle (#1). GENERAL contracts open a new cycle each time they need clearance before the next shipment.
|
||||
|
||||
> Clearance (section 3) is **pre-booking**. After GL creates the booking, the work continues as **GL Phase 2** — see section 4b.
|
||||
```
|
||||
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
|
||||
## 4) Booking — who creates it & the gate
|
||||
|
||||
> Goal: turn a cleared/executed contract into an actual shipment. **Who creates it depends on the path.**
|
||||
> Turn a ready contract into a shipment. Service: `ContractBookingService` (creation gate), `BookingTransitionService` (lifecycle).
|
||||
> Endpoint: `POST /contracts/:id/bookings`.
|
||||
|
||||
### The gate (`assertGate`)
|
||||
|
||||
```
|
||||
PATH A / DOMESTIC → the CUSTOMER creates the booking
|
||||
PATH B (customs) → GLOBAL LOGISTICS creates the booking on the customer's behalf
|
||||
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
|
||||
```
|
||||
|
||||
### The gate (who's allowed)
|
||||
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`)
|
||||
|
||||
```
|
||||
IF contract has customs clearing (Path B):
|
||||
only GL, and only when clearanceStatus = CLEARANCE_READY_FOR_BOOKING
|
||||
|
||||
IF no customs (Path A / DOMESTIC):
|
||||
customer (or staff), and only when contract is FULLY_EXECUTED / CONTRACT_ACTIVE
|
||||
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
|
||||
```
|
||||
|
||||
### Booking wizard (customer self-booking — 7 steps)
|
||||
Payment (`paymentStatus`): `PENDING → PNR_GENERATED → VERIFICATION_IN_PROGRESS → PAID` (or `FAILED`).
|
||||
Scheduling (`schedulingStatus`): `NOT_SCHEDULED · HOLDING · ELIGIBLE · SCHEDULED · DISPATCHED`.
|
||||
|
||||
```
|
||||
0 Operation type import / export / intercity (+ FF variants)
|
||||
1 Contract type ONE_TIME vs GENERAL ; new vs renewal
|
||||
2 Service & mile service type, currency (USD/ETB), first/last mile,
|
||||
equipment return, customs agent / customs on-off
|
||||
3 Cargo details container list (type, qty, VGM) OR bulk weight,
|
||||
hazardous / refrigerated flags
|
||||
4 Route origin & destination yard;
|
||||
scheduledDate REQUIRED for ONE_TIME (estimate only),
|
||||
NOT set for GENERAL (a day is chosen later)
|
||||
5 Documents per-booking document uploads
|
||||
6 Review notes, submit
|
||||
```
|
||||
|
||||
### Booking status journey
|
||||
### Customer-self-booking lifecycle (Path A / domestic — same approval shape as a contract)
|
||||
|
||||
```
|
||||
DRAFT
|
||||
→ generate price → SUBMITTED (if price changed: PRICE_CHANGED_PENDING_CONFIRM → confirm → SUBMITTED)
|
||||
→ PENDING_APPROVAL (staff accept intake)
|
||||
→ APPROVED / CONTRACT_READY (approval chain)
|
||||
→ SIGNED_CUSTOMER → counter-sign … SPLIT ↓
|
||||
|
||||
IF clearance applies → AWAITING_DOCUMENTS → DOCUMENTS_UNDER_REVIEW → CLEARANCE_READY
|
||||
IF no clearance → FULLY_EXECUTED directly
|
||||
→ 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)
|
||||
```
|
||||
|
||||
> The booking has its **own** document clearance loop, mirroring the contract one
|
||||
> (upload → APPROVED/QUERIED → re-upload → finalize). Re-uploading a queried doc
|
||||
> resets it to PENDING. Booking proceeds only when all required docs are APPROVED.
|
||||
Side branches: `requestChanges → CHANGES_REQUESTED`; `staffReject / reject → REJECTED`; `cancel → CANCELLED` (DRAFT…CONTRACT_READY).
|
||||
|
||||
### Pricing & payment
|
||||
### Transition methods (`BookingTransitionService`)
|
||||
|
||||
```
|
||||
price generated from rule engine + live rates, converted to chosen currency (USD/ETB)
|
||||
customer pays (Telebirr) once the booking is FULLY_EXECUTED / SELECTED_FOR_BATCH
|
||||
payment status: PENDING → VERIFICATION_IN_PROGRESS → PAID (or FAILED)
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4b) Global Logistics — Phase 2 (after the booking exists)
|
||||
## 5) Per-booking clearance + operation request
|
||||
|
||||
> Customs (Path B) shipments keep moving through GL after booking. This phase is
|
||||
> a **milestone timeline** plus a set of **structured GL actions**. The customer
|
||||
> only watches and, when asked, pays / uploads a duty slip.
|
||||
> The customs booking's own document loop, then everyone funnels into the operation request that puts the shipment in the schedule pool.
|
||||
|
||||
### Milestone timeline
|
||||
|
||||
When GL creates the booking, the system seeds the **post-booking milestones**
|
||||
for that direction (import ~15, export ~11). Each is `PENDING → COMPLETED`.
|
||||
### Per-booking clearance (customs bookings only)
|
||||
|
||||
```
|
||||
Import (post-booking): 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
|
||||
|
||||
Export (post-booking): 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
|
||||
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
|
||||
```
|
||||
|
||||
Each milestone has an **owner**: ET (GL Ethiopia), DJ (GL Djibouti), OPS (Operations),
|
||||
CUST (customer). Backoffice shows the timeline with a **Complete** button on the
|
||||
next pending step; the customer portal shows the same timeline **read-only**.
|
||||
inputCode / outputCode resolve from `(tradeDirection, freightType, customsClearingEnabled)` — same scheme as the contract loop. Doc review status: `PENDING · APPROVED · QUERIED`.
|
||||
|
||||
### GL actions (the structured part)
|
||||
|
||||
Plain "Complete" covers most steps. These carry extra data, so they have their
|
||||
own UI cards on the backoffice **milestones page** (`GlActionsPanel`):
|
||||
### Operation request (all paths)
|
||||
|
||||
```
|
||||
Station routing → route shipment to a station yard (+ bind GL staff) (GL US-02)
|
||||
Customs risk → assign GREEN / YELLOW / RED → completes RISK_ASSIGNED
|
||||
Duty & tax → GL advises amount + declaration serial → completes
|
||||
DUTY_TAXES_ADVISED → customer uploads slip → DUTY_TAX_PAID
|
||||
GL documents → upload DO / RO / T1 / import release / interchange /
|
||||
final declaration → auto-completes the matching milestone
|
||||
Cargo exception → log SEAL_BROKEN / CONTAINER_OPENED / CONTAINER_DAMAGED /
|
||||
FLUID_LEAKING with photos → alert GL Ethiopia (GL US-07)
|
||||
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)
|
||||
```
|
||||
|
||||
**Doc-triggered milestones:** uploading the mapped document completes the
|
||||
milestone automatically — no separate click:
|
||||
### Clearance / operation endpoints (`bookings.controller.ts`)
|
||||
|
||||
| Upload (code) | Completes milestone | Who |
|
||||
|---------------|---------------------|-----|
|
||||
```
|
||||
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 |
|
||||
@@ -356,86 +395,99 @@ milestone automatically — no separate click:
|
||||
| `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) | — |
|
||||
|
||||
### ET ↔ DJ handoff
|
||||
### Endpoints
|
||||
|
||||
```
|
||||
DEPARTED_FROM_DJIBOUTI (import) → lead returns to GL Ethiopia + Operations
|
||||
DEPARTED_TO_DJIBOUTI (export) → lead moves to GL Djibouti
|
||||
```
|
||||
Ownership region is encoded per-milestone in the catalog; notifications fire on
|
||||
handoff (notification module pending).
|
||||
|
||||
### What the customer does in Phase 2
|
||||
|
||||
```
|
||||
watch the timeline (read-only)
|
||||
pay duty/tax → upload payment slip (only when GL advised it)
|
||||
pay freight → Pay button on the booking when batch-selected
|
||||
that's all — every other step is GL / Ops / Terminal
|
||||
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
|
||||
```
|
||||
|
||||
### Where it lives (Phase 2)
|
||||
|
||||
| Area | Files |
|
||||
|------|-------|
|
||||
| Milestone seed/advance | `api/.../contracts/clearance-milestone.service.ts`, `clearance-milestone.catalog.ts` |
|
||||
| GL actions (risk/duty/station/docs/incident) | `api/.../contracts/gl-operations.service.ts`, `dto/gl-operations.dto.ts`, `entities/clearance-incident.entity.ts` |
|
||||
| Endpoints | `api/.../contracts/contracts.controller.ts` (`bookings/:id/risk` · `/duty` · `/station-assign` · `/documents` · `/incidents` · `/duty-slip`) |
|
||||
| Backoffice UI | `backoffice/.../pages/contracts/BookingMilestonesPage.tsx`, `components/contracts/ClearanceMilestoneTimeline.tsx`, `components/contracts/gl-actions/*` |
|
||||
| Portal UI | `portal/.../bookings/BookingDetailPage/components/ShipmentTrackingCard.tsx` |
|
||||
|
||||
### Still out of scope (per design doc §18)
|
||||
|
||||
Demurrage auto-calc & storage invoicing, finance AP closure, multimodal
|
||||
(sea/air + MTO/OBL/HBL), truck waybill PDF + POD signing. `STORAGE_INVOICE_RAISED`
|
||||
and `EXIT_NOTE_GENERATED` exist as **manual milestones** only — no fee engine yet.
|
||||
> 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.
|
||||
|
||||
---
|
||||
|
||||
## 5) Schedule (Operations)
|
||||
## 7) Schedule — demand batching (`booking-batch.service.ts`)
|
||||
|
||||
> Goal: put the booking on a train (or dispatch by road). Day-level pooling — the
|
||||
> customer picks a **day**, the batch engine assigns the actual **train** later.
|
||||
> 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)
|
||||
|
||||
```
|
||||
1. Customer requests operation pick a day that has an OPEN departure
|
||||
→ OPERATION_REQUEST_PENDING
|
||||
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
|
||||
```
|
||||
|
||||
2. Operations review the request → one of:
|
||||
ACCEPT → FULLY_EXECUTED (enters the train batch pool)
|
||||
(road service instead → ROAD_DISPATCH_PENDING, section 6)
|
||||
REQUEST_CHANGES → OPERATION_CHANGES_REQUESTED (note required; customer resubmits)
|
||||
ADJUST_PRICE → OPERATION_PRICE_PENDING_CONFIRM
|
||||
(customer confirms new price → pool, or rejects → changes requested)
|
||||
### The cron cycle (every 3h; prod `0 */3 * * *`)
|
||||
|
||||
3. Batch engine (cron) groups bookings by (origin yard, destination yard, day):
|
||||
allocates to open train schedules by priority score
|
||||
→ SELECTED_FOR_BATCH, assigns trainScheduleId, sets payment deadline
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
4. Payment (if not already paid) → PAID
|
||||
`reserve()` → SELECTED_FOR_BATCH. `allocate()` → create `TrainScheduleBooking`, PAID, schedulingStatus SCHEDULED. `expire()` → trainScheduleId null, EXPIRED, schedulingStatus ELIGIBLE (back in pool).
|
||||
|
||||
5. IN_TRANSIT → COMPLETED
|
||||
### Payment → transit
|
||||
|
||||
```
|
||||
SELECTED_FOR_BATCH / AWAITING_PAYMENT → pay (Telebirr) → PAID
|
||||
PAID → startTransit → IN_TRANSIT → complete → COMPLETED
|
||||
```
|
||||
|
||||
### Wagon math
|
||||
|
||||
```
|
||||
wagons per booking = sum over containers of (qty × wagonsPerUnit), rounded up
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Delivery / Last mile
|
||||
## 8) Delivery / last mile
|
||||
|
||||
```
|
||||
IF road service:
|
||||
ACCEPT → ROAD_DISPATCH_PENDING (skips the train pool)
|
||||
billed by KM, dispatched by truck (First-Mile operations)
|
||||
Road service: reviewOperationRequest ACCEPT → ROAD_DISPATCH_PENDING (skips train pool)
|
||||
billed by KM, dispatched by truck (First-Mile operations)
|
||||
|
||||
IF first/last mile chosen at booking:
|
||||
pickup + delivery addresses captured; equipment return = WITH / WITHOUT
|
||||
last-mile statuses: PAYMENT_PENDING → READY_TO_TRANSIT → IN_TRANSIT → RECEIVED_TO_PORT
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
@@ -444,47 +496,47 @@ IF first/last mile chosen at booking:
|
||||
|
||||
```
|
||||
ONBOARD
|
||||
nationality ─┬─ Ethiopian → TIN + Commercial License + National ID
|
||||
└─ Foreign → TIN + Investment License + National ID + Passport
|
||||
pick profiles (importer/exporter/FF) → upload license per profile
|
||||
→ backoffice approves profile → profile ACTIVE
|
||||
profile (importer/exporter/FF) approved → ACTIVE → can transact
|
||||
|
||||
CONTRACT
|
||||
wizard (setup → cargo+route → docs → review) → SUBMITTED
|
||||
→ staff accept → approval chain → CONTRACT_READY → customer sign → counter-sign
|
||||
→ SPLIT:
|
||||
customs ENABLED (import/export) → PATH B clearance
|
||||
customs DISABLED (import/export) → PATH A clearance
|
||||
DOMESTIC → no clearance, ready to book
|
||||
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
|
||||
|
||||
CLEARANCE (import/export only) loop: upload → approve/query → re-upload → finalize
|
||||
PATH A: Operations review → SELF_CLEARED → CUSTOMER books
|
||||
PATH B: GL review + GL output docs → READY_FOR_BOOKING → GL books
|
||||
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
|
||||
|
||||
BOOKING
|
||||
created by CUSTOMER (Path A / domestic) or GL (Path B)
|
||||
price → pay → (its own doc clearance if applicable) → ready to schedule
|
||||
PER-BOOKING CLEARANCE (customs only, BookingTransitionService)
|
||||
upload → review(approve/query) → finalize → CLEARANCE_READY → proceed → OPERATION_REQUEST_PENDING
|
||||
|
||||
GL PHASE 2 (customs/Path B, after booking)
|
||||
milestone timeline: wagon → pay → allocate → load → depart → handover
|
||||
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: station routing · risk (G/Y/R) · duty advise · DO/RO/T1 upload · incident
|
||||
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
|
||||
request a day → Operations accept → batch engine → train assigned
|
||||
→ pay → IN_TRANSIT → COMPLETED
|
||||
(road service → ROAD_DISPATCH_PENDING → truck)
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Where this lives in the code (quick map)
|
||||
### Code map
|
||||
|
||||
| Area | Key files |
|
||||
|------|-----------|
|
||||
| Onboarding | `portal/.../components/onboarding/OnboardingWizardDialog.tsx`, `api/.../companies/companies.service.ts`, `api/src/seed/file-upload-settings.seeder.ts` |
|
||||
| Contract | `portal/.../contracts/new-contract-form/`, `api/.../contracts/contract-transition.service.ts`, `entities/contract.entity.ts` |
|
||||
| Clearance | `api/.../contracts/contract-clearance.service.ts`, `contract-clearance.util.ts`, `portal/.../contracts/ContractClearancePanel.tsx` |
|
||||
| Booking | `portal/.../bookings/new-booking-form/`, `api/.../bookings/booking-transition.service.ts`, `contract-booking.service.ts` |
|
||||
| Schedule | `api/.../train-scheduling/booking-batch.service.ts`, `backoffice/.../operations/FirstMilePage.tsx` |
|
||||
| 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` |
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user