Demo 3.16: Air-Gapped Retail POS & Edge Sync (m_pos)
Functional Objective: Proves that a retail outlet or subsidised canteen can continue processing sales invoices during a complete network outage. Once connectivity is restored, the edge node’s local outbox is synchronised back to the central Finance and WMS ledgers via NATS JetStream, with automatic Last-Write-Wins (LWW) conflict resolution ensuring zero data loss and no duplicate postings.
🎥 Video Walkthrough & Feature Tour
Section titled “🎥 Video Walkthrough & Feature Tour”Offline Billing, JetStream Edge Sync & Beneficiary Quota Guard
Demonstrates a retail counter processing two sales invoices while disconnected from the cloud. The outbox is then flushed over JetStream once the network is restored. A stale edge payload is correctly rejected by the LWW resolver, and a beneficiary card over-spend attempt is blocked by the quota controller.
📋 Core Functional Capabilities
Section titled “📋 Core Functional Capabilities”- Zero-Network Resilience: A local counter billing engine (SQLite at the edge or IndexedDB on the frontend) allows retail outlets and canteens to process
SalesInvoicerecords without any cloud connectivity. - JetStream Outbox Sync: When the network is restored, the accumulated edge outbox is pushed in configurable batches via
POST /api/v1/pos/sync_outbox, which enqueues each payload into a NATS JetStream task processed by them_pos.process_jetstream_syncworker. - Last-Write-Wins Conflict Resolution: The
JetStreamSyncServicecompares themodifiedtimestamp on each incoming edge payload against the current cloud document. Stale payloads (edge timestamp ≤ cloud timestamp) are rejected withrejected_stale; fresh payloads update or insert accordingly. - Smart Card / Beneficiary Quota Management:
PosBeneficiaryCardenforces a monthly purchase quota per card holder. ThePosBeneficiaryCardControllerblocks any save whereutilized_quota > monthly_quota, and a scheduled cron job (m_pos.reset_beneficiary_quotas) auto-resets all active card quotas on the 1st of each month. - Master Data Pull API:
GET /api/v1/pos/master_dataprovides edge nodes with the latest item catalogue and customer list before going offline.
🏗️ Technical Architecture & DocType Mapping
Section titled “🏗️ Technical Architecture & DocType Mapping”In Business M and Framework M, this domain is powered by the m_pos library, which integrates with m_invoice for SalesInvoice sync and with framework_m_standard for the shared session and DB adapter.
Active Business M DocTypes
Section titled “Active Business M DocTypes”PosProfile, PosBeneficiaryCard, SalesInvoice (via m_invoice)
graph LR
Edge["Retail POS Edge Node\n(SQLite / IndexedDB)"] -- "GET master_data" --> Gateway["API Gateway"]
Edge -- "POST sync_outbox (batch)" --> Gateway
Gateway --> API["OfflineBillingAPIController\n(/api/v1/pos)"]
API --> JS["NATS JetStream\nm_pos.process_jetstream_sync"]
JS --> Worker["Sync Worker\nJetStreamSyncService (LWW)"]
Worker --> DB[("PostgreSQL Cloud DB\n(SalesInvoice / GL)")]
CronJob["Monthly Cron\nm_pos.reset_beneficiary_quotas"] --> DB
🧪 Verifiable Test Script (Human Review Flow)
Section titled “🧪 Verifiable Test Script (Human Review Flow)”Follow these steps in the running Business M instance to replicate the live PoC demonstration:
-
Create a POS Profile: Navigate to
wms → PosProfile → New. Link it to your Company and a default Warehouse. Setsync_batch_sizeto10. -
Issue a Beneficiary Card: Navigate to
wms → PosBeneficiaryCard → New. Enter a uniquecard_number, link it to a Customer, and setmonthly_quotato₹1,000. Save. Confirm the card appears in the list view. -
Simulate Offline Billing: With the network conceptually disconnected, create two
SalesInvoicepayloads locally at the edge (e.g., ₹150 and ₹200 for the beneficiary card holder). Verify these are only stored locally. -
Restore Network & Push Outbox: Call
POST /api/v1/pos/sync_outboxwith the two payload objects. Verify the response returns{"status": "success", "queued": 2}. This queues both invoices into JetStream for asynchronous processing. -
Verify LWW Rejection: Re-send one of the same invoice payloads but with a
modifiedtimestamp older than the one already processed. Confirm the sync worker rejects it withrejected_stalein the logs, proving no stale overwrites occur. -
Trigger Quota Violation: Open the beneficiary card and set
utilized_quotato a value exceedingmonthly_quota(e.g.,₹1,500). Attempt to save. Confirm the system blocks the save with a validation error: “Utilized quota cannot exceed monthly quota.” -
Verify Monthly Reset: Inspect the
m_pos.reset_beneficiary_quotascron job configuration. Confirm it is scheduled for the 1st of each month and that it would set all active cardutilized_quotavalues back to₹0.00.
🔗 Related Playwright Flow
Section titled “🔗 Related Playwright Flow”This PoC is automated end-to-end in flow-15-pos-edge-sync.spec.ts, covering all 7 steps above in a repeatable, isolated test run using unique POSFLOW-<runId> identifiers.