AkkadAkkad

Apps & platform

The same app on six platforms, and it keeps working when the network does not

A business app is judged on its worst moment, not its best one. The interesting engineering is not the screen that loads in two hundred milliseconds — it is the shift that carries on when the connection drops, the ticket that still reaches the right printer, and the notification that arrives in the language the person reading it actually speaks.

Akkad's admin app is one codebase compiled to six targets. Not a mobile app with a cut-down web viewer bolted on, and not a desktop build that is really a wrapped website — the same screens, the same logic and the same offline behaviour everywhere, which is why a feature does not arrive on one platform three months before another.

The practical consequence is that a business does not have to choose. The owner works on a laptop, the counter runs on a desktop machine with a scanner plugged into it, the delivery coordinator uses a phone, and someone checking a figure on a borrowed computer opens a browser. All of them are looking at the same application.

01

Where the app runs

TargetNotes
iOSNative build, with an in-app store version check and a per-version dismiss so an update prompt cannot become a permanent nag
AndroidNative build, same version check, and verified deep links so a shared install link opens the app rather than a browser page
WebFull application in the browser, installable as a progressive web app with a standalone display mode and maskable icons
macOSNative build with automatic updates delivered through a signed update feed
WindowsNative build with the same automatic update mechanism, on its own per-app feed
LinuxNative build from the same codebase

Desktop updates are signed. The macOS build carries a public key and verifies the signature on every update it downloads, so an update feed cannot be substituted for a hostile one. Each vertical points at its own feed, set at runtime, so the pharmacy app can never be handed the restaurant app's release.

02

The showpiece

Working with no connection

Offline mode is not a cached read-only view. The device holds a real local database — the catalogue with its variants, images and pack sizes, your customers, today's orders, pinned quick-sale items and, for restaurants, the tables — and the app keeps taking orders against it. Everything that happens while disconnected joins a queue, and the queue is designed around one assumption: the network will come back, and until it does nothing may be lost.

Phase 1Session opens
Phase 2Orders
Phase 2bDine-in rounds
Phase 3Session closes
Replays are idempotent — a re-sent order returns the original order number instead of creating a second one.
Strict phase order, so every queued order attaches to a real server session before the shift is closed out. Nothing is ever discarded — a stubborn row surfaces for manual retry instead.

The catalogue is searchable, not just listed

The local database is indexed on name, barcode and SKU, so scanning and searching behave the same offline as on. A cashier does not discover the limits of offline mode mid-transaction.

Some things are blocked, deliberately

Creating an item offline, or editing an order that already exists on the server, would produce conflicts that can only be resolved by guessing. Those actions are refused with a clear message rather than queued and reconciled badly later.

Retries never give up on a transient failure

There is no attempt counter that quietly discards work. Genuine rejections park in a distinct state; anything that merely failed to reach the server is retried on every subsequent flush, indefinitely. The flush cadence is the backoff.

Nothing is ever deleted

After around ten rejections across roughly two and a half hours of working connectivity, an order moves to a visible needs-attention state with a one-tap manual retry. It is surfaced to a human, not thrown away. That is the whole policy, and it does not have exceptions.

03

What works offline, and what does not

The line is drawn at conflict. If an action can be replayed later without ambiguity, it works offline. If replaying it would require guessing what happened in between, it is blocked.

Works offlineBlocked offline
Browse and search the full cached catalogue by name, barcode or SKUCreating, updating or deleting items
Browse cached customersCreating, updating or deleting customers
Create orders — queued, and visible in the orders list straight awayUpdating or deleting an order that already exists on the server
Open a cashier session, including when the shift starts offlineBulk import
Close a cashier session, including one that was opened on the serverBulk status updates
Add dine-in rounds to an order the server already knows aboutExporting items or orders
Delete an order that is still only queued locally

A blocked action explains itself in the moment rather than failing silently, so nobody spends a minute wondering whether the tap registered.

04

The flush, in four phases

When the connection returns, the queue is not sent all at once. It is sent in a strict order, because the pieces depend on each other. The flush runs on app start, on connectivity recovery, and on a fifteen-minute background timer, and it is guarded against running twice at the same time.

  1. 1

    Session opens

    Any shift that was opened while offline is created on the server first, so it has a real server identity before anything is attached to it.

  2. 2

    Orders

    Queued orders are submitted next, and they attach to the session identity that phase one just established. If orders went first they would either attach to a local identity that never becomes real, or attach to nothing at all — and a drawer that cannot account for its own sales is worse than no drawer report.

  3. 3

    Dine-in rounds

    Rounds added to existing orders are appended after the orders exist. Each carries its own idempotency key, so a round that was sent and whose response was lost is not appended twice.

  4. 4

    Session closes

    Only once every order belonging to a shift has landed is the shift closed. The close computes the drawer from orders that are all actually present, so the expected cash figure is right the first time.

05

Why the queue never loses anything

Four decisions, each of which exists because the obvious alternative destroys data in a way nobody notices until much later.

Lost responses adopt the existing session

If a session open is rejected because the server already created it — a request that arrived, succeeded, and whose response never made it back — the app looks up the user's current open session and adopts it rather than treating the whole thing as failed. Without this, one lost response would strand every queued order behind a session that could never be created.

Deletion propagation has a fail-safe

Every sync returns the complete set of live item and customer identifiers, and the device prunes anything cached that is no longer in that set, in batches. If that list arrives missing, malformed or empty, pruning is skipped entirely. An empty list is far more likely to be a partial response than a business that deleted its entire catalogue, and acting on it would leave a cashier with nothing to sell.

Going offline is debounced, coming back is not

A three-second debounce on going offline stops a moment of poor signal from flipping the whole app into offline mode and back. Recovery is immediate, because there is no reason to wait once the connection is real.

There is a recovery probe for lying networks

When a low-level network error forces a hard offline flip, a probe loop runs every ten seconds until it gets a real answer. Operating systems sometimes report a healthy connection through a captive portal or a dead uplink; the app checks for itself rather than believing them.

06

Staying current

Signed desktop updates

macOS and Windows update themselves from a per-app feed set at runtime. The macOS build verifies an update's signature against a bundled public key before installing it.

A store prompt that respects you

Mobile builds check the store for a newer version and offer it, with a dismissal remembered per version — so declining once does not mean being asked again the same afternoon.

Deep links that open the app

Verified links on Android and associated domains on iOS mean a shared download link resolves into the app when it is installed, instead of bouncing through a browser page first.

A What's New sheet

Shown once per version after an update, localised, and seeded quietly on a fresh install so a new user is not greeted by release notes for software they have never used.

One full sync after every update

An app that has just changed shape re-fetches everything once rather than trusting a cache written by the previous build.

Installable in the browser

The web build ships a manifest with a standalone display mode, theme colour, and both standard and maskable icons — so a shop machine can run it as an application rather than a tab.

07

Three print layouts

Each layout has its own paper sizes, and each is configured separately — an invoice and a thermal receipt are different documents with different jobs.

LayoutPaperTypical use
InvoiceA4 or US LetterThe document that goes to a business customer or into the file
Shipping label100 × 150 mm or 80 × 120 mmWhat the courier scans and reads at the door
Receipt80 mm or 58 mm thermal rollThe slip handed over at the counter

Thermal output is sized to the print head rather than the paper, which is the single most common source of receipts with a cut-off right edge. An 80 mm roll gets 576 dots across 72 mm at 203 DPI; a 58 mm roll gets 384 dots across 48 mm. Printers connect over the network using ESC/POS on the standard port, or over USB.

Every field is optional

Organisation logo and shipping-agent logo, name, city, address, postal code, phone and email; the same set for the customer; totals, order number, tracking number, who created it, notes, package weight and dimensions, tax, discount, custom amounts and custom fields. QR code or barcode, keyed to the order number or the tracking number — and mutually exclusive on the invoice, because both is neither.

The items table is a set of columns

Item name, image, SKU, quantity, unit price and total price are each their own toggle. Turning all of them off hides the table entirely, which is a legitimate document — a delivery note that deliberately does not list prices.

Kitchen station printers

For restaurants: named network printers, each mapped to a set of categories, so the grill ticket goes to the grill. A fallback station catches any category that is not mapped anywhere, because a dish that silently never prints is the worst possible failure in a kitchen. Each station can optionally list the rest of the round as context. Network-only by design, since stations sit nowhere near the counter.

Bulk label printing

Pick items, set a print quantity per item, and get one page per label sized exactly to the label. Code type is QR, barcode or none; width and height are set in millimetres, defaulting to 38 × 25 and clamped between 10 and 300. Name, SKU and price are individually shown or hidden, and the settings persist on the device.

Right-to-left detected from the content

Print output has its own right-to-left layer, and label printing detects direction from the text itself as well as from the app language. An English interface printing Arabic item names still renders them with the correct font and direction — which is the normal case, not an edge one.

The logo is treated like an icon

Uploaded logos are trimmed, given a rounded mask, capped on the long side and cached once per session rather than re-decoded for every job. A logo that was fine on screen does not arrive on paper as a stretched rectangle in a white box.

09

What triggers a notification

Every notification is localised to the recipient's own language and mirrored into a persistent in-app inbox, so nothing depends on someone having seen a banner at the right moment.

TriggerWho receives itWhen
New orderTeam members allowed to see that order's creator — including a pseudo-creator covering public storefront and landing-page ordersOn creation. A member restricted from seeing an order is not sent a push about it
Low stock and out of stockOrganisation ownersOn the stock change, one alert per item
Batch expiry digestOwners plus anyone holding the expiry report permissionDaily at 06:00 UTC, roughly 09:00 in Iraq. Two tiers: batches hitting the seven-day mark, and batches expiring today
Onboarding nudgesTrial usersDaily at 16:00 UTC, early evening in Iraq — one nudge, chosen by priority
Subscription noticesOwnersDaily at 08:30 UTC

The expiry digest needs no deduplication table. Because it matches on the exact day in the organisation's local time, and because it runs once a day, the daily run is the deduplication — a batch can only hit its seven-day mark on one day. Removing the need for state is usually better than managing the state well.

10

Twelve nudges, in priority order

A new business signing up has a specific sequence of things to do, and the order matters — there is no point suggesting shipping rules to someone who has not added an item. So the nudge system holds twelve messages in strict priority order and sends the highest-priority one that still applies: add items, create orders, set up categories, try bulk import, add a team member, try scanning, look at the analytics, customise the business details, check the invoice layout, set up the website, configure shipping rules, set tax.

Which one applies is decided by looking at the account itself — how many items, orders and categories exist, how large the team is, whether a logo has been uploaded, whether tax has been configured, and how long ago each of those was created. Each nudge carries a destination, so tapping it lands on the screen where the thing is actually done rather than on a home page with instructions.

11

Thirteen scheduled jobs

The work that happens whether or not anyone has the app open. Each job is wrapped so that a crash is recorded as a queryable failure event rather than disappearing into a log nobody reads.

JobCadence
Onboarding nudgesDaily at 16:00 UTC
Batch expiry digestDaily at 06:00 UTC
Subscription noticesDaily at 08:30 UTC
Expired-data cleanupEvery 2 hours
Notification cleanupDaily at 02:30
Inactivity cleanup — warn, then delete organisations inactive 12 monthsDaily at 03:00
Cashbox lifecycleDaily at 01:00
Monitoring retention — events over 30 days, request logs over 14Daily at 02:45
Subscription trial syncEvery 6 hours
Custom-domain certificate checkerEvery 5 minutes
Product catalogue syncEvery 5 minutes
Deferred purchase eventsEvery minute
Location cache refreshMonthly, on the 1st at 02:00

12

Thirteen languages, fourteen on storefronts

The app ships in English, Arabic, German, Spanish, Persian, French, Hindi, Indonesian, Portuguese, Russian, Turkish, Urdu and Chinese. Storefronts add a fourteenth, Kurdish Sorani, because the people buying from an Iraqi shop are not always reading the same language as the people running it.

Four of those are right-to-left: Arabic, Persian, Urdu, and Kurdish Sorani on storefronts. Direction drives layout throughout rather than being applied as a stylesheet afterwards — the navigation curve, the chevrons and the list separators all mirror. Two Arabic typeface families are bundled at four weights each, so Arabic text is set properly rather than falling back to whatever the device happens to have.

Localisation reaches past the interface. Every API error carries a message resolved into the calling user's own language, every push notification is localised per recipient rather than per organisation, and stock history is stored as structured keys and translated at the moment of reading — so the same log entry renders in Arabic for one colleague and English for another. Printing carries its own right-to-left layer, with fonts preloaded for every supported print language.

13

Questions

If the internet drops mid-shift, what actually happens?

The app switches to the local database after a three-second debounce and keeps taking orders. Everything created while offline queues locally and appears in the orders list immediately, so the shift looks normal. When the connection returns, the queue flushes in four phases — sessions, orders, rounds, then session closes — and nothing is deleted at any point.

Can two people take orders offline on different devices?

Yes. Each device holds its own queue and its own cashier session, and each flushes independently when it reconnects. Order creation carries an idempotency key, so a submission whose response was lost returns the original order rather than creating a second one.

Why can't I create an item while offline?

Because uniqueness on SKUs and barcodes is enforced across the whole organisation, and two disconnected devices cannot both guarantee it. Rather than accept the item and reject it hours later during a flush — after it has already been sold — the action is blocked with a message at the moment you try.

Does my thermal receipt need configuring for paper width?

You choose 80 mm or 58 mm and the printable area is derived from the print head, not the paper. That is why receipts do not come out with the right column clipped, which is the usual symptom of a layout sized to paper width instead.

How do kitchen tickets know which printer to use?

Each station printer is mapped to a set of categories, and a round's lines are routed accordingly. A category mapped to nothing goes to the fallback station rather than being dropped — in a kitchen, a ticket that silently never printed is the failure that costs a table.

Will my team get notifications in their own languages?

Yes. Notifications are localised per recipient rather than per business, so a mixed team each reads the same alert in their own language, and the same is true of API error messages and of stock history.

Counter & cashbox

Drawer sessions, scanning and the shift close that the offline flush is ordered around.

Orders

Idempotent creation, editing with a change log, and the statuses the queue replays into.

Team & permissions

The grants that gate printing and reports, and the visibility rules notifications honour.

اسے اپنے ہی کیٹلاگ پر آزما کر دیکھیں

اپنے لیے موزوں پلان سے شروع کریں، اشیاء ایکسل فائل سے درآمد کریں، اور اسی دن چلتا ہوا اسٹور، کاؤنٹر اور آرڈر بک حاصل کریں۔

مفت پلان دستیاب ہے۔ آزمانے کے لیے کارڈ کی ضرورت نہیں۔ درآمد آپ کا موجودہ کیٹلاگ آپ کے ساتھ لے آتی ہے۔