PHP code example of othmanhaba / ledger-core

1. Go to this page and download the library: Download othmanhaba/ledger-core library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

othmanhaba / ledger-core example snippets


return [
    'tables' => [
        'entities' => 'ledger_entities',
        'accounts' => 'ledger_accounts',
        'journal_entries' => 'journal_entries',
        'journal_lines' => 'journal_lines',
        'account_balances' => 'account_balances',
    ],

    'models' => [
        'entity' => LedgerEntity::class,
        'account' => LedgerAccount::class,
        'journal_entry' => JournalEntry::class,
        'journal_line' => JournalLine::class,
        'account_balance' => AccountBalance::class,
    ],

    'currency' => [
        'base_currency' => env('LEDGER_BASE_CURRENCY', 'USD'),
        '

use LedgerCore\Data\CreateAccountData;
use LedgerCore\Data\JournalEntryData;
use LedgerCore\Data\JournalLineData;
use LedgerCore\Enums\AccountType;
use LedgerCore\Enums\NormalBalance;
use LedgerCore\Services\LedgerManager;

$ledger = app(LedgerManager::class);

$entity = $ledger->createEntity(
    name: 'Main Book',
    code: 'MAIN',
    baseCurrency: 'USD',
);

$cash = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Cash',
    code: '1000',
    type: AccountType::ASSET,
    normalBalance: NormalBalance::DEBIT,
    currency: 'USD',
));

$equity = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Opening Balance Equity',
    code: '3000',
    type: AccountType::EQUITY,
    normalBalance: NormalBalance::CREDIT,
    currency: 'USD',
));

$entry = $ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'opening-cash:2026',
    description: 'Opening cash balance',
    lines: [
        JournalLineData::debit($cash->id, '1000.00000000', 'USD'),
        JournalLineData::credit($equity->id, '1000.00000000', 'USD'),
    ],
));

$balance = $ledger->getBalance($cash);

$entity = $ledger->createEntity(
    name: 'Main Book',
    code: 'MAIN',
    type: 'operating',
    baseCurrency: 'USD',
    parentId: null,
    metadata: [
        'external_id' => 'book_123',
    ],
);

use LedgerCore\Data\CreateAccountData;
use LedgerCore\Enums\AccountType;
use LedgerCore\Enums\NormalBalance;

$bank = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Bank Account',
    code: '1010',
    type: AccountType::ASSET,
    normalBalance: NormalBalance::DEBIT,
    currency: 'USD',
    parentId: null,
    isControlAccount: false,
    isPostable: true,
    allowNegative: false,
    metadata: [
        'note' => 'Primary settlement account',
    ],
));

$assets = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Assets',
    code: '1000',
    type: AccountType::ASSET,
    normalBalance: NormalBalance::DEBIT,
    isControlAccount: true,
    isPostable: false,
));

$cash = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Cash',
    code: '1010',
    type: AccountType::ASSET,
    normalBalance: NormalBalance::DEBIT,
    parentId: $assets->id,
    isPostable: true,
));

counterpartyType: User::class,
counterpartyId: $user->id,

use LedgerCore\Data\JournalEntryData;
use LedgerCore\Data\JournalLineData;

$entry = $ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'receipt:2026-000001',
    referenceType: 'receipt',
    referenceId: '2026-000001',
    description: 'Receipt posted by host application',
    lines: [
        JournalLineData::debit(
            accountId: $cashAccount->id,
            amount: '250.00000000',
            currency: 'USD',
            memo: 'Cash received',
        ),
        JournalLineData::credit(
            accountId: $revenueAccount->id,
            amount: '250.00000000',
            currency: 'USD',
            memo: 'Revenue recognized',
        ),
    ],
    metadata: [
        'source' => 'api',
    ],
    postedAt: now(),
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'single-currency-example',
    lines: [
        JournalLineData::debit($cash->id, '100.00000000', 'USD'),
        JournalLineData::credit($revenue->id, '100.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'split-entry-example',
    lines: [
        JournalLineData::debit($cash->id, '1000.00000000', 'USD'),
        JournalLineData::credit($clearing->id, '980.00000000', 'USD'),
        JournalLineData::credit($feeRevenue->id, '20.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'multi-currency-example',
    lines: [
        JournalLineData::debit(
            accountId: $eurCash->id,
            amount: '100.00000000',
            currency: 'EUR',
            baseAmount: '108.00000000',
            exchangeRate: '1.080000000000',
        ),
        JournalLineData::credit(
            accountId: $usdClearing->id,
            amount: '108.00000000',
            currency: 'USD',
            baseAmount: '108.00000000',
            exchangeRate: '1.000000000000',
        ),
    ],
));

$ledger->postOpeningBalance(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'opening:cash:2026',
    description: 'Opening cash balance',
    lines: [
        JournalLineData::debit($cash->id, '100000.00000000', 'USD'),
        JournalLineData::credit($openingBalanceEquity->id, '100000.00000000', 'USD'),
    ],
));

$data = new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'external-event:' . $eventUuid,
    lines: [
        JournalLineData::debit($cash->id, '50.00000000', 'USD'),
        JournalLineData::credit($revenue->id, '50.00000000', 'USD'),
    ],
);

$first = $ledger->post($data);
$second = $ledger->post($data);

// Same entry is returned when the payload is identical.
$first->id === $second->id;

use LedgerCore\Exceptions\IdempotencyConflictException;

try {
    $ledger->post($differentPayloadWithSameKey);
} catch (IdempotencyConflictException $exception) {
    report($exception);
}

$reversal = $ledger->reverse(
    entry: $entry,
    reason: 'Incorrect amount posted',
);

$balance = $ledger->getBalance($cash);

use LedgerCore\Services\BalanceService;

$balance = app(BalanceService::class)->getBalance($cash);

$account = $ledger->createAccount(new CreateAccountData(
    ledgerEntityId: $entity->id,
    name: 'Temporary Clearing',
    type: AccountType::ASSET,
    normalBalance: NormalBalance::DEBIT,
    allowNegative: true,
));

$trialBalance = $ledger->getTrialBalance($entity->id);

$statement = $ledger->getStatement(
    accountId: $cash->id,
    from: now()->startOfMonth(),
    to: now()->endOfMonth(),
);

use LedgerCore\Services\LedgerReportService;

$reports = app(LedgerReportService::class);

$trialBalance = $reports->trialBalance(
    entityId: $entity->id,
    from: now()->startOfYear(),
    to: now(),
);

$generalLedger = $reports->generalLedger($entity->id, [
    'account_id' => $cash->id,
    'from' => now()->startOfMonth(),
    'to' => now()->endOfMonth(),
    'reference_type' => 'receipt',
    'per_page' => 100,
]);

$statement = $reports->accountStatement(
    accountId: $cash->id,
    from: now()->subMonth(),
    to: now(),
);

$balances = $reports->accountBalances($entity->id, [
    'currency' => 'USD',
]);

use Filament\Panel;
use LedgerCore\LedgerCorePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(LedgerCorePlugin::make());
}

'posting' => [
    'allow_manual_entries_from_filament' => true,
],

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'cash-received:' . $event->uuid,
    referenceType: 'cash_received',
    referenceId: (string) $event->id,
    description: 'Cash received',
    lines: [
        JournalLineData::debit($cashAccountId, '500.00000000', 'USD'),
        JournalLineData::credit($clearingAccountId, '500.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'gross-receipt:' . $event->uuid,
    referenceType: 'gross_receipt',
    referenceId: (string) $event->id,
    description: 'Gross receipt with fee split',
    lines: [
        JournalLineData::debit($cashAccountId, '1000.00000000', 'USD'),
        JournalLineData::credit($clearingAccountId, '980.00000000', 'USD'),
        JournalLineData::credit($feeRevenueAccountId, '20.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'expense:' . $expense->uuid,
    referenceType: 'expense',
    referenceId: (string) $expense->id,
    description: 'Expense recognized',
    lines: [
        JournalLineData::debit($expenseAccountId, '75.00000000', 'USD'),
        JournalLineData::credit($cashAccountId, '75.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'liability-settlement:' . $settlement->uuid,
    referenceType: 'liability_settlement',
    referenceId: (string) $settlement->id,
    description: 'Liability settled',
    lines: [
        JournalLineData::debit($liabilityAccountId, '300.00000000', 'USD'),
        JournalLineData::credit($cashAccountId, '300.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'currency-exchange:' . $exchange->uuid,
    referenceType: 'currency_exchange',
    referenceId: (string) $exchange->id,
    description: 'Currency exchange executed',
    lines: [
        JournalLineData::debit(
            accountId: $usdCashAccountId,
            amount: '1000.00000000',
            currency: 'USD',
            baseAmount: '1000.00000000',
            exchangeRate: '1.000000000000',
            memo: 'USD received',
        ),
        JournalLineData::credit(
            accountId: $lydCashAccountId,
            amount: '4800.00000000',
            currency: 'LYD',
            baseAmount: '990.00000000',
            exchangeRate: '0.206250000000',
            memo: 'LYD delivered',
        ),
        JournalLineData::credit(
            accountId: $exchangeRevenueAccountId,
            amount: '10.00000000',
            currency: 'USD',
            baseAmount: '10.00000000',
            exchangeRate: '1.000000000000',
            memo: 'Exchange spread',
        ),
    ],
    metadata: [
        'rate_source' => 'host_application',
        'quoted_rate' => '4.800000000000',
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'order-payment-captured:' . $order->uuid,
    referenceType: 'order',
    referenceId: (string) $order->id,
    description: 'Order payment captured',
    lines: [
        JournalLineData::debit(
            accountId: $paymentClearingAccountId,
            amount: $order->total_amount,
            currency: $order->currency,
        ),
        JournalLineData::credit(
            accountId: $customerDepositAccountId,
            amount: $order->total_amount,
            currency: $order->currency,
        ),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'order-fulfilled:' . $order->uuid,
    referenceType: 'order',
    referenceId: (string) $order->id,
    description: 'Order fulfilled and revenue recognized',
    lines: [
        JournalLineData::debit(
            accountId: $customerDepositAccountId,
            amount: $order->total_amount,
            currency: $order->currency,
        ),
        JournalLineData::credit(
            accountId: $salesRevenueAccountId,
            amount: $order->total_amount,
            currency: $order->currency,
        ),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'order-refund:' . $refund->uuid,
    referenceType: 'order_refund',
    referenceId: (string) $refund->id,
    description: 'Order refund posted',
    lines: [
        JournalLineData::debit(
            accountId: $salesReturnsAccountId,
            amount: $refund->amount,
            currency: $refund->currency,
        ),
        JournalLineData::credit(
            accountId: $paymentClearingAccountId,
            amount: $refund->amount,
            currency: $refund->currency,
        ),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'platform-collection:' . $collection->uuid,
    referenceType: 'platform_collection',
    referenceId: (string) $collection->id,
    description: 'Platform collection with fee',
    lines: [
        JournalLineData::debit($cashAccountId, '1000.00000000', 'USD'),
        JournalLineData::credit($counterpartyPayableAccountId, '950.00000000', 'USD'),
        JournalLineData::credit($platformFeeRevenueAccountId, '50.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'platform-payout:' . $payout->uuid,
    referenceType: 'platform_payout',
    referenceId: (string) $payout->id,
    description: 'Platform payout sent',
    lines: [
        JournalLineData::debit($counterpartyPayableAccountId, '950.00000000', 'USD'),
        JournalLineData::credit($cashAccountId, '950.00000000', 'USD'),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'prepayment-received:' . $payment->uuid,
    referenceType: 'prepayment',
    referenceId: (string) $payment->id,
    description: 'Customer prepayment received',
    lines: [
        JournalLineData::debit($cashAccountId, $payment->amount, $payment->currency),
        JournalLineData::credit($customerDepositAccountId, $payment->amount, $payment->currency),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'prepayment-applied:' . $application->uuid,
    referenceType: 'prepayment_application',
    referenceId: (string) $application->id,
    description: 'Customer prepayment applied',
    lines: [
        JournalLineData::debit($customerDepositAccountId, $application->amount, $application->currency),
        JournalLineData::credit($revenueAccountId, $application->amount, $application->currency),
    ],
));

$ledger->post(new JournalEntryData(
    ledgerEntityId: $entity->id,
    idempotencyKey: 'reclassification:' . $adjustment->uuid,
    referenceType: 'reclassification',
    referenceId: (string) $adjustment->id,
    description: 'Internal account reclassification',
    lines: [
        JournalLineData::debit($correctAccountId, '125.00000000', 'USD'),
        JournalLineData::credit($originalAccountId, '125.00000000', 'USD'),
    ],
    metadata: [
        'approved_by' => $adjustment->approved_by,
    ],
));

namespace App\Services;

use App\Models\Receipt;
use LedgerCore\Data\JournalEntryData;
use LedgerCore\Data\JournalLineData;
use LedgerCore\Services\LedgerManager;

final readonly class ReceiptPostingService
{
    public function __construct(private LedgerManager $ledger)
    {
    }

    public function postReceipt(Receipt $receipt): void
    {
        $this->ledger->post(new JournalEntryData(
            ledgerEntityId: $receipt->ledger_entity_id,
            idempotencyKey: 'receipt:' . $receipt->uuid,
            referenceType: 'receipt',
            referenceId: (string) $receipt->id,
            description: 'Receipt posted',
            lines: [
                JournalLineData::debit(
                    accountId: $receipt->cash_account_id,
                    amount: $receipt->amount,
                    currency: $receipt->currency,
                ),
                JournalLineData::credit(
                    accountId: $receipt->clearing_account_id,
                    amount: $receipt->amount,
                    currency: $receipt->currency,
                ),
            ],
        ));
    }
}

use Illuminate\Support\ServiceProvider;
use LedgerCore\Contracts\PostingValidatorContract;

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            PostingValidatorContract::class,
            \App\Ledger\AppPostingValidator::class,
        );
    }
}

namespace App\Ledger;

use LedgerCore\Contracts\PostingValidatorContract;
use LedgerCore\Data\JournalEntryData;
use LedgerCore\Exceptions\LedgerException;

final class AppPostingValidator implements PostingValidatorContract
{
    public function validate(JournalEntryData $entry): void
    {
        if ($entry->idempotencyKey === '') {
            throw new LedgerException('Idempotency key is 

use LedgerCore\Contracts\AccountResolverContract;

$this->app->bind(AccountResolverContract::class, AppAccountResolver::class);

use LedgerCore\Contracts\CurrencyConverterContract;

$this->app->bind(CurrencyConverterContract::class, AppCurrencyConverter::class);

'models' => [
    'account' => App\Models\LedgerAccount::class,
],

use LedgerCore\Events\JournalEntryPosting;

Event::listen(JournalEntryPosting::class, function (JournalEntryPosting $event): void {
    logger()->info('Posting journal entry', [
        'idempotency_key' => $event->data->idempotencyKey,
    ]);
});

use LedgerCore\Events\JournalEntryPosted;

Event::listen(JournalEntryPosted::class, function (JournalEntryPosted $event): void {
    logger()->info('Journal entry posted', [
        'entry_id' => $event->entry->id,
    ]);
});

use LedgerCore\Events\JournalEntryReversed;

Event::listen(JournalEntryReversed::class, function (JournalEntryReversed $event): void {
    logger()->info('Journal entry reversed', [
        'original_id' => $event->original->id,
        'reversal_id' => $event->reversal->id,
    ]);
});

use LedgerCore\Exceptions\AccountCurrencyMismatchException;
use LedgerCore\Exceptions\InsufficientBalanceException;
use LedgerCore\Exceptions\UnbalancedJournalEntryException;

try {
    $ledger->post($entryData);
} catch (UnbalancedJournalEntryException $exception) {
    // Show validation feedback or fail the job permanently.
} catch (AccountCurrencyMismatchException $exception) {
    // Fix account mapping or currency selection.
} catch (InsufficientBalanceException $exception) {
    // Stop the workflow or route it to review.
}

$account->balance()->update(['balance' => '100.00000000']);

$accounts = app(AccountMappingService::class)->forReceipt($receipt);

$ledger->post(new JournalEntryData(
    ledgerEntityId: $receipt->ledger_entity_id,
    idempotencyKey: 'receipt:' . $receipt->uuid,
    lines: [
        JournalLineData::debit($accounts->cash, $receipt->amount, $receipt->currency),
        JournalLineData::credit($accounts->clearing, $receipt->amount, $receipt->currency),
    ],
));

DB::transaction(function () use ($receipt, $ledger): void {
    $receipt->markAsPosted();

    $ledger->post(new JournalEntryData(
        ledgerEntityId: $receipt->ledger_entity_id,
        idempotencyKey: 'receipt:' . $receipt->uuid,
        lines: [
            JournalLineData::debit($receipt->cash_account_id, $receipt->amount, $receipt->currency),
            JournalLineData::credit($receipt->clearing_account_id, $receipt->amount, $receipt->currency),
        ],
    ));
});

referenceType: 'receipt',
referenceId: (string) $receipt->id,

$ledger->createEntity(...);
$ledger->createAccount(CreateAccountData $data);
$ledger->post(JournalEntryData $data);
$ledger->postOpeningBalance(JournalEntryData $data);
$ledger->reverse(JournalEntry $entry, ?string $reason = null);
$ledger->getBalance(LedgerAccount|int|string $account);
$ledger->getStatement($accountId, $from = null, $to = null);
$ledger->getTrialBalance($entityId, $from = null, $to = null);

$accounts->create(CreateAccountData $data);
$accounts->findOrFail($id);
$accounts->ensurePostable($account);
$accounts->ensureCurrencyCompatible($account, $currency);

$posting->post(JournalEntryData $data);

$balances->initializeBalance($account);
$balances->applyLine($line);
$balances->getBalance($account);
$balances->assertSufficientBalance($account, $amount);

$reversals->reverse($entry, $reason);

$reports->trialBalance($entityId, $from = null, $to = null);
$reports->generalLedger($entityId, $filters);
$reports->accountStatement($accountId, $from = null, $to = null);
$reports->accountBalances($entityId, $filters);
bash
php artisan migrate