SaaS · Offline-First Healthcare
Pharmacy POS, stock, and finance that keeps trading when the internet drops.

~71k
Lines of TypeScript
~290
Source files
82
Commits, Mar → Jul 2026
6
Core modules
6
Permission levels
4
Stage import wizard
The brief
APHIA is a French-language, multi-tenant pharmacy management platform covering point of sale, inventory, and finance, built as an offline-first progressive web app. Roughly 71,000 lines of TypeScript across ~290 files and 82 commits. The defining constraint was not a feature list: pharmacies in the target market lose internet regularly, and a pharmacy that cannot sell is a pharmacy that is closed. Everything in the architecture follows from that.
Most retail software treats offline as an error state and shows a "you are offline" page. For a pharmacy that is unacceptable — checkout and stock reception are exactly the operations that cannot wait for a connection. The naive fix, replaying individual server actions from a queue, falls apart the moment a cart is edited several times offline: you end up reconciling a stream of conflicting mutations against server state you never saw. On top of that, the domain itself is unforgiving — mutual-insurance split billing, lot and expiry tracking, cash-register reconciliation, and six levels of role-based access over patient PII all have to stay correct across a sync boundary.
The approach
6 decisions that shaped the result — and why each one went the way it did.
Rather than replaying every server action, any cart mutated while offline is mirrored into IndexedDB and becomes the local source of truth until checkout. The completed checkout — one coherent transaction, not a stream of edits — is pushed through a durable sync queue and finalised server-side with last-write-wins. Conflict surface collapses from "every mutation" to "one settled sale".
A dedicated offline route hydrates the POS and stock-reception screens entirely from IndexedDB caches of products, stock levels, IPM insurance data, and register sessions. Staff get the real working interface during an outage, not a degraded placeholder — which is the difference between an offline mode that is a feature and one that is an apology.
Suppliers send delivery notes as Excel files where the entire product description is crammed into one unstructured cell. The importer is a four-stage wizard — upload, map, review, commit — built on a rule-ordered label parser with per-field confidence scoring, fuzzy column auto-mapping, two-tier validation that separates blocking errors from warnings, auto-creation of unknown products, and draft resume from a separate IndexedDB store.
Imports route through exactly the same domain code as manual reception. Stock history and the audit trail cannot diverge based on how the goods were entered — a property that is nearly impossible to retrofit and cheap to design in.
Pricing, discounts, payment summaries, inventory variance, and SPPS pricing live as pure modules with node:test coverage, deliberately separated from the React and database layers. The rules that decide what a customer owes are testable without a browser or a database.
A six-level role system governs invitations, audit trails, login-attempt throttling, alert visibility, and access to patient PII — enforced at the data layer rather than by hiding navigation items.
Inside the product
Full checkout flow with IPM mutual-insurance split billing, cart discount rules, price overrides, barcode scanning, and receipt printing.
Delivery/BL reception, lot and expiry tracking, shelf management, a stock-movement audit log, and physical inventory sessions with variance reports.
Cash-register sessions (open, close, reconcile), receivables, expenses, invoicing, and analytics dashboards.
A rule engine for low stock and expiring lots, with a cron runner and RBAC-scoped visibility.
Six-level role-based permissions, invitations, audit trail, and login-attempt throttling.
Patient records with validation and RBAC-gated access to personally identifiable information.
What it does
What it runs on
The result
Point of sale and stock reception stay fully operational through internet outages — the shop keeps trading.
Conflict resolution reduced to one settled transaction per sale instead of a stream of replayed mutations.
Unstructured supplier spreadsheets become validated stock entries without manual re-keying.
Imported and manually received stock share one audit history, so records cannot diverge.
Pricing and inventory rules are unit-tested independently of the UI and database.
See it live
Let’s Work Together
Thirty minutes on what you need and what it would take. You leave with a clear recommendation either way.
Usually replies within a day · No obligation · Fixed-price quotes