Multi-outlet retail POS
A point-of-sale and inventory platform for retailers running several outlets under one set of books.
SEC.01 — PROBLEM
What is actually wrong
Retailers with more than one outlet end up running each till as its own island. Stock counts drift between locations, pricing changes have to be repeated by hand, and tax treatment is worked out per invoice rather than by the system. At the point where the business needs consolidated numbers — a single stock position, a single day’s takings, a filing that reconciles — the data has to be reassembled manually from several places.
SEC.02 — APPROACH
How we are solving it
One domain model covering outlets, terminals, stock movements and tax, with the integrity rules held in the database rather than in each client. Tills keep selling when the network drops and reconcile when it returns, because an offline sale is a queued event with an idempotency key rather than a write that simply failed. Tax is modelled as effective-dated rules applied to line items, so a rate change is a data change rather than a release.
- Multi-outlet stock with transfers, adjustments and a consolidated position
- Offline-capable terminals with idempotent reconciliation
- Effective-dated tax rules, including Sri Lankan VAT and SSCL treatment
- Per-outlet and consolidated reporting on a separate read path
- Role-based access down to individual till operations
- Auditable price and stock history
SEC.03 — ARCHITECTURE
The decisions that matter
This is the part worth judging us on.
NOTE.01
A 49-table relational domain model. Stock is an append-only ledger of movements rather than a mutable quantity column, so a position is always derivable and always explainable.
NOTE.02
Tax rules are effective-dated rows, not constants. A rate change or a new levy is inserted with a validity window; historical invoices continue to compute exactly as they did on the day they were issued.
NOTE.03
Offline sales carry a client-generated idempotency key. Replaying a queue after a network partition cannot double-post, which means a till can be brutal about retrying.
NOTE.04
Integrity lives in the schema: check constraints on monetary sign and quantity direction, partial unique indexes for one-active-price-per-product-per-outlet, and foreign keys that make an orphaned stock movement unrepresentable.
NOTE.05
Reporting reads from a separate projection rather than the transactional tables, so an end-of-month report cannot slow down a queue of customers at a till.
SEC.04 — STACK
What it is built with
- TypeScript
- Next.js
- PostgreSQL
- Prisma
- Cloudflare
Who it is for
Retailers running two or more outlets who need one stock position and one set of books, and whose tax treatment has to be right rather than approximately right.
Want something like this built?
We have made the expensive decisions on this problem already. Tell us about yours.