Finance module — overview
The authoritative map of the Finance module: data model, workflow controls, and where to find each feature in the UI.
The Finance module is the most complex surface in the ERP. It sits on a classical double-entry ledger, enforces maker-checker segregation of duties on journal approvals, and blocks writes into locked accounting periods via a database trigger. Everything else — AR/AP aging, bank reconciliation, GST, TDS, vouchers, invoices, reports — is built on top of those three primitives.
1. Double-entry model
Every posting in Finance is a JournalEntry with ≥ 2 JournalLine rows.
The invariant enforced in code is Σ debit = Σ credit:
src/lib/api.ts:1887—createJournalWithLinesrejects entries whereMath.abs(debit - credit) > 0.01ordebit <= 0.src/lib/api.ts:1925-1933— the balance + minimum-lines check.
erDiagram
JournalEntry ||--o{ JournalLine : has
JournalEntry {
uuid id PK
text voucherNo
text referenceType
text referenceId
text currency
numeric fxRateUsed
text status "pending|approved|rejected"
bool isReversal
uuid reversalOfEntryId FK
uuid createdBy
timestamptz createdAt
}
JournalLine {
uuid id PK
uuid entryId FK
text accountId FK
numeric debit
numeric credit
numeric originalDebit
numeric originalCredit
text originalCurrency
numeric fxRate
}
Voucher numbers
Voucher prefixes are configurable via FinanceConfig (see
src/lib/api.ts:1940-1956). The shape is always
${prefix}-${YYYYMMDD}-${6-hex}. Known prefixes: RV (receipt),
PV (payment), SV (sales voucher), CN (credit note), DN (debit
note), CT (contra transfer), SN (settlement), REV (reversal),
JV (manual journal fallback).
2. Maker-checker (segregation of duties)
A user who creates a journal cannot approve, reject, or reverse it themselves. The creator must route the entry to a different approver.
- Enforcement in code:
src/lib/api.ts:22030-22039— approve-self guard.src/lib/api.ts:22079-22088— reject-self guard.src/lib/api.ts:21894-21902— reverse-self guard.src/lib/api.ts:21675-21698—approve-bulkfilters self-authored entries before the UPDATE.- Permission seeds:
supabase/migrations/20260418150000_journal_maker_checker_overrides.sql.
Override permissions (break-glass)
Two permissions let super-admins bypass the maker-checker rule — both are
seeded only to CEO, GM, and IT_ADMIN.
| Permission | Effect |
|---|---|
finance.journals.approve_own |
Bypass self-approve / self-reject guard. |
finance.journals.reverse_own |
Bypass self-reverse guard. |
No hardcoded role checks
The codebase does not contain if role === 'CEO' branches to
grant these bypasses. Super-admin roles hold the permission rows
explicitly (see CLAUDE.md → Permissions). If you grant a new role
these overrides, seed a migration — do not patch the handler.
3. Accounting-period lock
The AccountingPeriod table has a status of open, locked, or
closed. Writes are blocked at two layers.
- API layer —
assertAccountingPeriodAllowsPosting()insrc/lib/api.tsruns on everycreateJournalWithLinescall (src/lib/api.ts:1935). It reads the period containing the entry date and throws if the status islockedorclosed. - Database trigger —
enforce_accounting_period_lock()firesBEFORE INSERTonJournalEntry(supabase/migrations/20260418150000_journal_period_lock_trigger.sql). It resolves the period fromNEW."createdAt"::dateand raisescheck_violationwhen the period is locked or closed.
Reversals respect the lock
A reversal is a new posting — if the reversal date falls in a closed period, the trigger blocks it. The operator must either back-date the reversal into an open period or unlock the target period first. This is intentional (see the trigger comment).
Effective entry date
JournalEntry has no dedicated entryDate column. The API threads the
caller-supplied entryDate into createdAt at insert time
(src/lib/api.ts:1937). The trigger uses NEW."createdAt"::date for the
period lookup.
4. Finance page — tab structure
All URLs below live under /finance. The tab bar is defined in
src/pages/finance/Finance.tsx:4658-4683.
| Tab | URL query | Purpose |
|---|---|---|
| Intelligence | ?tab=intelligence |
AI co-pilot + insight cards. See FinanceIntelligencePanel.tsx, FinanceCopilotChat.tsx. |
| Accounts | ?tab=accounts |
Chart of accounts CRUD + ledger drill-down. ChartOfAccounts.tsx. |
| Vouchers | ?tab=vouchers |
Receipt / Payment / Contra vouchers. See vouchers.md. |
| Transactions | ?tab=transactions |
Payments + invoices listings. |
| Journal | ?tab=journal |
Manual double-entry ledger entries. See journals.md. |
| Reports | ?tab=reports |
Trial Balance, P&L, Day Book, aging, GST, TDS, etc. See reports.md. |
| Reconcile | ?tab=reconcile |
Bank statement import + match. See bank-reconciliation.md. |
| Approvals | ?tab=approvals |
Finance-tier review queue (payments, journals, bookings). |
| Settings | ?tab=settings |
Finance config (prefixes, GL defaults), periods, years, PIN. |
PIN gate
High-risk actions go through FinancePinGate.tsx — a second-factor
local PIN. Configured via finance_pin migration; endpoints under
/finance/pin/* in src/lib/api.ts.
5. Feature → permission map
The authoritative catalog is docs/PERMISSIONS.md. Summary as it applies
to Finance:
| Feature | Permission | Notes |
|---|---|---|
| View Finance route + reports | finance.view |
Parent route gate. |
| Create journals, vouchers, payments | finance.create |
|
| Reverse journal, edit config, reconcile | finance.edit |
|
| Record a payment receipt | finance.payments.record |
|
| Approve / reject journal | approvals.approve |
Plus maker-checker rule. |
| Bulk-approve journals | approvals.approve |
See /finance/journals/approve-bulk. |
| Approve / reject own journal | finance.journals.approve_own |
Override, super-admin only. |
| Reverse own journal | finance.journals.reverse_own |
Override, super-admin only. |
| Trial Balance (view / export) | finance.reports.trial_balance.view / .export |
|
| Day Book (view / export) | finance.reports.day_book.view / .export |
|
| Balance Sheet (view / export) | finance.reports.balance_sheet.view / .export |
|
| P&L, Cash Flow (view / export) | finance.reports.profit_loss.view / .export |
Cash Flow reuses the P&L permission. |
| Group P&L (view / export) | finance.reports.group_profit_loss.view / .export |
|
| AR / AP aging (view / export) | finance.reports.aging.view / .export |
Same gate for both. |
| Receivables & Payables | finance.reports.receivables_payables.view / .export |
|
| GST Summary / Returns | finance.reports.gst_summary.view / .export |
|
| Stock Status | finance.reports.stock_status.view / .export |
|
| Group Status | finance.reports.group_status.view / .export |
|
| Settlements, Cancellations, Pending Refunds | finance.reports.settlements.view / .export |
|
| TDS rate master + deductions (view) | finance.tds.view |
|
| Apply TDS on a supplier payment | finance.tds.deduct |
|
| Export 26Q CSV | finance.tds.export |
|
| Create / lock / close accounting period | finance.periods.create / .lock / .close |
|
| Create / close financial year | finance.years.create / .close |
|
| Rebuild ledger | finance.ledger.rebuild |
|
| Stock adjustment (opening / closing) | finance.stock_adjustment.post |
|
| Edit finance config (prefixes, GL defaults) | finance.config.edit |
See the per-feature pages for endpoint-level gating.
6. Where things live (code map)
| Concern | Path |
|---|---|
| API handler monolith | src/lib/api.ts |
| Finance page (UI) | src/pages/finance/Finance.tsx |
| Journal helper (createJournalWithLines) | src/lib/api.ts:1887 |
| Chart of accounts UI | src/pages/finance/ChartOfAccounts.tsx |
| Voucher detail dialog | src/pages/finance/VoucherDetailDialog.tsx |
| Receipt allocation dialog | src/pages/finance/AllocateReceiptDialog.tsx |
| Bank reconciliation UI | src/components/finance/BankReconciliationPanel.tsx |
| Bank statement parser | src/lib/bankStatementParser.ts |
| Intelligence / co-pilot | src/pages/finance/FinanceIntelligencePanel.tsx, FinanceCopilotChat.tsx |
| GSTIN helpers | src/lib/gstin.ts |
| GSTR-1 / 3B payload builder | src/lib/gstReports.ts |
| GSTN portal client (stub) | src/lib/gstnApiClient.ts |
| TDS calculator + 26Q CSV | src/lib/tds.ts |
| Finance math | src/pages/finance/financeMath.ts |
| Finance posting helper | src/lib/financePosting.ts |
| Finance operations | src/lib/financeOperations.ts |
| Test coverage | src/lib/api.finance.test.ts, src/lib/api.finance.audit.test.ts |