# Inventory Valuation

## Overview

Inventory Valuation costs stock movements and records the results directly in financial-accounting as posted journal entries. Each valuation-affecting InventoryLedger entry — created by goods receipt / goods issue posting or by confirmStockAdjustment — is costed by the method of the valuation policy assigned to the item in the movement's company. The costing treatment of each InventoryLedger entry is determined by (sourceType, action, direction): purchase receipts (INBOUND_SHIPMENT × QUANTITY_CHANGE IN) post against the accrual account, sales issues (OUTBOUND_SHIPMENT × QUANTITY_CHANGE OUT) relieve COGS, and adjustments (STOCK_ADJUSTMENT × QUANTITY_CHANGE IN / OUT) post inventory gains, losses, and scrap write-offs against the adjustment account; transfer legs and BLOCK / UNBLOCK are internal moves (action TRANSFER / STOCK_TYPE_CHANGE) and produce no costing.

Costing separates quantity from price. Every costed movement, regardless of method, maintains the same costed-quantity substrate: receipts create a CostLayer, issues consume layers first-in-first-out, recording a CostLayerConsumption per consumed layer. On top of that substrate, three costing methods price the moved quantities, selected per policy; price state is append-only, and the layer's remaining quantity is the only thing costing mutates in place:

- **STANDARD** values movements at the item's current standard cost (StandardCost generations published via publishStandardCost). Receipts value inventory at the standard against the accrual account at actual cost, recognizing the difference as PPV; issues relieve COGS at the standard. Inventory value is fully derived — standard × costed on-hand.
- **FIFO** values movements at actual cost per layer. Receipts write the layer's FifoCost price; issues are valued at the consumed layers' current cost.
- **AVERAGE** values movements at a moving average per item and company, kept as the AverageCost history. Receipts fold in at actual cost; issues are valued at the average in effect and never change it.

The valuation method is configured through ValuationPolicy — a company-scoped entity that defines the costing method and carries the owning company's posting accounts (inventory, accrual, invoice price variance, COGS, PPV, adjustment, standard cost adjustment, consumed price variance) used for costing journals. Each item is attached to a policy per company via setItemValuationPolicy, or auto-assigned the movement company's default policy on its first valuation-affecting movement; when neither an assignment nor a default policy exists in the movement's company, valuation-affecting movements fail rather than move stock without a journal.

Every valuation-affecting movement and revaluation creates and posts a balanced journal entry directly in financial-accounting, within the same transaction as the stock movement — the costing facts land in the general ledger atomically and can never be skipped.

## Business Purpose

- **Financial reporting accuracy**: The inventory journal is maintained transaction-by-transaction; balance sheet value needs no separate valuation run
- **Cost of Goods Sold (COGS) calculation**: Issues relieve COGS at the cost determined by the item's costing method
- **Variance visibility**: Under standard costing, actual-vs-standard purchase differences are posted to the PPV account at receipt and standard revisions to a separate adjustment account; under FIFO and AVERAGE, movements carry actual cost and no variance arises at movement time — invoice-driven price differences are trued up later by [Acquisition Cost Adjustment](./acquisition-cost-adjustment.md)
- **Transaction-driven recording**: Every valuation-affecting InventoryLedger entry creates its journal in the same transaction — no batch accounting job
- **Audit trail**: The journal entries and the append-only cost state are the audit trail, each entry referencing the originating InventoryLedger row or standard cost revision

## Process Flow

```mermaid
flowchart TD
    A[InventoryLedger Created] --> B{sourceType, action, direction?}
    B -->|TRANSFER or STOCK_TYPE_CHANGE| I[No costing]
    B -->|Costed movement| Q[Receipts create a cost layer; issues consume layers]
    Q --> M{Costing method of assigned policy?}
    M -->|STANDARD| C[Value at the current standard]
    M -->|FIFO| D[Value at the consumed layers' cost]
    M -->|AVERAGE| E[Value at the moving average]
    C --> F[Create and post journal entry in financial-accounting]
    D --> F
    E --> F
```

## Scenario Patterns

- **Standard cost with variance capture**: Goods are received and issued at the current standard cost. Any difference between the actual purchase price and the standard is recorded at receipt against the PPV account. Publishing a new standard via publishStandardCost records the revaluation of costed on-hand stock against the standard cost adjustment account, kept separate from PPV so purchasing performance and revision effects stay independently analyzable. Issued COGS is never restated.
- **FIFO actual costing**: Goods are received at actual cost; issues consume the oldest layers first, so COGS reflects the actual acquisition cost of the consumed stock.
- **Moving-average costing**: Goods are received at actual cost, each receipt folding into the item's moving average in the company; issues relieve COGS at the average in effect.
- **Inventory adjustment impact**: A positive adjustment (e.g., found stock) posts an inventory gain against the adjustment account — at the standard for STANDARD items, at the provided actual cost for FIFO and AVERAGE items; a negative adjustment (e.g., shrinkage) posts an inventory loss the same way. Scrap writes inventory off identically. Neither touches COGS or the purchase accrual, so sales margins and uninvoiced-receipt clearing stay clean.
- **Unconfigured policy**: A valuation-affecting movement for an item with no assigned policy falls back to the movement company's default policy (auto-assigning it); when no default is configured either, the posting fails so stock can never move without its journal.

## Test Cases

- Given an item configured with standard cost of 50 per unit, when 10 units are received at an actual cost of 55 per unit, the inventory should be valued at 500 and a purchase price variance of 50 should be recorded against the PPV account
- Given a standard cost item, when a new standard of 60 is published via publishStandardCost replacing 50 with 100 units on hand, a balanced inventory / standard-cost-adjustment journal entry of 1000 should be created
- Given a standard cost item, when an InventoryLedger entry is costed, a balanced journal entry should be created and posted in financial-accounting carrying the policy's posting accounts and the originating InventoryLedger reference
- Given an item with no ItemValuation record and no default policy configured, when a movement posts, the posting should fail
- Given a standard cost item with standard 50, when 2 units are scrapped, a journal entry of 100 relieving inventory against the adjustment account should be created and COGS should be untouched
- Given a FIFO item with layers of 2 units at 9 and 8 units at 10, when 4 units are issued, COGS of 38 should be recorded and the layers should retain 0 and 6 units
- Given a FIFO receipt line without a unit cost, when the movement posts, the posting should fail
- Given a costed issue consuming multiple layers, a consumption record should be appended per consumed layer regardless of costing method
- Given a costed issue exceeding the company's layered stock, the posting should fail under every costing method
- Given an AVERAGE item holding 10 units at an average of 9, when 10 units are received at 11, the average should become 10 and the receipt should post inventory at 110 with no PPV
- Given an AVERAGE item holding 10 units at an average of 9, when 4 units are issued, COGS of 36 should be recorded and the average should remain 9

## Reference Links

- [Inventory Module README](../../README.md) — module scope and dependency overview
- [Stock Tracking](./stock-tracking.md) — stock movements that trigger valuation updates
- [Inventory Adjustment](./inventory-adjustment.md) — adjustment scenarios with valuation impact
- [Scrap Management](./scrap-management.md) — scrap disposal and valuation write-off
- [Acquisition Cost Adjustment](./acquisition-cost-adjustment.md) — invoice-driven receipt cost correction on top of the cost layers and policy accounts defined here
