General Ledger
The general ledger module provides account-level transaction queries and the enhanced trial balance report (ميزان المراجعة). All endpoints read from posted journal entries only and do not modify data.
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/accounting/ledger/account/{id} | Account ledger with opening balance |
GET | /api/accounting/ledger/trial-balance | Enhanced trial balance |
GET | /api/accounting/ledger/balances | Multiple account balances |
Trial Balance (ميزان المراجعة)
Returns opening, period movement, and closing balances for each account. Supports date-range filtering, zero-balance inclusion, and account-type filtering.
Opening-balance entries
Journal entries of type opening always count toward the opening balance column, never toward period movement — even when the report's date_from equals the fiscal year's start date (the opening JE is dated on that date). This keeps opening + period movement = closing exact and prevents the opening balance being double-counted.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
date_from | date | No | — | Start of period. Entries before this date become opening balance |
date_to | date | No | — | End of period |
include_zero_balances | boolean | No | false | Include accounts with no movement |
level | string | No | detail | Account type filter: detail, header, or all |
fiscal_year_id | integer | No | — | Filter by fiscal year |
Request
curl -X GET \
"https://your-domain.com/api/accounting/ledger/trial-balance?date_from=2026-01-01&date_to=2026-06-30" \
-H "Authorization: Bearer {token}"final response = await dio.get('/api/accounting/ledger/trial-balance', queryParameters: {
'date_from': '2026-01-01',
'date_to': '2026-06-30',
});Response
{
"data": [
{
"account_id": 1,
"account_code": "1101",
"account_name": "النقد في الصندوق",
"classification": "assets",
"nature": "debit",
"is_header": false,
"opening_debit": 50000.000,
"opening_credit": 0,
"period_debit": 15000.000,
"period_credit": 8000.000,
"closing_debit": 57000.000,
"closing_credit": 0,
"total_debit": 65000.000,
"total_credit": 8000.000,
"balance": 57000.000
}
],
"totals": {
"total_opening_debit": 50000.000,
"total_opening_credit": 50000.000,
"total_period_debit": 15000.000,
"total_period_credit": 15000.000,
"total_closing_debit": 57000.000,
"total_closing_credit": 57000.000
},
"warnings": []
}Column Descriptions
| Column | Arabic | Description |
|---|---|---|
opening_debit | رصيد أول المدة مدين | Debit balance before date_from |
opening_credit | رصيد أول المدة دائن | Credit balance before date_from |
period_debit | حركة الفترة مدين | Debit movement between date_from and date_to |
period_credit | حركة الفترة دائن | Credit movement between date_from and date_to |
closing_debit | رصيد آخر المدة مدين | Opening + period debit net balance |
closing_credit | رصيد آخر المدة دائن | Opening + period credit net balance |
is_header | حساب رئيسي | true when the row is a header (control) account that carries direct postings — an anomaly (see warnings) |
Header-Account Warnings
A journal entry line must always target a detail account — header (control) accounts only aggregate their children's balances. Posting directly onto one makes the amount vanish from the detail-level report and unbalances the trial balance.
If any posted line sits on a header account, that account is included in the detail-level report anyway (so totals still foot) with is_header: true, and the response warnings array describes it:
"warnings": [
{
"type": "posting_on_header_account",
"account_id": 1,
"account_code": "1",
"account_name": "الأصول",
"closing_debit": 744.95,
"closing_credit": 0,
"message": "Journal entry lines cannot be posted to header (control) account(s): 1. ..."
}
]This condition is also reported by GET /api/accounting/integrity-check (posted_lines_on_header_accounts). New postings to header accounts are now rejected by the API — see Journal Entries.
Behavior Notes
- When no
date_fromis provided, all entries are treated as period movement (opening = 0) - When
include_zero_balances=true, all accounts of the selectedlevelare returned even if they have no entries - The
classificationfield values are:assets,liabilities,equity,revenue,expenses - Legacy fields
total_debit,total_credit, andbalanceare preserved for backward compatibility
Account Ledger
Returns all posted journal entry lines for a specific account, with an opening balance and per-entry running balance.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
from_date | date | No | Start date filter |
to_date | date | No | End date filter |
cost_center_id | integer | No | Filter by cost center |
Request
curl -X GET \
"https://your-domain.com/api/accounting/ledger/account/1?from_date=2026-01-01&to_date=2026-12-31" \
-H "Authorization: Bearer {token}"Response
{
"data": {
"opening_balance": 1500.000,
"entries": [
{
"id": 42,
"journal_entry_id": 15,
"entry_number": "JV-2026-00015",
"date": "2026-01-20",
"entry_description": "Office supplies purchase",
"debit": 500.000,
"credit": 0,
"base_debit": 500.000,
"base_credit": 0,
"running_balance": 2000.000,
"line_number": 1,
"description": null
}
]
}
}Multiple Account Balances
Returns balances for specific accounts, or all detail accounts when no IDs are provided.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
account_ids | string | No | Comma-separated account IDs. Omit for all detail accounts |
as_of_date | date | No | Date to calculate balance up to |
Request
curl -X GET \
"https://your-domain.com/api/accounting/ledger/balances?account_ids=1,2,3" \
-H "Authorization: Bearer {token}"Response
{
"data": [
{
"account_id": 1,
"account_code": "1101",
"account_name": "Cash on Hand",
"total_debit": 65000.000,
"total_credit": 8000.000,
"balance": 57000.000,
"nature": "debit"
}
]
}