Read the trial balance

Understand the chart of accounts, prove the books balance, and trace one number to its transaction.

Owned by Admin · 8 steps · about 20 minutes

Why this exists

The house-credit economy is a real double-entry ledger, not a balance column on the user row. Every movement of money is a transaction with at least two entries whose debits equal its credits, and the sum of every entry ever written is zero. That single property is what lets you answer "where did it go" without ever guessing.

Why go to that trouble for an in-app wallet? Because the platform holds customer money in three genuinely different ways and they must not be allowed to blur. Prepaid credit is money customers paid in and the venue owes back. Earned balance is money resale sellers made and the venue owes them. Tab used is money customers owe the venue. A single wallet number would hide all three, and the difference between them is the difference between a liability, a payable and a receivable.

The chart of accounts is small and fixed. Cash and payout clearing, the four per-user accounts — the three above plus promotional credit, which the venue mints rather than the customer paying in — two tax liability accounts, ticket revenue and royalty fees, processing expense, and a write-off account for resale clawbacks the venue absorbed. Per-user accounts carry a user id on every entry, enforced by the database, which is what makes a per-person sub-balance meaningful.

The books are append-only, enforced by triggers that fire on raw SQL as readily as on service calls. Reading the trial balance is therefore a genuinely trustworthy act: you are not reading a cache or a view someone could have edited, you are reading the sum of everything that ever happened.

Before you start

  • An admin session. This entire area is admin-only; a venue manager gets 403 even on the read-only routes.
  • A seeded database, so there is enough traffic for the numbers to be interesting.

Practise with

PersonaEmailPasswordNote
adminadmin@club.test admin123the only persona that can open any ledger surface

Steps 1–8 — Admin

their manual →
  1. 1
    Open the trial balance. Read it top to bottom once before you look for anything in particular.
    /admin/ledger ledger
    Expected result Every account with its debit total, credit total and net, plus a balanced flag and a grand total net that must be zero.
    Watch out for If the grand total is not zero, stop everything else you were doing. It means the append-only guarantee has been circumvented, and no other number on the platform can be trusted until you know how.
  2. 2
    Pull the chart of accounts and learn the codes. You will be typing them into adjustments and filters for the rest of your time here.
    Expected result Each account with its normal side and whether it is per-user.
    Watch out for CORRECTED 2026-08-30: FOUR accounts are per-user, not three. Prepaid member credit (2010), promotional credit (2015), seller earned balance (2020) and VIP tab used (2030) — 2015 arrived with the promo pot and this sentence did not move. Those four are the only ones where a per-person number exists at all, and if you are reconstructing somebody's credit from account filters, leaving 2015 out under-reports what they can spend.
  3. 3
    Pull the trial balance as of a specific instant when you need to answer a question about a moment in the past rather than about now.
    Expected result The same structure, computed as of the timestamp you asked for.
    Watch out for As-of is computed from the entries every time — `trial_balance` sums `ledger_entries` with a date filter and reads no stored total, which is why it can be trusted. CORRECTED 2026-08-30: the reason this step used to give was wrong twice. There IS a month-end close (close, lock and reopen a period; it closes months in order and refuses over a red invariant), and closing DOES store a trial-balance snapshot on the period row. Neither invalidates the as-of figure, because nothing reads that snapshot to answer this question — but do not go to a closed month expecting to post into it. An adjustment there is 409 PERIOD_CLOSED, and a locked month refuses even the prior-period escape.
  4. 4
    Move from totals to movements. Filter by kind to see one class of thing at a time — sales, refunds, top-ups, resale settlements, payout holds and releases, tab settlements, adjustments.
    Expected result A paged list with memos, kinds and references.
    Watch out for The reference type and id are the join back to the rest of the platform. A refund transaction points at a refund; a settlement points at a resale settlement. Follow the reference rather than matching amounts by eye.
  5. 5
    Open one transaction and confirm the double entry with your own eyes: debits on one side, credits on the other, the same total.
    /admin/ledger/transactions/{txn_id} ledger
    Expected result Entries with account codes, amounts and the user attribution on per-user lines.
    Watch out for Card sales post the processing fee as a SEPARATE transaction, not as a line on the sale. If a sale's numbers look too round, the fee is next door.
  6. 6
    Use the API when a question needs filtering the page cannot do — by user, by account, by date range, by reference.
    Expected result The filtered transactions as JSON.
    Watch out for Filtering by account plus user id is how you reconstruct one person's history of one balance. That is usually the fastest answer to a support question about credit.
  7. 7
    Run the integrity report and read every check, not just the overall flag.
    Expected result Balanced transactions, earning lots matching the earned account, no negative sub-balances, payout clearing matching live payouts, the trial balance balanced, and the protective triggers present.
    Watch out for The triggers check is the load-bearing one. Everything else on this page assumes those triggers exist.
  8. 8
    Finally, open the ledger entries in the universal data suite and try to change something.
    Expected result A read-only grid. The suite classified it automatically because the table carries append-only triggers.
    Watch out for There is no admin override. The one legitimate way to change a balance is a new balanced adjustment posting — which is a different workflow, and deliberately so.