Download the PHP package othmanhaba/ledger-core without Composer

On this page you can find all versions of the php package othmanhaba/ledger-core. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package ledger-core

Ledger Core

ledger-core is a generic, reusable double-entry ledger package for Laravel applications.

It provides the accounting core that many applications need: books, accounts, journal entries, journal lines, balances, currencies, reversals, idempotency, posting validation, reporting, auditability, and optional Filament administration.

The package is intentionally domain-neutral. It does not contain models or services for transfers, customers, providers, branches, remittance, wallets, invoices, orders, subscriptions, payouts, exchanges, or any other host-application workflow. Your application owns those workflows and translates them into ledger journal entries.

Table Of Contents

What This Package Solves

Most applications eventually need an audit-friendly financial record. A simple balance column is easy to start with, but it becomes difficult to reason about when you need corrections, historical reporting, idempotency, multiple accounts, multiple currencies, or audit trails.

ledger-core gives you a generic double-entry ledger:

Use this package when your Laravel app needs accounting-style records without hardcoding your business domain into the ledger.

Core Principles

  1. The ledger core is generic.
  2. Business-specific logic belongs in the host application.
  3. Every posted journal entry must be balanced.
  4. Posted journal entries and lines are immutable.
  5. Reversal entries correct mistakes.
  6. Idempotency is mandatory.
  7. Cached balances are updated only by the posting service.
  8. Amounts are decimal strings, not PHP floats.
  9. Posting is transactional.
  10. Filament support is optional.

Requirements

Installation

Install the package:

The package service provider is auto-discovered by Laravel through Composer.

Publish the config:

Publish the migrations:

Run migrations:

Configuration

The published config file is config/ledger.php.

Important Config Options

currency.base_currency : Default base currency for your installation.

currency.require_base_amount_for_multi_currency : When enabled, multi-currency entries must include base_amount on every line.

currency.scale : Decimal precision used by the package. The default is 8.

posting.return_existing_on_duplicate_idempotency_key : If enabled, reposting the same idempotency key with the same payload returns the existing entry.

posting.allow_cross_entity_entries : If disabled, all accounts in one entry must belong to the same ledger entity.

posting.prevent_negative_balances_by_default : If enabled, accounts cannot go negative unless the account has allow_negative = true.

posting.lock_accounts_during_posting : If enabled, affected accounts and balances are locked during posting.

posting.allow_manual_entries_from_filament : Controls whether users may create manual journal entries from Filament.

posting.allow_posted_metadata_updates : Controls whether posted entry metadata can be edited.

Database Model

ledger_entities

Represents an accounting book or entity. This can map to a company, tenant, organization, store, project, department, or any other book owner in the host application. The package does not care what the entity means.

Important fields:

ledger_accounts

Represents generic accounts inside a ledger entity.

Supported account types:

Important fields:

The package does not provide a chart of accounts. Your application creates whatever accounts it needs.

journal_entries

Represents the header for a posted accounting event.

Important fields:

journal_lines

Represents the debit and credit lines for a journal entry.

Important fields:

account_balances

Stores cached totals and balances for fast reads.

Important fields:

Do not update this table directly from application code. It is maintained by JournalPostingService.

Concepts

Ledger Entity

A ledger entity is the book that owns accounts and journal entries.

Examples in a host application might be:

The package stores the generic entity only. Your app decides what it represents.

Account

An account is a bucket where debit and credit movements are posted.

Examples:

These are examples only. The package does not create or enforce a chart of accounts.

Normal Balance

The normal balance decides how the cached balance is calculated.

Debit-normal accounts:

Credit-normal accounts:

Typical defaults:

Journal Entry

A journal entry is an accounting event. It contains two or more lines. Total debits must equal total credits.

Journal Line

A journal line is one debit or credit movement against one account.

Reversal

A reversal is a new posted journal entry with opposite lines. Reversal is how you correct posted records. Posted entries are not edited or deleted.

Idempotency

Idempotency prevents duplicate posting when a job, webhook, command, or API request is retried.

Every journal entry requires an idempotencyKey.

Quick Start

Creating Ledger Entities

Use LedgerManager::createEntity().

The type field is a free string. It is intentionally not an enum because different applications organize books differently.

Creating Accounts

Use LedgerManager::createAccount() or inject AccountService.

Parent Accounts And Postable Accounts

You may create account hierarchies:

Post only to postable accounts. Control accounts are useful for grouping and reporting.

Counterparty Fields

Accounts include optional generic counterparty fields:

These fields are generic. They let the host application associate accounts with any model without the package knowing what that model means.

Posting Journal Entries

Use LedgerManager::post() with JournalEntryData.

What Happens During Posting

The posting service:

  1. Starts a database transaction.
  2. Checks the idempotency_key.
  3. Computes and stores a payload hash.
  4. Dispatches JournalEntryPosting.
  5. Runs custom posting validators.
  6. Validates that the entry has at least two lines.
  7. Validates debit and credit totals.
  8. Resolves and locks affected accounts when configured.
  9. Ensures accounts are active and postable.
  10. Ensures account currencies are compatible.
  11. Creates the journal entry.
  12. Creates the journal lines.
  13. Updates cached account balances.
  14. Dispatches JournalEntryPosted.
  15. Commits the transaction.

If any step fails, nothing is partially posted.

Single-Currency Entries

For single-currency entries, total debit amount must equal total credit amount.

Split Entries

A single debit can be balanced by multiple credits:

Multiple debits can also be balanced by one credit, or by multiple credits.

Multi-Currency Entries

When entries contain more than one currency and require_base_amount_for_multi_currency is enabled, every line must include baseAmount.

For multi-currency entries, debit baseAmount totals must equal credit baseAmount totals.

Opening Balances

Opening balances are normal journal entries. Do not insert rows directly into account_balances.

Internally, postOpeningBalance() calls post(). The same validation, idempotency, immutability, and balance rules apply.

Idempotency

Every journal entry must have a unique idempotencyKey.

If the same key is used with a different payload, the package throws IdempotencyConflictException.

Choosing Idempotency Keys

Good keys are stable, unique, and based on the business event in the host app.

Examples:

Avoid keys based only on timestamps or random values when retry safety matters.

Reversals

Posted entries cannot be edited or deleted. To correct a posted entry, reverse it.

The reversal service:

  1. Locks the original entry.
  2. Ensures it is posted.
  3. Creates a new posted journal entry.
  4. Uses opposite debit and credit directions.
  5. Links the reversal to the original entry.
  6. Marks the original entry as reversed.
  7. Updates balances through normal posting.
  8. Dispatches JournalEntryReversed.

Original entry:

Reversal entry:

Balances

Balances are stored in account_balances for fast reads.

Use:

Or inject BalanceService:

Balance Formula

For debit-normal accounts:

For credit-normal accounts:

Negative Balances

By default, the package prevents negative balances when prevent_negative_balances_by_default is enabled.

Allow a specific account to go negative:

Reports

Use LedgerManager for common reports:

Use LedgerReportService for more control:

Trial Balance

Returns account-level totals:

General Ledger

Returns journal lines with entry and account information, filterable by:

Account Statement

Returns movements for one account over a period.

Filament Integration

Filament support is optional. The package can run without Filament installed.

If Filament is installed, register the plugin:

The plugin registers:

Filament Safety

Enable manual journal creation only if your operators understand double-entry posting:

Host Application Use Cases

The package does not know your domain. The host app should create posting services or recipes that convert business events into journal entries.

Use Case: Generic Cash Receipt

Host application concept:

Ledger entry:

Code:

Use Case: Fee Split

Host application concept:

Ledger entry:

Code:

Use Case: Expense Recognition

Ledger entry:

Code:

Use Case: Liability Settlement

Ledger entry:

Code:

Use Case: Currency Exchange

Host application concept:

Important ledger design:

Example:

Ledger entry:

Code:

The ledger package does not contain an exchange model, quote engine, rate provider, or settlement workflow. Those belong in the host app.

Use Case: Order Workflow

Host application concept:

The host app can post one journal entry per meaningful accounting event.

Order Payment Captured

Ledger entry:

Code:

Order Fulfilled And Revenue Recognized

Ledger entry:

Code:

Order Refund

Ledger entry:

Code:

This package does not decide when an order is fulfilled or whether revenue should be recognized at capture, shipment, delivery, or acceptance. The host application owns that policy.

Use Case: Marketplace Or Platform Payout

Host application concept:

Ledger entry when funds are collected:

Code:

Ledger entry when payout is sent:

Code:

Use Case: Customer Prepayment And Later Application

Host application concept:

When money is received:

When the deposit is applied:

Code:

Use Case: Internal Reclassification

Host application concept:

Ledger entry:

Code:

Use a reversal instead when the original posted journal entry itself is wrong and should be explicitly negated.

Use Case: Host-Specific Workflow Service

Create a service in your application:

This service belongs in your application, not in ledger-core.

Extending The Package

Custom Posting Validator

Bind PostingValidatorContract in your application:

Example validator:

Custom Account Resolver

Bind AccountResolverContract if your app needs custom account lookup.

Currency Converter

Bind CurrencyConverterContract if your app wants package-level conversion utilities.

Posting itself does not guess exchange rates. Your app should provide baseAmount and exchangeRate when needed.

Custom Models

You may replace model classes through config:

Custom models should extend the package models unless you are deliberately replacing behavior.

Events

The package dispatches Laravel events:

JournalEntryPosting

Dispatched before a journal entry is created, after idempotency has been checked.

JournalEntryPosted

Dispatched after the journal entry, lines, and balances have been created.

JournalEntryReversed

Dispatched after a reversal entry is posted and the original entry is marked reversed.

Exceptions

Common exceptions:

Example:

Testing

From the package directory:

The test suite covers:

Operational Guidance

Do Not Update Balances Manually

Never run application code like this:

Always post a journal entry.

Do Not Edit Posted Lines

Posted lines are the audit record. If something is wrong, reverse the entry and post a corrected entry.

Keep Account Mapping In Your App

Your app should decide which accounts are used for each workflow.

Good pattern:

Avoid putting receipt, invoice, transfer, wallet, or provider logic inside the ledger package.

Use Database Transactions Around Host Workflow State

If your application updates business state and posts to the ledger, wrap both in one transaction when possible.

The ledger posting service also runs its own transaction. Laravel safely nests transactions using savepoints where supported.

Queue And Webhook Safety

For jobs and webhooks:

FAQ

Does this package create a chart of accounts?

No. The package provides account storage and posting rules. Your application creates the accounts it needs.

Can I use this package for wallets?

Yes, but wallet concepts belong in your application. The package should only receive journal entries against generic accounts.

Can I delete a posted journal entry?

No. Reverse it.

Can I edit a posted amount?

No. Reverse the entry and post a corrected one.

Can I update cached balances directly?

No. Cached balances are maintained by JournalPostingService.

Can one journal entry include accounts from multiple entities?

Only if posting.allow_cross_entity_entries is enabled. The default is false.

Can accounts have no currency?

Yes. A null account currency means the package will accept line currencies for that account.

Can I use UUIDs externally?

Yes. Core tables include UUID columns for public references while keeping integer primary keys for database relationships.

How should I store business references?

Use reference_type and reference_id.

These are intentionally strings so the host application can reference any model or external event.

Why are amounts strings?

PHP floats are unsafe for money. This package uses decimal strings, decimal database columns, and bcmath.

Minimal Service Reference

LedgerManager

AccountService

JournalPostingService

BalanceService

ReversalService

LedgerReportService

License

MIT


All versions of ledger-core with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
ext-bcmath Version *
illuminate/database Version ^11.0|^12.0|^13.0
illuminate/support Version ^11.0|^12.0|^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package othmanhaba/ledger-core contains the following files

Loading the files please wait ...