# CalculatePurchasePrice

## Overview

CalculatePurchasePrice resolves the best matching supplier purchase price rule for a draft purchase-order line and returns the suggested unit price together with the matched rule reference.

## Business Rules

- Inputs include `companyId`, `supplierId`, `itemId`, ordered quantity, purchase unit, transaction currency, and order date
- Only active price lists and active price rules participate in matching
- Rule precedence is deterministic across supplier, price-list priority, item scope, purchase unit, minimum quantity break, effective date, and explicit rule priority
- When purchase unit differs from the rule comparison unit, quantity comparison uses shared unit conversion
- Currency conversion is out of scope; rules match only when the requested currency equals the rule currency
- Returns null suggestion when no active rule matches and manual pricing is expected

## Process Flow

```mermaid
flowchart TD
    A[Receive supplier, item, qty, unit, currency, date] --> B{Item and units valid?}
    B -->|No| C[Return reference error]
    B -->|Yes| D[Load active lists and active rules for supplier/company]
    D --> E[Normalize quantity for rule comparison]
    E --> F[Filter rules by item, UoM, currency, date, and min qty]
    F --> G{Rule found?}
    G -->|No| H[Return null suggestion]
    G -->|Yes| I[Pick highest precedence rule]
    I --> J[Return suggested unit price and matched rule reference]
```

## External Dependencies

- [item-management::GetItem](../../../item-management/docs/queries/GetItem.md) - Validate item existence and active status
- [primitives::ConvertQuantity](../../../primitives/docs/queries/ConvertQuantity.md) - Compare quantity breaks across compatible units of measure

## Error Scenarios

- **ITEM_NOT_FOUND**: Referenced item does not exist
- **ITEM_NOT_ACTIVE**: Referenced item is inactive
- **UNIT_NOT_FOUND**: Referenced purchase unit does not exist
- **CURRENCY_NOT_FOUND**: Referenced currency does not exist
- **INCOMPATIBLE_UNITS**: Quantity-break comparison requires units that do not share a conversion path
- **NO_MATCHING_PRICE_RULE**: No active rule matches; caller receives null suggestion

## Test Cases

- returns the best matching rule for a valid supplier item context
- picks the higher quantity-break rule when ordered quantity qualifies
- ignores inactive price lists and inactive rules
- returns null when no active rule matches
- returns error when item does not exist
- returns error when item is inactive
- returns error when units are incompatible for quantity-break comparison
