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
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
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/loginwith{ email, password }returns a JWT (token), sent asAuthorization: Bearer <token>. 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<schema>.otp_verificationsin Postgres, then signs within the 5-minute TTL.