Files
edr-platform/docs/freight-platform/SYSTEM-FLOW.md

491 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 37 — 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` |