Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Accountant Laravel Package

altek/accountant

Altek Accountant adds double-entry bookkeeping to Laravel: define a chart of accounts, record debit and credit journal entries, and query balances per account. It ships with migrations, Eloquent models, and helpers so you can build ledgers directly in your app without an external accounting service.

View on GitHub
Deep Wiki
Context7

Getting Started

Accountant wires Eloquent's model events into a persistence layer made of ledgers (who/where the change happened) and journals (the ordered set of model changes produced in that context). Install and bootstrap it in three steps:

composer require altek/accountant
php artisan vendor:publish --provider="Altek\Accountant\AccountantServiceProvider"
php artisan migrate

The publish step drops config/accountant.php and the package migrations. Read the config first — it drives which events are recorded, how the acting user is resolved into a Ledger, and what columns are captured per record. Then attach the trait to the model you want audited:

use Altek\Accountant\Contracts\Recordable;
use Altek\Accountant\Recordable as RecordableTrait;

class Article extends Model implements Recordable
{
    use RecordableTrait;

    protected $recordableEvents = ['created', 'updated', 'deleted', 'restored'];
}

First useful case: an Article update. Visit the record, edit the title, and query $article->journals — you should see one journal containing a record whose properties are the pre-change values. If that returns nothing, the event list, the trait, or the ledger resolution is wrong, in that order.

Implementation Patterns

Automatic CRUD auditing. Declare only the events you care about in $recordableEvents. A common pattern is a base model with the trait and a default event list, then override per model where e.g. restored should not apply. Pair it with a created/updated/deleted trio so your history is gapless.

Manual entries for domain events. Not every change is a model write. Use the Accountant facade to record a label alongside a journal, so domain actions (payments, approvals, exports) sit in the same timeline as CRUD:

use Altek\Accountant\Facades\Accountant;

Accountant::record('order.approved', ['order_id' => $order->id]);

Group related writes by recording them under a single journal when they represent one atomic user action.

Ledger as a request/session scope. Ledger ties a journal to the acting principal. Customize resolution when you have multi-guard auth or service-level actors:

Accountant::resolveLedgerUsing(fn () => TenantUser::find(Tenant::current()->actorId));

For CLI and queued work, set the ledger explicitly before the batch and reset after — otherwise you get orphan journals with a null ledger id.

Per-record history views. The journals relation is queryable; scope it by event type and eager-load the user through the ledger for audit screens:

$history = $article->journals()
    ->with('ledger')
    ->latest()
    ->paginate(25);

Compose with soft deletes and revisions. Because account records hold the old property values, they double as a lightweight revision store. A pattern is to build a "diff" presenter from consecutive journals and only rehydrate the full model when the user actually clicks "revert".

Revert workflow. Reverting is a first-class concept in the package but should be wrapped by you: gate it behind authorization, log the revert itself as a journal entry, and never let it run inside a transaction that also mutates unrelated models.

Gotchas and Tips

  • Version compatibility is the biggest risk. This package targets old Laravel 5.x-era APIs and has effectively no dependents. Pin your Laravel version, freeze composer.json, and smoke-test trait boot on a fresh install before adopting it. If you're on Laravel 8+, benchmark against owen-it/laravel-auditing or spatie/laravel-activitylog and treat Accountant as a migration source rather than a target.
  • Empty event list means silent no-op. $recordableEvents = [] records nothing and throws no error. When auditing "doesn't work", verify the property is present and spelled correctly before touching config.
  • JSON column requirements. Property snapshots are stored as JSON. MySQL < 5.7 and MariaDB < 10.2 need the package's alternate migration; check config/accountant.php for the column type toggle before migrating.
  • Ledger resolution returns null outside HTTP. Console commands and queued jobs have no Auth::user() by default. Bind a ledger manually or make your ledger resolver fall back to a system principal, otherwise journals are unattributed.
  • Trait method collisions. Recordable adds relations and helpers; if a model already defines journals() or a conflicting boot hook, you'll get obscure failures. Rename your relation or move the trait to a base class.
  • Recursion on self-updating models. An observer that touches the same model during updated produces an unbounded journal chain. Keep such writes out of $recordableEvents or guard with a static "recording" flag.
  • Bulk operations bypass events. Model::where(...)->update() and insert() don't fire Eloquent events, so no journals exist. For bulk changes, wrap the operation and call Accountant::record() with a summary payload, or chunk through save().
  • Reverting is destructive and partial. Only fields captured at record time are restored; relations, pivot data, and media are untouched. Snapshot the current state as a journal first so the revert itself is undoable.
  • Debugging checklist. Query the journals and ledgers tables directly to distinguish "not recorded" from "not resolved to a ledger". Enable the package's verbose logging config key in non-production if present; otherwise add temporary DB::listen output around the model save.
  • Extension points. Override the Ledger or Journal models via container bindings and the config's model map, and use resolveLedgerUsing for principal resolution. Custom Accountant implementations let you change how journals are grouped without forking the package — prefer this over editing vendor code, which you'll need to reapply on every update.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo