Interior of a clothing retail store with neatly arranged product displays
Business Central extension · v1.1 · System of record

Offline-first retail POS. Business Central is the truth.

The Business Central AL extension that anchors a five-component retail stack — store, lane and device master, pluggable tenders, native loyalty, promotions, fiscal artefacts and an idempotent POS sync API. Five country packs ship at launch. Re-posting a known sale never duplicates.

  • Idempotent sync — re-posting never duplicates a sale
  • 5 country packs · IN · AE · SA · US · EU
  • Native loyalty + promotions engine
  • Till sessions with multiple shifts + EOD posting
  • Fiscal artefacts: IRN · ZATCA QR · UUID · stamp
  • PA-DSS-friendly: no PAN ever stored

Built on infrastructure your finance and store teams already trust

Microsoft Dynamics 365 Business CentralPA-DSS friendly · no PAN storedFRD v1.1 traceability
Why we built it

Most retail POS that talks to BC silently drops sales on network outages. We refused.

Tills lose connectivity. Phones get reset. Networks reboot. In retail, an unposted sale is unaccounted revenue — and worse, a re-tried post can become a duplicate sale that finance has to chase. We built around this from day one. Every sale carries a client-generated `transactionId`. BC's Posted Sale Map remembers it forever. Retry is safe. Outages are safe. No sale is lost; no sale is duplicated.

0
duplicate sales possible

Client-generated transactionId + Posted Sale Map. Re-posting a known transaction returns the original document.

Offline-first
the till keeps selling

Sales captured in the local store. Posted to BC when connectivity returns. A complete outage doesn't stop the shift.

5
country packs at launch

India · UAE · Saudi Arabia · USA · EU. Each pack ships a tax projection plus the relevant fiscal artefact integration.

Scope of this product

One product, five components. This page is about the system of record.

The SourceForge Retail Solution ships as five composable components. The BC AL extension on this page is the authoritative back-end — master data, posting, the API contract that the client shells consume.

Built · production

1. BC AL extension

System of record

This product. Master data, posting, idempotent sync API, country packs, native loyalty, entitlement, till/EOD, audit and fiscal artefacts.

Separate workstream

2. Shared React core

Offline cart + pricing/promo/loyalty evaluation

Runs in both the Electron and Android shells. Offline queue, durable local store. BC re-computes tax authoritatively on posting.

Separate workstream

3. Electron desktop shell

Lane terminal · Node hardware adapters

ESC/POS receipts, cash drawer, scanner, scale, customer display, fiscal signer adapters, local SQLite database.

Separate workstream

4. Android / Capacitor shell

Mobile POS · Sunmi + PAX plugins

Sunmi (China) and PAX (international) terminal hardware. Same React core. Used for queue-busting and mobile checkout.

Separate workstream

5. Sync & entitlement SaaS

Multi-tenant regional middleware

Sits between the client shells and BC. Handles auth proxying, delta-sync batching, regional data residency, entitlement enforcement.

5 country packs at launch

Tax-correct on day one in five jurisdictions.

Each pack ships an offline tax projection the client uses at the till, plus the relevant fiscal artefact integration. BC re-computes tax authoritatively on posting and remains the source of truth.

IN
India

GST + e-invoice IRN integration

AE
United Arab Emirates

ZATCA QR + UUID + cryptographic stamp

SA
Saudi Arabia

ZATCA Phase 2 e-invoicing

US
United States

State + local tax via projection

EU
European Union

Per-country VAT + reverse charge

The full retail surface

Eight capability groups. All wired to BC for posting and audit.

Each group corresponds to a section of the FRD with explicit traceability — implementation status against each FR-ID is documented and reviewed at every release.

POS terminal & sales

Items via barcode (Item Reference) and variant. Parked sales, returns/exchanges as credit memos with reason codes, per-line overrides with manager approval, currency-aware, age-restricted flags synced.

Pluggable tenders

Cash, card, gift card, store credit, voucher. Each tender has Kind + Integration Style + Adapter Id. Enabled per country and store. Split payments with multiple lines, tip captured, refund-to-original honored.

Promotions engine

BOGO, Buy-X-Get-Y, mix-and-match, tiered, markdown. Time and day-of-week windows. Coupon support. Configurable stacking. Definitions live in BC; evaluation runs at the till for instant feedback.

Native loyalty

Accounts, tiers (auto-progression on lifetime spend), rules (category/tender/promotion), ledger with FIFO expiry. Accrual computed at the till; reconciled idempotently per transactionId on sync.

Receipts & fiscal artefacts

Country-pack-driven statutory fields. Fiscal Artefact table stores signed payloads (IRN, ZATCA QR/UUID/stamp, GST/VAT). RTL receipt flag for Arabic. Reprint is audited.

Customer & consent

Quick-create from POS with dedup via External POS Id. POS Tax Registration No. captured per sale. Marketing consent recorded with region (DPDP/GDPR), changeable from the customer card.

Till sessions & shifts

Open float, paid in/out, safe drop, blind or sighted close. Multiple shifts per till per day, sales attributed via Shift Id. EOD action posts settlement and variance to BC.

Store master + opening hours

Store / Lane / Device hierarchy. Weekly opening-hours schedule with calendar exceptions for holidays. `Store.IsOpenAt()` helper. Synced via dedicated API endpoints.

Integration contract

The API contract the client shells and middleware consume.

All endpoints under apiPublisher=sourceforge / apiGroup=retail / apiVersion=v2.0. Breaking changes go to a new publisher group; the v2.0 contract stays stable.

Pull (master → client cache)

stores · storeHours · posItems · itemBarcodes · posCustomers · priceListLines · promotions · tenderTypes · countryPacks · taxProjections · loyaltyAccounts · stockLevels

Push (client → BC)

insert posSales (with posSaleLines + posPayments) · insert tillSessions (with cashMovements + tenderCounts + shifts) · insert fiscalArtefacts · register/heartbeat devices

Bound actions

POST posSales(<id>)/postSale → idempotent posted document · POST tillSessions(<id>)/postEndOfDay → settlement + variance · POST devices(<id>)/heartbeat → device + entitlement grace

Auth: OAuth service-to-service. The service account holds the SFR Retail – Sync permission set plus standard D365 sales/finance posting permissions. Delta sync via `lastModifiedDateTime` on every master-data endpoint.

Security & governance

Built for retail audits where the journal must reconcile.

Every operator action lands in BC with full audit trail and is exportable for compliance. PA-DSS-friendly by construction — card data is never stored.

Idempotent by construction

Client-generated transactionId is the key. Re-posting returns the original document. Failed posts stay staged with the error visible for retry. No sale is lost.

No PAN, ever

Card numbers never touch BC. We store Card Last 4 + PSP Reference. The actual payment processing happens through client-side adapters; BC sees only the metadata.

OAuth service-to-service

Client middleware authenticates to BC via OAuth (FR-BC-009). Service account uses SFR Retail – Sync permission set plus standard D365 sales/finance posting permissions.

Full audit log

Price overrides, manager discounts, reason codes, refund reasons, receipt reprints — every operator action lands in SFR Audit Log with the user, the time, the before/after, and the sale it relates to.

Entitlement enforcement

Per-tenant entitlement gates feature activation by country pack and module. Device heartbeat refreshes the entitlement grace window. Expired tenants degrade gracefully.

Sync log per device

Every push surfaces in SFR Sync Log. Devices report pendingPostings on heartbeat so the ops team can see who is falling behind without opening every till.

How the idempotency works

The transactionId is the key. The Posted Sale Map is the memory.

Every sale on the lane gets a UUID at creation time. BC's Posted Sale Map records that UUID against the resulting posted document on first successful post. Every subsequent attempt with the same UUID returns the original document — never creates a new one.

  • Network drops mid-post → the client retries; BC sees the same transactionId and returns the original posted document.
  • Operator hits ‘send’ twice from confusion → second call is a no-op; finance sees one invoice.
  • Multi-day connectivity outage → every staged sale survives; the Job Runner posts the backlog in transactionId order when the link returns.
  • Failed post (permission / tax error) → the staged sale stays, the error is on it, no duplicate appears anywhere.
Posted Sale Map (excerpt)
transactionId           posted_doc_no
─────────────────────  ─────────────
a3f9...4c2e             SI-1043927
b7c2...91e0             SI-1043928
c1d4...8b5f             CM-0001839
d5e7...2a3b             SI-1043930

# 2nd attempt for a3f9...4c2e?
→ returns SI-1043927
→ no new document
→ no journal entry
→ no audit-log row

The Posted Sale Map is permanent. Even years later, replaying an old transactionId returns the original document — never duplicates.

Implementation

Single store live in 6-10 weeks.

The BC extension itself installs in under an hour. The real time is in business configuration, training and cutover. A senior SourceForge consultant runs the project end to end — same person from discovery through hypercare.

6–10
weeks · single store
10–16
weeks · multi-store
Modern POS terminal in a busy retail store
BC SaaS · minimum BC 25

Per-Tenant Extension installed in your BC tenant. AppSource publication planned for v2.0.

1

Discovery

Store inventory walk-through. Tender list per country. Country pack scope. Promotion + loyalty rules. Existing fiscal/IRN integrations. Cutover constraints.

2

BC setup

Install PTE. Activate country packs. Seed tenders. Create Store / Lane / Device master. Configure number series, posting groups, EOD journal template + cash variance account.

3

Client + sync

Sync middleware deployed. Master-data cache primed at lanes. First parallel-run shift alongside the legacy system. Track every sale through the Posted Sale Map.

4

Cut-over

Full operator training. Promotions + loyalty + manager-discount limits live. Open-hours + calendar exceptions configured. Daily check-in calls for the first week.

5

Hypercare

Daily review of Sync Log + posting errors. Tune tax projections after the first GST/VAT filing. Add country packs and stores as roll-out expands.

Retail for BC FAQ

Frequently asked questions

Business Central SaaS only, minimum BC 25 (runtime 14.0, platform/application 25.0.0.0). On-premises is not supported in this initial release. Object range 52500-52799 with the SFR prefix on every object. Distributed as a Per-Tenant Extension (PTE) installed privately — AppSource publication is planned for a later release.

Book a working session

Bring a sample SKU list. We will configure your country pack live.

A 60-minute working session, not a sales pitch. Bring a slice of your item master, your tender list, your country pack and an example fiscal scenario (a GST sale, a ZATCA sale, a tendered cash transaction). We bring up a BC sandbox, install the extension, run a sale through the staging API and post it — showing you the Posted Sale Map, the audit log and the fiscal artefact at every step.

  • Live install + country-pack activation in your BC sandbox
  • End-to-end posting demo with idempotency replay
  • Honest answer on whether you need all four shells or just one
  • Written proposal within 48 hours

Book a Retail for BC working session

Tell us how many stores, which countries, and which front-end (Electron desktop, Android, both, or your own). We respond within one working day.

WhatsAppCall us