Skip to content

PRD: Wallets, vouchers & ledger

ModulePayment & Transaction (CORE-08)PRD IDPRD-WAL-001
StatusShippedOwnerPayment & Finance squad
Date2026-05-28Versionv1.0
Packages@nx/finance · @nx/payment · @nx/ledgerURDWAL · VCH · CAT

TL;DR

Gives a merchant a place to record the money behind every payment: accounts (wallets) where cash, bank, QR, and mobile-POS balances sit, balanced double-entry vouchers for every movement, and auto-posting that books completed sales, received purchase orders, and stock movements into a finance ledger exactly once each. The result: an owner can see real balances and reconcile their books, instead of only knowing a payment was marked paid.

1. Context & Problem

KICKO can move a payment to paid, but it has nowhere to record the resulting money. There is no place for cash, bank, or QR balances to sit, no double-entry bookkeeping, and no way to classify income and expense - so a merchant can collect a payment yet cannot see a balance or reconcile their books. Cost tracking and owner-level financial visibility are blocked, which is a hard requirement for the HKD/SME bookkeeping KICKO targets.

This feature builds the finance side on top of the existing payment lifecycle: accounts where money sits, balanced vouchers for every movement, seeded income/expense categories, and auto-posting that connects sale, purchase, and inventory events to the ledger. "Account" is the canonical term for a place money sits, superseding the earlier "wallet" naming.

2. Goals & Non-Goals

Goals

  • Finance accounts of type CASH, BANK, QR code, and mobile POS, each with an opening and running balance, with internal control accounts maintained automatically and one default account per merchant for auto-posted sale income.
  • Balanced double-entry vouchers in four types - RECEIPT, PAYMENT, TRANSFER, ADJUSTMENT - with a DRAFT → ISSUED → VOIDED lifecycle, per-merchant/per-type numbering, and void-by-reversal (no hard delete).
  • Auto-posting from business events: a completed sale payment posts a RECEIPT, a received purchase order posts a PAYMENT, and a stock issue/adjustment posts the matching voucher - each idempotent on its source event id.
  • Income/expense category classification: 14 seeded system categories (protected, typed INCOME or EXPENSE) plus merchant-specific custom categories with a parent hierarchy.

Non-Goals

  • Structured refund / reversal back through the original payment provider (URD §7 - Planned).
  • Per-shift cash reconciliation dashboard (URD §7 - Planned).
  • Multi-currency conversion and cross-currency reconciliation.
  • Tax invoice issuance - owned by the Tax & Invoice module.

3. Success Metrics

MetricTarget / signal
Ledger coverage100% of completed sale payments, received POs, and stock movements post a matching voucher
Balance integrityEvery voucher balances (DEBIT total = CREDIT total); zero unbalanced entries
IdempotencyEach source event posts at most one voucher, even on redelivery - zero duplicates
ReconciliationOwners can read live account balances that match the sum of posted ledger lines

4. Personas & Use Cases

PersonaGoal in this feature
OwnerSee real balances, classify income/expense, reconcile the books, void mistakes
ManagerManage accounts, create manual vouchers, review the ledger
CashierCollect payments that auto-post income (no ledger access)

Core scenarios: an owner creates the accounts where money sits → a completed sale payment auto-posts a balanced RECEIPT to the default account → a received PO or stock movement auto-posts the matching voucher once → the owner reviews the ledger, classifies entries by category, and voids any mistake via a balanced reversal.

5. User Stories

  • As an owner, I want to create accounts of type cash, bank, QR, and mobile POS with an opening balance, so money has a place to sit and I can see a running balance.
  • As an owner, I want a completed sale payment to auto-post a balanced RECEIPT voucher to my default account, so income appears in the ledger without manual entry.
  • As an owner, I want a received purchase order and a stock issue/adjustment to auto-post the matching voucher exactly once, so expenses and stock movements reconcile automatically.
  • As an owner, I want to create a manual voucher (draft → issue) and void it by reversal, so I can correct the books without ever deleting financial history.
  • As an owner, I want income and expense classified by category, with custom categories under a parent, so I can report on where money came from and went.
  • As a manager, I want to see only the accounts of merchants I am granted access to, so visibility respects my scope.

6. Functional Requirements

#RequirementURD ref
FR-1Create accounts of type CASH, BANK, QR code, mobile POS; each tracks an opening and running balanceURD-WAL-001 · URD-WAL-003
FR-2Maintain internal control accounts (e.g. inventory, COGS) automaticallyURD-WAL-002
FR-3One default account per merchant receives auto-posted sale incomeURD-WAL-004
FR-4Non-owner roles see only accounts of merchants they are granted access toURD-WAL-005
FR-5Every voucher is balanced (DEBIT total = CREDIT total) in four types RECEIPT / PAYMENT / TRANSFER / ADJUSTMENTURD-VCH-001..002
FR-6A completed sale payment auto-posts a RECEIPT to the default account; a received PO auto-posts a PAYMENT; a stock issue/adjustment auto-posts the matching voucherURD-VCH-003..005
FR-7Each voucher records its source event so the same event posts only once (idempotent)URD-VCH-006
FR-8Vouchers are numbered per merchant and per type (e.g. PT / PC / PCK / PKT)URD-VCH-007
FR-9An owner can create a manual voucher (draft → issue) and void via a balanced reversal (no hard delete)URD-VCH-008..009
FR-10A transfer between two accounts is recorded as a zero-sum voucherURD-VCH-010
FR-1114 system categories are seeded, protected, and typed INCOME or EXPENSE; an owner can add custom categories with a parent hierarchyURD-CAT-001..003

Full requirement text and acceptance criteria live in the Payment & Transaction URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Data integrityEvery voucher must balance (DEBIT total = CREDIT total); account running balances always equal the sum of their posted ledger lines
IdempotencyEach business event posts at most one voucher, keyed on the source event id; redelivery has no second effect
ImmutabilityNo hard delete of financial history - corrections happen via balanced reversal vouchers
Tenancy & authzAll accounts, vouchers, and categories scoped per merchant (x-merchant-id); non-owner visibility limited to granted merchants
PrecisionMonetary math uses float(value, 4)
i18nUser-facing labels/types/statuses are bilingual ({ en, vi })

8. UX & Flows

Key screens (in apps/client): account list and create, the voucher view (editable transaction table, party/transaction sections, void banner, overview), and the ledger overview with balances and voucher history.

9. Data & Domain

EntityRole
FinanceAccountA place money sits - type CASH / BANK / QR_CODE / MOBILE_POS, opening + running balance; includes internal control accounts
FinanceVoucherA balanced bookkeeping entry of one type (RECEIPT / PAYMENT / TRANSFER / ADJUSTMENT) with a DRAFT → ISSUED → VOIDED lifecycle and source event id
Ledger line (transaction)One DEBIT or CREDIT line within a voucher, affecting one account's balance, optionally classified by category
FinanceCategoryAn income/expense label - 14 protected system categories plus custom ones with a parent hierarchy
Voucher sequencePer-merchant, per-type numbering source
Ledger snapshot / jobSnapshot entries and the ledger job queue (API + worker split, progress over WebSocket)

Conceptual only - full schema and invariants in the finance domain model.

10. Dependencies & Assumptions

Depends on

  • Payment lifecycle (URD-PAY) - a completed payment is what triggers the RECEIPT auto-posting.
  • Orders (Orders module) - the sale forwards finance metadata on payment.success.
  • Inventory (Inventory module) - purchase-order receipt and stock issue/adjustment events drive PAYMENT / ADJUSTMENT postings.
  • @nx/ledger - ledger snapshot and job machinery (API/worker split).

Assumptions

  • Each merchant has a default account designated to receive auto-posted sale income.
  • The 14 system categories are seeded when a merchant's finance ledger initializes.
  • Business events carry a stable source event id for idempotency.

11. Risks & Open Questions

Risk / questionMitigation / status
Duplicate postings if an event is redeliveredEach voucher records its source event id; posting is idempotent
An unbalanced voucher could corrupt the ledgerEnforced invariant: DEBIT total must equal CREDIT total before issue
Voiding vs. preserving financial historyVoid is a balanced reversal; the original voucher is never hard-deleted
No multi-currency supportOut of scope; single currency per voucher documented as a constraint
Reversing finance against an already-reversed source (e.g. PO revert)Open: define the compensation path across modules

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 (finance ledger) - see URD feature catalog
RolloutAll merchants; no feature flag
MigrationNone for data; finance accounts and 14 system categories seeded at merchant init
Launch criteriaAuto-post from sale/purchase/inventory verified idempotent; every voucher balances; running balances match summed ledger lines; manual create + void-by-reversal verified
MonitoringVoucher volume per merchant, auto-post error rate, balance-vs-ledger consistency checks, idempotency-collision count

13. FAQ

Where does the money go when a sale is paid? A completed sale payment auto-posts a balanced RECEIPT voucher to the merchant's default account - no manual entry needed.

Can a voucher be deleted if it's wrong? No. Financial history is never hard-deleted; an issued voucher is corrected by a balanced reversal that voids it while preserving the original.

What stops the same event from posting twice? Each voucher records its source event id; if the event is redelivered, no second voucher is created.

What's the difference between an account and a category? An account is where money sits (cash, bank, QR, mobile POS); a category classifies what kind of income or expense a movement is.

Can I rename or remove the seeded categories? The 14 system categories are protected and cannot be removed. You can add merchant-specific custom categories, optionally under a parent.

Does this issue tax invoices? No - tax invoice issuance is owned by the Tax & Invoice module.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.