Finance audit — 17 September 2026
Five independent audits of the finance module and its connections, run on integration/ops-upgrade (code as on disk, read-only). Lenses: (1) posting rules and double entry, (2) finance ↔ operations, (3) reports and reconciliation, (4) controls and tax compliance, (5) screens, workflow and intelligence. Line references are to src/lib/api.ts unless stated and will drift as Wave 1 merges; search by handler name.
Not legal or tax advice. Items marked [CA] need the company's Chartered Accountant.
Verdict
The finance module has broad features but its numbers cannot be relied on and its controls can be bypassed:
- Books: revenue is booked when a booking is created (not at tour completion, FIN-001); cancellations reverse entries more than once; foreign-currency purchases are posted without conversion; air seat cost never reaches the P&L; realised FX is always zero; posted entries are edited and hard-deleted.
- Money vs operations: operational screens use a stored
Booking.balanceAmountthat is wrong for rate-sheet groups (always 0 → tickets auto-cleared), after refunds (refund shows as due) and after "mark paid" (cash that never reaches the books). - Reports: silently truncated at 1,000 ledger lines; dated by entry time, not voucher date; opening balances counted twice; GST summary ignores cancellations; locked periods can still change.
- Controls: maker-checker is enforced by the database in one place only; receipts can be saved as already verified; anyone signed in can approve B2B cancellations; HR holds every finance permission; TCS is not implemented; tax invoices lack mandatory fields.
- Screens: one 14,000-line page for every role; the confirmation dialog is not used; "Approve all" and "Clear all ledger data" are one click; the intelligence panel reports "no FX exposure" for a business paying SAR; the AI copilot sends customer names and balances to outside providers from browser-supplied data.
Findings by severity
P0 — stop now (money can be lost, misstated or approved without authority)
| # | Finding | Rules | Audit |
|---|---|---|---|
| 1 | B2B cancellation approve/reject has no permission check; table policy USING(true) — any signed-in user, including customers, can approve |
ACC-020, FIN-032 | 4 |
| 2 | Receipts saved as verified from 5 staff screens; API accepts client status and verifiedBy; cashier can self-verify |
FIN-032 | 4, 5 |
| 3 | Journal approval maker-checker is browser-only; DB allows finance.edit to approve; reversals auto-approve regardless of original |
FIN-032, FIN-031 | 1, 4 |
| 4 | ADMIN_HR (and IT_ADMIN) hold every finance permission and can grant access | ACC-012 | 4 |
| 5 | Posted entries are mutable: payer/agent change rewrites posted lines; hard deletes on block/FIT/supplier/opening balance/account delete; ledger reset; rebuild-all reposts revenue (gated by inventory.create) |
FIN-031, AUD-002 | 1, 2, 4 |
| 6 | Mark-paid creates verified payments with no journal, excludes GST and clears tickets | FIN-033 | 1, 2 |
| 7 | Rate-sheet group bookings have totalAmount = 0 → balance 0 → tickets cleared, readiness green, group P&L revenue 0 |
FIN-033 | 2 (fixed for ticket clearance in 1D) |
| 8 | Payment refund: no cap, any status, wrong account; verified→rejected keeps the receipt journal | PRC-030 | 1, 2 |
| 9 | Periods and years: any staff user (incl. AGENT) can reopen periods; lock covers only journal inserts; year close is a label | FIN-031 | 3, 4 |
| 10 | Reports truncated at 1,000 rows (BS, P&L, cash flow, statements, GST, AP aging, bank match) | FIN-033 | 3 |
P1 — books are wrong
| # | Finding | Rules | Audit |
|---|---|---|---|
| 11 | No Advances account; revenue at booking create / group-invoice post; nothing at completion | FIN-001, LC-041 | 1 |
| 12 | Cancellation reversals stack (passenger → booking → delete); refund override ignored; retained charge never booked as income; GST reversed on a different basis than charged | CXL-*, PRC-020 | 1, 2 |
| 13 | Booking edit + group invoice double revenue; group invoice posting skipped for the whole group if one legacy booking exists | FIN-030 | 1, 2 |
| 14 | Foreign currency: block, FIT, ground, food purchase, group invoice and booking amounts posted without INR conversion; realised FX always 0; revaluation never auto-reversed | FIN-034 | 1, 2 |
| 15 | Air seat cost never expensed for package passengers; periodic closing-stock entry double-counts perpetual stock | FIN-030 | 1 |
| 16 | Non-TDS supplier payments post to the 2100 control account while purchases credit supplier sub-ledgers → suppliers never show paid | FIN-030 | 1 |
| 17 | Reports use createdAt, period locks use entryDate; opening balances double-counted; BS from filter drops history |
FIN-033 | 3 |
| 18 | Transfer moves only the base rate, auto-approved, no GST, no group-invoice credit note | LC-030 | 1, 2 |
| 19 | Partner-portal bookings, agent invoices, visa fees, ticket void/refund, incidents post nothing | FIN-030 | 1, 2 |
| 20 | Hotel edit reposts to expense instead of stock; B2B cancellation hardcodes 18% GST; airline filing credits stock with entered amounts, not seat cost | FIN-030 | 1 |
| 21 | Journal header and lines inserted non-atomically (browser and Razorpay webhook); posting failures swallowed | CXL-001 | 1, 2 |
| 22 | Customer/agent/portal ledgers built from operational tables, each on a different basis | FIN-033 | 2, 3 |
| 23 | Group and inventory P&L double-count and mix currencies; group P&L ignores invoices and credit notes | FIN-033 | 2, 3 |
P1 — statutory
| # | Finding | Rules | Audit |
|---|---|---|---|
| 24 | TCS (Income-tax Act 2025 s.394(1)) not implemented anywhere [CA] | FIN-020 | 4 |
| 25 | No GST on advances; no receipt voucher (r.50) or refund voucher (r.51) | FIN-011 | 4 |
| 26 | Group invoice print has no GSTIN/SAC/place of supply; booking invoice lacks GSTINs and tax split | FIN-010 | 4 |
| 27 | Cancelling an issued invoice removes it from GSTR-1 instead of raising a credit note | FIN-012 | 4 |
| 28 | GSTR-3B splits all GST as CGST/SGST (IGST never); GSTR-1 repeats taxable value across instalment invoices; B2CL threshold likely stale [CA] | FIN-010 | 3, 4 |
| 29 | 5% scheme with input tax credit still claimed on supplier GST [CA] | FIN-010 | 4 |
| 30 | TDS: only 194C/J/Q (no 194H commission, 195, no-PAN rate); YTD threshold from browser; no deposit journal; 26Q not in RPU format | FIN-021 | 4 |
| 31 | Invoice/receipt numbers: GINV/AINV non-atomic, counters editable, number functions callable anonymously, no FY series | FIN-010 | 4 |
P2 — workflow, screens, intelligence
| # | Finding | Rules | Audit |
|---|---|---|---|
| 32 | useConfirm unused in finance; ~40 state-changing actions without Yes/No; four window.confirm; "Approve all" and "Clear all ledger data" one click |
UX-001 | 5 |
| 33 | Only 13 permission gates across the finance page; ~40 finance permissions unused in the UI; every role lands on Accounts | ACC-012, UX-010 | 5 |
| 34 | Eight ways to record a receipt with different behaviour; record-payment drops date and notes; voucher receipt loops non-atomic posts | FIN-032 | 5 |
| 35 | Landing KPIs wrong (liquid funds = lifetime receipts; payables 0 until Reports opened) | INT-002 | 5 |
| 36 | FX intelligence reads wrong field names → always "no exposure"; supplier checks fed an empty list; forecast assumptions invented; GST calendar wrong | INT-002 | 5 |
| 37 | Copilot context built in the browser (injectable), PII to four external AI providers without masking or logging, only finance.view checked, data truncated while claimed complete |
INT-006, AUD-020 | 5 |
| 38 | Bank reconciliation: no reference matching or confidence, pending payments matchable, duplicate matches across imports, manual match unvalidated, no statement-vs-ledger proof | FIN-033 | 3, 5 |
| 39 | 47 swallowed errors (outage looks like "no data"); tabs cached for the session; deep links broken | PLT | 5 |
| 40 | Print uses the wrong brand red; no amount in words; negatives shown as ₹-1,234 |
UX | 5 |
Target design
- One posting function per business event, in the database. The operational change and its journal are written in one transaction; the actor comes from the session; approver ≠ maker; reversals link to the original, follow its status, and can never exceed what remains. Posted entries are never updated or deleted — corrections are reversals, credit notes or debit notes. (FIN-030/031/032, CXL-001)
- Posting rulebook (FIN-030). Advances on receipt (2150 Advances from Customers, with GST on advance and TCS); revenue at tour completion (principal 4000 / agent 4100, FIN-003); refunds via 2160 Refund Payable; retained cancellation charges to 4200 Cancellation Income; purchases to Stock-in-Hand at INR on the transaction date and supplier sub-ledgers only; consumption Dr 5100–5400 / Cr 1310; Razorpay via a clearing account with gateway fees. Full table in audit 1 §E — to be copied into
07-finance-accounting-tax.mdas FIN-030 detail once the CA answers FIN-002/003/010. - Balances come from the ledger (FIN-033). Booking paid/outstanding, invoice balances, party statements, portal balances, ticket clearance and readiness all read one ledger-derived view per booking/invoice/party. Stored totals become caches maintained by the database, never written by the browser.
- Dimensions on journal lines. Real foreign keys for booking, passenger, group, invoice, payment, block/FIT, supplier and partner, so group and inventory P&L are sums of ledger lines.
- Reports as database functions over approved lines dated by
entryDate(IST), paged, with standard filters, comparatives and drill-down report → account → voucher → source document → audit trail. - Periods and years. Lock trigger on every ledger-affecting table; unlock needs a second approver and a reason; year close posts closing entries and carries balances forward.
- Controls. Role bundles re-seeded (cashier records, accountant prepares and verifies others', finance manager approves, HR and IT hold no finance rights, auditor read-only); approval limits by amount (ACC-030); MFA required for finance functions.
- Tax. TCS module; GST on advances with receipt/refund vouchers; CGST/SGST/IGST by place of supply; statutory invoice fields; credit notes instead of cancelling issued invoices; per-FY atomic numbering; TDS rate table with 194H/195/no-PAN rate, database YTD, challan with deposit journal, RPU export.
- Nightly drift check (AUD-010). Unbalanced vouchers;
entryDatevscreatedAt; opening balances; control vs sub-ledger vs operational balances; verified payments without approved journals; GST and TDS ledgers vs reports; supplier ledger; bank vs ledger; changes inside locked periods; group P&L vs tagged lines; stored counters. Results go to a drift register; repairs only with management approval. - Finance workspace (UX-010/011). Role dashboards (cashier, accountant, finance manager, CEO, auditor); left rail Books · Receivables · Payables · Tax · Treasury · Close · Reports; one receipt composer; one approvals inbox; live Dr/Cr preview; 360 money panels on customer, partner, supplier and group;
useConfirmeverywhere; URL-driven state. - Smart finance (INT-150..154). Receipt auto-allocation, bank matching with confidence, anomaly feed, commitment-based cash forecast, FX exposure, dunning by risk, tax and close assistant — all computed in the database; AI only narrates, with PII masked and every figure linked to vouchers.
Remediation plan
| Workstream | Scope (finding #) | When |
|---|---|---|
| 1B Money integrity ✅ merged | Done: 1, 2, 6, 10 (TB/P&L/BS/cash flow/day book), refund caps and approval (8), immutability and removal of reset/force-delete/mark-paid (5), period/year writes (9), rebuild gate, entryDate in reports (17), realised FX (14), FinanceConfig read policy. Still open → F1–F4: direct insert of approved journals (3); reversal of pending originals auto-approved (3, 12); 1,000-row cap in statements/ledgers/GST/AP aging/settlement/bank match (10); opening balances double-counted (17); GST summary excludes reversals (28); permission checks on aging, receivables-payables, pending settlements, /finance/ledger, customer ledger, pl-summary, inventory P&L; receipt voucher loop and dropped date (34); Razorpay (21); block overpay guard; supplier payments to 2100 (16); SupplierTransaction/TDS delete policies; GINV/AINV numbering (31); automatic credit/debit notes (27); archive instead of delete for blocks/FIT |
Wave 1 ✅ |
| 1D Tickets/visa/comms (done) | 7 for ticket clearance (money-based check incl. group invoices) | Wave 1 ✅ |
| F1 Controls hardening ✅ merged | Done: direct insert of approved journals and reversal status/cap (3); role bundle re-seed with SUPER_ADMIN / CHARTERED_ACCOUNTANT (4); period and year write permissions, supplier-transaction and TDS immutability (9); GINV/AINV numbering and counter editing (31); supplier payments to the sub-ledger (16); opening balances double-counted (17); GST summary excluding reversals (28); the 1,000-row cap on statements, ledgers, GST, AP aging, settlement and bank match (10); permission checks on aging, receivables-payables, pending settlements, /finance/ledger, customer ledger, pl-summary, inventory P&L; receipt date and the receipt-voucher loop (34); block overpay guard; approval limits for refunds and manual journals (ACC-030). Still open → F2–F4: automatic credit/debit notes (27), Razorpay webhook (21), archive instead of delete for blocks/FIT, MFA for finance functions, approval limits for discount / supplier payment / fare / seat release / write-off, second approver above ₹2,00,000 |
Wave 1 follow-up ✅ |
| F2 Posting engine | 11–16, 18–21 — posting functions per event, rulebook, dimensions, FX, completion revenue | Wave 5 tax/closeout, starts after CA answers; defects 12–16, 20–21 fixed earlier where they don't depend on FIN-002/003/010 |
| F3 Ledger-derived balances | 7 (remaining), 22, 23 — views, portal/staff/ticket/readiness on one basis | With F2 |
| F4 Reports and close | 10, 17, 38 — report functions, year close, bank rec rebuild, drift register | Wave 4 (finance workspace) |
| F5 Tax | 24–30 | Wave 5, after CA review |
| F6 Finance workspace | 32–35, 39, 40 — split page, role dashboards, composer, approvals inbox, confirmations | Wave 2 (confirm + dashboards), Wave 4 (split) |
| F7 Finance intelligence | 36–37, INT-150..154 — server-side copilot context with masking and permission checks; fix FX/supplier/forecast/GST calendar | Wave 2 (copilot safety P0), Wave 4 |
Questions for the Chartered Accountant
- FIN-002: Ind AS or AS for Alhuda Travels; FIN-003: principal or agent per product (packages, air-only, visa-only, hotel-only, B2B seat sales).
- FIN-010: GST scheme for Hajj/Umrah packages (5% without ITC vs 18% with ITC) and whether current ITC claims must be reversed.
- FIN-020: TCS under s.394(1) — rate, threshold, collection timing (gross-up vs within package price), treatment of B2B sales to agents, refunds, return and certificate forms under the 2025 Act.
- TDS sections and rates under the 2025 Act numbering; commission (194H equivalent) and payments to Saudi suppliers (195 equivalent).
- GSTR-1 B2C large threshold and required tables (at/atadj/doc_issue); e-invoicing applicability by turnover.
- Time limits for credit notes and treatment of cancellations after invoicing.