Period Closing & Fiscal Controls

Structured fiscal period closing with validation checklists, automated year-end closing entries through the JournalEngine, and atomic period lock enforcement.

3 min read Accounting Controls As of Feb 9, 2026

Overview

CommunityPay enforces structured fiscal period management through the PeriodClosingService. Period closing is not a simple status toggle -- it is a validated, atomic operation that verifies accounting integrity before locking the period, and creates proper closing journal entries through the enforcement-guarded JournalEngine.

Once a period is closed, the ClosedPeriodGuard (GUARD_002 in the guard manifest) rejects any attempt to post journal entries into that period.


Period Closing Validation

Before a period can be closed, the service runs a multi-step validation checklist:

1. Period Status Check

The period must be in OPEN status. Already-closed or locked periods are rejected immediately.

2. Unposted Journal Entry Check

All journal entries within the period's date range must be in POSTED status. Draft or pending entries generate warnings that must be resolved (entries posted or deleted) before closing.

3. Trial Balance Verification

The service computes a full trial balance across all active accounts as of the period end date. Total debits must equal total credits within a $0.01 tolerance. An out-of-balance trial balance blocks closing entirely (not a warning -- a hard error).

4. Bank Reconciliation Check

Incomplete bank reconciliations within the period generate warnings. This is advisory (not blocking) because some reconciliations may legitimately span period boundaries, but the warning ensures the closing user is aware.


Period Closing Execution

When validation passes, the close_period method executes within a database transaction (@transaction.atomic). If any step fails, the entire operation rolls back -- no partial closes.

The close operation: 1. Validates all checklist items 2. Updates the period status to CLOSED 3. Records the closing user and timestamp 4. Logs the event with optional closing notes

A force parameter is available for cases where warnings exist but the closing user has determined they are acceptable. Hard errors (trial balance mismatch) cannot be forced.


Year-End Closing Entries

The close_fiscal_year method performs the standard accounting year-end close:

Step 1: Close Revenue Accounts

All revenue accounts (Operating Income, Non-Operating Income) with non-zero balances are debited to zero their balances.

Step 2: Close Expense Accounts

All expense accounts (Operating Expense, Non-Operating Expense) with non-zero balances are credited to zero their balances.

Step 3: Transfer Net Income to Retained Earnings

The difference between total revenue and total expenses (net income or net loss) is posted to the Retained Earnings account, which is identified by the RETAINED_EARNINGS role in the chart of accounts.

Enforcement Integration

The closing journal entry is posted through JournalEngine.post_transaction with transaction type CLOSING. This means: - All active guards evaluate the entry (BalanceGuard, ClosedPeriodGuard, etc.) - An EnforcementDecision record is created - The entry receives the same audit trail as any other journal entry

Period Lock

After the closing entry is posted, all fiscal periods for the year are closed. The ClosedPeriodGuard then prevents any future posting to those periods unless an AuditOverride is created.


Preview Capabilities

Before executing either operation, the service provides preview functions:

Period Closing Preview returns: - Journal entry counts (posted vs. unposted) - Bill and vendor payment counts for the period - Trial balance data - Validation results with pass/fail status

Year-End Preview returns: - Revenue account detail with balances - Expense account detail with balances - Computed net income - Period status overview (open vs. closed counts)

These previews allow the closing user to review the full impact before committing.


Guard Enforcement After Close

Once a period is closed, the ClosedPeriodGuard (GUARD_002) enforces the lock:

Guard Category Behavior
ClosedPeriodGuard TEMPORAL Rejects any journal entry where entry_date falls within a closed period

This guard is marked as required: True in the guard manifest, meaning it cannot be disabled. It can be overridden only through the AuditOverride mechanism with documented reason, authorized user, and expiration.

How CommunityPay Enforces This
  • Period closing requires passing a multi-step validation checklist
  • Year-end closing entries flow through JournalEngine with full guard evaluation
  • All period closings execute within database transactions (atomic rollback on failure)
  • ClosedPeriodGuard (GUARD_002) prevents any posting to closed periods
Login