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

18 KiB
Raw Blame History

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