elgibor-solution / laravel-accounting
Enterprise-grade accounting engine for Laravel applications.
Package info
github.com/elgiborsolution/laravel-accounting
pkg:composer/elgibor-solution/laravel-accounting
Requires
- php: >=8.2
- laravel/framework: ^11.0|^12.0|^13.0
README
elgibor-solution/laravel-accounting is a Laravel 11/12 accounting package that provides category-tree based chart-of-accounts management, journal entry storage, service-to-account mappings, monthly closing, and financial reporting.
For the full technical reference, start with docs/README.md. It documents the normalized chart-of-accounts design, architecture, public APIs, services, journal engine, mapping engine, and extension points.
Overview
The package is built around a small set of accounting entities:
- Account categories as a reporting tree
- Posting accounts
- Business services and account mappings
- Journal entries and journal entry details
- Fiscal periods
- Monthly balances
The package also ships with API controllers, reusable services, seeders, a factory, and package migrations. Tables are prefixed by default with acc_.
Features
- Chart of accounts with hierarchical account categories and leaf posting accounts
- Account category management with status toggling
- Business service definitions with debit/credit mappings
- Journal storage with validation and auto-posting
- Journal number generation using a configurable format
- Fiscal period locking to prevent posting into closed periods
- Monthly closing and reopening
- General ledger, trial balance, profit & loss, balance sheet, and cash flow reports aggregated through the category tree
- Tenant-aware routes when a
{tenantId}segment is used
Technical Design Summary
This documentation uses a normalized accounting design:
acc_account_categoriesowns the hierarchy throughparent_id.- Root category
typevalues areASSET,LIABILITY,EQUITY,REVENUE, andEXPENSE. category_nameis fully custom.acc_accountscontains posting accounts only.acc_accounts.parent_idandacc_accounts.levelare removed from the design.- Every account must have
category_id. - Financial reports aggregate posting balances through the category tree, not through account hierarchy.
Example category tree:
ASSET
`-- Current Asset
|-- Cash & Cash Equivalent
|-- Account Receivable
|-- Inventory
`-- Prepaid Expense
ASSET
`-- Fixed Asset
|-- Land
|-- Building
|-- Vehicle
`-- Equipment
REVENUE
`-- Sales Revenue
EXPENSE
`-- Operating Expense
|-- Salary Expense
|-- Electricity Expense
`-- Water Expense
Relationship diagram:
acc_account_categories (Tree)
|
|-- parent_id -> acc_account_categories.id
|
`-- 1 : N
|
v
acc_accounts
|
v
acc_journal_entry_details
|
v
acc_monthly_balances
|
v
Financial Reports
What is not in this package
The current codebase does not include invoice, tax, AR/AP, or payment controllers/routes. Those concepts may exist in a larger application, but they are not implemented here.
Installation
- Require the package in your Laravel application.
composer require elgibor-solution/laravel-accounting
- Publish the configuration file if you want to customize defaults.
php artisan vendor:publish --tag=accounting-config
- Publish the package migrations if you want them in your application database folder.
php artisan vendor:publish --tag=accounting-migrations
- Run migrations.
php artisan migrate --path database/migrations/accounting
- Seed the default chart of accounts, account categories, and ERP accounting services.
php artisan db:seed --class="ESolution\LaravelAccounting\Database\Seeders\AccountingSeeder"
The service provider also loads the package migrations automatically when Laravel is running in console.
AccountingSeeder now includes the default ERP accounting service catalog, so a fresh installation starts with standard transaction services in acc_services.
Configuration
The package configuration lives in config/accounting.php.
Available options
return [ 'table_prefix' => 'acc_', 'master_data' => [ 'use_shared_database' => env('ACCOUNTING_USE_SHARED_DATABASE', false), 'connection' => env('ACCOUNTING_MASTER_CONNECTION'), ], 'journal' => [ 'auto_post' => true, 'number_format' => 'JV/{YEAR}/{MONTH}/{SEQ}', ], 'fiscal' => [ 'start_month' => 1, ], 'route' => [ 'prefix' => 'api/accounting', 'middleware' => ['api'], ], ];
table_prefixcontrols every package table name.master_data.use_shared_databaseenables a shared master database foracc_account_categories,acc_accounts,acc_services, andacc_service_accounts.master_data.connectionpoints to the master database connection name used when shared master mode is enabled.journal.auto_postdetermines whether a created journal is immediately posted.journal.number_formatis used byJournalService::generateJournalNo()to build the journal number.fiscal.start_monthis present for fiscal-year configuration.route.prefixandroute.middlewarecontrol package API routing.
Shared master mode does not change the public API. When it is disabled, the package keeps using the active application or tenant connection exactly as before.
Usage
Resolve the high-level service
src/Services/AccountingService.php is a convenience wrapper that exposes the package services:
use ESolution\LaravelAccounting\Facades\Accounting; use ESolution\LaravelAccounting\Services\AccountingService; $accounting = app(AccountingService::class); $journalService = $accounting->journal(); $coaService = $accounting->coa(); $mappingService = $accounting->mapping(); $closingService = $accounting->closing(); $reportService = $accounting->report(); $salesCashService = Accounting::service('SALES_CASH'); $catalog = Accounting::catalog();
Use enum-backed service codes
The package now ships with an enum for standard accounting service codes so package logic can avoid hardcoded strings.
use ESolution\LaravelAccounting\Enums\AccountingServiceCode; use ESolution\LaravelAccounting\Facades\Accounting; $service = Accounting::service(AccountingServiceCode::SALES_CASH); $salesServices = Accounting::catalog()->sales();
Create a manual journal
JournalService::journalManual() accepts balanced debit/credit lines, validates account state and fiscal period, and creates a posted journal entry with details.
use ESolution\LaravelAccounting\Services\JournalService; $journal = app(JournalService::class)->journalManual([ 'trx_date' => '2026-01-15', 'reference_no' => 'JU-20260115-0001', 'description' => 'Office expense payment', 'details' => [ [ 'account_id' => 'uuid-account-1', 'type' => 'D', 'amount' => 150000, 'description' => 'Office supplies', ], [ 'account_id' => 'uuid-account-2', 'type' => 'K', 'amount' => 150000, 'description' => 'Cash payment', ], ], ]);
Create a journal from a mapped service
JournalService::journalByMapping() resolves the service and its mappings through repositories, validates service_code, checks mapping keys, and can auto-post when enabled in configuration. The method does not rely on cross-connection eager loading or whereHas() against master tables.
use ESolution\LaravelAccounting\Enums\AccountingServiceCode; use ESolution\LaravelAccounting\Services\JournalService; $journal = app(JournalService::class)->journalByMapping([ 'service_code' => AccountingServiceCode::SALES_CREDIT, 'trx_date' => '2026-01-15', 'reference_no' => 'INV-0001', 'description' => 'Sales invoice INV-0001', 'items' => [ [ 'mapping_key' => 'cash_debit', 'amount' => 1000000, ], [ 'mapping_key' => 'revenue_credit', 'amount' => 1000000, ], ], ]);
Read the chart of accounts tree
CoaService::getTree() is documented as returning the category tree, with posting accounts grouped under their assigned categories. Report hierarchy lives in categories, not in accounts.
use ESolution\LaravelAccounting\Services\CoaService; $tree = app(CoaService::class)->getTree();
Close a month
ClosingService::closeMonth() validates the fiscal period, checks journal balance, builds monthly balances, and marks the period as closed.
use ESolution\LaravelAccounting\Services\ClosingService; app(ClosingService::class)->closeMonth(2026, 1, auth()->id());
Close through current month
ClosingService::closeThroughCurrentMonth() closes every open fiscal period up to the current month and returns the closed periods.
use ESolution\LaravelAccounting\Services\ClosingService; $closedPeriods = app(ClosingService::class)->closeThroughCurrentMonth(auth()->id());
If a fiscal period does not exist yet, the package now creates it automatically from the earliest journal date through the current month before closing.
Generate reports
ReportService provides report methods that are also exposed through the API. General Ledger stays account-based for detail lookup, while Trial Balance, Profit Loss, Balance Sheet, and Cash Flow are documented as category-tree driven reports.
use ESolution\LaravelAccounting\Services\ReportService; $reportService = app(ReportService::class); $generalLedger = $reportService->generalLedger($accountId, '2026-01-01', '2026-01-31'); $trialBalance = $reportService->trialBalance(2026, 1); $profitLoss = $reportService->profitLoss(2026, 1); $balanceSheet = $reportService->balanceSheet(2026, 1); $cashFlow = $reportService->cashFlow(2026, 1);
Default ERP Accounting Services
DefaultAccountingServicesSeeder seeds acc_services with a standard ERP-ready service catalog. The seeder is idempotent and uses updateOrCreate() so rerunning seeds updates definitions without creating duplicates.
Module grouping
| Module | Service Codes |
|---|---|
| SALES | SALES_CASH, SALES_CASH_VAT, SALES_CREDIT, SALES_CREDIT_VAT, SALES_RETURN, SALES_DISCOUNT, SALES_WRITE_OFF |
| PURCHASE | PURCHASE_CASH, PURCHASE_CREDIT, PURCHASE_RETURN |
| INVENTORY | STOCK_OPENING, STOCK_ADJUSTMENT_PLUS, STOCK_ADJUSTMENT_MINUS, STOCK_TRANSFER, STOCK_OPNAME_GAIN, STOCK_OPNAME_LOSS |
| FINANCE | CASH_IN, CASH_OUT, BANK_TRANSFER, JOURNAL_MANUAL, PETTY_CASH |
| EXPENSE | EXPENSE, PREPAID_EXPENSE |
| PAYROLL | PAYROLL, PAYROLL_ACCRUAL |
| ASSET | ASSET_PURCHASE, ASSET_DEPRECIATION, ASSET_DISPOSAL, ASSET_REVALUATION |
| ACCOUNT_RECEIVABLE | CUSTOMER_RECEIVABLE_PAYMENT, CUSTOMER_RECEIVABLE_WRITE_OFF |
| ACCOUNT_PAYABLE | VENDOR_PAYMENT, VENDOR_PAYABLE_WRITE_OFF |
| TAX | TAX_OUTPUT, TAX_INPUT, VAT_PAYMENT, TAX_PAYMENT |
| CLOSING | MONTH_END_CLOSING, YEAR_END_CLOSING |
Purpose of each service
| Service Code | Purpose |
|---|---|
SALES_CASH |
Cash sales recognized immediately without creating receivables. |
SALES_CASH_VAT |
Cash sales with output VAT recognition and VAT payable. |
SALES_CREDIT |
Sales invoices that create customer receivables. |
SALES_CREDIT_VAT |
Credit sales with output VAT recognition and VAT payable. |
SALES_RETURN |
Reversals for goods returned by customers. |
SALES_DISCOUNT |
Discounts granted on sales transactions. |
SALES_WRITE_OFF |
Sales-related balances written off after approval. |
PURCHASE_CASH |
Purchases paid immediately by cash or bank. |
PURCHASE_CREDIT |
Purchases that create vendor payables. |
PURCHASE_RETURN |
Returns of purchased goods to vendors. |
STOCK_OPENING |
Initial inventory balance recognition. |
STOCK_ADJUSTMENT_PLUS |
Positive inventory adjustments. |
STOCK_ADJUSTMENT_MINUS |
Negative inventory adjustments. |
STOCK_TRANSFER |
Inventory movement between locations. |
STOCK_OPNAME_GAIN |
Surplus stock found during stock count. |
STOCK_OPNAME_LOSS |
Missing stock found during stock count. |
CASH_IN |
General non-sales cash receipts. |
CASH_OUT |
General non-purchase cash disbursements. |
BANK_TRANSFER |
Transfers between cash and bank accounts. |
JOURNAL_MANUAL |
Manual accounting adjustments entered by finance users. |
PETTY_CASH |
Petty cash funding, usage, and replenishment. |
EXPENSE |
Standard operating expense recognition. |
PREPAID_EXPENSE |
Prepaid expense acquisition and amortization flows. |
PAYROLL |
Payroll payment transactions. |
PAYROLL_ACCRUAL |
Payroll accrual and liability recognition. |
ASSET_PURCHASE |
Fixed asset acquisitions and capitalization. |
ASSET_DEPRECIATION |
Periodic depreciation entries. |
ASSET_DISPOSAL |
Disposal or retirement of fixed assets. |
ASSET_REVALUATION |
Approved asset revaluation entries. |
CUSTOMER_RECEIVABLE_PAYMENT |
Customer payments against receivables. |
CUSTOMER_RECEIVABLE_WRITE_OFF |
Write-off of uncollectible receivables. |
VENDOR_PAYMENT |
Vendor payments against payables. |
VENDOR_PAYABLE_WRITE_OFF |
Write-off of payable balances after reconciliation. |
TAX_OUTPUT |
Output tax recognition from taxable sales. |
TAX_INPUT |
Input tax recognition from taxable purchases or expenses. |
VAT_PAYMENT |
Settlement of output VAT payable to the tax authority. |
TAX_PAYMENT |
Settlement of tax liabilities. |
MONTH_END_CLOSING |
Month-end adjustment and closing entries. |
YEAR_END_CLOSING |
Year-end closing and retained earnings transfer. |
Service catalog registry
ServiceCatalog is the central registry for default services. It groups services by module through all(), sales(), purchase(), inventory(), finance(), expense(), payroll(), asset(), receivable(), payable(), tax(), and closing(). The sales module now includes VAT-aware entries, and the tax module includes a dedicated VAT payment service.
Default ERP Journal Templates
DefaultServiceAccountMappingsSeeder seeds acc_service_accounts with production-ready journal templates for the default ERP services. The seeder resolves account_id from seeded account_code, never hardcodes account IDs, and uses updateOrCreate() for idempotent installs and upgrades. In shared master mode, the seeders write master data through ACCOUNTING_MASTER_CONNECTION; otherwise they use the active application or tenant connection.
What acc_service_accounts does
- Journal template registry
- Account mapping engine
- Auto journal generator blueprint
- Dynamic account resolver
- Validation layer for required mapping keys
Default category and posting account examples
| Type | Category path | Example posting accounts |
|---|---|---|
ASSET |
Current Asset > Cash & Cash Equivalent |
Kas, Bank BCA, Bank Mandiri |
ASSET |
Current Asset > Account Receivable |
Piutang Dagang, Piutang Karyawan |
ASSET |
Current Asset > Inventory |
Persediaan Barang Dagang, Inventory In Transit |
ASSET |
Current Asset > Prepaid Expense |
Uang Muka Pembelian, Pajak Dibayar Dimuka |
ASSET |
Fixed Asset > Land / Building / Vehicle / Equipment |
Tanah, Bangunan, Kendaraan, Peralatan Kantor |
LIABILITY |
Current Liability |
Hutang Dagang, Hutang Pajak, Uang Muka Penjualan |
EQUITY |
Owner Equity |
Modal Pemilik, Saldo Laba Tahun Berjalan |
REVENUE |
Sales Revenue |
Penjualan Retail, Penjualan Online |
EXPENSE |
Operating Expense > Salary / Electricity / Water |
Salary Expense, Electricity Expense, Water Expense |
Template registry
ServiceAccountTemplateRegistry centralizes the default mapping blueprints. Each template definition includes:
mapping_keymapping_namepositionaccount_codesequence_nois_dynamicis_requiredis_active
Default mapping matrix
| Service Code | Default Mappings | Dynamic |
|---|---|---|
SALES_CASH |
sales_cash_cash_d, sales_cash_sales_k, sales_cash_cogs_d, sales_cash_inventory_k |
Cash/Bank |
SALES_CASH_VAT |
sales_cash_vat_cash_d, sales_cash_vat_sales_k, sales_cash_vat_vat_k, sales_cash_vat_cogs_d, sales_cash_vat_inventory_k |
Cash/Bank |
SALES_CREDIT |
sales_credit_ar_d, sales_credit_sales_k, sales_credit_cogs_d, sales_credit_inventory_k |
No |
SALES_CREDIT_VAT |
sales_credit_vat_ar_d, sales_credit_vat_sales_k, sales_credit_vat_vat_k, sales_credit_vat_cogs_d, sales_credit_vat_inventory_k |
No |
SALES_RETURN |
sales_return_sales_return_d, sales_return_receivable_k, sales_return_inventory_d, sales_return_cogs_k |
Receivable/Cash |
SALES_DISCOUNT |
sales_discount_discount_d, sales_discount_receivable_k |
Receivable/Cash |
SALES_WRITE_OFF |
sales_writeoff_bad_debt_d, sales_writeoff_ar_k |
No |
PURCHASE_CASH |
purchase_cash_inventory_d, purchase_cash_cash_k |
Cash/Bank |
PURCHASE_CREDIT |
purchase_credit_inventory_d, purchase_credit_ap_k |
No |
PURCHASE_RETURN |
purchase_return_ap_d, purchase_return_inventory_k |
No |
STOCK_OPENING |
stock_opening_inventory_d, stock_opening_opening_balance_k |
No |
STOCK_ADJUSTMENT_PLUS |
stock_adjustment_plus_inventory_d, stock_adjustment_plus_gain_k |
No |
STOCK_ADJUSTMENT_MINUS |
stock_adjustment_minus_loss_d, stock_adjustment_minus_inventory_k |
No |
STOCK_TRANSFER |
No default journal mapping | Future customization |
STOCK_OPNAME_GAIN |
stock_opname_gain_inventory_d, stock_opname_gain_gain_k |
No |
STOCK_OPNAME_LOSS |
stock_opname_loss_loss_d, stock_opname_loss_inventory_k |
No |
CASH_IN |
cash_in_cash_d, cash_in_other_income_k |
Cash/Bank |
CASH_OUT |
cash_out_expense_d, cash_out_cash_k |
Expense Account, Cash/Bank |
BANK_TRANSFER |
bank_transfer_destination_bank_d, bank_transfer_source_bank_k |
Source Bank, Destination Bank |
JOURNAL_MANUAL |
No default mapping | Manual journal engine |
PETTY_CASH |
petty_cash_fund_d, petty_cash_cash_k |
Cash/Bank |
EXPENSE |
expense_expense_d, expense_cash_k |
Expense Account, Cash/Bank |
PREPAID_EXPENSE |
prepaid_expense_asset_d, prepaid_expense_cash_k, prepaid_expense_amortization_d, prepaid_expense_asset_k |
Expense Account on amortization, optional customization |
PAYROLL |
payroll_salary_expense_d, payroll_cash_k |
Cash/Bank |
PAYROLL_ACCRUAL |
payroll_accrual_expense_d, payroll_accrual_payable_k |
No |
ASSET_PURCHASE |
asset_purchase_asset_d, asset_purchase_cash_k |
Asset Account, Cash/Bank/AP |
ASSET_DEPRECIATION |
asset_depreciation_expense_d, asset_depreciation_accumulated_k |
No |
ASSET_DISPOSAL |
asset_disposal_accumulated_d, asset_disposal_asset_k |
No |
ASSET_REVALUATION |
asset_revaluation_asset_d, asset_revaluation_reserve_k |
No |
CUSTOMER_RECEIVABLE_PAYMENT |
receivable_payment_cash_d, receivable_payment_ar_k |
Cash/Bank |
CUSTOMER_RECEIVABLE_WRITE_OFF |
receivable_writeoff_bad_debt_d, receivable_writeoff_ar_k |
No |
VENDOR_PAYMENT |
vendor_payment_ap_d, vendor_payment_cash_k |
Cash/Bank |
VENDOR_PAYABLE_WRITE_OFF |
vendor_writeoff_ap_d, vendor_writeoff_income_k |
No |
TAX_OUTPUT |
tax_output_receivable_d, tax_output_vat_k |
Cash/Receivable |
TAX_INPUT |
tax_input_vat_d, tax_input_payable_k |
Cash/AP |
VAT_PAYMENT |
vat_payment_vat_d, vat_payment_cash_k |
Cash/Bank |
TAX_PAYMENT |
tax_payment_payable_d, tax_payment_cash_k |
Cash/Bank |
MONTH_END_CLOSING |
month_closing_revenue_d, month_closing_income_summary_k, month_closing_income_summary_d, month_closing_expense_k |
Revenue Accounts, Expense Accounts |
YEAR_END_CLOSING |
year_closing_income_summary_d, year_closing_retained_earnings_k |
No |
API Reference
All endpoints return the package response format from ApiResponse:
{
"status": 200,
"message": "Accounts retrieved successfully",
"data": []
}
Validation failures use the same wrapper:
{
"status": 422,
"message": "Validation Error",
"errors": {
"category_id": ["The category id field is required."]
},
"data": null
}
Base route
The package registers routes under the configured prefix:
GET|POST /api/accounting/...- Tenant-aware variants:
/{tenantId}/...under the same prefix
The current route set is:
GET /api/accounting/categoriesPOST /api/accounting/categoriesGET /api/accounting/categories/{id}PUT /api/accounting/categories/{id}DELETE /api/accounting/categories/{id}PATCH /api/accounting/categories/{id}/toggle-statusGET /api/accounting/accountsPOST /api/accounting/accountsGET /api/accounting/accounts/{id}PUT /api/accounting/accounts/{id}DELETE /api/accounting/accounts/{id}PATCH /api/accounting/accounts/{id}/toggle-statusGET /api/accounting/servicesPOST /api/accounting/servicesGET /api/accounting/services/{id}PUT /api/accounting/services/{id}DELETE /api/accounting/services/{id}PATCH /api/accounting/services/{id}/toggle-statusGET /api/accounting/journalsGET /api/accounting/journals/{id}GET /api/accounting/reports/general-ledgerGET /api/accounting/reports/trial-balanceGET /api/accounting/reports/profit-lossGET /api/accounting/reports/balance-sheetGET /api/accounting/reports/cash-flow
Accounts API
Create account
POST /api/accounting/accounts
Request body:
{
"category_id": 12,
"code": "1002",
"name": "Bank BCA",
"is_postable": true,
"is_active": true
}
Sample response:
{
"status": 201,
"message": "Account created successfully",
"data": {
"id": 125,
"category_id": 12,
"code": "1002",
"name": "Bank BCA",
"is_postable": true,
"is_active": true
}
}
List accounts
GET /api/accounting/accounts?search=1001
The documented design assumes each returned account includes its related category because every posting account must belong to exactly one category.
Services API
Create service with mappings
POST /api/accounting/services
Request body:
{
"service_code": "TEST_SERVICE",
"service_name": "Test Service",
"module_name": "TEST",
"mappings": [
{
"mapping_key": "test_d",
"mapping_name": "Test Debit",
"position": "D",
"account_id": 125
},
{
"mapping_key": "test_k",
"mapping_name": "Test Credit",
"position": "K",
"is_dynamic": true
}
]
}
Sample response:
{
"status": 201,
"message": "Service created successfully",
"data": {
"id": "a11d8b76-1d6a-4d4c-b2cb-7e4f8d8f67c2",
"service_code": "TEST_SERVICE",
"service_name": "Test Service",
"module_name": "TEST",
"mappings": []
}
}
Journal API
List journals
GET /api/accounting/journals
Query parameters supported by the controller:
pageper_pagesearchstart_dateend_datestatus
Get journal detail
GET /api/accounting/journals/{id}
The controller returns the journal header with loaded details.account and service relations.
Reports API
General ledger
GET /api/accounting/reports/general-ledger?account_id={account_id}&start_date=2026-01-01&end_date=2026-01-31
Request validation requires:
account_idas a numeric account identifierstart_dateas a dateend_dateas a date that is on or afterstart_date
Sample response shape:
{
"status": 200,
"message": "General Ledger retrieved successfully",
"data": {
"account": {
"id": 101,
"code": "1000",
"name": "Kas",
"category_path": ["ASSET", "Current Asset", "Cash & Cash Equivalent"]
},
"opening_balance": 0,
"details": []
}
}
Trial balance
GET /api/accounting/reports/trial-balance?year=2026&month=1
The report is structured by acc_account_categories and rolls balances from posting accounts into the category tree.
Example shape:
{
"data": [
{
"type": "ASSET",
"category_name": "Current Asset",
"balance": 175000000,
"children": [
{
"category_name": "Cash & Cash Equivalent",
"balance": 150000000,
"accounts": [
{"code": "1000", "name": "Kas", "balance": 10000000},
{"code": "1010", "name": "Bank BCA", "balance": 90000000},
{"code": "1011", "name": "Bank Mandiri", "balance": 50000000}
]
}
]
}
]
}
Profit & loss
GET /api/accounting/reports/profit-loss?year=2026&month=1
Example shape:
{
"data": {
"revenue": [
{
"category_name": "Sales Revenue",
"balance": 250000000,
"accounts": [
{"code": "4001", "name": "Penjualan Retail", "balance": 180000000},
{"code": "4002", "name": "Penjualan Online", "balance": 70000000}
]
}
],
"expense": [
{
"category_name": "Operating Expense",
"balance": 95000000,
"children": [
{"category_name": "Salary Expense", "balance": 60000000},
{"category_name": "Electricity Expense", "balance": 20000000},
{"category_name": "Water Expense", "balance": 15000000}
]
}
],
"net_profit": 155000000
}
}
Balance sheet
GET /api/accounting/reports/balance-sheet?year=2026&month=1
Example shape:
{
"data": {
"asset": [
{"category_name": "Current Asset", "balance": 175000000},
{"category_name": "Fixed Asset", "balance": 320000000}
],
"liability": [
{"category_name": "Current Liability", "balance": 85000000}
],
"equity": [
{"category_name": "Owner Equity", "balance": 410000000}
]
}
}
Cash flow
GET /api/accounting/reports/cash-flow?year=2026&month=1
Example shape:
{
"data": {
"operating": [
{"category_path": ["REVENUE", "Sales Revenue"], "balance": 250000000},
{"category_path": ["EXPENSE", "Operating Expense"], "balance": -95000000}
],
"investing": [
{"category_path": ["ASSET", "Fixed Asset"], "balance": -20000000}
],
"financing": [
{"category_path": ["LIABILITY", "Long Term Liability"], "balance": 40000000}
]
}
}
Internal service methods
These methods are available through the package but are not exposed as routes in the current codebase:
JournalService::journalByMapping(array $data)JournalService::journalManual(array $data)JournalService::post($id)CoaService::createAccount(array $data)CoaService::getTree()CoaService::activateAccount($id)CoaService::deactivateAccount($id)MappingService::findByKey(string $key)MappingService::getByService($serviceId)ClosingService::closeMonth($year, $month, $userId = null)ClosingService::closeThroughCurrentMonth($userId = null)ClosingService::closeUntilCurrentMonth($userId = null)ClosingService::reopenMonth($year, $month, $userId = null)
Architecture Flow
Business Module
->
Service Catalog
->
Account Mapping Engine
->
Journal Engine
->
Fiscal Period
->
Monthly Summary
->
Financial Reports
Data Flow
- A business module selects a standard service from the default
ServiceCatalogor a custom service inacc_services. - The account mapping engine validates the selected service and loads its debit/credit mapping rules.
JournalServicevalidates that every entry is balanced and that the fiscal period is not closed.- A journal header is created in
acc_journal_entries, with detail lines stored inacc_journal_entry_details. - If
accounting.journal.auto_postis enabled, the journal is immediately marked asposted. ClosingServiceaggregates posted journal activity intoacc_monthly_balancesfor each posting account.ReportServicereads posting-account balances and rolls them up throughacc_account_categories.parent_idto generate category-tree based reports.
Package Models
The package defines these main Eloquent models:
AccountCategoryAccountServiceServiceAccountJournalEntryJournalEntryDetailFiscalPeriodMonthlyBalanceReportMapping
In this documentation baseline, account hierarchy is modeled only in AccountCategory, while Account is treated as a posting leaf with mandatory category_id.
Notes for Integrators
- The package uses cache tags for accounts, categories, services, and journals.
AccountingPeriodLockedExceptionis thrown when posting into a closed fiscal period.- Route handlers support an optional tenant context when the path includes
{tenantId}and atenancy()helper is available. - Master data lookup is repository-driven so the same service layer works for single database, multi database, and multi tenant setups.
- The service layer currently shows a naming mismatch between controller or request fields (
status,mappings) and theServicemodel or schema (is_active,accounts()), so confirm service create or update behavior against the live app before depending on it. - The codebase currently includes only one feature test file for accounts; additional coverage for journals, reports, and closing would be a useful follow-up.