# UpdatePurchaseOrder

## Permission Scope

purchaseOrder

## Overview

UpdatePurchaseOrder revises mutable commercial data on a draft purchase order, including lines, supplier-facing price overrides, and receiving context.

## Business Rules

- Target purchase order must exist
- Only orders with `orderStatus` = `DRAFT` can be updated
- Header-level receiving site may be updated and must reference a valid site if provided
- `ADD` lines and the merged result of `UPDATE` patches must satisfy the same validation rules as create (including item ACTIVE requirement — this is still a pre-order phase where inactive items should not enter new procurement)
- Every item chosen by an `ADD` line or an `UPDATE` patch must hold a [PurchaseItem](../model/PurchaseItem.md) record, and the line's `requiresPhysicalReceipt` is re-frozen from that record; lines whose item is untouched keep the value they already froze
- `UPDATE` / `REMOVE` must target an existing line of the order
- The order must retain at least one line after the changes
- Line-level receiving site overrides are validated independently; if not provided, the header default applies
- Draft buyers may override a defaulted supplier price while retaining the matched-rule reference for audit
- Updating a purchase order never changes already ordered, received, or billed documents

## Process Flow

```mermaid
flowchart TD
    A[Receive update request] --> B{Purchase order exists?}
    B -->|No| C[Return PURCHASE_ORDER_NOT_FOUND]
    B -->|Yes| D{Status is DRAFT?}
    D -->|No| E[Return PURCHASE_ORDER_NOT_DRAFT]
    D -->|Yes| F{Updated lines and references valid?}
    F -->|No| G[Return validation error]
    F -->|Yes| H[Update draft order and lines]
    H --> I[Return updated order]
```

## External Dependencies

- [item-management::GetItem](../../../item-management/docs/query/GetItem.md) - Re-validate item references on changed lines
- [organization::GetSite](../../../organization/docs/query/GetSite.md) - Re-validate receiving site references

## Error Scenarios

- **PURCHASE_ORDER_NOT_FOUND**: Referenced purchase order does not exist
- **PURCHASE_ORDER_NOT_DRAFT**: Purchase order is not in `DRAFT` status
- **LINE_NOT_FOUND**: Referenced line does not exist on the target order
- **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
- **ITEM_NOT_PURCHASABLE**: Referenced item holds no purchasing record and cannot be purchased
- **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

## Test Cases

- updates supplier-facing data on a draft purchase order
- passes the order to save unchanged when headerPatch is empty
- passes the line to save unchanged when its patch is empty
- returns error when purchase order does not exist
- returns error when purchase order is not in draft
- adds a line with item snapshot fields via an ADD change
- updates a line in place so its id survives the edit
- removes one of multiple lines via a REMOVE change
- returns error when a REMOVE would leave the order without lines
- returns error when an UPDATE targets a missing line
- returns error when an updated line item does not exist
- returns error when an added line item is inactive
- returns error when an added line item has no purchasing record
- re-freezes the receipt expectation when an UPDATE patch changes the line item
- returns error when an updated line quantity is not positive
- returns error when an added line unit price is negative
- returns error when header receiving site does not exist
- returns error when an updated line receiving site does not exist
