# JournalEntry

## Description

JournalEntry is the core transactional entity of the finance-ledger module, implementing double-entry bookkeeping for all financial postings. A journal entry consists of a header record carrying the entry date, journal type, reference number, status, posting period, description, and optional source document reference, along with one or more JournalLine records. 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 nine journal types (Sales, Purchase, Cash, Bank, Miscellaneous, Opening, Closing, Inventory, Manufacturing) to categorize entries by their business origin.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

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

### Command Definitions

- [createJournalEntry](../commands/CreateJournalEntry.md) - Create a new journal entry in DRAFT status
- [updateJournalEntry](../commands/UpdateJournalEntry.md) - Update mutable fields of a DRAFT journal entry
- [deleteJournalEntry](../commands/DeleteJournalEntry.md) - Delete a DRAFT journal entry and all associated lines
- [addJournalLine](../commands/AddJournalLine.md) - Add a journal line to a DRAFT journal entry
- [updateJournalLine](../commands/UpdateJournalLine.md) - Update a journal line on a DRAFT journal entry
- [removeJournalLine](../commands/RemoveJournalLine.md) - Remove a journal line from a DRAFT journal entry
- [postJournalEntry](../commands/PostJournalEntry.md) - Post a balanced DRAFT journal entry to the general ledger
- [reverseJournalEntry](../commands/ReverseJournalEntry.md) - Reverse a posted journal entry via a mirror entry
- [processPurchaseHandoff](../commands/ProcessPurchaseHandoff.md) - Process a purchase subledger handoff event into a journal entry
- [processSalesHandoff](../commands/ProcessSalesHandoff.md) - Process a sales subledger handoff event into a journal entry
- [processInventoryHandoff](../commands/ProcessInventoryHandoff.md) - Process an inventory subledger handoff event into a journal entry
- [processManufacturingHandoff](../commands/ProcessManufacturingHandoff.md) - Process a manufacturing subledger handoff event into a journal entry

### Query Definitions

- [GetJournalEntry](../queries/GetJournalEntry.md) - Retrieve a journal entry by id with all lines
- [ListJournalEntries](../queries/ListJournalEntries.md) - List journal entries with filters (period, type, status, date range)

### Models

- JournalEntry
- JournalLine

### Invariants

- Journal entries can only be created in DRAFT status
- Journal entry requires a valid journal type (SALES, PURCHASE, CASH, BANK, MISCELLANEOUS, OPENING, CLOSING, INVENTORY, MANUFACTURING)
- 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
- Total debits must equal total credits in the functional currency for posting to succeed
- 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
- 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
- The reversal entry must target a period that is in OPEN or FUTURE_ENTERABLE status
- Journal type and entry date cannot be changed after the entry is posted
- Adjusting entries created during period-end close must have a valid adjustingEntryType (ACCRUAL, DEFERRAL, or RECLASSIFICATION)
- Posting a journal entry to a CLOSED, PERMANENTLY_CLOSED, or NEVER_OPENED period is rejected
- Balance validation for multi-currency entries is performed on the functional currency amounts, not the foreign currency amounts
- All journal entry lifecycle operations emit audit events via the audit module recording the acting user, timestamp, and field-level details

### Relationships

- **References Company**: Each journal entry is scoped to a Company via companyId (organization module)
- **References AccountingPeriod**: Each journal entry references an AccountingPeriod via accountingPeriodId
- **Has Many JournalLine**: A journal entry contains one or more journal lines
- **References JournalEntry (reversal)**: A reversal entry references the original via reversalOfId
