Core Concepts
The foundational concepts of Ledger — double-entry bookkeeping, accounts, journal entries, wallets, invoices, transactions, and how it all fits together.
Core Concepts
Ledger implements proper double-entry bookkeeping. Understanding the core accounting model helps you use the system correctly and interpret your financial reports.
The Accounting Equation
Everything in Ledger follows this fundamental rule:
Assets = Liabilities + EquityEvery financial event is recorded as a journal entry that debits one account and credits another — the equation always stays balanced.
Account Types
Ledger uses five standard account types. Account code values in Ledger are alphanumeric tags (e.g. CASH-DEFAULT, WALLET-POOL, REVENUE-DELIVERY), not numeric ranges.
| Type | Normal Balance | Seeded examples |
|---|---|---|
| Asset | Debit | Cash, Bank Account, Accounts Receivable, Stripe Clearing |
| Liability | Credit | Accounts Payable, Wallet Liability Pool, Driver Earnings Payable, Tax Payable |
| Equity | Credit | Owner's Equity, Retained Earnings |
| Revenue | Credit | Delivery Revenue, Service Fee Revenue, Subscription Revenue, Other Revenue |
| Expense | Debit | Driver Payout Expense, Payment Gateway Fees, Refunds & Chargebacks, Other Operating Expenses |
Debit-normal accounts (Assets, Expenses): debits increase the balance; credits decrease it. Credit-normal accounts (Liabilities, Equity, Revenue): credits increase the balance; debits decrease it.
See Chart of Accounts for the complete seeded list.
Journal Entries
Every financial event creates one journal entry with:
- A debit account and a credit account
- An amount in the account's currency (stored as integer minor units)
- A type — system-written types include
general(default),manual_entry,revenue_recognition,invoice_payment,gateway_payment, andstorefront_sale. The form picker for manual entries also offersstandard,adjusting,closing,reversing, andopening - A status — currently only
postedis produced by the system; there is no built-in reversal or void action - An
is_system_entryflag —truefor automated entries,falsefor manual ones
Journal entry numbers follow the format JE-00001 (5-digit zero-padded).
Example — customer pays an invoice:
DEBIT Cash (Asset) +$100
CREDIT Accounts Receivable (Asset) +$100 ← decreases the AR balance(AR is debit-normal, so the credit reduces the customer's outstanding balance.)
Wallets
A Wallet is a digital balance tracked in Ledger. Wallets are polymorphic — subject_uuid + subject_type point at the entity that owns the balance:
| Wallet Type | Subject | Use Case |
|---|---|---|
| Driver | Fleet-Ops Driver | Track earnings from completed deliveries |
| Customer | Storefront / Fleet-Ops Customer | Prepaid credit or store loyalty balance |
| Company | Your organization | Operating account, platform revenue |
| User | Individual user | Personal account |
Wallet operations:
- Credit — manual credit, no gateway charge
- Top-Up — gateway-charged top-up via Stripe or QPay
- Transfer — move funds between two wallets — produces two transactions (
transfer_outandtransfer_in) and one journal entry between the two wallet liability accounts - Payout — debit the wallet (e.g., driver earnings payout)
- Freeze / Unfreeze — toggle status. The
is_frozenattribute is a computed accessor derived fromstatus === 'frozen' - Recalculate — recompute the balance from completed transactions
Every wallet operation automatically creates a journal entry:
Top-up: DEBIT Cash → CREDIT Wallet Liability
Payout: DEBIT Wallet Liability → CREDIT Cash
Transfer: DEBIT Wallet A's liability → CREDIT Wallet B's liability (direct, no intermediate)Each wallet has its own ledger account auto-created with code WALLET-{wallet_uuid}. See Wallets.
Invoices
An Invoice is a formal payment request with a lifecycle:
draft → sent → viewed → partial / paid / overdue / cancelled / refunded / void| Status | Meaning |
|---|---|
draft | Created, not yet sent |
sent | Marked as sent |
viewed | Customer opened the public URL |
partial | Some payment received |
paid | Fully paid (balance = 0) |
overdue | Past due date, not yet paid (set by the ledger:update-overdue-invoices console command) |
cancelled | User-cancelled |
refunded | Set when a gateway refund is processed |
void | Set programmatically when a draft invoice is superseded by a new one (e.g., from Fleet-Ops auto-invoicing) |
Key invoice fields:
- Number — auto-generated, format
INV-plus a 6-digit random integer (e.g.,INV-004821); not sequential - Customer — polymorphic reference (
customer_uuid+customer_type); supports Fleetbase contacts, vendors, drivers, and users - Line Items — description, quantity, unit price, tax_rate (decimal 0–100); subtotal, tax, total are computed
- Totals —
subtotal,tax,total_amount,amount_paid,balance(all computed) - Due Date — auto-set from the
due_date_offset_daysinvoice setting (only when non-zero) - Order link — optional
order_uuidlinking back to a Fleet-Ops order - Transaction link — optional
transaction_uuidlinking to the wallet transaction that paid it
When an invoice is created from a Fleet-Ops PurchaseRate, a revenue_recognition journal entry is posted (DEBIT Accounts Receivable → CREDIT Revenue).
When payment is recorded, an invoice_payment journal entry is posted (DEBIT Cash → CREDIT Accounts Receivable).
Transactions
A Transaction is the immutable record of money moving relative to a wallet. Transactions are never edited or deleted — they are the audit trail.
Key fields:
direction—credit(money in) ordebit(money out)status— currently the system only producescompleted(the model also supportspending,failed,voided,reversed,expiredtimestamps if you write them yourself)type— types written by the system aredeposit,withdrawal,transfer_in,transfer_out,earning,payout, andinvoice_paymentamount,fee_amount,tax_amount,net_amount— full cost breakdowngateway— the payment processor that handled this, if anysubject/payer/payee/initiator/context— polymorphic relationships capturing who/what was involved
See Transactions.
Payment Gateways
Gateways are payment processors with encrypted credentials. Ledger ships with three drivers:
| Driver | Capabilities |
|---|---|
stripe | purchase, refund, tokenization, setup_intent, checkout_session, webhooks, sandbox, recurring |
qpay | purchase, refund, webhooks, sandbox |
cash | purchase, refund |
Every gateway interaction creates a GatewayTransaction audit record — used for idempotency (duplicate webhook protection via the isProcessed() check) and debugging.
Third-party drivers can be registered via the PaymentGatewayManager::extend() API.
How It All Connects
Fleet-Ops:
Fleet-Ops Order → PurchaseRate created
→ Ledger PurchaseRateObserver creates a draft Invoice
→ Customer pays via gateway (or you record payment manually)
→ invoice_payment journal entry posted
→ Invoice marked paid; AR Aging clearedStorefront:
Storefront Checkout completes (order.type === 'storefront')
→ Ledger StorefrontOrderObserver creates a storefront_sale journal entry
DEBIT Cash → CREDIT Revenue
(No invoice — the order record itself is the receipt)Wallet flows:
Customer top-up via Stripe → GatewayTransaction logged → Wallet Transaction (deposit, credit) → Journal entry (DEBIT Cash → CREDIT Wallet Liability)
Driver earning payout → Wallet Transaction (payout, debit) → Journal entry (DEBIT Wallet Liability → CREDIT Cash)