Fund accounting is the practice of segregating an organization's money into distinct pools — each with its own constraints, its own balance, and its own reporting. For HOAs, the most important boundary is between the operating fund and the reserve fund.
CommunityPay enforces fund segregation at the ledger level. This page describes how that enforcement works, what it prevents, and why "tagging" transactions with fund labels is not the same as enforcing fund boundaries.
The Problem with Tagging
Most accounting software handles fund tracking by tagging transactions with a fund label. The transaction is posted to the general ledger. The fund label is metadata attached after the fact.
This approach has a structural weakness: the tag is advisory. Nothing prevents a user from posting a transaction that moves cash between funds without changing the tag. Nothing prevents a report from aggregating tagged and untagged transactions. Nothing prevents a migration or bulk import from omitting tags entirely.
The result is that fund balances in tag-based systems are only as reliable as the discipline of the people entering data. For an HOA with a volunteer board and a rotating property manager, that discipline is inconsistent.
CommunityPay takes a different approach. Fund assignment is not a tag — it is a constraint enforced by the same guard system that enforces double-entry balance, closed period restrictions, and subledger reconciliation.
FundSegregationGuard
The FundSegregationGuard is one of eight production enforcement guards in CommunityPay's L5 enforcement dispatcher. It runs on every journal entry that is not an explicit fund transfer.
What It Checks
For each journal entry, the guard examines which bank-reconciled cash accounts are touched by the entry's lines. It then determines which funds those accounts belong to.
The guard flags a violation when:
- The entry touches cash accounts from two or more different funds in a single posting
- The entry touches cash from one fund plus untagged accounts that could represent a different fund
What It Excludes
The guard recognizes three explicit transfer transaction types:
| Transaction Type | Direction |
|---|---|
| transfer_to_reserve | Operating to Reserve |
| transfer_from_reserve | Reserve to Operating |
| fund_equity_transfer | Any inter-fund equity movement |
When the transaction type is one of these, the guard passes without evaluation. These are the only code paths that are permitted to cross fund boundaries.
The guard also excludes accounts that are not bank-reconciled cash — clearing accounts, undeposited funds, suspense accounts, and accounts receivable holding accounts. These accounts exist to facilitate transaction processing and are not subject to fund segregation rules.
What Happens on Failure
If the guard detects cross-fund cash movement on a non-transfer transaction, it returns a failure with reason code cross_fund_cash_movement. The enforcement dispatcher records the failure in an EnforcementDecision, and the journal entry is blocked.
The failure message identifies the violation and suggests two remediation paths:
- Reclassify the transaction as a transfer type
- Create an AuditOverride with scope FUND_SEGREGATION (which requires documented justification and is itself an immutable audit record)
Override Path
The guard supports overrides through the AuditOverride system. An authorized user can create a time-limited override with scope FUND_SEGREGATION. The override must include:
- Justification (why the override is necessary)
- Authorization type (board approval, admin emergency, etc.)
- Expiration date
- Maximum uses (optional)
The override is itself an immutable record. Every journal entry that executes under an override links to the override in its EnforcementDecision. This means the audit trail shows not just that the entry was allowed, but why it was allowed and who authorized the exception.
Fund Model
Each HOA has one or more funds, each with a distinct type:
| Fund Type | Purpose |
|---|---|
| OPERATING | Day-to-day operational expenses |
| RESERVE | Long-term capital replacement |
| SPECIAL | Special assessment collections |
| CAPITAL | Capital improvement projects |
Balance Computation
Fund balances are computed from the general ledger, not maintained as a separate field. The get_balance() method sums the balances of all accounts assigned to the fund, applying the accounting equation:
Fund Balance = Total Assets - Total Liabilities
A separate get_equity_balance() method sums only the equity accounts assigned to the fund. In a properly balanced fund, these two values are equal. The get_balance_summary() method returns both values and flags whether they agree — a built-in reconciliation check.
Funding Adequacy
For reserve funds, the model tracks:
| Field | Source |
|---|---|
| Funding target | Set from the most recent professional reserve study |
| Percent funded | Current balance divided by funding target |
| Funding status | fully_funded (>=100%), adequate (70-99%), underfunded (30-69%), critically_underfunded (<30%) |
These values are computed on demand from the ledger, not cached. The percent funded that appears in a report is the percent funded at the moment the report is generated.
Fund Policies
Fund policies are declarative constraints that govern how fund money can be spent, transferred, or received. Each policy has a type, a set of rules, and an enforcement level.
Policy Types
| Type | What It Controls |
|---|---|
| EXPENSE_CATEGORY | Which expense accounts can be charged to this fund |
| PROJECT_RESTRICTION | Limits spending to a specific project, with optional vendor and budget restrictions |
| APPROVAL_THRESHOLD | Requires board vote or specific approver role above a dollar amount |
| TRANSFER_RESTRICTION | Limits which funds can receive transfers, with maximum amounts and approval requirements |
| SOURCE_RESTRICTION | Controls which revenue sources can contribute to this fund |
Enforcement Levels
| Level | Behavior |
|---|---|
| BLOCK | Transaction is prevented if it violates the policy |
| WARN | Transaction is allowed with a warning logged |
| AUDIT | Transaction is allowed with a silent audit log entry |
Policy Snapshots
When a policy is evaluated, a PolicySnapshot is created — an immutable record of the policy's exact configuration at the time of evaluation. The snapshot includes:
- Complete rules JSON
- SHA-256 hash of the canonicalized rules (
rules_hash) - SHA-256 hash of the complete snapshot (
snapshot_hash) verify_integrity()method for post-hoc verification
This means the policy that governed a transaction on January 15 can be verified exactly, even if the policy was subsequently modified or deleted. The snapshot is the authoritative record.
Policy Violation Records
When a transaction violates a fund policy, a PolicyViolation record is created. The violation captures:
- Which policy was violated
- The attempted amount and fund
- A snapshot of the policy rules at violation time
- The resolution: BLOCKED, OVERRIDDEN, REALLOCATED, SPLIT, or CANCELLED
Violation records are linked to their policy snapshots, creating a traceable chain from the attempted action through the policy evaluation to the resolution.
Fund Transfer Workflow
Moving money between funds is a controlled operation with a mandatory approval workflow.
Transfer Lifecycle
DRAFT → PENDING_APPROVAL → APPROVED → COMPLETED
↘ REJECTED
↘ CANCELLED
- DRAFT: Created but not submitted. Can be edited.
- PENDING_APPROVAL: Submitted for board review. Cannot be edited.
- APPROVED: Board has approved. Ready for execution.
- COMPLETED: Transfer executed. Journal entry created via JournalEngine.
- REJECTED: Board rejected. Cannot be resubmitted (new transfer required).
- CANCELLED: Withdrawn before completion.
No backward transitions. A rejected transfer cannot be re-opened. A completed transfer cannot be reversed through the transfer workflow — it requires a separate reversal entry through the enforcement dispatcher.
Journal Entry Creation
When a transfer is completed, it creates a journal entry through the JournalEngine — the same choke point used by all financial transactions. The entry:
- Debits the destination fund's cash account
- Credits the source fund's cash account
- Passes through the enforcement dispatcher (which skips the FundSegregationGuard for transfer types)
- Creates an immutable EnforcementDecision
The transfer record links to the resulting journal entry, creating a traceable chain from the board approval to the ledger posting.
Validation
Fund transfers enforce several constraints:
- Amount must be greater than zero
- Source and destination funds must be different
- Source and destination accounts must belong to their respective funds
- Justification is required before submission for approval
These constraints are enforced at the model level, not just in the UI. A transfer that violates them cannot be saved regardless of how the save is attempted.
CARI Integration
Fund accounting health signals — reserve ratio, operating ratio, and fund balance accuracy — feed directly into the CARI Financial Health sub-score, which carries a 30% weight, the heaviest single component in the composite score. Reserve adequacy and fund segregation compliance are foundational CARI signals. An HOA with a critically underfunded reserve or a fund segregation guard override will see a measurable impact on its CARI Financial Health component.
For published methodology and component weights, see CARI Methodology and Scoring Framework.
How CommunityPay Enforces This
- FundSegregationGuard blocks cross-fund cash movement on every non-transfer journal entry
- Transfer transactions require explicit type declaration (transfer_to_reserve, transfer_from_reserve, fund_equity_transfer)
- FundPolicy model enforces expense category restrictions, transfer restrictions, and approval thresholds per fund
- PolicySnapshot captures SHA-256 hashed policy state at every evaluation — policy changes cannot retroactively affect prior decisions
- Fund transfers follow a mandatory approval workflow: DRAFT to PENDING_APPROVAL to APPROVED to COMPLETED
- Fund balance computed from the double-entry ledger (assets minus liabilities), not from a separate tracking field