Installation:
composer require ekmungai/eloquent-ifrs
Publish the migrations and config:
php artisan vendor:publish --provider="EkmunGai\EloquentIfrs\EloquentIfrsServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="EkmunGai\EloquentIfrs\EloquentIfrsServiceProvider" --tag="config"
Run migrations:
php artisan migrate
First Use Case:
Define an Entity (company) and Account (e.g., "Cash", "Revenue"):
use EkmunGai\EloquentIfrs\Models\Entity;
use EkmunGai\EloquentIfrs\Models\Account;
$entity = Entity::create(['name' => 'My Company']);
$cashAccount = Account::create([
'entity_id' => $entity->id,
'code' => '1000',
'name' => 'Cash',
'type' => 'asset',
]);
Record a Transaction:
use EkmunGai\EloquentIfrs\Models\Transaction;
$transaction = Transaction::create([
'entity_id' => $entity->id,
'date' => now(),
'description' => 'Initial cash deposit',
'entries' => [
['account_id' => $cashAccount->id, 'debit' => 1000.00],
['account_id' => $cashAccount->id, 'credit' => 1000.00], // Balancing entry
],
]);
Verify Integrity:
$transaction->validate(); // Throws exception if debits ≠ credits
$transaction->save(); // Persists validated transaction
config/eloquent-ifrs.php (e.g., default currency, VAT settings).EkmunGai\EloquentIfrs\Models\ (e.g., Entity, Account, Transaction).EkmunGai\EloquentIfrs\Reports\ for pre-built IFRS-compliant statements.$entity = Entity::find(1);
$accounts = $entity->accounts()->where('type', 'asset')->get();
parent_id to nest accounts (e.g., "Current Assets" → "Cash").
$currentAssets = Account::where('code', 'like', '1%')->with('children')->get();
Transaction::createBatch([
[
'entity_id' => $entity->id,
'date' => now(),
'entries' => [['account_id' => $cashAccount->id, 'debit' => 500.00]],
],
// ... more entries
]);
$vatAccount = Account::where('code', '2000')->first();
$transaction = Transaction::create([
'entity_id' => $entity->id,
'date' => now(),
'entries' => [
['account_id' => $cashAccount->id, 'debit' => 1200.00],
['account_id' => $vatAccount->id, 'credit' => 200.00], // VAT component
],
'vat_rate' => 16.67, // 200/1200 = 16.67%
]);
opening_balance table:
use EkmunGai\EloquentIfrs\Models\OpeningBalance;
OpeningBalance::create([
'entity_id' => $entity->id,
'account_id' => $cashAccount->id,
'amount' => 5000.00,
'year' => 2025,
]);
use EkmunGai\EloquentIfrs\Reports\TrialBalance;
$report = new TrialBalance($entity, 2025);
$report->generate(); // Returns array of {account, debit, credit} pairs
use EkmunGai\EloquentIfrs\Reports\IncomeStatement;
$incomeStatement = new IncomeStatement($entity, 2025);
$revenues = $incomeStatement->getRevenues();
transaction.created or transaction.updated:
Event::listen('EkmunGai\EloquentIfrs\Events\TransactionCreated', function ($transaction) {
// Sync with ERP, notify stakeholders, etc.
});
return TransactionResource::collection(
$entity->transactions()->with('entries.account')->get()
);
Extend EkmunGai\EloquentIfrs\Models\Account to add domain-specific fields:
class CustomAccount extends Account
{
protected $casts = [
'is_tax_deductible' => 'boolean',
];
}
Update the migration to include new columns.
Use Laravel’s audit package alongside eloquent-ifrs to track changes to transactions and accounts:
use OwlLabs\Auditing\Contracts\Auditable;
class Transaction extends \EkmunGai\EloquentIfrs\Models\Transaction implements Auditable
{
// Audit all changes to transactions
}
Automate trial balance checks via Laravel Scheduler:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('eloquent-ifrs:reconcile')->dailyAt('02:00');
}
Register the command in routes/console.php:
Artisan::command('eloquent-ifrs:reconcile', function () {
// Logic to validate debits == credits across all entities
});
Extend the Transaction model to handle currency conversion:
class Transaction extends \EkmunGai\EloquentIfrs\Models\Transaction
{
protected $casts = [
'amount_usd' => 'float',
];
protected static function boot()
{
parent::boot();
static::saving(function ($transaction) {
$transaction->amount_usd = $transaction->entries->sum('debit') * $transaction->exchange_rate;
});
}
}
Debit/Credit Validation:
validate() before save() will silently persist unbalanced transactions.Transaction::observe(TransactionObserver::class);
class TransactionObserver
{
public function saving(Transaction $transaction)
{
$transaction->validate();
}
}
VAT Calculation Errors:
$transaction->vat_rate = config('eloquent-ifrs.default_vat_rate');
Opening Balances:
opening_balances:
$entity->accounts()->chunk(100, function ($accounts) {
foreach ($accounts as $account) {
OpeningBalance::firstOrCreate([
'entity_id' => $entity->id,
'account_id' => $account->id,
'year' => 2024,
], [
'amount' => $account->opening_balance ?? 0,
]);
}
});
Concurrency Issues:
transactions or accounts can corrupt the ledger.Transaction::create()) and avoid raw queries.Reporting Periods:
How can I help you explore Laravel packages today?