# UpdateJournalEntry

## Permission Scope

journalEntry

## Overview

updateJournalEntry revises the editable draft journal document. Header changes travel as a `headerPatch` (omitted = untouched, `null` = clear, value = update). Line changes travel as three explicit collections: `addLines` (new lines), `updateLines` (`{ lineId, patch }` pairs), and `removeLineIds`. Existing lines are updated in place so line IDs and `createdAt` stay stable across edits; lines the client never loaded are simply absent and remain untouched. The command returns the updated document root (header); callers read lines separately.

## Business Rules

- Journal entry must exist and be in DRAFT status
- Only DRAFT entries can be updated; CANCELLED and POSTED entries are immutable
- Entry date can be updated through `headerPatch` while in DRAFT status
- Description, source document type, and source document ID can be updated through `headerPatch` while in DRAFT status
- At least one header or line change must be provided
- `addLines` and the merged result of `updateLines` patches must satisfy the same validation rules as create
- `updateLines` mutate existing JournalLine rows in place; `removeLineIds` delete only the referenced draft lines
- Lines not listed in `addLines`, `updateLines`, or `removeLineIds` are untouched
- The effective line set after add/update/remove must contain at least two prepared journal lines
- Each effective journal line must reference an existing ACTIVE GL account
- Each effective journal line must specify either a debit amount or a credit amount, not both
- Journal line amounts are recorded in the company's base currency; multi-currency conversion is outside the current scope

## Process Flow

```mermaid
flowchart TD
    A[Receive update journal entry request] --> B{Journal entry exists?}
    B -->|No| C[Return error: journal entry not found]
    B -->|Yes| D{Entry status is DRAFT?}
    D -->|No| E[Return error: cannot modify non-draft entry]
    D -->|Yes| I[Apply headerPatch + addLines/updateLines/removeLineIds]
    I --> J{Effective lines valid?}
    J -->|No| K[Return line validation error]
    J -->|Yes| L[Apply line additions, in-place updates, and removals]
    L --> M[Return updated journal entry]
```

## External Dependencies

- [coa-management::ListAccounts](../../../coa-management/docs/query/ListAccounts.md) - Validates that all referenced GL accounts exist and are ACTIVE

## Error Scenarios

- **JOURNAL_ENTRY_NOT_FOUND**: Referenced journal entry does not exist
- **INVALID_STATUS_FOR_UPDATE**: Journal entry is not in DRAFT status; CANCELLED and POSTED entries cannot be modified
- **EMPTY_JOURNAL_ENTRY_CHANGES**: Update contains no header or line changes
- **JOURNAL_LINE_NOT_FOUND**: Referenced journal line does not exist on the draft journal entry
- **INVALID_ENTRY_DATE**: Entry date is not a valid date
- **MINIMUM_LINES_NOT_MET**: Journal entry has fewer than two journal lines
- **ACCOUNT_NOT_FOUND**: Referenced GL account does not exist
- **ACCOUNT_INACTIVE**: Referenced GL account is not ACTIVE
- **INVALID_DEBIT_CREDIT**: Debit/credit values violate constraints

## Test Cases

- returns error when journal entry does not exist
- returns error when journal entry is in POSTED status
- returns error when journal entry is in CANCELLED status
- updates description on a DRAFT journal entry without touching lines
- updates source document on a DRAFT journal entry
- updates entry date on a DRAFT journal entry
- adds a new line without touching existing lines
- updates a line in place
- removes a line while preserving remaining lines
- returns error when neither headerPatch nor line changes are provided
- returns error when headerPatch entryDate is invalid
- returns error when a line edit references a missing line
- returns error when effective lines would drop below two
- returns error when an added or updated line account does not exist
- returns error when an added or updated line account is inactive
- returns error when an added or updated line has invalid debit and credit values
- passes custom header and line fields through incremental changes
