Download the PHP package ringlesoft/laravel-accounting without Composer

On this page you can find all versions of the php package ringlesoft/laravel-accounting. 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 laravel-accounting

Laravel Accounting

Latest Version on Packagist Total Downloads License

PHP 8.1+, Laravel: 10+

A double-entry accounting engine for Laravel. Turn any model to a source of accounting transactions. Attach a Postable trait, map it to an AccountingTemplate, and posting produces a balanced, immutable journal entry.

Project status: Fully functional and under active development, not yet 1.0 (still improving based on feedback).

Table of Contents

Installation

  1. Install using composer

  2. Run the installer: it publishes the config, migrations and seeders, offers to run migrations, and offers to seed account types + a full chart of accounts + fiscal periods for the current year:

Quick Start

Option A. Using PHP attributes

Implement the PostableModel interface and the getPostableAmount() method. Apply the #[DefaultAccountingTemplate] attribute

Now you can post your model

Option B. Manual Template assigning

Post with an explicit template and overridden accounts instead of the default attribute:

Note: Accounts are never posted to a parent.


Diving Deeper

Core Concepts

The mental model is a straight line, and each link below is explained in its own subsection:

  1. An Account, identified by an Account Preset so you never hardcode IDs, is where money lives.
  2. An Accounting Template describes the debit/credit shape of a transaction type (e.g. a credit sale debits Accounts Receivable and credits Sales Revenue).
  3. Your own Eloquent model becomes a transaction source via the Postable trait, which builds Voucher Data from the template and posts it as an immutable Journal Entry (made up of balanced Journal Entry Items).
  4. Mistakes are fixed by reversing or voiding, never editing.
  5. Posting only succeeds inside an open Fiscal Period.
  6. Every account and entry automatically respects multi-currency and multi-tenancy.
  7. Reports read the ledger back out as Trial Balance, Balance Sheet, Income Statement, etc.

Accounts & Account Presets

This package ships with a complete Chart of Accounts (COA) that you can modify and extend for your own needs. Accounts can be nested (a parent category with child accounts), but accounts are never posted to directly if they have children — posting always targets a specific leaf account.

To avoid hardcoding database IDs or names, every account can carry an Account Preset: an enum of ~250 predefined roles (CASH, ACCOUNTS_RECEIVABLE, SALES_REVENUE, ...) that templates and your own code refer to instead of a concrete account. This is what lets an AccountingTemplate be defined once and still resolve to the correct account in any application's chart of accounts.

Journal Entries, Journal Entry Items & Voucher Data

A Journal Entry plus its balanced debit/credit Journal Entry Items are the immutable ledger components — posted entries can only be reversed or voided, never edited.

Before a Journal Entry exists, a template (or your own code) builds a Voucher Data object: an intermediate DTO that standardizes how debit/credit lines are described. Journal Entries only ever accept lines in this format, which is why both posting paths below — a template, or a hand-built VoucherData — end up going through the same createDraft()/post() pipeline.

Accounting Templates

An accounting template encapsulates the debit/credit pattern for a transaction type. It supports any number of lines per side, optional conditional lines (e.g. tax), and dynamic amounts via closures. The package comes with 39 prebuilt accounting templates that cover most of the well known accounting operations. You can always create yours on the fly, or by extending the AccountingTemplate class — see Creating Custom Templates in docs/templates-api.md.

The Postable Trait

This is the trait that turns your model into a source of a transaction in your ledger. This opinionated approach to ledger posting makes the accounting abstraction even more valuable by eliminating the need to manually write posting code for every type of transaction. The trait gives any Eloquent model post(), toVoucher(), postWithTemplate(), isPosted(), journalEntry(), and expects you to override getPostableAmount(), which is the trusted source of what amount is posted (see the Quick Start above for a minimal example).

Posting Transactions

Once a model uses Postable (or you'd rather skip models entirely), there are several ways to actually post:

Via a template directly
Via PostingService

For common transactions, PostingService wraps the right template plus createDraft() + post() in one call:

See the PostingService Method column in docs/templates-api.md for every available method.

Two-step posting (review before posting)
Resolving ambiguous accounts (children of a preset)

Sometimes a preset (e.g. COST_OF_GOODS_SOLD) resolves to an account that has multiple children under it. Since accounts are never posted to a parent, posting then fails with ParentAccountPostingException — the package refuses to guess which child you meant. There are two ways to resolve it:

  1. Pass the template → account mapping as you instantiate the template:

  2. Implement ResolvesAccountPresets on the postable model and return the mapping account via resolveAccountForPreset():

See docs/templates-api.md for the full catalog of 39 preset templates (sales, purchases, payroll, depreciation, loans, equity, inventory, bad debt).

Reversing and Voiding

Posted entries are immutable — correct them by reversing (creates an offsetting entry, full audit trail) rather than editing:

void() only applies to a draft, unposted entry (cancelling it before it's ever posted) — once posted, canBeVoided() returns false and you must reverse() instead.

Fiscal Periods

Posting validates the fiscal period is open; closing validates every entry in it is posted and the trial balance is in balance.

Closing entries

PeriodService::closePeriod() doesn't post anything by itself — it just validates and locks. To actually zero out Revenue/Expense into Retained Earnings before closing a period (the step that was missing before, and why the Balance Sheet wouldn't balance at year-end), use ClosingService:

Each of the four steps (close Revenue, close Expenses, close Income Summary → Retained Earnings, close Dividends → Retained Earnings) also has its own previewCloseX()/commitCloseX() pair if you want to run them independently rather than all at once — e.g. commitCloseRevenue($period). Every step reads the ledger's actual current state, not a cached calculation, so calling them in any order, or calling the same one twice, is safe: a step with nothing left to close just returns null. Requires INCOME_SUMMARY and RETAINED_EARNINGS accounts to be seeded (throws AccountNotFoundException if not); a missing DIVIDENDS_DECLARED account just means that step is skipped, since most businesses never declare dividends at all. Must run before PeriodService::closePeriod(), since posting is rejected once a period is closed.

Scheduling period closing

php artisan accounting:close-period wraps the two steps above (ClosingService::commit() then PeriodService::closePeriod()) into one command built to run unattended: with no options it finds every open fiscal period whose end_date has already passed and closes each one, continuing past any individual failure rather than aborting the whole run.

It exits 0 only if every period it processed closed successfully, and 1 if at least one failed (e.g. an unbalanced trial balance, or an unposted draft entry still sitting in that period) — check the exit code if you wire alerting around it. A period that's already closed is reported and skipped rather than erroring.

Schedule it like any other command — in routes/console.php (Laravel 11+):

or in app/Console/Kernel.php's schedule() method on earlier Laravel versions:

Running it daily (rather than exactly on the period boundary) is deliberate: a period only closes once its end_date has passed, so a daily run just means it closes the morning after the period actually ends, whenever that falls. Since this runs outside an authenticated context, it is not scoped to a single tenant by the usual global scope (see Multi-Currency & Multi-Tenancy below) — one scheduled run naturally closes the due period for every tenant, which is normally what you want; pass --tenant= if you need to isolate it to one.

Multi-Currency & Multi-Tenancy

Multi-tenancy isn't a toggle — any model using the MultiTenant trait (Account, JournalEntry, FiscalYear, FiscalPeriod, AccountBalance all do) is always scoped to a tenant_id column via a global scope. multi_tenancy_field only controls which attribute is read off the authenticated user to populate it on create (defaults to tenant_id); if there's no authenticated user (queues, CLI) it's left unset.

By default (strict_multi_tenancy_check => true), every record must belong to exactly one resolvable tenant: a row with a null tenant_id is never visible to an authenticated tenant, and creating a record with no resolvable tenant_id (no authenticated user, and none set explicitly) throws TenantRequiredException. Set it to false to allow genuinely global/shared records instead — null-tenant rows become visible to every tenant, and can be created without a resolvable tenant (e.g. from a seeder or console command) without being rejected. Recommended is true whenever using multi-tenancy.

Reports

This package generates all the standard (IFRS) reports for accounting systems — made possible by the opinionated structure of the COA and the use of account presets. Every report goes through LaravelAccounting::reports()->{reportType}($params), which returns an already-generated report - call ->getData() (or ->toArray()/->toJson()) directly. $params keys are snake_case and vary per report:

Report Key params
Trial Balance as_of_date
Balance Sheet as_of_date
Income Statement start_date, end_date
Cash Flow Statement start_date, end_date
General Ledger start_date, end_date, optional account_id
Account Ledger account_id (required), start_date, end_date
Chart of Accounts include_inactive, account_type, show_balances, as_of_date

Results are cached per report_cache_ttl, keyed by report type + params, and invalidated automatically whenever a journal entry posts, reverses, or voids - LaravelAccounting::reports()->clearCache($reportType) clears it manually if needed. Calling ->generate() again on a returned report is safe but recomputes live and bypasses the cache for that call, so there's no need to.

Configuration

The installer (or a manual vendor:publish, see below) publishes config/laravel_accounting.php into your Laravel application:

Posting fails with a validation error ("the fiscal period id field is required") if no fiscal period covers today — make sure FiscalPeriodSeeder has run, or create one via LaravelAccounting::periods()->createPeriodsForYear($fiscalYear).

Manual Installation and Resources Publishing

If you don't want to run the install command, you can manually publish package resources. This is what the install command does in the background:

Troubleshooting & FAQ

"Failed to post to journal. The fiscal period id field is required." No fiscal period covers the transaction date. Seed one (FiscalPeriodSeeder) or create it directly: LaravelAccounting::periods()->createPeriodsForYear($fiscalYear).

ArgumentCountError when calling TemplateRegistry::make('key') or YourTemplate::getPostableAccounts() Both call new $class() with zero arguments. Every constructor parameter on a template needs a default value — see Gotchas in docs/templates-api.md.

ParentAccountPostingException A preset (e.g. COST_OF_GOODS_SOLD, ACCOUNTS_RECEIVABLE) resolved to a parent/category account with more than one child, so the package refuses to guess which one to post to — accounts are never posted to a parent. Pick one with resolveAccount(), or implement ResolvesAccountPresets on the model — see Posting Transactions above.

AccountNotFoundException No seeded account carries the preset a template needs. Check your chart of accounts, or verify with YourTemplate::getPostableAccounts().

JournalNotBalancedException Debits ≠ credits. Common causes: a template's configure() was called twice (entries only ever append, never replace — never call ->configure() yourself), or a closure-based amount produced an unexpected value.

Why doesn't my Balance Sheet balance at year-end? Run LaravelAccounting::closing()->commit($period) before closing the period — see Closing entries under Fiscal Periods above. Nothing runs this automatically; PeriodService::closePeriod() won't do it for you. BalanceService::calculateUnclosedEarnings() still exists as a lower-level primitive if you need the unclosed-earnings figure without actually posting anything.

Is multi-tenancy optional? No — it's not a config toggle. Any model using the MultiTenant trait (Account, JournalEntry, FiscalYear, FiscalPeriod, AccountBalance) is always scoped to tenant_id via a global scope. multi_tenancy_field only controls which attribute is read off the authenticated user to populate it.

The tenant scope relies on Auth::user() — what happens in a queued job or artisan command, where there's no authenticated user? No tenant filter is applied at all in that context — the global scope only ever narrows results to the authenticated user's own tenant; it never fails open onto other tenants' data. Under the default strict check it never matches a null-tenant_id row either. But without an authenticated user, there's nothing for it to scope to, so a queued job or console command operating across tenants is your responsibility to scope explicitly (e.g. Account::where('tenant_id', $tenantId)->get(), or set tenant_id yourself before saving). Don't assume the package will scope non-HTTP code for you.

I want some accounts/data shared across every tenant (e.g. a default chart of accounts), not owned by just one. Set strict_multi_tenancy_check to false. A record with no resolvable tenant — created explicitly with tenant_id = null, or from a seeder/console command with no authenticated user — is then allowed to exist and becomes visible to every tenant, instead of being rejected at creation and invisible everywhere except to whoever created it outside an authenticated context. Tenant-owned rows (a real tenant_id set) are still isolated from each other exactly as before — this only changes how null-tenant rows behave.

Can I use cash-basis instead of accrual accounting? The cash_basis config flag is informational only — it isn't enforced anywhere. All shipped templates post on an accrual basis (revenue/expense recognized when earned/incurred, not when cash moves). See Accounting Basis: Accrual vs Cash in docs/templates-api.md before building cash-basis behavior yourself.

UUIDs or auto-incrementing IDs? A one-time choice at install (config('laravel_accounting.use_uuid')). Switching after you have real data requires a manual data migration — decide before going to production.

How do I write my own template? See Creating Custom Templates in docs/templates-api.md — covers registration, validate(), testing it without a database, and common gotchas.

Does the package handle authorization (who's allowed to post, close a period, etc.)? No — that's left entirely to your application. There are no policy classes or permission checks anywhere in the package; wrap calls into PostingService/PeriodService/JournalService with your own authorization logic (a policy, a middleware, a form request) before invoking them.

API Reference

Facade

Models

Account, AccountType, AccountBalance (cached per-period balances), JournalEntry, JournalEntryItem, FiscalYear, FiscalPeriod, ExchangeRate.

Services

JournalService (create/validate/post/reverse/void), AccountService (CRUD, hierarchy, preset lookup), BalanceService (balance calculation, trial balance, net income), ReportService, PostingService (named transaction shortcuts), PeriodService (open/close/lock fiscal periods), ClosingService (preview/commit period-end closing entries).

Key Exceptions

All extend LaravelAccountingException: AccountNotFoundException, AccountInactiveException, ParentAccountPostingException (preset resolves to a parent/category account with no unambiguous child), JournalNotBalancedException, DuplicatePostingException, PeriodClosedException, InvalidVoucherException, TemplateNotFoundException. See src/Exceptions/ for the full list.

Testing

Contributing

Let me pave the way, I'll let you know when you can contribute.

Security

If you discover any security-related issues, please email [email protected] or use the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Support

-

Issues: https://github.com/ringlesoft/laravel-accounting/issues

Contacts

Follow me on X: @ringunger
Email me: [email protected]
Website: https://github.com/ringlesoft/laravel-accounting


All versions of laravel-accounting with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
illuminate/contracts Version ^10.0|^11.0|^12.0|^13.0
illuminate/database Version ^10.0|^11.0|^12.0|^13.0
illuminate/routing Version ^10.24|^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 ringlesoft/laravel-accounting contains the following files

Loading the files please wait ...