# Account Management

## Overview

Account Management provides CRUD operations for General Ledger (GL) accounts within a Chart of Accounts (CoA). A GL account is the fundamental building block of financial recording — every journal entry, sub-ledger posting, and financial report ultimately references one or more GL accounts. Each account carries a structured numeric code, a descriptive name, an account type drawn from the standard five-category classification (ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE), an account classification that describes its operational role (e.g., RECEIVABLE, PAYABLE, BANK_CASH, FIXED_ASSET, RETAINED_EARNINGS, NON_POSTING, REGULAR), and optional attributes such as currency restriction, reconciliation flag, default tax code reference, and tags.

Accounts are always scoped to a parent CoA and may optionally belong to an account group for hierarchical reporting. The account code follows a structured numbering convention (e.g., 1000-1999 for assets, 2000-2999 for liabilities) that encodes classification directly in the identifier, enabling consistent sorting and range-based queries across the ledger.

## Business Purpose

- **Standardized financial classification**: The five account types (ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE) map directly to the structure of the balance sheet and income statement, ensuring every transaction is categorized correctly
- **Operational account classification**: Beyond the five financial types, each account carries a classification that describes its operational role in the ERP system. Classifications include RECEIVABLE (sub-ledger integration with AR), PAYABLE (sub-ledger integration with AP), BANK_CASH (cash management and bank reconciliation), FIXED_ASSET (asset accounting integration), RETAINED_EARNINGS (equity roll-forward), NON_POSTING (a postability attribute that marks accounts as excluded from journal entry posting; this is not a hierarchy mechanism — structural hierarchy is owned exclusively by account groups), and REGULAR (general-purpose accounts with no special behavior). This classification drives downstream module behavior — for example, only RECEIVABLE accounts can serve as AR control accounts, and NON_POSTING accounts are excluded from journal entry target selection
- **Structured code numbering**: Numeric code ranges provide an immediately recognizable, sortable identifier that conveys account classification without requiring a lookup
- **Currency restriction**: Restricting an account to a specific currency prevents mixed-currency postings and simplifies reconciliation for foreign-currency bank or receivable accounts
- **Reconciliation control**: The reconciliation flag designates accounts (such as bank, accounts receivable, and accounts payable) where individual line items must be matched and cleared, supporting accurate cash and partner balance tracking. Accounts classified as RECEIVABLE, PAYABLE, or BANK_CASH must have reconciliation enabled — this is enforced as an invariant, not a default
- **Control account posting restrictions**: Accounts classified as RECEIVABLE or PAYABLE serve as sub-ledger control accounts. Direct manual journal postings to these accounts must be blocked; only automated postings from the AR/AP sub-ledger modules are permitted. This invariant preserves the integrity of the sub-ledger ↔ GL reconciliation and prevents manual entries from creating discrepancies between the sub-ledger detail and the GL summary balance
- **Structural immutability after posting**: Once an account has received posted transactions, structural fields (account code, account type, account classification, currency restriction) become immutable. This applies to all accounts but is especially critical for control accounts (RECEIVABLE, PAYABLE) where a classification change would break sub-ledger integration
- **Default tax code**: An optional free-text tax code identifier stored on the account for downstream use. When the `tax-configuration` module is implemented, this field will be migrated to a validated foreign-key reference against the tax code catalog. Until then, this module stores the identifier as free text with no external validation. The migration contract: the `tax-configuration` module will own the validation rule; this module will add a dependency and enforce referential integrity at that time
- **Tag-based categorization**: Tags provide flexible, cross-cutting labels for filtering and reporting beyond the rigid type and group hierarchy — for example, tagging accounts as "intercompany", "cost-center-allocable", or "cash-flow-relevant"
- **CoA and group membership**: Scoping accounts to a CoA and optional account group maintains structural integrity and supports hierarchical roll-up in trial balances and financial statements

## Process Flow

```mermaid
flowchart TD
    A[Start] --> B[Create Account in CoA]
    B --> C{All required fields valid?}
    C -- No --> D[Return validation error]
    C -- Yes --> E[Account created]
    E --> F{Update needed?}
    F -- Yes --> G[Update account attributes]
    G --> H{Account referenced in journal entries?}
    H -- Yes --> I[Restrict updates to non-structural fields]
    H -- No --> J[Allow full update]
    I --> E
    J --> E
    F -- No --> K{Delete requested?}
    K -- Yes --> L{Account in DRAFT status?}
    L -- No --> M[Reject deletion — must deactivate instead]
    L -- Yes --> L2{Account has posted transactions?}
    L2 -- Yes --> M
    L2 -- No --> N[Delete account]
    K -- No --> O[Read / List / Filter accounts]
```

```mermaid
flowchart TD
    subgraph Code Structure
        A1[1000-1999: ASSET] --> A2[2000-2999: LIABILITY]
        A2 --> A3[3000-3999: EQUITY]
        A3 --> A4[4000-4999: REVENUE]
        A4 --> A5[5000-5999: EXPENSE]
    end
```

## Scenario Patterns

- **Standard Account Creation**: A finance team sets up a new CoA and creates GL accounts following the structured numbering convention — cash at 1010 (BANK_CASH), accounts receivable at 1200 (RECEIVABLE), accounts payable at 2100 (PAYABLE), retained earnings at 3100 (RETAINED_EARNINGS), sales revenue at 4000 (REGULAR), and office supplies expense at 5200 (REGULAR)
- **Foreign Currency Bank Account**: A treasury team creates a bank account (ASSET, BANK_CASH, code 1015) with a currency restriction set to USD, ensuring only USD-denominated entries can be posted to it
- **Reconcilable Receivable Account**: An accounts receivable control account (ASSET, RECEIVABLE, code 1200) is created with the reconciliation flag enabled, allowing individual customer invoice and payment lines to be matched and cleared. The RECEIVABLE classification marks it as the AR sub-ledger control account
- **Non-Posting Control Account**: A clearing or suspense account (ASSET, NON_POSTING, code 1000) is created with the NON_POSTING classification, marking it as excluded from journal entry posting. This is strictly a postability control — it prevents the account from being selected as a target in journal entries. Structural grouping, subtotals, and report labels are owned by account groups and downstream reporting modules, not by account classification
- **Tax-Defaulted Revenue Account**: A domestic sales revenue account (REVENUE, REGULAR, code 4010) is created with a default tax code referencing the standard VAT rate, so journal lines hitting this account auto-populate the correct tax treatment
- **Tagged Intercompany Account**: An intercompany receivable account is created with RECEIVABLE classification and tagged with "intercompany" to enable cross-entity elimination reports without relying solely on account code ranges
- **Retained Earnings Account**: An equity account (EQUITY, RETAINED_EARNINGS, code 3100) is designated to receive the net income roll-forward at period close. Only one account per CoA should carry this classification
- **Fixed Asset Account**: Equipment account (ASSET, FIXED_ASSET, code 1500) is created with the FIXED_ASSET classification, enabling future integration with the fixed-asset module for depreciation scheduling
- **Account Group Assignment**: Multiple expense accounts (5100 salaries, 5200 rent, 5300 utilities) are assigned to an "Operating Expenses" account group for hierarchical organization
- **Blocked Manual Posting to Control Account**: A user attempts to create a manual journal entry debiting the AR control account (1200, RECEIVABLE); the posting is rejected because RECEIVABLE accounts only accept automated sub-ledger postings from the AR module
- **Prevented Deletion of Active Account**: An attempt to delete account 1010 (cash), which is in ACTIVE status, fails because only DRAFT accounts can be deleted; accounts with posted transactions must be deactivated instead
- **Prevented Structural Change on Posted Control Account**: An attempt to change the classification of account 2100 (PAYABLE) to REGULAR is rejected because the account has posted transactions; the PAYABLE classification is immutable after posting
- **Bulk Account Setup**: During initial CoA configuration, a batch of accounts is created from a template covering all five types with appropriate classifications, codes, names, and default attributes

## Test Cases

- Creating an account requires a valid parent CoA reference
- Account code must be unique within the same CoA
- Account code must be a structured numeric identifier and cannot be empty
- Account name is required and must be non-empty
- Account type must be one of ASSET, LIABILITY, EQUITY, REVENUE, or EXPENSE
- Account classification must be one of REGULAR, RECEIVABLE, PAYABLE, BANK_CASH, FIXED_ASSET, RETAINED_EARNINGS, or NON_POSTING
- Account classification defaults to REGULAR when not explicitly set
- Account classification must be compatible with account type: RECEIVABLE and BANK_CASH and FIXED_ASSET require type ASSET; PAYABLE requires type LIABILITY; RETAINED_EARNINGS requires type EQUITY; REGULAR and NON_POSTING are allowed with any account type
- NON_POSTING accounts cannot receive journal postings — downstream modules must exclude them from posting target selection. NON_POSTING is a postability attribute only; it does not imply parent-child relationships between accounts (hierarchy is owned by account groups)
- Only one account per CoA may carry the RETAINED_EARNINGS classification
- Currency restriction, when provided, must reference a valid currency from the primitives module
- An account with currency restriction only accepts postings in that currency
- Reconciliation flag defaults to false when not explicitly set for REGULAR, FIXED_ASSET, RETAINED_EARNINGS, and NON_POSTING classifications
- Accounts with RECEIVABLE, PAYABLE, or BANK_CASH classification must have reconciliation enabled; attempting to set reconciliation to false for these classifications is rejected
- Creating a RECEIVABLE or PAYABLE account automatically enforces reconciliation enabled as an invariant
- RECEIVABLE and PAYABLE accounts block direct manual journal postings; only sub-ledger module automated postings are permitted
- Attempting a direct manual journal posting to a RECEIVABLE or PAYABLE control account is rejected by downstream posting modules (this module marks the constraint; enforcement is at the GL/posting layer)
- Default tax code is an optional free-text identifier (validation against a tax configuration module is a future integration point)
- Tags are optional and may contain zero or more string labels
- An account can optionally belong to an account group within the same CoA
- Updating an account that has posted journal entries restricts changes to non-structural fields (name, tags, default tax code); structural fields (account code, account type, account classification, currency restriction) become immutable
- Updating account type, code, or classification is rejected when the account has posted transactions
- Changing account classification from RECEIVABLE or PAYABLE to any other classification is rejected once the account has posted transactions, preserving sub-ledger integrity
- Only DRAFT accounts with no posted transactions can be deleted (consistent with account lifecycle state machine)
- Deleting an ACTIVE or INACTIVE account is rejected regardless of transaction history; these accounts must be deactivated instead
- Deleting a DRAFT account that has posted transactions is rejected to preserve ledger integrity
- Listing accounts supports filtering by type, account group, tags, and currency restriction
- Accounts within a CoA can be retrieved sorted by account code
- Creating two accounts with the same code in the same CoA fails with a uniqueness constraint error
- Creating an account with a code outside valid numeric format fails validation
- Assigning an account to a group that belongs to a different CoA fails validation
- Creating an account emits an audit event via the audit module recording the acting user, timestamp, and initial field values
- Updating an account emits an audit event recording the previous and new field values for all changed attributes
- Deleting a DRAFT account emits an audit event recording the deletion and acting user
- Audit events for account operations include the CoA reference, account code, and account classification for traceability

## Reference Links

- [SAP GL Account Master Data](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/ee4e846b9aea4e78b6665038855e5148/4d9a976ecc0e7a30e10000000a15822b.html)
- [Odoo Chart of Accounts (account.account)](https://www.odoo.com/documentation/19.0/applications/finance/accounting/get_started/chart_of_accounts.html)
- [IFRS Conceptual Framework — Elements of Financial Statements](https://www.ifrs.org/issued-standards/list-of-standards/conceptual-framework/)
