# 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 creates a DRAFT stock movement in the inventory module for stock-item lines. 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`.

## 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 create a DRAFT stock movement exactly once; service lines do not require inventory execution
- PO-linked receipt lines are grouped by parent purchase order; each purchase order's `receiptStatus` is rolled up independently
- Over-receipt validation uses lineage-aware received quantity: cumulative received quantity is the sum across all lines sharing the same `rootLineId`
- New receipt lines must reference the current ACTIVE purchase-order line, not a SUPERSEDED line
- 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 using lineage-aware qty?}
    G -->|No| H[Return receipt validation error]
    G -->|Yes| I[Increase PO line received quantities per PO]
    I --> J[Set goods receipt to POSTED]
    J --> K{Stock line present?}
    K -->|Yes| L[Create DRAFT stock movement]
    K -->|No| M[Skip stock movement]
    L --> N["Roll up receiptStatus for each affected PO"]
    M --> N
    N --> O[Return posted receipt]
```

## External Dependencies

- [inventory::CreateStockMovement](../../../inventory/docs/commands/CreateStockMovement.md) - Downstream stock execution target for stock-item receipts

## Error Scenarios

- **GOODS_RECEIPT_NOT_FOUND**: Target goods receipt does not exist
- **GOODS_RECEIPT_NOT_DRAFT**: Goods receipt is not in `DRAFT` status
- **PURCHASE_ORDER_NOT_RECEIVABLE**: A referenced purchase order is cancelled or closed
- **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)
- **SUPERSEDED_LINE_REFERENCED**: Receipt line references a SUPERSEDED purchase-order line instead of the current ACTIVE line
- **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 createStockMovement for stock-item receipt lines
- skips createStockMovement for a service receipt
- 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 ACTIVE line's quantity using lineage-aware received quantity
- receipts referencing superseded lines count toward lineage total for over-receipt validation
- returns error when receipt line references a SUPERSEDED purchase-order line
- returns error when receipt line references a PO line not in the fetched orderLineMap
- returns error when createStockMovement fails
- uses destinationStorageLocation as null when not set on receipt line
