# UpdateCustomerAccount

## Permission Scope

partner

## Overview

Updates mutable customer account fields and applies add, update, and remove changes to address and bank account usages.

## Business Rules

- Omitted account names retain their current value; a supplied name cannot be null, empty, or whitespace-only
- Within an account, each address and purpose combination and each bank account and purpose combination must be unique after applying all changes
- Target customer account must exist
- The account row is locked before usages are loaded and changed
- Added address references are locked before bank account references, each in ascending ID order, before ownership and availability are checked
- Added usages must reference active address or bank account records owned by the account's partner
- Updated and removed usage IDs must belong to the target account
- Address and bank account identity and purpose are immutable; changing either requires remove plus add
- Setting a usage as default automatically clears the previous default for the same purpose
- At most one newly selected default may be supplied for each purpose in one request
- Optional preferred currency must exist

## Process Flow

```mermaid
flowchart TD
    A[Receive update request] --> B[Lock and load account aggregate]
    B --> C{Account exists?}
    C -->|No| D[Return not found]
    C -->|Yes| C1{Changed preferred currency valid?}
    C1 -->|No| F[Return validation error]
    C1 -->|Yes or unchanged| C2[Lock added addresses then bank accounts in ID order]
    C2 --> E{Added references valid?}
    E -->|No| F
    E -->|Yes| G[Merge header and usage changes]
    G --> H[Normalize defaults per purpose]
    H --> I[Save aggregate differences]
```

## External Dependencies

- [primitives::Currency](../../../primitives/docs/model/Currency.md) - Validates a changed preferred currency

## Error Scenarios

- **INVALID_NAME**: Name is empty, whitespace-only, or not provided
- **CUSTOMER_ACCOUNT_NOT_FOUND**: Account does not exist
- **CURRENCY_NOT_FOUND**: Referenced currency does not exist
- **ADDRESS_NOT_FOUND**: Specified address ID does not exist
- **BANK_ACCOUNT_NOT_FOUND**: Specified bank account ID does not exist
- **ADDRESS_INACTIVE**: Address is unavailable for a new usage
- **BANK_ACCOUNT_INACTIVE**: Bank account is unavailable for a new usage
- **ADDRESS_NOT_OWNED_BY_PARTNER**: Address belongs to another partner
- **BANK_ACCOUNT_NOT_OWNED_BY_PARTNER**: Bank account belongs to another partner
- **ADDRESS_USAGE_NOT_FOUND**: Address usage does not exist on the target account
- **BANK_ACCOUNT_USAGE_NOT_FOUND**: Bank account usage does not exist on the target account
- **DUPLICATE_DEFAULT_USAGE**: More than one default was supplied for a purpose
- **DUPLICATE_ADDRESS_USAGE**: Address and purpose combination already exists in the account
- **DUPLICATE_BANK_ACCOUNT_USAGE**: Bank account and purpose combination already exists in the account

## Test Cases

- loads the partner aggregate under a lock
- returns DuplicateAddressUsageError without saving
- rejects an inactive address under the reference lock without saving
- returns DuplicateBankAccountUsageError without saving
- rejects an inactive bankaccount under the reference lock without saving
- updates the aggregate with line-style usage changes and switches the default
- returns not found without saving
- rejects an inactive address added as a new usage
- rejects an update for a usage outside the account
