Brian Omariba
Start a project →
← All systems
Dossier /05 Pharmacy retail & dispensing Live

OmniPharmacy OS

A till that refuses the sale is doing its job.

Retail software for a regulated counter: the same screen that takes the money also decides whether the medicine is allowed to leave the shelf, and records who decided.

Demo PINs  740126 Owner  ·  583641 Pharmacist  ·  618352 Manager  ·  294718 Cashier
Sample data only. Pick a branch when prompted — the owner works across both.

Category
Regulated retail & dispensing
Role
Domain modelling · Engineering · Deployment
Stack
Django 5 · SQLite · PWA · M-Pesa Daraja
Status
Live demo
01 — Problem

A pharmacy is a shop with a licence attached.

Most pharmacy software is a supermarket till with drug names typed into it. It will happily sell a prescription-only medicine to anyone, in any quantity, dispensed by whoever happens to be standing at the counter.

The controls that matter are not commercial, they are legal: who may hand over a POM, what goes in the controlled drugs register, which batch was sold and when it expires, and whether the two medicines in the same basket should be taken together.

If those rules live only in the pharmacist’s head, they stop existing the moment the pharmacist steps out.

POM sold unsupervised
Controlled register kept in a book
Expired batches on shelf
Interactions unchecked
No audit trail
Stock counted on paper
02 — System

The rules are in the transaction, not in the training manual.

Every sale passes through the same gate. The product carries its own classification and the person at the till carries their own role and registration number, so the system can answer “is this allowed?” before it answers “how much?”

01Sign in

A PIN, not a password — a pharmacist signs in dozens of times a shift, and a shared account destroys the audit trail.

02Branch

The terminal states which premises it is in. Stock, tills and the controlled register are all branch-scoped.

03Scan

The product resolves to a batch with an expiry date, not just a name and a price.

04Check

Classification, prescription requirement and ingredient interactions are evaluated against the basket.

05Authorise

A POM needs a licensed dispenser. A controlled drug needs a pharmacist. The system will not be talked round.

06Record

Who dispensed, to whom, from which batch — written at the moment it happens.

07Settle

Cash or M-Pesa, then a till session that has to reconcile at the end of the shift.

03 — Architecture

Decisions worth defending.

PIN authentication with an escalating lockout. A short PIN is weak against a script, so attempts are throttled per terminal and each further block of failures costs more time. The threat model is a busy counter, not a login page.

Permissions are a matrix, not a flag. Six roles map to explicit capabilities — dispensing a controlled drug, approving a stock adjustment, editing a price, viewing financial reports. Nobody is simply an “admin”.

Policy is typed, per business. Whether a POM requires a recorded prescription is a field an auditor can query, not a setting buried in code. The defaults are the safe posture, and loosening one is itself an audited change.

Stock is held as lots. A batch number and an expiry date, because a recall is a question about a batch and not about a product.

04 — Interface

Built for someone standing up.

Four surfaces on one data model: the till, the dispensary, the stockroom, and a command view for the owner. Each shows only what its operator can act on.

OmniPharmacy OS interface

The command view after signing in as owner — branch, staff member and the day’s operational state.

05 — Outcome

What the system changes.

The register stops being a book that someone remembers to fill in. Expiry and interaction checks happen on every basket rather than on the ones where somebody is paying attention, and the question “who dispensed this?” has an answer that does not depend on anyone’s recall.

This instance is a live demo seeded with sample data — a real pharmacy model, real regulations, invented patients.