alimarchal/laravel-chart-of-accounts
Professional-grade Chart of Accounts and double-entry accounting module for Laravel with one-command install. Supports Jetstream and Breeze, a versioned REST API, 10 financial reports, role-based permissions, and balance enforcement at the database and application layers.
Architecture fit
accounting_* tables, a versioned REST API, and a dual frontend driver (inertia for React/Breeze, blade for Livewire/Jetstream). That is architecturally aligned with a conventional Laravel app and would be a hostile fit for a service-oriented or event-sourced ledger design.AccountingPeriod, ChartOfAccount (hierarchical), JournalEntry/JournalEntryLine, Currency with fx_rate_to_base, TaxCode/TaxRate, CostCenter, AccountBalanceSnapshot, AccountingAuditLog. A TPM can map these to requirements without reverse-engineering.public/vendor/accounting/, plus "database objects (stored procedures, views, triggers)." The last item is the most consequential architectural coupling and is the first thing to interrogate.JournalEntry::record() is a two-line convenience API (one debit account, one credit account, single amount). Useful for simple GL postings; insufficient for multi-line vouchers, tax splits, or invoice-level AR/AP. Treat it as sugar over the service layer, not the integration surface.Integration feasibility
accounting:install is 11 mutating steps against the host application: publishes and runs migrations, publishes Spatie migrations if absent, seeds master data, syncs DB objects, and assigns super-admin to the first user. In a greenfield app this is convenient; in an existing app it is an unacceptably broad blast radius and must be decomposed manually (vendor:publish, review migrations, run accounting:seed separately, assign role deliberately).HasRoles added to the User model; hardcoded ['web','auth'] middleware (no documented hook to append tenant, audit, or MFA middleware); fixed route names accounting.* even when the URL prefix changes.ACCOUNTING_UI_DRIVER) is a pre-install decision. There is no documented path to switch drivers after install — an operational trap worth designing around up front.Authorization: Bearer {token}; whether that is Sanctum, Passport, or hand-rolled is unstated — a blocking question for any SPA or external integration.api_prefix to api/v1/accounting while the README declares the base URL as /api/accounting/v1; the package description advertises multi-company, AR/AP, payroll, tax, and WhatsApp, while the README substantively covers COA, journals, periods, and reports only. Description-vs-docs drift is the single clearest vaporware risk here.Technical risk
accounting:sync-db-objects are the highest-risk architectural element. They constrain DB portability (SQLite/Postgres parity unverified), introduce invisible behavior outside migrations, complicate blue/green and read-replica deploys, and can silently drift after a package upgrade.JournalEntryLine directly rather than through the service layer is a correctness hazard. Whether model events or guards block direct writes must be verified, not assumed.accounting:update re-publishes views, assets, config, and JS. If the team customizes published views in place, upgrades will silently revert them. Customization must be confined to overrides outside the publish paths.public/; a CDN fallback exists. Acceptable, but it means the accounting UI drags a legacy jQuery stack into an otherwise modern app, and the fallback introduces an external runtime dependency.Key questions
use_permissions behavior when the host app already runs its own RBAC — coexistence or collision?accounting_audit_logs hash-chained or otherwise tamper-evident for audit or regulatory review?Stack fit
JournalEntry::record()/the service layer for programmatic postings. Higher setup cost, dramatically lower blast radius, and the posture a TPM should default to. It also makes eventual replacement a routing change rather than a rewrite.Migration path
Ledger interface (post entry, reverse, void, trial balance, GL) so the package is never called directly from domain code. This is the cheapest insurance available and costs days, not weeks.accounting:update as a reviewed change requiring a diff of republished assets.Compatibility
super-admin assignment is presumed), existing Activity Log configuration, an existing User model with its own RBAC, existing /accounting or /settings route collisions, and existing jQuery/Select2 versions in the host app.inertia vs blade decision must be made at kickoff and recorded as an ADR; reversing it later is an uninstall/reinstall, not a config toggle.Sequencing
accounting:verify and accounting:health-check pass.Ledger wrapper (3–5 days) → verify: your domain code compiles against your own interface, zero direct package references outside the adapter, contract tests green.Maintenance
accounting:update, requiring a diff-and-restore discipline each time) and DB-object drift (triggers and procedures outside the migration ledger). Budget a named owner and a per-upgrade checklist; this is not a "composer update and forget" dependency.Support
Ledger interface bounds the worst case to reimplementing an adapter rather than an application. Treat the author as a supplier of source code, not a support contract.PKR, account codes 1101/3101/5201) and must be reviewed and re-seeded for your entity.Scaling
accounting_journal_entry_lines, which grows monotonically. AccountBalanceSnapshot plus accounting:rebuild-snapshots is the intended escape hatch; whether reports actually use snapshots or recompute from lines is unverified and determines whether reporting stays fast past a few million lines.Failure modes
super-admin to the first user. Highest-severity footgun; never run on shared or production environments. Decompose the 11 steps.accounting:update; user-facing regressions discovered post-deploy.HasRoles is added to User.Ramp-up
TPM recommendation
Buy-and-harden, scoped. Building COA + double-entry + period close + ten financial reports + RBAC + REST API in-house is realistically 10–16 engineer-weeks of parity work; this package plausibly delivers 60–70% of that for zero license cost. The trade is a single-maintainer dependency and deep database coupling. That trade is acceptable for an internal, single-entity back office deployed behind an internal Ledger interface with a pinned fork. It is not acceptable, on current evidence, for multi-tenant SaaS, external-facing financial products, or any context requiring tamper-evident audit — those need the key questions answered and probably upstream hardening before commit.
How can I help you explore Laravel packages today?