# JournalEntry

## Description

JournalEntry is the accounting document root for the financial-accounting module. It owns the journal lifecycle, company scope, accounting period, entry date, description, optional structured source document reference, and its JournalLine records. JournalLine records are debit or credit lines within the journal entry; each line references a GL account from the coa-management module, specifies either a debit or credit amount, and can include an optional line-level description. DRAFT journal entries use JournalLine records as prepared lines. Cancelling a draft transitions it to CANCELLED while preserving the header and prepared lines for traceability. Posting validates the prepared lines and transitions the JournalEntry to POSTED; after posting, those JournalLine records are the immutable ledger surface used for balances, reporting, and traceability. Journal entries follow a lifecycle of DRAFT to either CANCELLED or POSTED, with posted corrections represented by separate posted reversal entries rather than by changing the original entry status.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Draft: createJournalEntry
    Draft --> Draft: updateJournalEntry
    Draft --> Cancelled: cancelJournalEntry
    Draft --> Posted: postJournalEntry
```

| Operation | From | To | Command |
|-----------|------|----|---------|
| cancel | DRAFT | CANCELLED | [cancelJournalEntry](../command/CancelJournalEntry.md) |
| post | DRAFT | POSTED | [postJournalEntry](../command/PostJournalEntry.md) |

### Command Definitions

- [createJournalEntry](../command/CreateJournalEntry.md) - Create a new journal entry in DRAFT status with initial prepared lines
- [updateJournalEntry](../command/UpdateJournalEntry.md) - Update mutable header fields and incrementally add, update, or remove prepared lines of a DRAFT journal entry
- [cancelJournalEntry](../command/CancelJournalEntry.md) - Cancel a DRAFT journal entry while preserving its prepared lines
- [postJournalEntry](../command/PostJournalEntry.md) - Post a balanced DRAFT journal entry to the general ledger
- [reverseJournalEntry](../command/ReverseJournalEntry.md) - Reverse a posted journal entry via a mirror entry

### Query Definitions

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

### Models

- JournalEntry
- JournalLine

### Invariants

- Journal entries can only be created in DRAFT status
- Journal entry requires a valid entry date
- Journal entry must reference a valid companyId
- Journal entry must reference a valid accounting period; the period must be in OPEN status for posting
- Journal entry description is optional and can be updated while in DRAFT status
- Source document type and source document ID are 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 an existing GL account
- 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 for posting to succeed
- Posting makes the JournalLine records immutable posted ledger lines
- JournalLine records can only be added to, updated on, or removed from a DRAFT journal entry
- Line-level description is optional and can be updated while the parent journal entry is in DRAFT status
- RECEIVABLE and PAYABLE classified accounts block direct manual journal postings (control account constraint)
- 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 cancelled; cancellation preserves the header and prepared JournalLine records
- CANCELLED journal entries cannot be updated, posted, reversed, or deleted
- 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 remains POSTED after reversal so its original journal lines stay in the ledger
- Reversing an entry that already has a reversal entry fails; `reversalOfId` is a one-to-one self relation to enforce one reversal per original entry
- The reversal entry must target a period that is in OPEN status
- Entry date cannot be changed after the entry is posted
- Posting a journal entry to a CLOSED, PERMANENTLY_CLOSED, or NEVER_OPENED period is rejected

### 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 via journalEntryId
- **JournalLine References Account**: Each journal line references a GL Account via accountId (coa-management module)
- **References JournalEntry (reversal)**: A reversal entry references the original via reversalOfId
