# UpdateAccount

## Permission Scope

accountManagement

## Overview

updateAccount modifies attributes of an existing GL account. Structural fields (code, type, classification, currency restriction) become immutable once the account has posted transactions. Non-structural fields (name, tags, default tax code, account group assignment) remain updatable regardless of posting history.

## Business Rules

- Target account must exist
- Parent CoA must be in DRAFT or ACTIVE status; updating an account under an ARCHIVED CoA is rejected
- Name, when provided, must be non-empty
- Account code, when changed, must be a valid structured numeric identifier
- Account code, when changed, must remain unique within the CoA
- If the account has posted transactions, structural fields (code, type, classification, currency restriction) cannot be changed
- Classification changes must maintain compatibility with account type
- Changing classification to RETAINED_EARNINGS is rejected if another account in the CoA already has that classification
- The reconciliation invariant is enforced on every update: accounts with RECEIVABLE, PAYABLE, or BANK_CASH classification must have reconciliation enabled. Explicitly setting isReconciliation to false on an account that currently has (or is being changed to) one of these classifications is rejected. This applies to three update paths: (1) changing classification to RECEIVABLE/PAYABLE/BANK_CASH while isReconciliation is false, (2) setting isReconciliation to false on an account already classified as RECEIVABLE/PAYABLE/BANK_CASH, and (3) simultaneously changing classification to one of these types and setting isReconciliation to false
- Classification changes that affect posting constraints must update the stored constraint accordingly: changing to NON_POSTING marks the account as excluded from posting target selection; changing to RECEIVABLE or PAYABLE marks the account as a control account blocking manual postings; changing away from these classifications removes the corresponding constraint (all enforced by downstream posting modules)
- Currency restriction changes must reference a valid currency from the primitives module. An account with currency restriction only accepts postings in that currency (enforced by downstream posting modules)
- Account group changes must reference a group in the same CoA, and the account code must fall within the group's number range (if defined)
- When the account code is changed and the account belongs to an account group (whether the group assignment is also changing or staying the same), the new code must fall within the target group's number range (if defined). This applies to both cases: changing only the code while keeping the current group, and changing both the code and the group simultaneously

## Process Flow

```mermaid
flowchart TD
    A[Receive update request] --> B{Account exists?}
    B -->|No| C[Return error: not found]
    B -->|Yes| B2{Parent CoA is DRAFT or ACTIVE?}
    B2 -->|No| B3[Return error: CoA is ARCHIVED]
    B2 -->|Yes| D{Structural fields changed?}
    D -->|Yes| E{Account has posted transactions?}
    E -->|Yes| F[Return error: structural fields immutable]
    E -->|No| D2{Code changed?}
    D2 -->|Yes| D3{Valid numeric format?}
    D3 -->|No| D4[Return error: invalid code format]
    D3 -->|Yes| D5{Code unique within CoA?}
    D5 -->|No| D6[Return error: duplicate code]
    D5 -->|Yes| G{Validate other structural changes}
    D2 -->|No| G
    D -->|No| H{Validate non-structural changes}
    G --> H
    H --> H2{Reconciliation invariant violated?}
    H2 -->|Yes| H3[Return error: reconciliation required]
    H2 -->|No| I[Update account fields]
    I --> J[Return updated account]
```

## External Dependencies

- [primitives::getCurrency](../../../primitives/docs/queries/GetCurrency.md) - Validates currency restriction changes

## Error Scenarios

- **ACCOUNT_NOT_FOUND**: Specified account ID does not exist
- **CHART_OF_ACCOUNTS_ARCHIVED**: Parent CoA is in ARCHIVED status and cannot be modified
- **NAME_REQUIRED**: Name is empty, whitespace-only, or not provided
- **INVALID_ACCOUNT_CODE**: Account code is not a valid structured numeric identifier
- **DUPLICATE_ACCOUNT_CODE**: An account with the same code already exists in the CoA
- **STRUCTURAL_FIELD_IMMUTABLE**: Attempted to change a structural field on an account with posted transactions
- **INCOMPATIBLE_CLASSIFICATION**: Account classification is not compatible with the account type
- **DUPLICATE_RETAINED_EARNINGS**: Another account in the CoA already has RETAINED_EARNINGS classification
- **RECONCILIATION_REQUIRED**: Reconciliation cannot be disabled for RECEIVABLE, PAYABLE, or BANK_CASH classifications
- **CURRENCY_NOT_FOUND**: Referenced currency does not exist or is not active
- **ACCOUNT_GROUP_NOT_FOUND**: Referenced account group does not exist or belongs to a different CoA
- **CODE_OUT_OF_RANGE**: Account code does not fall within the assigned group's number range

## Test Cases

- returns error when account does not exist
- returns error when parent CoA is ARCHIVED
- returns error when name is empty
- returns error when new account code is not a valid numeric identifier
- returns error when new account code already exists in the CoA
- returns error when changing structural field on account with posted transactions
- returns error when new classification is incompatible with type
- returns error when changing to RETAINED_EARNINGS and another account already has it
- returns error when new currency does not exist
- returns error when new group belongs to different CoA
- updates name of an account
- updates tags of an account
- updates default tax code
- updates account group assignment
- updates structural fields when no posted transactions exist
- returns error when setting isReconciliation to false on a RECEIVABLE account
- returns error when setting isReconciliation to false on a PAYABLE account
- returns error when setting isReconciliation to false on a BANK_CASH account
- returns error when changing classification to RECEIVABLE with isReconciliation explicitly set to false
- enforces reconciliation when classification changes to RECEIVABLE
- marks account as excluded from posting when classification changes to NON_POSTING
- marks account as control account blocking manual postings when classification changes to RECEIVABLE
- marks account as control account blocking manual postings when classification changes to PAYABLE
- removes posting exclusion when classification changes from NON_POSTING to REGULAR
- updates currency restriction and posting currency constraint accordingly
- returns error when account code outside new group number range
- returns error when changing code to a value outside the current group's number range
- returns error when changing classification from RECEIVABLE after posted transactions
- returns error when changing classification from PAYABLE after posted transactions
- passes custom fields through to update
