# ProcessSalesHandoff

## Permission Scope

subledgerHandoff

## Overview

processSalesHandoff receives a sales invoice or credit note handoff event from the sales module and creates a corresponding Sales-type journal entry in the general ledger. For invoices, the entry debits accounts receivable and credits the revenue account. For credit notes, the entry reverses these (debit revenue, credit accounts receivable). The command enforces idempotency via source document reference and returns an acknowledgment upon successful posting.

## Business Rules

- Receives a handoff event containing the sales invoice or credit note data, accounts receivable reference, and revenue account reference
- Creates a journal entry classified as Sales journal type
- For sales invoices: debits accounts receivable and credits the revenue account
- For credit notes: creates a reversing entry with debit to revenue and credit to accounts receivable
- The journal entry must be balanced (total debits equal total credits in functional currency)
- The journal entry carries a source document reference linking to the originating sales invoice or credit note
- Idempotent processing: duplicate events with the same source document reference return the original acknowledgment without re-posting
- Validates that the target accounting period is in OPEN or FUTURE_ENTERABLE status
- Validates that all referenced GL accounts are active in the Chart of Accounts
- Handles deprecated GL accounts with successor mappings by redirecting to the successor
- Supports multi-currency: foreign currency amounts are converted to base currency using the provided exchange rate
- Rejects events with zero or negative amounts
- Events are scoped to a company
- Returns an acknowledgment with the generated journal entry ID and posting timestamp
- Emits an audit event recording the source module, document reference, and journal entry created

## Process Flow

```mermaid
flowchart TD
    A[Receive sales handoff event] --> B{Idempotency check: already posted?}
    B -->|Yes| C[Return existing acknowledgment]
    B -->|No| D{Accounting period OPEN or FUTURE_ENTERABLE?}
    D -->|No| E[Return error: invalid period status]
    D -->|Yes| F{GL accounts valid and active?}
    F -->|No| G[Return error: account validation failed]
    F -->|Yes| H{Invoice or credit note?}
    H -->|Invoice| I[Create entry: debit AR, credit revenue]
    H -->|Credit note| J[Create entry: debit revenue, credit AR]
    I --> K[Post journal entry]
    J --> K
    K --> L[Emit audit event]
    L --> M[Return acknowledgment]
```

## External Dependencies

- [coa-management::getAccount](../../../coa-management/docs/queries/GetAccount.md) - Validates that AR and revenue GL accounts are active
- [primitives::getCurrency](../../../primitives/docs/queries/GetCurrency.md) - Validates currency for multi-currency transactions

## Error Scenarios

- **INVALID_PERIOD_STATUS**: Target accounting period is not in OPEN or FUTURE_ENTERABLE status
- **ACCOUNT_NOT_FOUND**: Referenced GL account does not exist
- **ACCOUNT_NOT_ACTIVE**: Referenced GL account is not in active status
- **INVALID_AMOUNT**: Handoff event contains zero or negative amounts
- **COMPANY_MISMATCH**: Handoff event company does not match the GL account company scope

## Test Cases

- returns existing entry for idempotent duplicate
- returns error when amount is zero or negative
- returns error when amount is negative
- returns error when period is not OPEN or FUTURE_ENTERABLE
- returns error when period is CLOSED
- returns error when company does not match period company
- returns error when debit account is not active
- returns error when credit account is not active
- returns error when debit account does not exist
- returns error when credit account does not exist
- creates journal entry with correct type and amounts
- handles multi-currency with exchange rate
- swaps debit and credit accounts for credit note document type
