# UpdateSupplierAccount

## Permission Scope

partner

## Overview

Updates mutable supplier 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 supplier 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 and default expense account may be set or cleared
- A changed default expense account must exist, be active, have type `EXPENSE`, and belong to the supplier account's company

## 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
- [coa-management::Account](../../../coa-management/docs/model/Account.md) - Validates a changed default expense account

## Error Scenarios

- **INVALID_NAME**: Name is empty, whitespace-only, or not provided
- **SUPPLIER_ACCOUNT_NOT_FOUND**: Account does not exist
- **CURRENCY_NOT_FOUND**: Referenced currency does not exist
- **DEFAULT_EXPENSE_ACCOUNT_NOT_FOUND**: Referenced default expense account does not exist
- **INVALID_DEFAULT_EXPENSE_ACCOUNT**: Default expense account is inactive, is not an EXPENSE account, or belongs to another company
- **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
- adds a new default bank usage and clears the previous default
- returns not found without saving
- sets and clears a valid default expense account
- rejects a missing default expense account
- rejects an invalid default expense account
