# Valuation pipeline

**Stance:** Cost is calculated **as movements commit** (or in the same atomic boundary defined by product rules), **attached** to movement records as immutable cost lines, and **rebuildable** from the movement + policy inputs alone.

## When cost is calculated

| Trigger | Valuation action |
| --- | --- |
| **Inbound movement committed** | Create layer(s) or average snapshot row; record **unit cost basis** from receipt costing (PO price + allocated charges per policy) |
| **Outbound movement committed** | Run consumption algorithm; persist **allocated cost** per movement line |
| **Transfer ship** | Consume layers at source; fix **transfer price** for destination inbound (at cost or policy) |
| **Transfer receive** | Create inbound layers linked to ship event |

**Default:** Calculate **before** marking movement **posted** for operational workflows that require a known COGS at issue time; alternatively **two-phase** commit with valuation step **must** be idempotent on retry (same movement id → same cost lines).

## How cost attaches to movements

- **Movement line** (or child **cost allocation** table): `movement_id`, `quantity`, `extended_cost`, `currency`, optional `layer_consumption_ids`.
- **Outbound:** Sum of extended costs equals **total issue cost** for the line (rounding rules explicit).
- **Posting** to GL reads **these** numbers—no second hidden valuation path that could diverge (`posting-mapping.md`).

## Replay and rebuild rules

**Replay:** Re-process movements in **strict commit order** for each valuation scope with identical policy version → identical layers and outbound costs.

**Rebuild** (disaster recovery, bug fix, policy fork):

1. **Freeze** new movements or queue them until rebuild completes (or rebuild per partition).
2. **Truncate or swap** derived tables (layers, consumption links, movement cost lines)—**never** raw operational movements.
3. Apply movements in order from **immutable movement log**.
4. Verify **checksums**: on-hand qty/value vs operational snapshot per scope.

**Forbidden:** Editing a single historical movement’s cost in the DB without replaying the stream—breaks determinism and audit.

## Failure handling

- Valuation failure **after** quantity commit: movement stays **posted**; cost lines missing triggers **alert** and **blocking** on dependent GL posting until repaired via replay.
- **Retry** uses movement id as idempotency key for cost attachment.

## Ordering dependencies

- **Receipt** must run before **issue** that depends on that stock—enforced by operations.
- **Concurrent** movements on same SKU-site: serialize by **sequence number** or transactional locking so layer consumption order is **total** and reproducible.

---

## Phase completion checklist (inventory valuation)

- Cost model defined (`cost-models.md`)
- Layers defined (`cost-layers.md`)
- Pipeline defined (this document)
- Reversals handled (`valuation-reversals.md`)
- Reconciliation defined (`valuation-reconciliation.md`)

**Critical stance:** Inventory value must match movement reality. **Replayability > shortcuts.** **Cost integrity > financial reporting.**
