The platform · MSFA
The complete solution.
MSFA covers the whole distribution cycle on one data model — from the salesman opening his day on a van in Doha, to the claim raised against a supplier six weeks later. Seven families, roughly 250 database objects, and one rule underneath all of it: the field must keep working when the network doesn't.
01 · Field apps
One APK.
Five personas.
In production
A single signed Android build serves the van salesman, the pre-seller, the delivery driver, the merchandiser and the auditor. The navigator branches on role, and every drawer item, action and dashboard widget is switched on or off from the portal — about 41 feature flags, no rebuild, no re-login.
VanSales
Sell from the van. Load, price, invoice, collect, return, audit, unload, close the day.
PreSales
Book orders for later delivery. No stock movement, no invoice — those are minted downstream.
Delivery
Execute the plan. Mints the full financial footprint on the device, offline.
Driver
Dispatch and deliver against a van warehouse, with day gates and a GPS stop check.
Merchandiser
Audit-only build. Perfect Store, planogram, POSM, competitor — no order screens at all.
QAR 2,640
The atomic sale
One tap moves four books at once.
Submitting a sale is a single local transaction — stock, ledger, receivable and store ledger all move together, on the device, with no signal.
- All-or-nothing. Every book commits together or the sale doesn't happen — no half-written invoice.
- Re-entry guarded. A double-tap or a lost network reply updates the same row; it never creates a twin.
- A day built from clones. Each trip is its own beat, stitched to one parent — three trips, one day's trading.
Order to invoice
Only priced SKUs appear; tabs come from your SKU class groups; a custom numeric keypad replaces the keyboard.
Priced-only · fast entryReturns, with reference
Locked to the original invoice and capped in base units, so the cap holds across singles, packs or cases. Auto credit note.
Base-unit cappedCollections
Oldest-first or hand-picked, split across cash, card, cheque, online and credit notes — each leg its own TransNo.
Multi-mode · per-legStock audit
A separate auditor signs in on the salesman's own phone; salable, damaged and expired counted with live variance. Shortage auto-bills.
Two-party · auto shortageEnd-of-trip gates
The day won't close over approved-but-uncollected loads, pending unloads, unsynced rows or a queue holding failures.
4 hard gates · queue-awarePrinting is submitting
Five EOD reports print to Bluetooth or save as PDF — a successful print is the submission, stamped and synced.
Offline PDF · no server call02 · Merchandising & on-device AI
The shelf,
priced in money.
In production
Most merchandising tools report a store average and leave you to guess what it's worth. Ours weights every rule by the SKU's real revenue run-rate and answers in currency per week at stake — then hands the rep a one-tap order to fix it.
Money-first scoring
A shelf gap is not a percentage.
The same out-of-stock is worth QAR 951 a week on a fast SKU and QAR 0 on a dead one — and we say so, instead of averaging them into a number that sends the rep to the wrong shelf.
- Weighted by real revenue. Every rule scores against that SKU's run-rate, per store, in currency.
- Resolved by precedence. Store beats route beats org beats all — the device picks the right scorecard offline.
- Worst-first, with a button. Findings sort by money and open a one-tap order to fix them.
Perfect Store
Build scorecards in the portal — sections, rules, weights — target them anywhere; the device resolves and scores offline.
Configured, not codedShelfLens
Photograph the shelf; the phone detects each product, embeds the crop, matches your catalogue and fills the scorecard.
~150 ms · zero costThe Fix-It loop
A finding is a button, not a note. Out-of-stock offers “+ Order”, seeds the rep's screen and verifies the fix next visit.
See → fix → verifyPlanogram compare
The store's planogram resolves automatically; the compare view scores by how much of the expected facing count is really there.
Facing-weightedPOSM & competitor
POSM graded against a reference photo; competitor capture on a normalised spine, so share-of-shelf never fractures on spelling.
Reference-image · normalisedShelf Command
The supervisor's view: money in play, worst-first leaderboard, biggest leaks — from the latest submitted visit per store.
Worst-first · money-ranked03 · Intelligence
The app thinks —
and shows its working.
In production
Every module here is deterministic. There is no language model in the loop, nothing is metered per token, and every figure drills back to the records that produced it. It is explainable in a board meeting and defensible in an audit.
Route optimiser
The drive, 40% shorter.
Nearest-neighbour plus 2-opt for an instant answer, or real road-network routing over OpenStreetMap for the true drive. Verified on a live route: 121 km down to 73.
- Measured, not modelled. 121 → 73 km on a real Doha route — fuel and hours you can bank.
- Degrades gracefully. No map tiles? It falls back to haversine + 2-opt and still re-orders the stops.
- You opt in. It proposes the shorter order and shows the km and minutes saved — never a silent reorder.
Target Engine
Targets that own up to themselves.
Set targets by revenue, volume, collection, visits, productive calls or new stores — then a coach asks whether the number is genuine, or propped by one lumpy deal.
- Six target types. Revenue, volume, collection, visits, productive calls, new stores — mix them per role.
- A genuineness score. Flags achievement concentrated in one account, one deal, or a lumpy last week.
- Since you last checked. The coach speaks in deltas, not a static month-to-date wall.
Dying-store early warning
It sees the store go quiet first.
Four leading micro-signals — basket narrowing, drift to cheaper SKUs, stretching order gaps, the anchor SKU dropped. Two or more, and the store is flagged roughly three weeks before it churns.
- Leading, not lagging. It reads the slope, not last month's total — the warning arrives while you can still act.
- Two strikes flag it. A single soft month is noise; two signals together is a pattern.
- Back-testable. Replay it against history and see the stores it would have caught.
Sales Co-pilot
The leave-money guard: at check-out, regulars sitting in van stock but not on the invoice trigger a one-tap add.
Offline · zero API costCommand Center
The whole fleet as money and named actions. A hard bar: no leaderboards, no MTD tiles, no coverage % — only insight from a slope or a behaviour.
Opportunity → actionBasket affinity
Association-rule mining: stores buying A whose lookalikes overwhelmingly also buy B — but don't. Validated at 88% confidence.
Cross-sell · evidence-backedSmart Load Advisor
Forecasts what the route actually buys, then flags what's missing, short, dead weight, or breaking a promo. Scores readiness 0–100.
Forecast · promo-awareBeat Doctor
“Why is there no beat for this route today?” answered in plain language by a rules engine mirroring the scheduler, with a one-click fix.
Diagnostic · one-click
04 · Analytics
Your analysts stop
queueing behind us.
In production
Xpress BI is a self-service query engine over the sales spine — a layered pipeline that compiles your choices into safe, parameterised SQL. If a report you need doesn't exist, you build it in an afternoon, not a sprint.
Xpress BI
You build the report, not a ticket.
A layered pipeline turns your picks into safe, parameterised SQL — and renders it as any of fifteen views, four of them true WebGL 3D. The engine behind the built-in reports is the same one you drive, so nothing is a hard-coded dead end.
- Ten window primitives. Rank, running total, percent-of-total, moving average, lag, lead, growth % — first-class.
- Pivots discovered at runtime. One column summed under different conditions becomes side-by-side columns.
- True 3D, on demand. Bar, surface, scatter and pie in WebGL — a second engine, loaded only when a 3D view is picked, driven by the same report config.
- Dashboards from reports. A saved grid whose cells are existing reports — parallel, per-cell error isolation.
Every level sees its own total
“Group by supervisor” resolves up the real reporting chain through a precomputed ancestry, refreshed the moment a user is saved.
Recursive · event-drivenPivots & filtered measures
Crosstab with values found at runtime; conditional aggregation splits sale cash, sale credit, return cash, return credit into columns.
Crosstab · conditionalXpress Dashboards
A saved grid whose cells are existing reports — no second query engine to keep honest. One bad panel never kills the board.
Parallel · isolatedExecutive dashboard
Four tabs — VanSales, PreSales, distribution, merchandising. One light call per tab, code-split so first paint loads only what you're viewing.
1 round-trip/tab · cached
05 · Distribution & supply
Goods down.
Money back.
In production
The DMS half mirrors the sales half exactly: sales pair with sales returns, GRN pairs with purchase returns, and claims sit beside them as the money-recovery layer for what you can't — or needn't — physically send back.
Purchase order
The BU raises a costed PO. Prices, discounts and tax are resolved server-side — never sent by the client.
Dispatch
The company ships against it — one PO can become many shipments — debiting the plant warehouse.
Receive
The BU receives. Every shortfall must be itemised by reason before submit will unlock.
Return
Stock goes back up into reason-keyed bins — damage, expiry, quarantine — with a credit note.
Claim
What can't be returned is recovered as money, against the right party, on an SLA clock.
Three-tier purchase pricing
The right price finds the buyer.
A price set for that buyer wins; failing that, one for their location or the region above; failing that, a supplier default that reaches everyone below — and units convert on the way.
- Org → location → default. The most specific price wins, resolved server-side, never sent by the client.
- Units convert in the resolve. Price a single, order a carton — the line total still comes out right.
- Date-windowed. Multiple price rows per SKU across windows, so a promo price and a base price coexist.
Shortfall must balance
Five short means five accounted for — broken or missing. A live guard blocks submit until it reconciles; evidence photos attach.
Reason-itemisedClaims as data
Nine scenarios — shortage, transit damage, quality, expiry, price protection, scheme, market return, display, other — are rows in a master. A tenth is a row, not a release.
One engine · zero codeThe stock invariant
The server writes only the ledger, never the balance. Devices apply deltas locally and record they did — nothing is ever counted twice.
Append-only · idempotentReason-keyed bins
Returns ride the existing stock types, so damaged goods land in a real quarantine bin — each reason its own bin and disposition.
No schema changePartial delivery, fairly
A deal's discount and free goods freeze at approval and realise pro-rata on what arrives. The balance lapses — no claw-back, no surprise invoice.
Pro-rata · frozen06 · Financial
One source of truth
for the money.
In production
Each table owns exactly one thing. Receivables own what's owed against an invoice. Credit notes are the customer's wallet, independent of them. The store ledger is an append-only record of every event, where the sign lives in a direction column rather than in a negative number.
| Table | Owns | Balance |
|---|---|---|
| Accounts receivable | Money owed against a specific invoice. Invoice rows only — nothing else is allowed in. | Total − Paid − Credit applied |
| Credit note | The customer's credit wallet, created automatically from a sales return. | Total − Used |
| Store ledger | Append-only, chronological audit of every financial event per store. | Running balance after each row |
| Collection | Payment received: header, per-mode breakup, and per-invoice allocation. | Allocated across invoices |
| Shortage invoice | The salesman's liability for van stock that an audit couldn't find. | Billed to the salesman |
Loyalty
Points that settle themselves.
The phone computes points instantly so the customer sees them, marks them local, and never uploads them. A nightly worker is the only authority that posts — and the local twin is superseded at read time, so nothing double-counts.
- A signed ledger. The balance is the sum of the lots, nothing else; redemption eats oldest-expiry-first with an audit.
- Provisional, offline. Instant points on the device that never upload — the authoritative row wins on sync.
- Redeem as a free line. Points become an FOC SKU inside the same order — no vouchers, no codes.
Two-phase pricing
Price lists resolve into flat per-org and per-store tables, so the device never runs a resolution engine at cart time.
Resolved · date-windowedPDF suite
Invoices, picklists, dispatch and goods-return notes, claim notes and loyalty receipts — branded by walking the org chain.
Barcoded · parent-fallbackNet outstanding, one way
Open receivables minus open credit — or the last balance on the store ledger. Both agree, always; no matching negative receivable to drift.
No double-countA design we removed on purpose. An earlier model wrote a matching negative receivable for every credit note. It double-counted, and it broke the moment a collection was partial. Net outstanding is now simply open receivables minus open credit — both agree, always.
07 · Platform
The part nobody demos.
In productionSync, promotions, journeys, tenancy, voice, surveys, approvals, theming. None of it wins a demo and all of it decides whether the thing survives contact with 500 vans on bad networks.
Promotion engine
Promotions that stack without leaking.
A real engine on the device, not a flattened lookup. Percent, amount, cashback, buy-X-get-Y, slabs by quantity or value, and invoice-level deals — stacking by exclusion group, clamped so overlaps can't go negative.
- Every promo type. Line and invoice, percent and amount, cashback, slabs, free goods — one engine, offline.
- Exclusion groups. Two deals in the same group can't both fire; parent-group walking decides who wins.
- Clamped. Overlapping deals are floored at zero — a discount can never invent money.
Dynamic Survey
You draw the form. It answers back.
Sections, rules, conditions — all data. Attach a survey to login, start of day, check-in, check-out, order creation or end of day, and it evaluates on the device, offline.
- Conditional by design. A “No” opens the follow-up; conditional-mandatory blocks the workflow until answered.
- Six trigger points. Login, start-of-day, check-in, check-out, order, end-of-day — you choose when it fires.
- Auto-populated. Pre-fills what it already knows — store, route, the SKU in question.
Org A runs a 2-step chain; Org B a 3-step — same engine, no code.
Dynamic Approval Matrix
The approval chain is a row you edit.
Define each document's approval flow — Draft → Submitted → Approved, with the off-ramps — from an admin screen, per document and per organisation. Change the chain on Tuesday; it routes differently on Wednesday, with no release.
- Configured, not coded. Every step is an editable transition row — add or remove a stage without a deploy.
- One engine, six documents. Sales & purchase orders, load requests, collections, returns and stock transfers all route through it.
- Per organisation. The same document can carry a different chain for a different business unit.
“Add twelve cartons of Chocolate 200g”
Voice ordering
Say it. It's on the invoice.
Hands-free navigation and order entry running offline. Constraining the recogniser to the ~600 words that matter took accuracy from roughly 85% to 97–99%. Say the wake phrase once, then work.
- 85% → 97–99%. A domain grammar, not a general model — accuracy where a generic engine fails.
- Fully offline. Speech-to-text runs on the device; no audio leaves the phone, no per-call cost.
- Navigate and order. Open a screen, add a line, set a quantity — hands stay on the boxes.
Per-salesman snapshots
A Windows service builds one compressed SQLite DB per salesman — 60+ entities, filtered to exactly what they may see, ~12–30 KB on the wire.
Filtered at source · tinyOne rule for who sees what
Visibility is decided in one place — company, route, store or salesman — and both the offline copy and live sync are built through it. Phone and portal can't disagree.
One resolverAtomic bulk upload
An order posts as one transaction of every related batch, in order — parents before children — so the orphan class of failure is gone. 7-batch order queued in 20 ms.
Race window: zeroIdempotency everywhere
Client-generated IDs make collisions across 500 phones a non-event; shared singletons use deterministic IDs, so a re-send updates rather than duplicates.
UUID · upsertSelf-healing local schema
The device upgrades its own tables on open. A column added on the server reaches a phone in the field with no reinstall and no fresh snapshot.
No reinstall · no downtimeDeletes that arrive
Soft-deletes can't reach a device that only pulls active rows, so deletions ride their own tombstone feed with its own cursor — broadcast fleet-wide when global.
Tombstoned · whitelistedMulti-tenancy as data
Company → BU → warehouse → van → plant is configuration, not code. Org types carry capability flags; visibility resolves by walking the tree.
Capability-flaggedDevice binding
One active user per device, enforced in the database. Sign a different person in and the local DB is wiped and rebuilt before they see a row of the last user's data.
Bound · wiped on switchBranding & theming
A company admin re-skins the login — layout, colours, imagery, copy — from a named preset library, with a preview that renders the real component.
WYSIWYG · DB-storedNext
Bring your hardest question.
Preferably the one about what happens when the signal dies mid-invoice. We have an answer, and it's in the architecture rather than the brochure.