# 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 posts its InventoryExecution and creates linked InventoryLedger entries, 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 linked to the StockAdjustment InventoryExecution]
    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
- **Quarantined stock scrap**: Items held from use can be scrapped via a StockAdjustment(SCRAP). When scrapping from blocked stock, set `fromStockCategory=BLOCKED` on the StockAdjustmentLine to draw from the blocked type instead of available stock. BLOCKED-origin scrap creates a single SCRAP (CR) ledger entry against the BLOCKED type. 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`
- `effectiveDate`

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`, `effectiveDate`, 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)
