mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 17:50:54 +00:00
491 lines
18 KiB
Markdown
491 lines
18 KiB
Markdown
# EDR Freight — How the System Works (Step by Step)
|
||
|
||
A plain-language walkthrough of the whole customer journey:
|
||
|
||
**Onboarding → Contract → Clearance → Booking → Schedule → Delivery**
|
||
|
||
Every step shows its branches. Read the arrows (`→`) as "then". Read **IF** blocks as the different paths.
|
||
|
||
---
|
||
|
||
## 1) Onboarding
|
||
|
||
> Goal: register the company so it can make bookings. The wizard has **9 steps** in this order.
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
### Step 1 — Pick 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 |
|
||
|-------------|----------------------------|
|
||
| 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.
|
||
|
||
---
|
||
|
||
## 2) Contract
|
||
|
||
> Goal: agree the terms (route, cargo, price) and sign. Only an **active profile** can do this.
|
||
|
||
### Create — the wizard (4 steps)
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
Two key choices made here decide later paths:
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
### Status journey (happy path)
|
||
|
||
```
|
||
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 ↓
|
||
```
|
||
|
||
### The counter-sign split → which path?
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
**Side branches at any review stage**
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
---
|
||
|
||
## 3) Clearance
|
||
|
||
> Only happens for IMPORT/EXPORT contracts. Two paths. The loop is the same idea:
|
||
> **customer uploads → reviewer approves or queries → customer re-uploads → … → finalize.**
|
||
|
||
### Who reviews?
|
||
|
||
```
|
||
PATH A (self-clear, customs DISABLED) → reviewed by OPERATIONS team
|
||
PATH B (customs, customs ENABLED) → reviewed by GLOBAL LOGISTICS (GL)
|
||
```
|
||
|
||
### The status sub-states
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
### The review loop (both paths)
|
||
|
||
```
|
||
1. Customer uploads all required documents
|
||
→ status = CLEARANCE_UNDER_REVIEW
|
||
→ each document = PENDING
|
||
|
||
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)
|
||
```
|
||
|
||
### PATH A — self-clear (Operations)
|
||
|
||
```
|
||
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
|
||
→ clearanceStatus = SELF_CLEARED
|
||
→ contract status = CONTRACT_ACTIVE (GENERAL) or FULLY_EXECUTED (ONE_TIME)
|
||
→ CUSTOMER creates the booking (section 4)
|
||
```
|
||
|
||
### 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
|
||
→ clearanceStatus = CLEARANCE_READY_FOR_BOOKING
|
||
→ GL (not the customer) creates the booking (section 4)
|
||
```
|
||
|
||
### Cycles (GENERAL contracts)
|
||
|
||
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.
|
||
|
||
---
|
||
|
||
## 4) Booking
|
||
|
||
> Goal: turn a cleared/executed contract into an actual shipment. **Who creates it depends on the path.**
|
||
|
||
```
|
||
PATH A / DOMESTIC → the CUSTOMER creates the booking
|
||
PATH B (customs) → GLOBAL LOGISTICS creates the booking on the customer's behalf
|
||
```
|
||
|
||
### The gate (who's allowed)
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
### Booking wizard (customer self-booking — 7 steps)
|
||
|
||
```
|
||
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
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
> 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.
|
||
|
||
### Pricing & payment
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
---
|
||
|
||
## 4b) Global Logistics — Phase 2 (after the booking exists)
|
||
|
||
> 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.
|
||
|
||
### 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`.
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
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**.
|
||
|
||
### 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`):
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
**Doc-triggered milestones:** uploading the mapped document completes the
|
||
milestone automatically — no separate click:
|
||
|
||
| Upload (code) | Completes milestone | Who |
|
||
|---------------|---------------------|-----|
|
||
| `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** |
|
||
|
||
### ET ↔ DJ handoff
|
||
|
||
```
|
||
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
|
||
```
|
||
|
||
### 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.
|
||
|
||
---
|
||
|
||
## 5) Schedule (Operations)
|
||
|
||
> 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.
|
||
|
||
```
|
||
1. Customer requests operation pick a day that has an OPEN departure
|
||
→ OPERATION_REQUEST_PENDING
|
||
|
||
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)
|
||
|
||
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
|
||
|
||
4. Payment (if not already paid) → PAID
|
||
|
||
5. IN_TRANSIT → COMPLETED
|
||
```
|
||
|
||
### Wagon math
|
||
|
||
```
|
||
wagons per booking = sum over containers of (qty × wagonsPerUnit), rounded up
|
||
```
|
||
|
||
---
|
||
|
||
## 6) Delivery / Last mile
|
||
|
||
```
|
||
IF road service:
|
||
ACCEPT → ROAD_DISPATCH_PENDING (skips the 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
|
||
```
|
||
|
||
---
|
||
|
||
## The whole thing on one page
|
||
|
||
```
|
||
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
|
||
|
||
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
|
||
|
||
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
|
||
created by CUSTOMER (Path A / domestic) or GL (Path B)
|
||
price → pay → (its own doc clearance if applicable) → ready to schedule
|
||
|
||
GL PHASE 2 (customs/Path B, after booking)
|
||
milestone timeline: wagon → pay → allocate → load → depart → handover
|
||
→ arrive → offload → T1 close → risk → release → complete
|
||
GL actions: station routing · risk (G/Y/R) · duty advise · 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)
|
||
```
|
||
|
||
---
|
||
|
||
### Where this lives in the code (quick 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` |
|