# ProcessPurchaseHandoff

## Permission Scope

subledgerHandoff

## Overview

processPurchaseHandoff receives a purchase bill handoff event from the purchase module and creates a corresponding Purchase-type journal entry in the general ledger. The entry debits the expense or asset account and credits the accounts payable account, reflecting the payable recognition for a validated purchase bill. The command enforces idempotency via source document reference to prevent duplicate postings from retry scenarios. Upon successful posting, it returns a PurchaseBillPostingAcknowledgment to the purchase module.

## Business Rules

- Receives a handoff event containing the purchase bill amount, expense/asset account reference, and accounts payable account reference
- Creates a journal entry classified as Purchase journal type
- Debits the expense or asset account and credits the accounts payable account
- 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 purchase bill
- 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; events from one company cannot post to another company's GL
- Returns a PurchaseBillPostingAcknowledgment 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 purchase 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{Amounts valid and positive?}
    H -->|No| I[Return error: invalid amounts]
    H -->|Yes| J[Create Purchase journal entry with debit expense/asset, credit AP]
    J --> K[Post journal entry]
    K --> L[Emit audit event]
    L --> M[Return PurchaseBillPostingAcknowledgment]
```

## External Dependencies

- [coa-management::getAccount](../../../coa-management/docs/queries/GetAccount.md) - Validates that expense/asset and AP 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 does not exist
- returns error when credit account does not exist
- returns error when debit account is not active
- returns error when credit account is not active
- creates journal entry with correct type and amounts
- handles multi-currency with exchange rate
