Oksa Satya.

July 8, 2026 · 8 min read

Building an Auditable POS: Shifts, Cash Reconciliation, and QRIS

POSCashierReconciliationMidtrans

A bad cashier system only reveals itself at the end of the day — when the physical cash in the drawer does not match the number in the system, and nobody can explain the gap. I built Dexova's POS module with one obsession: every rupiah must be traceable. These are the decisions that make it auditable.

A shift is a unit of accountability, not just a login

A cashier opens a shift by recording the opening cash (drawer float). Every transaction — cash, card, QRIS — attaches to that shift. At close, the system computes the cash that should be there, the cashier counts the physical amount, and the difference is recorded, not hidden.

Without shift boundaries, 'the cash is short by 50,000' is a mystery spanning the whole day. With shifts, it becomes a specific question to one person over one time window — far easier to answer.

Void and return are not just deleting a row

Deleting a wrong transaction is the fastest way to destroy an audit trail. In Dexova, voids and returns are new recorded events — with reason, operator, and time — not deletions of old data.

The commonly forgotten part: a return must restore stock. Items a customer returns go back into inventory automatically. If they don't, sales and stock reports slowly diverge until nobody trusts either.

Payments: split payment and QRIS that doesn't hang

Real customers pay in mixed ways: part cash, part QRIS. The system has to treat one transaction as a set of payments, not a single method.

QRIS via Midtrans is asynchronous: the transaction stays pending until a webhook confirms settlement. The key is to treat the webhook as the source of truth, verify its signature, and make it idempotent — the same notification may arrive twice without double-counting the payment.

Summary

An auditable POS comes from three things: shifts as the unit of accountability, void/return as events (not deletions), and payments honest about their async status. The rest is just adding up prices.

Building something similar?

I take on projects and technical consulting around business systems.