# CreatePurchaseOrder

## Permission Scope

purchaseOrder

## Overview

CreatePurchaseOrder creates a new purchase order in `DRAFT` status with supplier, company, receiving context, and one or more commercial lines. Snapshots (supplier name, payment-term lines, item name/SKU) are captured at creation time.

## Business Rules

- Company, supplier, order date, and at least one line are required
- Supplier must exist, be active, and carry the `SUPPLIER` role
- Each line must reference an active item, a unit of measure, have quantity greater than zero, and have a non-negative unit price — ACTIVE is required because this is a new transaction entry point; inactive items should not start new procurement (contrast with post-order flows like PostGoodsReceipt where inactive items are accepted to avoid blocking in-progress fulfillment)
- A header-level receiving site is optional; when provided it must reference a valid site and serves as the default for lines that do not specify their own
- Lines may optionally override the header receiving site; if provided, the override must also reference a valid site
- A payment term must be specified at creation time and must be active
- Order is always created with `orderStatus` = `DRAFT`; `receiptStatus` and `billingStatus` are not set at creation

## Process Flow

```mermaid
flowchart TD
    A[Receive create request] --> B{Supplier valid and active?}
    B -->|No| C[Return supplier error]
    B -->|Yes| D{At least one line?}
    D -->|No| E[Return EMPTY_PURCHASE_ORDER_LINES]
    D -->|Yes| F{Validate each line item, quantity, price, site}
    F -->|No| G[Return line validation error]
    F -->|Yes| H{Payment term active?}
    H -->|No| I[Return payment term error]
    H -->|Yes| J[Insert order and lines in DRAFT]
    J --> K[Return created order]
```

## External Dependencies

- [business-partner::GetPartner](../../../business-partner/docs/queries/GetPartner.md) - Validate supplier identity, status, and supplier role
- [item-management::GetItem](../../../item-management/docs/queries/GetItem.md) - Validate each referenced item and item status
- [organization::GetSite](../../../organization/docs/queries/GetSite.md) - Validate receiving site references

## Error Scenarios

- **SUPPLIER_NOT_FOUND**: Referenced supplier partner does not exist
- **SUPPLIER_NOT_ACTIVE**: Referenced supplier is not in ACTIVE status
- **PARTNER_NOT_SUPPLIER**: Referenced partner does not carry the SUPPLIER role
- **EMPTY_PURCHASE_ORDER_LINES**: Purchase order has no lines
- **ITEM_NOT_FOUND**: Referenced item does not exist
- **ITEM_NOT_ACTIVE**: Referenced item is not in ACTIVE status
- **INVALID_QUANTITY**: Quantity is zero or negative
- **INVALID_UNIT_PRICE**: Unit price is negative or otherwise invalid
- **RECEIVING_SITE_NOT_FOUND**: Referenced receiving site does not exist
- **PAYMENT_TERM_NOT_FOUND**: Referenced purchase payment term does not exist
- **PAYMENT_TERM_INACTIVE**: Referenced purchase payment term is inactive

## Test Cases

- creates a draft purchase order with valid supplier and lines
- returns error when supplier does not exist
- returns error when supplier is inactive
- returns error when partner is not a supplier
- returns error when no lines are provided
- returns error when an item does not exist
- returns error when an item is inactive
- returns error when quantity is zero or negative
- returns error when unit price is negative
- returns error when header receiving site does not exist
- returns error when line receiving site does not exist
- returns error when specified payment term does not exist
- returns error when specified payment term is inactive
- creates a purchase order with lines that skip physical receipt when requiresPhysicalReceipt is false
- stores line receivingSiteId as null when not provided (header is the default)
- creates a service-only purchase order without header receivingSiteId
- returns error when physical line has no receiving site and header has none
