# Journal Entry Management

## Overview

Journal Entry Management is the core transactional capability of the finance-ledger module, implementing double-entry bookkeeping for all financial postings. A journal entry consists of a header record (JournalEntry) carrying the entry date, journal type, reference number, status, posting period, description, and optional source document reference, along with one or more line records (JournalLine) that each specify a GL account (from coa-management), debit or credit amount, currency, exchange rate, functional currency amount, and line-level description. Every journal entry must satisfy the fundamental accounting constraint: total debits must equal total credits in the functional currency before it can be posted.

Journal entries follow a lifecycle of DRAFT to POSTED, with posted entries eligible for reversal via a mirror entry that produces a REVERSED status on the original. The feature supports six journal types (Sales, Purchase, Cash, Bank, Miscellaneous, Opening) to categorize entries by their business origin, and handles multi-currency postings by converting foreign currency amounts to the company's functional currency using a specified exchange rate. All mutations are recorded in an immutable audit trail, ensuring regulatory compliance and full traceability from entry creation through posting and reversal.

## Business Purpose

Organizations need a rigorous, auditable journal entry system to record financial transactions and maintain the integrity of their general ledger:

- **Double-entry enforcement**: Every journal entry must balance total debits against total credits in the functional currency, preventing one-sided postings that would corrupt the trial balance and financial statements
- **Journal type categorization**: Classifying entries by type (Sales, Purchase, Cash, Bank, Miscellaneous, Opening) mirrors the journal/book structure used by all major ERPs (Odoo, SAP, Oracle) and enables filtering, reporting, and access control by transaction origin
- **Lifecycle gating**: The DRAFT to POSTED progression ensures entries can be reviewed and corrected before they affect account balances. Only posted entries contribute to the general ledger, trial balance, and financial statements
- **Immutable posted entries**: Once posted, a journal entry cannot be modified or deleted — only reversed. This preserves the audit trail required by GAAP, IFRS, and local accounting regulations
- **Reversal via mirror entry**: Reversing a posted entry creates a new journal entry with inverted debit/credit lines rather than deleting the original, maintaining a complete history of all postings and corrections
- **Multi-currency support**: Companies operating across borders must record transactions in the original foreign currency while converting to the functional (base) currency for consolidated reporting. Exchange rate capture at posting time ensures accurate currency translation
- **Period-aware posting**: Journal entries reference an accounting period and can only be posted to periods that are in OPEN or FUTURE_ENTERABLE status, enforcing the period lifecycle managed by the accounting-period-management feature
- **Source document traceability**: Optional source document references (e.g., invoice number, bill ID) link journal entries back to their originating business transactions, supporting audit inquiries and subledger reconciliation
- **GL account validation with successor mapping**: Journal lines must reference active GL accounts from an active Chart of Accounts. When a GL account has been deprecated with a successor account mapping (from coa-management), postings are redirected to the successor account, ensuring continuity without manual re-entry
- **Audit trail**: Every state change — creation, update, posting, reversal — is recorded with the acting user, timestamp, and field-level details via the audit module, satisfying internal control and external audit requirements

## Process Flow

```mermaid
stateDiagram-v2
    [*] --> Draft: createJournalEntry
    Draft --> Draft: updateJournalEntry / addJournalLine / removeJournalLine
    Draft --> Posted: postJournalEntry
    Posted --> Reversed: reverseJournalEntry
    Draft --> [*]: deleteJournalEntry
```

```mermaid
flowchart TD
    A[Identify transaction to record] --> B[Create JournalEntry header in DRAFT]
    B --> C[Add JournalLine records with debit/credit amounts]
    C --> D{Multi-currency?}
    D -- Yes --> E[Specify foreign currency and exchange rate per line]
    E --> F[System converts to functional currency amounts]
    D -- No --> F
    F --> G{Debits = Credits in functional currency?}
    G -- No --> C
    G -- Yes --> H{GL accounts valid and active?}
    H -- No --> I[Resolve account issues]
    I --> C
    H -- Yes --> J{Target period OPEN or FUTURE_ENTERABLE?}
    J -- No --> K[Select valid posting period]
    K --> J
    J -- Yes --> L[Post journal entry]
    L --> M[Account balances updated]
```

```mermaid
flowchart TD
    A[Select posted journal entry to reverse] --> B[Create mirror entry with inverted debits/credits]
    B --> C{Reversal period OPEN or FUTURE_ENTERABLE?}
    C -- No --> D[Select valid reversal period]
    D --> C
    C -- Yes --> E[Post reversal entry]
    E --> F[Original entry status set to REVERSED]
    F --> G[Original entry balances offset to zero]
```

## Scenario Patterns

- **Manual Miscellaneous Entry**: An accountant creates a miscellaneous journal entry to record an adjusting entry (e.g., accrual or reclassification) that does not originate from a subledger. The entry is created in DRAFT, journal lines are added with appropriate debit/credit accounts and amounts, the balance is verified, and the entry is posted to the current open period
- **Sales Invoice Posting**: The sales module generates a journal entry of type Sales with a debit to Accounts Receivable and a credit to Revenue, referencing the sales invoice as the source document. The entry flows through the subledger integration contract and is posted automatically upon invoice confirmation
- **Purchase Bill Recording**: A purchase bill triggers a journal entry of type Purchase with a debit to the relevant expense or inventory account and a credit to Accounts Payable. The source document reference links back to the purchase bill for reconciliation
- **Multi-Currency Transaction**: A company records a purchase invoice denominated in a foreign currency. The journal lines capture both the foreign currency amount and the exchange rate, with the system computing the functional currency equivalent. The entry balances in the functional currency, and any rounding difference is posted to a designated exchange gain/loss account
- **Opening Balance Entry**: During initial system setup or at the start of a new fiscal year, an Opening-type journal entry is created to establish beginning balances for all balance sheet accounts. This entry typically carries a posting date matching the first day of the new fiscal period
- **Error Correction via Reversal**: A posted journal entry is discovered to contain an incorrect account assignment. The accountant reverses the original entry (creating a mirror posting that offsets all amounts) and then creates a new corrective entry with the correct accounts. The original entry, its reversal, and the correction all remain in the ledger for full auditability
- **Cash Receipt Recording**: A cash payment received from a customer is recorded as a Cash-type journal entry, debiting the bank/cash account and crediting Accounts Receivable. The source document reference links to the customer payment record
- **Bank Transaction Entry**: A bank fee or interest charge is recorded as a Bank-type journal entry, debiting bank charges expense and crediting the bank account, referencing the bank statement line as the source document
- **Successor Account Redirect**: An accountant creates a journal line referencing a GL account that has been deprecated with a successor mapping in coa-management. The system automatically redirects the posting to the successor account, logs the redirect in the audit trail, and posts the entry without requiring manual correction
- **Period Boundary Posting**: An entry needs to be recorded in a future accounting period that is in FUTURE_ENTERABLE status. The entry is created and posted to that period, allowing advance preparation of known transactions before the current period closes

## Test Cases

- Journal entry lifecycle follows DRAFT -> POSTED state machine; posted entries can transition to REVERSED
- Journal entry can only be created in DRAFT status
- Journal entry requires a valid journal type (Sales, Purchase, Cash, Bank, Miscellaneous, Opening)
- Journal entry requires a valid entry date
- Journal entry requires a reference number that is unique within the company scope
- Journal entry must reference a valid companyId; the company must be in ACTIVE status
- Journal entry must reference a valid accounting period; the period must be in OPEN or FUTURE_ENTERABLE status for posting
- Journal entry description is optional and can be updated while in DRAFT status
- Source document reference is optional and can be set on creation or update in DRAFT status
- A journal entry must have at least two journal lines before it can be posted
- Each journal line must reference a valid GL account from an ACTIVE Chart of Accounts
- Each journal line must specify either a debit amount or a credit amount (not both, and not zero for both)
- Debit and credit amounts must be positive values
- Total debits must equal total credits in the functional currency for posting to succeed
- An unbalanced journal entry (debits != credits in functional currency) cannot be posted
- Posting a balanced DRAFT journal entry transitions its status to POSTED
- Posting an already POSTED journal entry fails with an invalid status transition error
- Posted journal entries cannot be modified (no field updates, no line additions or removals)
- Posted journal entries cannot be deleted; only reversal is permitted
- DRAFT journal entries can be deleted; deletion removes all associated journal lines
- Deleting a DRAFT journal entry emits an audit event recording the deletion and acting user
- Journal lines can be added to or removed from a DRAFT journal entry
- Journal lines cannot be added to or removed from a POSTED or REVERSED journal entry
- Updating a journal line (account, amount, description) is only permitted while the parent entry is in DRAFT status
- Reversing a posted entry creates a new journal entry with all debit/credit amounts inverted
- The reversal entry references the original entry and is automatically posted
- The original entry status transitions from POSTED to REVERSED upon successful reversal
- Reversing an entry that is already REVERSED fails with an invalid status transition error
- Reversing a DRAFT entry fails with an invalid status transition error
- The reversal entry must target a period that is in OPEN or FUTURE_ENTERABLE status
- Multi-currency journal lines must specify a currency code and exchange rate
- The functional currency amount is computed as the foreign amount multiplied by the exchange rate
- Journal lines without an explicit currency default to the company's functional (base) currency with an exchange rate of 1.0
- Balance validation for multi-currency entries is performed on the functional currency amounts, not the foreign currency amounts
- Posting a journal entry to a CLOSED or PERMANENTLY_CLOSED period is rejected
- Posting a journal entry to a NEVER_OPENED period is rejected
- Journal lines referencing an inactive or draft GL account are rejected at posting time
- Journal lines referencing a deprecated GL account with a successor mapping are redirected to the successor account at posting time
- Successor account redirect is recorded in the audit trail with the original and successor account references
- If the successor account is itself inactive or deprecated without a further successor, the posting is rejected
- Creating a journal entry emits an audit event via the audit module recording the acting user, timestamp, and initial field values
- Updating a journal entry (header fields or line changes) emits an audit event recording previous and new field values
- Posting a journal entry emits an audit event recording the status transition from DRAFT to POSTED and the posting user
- Reversing a journal entry emits an audit event recording the status transition from POSTED to REVERSED, the reversal entry reference, and the acting user
- Journal entries are scoped to a company; entries from different companies are fully isolated
- Only users with appropriate permissions (from user-management) can create, post, or reverse journal entries
- Unauthorized users receive a permission denied error when attempting journal entry operations
- A journal entry with a single line (only debit or only credit) cannot be posted
- Journal type cannot be changed after the entry is posted
- The entry date cannot be changed after the entry is posted

## Reference Links

- [Odoo Journal Entries and Journal Types](https://www.odoo.com/documentation/19.0/applications/finance/accounting/get_started/cheat_sheet.html)
- [SAP S/4HANA Journal Entry Processing](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/ee3509643e954b3da2e0ebfa846c02e1/4cae4cc1ecaa7286e10000000a42189b.html)
- [Oracle Fusion General Ledger Journal Entries](https://docs.oracle.com/en/cloud/saas/financials/24d/oafcf/journal-entries.html)
- [NetSuite Journal Entry Overview](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_N3444641.html)
- [Sage Intacct Journal Entries](https://www.sage.com/en-us/blog/what-is-a-journal-entry-in-accounting/)
