Fiscal Periods & Year-End Closing
v0.1.0Fiscal calendars, transaction date resolution, soft and hard period locks, nominal account sweeping, and Retained Earnings rollover.
Fiscal Periods & Year-End Closing
Financial reporting requires strict temporal boundaries. Obelawium Accounting provides automated fiscal calendars, multi-stage period locks, and statutory Year-End closing sequences.
1. Fiscal Calendar Hierarchy
The temporal model consists of two tiers:
- Fiscal Years (
FiscalYear): Represents an enterprise financial year (e.g., FY-2026: Jan 1 – Dec 31). - Fiscal Periods (
FiscalPeriod): Sub-divisions (typically 12 monthly or 4 quarterly periods) with distinct lifecycle lock states.
Fiscal Year: 2026 (Jan 01, 2026 - Dec 31, 2026)
├── Period 01: 2026-01-01 to 2026-01-31 (CLOSED)
├── Period 02: 2026-02-01 to 2026-02-28 (CLOSED)
├── Period 03: 2026-03-01 to 2026-03-31 (OPEN)
└── ...
2. Managing Fiscal Years & Periods
Creating a Fiscal Year and Generating Periods
use Obelaw\Ium\Accounting\Data\CreateFiscalYearDto;
// Create fiscal year with 12 monthly periods generated automatically
$year = ium()->accounting()->fiscalPeriods()->createYear(
new CreateFiscalYearDto(
name: 'FY-2026',
startDate: '2026-01-01',
endDate: '2026-12-31',
autoGenerateMonthlyPeriods: true
)
);
Automatic Date Resolution
When a journal voucher is submitted, the system queries findPeriodForDate() to assign the voucher to the matching period:
$period = ium()->accounting()->fiscalPeriods()->findPeriodForDate('2026-03-15');
if (!$period->status->allowsPosting()) {
throw new FiscalPeriodClosedException("Period {$period->name} does not permit new postings.");
}
3. Period Lock States
A fiscal period transitions through three governance phases:
| Status Enum | Posting Allowed? | Description |
|---|---|---|
FiscalPeriodStatus::Open | Yes | Normal daily operations; vouchers can be drafted, approved, and posted. |
FiscalPeriodStatus::ClosingReview | Restricted | Month-end financial close; only users with the accounting.override-review-lock permission may post adjustment entries. |
FiscalPeriodStatus::Closed | No | Statutory hard lock; all write attempts throw FiscalPeriodClosedException. |
Locking and Closing a Period
// Step 1: Transition into Closing Review (soft lock)
ium()->accounting()->fiscalPeriods()->setReviewStatus($periodId, $actorId);
// Step 2: Finalize hard statutory lock
ium()->accounting()->fiscalPeriods()->closePeriod($periodId, $actorId);
4. Statutory Year-End Closing Workflow
At the end of a fiscal year, nominal (temporary) accounts—Revenues and Expenses—must be zeroed out, and the resulting Net Profit or Loss transferred into the Retained Earnings equity account on the Balance Sheet.
The ium()->accounting()->closing() service automates this sequence:
flowchart LR
A["Nominal Revenue Accounts<br/>(Credit Balance)"] -->|Debit to Zero| C["Year-End Closing Journal Entry"]
B["Nominal Expense Accounts<br/>(Debit Balance)"] -->|Credit to Zero| C
C -->|Net Profit/Loss Rollover| D["Retained Earnings (Equity)<br/>Account 3100"]
Executing the Year-End Close
use Obelaw\Ium\Accounting\Data\FiscalYearCloseDto;
$closeResult = ium()->accounting()->closing()->closeFiscalYear(
new FiscalYearCloseDto(
fiscalYearId: $fiscalYearId,
actorId: $cfoUserId,
retainedEarningsAccountCode: '3100', // Retained Earnings
closingDate: '2026-12-31',
memo: 'FY-2026 Statutory Annual Books Closure'
)
);
echo "Closed Year: {$closeResult->fiscalYear->name}\n";
echo "Closing Voucher ID: {$closeResult->closingJournalEntry->id}\n";
echo "Net Income Rolled Over: {$closeResult->netIncomeMajor}\n";
Guarantees Enforced During Year-End Closing
- Prior Periods Verification: Verifies that all 12 prior monthly periods are already
CLOSED. - Atomic Voucher Creation: Generates a zero-sum
JournalEntryType::Closingvoucher that zeroes every nominal account. - Immutability Seal: Seals the
FiscalYearrecord withis_closed = true. - Append-Only Audit: An immutable audit entry is appended with the calculation breakdown.