# Acquisition Cost Adjustment

## Overview

Acquisition Cost Adjustment corrects the cost of purchase receipts when the price they are actually owed at differs from the price the receipt was costed at. Receipts are costed at the inbound unit cost (normally the purchase order price at receipt time) before the supplier invoice exists, and three events can move the owed price away from that booking, each calling inventory's `postAcquisitionCostAdjustment` in the same transaction as its own posting:

- **account-payable posts** an invoice, credit memo, or price/quantity correction whose price differs from the order price its lines were matched at
- **purchase reprices** an ORDERED line — declared over the received-minus-billed quantity, since the billed quantity's price basis was fixed at matching
- **account-payable cancels** a REGISTERED document — the matched-versus-current difference on the quantity returning to the unbilled pool

The declaring modules own the amounts; inventory owns the state and distribution. How each caller computes its amount is its own concern (see [PostAccountPayableDocument](../../../account-payable/docs/command/PostAccountPayableDocument.md), [CancelAccountPayableDocument](../../../account-payable/docs/command/CancelAccountPayableDocument.md), and [AmendOrderedPurchaseOrder](../../../purchase/docs/command/AmendOrderedPurchaseOrder.md)).

Inventory keeps every declared amount as an event on the order line (the AcquisitionCostAdjustment register), so the register's sum is the cumulative variance the line's receipts must carry. Each posting recomputes from that sum: every cost layer's target is its received-quantity share of the cumulative variance, and only the shortfall against what its allocations already carry is posted. This makes event order irrelevant — partial invoices top layers up, corrections pull them back down, a price change re-anchors the line, and an event posted before any receipt waits in the register until the receipt posting redistributes it onto the new layer. The command resolves the affected CostLayers through the receipt ledger entries' persisted order reference.

Each layer's delta splits by quantity between its on-hand and already-consumed portions, and the item's costing method decides the treatment: FIFO restates the on-hand share as a new FifoCost version and classifies each consumed slice by the consuming ledger entry — sales issues restate COGS, adjustments and scrap the adjustment account, anything else fails rather than defaulting silently; AVERAGE folds the on-hand share into the moving average (a COST_ADJUSTMENT event) and expenses the consumed share to the consumed price variance account; STANDARD posts the full delta to PPV. Every distribution appends price state, records per-layer allocations, and posts a balanced journal entry in financial-accounting atomically with the triggering posting.

Two clearing accounts wash, split by what each declaration re-prices (the declared variance kind). The accrual account carries stock at the order price: the receipt credits it at the order price at receipt time, AP debits it at the matched order price, and ORDER_PRICE declarations (order reprices and cancellations) re-anchor it — so its balance is always received-not-yet-invoiced value at the order price. The invoice price variance account carries the invoice-versus-order price difference: AP debits it for the signed difference between the invoiced price and the matched order price, and INVOICE_PRICE declarations wash it when the adjustment lands the difference on the receipts — so a lingering balance there is exactly the variance not yet absorbed (e.g. an invoice posted before its receipt). A fully received and invoiced line leaves no residue in either account. This assumes PO-sourced AP lines post to the same accrual and invoice price variance accounts the policy carries and receipts are costed at the order price current at receipt — a receipt costed off the order price for any other reason leaves its difference in accrual, surfaced as a receipt-side discrepancy rather than absorbed into the adjustments. Variances are pure base-currency price differences; tax, freight, and landed-cost allocation are out of scope.

## Business Purpose

- **Inventory valued at actual acquisition cost**: FIFO layers and moving averages reflect what was paid, not the provisional order price
- **Books converge on the documents**: every posting redistributes the cumulative variance, so the final state is independent of billing order
- **COGS accuracy under FIFO**: sales issues consumed at a provisional cost are restated to the invoiced cost — no period-end revaluation run
- **Variance visibility**: differences that cannot be capitalized post to dedicated accounts — PPV under standard costing, the consumed price variance account for consumed AVERAGE stock
- **Accrual hygiene**: the uninvoiced-receipt accrual carries only order-price value and washes to zero once receipts and invoices net; a lingering balance signals an unposted leg
- **Invoice price variance visibility**: invoice-versus-order price differences flow through a dedicated clearing account, so the period's gross variance is readable from the trial balance
- **Transaction-driven recording**: the adjustment posts in the same transaction as the AP document or receipt — no batch job, no stale window
- **Audit trail**: each AcquisitionCostAdjustment references its causing document and records how much variance it distributed per layer

## Process Flow

```mermaid
flowchart TD
    A[postAccountPayableDocument posts a purchase-order sourced line] --> D{Variance amount vs the line's matched order price?}
    D -->|Zero| E[No call: nothing declared]
    D -->|Non-zero| B[Record the signed amount on the order line's event register]
    A3[amendOrderedPurchaseOrder reprices a line] --> D3{Signed price delta × received minus billed quantity?}
    D3 -->|Zero| E
    D3 -->|Non-zero| B
    A4[cancelAccountPayableDocument cancels a REGISTERED document] --> D4{Matched-versus-current difference × quantity?}
    D4 -->|Zero| E
    D4 -->|Non-zero| B
    A2[postInventoryLedger posts a purchase receipt] --> B2{Register sum differs from distributed sum?}
    B2 -->|No| E2[No redistribution]
    B2 -->|Yes| B3[Record a zero-amount event anchored to the receipt]
    B --> C[Resolve receipt CostLayers via ledger order reference]
    B3 --> C
    C -->|None| W[Wait: first receipt redistributes]
    C -->|Found| F2[Compute each layer's target: received-quantity share of the cumulative variance]
    F2 --> F3[Post each layer's shortfall against its distributed allocations]
    F3 --> F[Split each shortfall proportionally: on-hand vs consumed quantities]
    F --> G{Costing method of assigned policy?}
    G -->|STANDARD| H[Inventory unchanged; full share to PPV]
    G -->|FIFO| I[On-hand share: new FifoCost version restates layer]
    I --> J{Classify each consumption by sourceType and action}
    J -->|Sales issue| K[Restate COGS]
    J -->|Adjustment or scrap| L[Restate adjustment account]
    J -->|Unmapped kind| N[Fail: consumption treatment not supported]
    G -->|AVERAGE| O[On-hand share: COST_ADJUSTMENT event folds into the average]
    O --> P[Consumed shares expensed to consumed price variance account]
    H --> Q[Record allocations per layer]
    K --> Q
    L --> Q
    P --> Q
    Q --> R[Create and post journal entry in financial-accounting, same transaction]
```

## Scenario Patterns

- **Price increase, stock untouched (FIFO / AVERAGE)**: 100 units received at 10 are invoiced at 12, all on hand. AP posts accrual at the matched 1,000 plus invoice price variance +200 and declares +200; the layer restates to 12 (FIFO: new FifoCost version; AVERAGE: COST_ADJUSTMENT event) and the journal debits inventory / credits invoice price variance 200 — both clearing accounts wash to zero.
- **Price increase after partial consumption (FIFO)**: 40 of 100 units already issued to a sales order. The 200 splits by quantity: on-hand 60 restates the layer (inventory +120), consumed 40 restates COGS (+80). Issued COGS is corrected, not left provisional.
- **Price increase after partial consumption (AVERAGE)**: only the on-hand share folds into the average (a COST_ADJUSTMENT event moving value without quantity); consumed shares expense to the consumed price variance account. Historical issues are never restated.
- **Standard cost item**: inventory stays at standard; the full variance posts to PPV, like the variance captured at receipt. No price state is written.
- **Consumption by adjustment or scrap (FIFO)**: consumed slices from stock adjustments or scrap write-offs restate the adjustment account, mirroring the original consumption.
- **Consumption by transfer**: never occurs — transfer legs produce no costing and consume no layers, and cross-company transfers are rejected. A transfer-sourced consumption is an unmapped kind and fails defensively.
- **Price decrease / credit memo / corrections**: a negative (or positive) amount into the same register — targets drop, layers restate downward, postings reverse. No correction-specific logic; a correction is just another event in the sum.
- **Invoice before receipt**: the variance waits in the register (no unit to carry it); the first receipt detects and redistributes it onto the new layer in the same transaction. Until then it rests visibly in the invoice price variance account.
- **Offsetting kinds waiting for the receipt**: an invoice variance and an order reprice that cancel out while unreceived leave the register at zero but the clearing accounts crossed; the first receipt posts a rebalancing entry between the invoice price variance account and the accrual with no inventory effect.
- **PO price change after receipt (FIFO)**: 100 units received at 10 and unbilled when the order is repriced to 12. Purchase declares +200; the layer restates to 12 and accrual is re-accrued at the new price, so the later invoice at 12 declares nothing and still washes.
- **PO price change on a partially billed line**: only the unbilled quantity is declared; the billed quantity's difference arrives, if the supplier bills it, as a PRICE correction into the same register — the two declarations cover disjoint quantities, so nothing double-counts.
- **PO price change on a pre-billed line**: received minus billed is negative, so the declaration is negative and waits in the register until the receipt arrives at the new price, where it pulls the layer back to the price the quantity was billed at.
- **Registered invoice cancelled after a price change**: the change skipped its quantity while it counted as billed, so cancellation declares the difference it missed and a re-bill at the current price declares zero.
- **Multiple receipts against one PO line**: targets are proportional to received quantities, so a receipt after an invoice pulls its share of the already-distributed variance from earlier layers (offsetting entries, no net journal effect), and every received unit carries the same share.
- **Over-billing**: quantity billed beyond receipts needs no special case — the declared variance distributes over the units actually received, and later receipts or take-back corrections re-spread it.
- **Partial and repeated invoicing**: each invoice tops the layers up by its shortfall — 60 then 40 of 100 units at the same price lands the same totals as one invoice for 100, and the accrual still washes to zero.

## Test Cases

- Given a FIFO item with a layer of 100 units at 10 fully on hand, when an invoice for 100 units at 12 posts, the layer cost should become 12 via a new FifoCost version and a journal of inventory 200 against invoice price variance 200 should be posted in the same transaction
- Given a FIFO layer of 100 units at 10 with 40 units consumed by a sales issue, when an invoice at 12 posts, inventory should be debited 120, COGS debited 80, and invoice price variance credited 200
- Given a FIFO layer with consumptions from a stock adjustment, when an invoice with a price difference posts, the consumed portion should post against the adjustment account
- Given an AVERAGE item holding 200 units at an average of 9.50, when an invoice declaring +120 on a fully on-hand layer posts, an AverageCost COST_ADJUSTMENT event moving value without quantity should be appended, the pool quantity should remain 200, and the average should become 10.10
- Given an AVERAGE layer with consumed quantity, when an invoice with a price difference posts, the consumed portion should be expensed to the consumed price variance account and no historical issue should be restated
- Given a STANDARD item, when an invoice with a price difference posts, inventory value and price state should be unchanged and the full difference should post to the PPV account
- Given an invoice whose unit price equals the purchase order unit price, when it posts, account-payable should skip the adjustment call and no adjustment record, price state, or journal entry should be created
- Given a credit memo or PRICE correction reducing the price, when it posts, the register should receive the negative amount and the journal should reverse direction
- Given an invoice covering a PO line with multiple receipt layers, when it posts, the variance should spread across the layers in proportion to their received quantities with allocations per layer
- Given a receipt of 100 units invoiced by two partial invoices of 60 and then 40 units at the same higher price, when both post, the second posting should distribute only the shortfall and the layer should end at the same cost one full invoice produces
- Given an invoice posted before any receipt, when it posts, the event should be recorded with no distribution, and when the receipt posts, the waiting variance should redistribute onto the new layer in the same transaction
- Given variance fully distributed to an existing layer, when a second receipt for the same order line posts, the variance should re-spread over both layers so every received unit carries the same share
- Given a layer whose consumption has no defined treatment mapping, when an adjustment posts, the posting should fail rather than default an account
- Given an AP document posting date in a closed accounting period, when the adjustment posts, the posting should fail
- Given a successful adjustment, an AcquisitionCostAdjustment record carrying the generic source reference to the causing document and per-layer allocations should be created
- Given receipt, invoice, and adjustment all posted for the same fully invoiced quantity, the accrual and invoice price variance account balances for the receipt should both be zero
- Given a FIFO layer of 100 units received at 10 and unbilled, when the order line is repriced to 12, purchase should declare +200 sourced to the revision and the layer should restate to 12 with a journal of inventory 200 against accrual 200
- Given a repriced line whose received quantity is fully billed, when the amendment posts, no variance should be declared
- Given a registered invoice cancelled after a price change, the cancellation should declare the difference the change skipped so a re-bill at the current price declares zero

## Reference Links

- [Inventory Valuation](./inventory-valuation.md) — costing methods, cost layers, and the valuation policy accounts this feature extends
- [Stock Tracking](./stock-tracking.md) — the ledger entries this feature extends with a persisted order reference to resolve receipt layers
- [Inventory Module README](../../README.md) — module scope and dependency overview
