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.

Submit sale · one local transactionoffline
One tap
Invoice
QAR 2,640
Stock decremented
Warehouse ledger written
Receivable opened
Store ledger appended
All four, or none. A double-tap cannot mint a second invoice.

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 entry

Returns, 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 capped

Collections

Oldest-first or hand-picked, split across cash, card, cheque, online and credit notes — each leg its own TransNo.

Multi-mode · per-leg

Stock 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 shortage

End-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-aware

Printing 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 call

02 · 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.

Perfect Store · money at stakethis visit
Out of stock · high-revenue SKU QAR 951/wk
Out of stock · slow SKU QAR 0/wk
Below eye level · anchor brand QAR 604/wk
STORE> ROUTE > ORG> ALL

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 coded

ShelfLens

Photograph the shelf; the phone detects each product, embeds the crop, matches your catalogue and fills the scorecard.

~150 ms · zero cost

The 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 → verify

Planogram compare

The store's planogram resolves automatically; the compare view scores by how much of the expected facing count is really there.

Facing-weighted

POSM & competitor

POSM graded against a reference photo; competitor capture on a normalised spine, so share-of-shelf never fractures on spelling.

Reference-image · normalised

Shelf Command

The supervisor's view: money in play, worst-first leaderboard, biggest leaks — from the latest submitted visit per store.

Worst-first · money-ranked

The full merchandising and ShelfLens story

03 · 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 · this routeOpenStreetMap
As planned121 km
Re-sequenced73 km

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 & Coachlive
revenue · month to date
Coach: is the number genuine? 62% in one deal

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 warningBlumoon Market
Basket narrowingtripped
Order gap stretchingtripped
Drift to cheaper SKUswatch
Anchor SKU droppedclear
2 of 4 signals — flagged ~3 weeks to churn

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 cost

Command 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 → action

Basket affinity

Association-rule mining: stores buying A whose lookalikes overwhelmingly also buy B — but don't. Validated at 88% confidence.

Cross-sell · evidence-backed

Smart 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-aware

Beat 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
The Co-pilot Command Center showing a daily briefing, money in play, a leak ledger and an action queue
Command Center — the daily briefing reads in five seconds, the Leak Ledger says where value is slipping, and the Action Queue is ranked by money with a one-tap WhatsApp nudge attached to each row.

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.

Report builder · your choices → safe SQLno ticket
01
Pick fields
02
Filter & pivot
03
Window fns
04
Compile
→ parameterised SQL · rendered as any of 15 views, incl. true 3D
TableBarStacked LineComboPie Radar+53D ×4

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.
Four true WebGL 3D charts from the Xpress BI report builder — a 3D bar chart of sales versus returns by channel, a 3D surface field, a 3D donut and a 3D scatter over quantity, unit price and gross
True WebGL 3D — bar, surface, pie and scatter, on a second engine loaded only when a 3D view is picked. Same report config drives it as the flat views.
The Xpress BI Leaderboard view in three designs — a podium with gold, silver and bronze places, a ranked medal list with progress bars, and a horizontal bar race — ranking sales by category
Leaderboards, three ways — turn any Name + Value report into a gamified ranking: a podium, a medal list, or a bar race. One click switches the design.
Cross-filter, before and after clicking a category — every panel (bar chart, treemap, detail table, leaderboard) instantly re-scopes to the selection while the panel you clicked stays full
Click once, the whole board reacts — cross-filter any panel and every other panel re-scopes instantly. No reload, no query — the selection just propagates.
A store sales map across Doha — every store plotted by its real coordinates, bubbles sized and shaded by sales, click any store to focus the whole board
See it on the ground — plot stores or routes on a real map, sized by sales, and click a point to focus the board. A field-sales view generic BI tools don't ship.

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-driven

Pivots & filtered measures

Crosstab with values found at runtime; conditional aggregation splits sale cash, sale credit, return cash, return credit into columns.

Crosstab · conditional

Xpress Dashboards

A saved grid whose cells are existing reports — no second query engine to keep honest. One bad panel never kills the board.

Parallel · isolated

Executive 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
Sales performance report with KPI tiles, invoice-size distribution, cash versus credit mix and a salesman scorecard
Sales performance — one of the built-in report surfaces. Every figure here is also reachable from the builder, so nothing is a hard-coded dead end.

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.

01

Purchase order

The BU raises a costed PO. Prices, discounts and tax are resolved server-side — never sent by the client.

02

Dispatch

The company ships against it — one PO can become many shipments — debiting the plant warehouse.

03

Receive

The BU receives. Every shortfall must be itemised by reason before submit will unlock.

04

Return

Stock goes back up into reason-keyed bins — damage, expiry, quarantine — with a credit note.

05

Claim

What can't be returned is recovered as money, against the right party, on an SLA clock.

Purchase price · resolve for this buyerunits convert
1A price set for this buyerWINS
Price a single, order a carton line still comes out right

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-itemised

Claims 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 code

The 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 · idempotent

Reason-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 change

Partial 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 · frozen

06 · 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.

The financial spine
TableOwnsBalance
Accounts receivableMoney owed against a specific invoice. Invoice rows only — nothing else is allowed in.Total − Paid − Credit applied
Credit noteThe customer's credit wallet, created automatically from a sales return.Total − Used
Store ledgerAppend-only, chronological audit of every financial event per store.Running balance after each row
CollectionPayment received: header, per-mode breakup, and per-invoice allocation.Allocated across invoices
Shortage invoiceThe salesman's liability for van stock that an audit couldn't find.Billed to the salesman
Loyalty · on-device accruallive
Invoice
QAR 2,640
Earned
+264
balance · 820 to next reward
Redeem 2,000 pts→ 1× Chocolate 200g FREE

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-windowed

PDF suite

Invoices, picklists, dispatch and goods-return notes, claim notes and loyalty receipts — branded by walking the org chain.

Barcoded · parent-fallback

Net 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-count

A 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 production

Sync, 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 · at cart timeoffline
Line %−QAR 132
Qty slab−QAR 210
Buy-X-get-Y+2 FOC
Stacked by exclusion group clamped ≥ 0 · can't go negative

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.
Survey · builder → handsetoffline
Q1 · Single select
Is the brand block intact at eye level?
Yes
No
▲ shown because “No”auto-fills store
Photograph the gap & tag the missing SKU.
📷 Capture
Pick SKU
LoginCheck-in OrderCheck-outEOT

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.
Approval matrix · configured, not codedper org
Draft Submitted Approved
…or Cancelled — the off-ramp is a configured transition too
Documents that plug into the same engine
Sales orderPurchase order Load requestCollection ReturnStock transfer

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.
Voice · on the deviceoffline STT

“Add twelve cartons of Chocolate 200g

Chocolate 200g · matched SKU× 12
added to the open invoice

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 · tiny

One 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 resolver

Atomic 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: zero

Idempotency 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 · upsert

Self-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 downtime

Deletes 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 · whitelisted

Multi-tenancy as data

Company → BU → warehouse → van → plant is configuration, not code. Org types carry capability flags; visibility resolves by walking the tree.

Capability-flagged

Device 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 switch

Branding & 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-stored

Next

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.