# Contract seed driver Creates freight contracts across every flow variant by driving the freight API over HTTP end-to-end — from DRAFT through **both signatures** (customer sign + staff counter-sign). It stops right after the staff counter-sign; no clearance or booking steps are run. ## What it builds 20 real flows (movement × kind × customs × freight), each created twice → **40 contracts** on `all`. | Movement | Kind | Customs | Freight | Count | | --- | --- | --- | --- | --- | | intercity (DOMESTIC) | one-time / general | without only¹ | bulk / container | 4 | | import (IMPORT) | one-time / general | with / without | bulk / container | 8 | | export (EXPORT) | one-time / general | with / without | bulk / container | 8 | ¹ intercity + customs is not a real combo — DOMESTIC has no clearance gate, so the customs flag is ignored. Those four are skipped, leaving 20 (16 working + 4 `with-customs + bulk`). The four `with-customs + bulk` flows are still built here: the known break is downstream in clearance (customs output docs are container-only), which this script does not reach, so all 20 reach a signed state. ## Terminal status after both signatures (by dimension) - DOMESTIC one-time → `FULLY_EXECUTED` - any GENERAL, and DOMESTIC general → `CONTRACT_ACTIVE` - IMPORT/EXPORT one-time (customs or self-clearance) → `AWAITING_CLEARANCE_DOCUMENTS` (fully signed; clearance not driven) ## Setup ```bash cd apps/edr-freight-api/py python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt cp .env.example .env # then fill it in ``` Fill `.env`: customer + admin IAM credentials (admin should be a **super_admin**), `OTP_PHONE`, and the Postgres connection (used only to read the sign-OTP). ## Run ```bash python create_contracts.py # all 20 flows python create_contracts.py intercity # only DOMESTIC flows (4) python create_contracts.py import # only IMPORT flows (8) python create_contracts.py import export # IMPORT + EXPORT (16) ``` Filters are by movement: `intercity`, `import`, `export` (pass one or many); no arg or `all` runs everything. ## How auth + OTP work - **Login**: `POST /api/auth/login` with `{ email, password }` returns a JWT (`token`), sent as `Authorization: Bearer `. MFA accounts are not supported — the script errors out clearly if MFA is required. - **Actors**: the customer token does create/submit/customer-sign; the admin token does staff-accept/approve/generate/counter-sign. - **Sign OTP**: customer sign needs a fresh 6-digit OTP. The script calls `POST /api/otp/send { phone }`, reads the plaintext code from `.otp_verifications` in Postgres, then signs within the 5-minute TTL.