Finance
The finance module keeps the books: receipts and refunds, double-entry vouchers, customer and group invoices, supplier ledgers, GST and TDS, bank reconciliation, period locks and reports.
The business rules it implements are in 07 · Finance, accounting and tax and 08 · Access control. This section describes the software; where the two disagree, the rule is right.
The controls are in the database
Every control on this page — maker-checker, caps, immutability, period locks, approval
limits — is a SECURITY DEFINER function or a trigger in Postgres. src/lib/api.ts
runs in the browser and only shapes the screen
(ACC-001).
Start here
- Overview — the five controls that hold the module up, and the tab map.
- Receipts and refunds — recording money, verifying it, refunding it.
- Maker-checker — who may approve what, and why nobody approves their own work.
- Vouchers — what a voucher is, what may never change about one.
- Journals — manual entries, approval, reversal.
- Reports — the catalogue, and why reports can no longer be cut short.
- Numbering and approval limits — document counters, money limits.
Reference
- Chart of accounts — the
Accounttree and per-party sub-ledgers. - Double-entry engine — how a posting is written.
- Audit trail — what is recorded and who may read it.
- AR aging · AP aging · Bank reconciliation
- GST · TDS · India compliance
- Invoices · Permissions matrix
- Workflows — task-by-task procedures.
Unposted accounting entries
At the top of the Finance page, above the tabs, sits a card listing every posting that failed and could not be undone — a business record that exists with no journal behind it. It is empty on a healthy system and renders nothing at all when there is nothing open.
Most posting failures never reach it. A booking, a group expense, a hotel, a hotel room assignment, an airline block and a FIT all write their business row and their voucher in one database transaction, so a refused posting leaves nothing behind and the operator is told what went wrong. The card is for the cases where the operational half has already committed and cannot be put back — a cancelled passenger whose seats are released and visa case closed, a transferred passenger, a B2B sale whose seats have left the block. The row carries the operation, what it was posting against, the error, and the exact payload.
Retry re-posts the accounting lines the failure recorded, through the same database path a fresh posting takes, and closes the row when they land. The attempt is recorded either way, so a row that keeps failing shows how many times it has been tried and why it still will not post. Where the failure carries no lines — rows from before Retry existed, and postings that failed before their accounts could be resolved — the card says Post by hand rather than offering a button that cannot work.
Retry and "Mark resolved" both need finance.create and a reason. The failure record itself
can never be edited or deleted by anyone — see
FIN-030 and FIN-031 and
the API reference.
How fast the finance page loads
Each finance tab asks the database once (PRF-010). From Srinagar each request to the Mumbai database costs about 0.24 s before any work is done, so the number of requests made one after another is what you feel.
| You open | Requests the database answers before you see it |
|---|---|
/finance (the page, and the Chart of Accounts it lands on) |
one for the finance data, sent at the same moment as the PIN check; the bookings list goes beside it |
| Transactions, Journal, Approvals, Reports | one each |
| A voucher, an account's ledger | one each |
| Profit & loss, cash flow, day book, receivables & payables | one each |
Before this, opening /finance made 83 requests, 44 of them one after another (about ten
seconds), and each tab three to ten more in a row. Coming back to the page within ten
minutes shows the last answer at once and refreshes it behind the scenes.
What each person sees is unchanged: every section is checked in the database against the same permission its screen always needed and the same table policies, and a section you may not see is left out. Nothing on this page writes anything by being opened.
Deliberately not changed yet:
- The first load still reads every receipt and every booking (the receipt allocator and the queues index into them). That is the bookings list's own read, not the finance call.
- The account statement, group profitability and GST summary reports still take three to five requests in a row.
- A payment account's balance on the Chart of Accounts counts every voucher line on the account, pending ones included, exactly as it did before; the trial balance counts approved lines only. The two can differ while vouchers wait for approval.
- The intelligence and bank-reconciliation tabs download their code when first opened.
What finance does not do yet
Stated plainly so nobody plans around a gap:
| Not built | Rule |
|---|---|
| Money before travel held as Advances from Customers, becoming revenue at tour completion | FIN-001 |
| Receipt vouchers on advances, and GST on advances | FIN-011 (LAW) |
| TCS on overseas tour packages (2% from 1 April 2026) | FIN-020 (LAW) |
| Automatic credit and debit notes against an issued invoice | FIN-012 (LAW) |
| Customer ledger and balances derived from ledger entries rather than stored totals | FIN-033 |
| FX gain and loss computed in the database rather than the browser | FIN-034 |
| A second approver above ₹2,00,000 on a refund | ACC-030 |
Razorpay online payment from the web customer portal (the app has it: API → Online payment; so do the partner web portal, PTR-093, and the WhatsApp payment link /pay/:token, COMM-034) |
Wave 3 |
Open questions for the company's Chartered Accountant — which accounting standard applies, whether Alhuda is principal or agent per product line, and which GST scheme the company uses — are FIN-002, FIN-003 and FIN-010.
Where to look
| Concern | Path |
|---|---|
| Finance page (UI) | src/pages/finance/Finance.tsx |
| Route handlers | src/lib/api.ts |
| Posting helpers | src/lib/financePosting.ts, src/lib/financeOperations.ts |
| Money integrity (Wave 1B) | supabase/migrations/20260918110000_money_integrity.sql |
| Finance controls (F1) | supabase/migrations/20260919100000 … 20260919100900 |
| Tests | supabase/tests/money_integrity.sql, supabase/tests/finance_controls.sql, src/lib/api.finance*.test.ts |
| What was found and fixed | Finance audit, 2026-09-17 |