TrayLedger is a two-application web system backed by PostgreSQL. The current Docker Compose route is a loopback-bound development topology.
flowchart LR
U["Browser user"] -->|"HTTP on localhost:5173"| F["React and Vite frontend"]
F -->|"Session cookie, CSRF token, JSON and CSV"| A["Django REST API"]
A --> P["Permission and tenant-scope policy"]
P --> S["Domain services"]
S --> D[("PostgreSQL")]
S --> B["Encrypted or local backup archives"]
S --> R["Deterministic non-ML analytics"]
A --> E["Application audit events"]
E --> D
B --> X["Operator-controlled storage"]
frontend/ contains a React 19 single-page application built with TypeScript,
Vite, React Router and TanStack Query. It provides protected routes, role-aware
navigation, query caching, forms and browser print styling. Vite proxies /api
to Django in local development.
The frontend hides unavailable actions for usability, but does not define the security boundary. Protected domain views authenticate requests and apply role permissions plus tenant scope wherever tenant records are involved; self-service authentication routes apply the narrower checks appropriate to their purpose.
backend/ is a Django 5.2 application using Django REST Framework. Domain apps
separate accounts, tenancy, catalogue, inventory, patients, Dosette/MDS,
analytics, reports, notifications, reviews, audit and backups.
Service functions contain stock mutations and derived calculations. Database transactions protect multi-record operations such as transfers and stock deduction. Application models retain movement, timestamp and actor information where implemented.
Browser authentication uses a server-side Django session. Mutating requests also require a CSRF token. Each user has at most one active application membership:
- Admin: global scope.
- Superintendent and stock employee: group scope, with stock-location rules.
- Pharmacist and dispenser: one pharmacy scope.
The backend combines the active membership, required action and target object scope. See SECURITY_AND_PRIVACY.md.
sequenceDiagram
participant B as Browser
participant API as Django API
participant Policy as Scope policy
participant Service as Domain service
participant DB as PostgreSQL
B->>API: Request with session and CSRF where required
API->>Policy: Check action and target scope
Policy-->>API: Allow or reject
API->>Service: Validated input and actor
Service->>DB: Transactional read or write
DB-->>Service: Scoped records
Service-->>API: Result or deterministic explanation
API-->>B: JSON or CSV response
Analytics read inventory, movement and eligible Dosette records, apply fixed thresholds and arithmetic, and return explanations. They do not call an ML model or external decision service.
- PostgreSQL is the authoritative application datastore.
- Selected patient values and append-only note bodies are encrypted by the application before storage.
- The last-name search index is a keyed HMAC, not plaintext.
- Backup archives contain a wider serialised dataset; production settings require AES-256-GCM archive encryption.
- CSV exports and browser print output leave server-side access controls once saved or printed.
The committed Dockerfiles run Django's and Vite's development servers and are labelled as development images. A production design must add a production WSGI or ASGI server, built static frontend delivery, TLS termination, durable secrets and storage, monitoring, backups and independent review. See DEPLOYMENT.md.