Multi-tenant ERP platform
One codebase serving many companies: sales, receivables, invoicing and reliable hand-off of orders to the warehouse system.
- 584
- source files
- 23
- DB migrations
- 4
- access levels
- 2
- tenant modes
Problem
Building a separate system per company is expensive and unmaintainable. But putting everyone in one shared database risks one company seeing another's data. On top of that, receivables lived in Excel — there was no single source of truth for who owed what and when an invoice was actually settled.
Solution
Tenant context travels inside the JWT, and a company can run in `shared` or `dedicated` mode without rewriting the domain layer. Access is two-dimensional: the role decides what you may do, the data scope decides what you may see — the same endpoint returns different rows to a branch manager and a field rep. Receivables are modelled properly: invoice lifecycle, allocation of payments across invoices, aging by due date, and a client statement.
Key capabilities
- Multi-tenancy: a company runs on a shared or a dedicated database, and the choice never reaches the domain code
- Four data-scope levels: all data / own branch / own department / own records only
- The complete sales-order lifecycle, from draft through to fulfilment
- Receivables: invoice draft → issued → paid, payment allocation, and an aging report bucketed by due date
- Client statements: chronological invoice and payment lines with allocation audit notes
- Outbox pattern: orders reach the warehouse system through a queue, with `FOR UPDATE SKIP LOCKED` keeping parallel workers apart
- Crash recovery: jobs stuck mid-flight are automatically returned to the queue after a timeout
- 23 Alembic migrations — the schema history is fully versioned and reversible
- A Next.js admin panel and a Flutter mobile app for field sales
Architecture
A request pulls its tenant and access boundary out of the JWT, and domain services run inside that context. Every outbound event is written to a queue table first, so an order survives a dropped network.
Clients
API & access control
ALL_DATA / OWN_BRANCH / OWN_DEPARTMENT / OWN_RECORDS
Domain modules
Integration
SKIP LOCKED, retries, stale-job recovery
Data
Stack
Backend
Web
Mobile
Infrastructure
My role
Backend architecture and the entire domain layer, the RBAC and data-scope model, the integration worker, the admin panel and the Flutter mobile app.
Client names, domains and server addresses are withheld for confidentiality.