Charts & flow diagrams

See how the whole system works

16 diagrams covering the architecture, the role hierarchy, the data model and every operational workflow. The trip lifecycle and the permission matrix are generated from the code that enforces them, so they cannot fall out of date.

Flowcharts

Planning, publication rules, exceptions, attendance, billing and notifications — each decision drawn out.

Data models

Three entity diagrams: the core spine, fleet and compliance, and everything commercial.

Live from the code

The lifecycle state machine and the role permission grid are read straight from the enforcing source.

Start here

Two diagrams that explain the shape of the whole product.

The core journey: demand becomes a trip

This is the loop the product exists to run. Everything else supports it. Note that a route is a plan you can edit freely, and publishing is the one-way door that turns it into an operational record.

flowchart TD
    START(["Transport Manager opens the Route Planner"]) --> PICK["Choose service date,<br/>site and shift"]
    PICK --> LOAD["Load demand"]
    LOAD --> HAS{"Any open<br/>demand?"}
    HAS -->|"No"| GEN["Generate demand<br/>from the roster"]
    GEN --> LOAD
    HAS -->|"Yes"| POOL["Generate routes<br/>pool by pickup area,<br/>split to fit capacity"]
    POOL --> EDIT["Reorder stops,<br/>move passengers"]
    EDIT --> ALLOC["Allocate vendor,<br/>vehicle and driver"]
    ALLOC --> VALID{"All rules<br/>pass?"}
    VALID -->|"No"| FIX["Fix the cause,<br/>or record an<br/>authorised override"]
    FIX --> VALID
    VALID -->|"Yes"| PUB["Publish route"]
    PUB --> TRIP["Trip created<br/>Manifest built"]
    TRIP --> NOTIFY["Passengers and driver<br/>notified"]
    NOTIFY --> DONE(["Ready to run"])

    classDef gate fill:#fdf3e3,stroke:#f2d9a8,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    class VALID,HAS gate
    class TRIP,NOTIFY,DONE good
Rendering diagram…
apps/planning/services.py

System architecture

Five kinds of client reach the same domain modules through one access layer. Nothing bypasses authentication, capability checks or tenant scoping — including the REST API. The transport policy sits to one side because it configures the others rather than being called by them.

flowchart TB
    subgraph Clients["Who uses it"]
        OPS["Operations console<br/>desktop"]
        EMP["Employee<br/>mobile web"]
        DRV["Driver<br/>mobile web"]
        VEN["Vendor portal"]
        API["Mobile apps &<br/>integrations"]
    end

    subgraph Access["Access layer"]
        AUTH["Authentication<br/>apps/accounts"]
        RBAC["Capability RBAC<br/>core/permissions.py"]
        TEN["Tenant scoping<br/>core/middleware.py"]
    end

    subgraph Domain["Domain modules"]
        PLAN["planning<br/>shifts, demand, routes"]
        FLEET["fleet<br/>vendors, vehicles, drivers"]
        TRIP["trips<br/>lifecycle, manifest, GPS"]
        SAFE["safety<br/>incidents, SOS"]
        BILL["billing<br/>rate cards, invoices"]
        EMPL["employees<br/>profiles, eligibility"]
    end

    subgraph Cross["Cross-cutting"]
        POLICY["Transport policy<br/>the rules engine"]
        AUDIT["Audit log"]
        NOTIF["Notifications"]
        REP["Reports"]
    end

    DB[("PostgreSQL")]

    OPS --> AUTH
    EMP --> AUTH
    DRV --> AUTH
    VEN --> AUTH
    API --> AUTH
    AUTH --> RBAC --> TEN
    TEN --> Domain
    Domain --> Cross
    Domain --> DB
    Cross --> DB
    POLICY -.->|"configures"| PLAN
    POLICY -.->|"configures"| TRIP
    POLICY -.->|"configures"| SAFE
Rendering diagram…
config/settings.py, apps/core/

People & permissions

Who does what, and what the server actually allows them to do.

Role hierarchy

Solid lines are administrative authority; dashed lines are working relationships. Vendors, drivers and employees sit outside the internal console and see deliberately narrower views of the same data.

flowchart TB
    SA["Platform Super Admin<br/><i>provisions tenants</i>"]
    TA["Tenant Admin<br/><i>configures the rules</i>"]
    TM["Transport Manager<br/><i>plans and publishes</i>"]
    DP["Dispatcher<br/><i>runs the shift</i>"]
    CR["Control Room<br/><i>answers safety alerts</i>"]
    FI["Finance<br/><i>rate cards and invoices</i>"]
    VE["Vendor<br/><i>supplies cabs</i>"]
    DR["Driver<br/><i>drives the trip</i>"]
    EM["Employee<br/><i>travels</i>"]

    SA --> TA
    TA --> TM
    TA --> FI
    TA --> CR
    TM --> DP
    TM -.->|"contracts"| VE
    VE --> DR
    DP -.->|"dispatches"| DR
    DR -.->|"carries"| EM
    DP -.->|"serves"| EM

    classDef platform fill:#111a4c,stroke:#111a4c,color:#fff
    classDef internal fill:#eaf1fd,stroke:#c3d8f8,color:#101828
    classDef external fill:#f1f3f7,stroke:#dfe3ea,color:#101828
    class SA platform
    class TA,TM,DP,CR,FI internal
    class VE,DR,EM external
Rendering diagram…
apps/accounts/models.py

Navigation map

Every screen in the product, grouped the way the sidebar groups them, with the roles that can reach each one. Read from the same schema that builds the real sidebar, so it always matches what people actually see.

apps/core/navigation.py — NAV_SCHEMA Generated from code

Permission matrix

Read straight out of the role table the server enforces on every view and every API endpoint. A tick means that role holds the capability; hiding a button in the interface is never what grants access.

Role Operations Planning People & fleet Safety Commercial Insight & admin Self-service
View dashboard
Dispatch
View trips
View location
Plan routes
Publish routes
Override policy
Manage requests
Manage shifts
View employees
Manage employees
View fleet
Manage fleet
Manage vendors
View incidents
Manage incidents
Manage billing
Approve invoices
Submit invoice
View reports
Manage settings
Manage users
View audit log
Employee portal
Driver app
Vendor portal
Tenant Admin Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Not allowed Allowed Allowed Allowed Allowed Not allowed Not allowed Not allowed
HR / Transport Manager Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Allowed Not allowed Not allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed
Transport Desk / Dispatcher Allowed Allowed Allowed Allowed Allowed Allowed Not allowed Allowed Not allowed Allowed Not allowed Allowed Not allowed Not allowed Allowed Allowed Not allowed Not allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed
Security / Control Room Allowed Not allowed Allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Not allowed Allowed Not allowed Not allowed Allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed
Management / Finance Allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Allowed Allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed
Vendor Manager Not allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed
Driver Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Not allowed
Employee Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Not allowed Allowed Not allowed Not allowed
apps/core/permissions.py — ROLE_PERMISSIONS Generated from code

Data model

Three views of the schema, split so each one stays readable.

Core: tenant, people, demand and trips

Follow the spine: an employee raises a transport request for a shift, the request becomes a route stop, the route is published as a trip, and the trip carries passengers. Everything hangs off a tenant.

erDiagram
    TENANT ||--o{ SITE : "operates"
    TENANT ||--|| TRANSPORT_POLICY : "configured by"
    TENANT ||--o{ BUSINESS_UNIT : "reports against"
    SITE ||--o{ SHIFT : "runs"
    SITE ||--o{ EMPLOYEE : "employs"
    BUSINESS_UNIT ||--o{ EMPLOYEE : "groups"
    EMPLOYEE ||--o{ EMERGENCY_CONTACT : "has"
    PICKUP_POINT ||--o{ EMPLOYEE : "collects from"
    EMPLOYEE ||--o{ TRANSPORT_REQUEST : "raises"
    SHIFT ||--o{ TRANSPORT_REQUEST : "for"
    SHIFT ||--o{ ROUTE : "planned for"
    TRANSPORT_REQUEST ||--o| ROUTE_STOP : "rostered as"
    ROUTE ||--o{ ROUTE_STOP : "sequences"
    PICKUP_POINT ||--o{ ROUTE_STOP : "at"
    ROUTE ||--o| TRIP : "published as"
    TRIP ||--o{ TRIP_PASSENGER : "manifests"
    TRIP ||--o{ TRIP_EVENT : "records"
    TRIP ||--o{ LOCATION_PING : "reports"
    EMPLOYEE ||--o{ TRIP_PASSENGER : "travels as"
Rendering diagram…
apps/core/, apps/employees/, apps/planning/, apps/trips/

Fleet, vendors and compliance

A compliance document belongs to a driver or a vehicle, never both — a database constraint enforces that. Mandatory documents with a past expiry are what block allocation.

erDiagram
    TENANT ||--o{ VENDOR : "contracts"
    VENDOR ||--o{ VEHICLE : "supplies"
    VENDOR ||--o{ DRIVER : "employs"
    VEHICLE_TYPE ||--o{ VEHICLE : "classifies"
    DRIVER ||--o| VEHICLE : "usually drives"
    DRIVER ||--o{ ASSIGNMENT_HISTORY : "history"
    VEHICLE ||--o{ ASSIGNMENT_HISTORY : "history"
    DOCUMENT_TYPE ||--o{ COMPLIANCE_DOCUMENT : "defines"
    DRIVER ||--o{ COMPLIANCE_DOCUMENT : "holds"
    VEHICLE ||--o{ COMPLIANCE_DOCUMENT : "holds"
    VENDOR ||--o{ TRIP : "operates"
    VEHICLE ||--o{ TRIP : "used on"
    DRIVER ||--o{ TRIP : "drives"
    USER ||--o| DRIVER : "signs in as"
    USER }o--o{ VENDOR : "portal access"
Rendering diagram…
apps/fleet/models.py

Rate cards, invoices and payments

An invoice line points back at the trip it prices and the rate card it was priced with, so any figure can be traced to the journey that produced it.

erDiagram
    VENDOR ||--o{ RATE_CARD : "priced by"
    VEHICLE_TYPE ||--o{ RATE_CARD : "narrows"
    SITE ||--o{ RATE_CARD : "narrows"
    VENDOR ||--o{ INVOICE : "billed on"
    INVOICE ||--o{ INVOICE_LINE : "itemises"
    TRIP ||--o| INVOICE_LINE : "priced as"
    RATE_CARD ||--o{ INVOICE_LINE : "applied to"
    INVOICE ||--o{ PAYMENT : "settled by"
    USER ||--o{ INVOICE : "approves"
    USER ||--o{ INVOICE_LINE : "adjusts"
Rendering diagram…
apps/billing/models.py

Trip execution

How a trip moves, who moves it, and what the system refuses to do.

Trip lifecycle

Generated directly from the transition table the product enforces, so this diagram cannot drift from the code. Any jump not drawn here is rejected — a planned trip cannot become completed, for example.

stateDiagram-v2
    direction LR
    [*] --> Planned
    Planned --> Assigned
    Planned --> Cancelled
    Planned --> Exception
    Assigned --> Cancelled
    Assigned --> Confirmed
    Assigned --> Exception
    Assigned --> Planned
    Confirmed --> Cancelled
    Confirmed --> EnRoute
    Confirmed --> Exception
    EnRoute --> AtPickup
    EnRoute --> Cancelled
    EnRoute --> Exception
    EnRoute --> InProgress
    AtPickup --> Cancelled
    AtPickup --> Exception
    AtPickup --> InProgress
    InProgress --> Completed
    InProgress --> Exception
    Exception --> Cancelled
    Exception --> Completed
    Exception --> InProgress
    Completed --> [*]
    Cancelled --> [*]
    Planned : Planned
    Assigned : Assigned
    Confirmed : Confirmed
    EnRoute : Driver en route
    AtPickup : At pickup
    InProgress : In progress
    Completed : Completed
    Cancelled : Cancelled
    Exception : Exception
    note right of Completed
        Locked for billing.
        No further edits.
    end note
Rendering diagram…
apps/trips/models.py — Trip.TRANSITIONS Generated from code

Passenger attendance

Drivers record boarding and drops as they go. A no-show raises an incident and feeds vendor and route metrics. Dispatchers can correct any of it, and the correction is attributed and reasoned.

flowchart LR
    S["Scheduled"] --> N["Notified"]
    N --> B["Boarded"]
    N --> X["No-show"]
    S --> B
    S --> X
    B --> D["Dropped"]
    X -.->|"Dispatcher correction<br/>with a reason"| S
    B -.->|"Dispatcher correction"| S
    D -.->|"Trip completion<br/>closes any open boarding"| D
    X --> INC["Incident raised<br/>Feeds vendor and<br/>route metrics"]

    classDef bad fill:#fdeceb,stroke:#f6c9c5,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    class X,INC bad
    class D good
Rendering diagram…
apps/trips/services.py — mark_passenger

Location handling

The rule that matters is at the bottom: a position older than the configured threshold is labelled last known, never live. Out-of-order pings cannot rewind the marker.

flowchart TD
    PING(["Location ping arrives<br/>driver app or GPS provider"]) --> VALID{"Coordinates<br/>valid?"}
    VALID -->|"No"| REJECT["Rejected"]
    VALID -->|"Yes"| STORE["Stored as a LocationPing"]
    STORE --> NEWER{"Newer than the trip's<br/>last known position?"}
    NEWER -->|"No"| KEEP["Marker unchanged<br/>out-of-order pings<br/>never rewind it"]
    NEWER -->|"Yes"| UPDATE["Update the trip's<br/>last known position<br/>and timestamp"]
    UPDATE --> AGE{"How old is it?<br/><i>stale threshold from policy</i>"}
    KEEP --> AGE
    AGE -->|"Within threshold"| LIVE["Shown as Live"]
    AGE -->|"Older"| STALEV["Shown as Last known<br/>with the timestamp<br/><i>BR-05</i>"]
    AGE -->|"Never received"| NONE["Shown as No GPS"]

    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    classDef warn fill:#fdf3e3,stroke:#f2d9a8,color:#101828
    class LIVE good
    class STALEV,NONE warn
Rendering diagram…
apps/trips/services.py — record_location, trip_location_state

Rules & exceptions

The decisions the product makes on its own, and where a human overrules it.

Route publication checks

Every gate a route passes before it can be published. Five blocks can be overridden by an authorised user with a written reason; a duplicate passenger cannot be overridden at all, because it would double-book a person.

flowchart TD
    PUB(["Publish requested"]) --> EMPTY{"Has<br/>passengers?"}
    EMPTY -->|"No"| BLOCK1["Blocked<br/>route is empty"]
    EMPTY -->|"Yes"| ALLOC{"Vendor, vehicle<br/>and driver set?"}
    ALLOC -->|"No"| BLOCK2["Blocked<br/>allocation incomplete"]
    ALLOC -->|"Yes"| CAP{"Seats within<br/>vehicle capacity?<br/><i>BR-01</i>"}
    CAP -->|"No"| BLOCK3["Blocked<br/>over capacity"]
    CAP -->|"Yes"| DOC{"Driver and vehicle<br/>documents valid?<br/><i>BR-03</i>"}
    DOC -->|"No"| BLOCK4["Blocked<br/>expired documents"]
    DOC -->|"Yes"| DUP{"Passenger on<br/>another route<br/>this shift?"}
    DUP -->|"Yes"| HARD["Blocked — cannot<br/>be overridden"]
    DUP -->|"No"| SAFE{"Protected passenger<br/>as last drop at night?<br/><i>BR-02</i>"}
    SAFE -->|"Yes"| BLOCK5["Blocked<br/>last-drop policy"]
    SAFE -->|"No"| OK["Published"]

    BLOCK1 --> OVR{"Authorised<br/>override with<br/>a reason?<br/><i>BR-09</i>"}
    BLOCK2 --> OVR
    BLOCK3 --> OVR
    BLOCK4 --> OVR
    BLOCK5 --> OVR
    OVR -->|"Yes, and the user holds<br/>override permission"| AUDIT["Published<br/>override and reason<br/>written to the audit log"]
    OVR -->|"No"| STOP["Not published"]

    classDef bad fill:#fdeceb,stroke:#f6c9c5,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    classDef warn fill:#fdf3e3,stroke:#f2d9a8,color:#101828
    class BLOCK1,BLOCK2,BLOCK3,BLOCK4,BLOCK5,HARD,STOP bad
    class OK good
    class AUDIT warn
Rendering diagram…
apps/planning/services.py — validate_route, publish_route

Transport request and cut-offs

Whether a request is approved automatically depends on one configured number. The same idea governs cancellation.

flowchart TD
    RAISE(["Employee raises a<br/>transport request"]) --> CHECK{"Is it before the<br/>request cut-off?<br/><i>BR-06</i>"}
    CHECK -->|"Yes"| APPROVED["Approved automatically"]
    CHECK -->|"No"| PENDING["Pending approval<br/>Transport desk decides"]
    PENDING --> DECIDE{"Desk decision"}
    DECIDE -->|"Approve"| APPROVED
    DECIDE -->|"Reject"| REJECTED["Rejected<br/>Employee notified"]
    APPROVED --> POOL["Enters the demand pool<br/>for that shift and date"]
    POOL --> ROSTERED["Rostered onto a route"]
    ROSTERED --> ASSIGNED["Assigned<br/>route published, trip created"]

    APPROVED -.->|"Employee cancels"| CANCELCHK{"Before the<br/>cancellation cut-off?"}
    CANCELCHK -->|"Yes"| CANCELLED["Cancelled<br/>Seat released"]
    CANCELCHK -->|"No"| LATE["Cancelled late<br/>Policy applies<br/>Transport team notified"]

    classDef warn fill:#fdf3e3,stroke:#f2d9a8,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    classDef bad fill:#fdeceb,stroke:#f6c9c5,color:#101828
    class PENDING,LATE warn
    class APPROVED,ASSIGNED good
    class REJECTED bad
Rendering diagram…
apps/planning/services.py — is_after_cutoff

Exception detection and dispatcher response

The system watches for delay, stale telemetry and no-shows, raises the exception itself, and puts it in front of the dispatcher. Closing it always needs a resolution.

flowchart TD
    RUN(["Trips running"]) --> SCAN["System scans active trips<br/><i>trips/services.detect_exceptions</i>"]
    SCAN --> LATE{"Past scheduled start<br/>by more than the<br/>late threshold?"}
    LATE -->|"Yes"| INC1["Raise 'Driver late'<br/>incident"]
    SCAN --> STALE{"Last GPS ping older<br/>than the staleness<br/>threshold?"}
    STALE -->|"Yes"| INC2["Raise 'GPS unavailable'<br/>incident<br/>Label position as last known"]
    SCAN --> NOSHOW{"Passenger not boarded<br/>after the wait time?"}
    NOSHOW -->|"Yes"| INC3["Driver marks no-show<br/>Incident raised"]

    INC1 --> QUEUE["Exception appears on the<br/>dispatch board and pins to<br/>the top of Live Operations"]
    INC2 --> QUEUE
    INC3 --> QUEUE
    QUEUE --> ACK["Dispatcher acknowledges"]
    ACK --> DECIDE{"What is needed?"}
    DECIDE -->|"Different cab or driver"| REASSIGN["Reassign with a reason<br/>Passengers notified<br/>Audit written"]
    DECIDE -->|"Cannot run"| CANCEL["Cancel trip<br/>Non-billable"]
    DECIDE -->|"Senior decision"| ESC["Escalate"]
    DECIDE -->|"Handled"| RESOLVE["Resolve with a resolution"]
    REASSIGN --> RESOLVE
    ESC --> RESOLVE
    CANCEL --> RESOLVE
    RESOLVE --> CLOSED(["Closed with an audit trail"])

    classDef warn fill:#fdf3e3,stroke:#f2d9a8,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    class INC1,INC2,INC3,QUEUE warn
    class RESOLVE,CLOSED good
Rendering diagram…
apps/trips/services.py — detect_exceptions

Safety

The path an emergency takes, end to end.

SOS escalation

Numbered so you can follow the order. Two details matter: the person is told what will happen before they confirm, and they see the acknowledgement when it arrives — so they know someone is responding.

sequenceDiagram
    autonumber
    actor E as Employee or Driver
    participant A as Mobile experience
    participant S as Safety service
    participant N as Notification service
    actor C as Control Room

    E->>A: Press Emergency
    A-->>E: Explain what will happen
    E->>A: Confirm
    A->>S: raise_sos(trip, location)
    S->>S: Create critical incident
    S->>S: Write trip event and audit record
    S->>N: notify_sos(incident)
    N->>C: Critical alert, bypasses quiet hours
    C->>S: Acknowledge
    S-->>E: Status: acknowledged
    C->>C: Call traveller, driver, emergency contacts
    C->>S: Add investigation notes
    C->>S: Resolve with a resolution
    S-->>E: Status: resolved
    Note over S: A resolution is required.<br/>Nothing closes silently.
Rendering diagram…
apps/safety/services.py — raise_sos

Commercial

From a completed trip to money leaving the building.

Billing and reconciliation cycle

The gate before approval is the point of the whole flow: while any line is still disputed, the invoice cannot be approved.

flowchart TD
    PERIOD(["Billing period ends"]) --> GEN["Finance generates the invoice<br/>for one vendor and period"]
    GEN --> ELIG["Select completed, billable trips<br/>not already invoiced<br/><i>BR-04</i>"]
    ELIG --> PRICE["Price each trip from the<br/>most specific active rate card"]
    PRICE --> DRAFT["Draft invoice"]
    DRAFT --> CLAIM["Vendor submits their claim<br/>per line"]
    CLAIM --> COMPARE{"Claim matches the<br/>system figure?"}
    COMPARE -->|"Yes"| CLEAN["Line agreed"]
    COMPARE -->|"No"| DISPUTE["Line disputed<br/>Goes to the reconciliation queue"]
    DISPUTE --> RESOLVE{"Which figure<br/>stands?"}
    RESOLVE -->|"System"| CLEAN
    RESOLVE -->|"Vendor"| ADJ["Adjustment recorded<br/>with a reason and an actor"]
    ADJ --> CLEAN
    CLEAN --> ALLCLEAR{"Any line still<br/>disputed?"}
    ALLCLEAR -->|"Yes"| DISPUTE
    ALLCLEAR -->|"No"| APPROVE["Approve"]
    APPROVE --> EXPORT["Export to finance or ERP"]
    EXPORT --> PAY["Record payment"]
    PAY --> PAID(["Paid"])

    classDef bad fill:#fdeceb,stroke:#f6c9c5,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    class DISPUTE bad
    class APPROVE,EXPORT,PAID good
Rendering diagram…
apps/billing/services.py

Notification dispatch

Critical safety messages bypass quiet hours; everything else respects them. An unconfigured channel is recorded as failed rather than quietly pretending to have sent.

flowchart TD
    EVENT(["Domain event<br/>cab assigned, en route,<br/>arrived, delayed, SOS"]) --> TPL{"Tenant template<br/>for this event<br/>and channel?"}
    TPL -->|"Yes"| RENDER["Render the tenant's copy"]
    TPL -->|"No"| DEFAULT["Render the built-in copy"]
    RENDER --> CRIT{"Marked critical?"}
    DEFAULT --> CRIT
    CRIT -->|"Yes"| SEND["Deliver now<br/>quiet hours bypassed"]
    CRIT -->|"No"| QUIET{"Inside quiet<br/>hours?"}
    QUIET -->|"Yes"| SUPPRESS["Suppressed<br/>recorded in the delivery log"]
    QUIET -->|"No"| SEND
    SEND --> ADAPTER{"Channel adapter<br/>configured?"}
    ADAPTER -->|"In-app"| STORED["Stored and shown<br/>in the recipient's inbox"]
    ADAPTER -->|"SMS, email, push,<br/>WhatsApp"| PROVIDER["Provider adapter"]
    ADAPTER -->|"Not configured"| FAILED["Recorded as failed<br/>never reported as sent"]
    PROVIDER --> SENTOK["Sent"]
    STORED --> SENTOK
    SENTOK --> LOG[("Delivery log")]
    FAILED --> LOG
    SUPPRESS --> LOG

    classDef bad fill:#fdeceb,stroke:#f6c9c5,color:#101828
    classDef good fill:#e8f6ee,stroke:#b6e2c9,color:#101828
    class FAILED bad
    class SENTOK,STORED good
Rendering diagram…
apps/notifications/services.py

Code structure

How a request travels through one Django app.

Request path inside a module

Every app follows this shape. Business rules live in services.py, read queries in selectors.py, and views only orchestrate — so a rule is written once and cannot be bypassed by reaching a different screen.

flowchart LR
    URL["urls.py<br/>routing"] --> VIEW["views.py<br/>orchestration only"]
    VIEW --> FORM["forms.py<br/>validation"]
    VIEW --> SVC["services.py<br/>business rules"]
    VIEW --> SEL["selectors.py<br/>read queries"]
    FORM --> SVC
    SVC --> MODEL["models.py<br/>data & constraints"]
    SEL --> MODEL
    SVC --> AUD["core/services.py<br/>record_audit"]
    SVC --> NOT["notifications<br/>services.py"]
    MODEL --> DB[("PostgreSQL")]
    VIEW --> TPL["templates<br/>presentation"]
Rendering diagram…
every apps/* module

Now see it running

Diagrams explain the shape. Signing in as a role and following its guided tour shows you the screens.