obelaw / ium-accounting
Financial Management & Accounting (FMS) Domain Module for Obelawium ERP
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/validation: ^11.0|^12.0|^13.0
- obelaw/ium: *
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-11-01 15:16:20 UTC
README
Obelaw IUM Accounting (FMS)
A headless, mathematically rigorous General Ledger and Financial Management System (FMS) domain module for Laravel and the Obelawium ERP ecosystem.
Designed from first principles using Domain-Driven Design (DDD), obelaw/ium-accounting enforces the non-negotiable laws of statutory financial accounting: zero-sum integer minor unit parity, immutable ledger records, Storno reversal patterns, Maker-Checker segregation of duties, multi-currency ledger denomination, and decoupled Anti-Corruption Layer (ACL) ingestion.
📖 Documentation
Complete guides, architectural references, and fluent API specifications are available at:
👉 https://obelaw.com/docs/ium/accounting/introduction/
- Overview & Architecture
- Installation & Setup
- Chart of Accounts (COA)
- Journal Vouchers & General Ledger
- Fiscal Periods & Year-End Closing
- Multi-Currency & Exchange Rates
- Analytical Cost Centers
- Anti-Corruption Layer (ACL)
- Domain Invariants & Forensic Audit
- Fluent API Reference
✨ Key Features
-
Strict Zero-Sum Parity ($\sum \text{Debits} = \sum \text{Credits}$): Every voucher balances perfectly down to the single integer minor unit (cents/fils). Unbalanced submissions immediately throw
UnbalancedJournalEntryException. -
Immutable General Ledger & Storno Reversals: Posted vouchers (
POSTED,REVERSED) are strictly immutable. Eloquent hooks prevent in-place SQL updates and deletes. Erroneous vouchers must be corrected via equal-and-opposite Storno reversal vouchers (reverse()). -
Hierarchical Chart of Accounts (COA): Multi-level account classification tree (Assets, Liabilities, Equity, Revenue, Expense) with automated leaf-only posting validation (
NonPostingAccountException). -
Maker-Checker Segregation of Duties: When strict governance is enabled, the author of a voucher (
creator_id) cannot approve that voucher (approver_id), preventing unauthorized ledger adjustments. -
Fiscal Calendar & Hard Period Locks: Automated transaction date resolution to fiscal periods with soft review locks and statutory hard locks (
FiscalPeriodClosedException). - Multi-Currency & FX Engine (ISO 4217): Vouchers record in original transaction currency while General Ledger postings automatically convert and persist in functional base currency minor units at posting time.
- Multi-Dimensional Analytical Cost Centers: Tag journal voucher lines with Cost Centers, Departments, Projects, and Business Units for granular managerial P&L breakdown without bloating the Chart of Accounts.
-
Anti-Corruption Layer (ACL): External operational domains (Billing, WMS, POS, Purchasing) emit domain events or call
acl()->ingest(), insulating the General Ledger from operational logistics. - Statutory Year-End Closing Sequence: Sweeps and zeroes nominal accounts (Revenues/Expenses) and rolls the net annual profit or loss directly into Retained Earnings.
- Append-Only Forensic Audit Trail: Tamper-proof logging of every voucher lifecycle state transition, actor ID, timestamp, IP address, and payload snapshot.
🏛 The 9 Core Domain Services
All financial capabilities are accessed through Obelawium's centralized fluent gateway:
ium()->accounting()->[service]()->[method]()
| Service Accessor | Service Class | Primary Responsibilities |
|---|---|---|
ium()->accounting()->accounts() |
AccountService |
Chart of Accounts hierarchy, leaf-only validation, COA tree traversal. |
ium()->accounting()->journalEntries() |
JournalEntryService |
Drafting vouchers, zero-sum checking, Maker-Checker reviews, Storno reversals. |
ium()->accounting()->ledger() |
LedgerService |
Immutable General Ledger book, Trial Balance, Balance Sheet, Income Statement (P&L). |
ium()->accounting()->fiscalPeriods() |
FiscalPeriodService |
Fiscal years, monthly/quarterly periods, soft review locks, hard period closures. |
ium()->accounting()->costCenters() |
CostCenterService |
Analytical cost dimensions (Departments, Projects, Business Units). |
ium()->accounting()->closing() |
ClosingService |
Fiscal year-end nominal account zeroing and Retained Earnings rollover transfer. |
ium()->accounting()->audit() |
AuditService |
Append-only forensic accounting audit logs for statutory compliance. |
ium()->accounting()->acl() |
AclIngestionService |
Anti-Corruption Layer translating upstream domain events into balanced vouchers. |
ium()->accounting()->currencies() |
CurrencyService |
ISO 4217 registry, daily FX rate snapshots, minor unit conversions and formatting. |
🚀 Quick Start
1. Installation
composer require obelaw/ium-accounting
2. Run Migrations
All accounting tables use the isolated ium_accounting_ table prefix. By default, migrations are loaded automatically:
php artisan migrate
Custom Migration Path & Auto-Migrate Options
You can control whether migrations auto-load or specify a custom path in config/ium/accounting.php:
'migrations' => [ 'auto_migrate' => (bool) env('IUM_ACCOUNTING_AUTO_MIGRATE', true), 'path' => env('IUM_ACCOUNTING_MIGRATIONS_PATH', null), ],
Or execute migrations explicitly via the dedicated Artisan command with an optional custom path:
# Run accounting migrations from default package path php artisan ium:accounting:migrate # Run accounting migrations from a custom selected path php artisan ium:accounting:migrate --path=database/migrations/accounting
You can also configure auto-migration or execute migrations programmatically:
// Disable auto-migration at runtime (e.g. in multi-tenant environments) ObelawAccountingServiceProvider::ignoreMigrations(); // or ium()->accounting()->ignoreMigrations(); // Set a custom migration directory ium()->accounting()->setMigrationPath('/custom/migrations/path'); // Run migrations on demand ium()->accounting()->migrate('/custom/migrations/path');
3. Seed Turnkey Demo Ledger (Optional)
Instantly stand up a balanced, multi-currency ledger with sample accounts, periods, and postings:
php artisan ium:accounting:demo-seed
💻 Code Examples
Drafting & Posting a Balanced Journal Voucher
use Obelaw\Ium\Accounting\Data\CreateJournalEntryDto; use Obelaw\Ium\Accounting\Enums\JournalEntryType; // 1. Prepare a balanced voucher using major decimal units $dto = CreateJournalEntryDto::fromMajor([ 'entry_date' => '2026-03-31', 'entry_type' => JournalEntryType::Standard, 'currency_code' => 'USD', 'memo' => 'Client Q1 Retainer & Sales Tax', 'lines' => [ ['account_code' => '1120', 'side' => 'debit', 'amount' => 11500.00, 'memo' => 'Accounts Receivable'], ['account_code' => '4100', 'side' => 'credit', 'amount' => 10000.00, 'memo' => 'Consulting Revenue'], ['account_code' => '2150', 'side' => 'credit', 'amount' => 1500.00, 'memo' => 'Sales Tax Payable'], ], ]); // 2. Draft the voucher (saved with status = DRAFT) $entry = ium()->accounting()->journalEntries()->createDraft($dto); // 3. Maker-Checker Segregation: Preparer submits, distinct Controller approves and posts ium()->accounting()->journalEntries()->submit($entry->id, $preparerId); ium()->accounting()->journalEntries()->approve($entry->id, $controllerId); $posted = ium()->accounting()->journalEntries()->post($entry->id, $controllerId);
Immutable Storno Reversal (Never Edit in Place!)
// If a posted voucher contains an error, issue an equal-and-opposite Storno reversal: $reversal = ium()->accounting()->journalEntries()->reverse( entryId: $posted->id, actorId: $controllerId, reason: 'Incorrect tax calculation; reissuing updated invoice' );
Compiling Financial Statements in Real Time
// 1. Real-time Trial Balance verification $tb = ium()->accounting()->ledger()->generateTrialBalance($periodId); if (!$tb['is_balanced']) { throw new RuntimeException('Trial balance disparity detected!'); } // 2. Statutory Balance Sheet (Assets = Liabilities + Equity) $balanceSheet = ium()->accounting()->ledger()->generateBalanceSheet(); // 3. Income Statement (P&L) for active period $pnl = ium()->accounting()->ledger()->generateIncomeStatement($periodId); echo "Net Profit: " . $pnl['net_income_major'];
Ingesting Upstream Facts via the Anti-Corruption Layer (ACL)
use Obelaw\Ium\Accounting\Data\AclIngestEventDto; // Ingest domain event from Billing without coupling tables $voucher = ium()->accounting()->acl()->ingest( new AclIngestEventDto( eventName: 'billing.invoice_settled', amountMinor: 450000, // $4,500.00 in integer cents currencyCode: 'USD', transactionDate: '2026-03-31', description: 'Customer INV-8921 settlement via Stripe', sourceType: 'billing.invoice', sourceId: '8921' ) );
🤖 AI Agent Pair-Programming Skill
Developing double-entry financial software requires strict adherence to domain invariants. We provide an official AI development skill tailored for Google Antigravity, Claude Code, and Cursor:
👉 https://github.com/obelaw/skill-ium-accounting-developmint
Equip your AI assistants with step-by-step decision matrices, automated DTO blueprints, invariant verification procedures, and Pest PHP testing harnesses.
🤝 Contributing
Thank you for considering contributing to the Obelawium ecosystem! Please review our domain invariants and testing standards before submitting pull requests.
📄 License
Obelaw IUM Accounting is open-sourced software licensed under the MIT license.