PHPackages                             elgibor-solution/laravel-accounting - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. elgibor-solution/laravel-accounting

ActiveLibrary[Database &amp; ORM](/categories/database)

elgibor-solution/laravel-accounting
===================================

Enterprise-grade accounting engine for Laravel applications.

1.0.0(2d ago)001MITPHPPHP &gt;=8.2

Since Jun 8Pushed 2d agoCompare

[ Source](https://github.com/elgiborsolution/laravel-accounting)[ Packagist](https://packagist.org/packages/elgibor-solution/laravel-accounting)[ RSS](/packages/elgibor-solution-laravel-accounting/feed)WikiDiscussions main Synced yesterday

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

Laravel Accounting Package
==========================

[](#laravel-accounting-package)

`elgibor-solution/laravel-accounting` is a Laravel 11/12 accounting package that provides chart-of-accounts management, journal entry storage, service-to-account mappings, monthly closing, and financial reporting.

Overview
--------

[](#overview)

The package is built around a small set of accounting entities:

- Account categories
- Accounts
- Business services and account mappings
- Journal entries and journal entry details
- Fiscal periods
- Monthly balances
- Report mappings

The package also ships with API controllers, reusable services, seeders, a factory, and package migrations. Tables are prefixed by default with `acc_`.

Features
--------

[](#features)

- Chart of accounts with hierarchical parent/child 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 &amp; loss, balance sheet, and cash flow reports
- Tenant-aware routes when a `{tenantId}` segment is used

### What is not in this package

[](#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
------------

[](#installation)

1. Require the package in your Laravel application.

```
composer require elgibor-solution/laravel-accounting
```

2. Publish the configuration file if you want to customize defaults.

```
php artisan vendor:publish --tag=accounting-config
```

3. Publish the package migrations if you want them in your application database folder.

```
php artisan vendor:publish --tag=accounting-migrations
```

4. Run migrations.

```
php artisan migrate
```

5. Seed the default chart of accounts and account categories.

```
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.

Configuration
-------------

[](#configuration)

The package configuration lives in [`config/accounting.php`](./config/accounting.php).

### Available options

[](#available-options)

```
return [
    'table_prefix' => 'acc_',
    'journal' => [
        'auto_post' => true,
        'number_format' => 'JV/{YEAR}/{MONTH}/{SEQ}',
    ],
    'fiscal' => [
        'start_month' => 1,
    ],
    'route' => [
        'prefix' => 'api/accounting',
        'middleware' => ['api'],
    ],
];
```

- `table_prefix` controls every package table name.
- `journal.auto_post` determines whether a created journal is immediately posted.
- `journal.number_format` is used by `JournalService::generateJournalNo()` to build the journal number.
- `fiscal.start_month` is present for fiscal-year configuration.
- `route.prefix` and `route.middleware` control package API routing.

Usage
-----

[](#usage)

### Resolve the high-level service

[](#resolve-the-high-level-service)

[`src/Services/AccountingService.php`](./src/Services/AccountingService.php) is a convenience wrapper that exposes the package services:

```
use ESolution\LaravelAccounting\Services\AccountingService;

$accounting = app(AccountingService::class);

$journalService = $accounting->journal();
$coaService = $accounting->coa();
$mappingService = $accounting->mapping();
$closingService = $accounting->closing();
$reportService = $accounting->report();
```

### Create a manual journal

[](#create-a-manual-journal)

[`JournalService::journalManual()`](./src/Services/JournalService.php) accepts balanced debit/credit lines and creates a journal entry with details.

```
use ESolution\LaravelAccounting\Services\JournalService;

$journal = app(JournalService::class)->journalManual([
    'trx_date' => '2026-01-15',
    'description' => 'Office expense payment',
    'items' => [
        [
            'account_code' => '5100',
            'type' => 'D',
            'amount' => 150000,
            'description' => 'Office supplies',
        ],
        [
            'account_code' => '1000',
            'type' => 'K',
            'amount' => 150000,
            'description' => 'Cash payment',
        ],
    ],
]);
```

### Create a journal from a mapped service

[](#create-a-journal-from-a-mapped-service)

[`JournalService::journalByMapping()`](./src/Services/JournalService.php) validates `service_code`, checks mapping keys, and can auto-post when enabled in configuration.

```
use ESolution\LaravelAccounting\Services\JournalService;

$journal = app(JournalService::class)->journalByMapping([
    'service_code' => 'SALE_INVOICE',
    '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

[](#read-the-chart-of-accounts-tree)

[`CoaService::getTree()`](./src/Services/CoaService.php) returns categories with top-level accounts and nested children.

```
use ESolution\LaravelAccounting\Services\CoaService;

$tree = app(CoaService::class)->getTree();
```

### Close a month

[](#close-a-month)

[`ClosingService::closeMonth()`](./src/Services/ClosingService.php) 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());
```

### Generate reports

[](#generate-reports)

[`ReportService`](./src/Services/ReportService.php) provides report methods that are also exposed through the API.

```
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);
```

API Reference
-------------

[](#api-reference)

All endpoints return the package response format from [`ApiResponse`](./src/Traits/ApiResponse.php):

```
{
  "status": 200,
  "message": "Accounts retrieved successfully",
  "data": []
}
```

Validation failures use the same wrapper:

```
{
  "status": 422,
  "message": "Validation Error",
  "errors": {
    "account": ["Cannot delete account with children"]
  },
  "data": null
}
```

### Base route

[](#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/categories`
- `POST /api/accounting/categories`
- `GET /api/accounting/categories/{id}`
- `PUT /api/accounting/categories/{id}`
- `DELETE /api/accounting/categories/{id}`
- `PATCH /api/accounting/categories/{id}/toggle-status`
- `GET /api/accounting/accounts`
- `POST /api/accounting/accounts`
- `GET /api/accounting/accounts/{id}`
- `PUT /api/accounting/accounts/{id}`
- `DELETE /api/accounting/accounts/{id}`
- `PATCH /api/accounting/accounts/{id}/toggle-status`
- `GET /api/accounting/services`
- `POST /api/accounting/services`
- `GET /api/accounting/services/{id}`
- `PUT /api/accounting/services/{id}`
- `DELETE /api/accounting/services/{id}`
- `PATCH /api/accounting/services/{id}/toggle-status`
- `GET /api/accounting/journals`
- `GET /api/accounting/journals/{id}`
- `GET /api/accounting/reports/general-ledger`
- `GET /api/accounting/reports/trial-balance`
- `GET /api/accounting/reports/profit-loss`
- `GET /api/accounting/reports/balance-sheet`
- `GET /api/accounting/reports/cash-flow`

### Accounts API

[](#accounts-api)

#### Create account

[](#create-account)

`POST /api/accounting/accounts`

Request body:

```
{
  "category_id": "b3d2f1d0-3a8b-4b42-9d18-3b3e7d1f2f11",
  "code": "1002",
  "name": "Bank BCA",
  "status": true
}
```

Sample response:

```
{
  "status": 201,
  "message": "Account created successfully",
  "data": {
    "id": "6c3c8b8d-8c3f-4e5c-a8ea-2d8d3ef2f4f1",
    "category_id": "b3d2f1d0-3a8b-4b42-9d18-3b3e7d1f2f11",
    "code": "1002",
    "name": "Bank BCA",
    "status": true
  }
}
```

#### List accounts

[](#list-accounts)

`GET /api/accounting/accounts?search=1001`

The controller loads the related category in the cached result.

### Services API

[](#services-api)

#### Create service with mappings

[](#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": "UUID-OF-ACCOUNT"
    },
    {
      "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

[](#journal-api)

#### List journals

[](#list-journals)

`GET /api/accounting/journals`

Query parameters supported by the controller:

- `page`
- `per_page`
- `search`
- `start_date`
- `end_date`
- `status`

#### Get journal detail

[](#get-journal-detail)

`GET /api/accounting/journals/{id}`

The controller returns the journal header with loaded `details.account` and `service` relations.

### Reports API

[](#reports-api)

#### General ledger

[](#general-ledger)

`GET /api/accounting/reports/general-ledger?account_id={uuid}&start_date=2026-01-01&end_date=2026-01-31`

Request validation requires:

- `account_id` as a UUID
- `start_date` as a date
- `end_date` as a date that is on or after `start_date`

Sample response shape:

```
{
  "status": 200,
  "message": "General Ledger retrieved successfully",
  "data": {
    "account": {
      "id": "6c3c8b8d-8c3f-4e5c-a8ea-2d8d3ef2f4f1",
      "code": "1000",
      "name": "Kas"
    },
    "opening_balance": 0,
    "details": []
  }
}
```

#### Trial balance

[](#trial-balance)

`GET /api/accounting/reports/trial-balance?year=2026&month=1`

#### Profit &amp; loss

[](#profit--loss)

`GET /api/accounting/reports/profit-loss?year=2026&month=1`

#### Balance sheet

[](#balance-sheet)

`GET /api/accounting/reports/balance-sheet?year=2026&month=1`

#### Cash flow

[](#cash-flow)

`GET /api/accounting/reports/cash-flow?year=2026&month=1`

### Internal service methods

[](#internal-service-methods)

These methods are available through the package but are not exposed as routes in the current codebase:

- [`JournalService::journalByMapping(array $data)`](./src/Services/JournalService.php)
- [`JournalService::journalManual(array $data)`](./src/Services/JournalService.php)
- [`JournalService::post($id)`](./src/Services/JournalService.php)
- [`CoaService::createAccount(array $data)`](./src/Services/CoaService.php)
- [`CoaService::getTree()`](./src/Services/CoaService.php)
- [`CoaService::activateAccount($id)`](./src/Services/CoaService.php)
- [`CoaService::deactivateAccount($id)`](./src/Services/CoaService.php)
- [`MappingService::findByKey(string $key)`](./src/Services/MappingService.php)
- [`MappingService::getByService($serviceId)`](./src/Services/MappingService.php)
- [`ClosingService::closeMonth($year, $month, $userId = null)`](./src/Services/ClosingService.php)
- [`ClosingService::reopenMonth($year, $month, $userId = null)`](./src/Services/ClosingService.php)

Data Flow
---------

[](#data-flow)

1. A business event is transformed into a journal either manually or through a service mapping.
2. `JournalService` validates that every entry is balanced and that the fiscal period is not closed.
3. A journal header is created in `acc_journal_entries`, with detail lines stored in `acc_journal_entry_details`.
4. If `accounting.journal.auto_post` is enabled, the journal is immediately marked as `posted`.
5. `ClosingService` aggregates posted journal activity into `acc_monthly_balances` for each account.
6. `ReportService` reads `acc_monthly_balances`, `acc_report_mappings`, and posted journal details to generate reports.

Package Models
--------------

[](#package-models)

The package defines these main Eloquent models:

- [`AccountCategory`](./src/Models/AccountCategory.php)
- [`Account`](./src/Models/Account.php)
- [`Service`](./src/Models/Service.php)
- [`ServiceAccount`](./src/Models/ServiceAccount.php)
- [`JournalEntry`](./src/Models/JournalEntry.php)
- [`JournalEntryDetail`](./src/Models/JournalEntryDetail.php)
- [`FiscalPeriod`](./src/Models/FiscalPeriod.php)
- [`MonthlyBalance`](./src/Models/MonthlyBalance.php)
- [`ReportMapping`](./src/Models/ReportMapping.php)

All package models use UUID primary keys through the shared `HasUuid` trait.

Notes for Integrators
---------------------

[](#notes-for-integrators)

- The package uses cache tags for accounts, categories, services, and journals.
- `AccountingPeriodLockedException` is thrown when posting into a closed fiscal period.
- Route handlers support an optional tenant context when the path includes `{tenantId}` and a `tenancy()` helper is available.
- The service layer currently shows a naming mismatch between controller/request fields (`status`, `mappings`) and the `Service` model/schema (`is_active`, `accounts()`), so confirm service create/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.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance99

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 66.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

2d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/5470172?v=4)[esolution](/maintainers/esolution)[@esolution](https://github.com/esolution)

---

Top Contributors

[![elgibor-solution](https://avatars.githubusercontent.com/u/783039?v=4)](https://github.com/elgibor-solution "elgibor-solution (2 commits)")[![bayuelgibor](https://avatars.githubusercontent.com/u/162445024?v=4)](https://github.com/bayuelgibor "bayuelgibor (1 commits)")

### Embed Badge

![Health badge](/badges/elgibor-solution-laravel-accounting/health.svg)

```
[![Health](https://phpackages.com/badges/elgibor-solution-laravel-accounting/health.svg)](https://phpackages.com/packages/elgibor-solution-laravel-accounting)
```

###  Alternatives

[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11122.5M32](/packages/anourvalar-eloquent-serialize)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135212.4k7](/packages/statamic-rad-pack-runway)[mozex/laravel-scout-bulk-actions

Import, flush, and queue-import all your Laravel Scout searchable models at once. Auto-discovers models, runs in bulk, tracks progress.

1437.7k](/packages/mozex-laravel-scout-bulk-actions)[ramadan/easy-model

A Laravel package for enjoyably managing database queries.

111.6k](/packages/ramadan-easy-model)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
