The interview that never happened
I went to SMINAC Pharmacy in Accra with a page of discovery questions. The owner did not want to answer them — "when the drugs come we just type it in." So I photographed his counter instead. Four photos. Every requirement below came off them.
They run LAVABMS in a Chrome app window. It is a website — no internet, no selling, and their data sits on somebody else's server. So the job was a till that keeps working when the link drops.
Expiry dates that say 2050
Some expiry dates on their screen read 01-Jan-2050 or 30-Apr-2039 — on the cannulas, the plasters and the multivitamins. The field is compulsory, so staff typed something to get past it. Those invented dates show up in the same green as stock that is genuinely fine.
One product, three ways to sell it
Their product list has a Form column — Unit, Strip, Tab. Part-pack selling is normal here: somebody buys two tablets, not a box of two hundred. A normal till gives a product one price and one count, so a shop like this ends up with three products whose stock numbers drift apart. Here box, strip and tablet are one product sharing one pool of stock.
Search that survives their own catalogue
Real entries from their shelf list: PLASTES STRIPES,
PENICILIN V TAB. 125MG 100'S, MULTIVITAMINE TABLETS. The
typos are in the data, not just in what the cashier types, so search forgives both.
A cashier who cannot find a product does not sell it.
Barcodes are the exception. A scan matches exactly or not at all, because a scanner makes no typos and a near miss is the wrong box in a customer's hand.
Offline by construction
The whole shop is one SQLite file on the counter PC. Stock is never stored as a single number. Every sale, delivery, return and write-off is its own row, and the count is those rows added up. A mistake is corrected by adding an opposite row, so nothing is ever quietly edited or deleted.
the counter the internet
│ ✕
▼ not needed
Sell · Receive · Count to sell
│
▼
append a stock_movement row (never edited)
receipt +200 · sale −10 · write-off −6
│
▼
pharmacy.sqlite — one file, atomic save
│
├─▶ stock = SUM(movements), not a column
└─▶ backups\ daily, fourteen kept
- Parked baskets survive a power cut — they are saved to the file.
- Shifts open with a float and close with a cash count.
- Sales take the earliest-expiring batch first, and expired stock leaves by a write-off, never a sale.
It is not only for pharmacies
A pharmacy was the hardest first customer I could pick, which is why I picked it. Nothing in the build is medicine-specific — the same three problems turn up in most shops here.
- Supermarkets and provision shops — expiry matters as much for milk, bread and yoghurt as it does for antibiotics, and a crate, a pack and a single bottle are the same part-pack problem as a box, a strip and a tablet.
- Cosmetics and beauty supply — batch dates, testers, and a catalogue people spell six different ways.
- Hardware, stationery, phone accessories — no expiry, but the same barcode scanning, the same shifts and cash counts, and the same need to keep selling when the network is down.
- Anywhere with a counter and bad internet — the wedge is not the industry, it is that the till belongs on the shop's own machine.
Setting up a different kind of shop is a catalogue import and turning expiry tracking off if it is not wanted. It is the same software.
What came back from the counter
It went to the shop on my own laptop and was used by one person who sells there. Four faults came back, all four real, all four fixed.
- The quantity boxes could not be typed into. Clearing one put a 1 back under the cursor, so typing 12 gave you 21.
- No barcode scanner support. A scanner plugs in as a second keyboard, so the app now tells a scan from a person typing.
- No reprinting. Customers come back an hour later wanting the paper. Any past sale can now be reprinted.
- Receiving sent you away to add a product. You can now add one without leaving the invoice.
What it is not
The real gap is a screen the owner can check from outside the shop. That one is not built yet.