Brian Omariba
Start a project →
← All systems
Dossier /06 Offline-first point of sale Live

OmniPOS

The connection drops. The shop does not close.

A till built for the actual conditions of a Kenyan duka: intermittent connectivity, mobile money, and a tax authority that wants an invoice for every sale.

Demo PINs  4713 Owner  ·  2586 Cashier
Tap the digits on the keypad — it signs in on the fourth.

Category
Point of sale · Retail & restaurant
Role
System design · Engineering · Deployment
Stack
Django 5 · Service worker PWA · SQLite · eTIMS · Daraja
Status
Live demo
01 — Problem

Cloud software assumes a connection that Kenya does not guarantee.

A hosted till is elegant until the line goes down. Then the queue stops, the shop starts writing on paper, and the day ends with someone typing yesterday into today’s system and hoping the totals agree.

The outage is not the exception to design around later. On most days in most shops it is a normal part of trading.

A till that needs the internet to take money is not a till. It is a website that sometimes sells things.

Sales stopped by outages
Paper fallback
Re-keyed at end of day
Totals that disagree
Manual eTIMS filing
Unverified M-Pesa payments
02 — System

Local first, sync second.

The register is a progressive web app with its own local database. Selling, pricing and receipting all happen on the device. The network is used to catch up, never to complete a sale.

01Sell

The basket is priced, VAT applied and the sale committed against local storage.

02Queue

With no connection the sale is durable locally, not held in a browser tab.

03Reconnect

The service worker drains the queue in order when the line returns.

04Reconcile

Server-side totals are checked against the device, and disagreements surface rather than silently resolve.

05Invoice

eTIMS invoicing for KRA compliance, driven by the business’s own PIN.

06Collect

M-Pesa C2B, so payment confirmation is a callback rather than a cashier reading a text message.

03 — Architecture

Decisions worth defending.

A four-digit PIN and a keypad, not a password field. The operator is standing, often with one hand full. The interface is a numeric pad, sized for a thumb.

Four digits is 10,000 guesses, so the lockout is the real lock. Failed sign-ins are counted per terminal — or per client address before a terminal is paired, never per employee, or anyone could lock a cashier out of their own till. Past five misses each further attempt costs twice the wait, to a ceiling of fifteen minutes.

A terminal is paired to a branch, not told which one it is. A manager generates a six-digit code — it gets read out across a counter, so digits win — and the till trades it once, within fifteen minutes, for a permanent token. From then on the server resolves the branch before a PIN is checked, which is what lets a PIN be unique within a branch instead of across every business on the server.

One model, two verticals, many branches. The same catalogue and sale pipeline runs a retail counter or a restaurant floor; a location declares its mode, and the floorplan, tables and kitchen routing appear only where they apply. Every transactional row hangs off a branch, so stock, receipt numbering and reporting are branch-scoped by construction.

Stock movements are an append-only log. Quantity on hand is derived from movements rather than stored and adjusted, so a stocktake becomes a reconciliation with an explanation instead of an overwrite.

eTIMS is treated as part of the sale. Tax compliance that runs as a separate monthly ritual is compliance that fails in the month somebody is on leave.

04 — Interface

The till, after sign-in.

Category rail, current sale, and a payment button that always shows the number the customer is about to be asked for.

OmniPOS interface

The register signed in as owner — sync state is shown in the header, because the operator needs to know whether they are online before they need to know it.

05 — Outcome

What the system changes.

The shop keeps trading through an outage and the day reconciles itself instead of being re-keyed. Tax invoices are produced by the act of selling rather than by a separate month-end exercise.

The owner stops reconstructing the month from receipts. A daily view for the shift, a monthly one that compares itself against the month before, and both exportable as CSV for whoever does the books — split by branch, and by how the money actually arrived.

This instance is a live demo seeded with a sample catalogue.