Skip to content

Vouchers

The Receipt, Payment and Contra forms under Finance → Vouchers. Each one posts a balanced double-entry journal.

A voucher written from these forms lands pending and waits for someone else (Maker-checker). Once approved it can never be edited or deleted (FIN-031); a mistake is corrected by a reversal and a new entry (Journals).


1. Types

Voucher Debit Credit Used for
Receipt cash / bank account customer, agent or supplier-refund source money coming in
Payment supplier, expense, agent commission or booking cash / bank account money going out
Contra target cash/bank account source cash/bank account a transfer between two of our own accounts

Prefixes: RV, PV, CT. Both forms refuse a group (non-leaf) account, an inactive account, and an account on the wrong side of the entry.

A customer receipt against a booking is a payment, not a voucher

On the Receipt form, a customer receipt with a booking or invoice chosen is sent to record a payment → verify it (POST /finance/payments, or /finance/payments/batch for several bookings). That path mints a receipt number, dates the voucher by the day the money arrived, and requires a second person. It needs finance.payments.record, so a cashier can use it. The on-account receipt endpoints below exist for money that is not yet applied to a booking.

What the form offers. Only the vouchers the user may post are shown, each by the permission its route asks for (ACC-021):

On the form Route Permission
Receipt — customer, against a booking or invoice /finance/payments, /finance/payments/batch finance.payments.record
Receipt — customer, on account (no booking or invoice) /finance/vouchers/customer-on-account-receipt finance.edit
Receipt — supplier refund, business partner /finance/vouchers/supplier-refund-receipt, /agent-on-account-receipt finance.create
Payment, Contra, Debit Note, Credit Note, Settlement, Journal their own routes finance.create

A cashier (finance.view + finance.payments.record) sees only Receipt, with the party type Customer. Post Voucher shows once a booking or invoice is chosen.

The preview says what happens next. A receipt against a booking stays pending until another finance user verifies it. An on-account, supplier refund or business partner receipt stays pending until a second finance user approves its voucher. None of them posts at once.

2. Party sub-ledgers

Every party posts to its own ledger account, never to a control account:

Prefix Party
CUS-xxxxxxxx customer receivable
AGR-xxxxxxxx / AGP-xxxxxxxx agent receivable / payable
SUP-xxxxxxxx supplier creditor

The suffix is the first eight characters of the party id. SUP-* accounts hang under Sundry Creditors → Current Liabilities → Liabilities.

Supplier payments used to miss the sub-ledger

A supplier purchase posted to SUP-xxxxxxxx while the payment for it posted to the 2100 control account, so the supplier never showed as paid (finance audit 2026-09-17, P1 #16). Purchases and payments now both settle the supplier's own ledger. The same fix corrected the airline-block overpayment guard, which compares payments against the block cost instead of subtracting the purchase invoice.

If a party ledger does not exist yet the posting is refused by name — "Receivable ledger X not found — open the party ledger first" — rather than falling back to a control account.

3. Foreign currency

When the amount is not in INR, the posting stores the rate on the header (fxRateUsed) and the original amount, currency and rate on each line. Airline-block and FIT purchase, initial-payment and adjustment vouchers refuse to post without a rate, and group P&L refuses to count a foreign contract that has none (FIN-034).

Pure-INR postings leave the original-currency columns null — nothing was converted.

The realised gain/loss lines carry the real difference and balance. This is still computed in the browser, not the database; FIN-034 records that as a gap.

4. Endpoints

handleFinanceVouchers in src/lib/api.ts.

Method Path Permission
POST /finance/vouchers/customer-on-account-receipt finance.edit
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
POST /finance/vouchers/customer-on-account-receipts/:id/allocate finance.edit

On-account receipts are idempotent on the combination of party, amount, method, source account, reference and date — a retry returns the original with deduplicated: true.

5. Permissions

Action Permission
Post a receipt (supplier refund, business partner), payment or contra finance.create
Post a customer receipt on account finance.edit
Allocate an on-account receipt finance.edit
Record a customer payment (a receipt against a booking or invoice) finance.payments.record
Verify a recorded payment finance.payments.verify
Reject a recorded payment finance.payments.verify or finance.payments.reject
Raise a refund finance.payments.refund
Approve a refund finance.refunds.approve (plus the amount limit)
Approve or reject the resulting voucher finance.journals.approve / .reject (plus maker-checker)

6. Not built yet

Credit and debit notes are posted by hand here. FIN-012 (LAW, CGST s.34) asks for them to be produced automatically against the original invoice when a price drops or a passenger cancels. An issued invoice is already protected — Invoice_guard refuses any edit to its amount, GST, number, booking or issue date — but the note that should follow a change is still a manual step.