# Finance Module — Business & Functional Documentation

## Overview

The Finance module is the general ledger backbone of the ERP. It provides double-entry bookkeeping, voucher management, expense tracking, and a suite of financial reports. Every monetary transaction in the system — sales invoices, purchase receipts, payroll disbursements, ad-hoc expenses — ultimately flows through this module as GL entries in the journal. The module enforces fiscal period discipline, requires balanced entries before posting, and produces auditable records that cannot be silently modified once posted.

---

## Chart of Accounts

### Account Types

Every account belongs to exactly one of five standard accounting types:

- **Asset** — resources owned by the company (cash, bank, accounts receivable, inventory, fixed assets). The normal balance for asset accounts is a debit balance.
- **Liability** — amounts owed to outsiders (accounts payable, loans, accruals). Normal balance is credit.
- **Equity** — ownership interest and retained earnings. Normal balance is credit.
- **Income** (Revenue) — money earned from operations. Normal balance is credit.
- **Expense** — costs incurred in operations. Normal balance is debit.

### Account Hierarchy

Accounts are organized in a parent-child tree. A top-level account (e.g., "Current Assets") is called a header account and sits at level 1. Its children (e.g., "Cash at Bank") sit at level 2, and so on. The system automatically calculates the level of a new account based on its parent.

Only leaf accounts flagged as **postable** (`is_postable = true`) can receive journal entry lines. Header accounts exist purely for grouping and reporting. This ensures that detail is captured at the right level and that totals roll up cleanly.

Accounts support bilingual names (Arabic and English) stored as JSON. The active locale governs which name appears on reports.

Accounts use soft deletes — removing an account marks it as deleted rather than erasing its history.

### Account Codes

Each account has a unique alphanumeric code. Reports and the General Ledger sort and group accounts by code, so the code structure should mirror the hierarchy (e.g., 1000 for total assets, 1100 for current assets, 1110 for cash).

---

## Fiscal Year and Periods

### Fiscal Year

A fiscal year has a name, a start date (`date_from`), and an end date (`date_to`). Its status is either **open** or **closed**. When a fiscal year is created the system automatically generates four quarterly fiscal periods (Q1 through Q4) that together span the full year range.

### Fiscal Periods

Each fiscal period is a quarter of the parent fiscal year. A period has its own status — **open** or **closed** — independent of the year status. Periods are the granular unit of financial reporting: the trial balance, balance sheet, and income statement are all filtered to a single period.

### Closing Periods

A period is closed by an authorized user. Once closed, no new journal entries may be posted to it and no existing entries in that period may be modified or deleted. This prevents retroactive alteration of finalized financials. A period can be administratively reopened when needed.

### Closing the Year

Closing a fiscal year sets the year status to closed. The system does not automatically close all child periods when a year is closed — each period must be individually closed. Year closure is a management-level control action tracked with a timestamp and the identity of the closing user.

### Why Periods Matter

Every financial transaction — journal entry, receipt voucher, payment voucher — is stamped with both a fiscal year ID and a fiscal period ID. This allows all reporting to be period-scoped. The posting engine checks that the target period is open before permitting any GL write. The `account_period_balances` table stores running balances per account per period, enabling fast report generation without re-summing all historical lines every time.

---

## Journals

A journal is a named, coded book in which related entries are grouped (analogous to a subledger type). Examples: General Journal, Cash Receipts Journal, Cash Payments Journal. Journals have an active/inactive flag. All journal entries must be assigned to a journal. Journals support bilingual names and use soft deletes.

---

## Journal Entries and Lines

### What a Journal Entry Is

A journal entry is the atomic unit of accounting. It records a business event by simultaneously increasing some accounts and decreasing others. The sum of debits across all lines must equal the sum of credits — this is the double-entry rule.

A journal entry header records the journal, date, branch, fiscal year, fiscal period, and a free-text description. The entry is assigned a unique sequential entry number generated from the branch's number sequence for the current year.

Each entry has one or more **lines**. Every line names:
- The account to be affected
- Whether the amount is a debit or a credit (one must be positive, the other zero)
- An optional cost center
- An optional line-level description

### Status Lifecycle: Draft to Posted

Journal entries begin in **draft** status. In draft, they can be edited or deleted freely. A draft entry has no impact on any account balances.

When a user with the `finance.entries.post` permission clicks Post, the system runs through a series of checks before making any changes. If all checks pass the entry is stamped as **posted** with the posting timestamp and the poster's identity. Posted entries are immutable — the edit and delete actions are blocked.

There is no "reversed" status on journal entries themselves. Reversals are handled at the voucher level for receipt and payment vouchers.

### Posting Checks

Before a journal entry can be posted the system verifies all of the following:

- The entry must currently be in draft status.
- The entry must have at least one line.
- The target fiscal period must be open (not closed).
- Every line must have exactly one non-zero value (either debit or credit, not both, not neither).
- Every line's account must exist and must be flagged as postable.
- The total debits across all lines must exactly equal the total credits (checked to two decimal places).

If any check fails the posting is rejected with an error message and no changes are made.

---

## Posting Flow — How GL Balances Are Built

When a journal entry is posted, the system updates the `account_period_balances` table. This table holds one row per unique combination of (branch, fiscal year, fiscal period, account, cost center). Each row carries three pairs of monetary columns:

- **opening debit / opening credit** — the balance inherited from the end of the previous period
- **period debit / period credit** — the cumulative movement within the current period
- **closing debit / closing credit** — the net result (opening + period movement)

For each line in the posting:
1. The system looks for an existing balance row matching the line's account, cost center, branch, and period. If none exists, it walks backwards through prior periods to find the most recent closing balance and uses that as the opening balance for the new row. If there is no prior history, both opening columns start at zero.
2. The period debit and period credit columns are incremented by the line's amounts.
3. The closing balance is recalculated: opening debit minus opening credit plus period debit minus period credit gives a net figure. If the net is positive (or zero) it goes into closing debit with closing credit set to zero; if the net is negative the absolute value goes into closing credit with closing debit set to zero.

All of this happens inside a single database transaction with row-level locking to prevent race conditions when multiple entries post simultaneously to the same account.

---

## Reversal Flow

Reversal applies to receipt vouchers and payment vouchers (not to raw journal entries). When a posted voucher is reversed the system:

1. Creates a new voucher record with status immediately set to "posted" and notes referencing the original voucher code and the reason.
2. Creates a new journal entry (also immediately posted) whose lines are the mirror image of the original: every debit becomes a credit and every credit becomes a debit, for the same amounts and accounts.
3. Records a reversal record linking the original voucher ID to the reversal voucher ID, along with the reason, the reversing user, and the timestamp.
4. Sets the original voucher's status to **reversed**.

The reversal entry uses a fresh sequential entry number drawn from the same number sequence as the original. The reversal is posted to the same fiscal period as the original, so the fiscal period must still be open at the time of reversal.

---

## Expenses

### Expense Categories and Items

Expenses are organized in a two-level taxonomy. An **expense category** groups related items (e.g., "Office Costs"). An **expense item** belongs to a category and represents a specific type of spending (e.g., "Office Supplies"). Each item has a code and a name and can be enabled or disabled.

### Creating and Submitting Expenses

A user creates an expense in **draft** status by specifying the expense item, amount, date, branch, and optionally a cost center, fiscal period, payment method, and description. Supporting documents can be attached as files. The draft can be freely edited or deleted.

When the expense is ready for review the creator submits it. Submission moves the status to **pending** and locks editing to users with the approve permission.

### Approval and Rejection

An approver reviews the pending expense and either approves or rejects it. Approval sets status to **approved** and records the approver identity and timestamp. Rejection sets status to **rejected** and can capture a rejection note. Only pending expenses can be approved or rejected.

### Payment

Once approved an expense can be paid. Payment is recorded through a payment voucher (see below). The expense tracks a `paid_amount` and a `paid_status` of unpaid, partially paid, or paid. An expense can be partially paid over multiple payments until the full amount is settled.

Cancelled expenses (which can only be cancelled while in draft or pending) and rejected expenses are excluded from payment processing and most reports.

---

## Receipt Vouchers

### Purpose

A receipt voucher documents money received by the business. The most common use is collecting payment from a customer against an invoice. The voucher records the amount, the date, the customer, the bank or cash account where the money was deposited (`received_in_account`), the source account (typically the customer's AR account, `from_account`), and an optional invoice reference.

### Status Lifecycle

Receipt vouchers follow the same draft → approved → posted → reversed lifecycle as the broader posting framework.

- **Draft**: Fully editable.
- **Approved**: Locked against further edits. The approval step is a control gate before the GL is touched.
- **Posted**: GL entries have been created. The voucher is immutable. A posted journal entry is linked via `posted_journal_entry_id`.
- **Reversed**: A reversal entry has been created. The original voucher is frozen.
- **Cancelled**: Cancelled before posting (no GL impact).

### Posting a Receipt Voucher

Posting requires the voucher to be in approved status and to have a journal assigned. The fiscal period must be open. On posting the system creates a journal entry with two lines:

- Debit: the `received_in_account` (cash or bank) for the full amount — money enters the business's cash/bank account.
- Credit: the `from_account` (accounts receivable or income) for the full amount — the receivable is cleared.

### AR Integration

When a sales payment is collected through the `InvoiceCollectionService`, the service validates that the invoice is not already fully paid and that the payment amount does not exceed the remaining balance. It then creates a receipt voucher in draft status and immediately updates the invoice's `paid_amount` and `payment_status` (unpaid, partially paid, or paid).

---

## Payment Vouchers

### Purpose

A payment voucher documents money paid out by the business. It records the amount, date, beneficiary name and reference, the bank or cash account from which payment was made (`paid_from_account`), an offset account (typically an expense or payable account), and an optional expense reference.

### Status Lifecycle

Identical to receipt vouchers: draft → approved → posted → reversed → cancelled.

### Posting a Payment Voucher

Posting requires approved status and an assigned journal. On posting the system creates a journal entry with two lines:

- Debit: the `offset_account` (expense or payable) — the cost is recognized.
- Credit: the `paid_from_account` (cash or bank) — money leaves the account.

### Expense Payment Integration

When an approved expense is paid via the `ExpensePaymentService`, the service validates that the expense is approved, not yet fully paid, and that the payment amount does not exceed the remaining unpaid balance. It creates a payment voucher in draft status and updates the expense's `paid_amount` and `paid_status`.

### Reversal

Reversing a posted payment voucher creates a mirror voucher and a mirror journal entry (debit and credit swapped), and a `PaymentReversal` record linking original and reversal. The original voucher is set to reversed.

---

## Cost Centers

Cost centers allow expenses and GL lines to be tagged to an organizational unit (department, project, region, etc.) below the branch level. A cost center has a code, a bilingual name, an optional parent cost center (hierarchical), and is scoped to a branch. Every journal entry line and expense record can optionally carry a cost center ID.

The `account_period_balances` table stores balances separately per cost center, enabling cost center-level reporting on the trial balance. Rows with no cost center use a `cost_center_key` of zero; rows with a cost center use the cost center's database ID as the key.

Cost centers use soft deletes.

---

## Reports

All financial reports require the user to select a branch, a fiscal year, and a fiscal period before data is displayed. The reports read from pre-computed balance rows (`account_period_balances`) rather than re-aggregating raw journal entry lines, making them fast even for large datasets.

### Trial Balance

Shows every account that had any movement or balance in the selected period. For each account the report displays:

- Opening debit and credit (balance carried from the previous period)
- Period debit and credit (movement within the current period)
- Closing debit and credit (net balance at period end)

Column totals are provided. A filter option can include or exclude accounts with all-zero balances. The trial balance is the primary tool for verifying that the double-entry equation holds: total closing debits must equal total closing credits.

### Balance Sheet

Categorizes accounts by type and shows their closing balance at the end of the selected period. Asset and expense accounts show a positive amount when their closing debit exceeds their closing credit. Liability, equity, and income accounts show a positive amount when their closing credit exceeds their closing debit.

The current-period net profit or net loss (total income account balances minus total expense account balances) flows into equity as the period's net result. The report verifies that total assets equal total liabilities plus equity (including the net result). An `is_balanced` flag is returned to indicate whether the accounting equation holds within a 0.01 rounding tolerance.

### Income Statement

Shows only income and expense type accounts for the selected period. Income amounts are calculated as credit movement minus debit movement. Expense amounts are calculated as debit movement minus credit movement. The summary shows total revenues, total expenses, and the net result (profit if positive, loss if negative).

### General Ledger

A transaction-by-transaction drill-down for a single account. The user selects the account, branch, and optionally a fiscal year, fiscal period, and date range. The report shows each posted journal entry line touching that account, in date order, with the journal name, entry number, entry date, description, cost center, debit, credit, and a running balance. The running balance is maintained as a cumulative debit-minus-credit net figure.

### Cash and Bank Movement

A paginated register of all posted journal entry lines for a selected account (filtered to cash or bank type accounts). Filterable by account, branch, and date range. Shows entry number, date, descriptions, debit, credit, and totals for net movement.

### Receipt Register

A paginated listing of all non-cancelled receipt vouchers. Filterable by branch, customer, status, and date range. Shows amounts and provides totals for count and total amount.

### Payment Register

A paginated listing of all non-cancelled payment vouchers. Filterable by branch, payment method, beneficiary name, status, and date range.

### Expense Register

A paginated listing of all non-cancelled, non-rejected expenses. Filterable by branch, expense item, cost center, status, paid status, and date range. Shows totals for count, total amount, and total paid.

### Expense Analytics

Aggregated views of expense spending, filterable by branch, expense item, cost center, and date range. Provides three pivot groupings — by expense item, by branch, and by cost center — each showing count, total amount, and total paid. A grand-total summary is also returned.

### AR Aging (Accounts Receivable)

A customer-level aging report showing outstanding invoice balances bucketed by how many days past due each invoice is. The buckets are: current (not yet due), 1–30 days overdue, 31–60 days, 61–90 days, and over 90 days. Only invoices in confirmed or posted status that are not fully paid are included.

Because the AR ledger account is shared across all customers, the report also provides a reconciliation figure comparing the ERP-side open balance (sum of unpaid invoice amounts) against the GL-side balance of all AR accounts. Any difference indicates a timing gap between invoice confirmation and GL posting.

A customer statement view is also available, showing the customer's opening balance, all invoices (as debits), all payments (as credits), and all returns (as credits) within a user-selected date range, with a running balance.

### AP Aging (Accounts Payable)

A supplier-level aging report built from posted purchase receipts. Bucketed the same way as AR aging. A supplier statement is also available showing receipt activity within a date range.
