Vouchers
Receipt, Payment, and Contra voucher forms. The operator's day-to-day
entry point into the ledger — every voucher posts a balanced
double-entry journal under the hood via
createJournalWithLines.
1. Types
| Voucher | Debit | Credit | Use case |
|---|---|---|---|
| Receipt | Cash / Bank account | Customer / Agent / Supplier-refund source | Money coming in from a customer, agent, or as a supplier refund. |
| Payment | Supplier / Expense / Agent-commission / Booking | Cash / Bank account | Money going out to a supplier, as an expense, commission, or booking-side payout. |
| Contra | Cash/Bank target account | Cash/Bank source account | Transfer between two cash/bank accounts (e.g. bank → petty cash). |
All three share one guarantee: debits = credits, enforced by
createJournalWithLines (src/lib/api.ts:1929-1933).
2. Receipt vouchers — smarter forms
PRs #99 and the #120 lineage reshaped the receipt voucher forms so the UI blocks invalid combinations up-front instead of failing at post time.
Receipt sources
The form supports four receipt flavours
(src/lib/api.ts:11125-11128):
| Path | Purpose |
|---|---|
/finance/vouchers/customer-on-account-receipt |
Customer pays on account (not yet applied to a specific booking). |
/finance/vouchers/agent-on-account-receipt |
Agent pays on account. |
/finance/vouchers/agent-receipt-allocate |
Apply an existing on-account agent receipt to a booking / invoice. |
/finance/vouchers/supplier-refund-receipt |
Supplier refunds us (money in on a purchase-side ledger). |
Source account validation
The receipt source account (the account we credit on payment / debit on refund) now goes through strict validation:
- Must be a leaf — group accounts rejected.
- Must be the correct side — a supplier-refund receipt can't debit a revenue account, etc.
- Must exist + be active at post time.
Source account column
supabase/migrations/20260418123000_payment_source_account.sql adds
Payment.sourceAccountId TEXT so a receipt/payment can explicitly
carry its cash/bank account rather than relying on lookup by side
effect. This unlocks:
- Cash drawer audit trails (cashier X, drawer Y).
- Multi-bank setups where a payment could otherwise ambiguously hit any bank account.
3. Payment vouchers
Mirror of receipt — same shape, money flows the other way. Key paths:
- Supplier payments — post as
referenceType = 'supplier_payment'; the voucher prefix isPV(src/lib/api.ts:1954). - Booking-side payouts (airline refunds, group cost outflow) — pick the appropriate reference type so drill-downs work.
- TDS toggle — planned on the supplier-payment form (see TDS); not yet wired.
4. Contra vouchers
Voucher prefix CT (src/lib/api.ts:1952). referenceType =
'contra_transfer'. Two lines only: debit target, credit source. The
form ensures both accounts are cash/bank leaves.
5. Multi-currency — FX capture
When the source account or the voucher amount is in a non-INR currency, the posting captures both the header-level rate and per-line original amounts. See Journals → FX rate capture.
Voucher form UX:
- User enters amount + picks source currency.
- Form looks up today's rate from
ExchangeRate(or uses a manually-set rate ifisManual = true). - On post,
createJournalWithLinesreceivesfxRateUsed,originalAmount, and per-lineoriginalDebit/Credit/Currency/fxRate. - The INR line amounts derive from the rate, and the original fields pin the audit trail.
Pure-INR postings
When both legs are INR, all four original-currency columns stay
NULL. No conversion happened.
6. UI
Finance page → Vouchers tab (Finance.tsx:4660,
value="vouchers"). Three sub-tabs for Receipt / Payment / Contra.
Each form:
- Gates the "Post" button behind
finance.create. - Runs client-side validation (balance, account side, required fields).
- Submits to the per-voucher endpoint.
- On success, shows the generated voucher number and routes the user
to the voucher drill-down (
VoucherDetailDialog.tsx).
7. Endpoints
Handled by handleFinanceVouchers at src/lib/api.ts:11124. The core
receipt/payment paths call through to createJournalWithLines with
the appropriate reference type + prefix.
| Method | Path | Permission |
|---|---|---|
POST |
/finance/vouchers/customer-on-account-receipt |
finance.create |
POST |
/finance/vouchers/agent-on-account-receipt |
finance.create |
POST |
/finance/vouchers/agent-receipt-allocate |
finance.payments.record |
POST |
/finance/vouchers/supplier-refund-receipt |
finance.create |
POST |
/finance/vouchers/debit-note |
finance.create |
POST |
/finance/vouchers/credit-note |
finance.create |
GET |
/finance/vouchers/customer-on-account-receipts/:customerId |
finance.view |
All write endpoints route through the journal state machine — a
freshly-posted voucher lands in pending status unless it's a
system-reversal (see
journals.md).
8. Permissions
| Action | Permission |
|---|---|
| Post receipt / payment / contra | finance.create |
| Record a payment receipt against an invoice | finance.payments.record |
| Refund (issue money out) | finance.edit |
| Approve / reject the resulting journal | approvals.approve (plus maker-checker) |