A deep case study of the offline-first Business Management Platform Desishub built for a multi-branch Kampala phone retailer — Sales, Purchases, Inventory, IMEI Tracking, Finance and Reports in a single Grit Framework desktop binary.
A few months ago, a multi-branch phone retailer in Kampala came to Desishub with a familiar problem: three shops, five tills, a spreadsheet for inventory, and a creeping suspicion that handsets were going missing. Their cloud POS dropped offline every time fibre wobbled, customers walked out mid-sale, and reconciliation between branches was a Monday-morning nightmare.
We built them a desktop Business Management Platform on the Grit Framework. It runs offline, syncs in the background, tracks every IMEI to its handset, and in the first week it caught 28 missing phones the spreadsheet had never seen.
This post walks through the build — the architecture, the modules, the offline sync, the IMEI module, and the production patterns we now reuse across every Desishub desktop project.
A consumer electronics retailer, three branches in Kampala, around 40 staff across sales, stock and accounts. Stock: smartphones (Samsung, Tecno, iPhone), accessories, airtime.
The presenting symptoms:
Underneath all of that, the real problem: their software model didn't match the connectivity reality of running a shop in Kampala.
We considered three options and ruled out two quickly:
The desktop binary is built with Grit Framework's desktop architecture — Wails + Go + React. The full app installs in a 20 MB binary, runs on every cashier's Windows PC, and reads/writes to a local SQLite database that syncs to a central Postgres on a Hetzner server whenever the network is available.

┌────────────────────────────────────────────────────────────┐
│ Cashier PC (Branch A) Cashier PC (Branch B) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Grit Desktop App │ │ Grit Desktop App │ │
│ │ React UI │ │ React UI │ │
│ │ ──────────── │ │ ──────────── │ │
│ │ Go business logic │ │ Go business logic │ │
│ │ Local SQLite │ │ Local SQLite │ │
│ │ Sync worker ⇅ │ │ Sync worker ⇅ │ │
│ └──────────┬───────────┘ └──────────┬───────────┘ │
└─────────────┼─────────────────────────────┼────────────────┘
│ │
│ HTTPS (when online) │
▼ ▼
┌──────────────────────────────────────────┐
│ Central Sync API (Go, Grit web stack) │
│ Postgres (transactions, IMEIs, audit) │
│ Object storage (receipts, images) │
│ Pulse observability + Sentinel limiter │
└──────────────────────────────────────────┘
Every desktop client is the same binary. The only difference between Branch A and Branch B is the branch ID stored in a local config file at install time, and the role assigned to the logged-in user.
The platform shipped with six modules in the first release:
| Module | What it does | Offline? |
|---|---|---|
| Dashboard | Live KPIs — sales today, top SKUs, low stock, missing IMEIs | ✅ (reads local DB) |
| Sales | Walk-in POS, credit sales, refunds, receipt printing | ✅ Full transactions |
| Purchases | Goods received notes, supplier invoices, landed cost | ✅ |
| Inventory | Stock per branch, transfers, adjustments, reorder alerts | ✅ |
| IMEI Tracking | Every handset's IMEI registered against the stock unit | ✅ |
| Setup | Products, categories, branches, tax rates, payment methods | ✅ |
| Finance | Customer ledger, supplier ledger, daily P&L | ✅ |
| Administration | Users, roles, audit logs | ✅ (writes sync on reconnect) |
| Reports | Sales, stock, IMEI gap, finance, branch comparison | ✅ |
The sidebar lives in the desktop shell so navigation is instant — no page loads, no spinners. Every list view paginates against the local SQLite.
This is the module that paid for the project in the first month.
![]()
The problem. When a phone retailer receives a shipment of, say, 8 × Samsung Galaxy S24 256GB, the stock-count says 8. But each of those eight handsets has a unique IMEI printed on its box. Without recording the IMEI against the stock unit, a dishonest staff member can swap a real handset for an empty box, sell the real one off the books, and the spreadsheet still says 8.
The fix. The IMEI Tracking page shows, for every tracked product, the expected stock count and the number of IMEIs registered against it. The gap is rendered in red.
| Product | Category | SKU | Stock | IMEIs in Stock | Sold | Missing |
|---|---|---|---|---|---|---|
| Samsung Galaxy S24 256GB | Phones | SAM-S24-256 | 8 | 0 | 0 | 8 |
| Tecno Camon 30 | Phones | TEC-C30-128 | 15 | 0 | 0 | 15 |
| iPhone 16 Pro 256GB | Phones | APL-IP16P-256 | 5 | 0 | 0 | 5 |
A KPI strip at the top makes the same point loud: Tracked products 3 • IMEIs in Stock 0 • IMEIs Sold 0 • Missing 28.
Workflow. When a shipment arrives, the stock controller hits Upload IMEIs on each product card. They paste in the IMEIs (one per line) or scan them with a USB barcode reader. The desktop app validates the format (Luhn-checksum for 15-digit IMEIs), stores them locally, and syncs to the central server. At the till, scanning a handset's IMEI checks out one unit and updates Sold automatically.
Compliance bonus. UCC (Uganda Communications Commission) and customs queries about specific handsets now take seconds instead of hours — the IMEI ledger is queryable per branch and per timeframe.
The hardest part of offline-first is conflict resolution. Two cashiers might sell the same SKU in different branches while the network is down. When fibre returns, we can't blindly add both transactions — we need to keep the data honest.
The pattern Grit Desktop apps use:
outbox table first. Each row has an idempotency key, a vector clock and a timestamp.This is the only way to run multi-branch retail in Uganda honestly. Anything else is "last write wins" and quietly loses you stock.
In the top-right of every screen, the workspace shows a small green dot with the word Synced. When the sync worker is mid-cycle it switches to a spinner; when the connection drops it switches to an amber Working offline — 3 pending. Staff trust that indicator more than they trust the network bars in the system tray, because it tells them whether the business data is safe.
The pattern is in every Desishub desktop app now, including the Business Management Platform and HMK Estates. Borrow it — your users will thank you.
The branch picker (in the screenshot it's set to Head Office) scopes every list view, every report and every sale to the chosen branch. Users with the Administrator role can switch branches; cashiers are locked to theirs.
Stock transfers between branches are a first-class workflow:
Auditable, offline-friendly, and reconcilable per day.
The same desktop architecture works for:
The Grit Framework scaffold means we can typically have a domain-tailored version of this platform in a client's hands inside 8–12 weeks, including hardware integration and staff training.
1. Will the platform work in a power cut? Yes — every cashier PC should have a UPS, but the moment power returns the app picks up exactly where it left off. No data loss.
2. Can my accountant access the data without installing the desktop app? Yes. The central server exposes a web admin (also built on Grit) where Finance can run reports, view ledgers and export to Excel/PDF without touching the desktop app.
3. How is the data backed up? Three layers: (1) local SQLite file is auto-snapshotted hourly on each PC, (2) the central Postgres has nightly backups to encrypted object storage, (3) we keep 30 days of point-in-time recovery on the central database.
4. Can I plug in a thermal receipt printer? Yes — 80mm thermal printers (Epson TM-T20, Xprinter XP-58) print receipts natively via the Go backend. Cash drawers triggered from the printer are supported too.
5. Does it support mobile money? Yes — MTN MoMo and Airtel Money are both wired in as payment methods at the till. Manual confirmation, or auto-confirmation via the operator's collections API depending on your account.
6. Can I limit cashiers to seeing only their own sales? Yes — RBAC is enforced per resource. Cashiers see their own till; supervisors see the branch; administrators see the company.
7. How much does a build like this cost in UGX? For a 3-branch, 6-module deployment with IMEI tracking, expect UGX 35M – UGX 60M depending on hardware integration scope. Read the pricing breakdown in Best Desktop App Development Company in Uganda 2026.
If you run a shop, clinic, school or estate that needs to be honest about its inventory and reliable when fibre is down, book a discovery call with Desishub. We'll quote you in UGX and have a working demo of the platform — wired to your real product catalogue — in your hands within three weeks.