Skip to content

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:1887createJournalWithLines rejects entries where Math.abs(debit - credit) > 0.01 or debit <= 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-21698approve-bulk filters 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.

  1. API layerassertAccountingPeriodAllowsPosting() in src/lib/api.ts runs on every createJournalWithLines call (src/lib/api.ts:1935). It reads the period containing the entry date and throws if the status is locked or closed.
  2. Database triggerenforce_accounting_period_lock() fires BEFORE INSERT on JournalEntry (supabase/migrations/20260418150000_journal_period_lock_trigger.sql). It resolves the period from NEW."createdAt"::date and raises check_violation when 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