Knowledge baseFinance

Finance

The finance module covers the full money cycle for a scheme — budgets, levy runs, payments and arrears, bank reconciliation, expenses, and year-end statements. It is built around two principles: money math is allocated by unit entitlement to the exact cent, and the bank ledger is immutable and import-only so the audit trail can be trusted.

The money model

Three concepts sit underneath everything else:

  • Financial year — each scheme runs on financial years. Budgets, levies, expenses, and statements are all scoped to a year. A year is eventually closed, which locks it for reporting.
  • Funds — money is tracked across separate funds, with the administrative fund (day-to-day running costs) and the capital works fund (long-term sinking fund) kept distinct. Balances never co-mingle: a fund balance is opening + credits − debits for that fund alone.
  • Unit entitlement (UE) — every lot carries a unit entitlement, and levies are split in proportion to it. Before a levy run can be generated, the sum of lot entitlements must equal the scheme's recorded total; a mismatch returns HTTP 422 rather than producing a silently wrong allocation.

Access tiers

Finance uses a three-tier access model, distinct from the platform billing role. Every finance endpoint resolves the caller into one of three levels:

TierWhoCan do
MemberAny scheme membership.Read finance data. Owners are scoped to their own lot's levy position and payments.
WriterAny committee role, or a member flagged is_financials_admin.Operational work: draft budgets, generate a levy draft, record payments and expenses, import and reconcile bank statements.
Financials adminOnly members flagged is_financials_admin.Sensitive actions: approve a budget, issue a levy run, reverse a payment, and close a financial year. Each writes an entry to the audit log.

is_financials_admin is separate from is_billing_admin (which governs the scheme's own Stripe subscription). The founder is bootstrapped as a financials admin during onboarding; the role is granted to others by a committee member and the grant is itself audited.

Budgets

A budget is the planned income and expenditure for a financial year, broken down by fund. The lifecycle is draft → approved:

  • A writer drafts and edits the budget while it is in draft.
  • A financials admin approves it. Approval is the gate that lets a levy schedule be built from the budget, and it is recorded in the audit log.

Levies

A levy schedule turns an approved budget into the instalments that owners actually pay. Generating a schedule:

  1. Takes the budgeted amount per fund and divides it into instalments (e.g. quarterly).
  2. Allocates each instalment across lots by unit entitlement. The allocator sums to the exact cent — any rounding residual is assigned to the largest-entitlement lot so the parts always reconcile to the whole.
  3. Produces the individual levy charges — one row per lot per instalment — that drive each owner's account.

A schedule is drafted first (so it can be reviewed) and then issued. Issuing is a financials-admin action: it commits the charges, makes them payable, and refreshes each lot's financial status. Until issued, no charge affects an owner's balance.

Payments & arrears

Payments are recorded against a lot and applied to its outstanding charges. Two rules keep the ledger honest:

  • Payments are reversed, never deleted. A mistaken payment is soft-voided with a reason and an audit entry, leaving the original record visible. Reversal is a financials-admin action.
  • Manual money observations are payments, not bank rows. A cheque or cash receipt is entered as a levy payment; nobody hand-types a row into the bank ledger (see below).

Arrears are derived from unpaid charges. After a payment is recorded or reversed, and after a levy run is issued, each lot's financial status is recalculated — a lot in arrears is marked non-financial on its membership, which affects voting eligibility. A daily job re-checks every scheme. An explicit override is respected where set, and the recalculation never touches a separate voting suspension.

Bank reconciliation

The bank ledger is deliberately immutable and import-only. Bank transactions are created only by importing a statement (CSV or OFX) against a bank account; a database trigger blocks deletes and edits to any financial field on a bank row. The only thing that can change after import is a transaction's reconciliation status.

The flow is:

  1. Add a bank account for the scheme.
  2. Upload a statement file. A preview step shows what will be imported before it is committed.
  3. For each imported transaction, either match it to a payment or expense (reconcile) or ignore it. Matching is what links the real-world bank movement to the scheme's records.

Because money observations live in payments and expenses while the bank statement is the independent source of truth, reconciliation is the cross-check between the two — not a place to create new financial facts.

Expenses

Expenses record money spent by the scheme, scoped to a financial year and a fund. An expense can optionally link to an accepted quote on a matter:

  • Setting quote_id ties the expense back to a TicketQuote, and through it to the matter and supplier. The matter reference (ticket_id) is derived from the quote so the two can never disagree.
  • This powers quoted-vs-actual variance on the matter — committees can see whether a job came in over or under its accepted quote.
  • Recurring spend that isn't tied to a job (insurance, cleaning, utilities) simply leaves the quote link empty.

Year-end statement

At the close of a financial year, the scheme can produce a year-end statement — a per-fund summary of opening balances, levy income, expenses, and closing balances. It renders on a dedicated print page designed for browser print-to-PDF, suitable for distribution to owners and for audit.

API reference

All routes are under /api/schemes/{schemeId}/.... The Auth column is the minimum tier.

EndpointAuthNotes
GET /finance/overviewMemberFinancial year, fund balances, and budget summary for the landing page.
GET /finance/registerMemberThe levy register — charges and balances per lot.
GET /finance/fund-balancesMemberPer-fund opening / credits / debits / closing. Optional ?fy=.
GET /finance/year-end-statementMemberData behind the year-end statement. Optional ?fy=.
GET /budgets · POST /budgetsMember / WriterList or draft a budget.
POST /budgets/{id}/approveAdminApprove a draft budget. Audited.
GET /levy-schedules · POST /levy-schedulesMember / WriterList or generate a draft levy schedule from an approved budget.
POST /levy-schedules/{id}/issueAdminIssue the run — commits charges and refreshes financial status. Audited.
GET /levy-charges · GET /arrearsMemberIndividual charges; arrears summary across lots.
GET /lots/{lotId}/levy-positionMemberA single lot's charges, payments, and balance.
GET /levy-payments · POST /levy-paymentsMember / WriterList or record a payment. Filter by ?lot=.
POST /levy-payments/{id}/reverseAdminSoft-void a payment with a reason. Audited.
GET /expenses · POST /expensesMember / WriterList or create an expense. Optional quote_id link.
GET /bank-accounts · POST /bank-accountsMember / WriterList or add a bank account.
POST /bank-accounts/{id}/imports/previewWriterPreview a statement file before committing.
POST /bank-accounts/{id}/importsWriterImport a CSV / OFX statement.
POST /bank-transactions/{txnId}/matchesWriterReconcile a transaction to a payment or expense.
POST /bank-transactions/{txnId}/ignoreWriterMark a transaction as ignored.

Business rules

  • Levy allocation is by unit entitlement and sums to the exact cent; the rounding residual goes to the largest-entitlement lot.
  • A levy run cannot be generated unless the sum of lot unit entitlements equals the scheme total (otherwise HTTP 422).
  • The administrative and capital works funds are tracked separately and never co-mingle.
  • Bank transactions are import-only; a database trigger blocks deletes and edits to financial fields, allowing only the reconciliation status to change.
  • Payments are reversed (soft-void + reason + audit), never hard-deleted.
  • Approving a budget, issuing a levy run, reversing a payment, and closing a financial year are financials-admin-only and are written to the audit log.
  • A lot's financial status is derived from arrears and drives voting eligibility; it is recalculated after payments, levy issuance, and on a daily schedule.