# PostGoodsReceipt

## Permission Scope

goodsReceipt

## Overview

PostGoodsReceipt validates a draft receipt, updates cumulative received quantities on the referenced purchase-order lines (for PO-linked lines), transitions each affected purchase order's `receiptStatus` to the correct receiving state, and calls receiveStock in the inventory module for each stock-item line. PO-unlinked receipt lines are posted without affecting any purchase order. Because a single receipt may span lines from multiple purchase orders (consolidated shipment) and may include PO-unlinked lines, the command separates PO-linked and PO-unlinked lines, groups PO-linked lines by their parent purchase order, and updates each purchase order's `receiptStatus` independently. This command only affects `receiptStatus` and never modifies `orderStatus` or `billingStatus`.

Applications that require mandatory quality inspection for incoming goods can wire up an optional `receiveStockWithInspection` command in the inventory commands. When present, stock-item receipt lines are routed through that command, landing received stock directly in the blocked (inspection) state. When absent, stock-item lines call the default `receiveStock` command and stock becomes available immediately. The presence of `receiveStockWithInspection` is therefore the application-level switch between "trust on receipt" and "inspect on receipt" policies.

## Business Rules

- Target goods receipt must exist
- Only receipts in `DRAFT` status can be posted
- PO-linked receipt lines must still reference open purchase-order quantity
- PO-linked receipt lines: each referenced purchase order must still be receivable (not cancelled or closed)
- PO-unlinked receipt lines are posted without PO validation
- Over-receipt beyond configured tolerance is blocked unless an explicit override path is provided (PO-linked lines only)
- Only posted goods receipts count toward 3-way supplier bill matching
- Stock-item lines call receiveStock once per line; service lines do not require inventory execution
- When the optional `receiveStockWithInspection` command is provided, stock-item lines are routed through it (inspection-hold receipt); otherwise lines are routed through `receiveStock`
- PO-linked receipt lines are grouped by parent purchase order; each purchase order's `receiptStatus` is rolled up independently
- Over-receipt validation uses the line's received quantity directly: cumulative received quantity is the sum of all posted GoodsReceiptLine records referencing the same line ID
- Item ACTIVE status is not checked: goods receipt is a post-order flow that completes an already-committed purchase order, so inactive items must still be receivable to avoid blocking in-progress fulfillment

## Process Flow

```mermaid
flowchart TD
    A[Receive post request] --> B{Goods receipt exists and is DRAFT?}
    B -->|No| C[Return not found or invalid status]
    B -->|Yes| D[Separate PO-linked and PO-unlinked lines]
    D --> E{PO-linked: all referenced POs still receivable?}
    E -->|No| F[Return PURCHASE_ORDER_NOT_RECEIVABLE]
    E -->|Yes| G{PO-linked: open quantity within tolerance?}
    G -->|No| H[Return receipt validation error]
    G -->|Yes| I[Set goods receipt to POSTED]
    I --> J{Stock line present?}
    J -->|Yes| K[Call receiveStock per stock-item line]
    J -->|No| L[Skip receiveStock]
    K --> M["Recalculate receiptStatus for each affected PO"]
    L --> M
    M --> N[Return posted receipt]
```

## External Dependencies

- [inventory::receiveStock](../../../inventory/docs/commands/ReceiveStock.md) - Receives stock into inventory (immediate ledger + stock level update) for stock-item receipts. Used by default.
- [inventory::receiveStockWithInspection](../../../inventory/docs/commands/ReceiveStockWithInspection.md) (optional) - When wired up by the application, stock-item receipts are routed through this command instead, so incoming stock is placed directly in the blocked (inspection) state. The presence of this command is the application-level policy switch.

## Error Scenarios

- **GOODS_RECEIPT_NOT_FOUND**: Referenced goods receipt does not exist
- **GOODS_RECEIPT_NOT_DRAFT**: Goods receipt is not in `DRAFT` status
- **PURCHASE_ORDER_NOT_RECEIVABLE**: Referenced purchase order is cancelled, closed, or otherwise unavailable for receipt
- **NO_OPEN_RECEIPT_QUANTITY**: Purchase-order line has no remaining open quantity to receive
- **OVER_RECEIPT_TOLERANCE_EXCEEDED**: Received quantity exceeds allowed tolerance
- **INACTIVE_EXECUTION_REFERENCE**: Supplier is no longer active (item ACTIVE status is not checked because this is a post-order flow)
- **STOCK_MOVEMENT_CREATION_FAILED**: Downstream stock movement creation failed during posting

## Test Cases

- posts a valid goods receipt and increases purchase-order received quantity
- posts PO-unlinked receipt lines without affecting any purchase order
- posts a consolidated receipt spanning multiple purchase orders and updates each PO independently
- transitions multiple purchase orders to different receipt statuses from a single receipt
- transitions purchase order to partially received when open quantity remains
- transitions purchase order to received when all quantity is posted
- calls receiveStock for stock-item receipt lines
- routes through receiveStockWithInspection when provided
- skips receiveStock for a service receipt
- calls receiveStock once per stock-item receipt line and posts successfully
- calls receiveStock for multiple stock-item receipt lines
- returns error when goods receipt does not exist
- returns error when goods receipt is not draft
- returns error when posting exceeds tolerance
- returns error when posting a receipt line with no open quantity remaining
- returns error when any referenced purchase order is no longer receivable
- returns error when supplier is inactive
- posts successfully when item is inactive
- validates over-receipt against the PO line's quantity using received quantity
- returns error when receipt line references a PO line not in the fetched orderLineMap
- returns error when receiveStock fails
- returns error when receipt line requires inventory handoff but has no destination location
