# Scrap Management

## Overview

Scrap management handles the controlled removal of damaged, expired, defective, or obsolete stock from usable inventory. Scrap is modeled as a `StockAdjustment` with `adjustmentType=SCRAP`. When confirmed, the adjustment creates InventoryLedger entries with sourceType=SCRAP and sourceId referencing the StockAdjustment, effectively removing stock from available inventory at the storage location and reducing inventory valuation.

Organizations may enforce approval workflows for scrap requests that exceed a defined value threshold, ensuring proper oversight of material losses. The erp-kit module provides the DRAFT → SUBMITTED → CONFIRMED lifecycle (with SUBMITTED → REJECTED → DRAFT for rework, and DRAFT → CANCELLED for abandonment). Applications route submitted adjustments through their own approval logic before calling confirmStockAdjustment or rejectStockAdjustment. Once confirmed and the InventoryLedger entries are created, the ledger records are immutable -- if scrap was created in error, a new StockAdjustment(CORRECTION) with adjustmentDirection=INCREASE can restore the stock.

Inventory accepts scrap input both from direct inventory users and from upstream manufacturing execution. Manufacturing-originated scrap enters inventory through the `ManufacturingScrapHandoff` contract defined in manufacturing `work-order-execution.md`, after which inventory creates the `StockAdjustment(SCRAP)` that owns stock and valuation effects.

## Business Purpose

- Remove physically damaged, expired, or quality-rejected stock from available inventory so it is not accidentally picked, sold, or consumed
- Maintain accurate inventory valuation by writing off the cost of scrapped items at the time of disposal
- Provide an auditable trail of why, when, and by whom stock was disposed, supporting regulatory and internal compliance requirements
- Enable approval controls for high-value scrap events, preventing unauthorized write-offs
- Support root-cause analysis by categorizing scrap reasons (damage, expiration, quality failure, obsolescence), feeding continuous improvement initiatives
- Preserve lot and serial traceability through the scrap process for recall and warranty tracking

## Process Flow

```mermaid
flowchart TD
    A[Identify unusable stock] --> B[createStockAdjustment with adjustmentType=SCRAP]
    B --> C[submitStockAdjustment]
    C --> D{Application approval workflow?}
    D -- Yes --> E[Application: Custom Approval]
    E --> F{Approved?}
    F -- Yes --> G[confirmStockAdjustment]
    F -- No --> H[rejectStockAdjustment]
    H --> I[Update and resubmit or cancelStockAdjustment]
    D -- No --> G
    G --> J[Create InventoryLedger entries with sourceType=SCRAP, sourceId=stockAdjustmentId]
    J --> K[Adjust inventory valuation]
    K --> L[Scrap completed]
```

## Scenario Patterns

- **Damaged goods scrap**: Site staff discover physically damaged items during handling or counting and create a StockAdjustment(SCRAP) referencing the damage reason and storage location
- **Expired stock disposal**: Items that have passed their expiration date are identified (often via lot tracking) and scrapped via a StockAdjustment(SCRAP) to clear them from available inventory
- **Quality failure scrap**: Items that fail incoming or in-process quality inspection are scrapped via a StockAdjustment(SCRAP). When scrapping from blocked/inspection stock, set `fromStockCategory=BLOCKED` on the StockAdjustmentLine to draw from the blocked bucket instead of available stock. BLOCKED-origin scrap creates two ledger entries: SCRAP (CR, decrements onHand) and UNBLOCK (CR, decrements blocked). The default `fromStockCategory` is AVAILABLE, which draws from available stock and creates a single SCRAP (CR) entry
- **Obsolescence write-off**: Slow-moving or discontinued items are scrapped via a StockAdjustment(SCRAP) as part of a periodic inventory review to reflect their true recoverable value
- **High-value approval scrap (application-level)**: A StockAdjustment(SCRAP) exceeds the organization's value threshold, triggering a manager approval step after submission before the adjustment can be confirmed. The approval workflow is implemented at the application layer; the module transitions through SUBMITTED → CONFIRMED or SUBMITTED → REJECTED.
- **Manufacturing-originated scrap**: A work order reports scrap and sends `ManufacturingScrapHandoff`; inventory resolves the source location, creates a StockAdjustment(SCRAP), and runs the same approval policy as any other scrap event
- **Scrap error correction**: A scrap adjustment is found to have been created in error; a new StockAdjustment(CORRECTION) with adjustmentDirection=INCREASE restores the stock to the storage location
- **Lot/serial-tracked scrap**: Specific lots or serial numbers are scrapped via StockAdjustment(SCRAP), maintaining full traceability from receipt through disposal. Serial numbers transition to SCRAPPED state on confirmation.

Inventory accepts `ManufacturingScrapHandoff` from manufacturing work-order execution with the following minimum fields:

- `productionOrderReference`
- `workOrderReference`
- `itemReference`
- `scrapQuantity`
- `unitOfMeasure`
- `scrapReasonCode`
- `siteReference`
- `postingDate`

Optional and conditional fields:

- `sourceStorageLocationReference`: optional when inventory can derive the source location from prior issue, backflush, or site-level manufacturing scrap policy; otherwise required
- `inventoryLotReference`: required when the scrapped item is lot-tracked
- `serialReferences`: required when the scrapped item is serial-tracked
- `scrapDispositionCode`: optional classification retained for downstream analysis

Inventory maps the handoff into an inventory-owned `StockAdjustment(SCRAP)` as follows:

- `adjustmentType = SCRAP`
- `itemReference`, `siteReference`, `postingDate`, and reason fields come directly from the handoff
- `productionOrderReference` and `workOrderReference` are retained as source-document traceability fields on the adjustment
- `sourceStorageLocationReference` is taken from the payload when present; otherwise inventory must derive it from manufacturing issue history, staging-location policy, or other site-level configuration before the adjustment can proceed
- `inventoryLotReference` and `serialReferences` flow into the same lot or serial validation rules used by direct inventory scrap

Approval and confirmation rules are inventory-owned:

- If the resulting scrap value exceeds the configured approval threshold, the application gates confirmation behind its own approval workflow (the StockAdjustment remains in SUBMITTED until confirmed or rejected)
- If the threshold is not exceeded, the application may confirm the adjustment immediately
- If location, lot, serial, or quantity resolution fails, inventory rejects the handoff and does not mutate stock

## Test Cases

- Confirming a StockAdjustment(SCRAP) removes the specified quantity from the storage location's available stock (quantity is always positive; adjustmentType=SCRAP implicitly means stock is removed)
- Scrapped stock is excluded from available-to-promise calculations
- Inventory valuation decreases by the cost of the scrapped items upon confirmation
- StockAdjustment(SCRAP) records the item, quantity, storage location, reason, date, and responsible user
- When lot tracking is enabled, the StockAdjustmentLine must reference a valid lot number with sufficient quantity
- When serial tracking is enabled, the StockAdjustmentLine must reference specific serial numbers at the storage location; serial transitions to SCRAPPED on confirmation
- StockAdjustment(SCRAP) exceeding the configured value threshold requires approval before confirmation (application-level)
- A StockAdjustment(SCRAP) must be submitted before it can be confirmed; calling confirmStockAdjustment on a DRAFT is rejected
- A rejected StockAdjustment(SCRAP) returns to DRAFT status and can be updated and resubmitted
- Cancelled StockAdjustment(SCRAP) remains in CANCELLED status and does not affect stock or valuation
- Scrapped items cannot re-enter available stock without a new StockAdjustment(CORRECTION)
- Scrap reason is mandatory and must be one of the defined reason categories
- Listing StockAdjustments filtered by `adjustmentType=SCRAP` returns only scrap records
- A valid `ManufacturingScrapHandoff` must create a StockAdjustment(SCRAP) that preserves the production-order and work-order references for traceability
- Manufacturing-originated scrap must follow the same approval threshold policy as manually entered scrap adjustments
- If inventory cannot resolve the source storage location required for `ManufacturingScrapHandoff`, the handoff must be rejected without mutating stock

## Reference Links

- [Odoo Scrap Management](https://www.odoo.com/documentation/17.0/applications/inventory_and_mrp/inventory/warehouses_storage/removal_strategies.html)
- [SAP Movement Types 551/552 for Scrapping](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/f7fddfe4caca43b4a511ab5e99f65567/e457ae2bbbf71014bfc0e6eee74504ce.html)
- [Oracle Inventory Scrap Transactions](https://docs.oracle.com/en/cloud/saas/supply-chain-and-manufacturing/25a/fammi/inventory-transactions.html)
