# Purchase Order Lifecycle

## Overview

Purchase Order Lifecycle governs how a supplier commitment is created, reviewed, placed, fulfilled, and closed. A `PurchaseOrder` records the commercial agreement with a supplier for one or more items, including ordered quantity, expected delivery, unit price, currency, payment-term reference, and receiving site. The order is the primary document that downstream receipts and supplier bills match against.

The lifecycle separates status into three independent axes. `orderStatus` governs the document-control lifecycle: a purchase order begins in draft, may be submitted for approval, can be explicitly rejected back to draft with a reason, becomes ordered when released to the supplier, and may eventually be cancelled or closed. `receiptStatus` independently tracks how much of the order has been physically received (not received, partially received, received). `billingStatus` independently tracks how much has been invoiced by the supplier (not billed, partially billed, billed). This separation ensures receipt and billing can progress in any order without overwriting each other, giving procurement teams a single source of truth for what is still open with each supplier.

Once a purchase order reaches `ORDERED` status, its commercial terms are immutable through normal update commands. The `amendOrderedPurchaseOrder` command allows atomic amendments — adding lines, modifying quantities or prices, or removing lines. Each amendment updates the affected lines in-place (stable line IDs), records pre-change snapshots as `PurchaseOrderLineRevision` records, and increments the PO's `revisionNumber`. REMOVE physically deletes the line from the table when no downstream references exist. Existing goods receipt and bill references remain valid because line IDs never change.

## Business Purpose

- Create a formal, auditable supplier commitment before goods or services are received
- Freeze negotiated commercial terms at the time an order is placed, even if future price lists change
- Capture the company and receiving-site context needed for receipt routing and supplier traceability
- Track open quantity, received quantity, billed quantity, and remaining commitment per line
- Provide the reference document for receipt matching and supplier bill matching (receipts and bills can also exist without a PO reference)
- Support partial fulfillment without losing visibility into remaining supplier obligations
- Prevent inactive suppliers, inactive items, or incomplete orders from entering downstream execution

## Process Flow

```mermaid
flowchart TD
    A[Create Purchase Order] --> B["orderStatus: DRAFT"]
    B --> C[Add supplier, lines, prices, terms]
    C --> D{Submit?}
    D -->|No| C
    D -->|Yes| E["orderStatus: SUBMITTED"]
    E --> F{Approve?}
    F -->|Reject| R[REJECTED → back to DRAFT]
    R --> B
    F -->|Approve and send| G["orderStatus: ORDERED\nreceiptStatus: NOT_RECEIVED\nbillingStatus: NOT_BILLED"]
    G --> H{Receipt posted?}
    H -->|Partial| I["receiptStatus: PARTIALLY_RECEIVED"]
    H -->|Full| J["receiptStatus: RECEIVED"]
    G --> K{Supplier bill matched?}
    K -->|Partial| L["billingStatus: PARTIALLY_BILLED"]
    K -->|Full| M["billingStatus: BILLED"]
    G --> N{Close remaining?}
    I --> N
    J --> N
    L --> N
    M --> N
    N -->|Close| O["orderStatus: CLOSED"]
    B --> P["orderStatus: CANCELLED"]
    E --> P
    G --> P
```

## Scenario Patterns

- **Standard stock purchase**: A buyer creates an order for replenishment items, applies supplier pricing and payment terms, submits it, and sends it to the supplier once approved
- **Rejected for revision**: An approver rejects a submitted order because the receiving site or supplier quote is wrong, and the order returns to draft with the rejection reason preserved for audit
- **Split delivery**: A supplier confirms the order but ships in two batches. The purchase order remains open after the first receipt and closes only when all ordered quantity is received or explicitly closed out. Each receipt references the purchase order through its receipt lines, not through a header-level foreign key
- **Price override before release**: A buyer starts from an automatically suggested supplier price rule but overrides one line before approval due to a one-time negotiated exception
- **Service or non-stock procurement**: A purchase order is created for a supplier-provided service or consumable item that may still require bill matching even if no warehouse stock is updated
- **Order cancellation before fulfillment**: An order is cancelled after submission because demand disappears and no receipt or bill has yet been matched
- **Close remaining balance**: A supplier underdelivers the final 2 units and the buyer chooses to close the remaining open quantity instead of keeping the order indefinitely open
- **Multi-line mixed fulfillment**: Some lines on the same order are fully received while others remain open, and line-level progress rolls up to `receiptStatus` and `billingStatus` independently
- **Supplier change control**: A buyer duplicates and revises a draft order rather than changing the supplier on an already ordered document, preserving traceability
- **Post-approval price amendment**: The supplier renegotiates a price increase on an ordered PO. Procurement amends the unit price on the affected line. The line is updated in-place and a revision snapshot captures the old price
- **Post-approval quantity increase**: Production needs more raw material. An amendment increases the ordered quantity. Receipt status may move from RECEIVED back to PARTIALLY_RECEIVED
- **Post-approval line removal**: A line item is cancelled before any receipts or bills. An amendment removes the line, physically deleting it after creating a revision snapshot
- **Blocked amendment removal**: A buyer tries to remove a line with existing receipts. The amendment rejects the REMOVE and instructs the buyer to MODIFY the quantity down instead

## Test Cases

- Creating a purchase order with a valid ACTIVE supplier, company, and at least one line should succeed
- A purchase order line must require an ACTIVE item, quantity greater than zero, and a non-negative unit price
- Purchase orders are always created with `orderStatus` = `DRAFT`; `receiptStatus` and `billingStatus` are not set until the order is approved
- A `DRAFT` order can be revised freely, but `ORDERED` commercial fields must not change without an explicit revision workflow
- Submitting a purchase order without any lines should fail
- Submitting a purchase order for a partner that does not carry the SUPPLIER role should fail
- Approving or ordering a purchase order whose supplier is INACTIVE should fail
- Rejecting a submitted purchase order should record the rejection reason and return the document to `DRAFT`
- Cancelling a purchase order after a posted receipt or matched supplier bill exists should be prevented
- Posting a partial receipt should update line received quantities and move `receiptStatus` to `PARTIALLY_RECEIVED` without affecting `billingStatus`
- Matching a supplier bill should update line billed quantities and move `billingStatus` independently of `receiptStatus`
- Closing a purchase order should set `orderStatus` to `CLOSED` only when all remaining open quantities are fulfilled, cancelled, or explicitly written off
- Purchase order totals and open balances must be isolated per company
- The applied unit price on a purchase order line must remain unchanged even if the underlying supplier price rule is later edited
- A purchase order must preserve its assigned payment term snapshot for downstream bill due-date calculation
- Each purchase order line should expose remaining quantity to receive and remaining quantity to bill
- Duplicate external supplier order references should be rejected or flagged according to company policy
- Only authorized procurement users should be able to submit, approve, cancel, or close purchase orders
- Amending an `ORDERED` PO should update lines in-place with stable IDs and create PurchaseOrderLineRevision snapshots
- Amending should increment the PO's `revisionNumber`
- MODIFY amendment should support price-only changes (quantity not provided preserves current value)
- MODIFY amendment should fail if new quantity is below received or billed quantity
- REMOVE amendment should fail if the line has posted receipts, matched bills, or draft downstream references
- ADD amendment should create a new line with item snapshot and positive quantity

## Reference Links

- [Odoo Purchase documentation](https://www.odoo.com/documentation/19.0/applications/inventory_and_mrp/purchase.html)
- [Oracle Fusion Cloud Procurement guide](https://docs.oracle.com/en/cloud/saas/procurement/index.html)
- [SAP procurement overview](https://help.sap.com/docs/SAP_S4HANA_CLOUD)
