Selected engineering work · Commerce & retail
Building a configurable receipt, cash-office, and ERP posting layer for grocery checkout
A production till layer that printed the right receipt, took split tenders, and posted every sale, refund, parked basket, and shift event into LS Retail.
This case study represents work previously performed by a member of our engineering team before the formation of our consulting firm. It was delivered at MishiPay, a scan-and-go and in-store checkout platform. The retailer is not named, and certain implementation details have been anonymized for confidentiality.
Context
A grocery till event is only finished when four records of it agree: the receipt in the shopper’s hand, the cash office at the end of the shift, the supervisor audit trail, and the retailer’s ERP.
This work sat on a production scan-and-go and in-store checkout platform used across POS, cashier kiosk, public kiosk, web, scan-and-go, and click-and-collect. The retailer already ran Microsoft LS Retail as the system of record. Checkout, however, was happening on a separate platform, and finance, inventory, and loyalty all had to be able to trust the result.
The challenge
If the receipt said one thing and LS Retail said another, the store had a real problem, not a logging problem.
A print-receipt structure already existed on the platform. What did not exist, for this retailer, were the retailer-specific receipts, the digital receipt channel, the cash-office documents, the LS Retail posting variants, split tenders, a usable supervisor session, and the jobs that kept master data and reconciliation moving.
A new grocery retailer required customized receipt formats and business logic without introducing unnecessary duplication into the existing system.
The complexity
Four constraints, one lane:
- The artefact the human holds has to be complete. Store, staff, slip and transaction numbers, every line (including weighed and open-key items), promotions, discounts, each tender, tax, loyalty, coupons, barcode and QR.
- The artefact the back office holds has to match. LS Retail is not a clean REST resource. Sales, refunds, suspended baskets, and shift events are related, but they are not the same document. A “post the JSON” wrapper would have been wrong.
- Cash office is a different domain from the customer receipt. Float, remove-tender, tender declaration, and the Z-report have to be printable on the cashier kiosk and postable to the ERP.
- Privileged actions cannot stop the lane. Admin-menu and basket tabs that need a supervisor were prompting for credentials on every click. That is technically “secure” and operationally unusable.
Our approach
Treat one till event as a single business fact with several projections (a receipt, an ERP payload, a cash-office document, an audit row) rather than four unrelated features.
The existing receipt architecture was extended using a strategy-based approach, so retailer-specific implementations could inherit from a shared base structure. Common functionality stayed shared. Retailer-specific behavior stayed isolated.
- Extend, don’t fork, the receipt model. A retailer-specific print strategy on top of the existing receipt base, then the same payload reused for email, with SMS carrying a short message and a link to the hosted receipt.
- Share a posting path, vary the payload. Sales, suspended baskets, and refunds go through one transaction pipeline. Polymorphism sends each type to the XML builder that LS Retail actually expects, including the refund case, which is three documents, not one.
- Get posting off the checkout request. Kafka consumers on Azure handle sales and suspended-basket posting so a slow ERP round-trip does not hold the till.
- Give supervisors a session, not a prompt per tab. A time-bounded staff session authorises privileged actions and the admin menu. Every privileged action is written to a staff action log tied to that session.
- Automate the overnight retail chores. Airflow for daily LS Retail imports (receipt barcodes, member contacts) and for the morning reconciliation email once the overnight report is ready.
POS / kiosk / web / scan-and-go / click-and-collect
│
▼
Python services: inventory · payments · promotions · tax
│
├── print / email / SMS receipts
├── cash-office receipts (cashier kiosk)
├── staff session + action log
▼
Kafka (Azure)
│
▼
LS Retail ← SOAP / XML (sale · refund · parked basket · shift)
▲
│
Airflow: daily imports + reconciliation emailThe solution
Receipts on every checkout surface. Retailer-specific print receipts covering store and staff identity, LS Retail slip number, transaction number, line items (including weighted and open-key), promotions, discounts, cash / card / split tenders, tax inclusive and tax summary, loyalty points, coupons, QR, and barcode. Email receipts with the same payload. SMS receipts with a link to the hosted copy.
Cash office on the cashier kiosk. Float-entry receipt at shift start. Remove-tender receipt when excess cash is pulled. Tender-declaration receipt at close. Z-report alongside close.
Parked baskets. Suspended-basket receipt with line items and a barcode. Scanning the barcode resumes the basket.
ERP posting. End-to-end sales posting to LS Retail. Shift posting for float, remove tender, tender declaration, and open drawer. Suspended-basket posting on the sales path, without payment lines. Standard refund posting, including LS Retail’s three-document refund protocol.
Tenders. Split payments for this retailer: cash plus card, including multiple of each in one basket.
Supervisor session. Staff only see tabs they can edit, view, or open with approval. One session token covers subsequent privileged tabs and the whole admin menu.
Operations. Daily Airflow jobs to pull receipt barcodes and member contacts from LS Retail, and to email the reconciliation report. Kafka consumers for sales and suspended-basket posting. Monthly on-call on the live stack.
Outcome
The retailer went live with this checkout stack. A sale, a parked basket, a split tender, or a shift close produced the right document for the human and the right document for LS Retail.
The new implementation enabled client-specific receipt customization without requiring changes across the broader receipt-generation flow. The architecture also provided a clearer foundation for supporting additional client variations later.
No percentage is attached to this, because none was measured in a way we would put on a website. The proof is the unforgiving kind: it ran in real grocery stores, on real tills, against a real ERP.
Technology
Python services · SOAP / XML · Microsoft LS Retail · Kafka on Azure · Apache Airflow · Azure Blob Storage · staff-session authz and action logging · POS, kiosk, web, scan-and-go, click-and-collect clients
Why this is here
This is prior individual work, not a client of this firm.
It is the same class of problem we take now: a business system that already exists, a third-party platform that cannot be hand-waved, and a production constraint that the integration has to be right or the operation breaks.
Next step
Let’s talk about what you’re building.
Whether you are planning a new product, modernizing an existing platform, or facing a complex technical challenge, we’d be happy to start with a conversation.
Start a conversation