> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vinmake.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Inventory and costing

> The one-way costing chain, immutably stamped unit costs, and why a frozen cost is what makes the WIP check real.

# Inventory and costing

Sub-ledger 2, migration `0004`.
Goods in, value through work in progress, finished goods out, cost of goods sold.

## The chain flows one way

```mermaid theme={null}
flowchart TB
  R["RECEIPT<br/>goods arrive"] --> I["ISSUE<br/>material to WIP"]
  I --> P["PRODUCTION_IN<br/>WIP to finished goods"]
  P --> S["SHIP<br/>finished goods to COGS"]
```

Every movement's `unit_cost` and value is **stamped immutably on the movement at post time**.

That is design decision D5, and it is the reason the costing checks have teeth.
Because a movement's cost is frozen, a later price change cannot retroactively move both the WIP-in and the WIP-out legs together.

In the v12 workbook, `stock_movement!G` is a live formula.
Change a price and both legs move, so "WIP nets to zero" is a tautology: it is true by construction and therefore tests nothing.
Here it is a real check, and it can fail.

## The account legs

| Operation                 | Entry                                                      | Value                                 |
| ------------------------- | ---------------------------------------------------------- | ------------------------------------- |
| goods receipt (GRN)       | `Dr 1200` (VAS 152) / `Cr 2010` (GRNI)                     | at PO price                           |
| material bill clears GRNI | `Dr 2010` / `Dr 1300` (VAS 133 input VAT) / `Cr 2000` (AP) |                                       |
| landed cost               | `Dr 1200` / `Cr 2000`                                      | exact-sum allocated by quantity       |
| material issue to WIP     | `Dr 1210` (VAS 154) / `Cr 1200`                            | qty x weighted-average cost           |
| labour to WIP             | `Dr 1210` / `Cr 2200` (wages payable)                      |                                       |
| overhead absorbed to WIP  | `Dr 1210` / `Cr 5120` (VAS 627 pool)                       |                                       |
| subcontract (CMT) to WIP  | `Dr 1210` / `Cr 2000`                                      |                                       |
| 627 pool sourced          | `Dr 5120` / `Cr 2000` or `1510`                            | rent, utilities, depreciation         |
| finished goods            | `Dr 1220` (VAS 155) / `Cr 1210`                            | total WIP value                       |
| COGS on ship              | `Dr 5000` (VAS 632) / `Cr 1220`                            | qty x finished-goods weighted average |

### Typed inventory adjustments

An inventory adjustment derives its direction from signed quantity and signed value and chooses the inventory account from the item's typed classification.
A caller cannot choose an arbitrary debit/credit account to reinterpret the sign, and inconsistent sign combinations refuse.
AP/AR control-account corrections use typed adjustment notes rather than the generic adjustment surface.

The Close inventory family reuses the same read-only Books inventory-to-GL tie-out instead of maintaining a second formula.
Open GRNI, missing historical evidence or an unreadable native register therefore remains partial/inconclusive in both surfaces rather than passing in Close while Books reports a break.

## Two ideas that carry the family

**GRNI is the accrual between receipt and invoice.**
Goods arrive before the bill does.
`2010` holds the liability from receipt at PO price, and the supplier's bill clears it - which is also the point where input VAT lands on `1300` and the AP obligation opens.
`2010` has no TT200 code because it is a management accrual that nets to zero by the time a return is filed.

**Landed cost allocates exactly.**
Freight, duty and handling are spread across the received quantities using `money.allocate_exact`, so the shares sum to the source amount to the dong.
No residue, no rounding account.

## The derived balances

Nothing here is stored.

| Balance                    | Derived from                               |
| -------------------------- | ------------------------------------------ |
| stock on hand, per item    | the movement rows                          |
| weighted-average unit cost | the stamped costs on the receipt movements |
| WIP (`154`)                | in-movements minus out-movements           |
| GRNI (`2010`)              | receipts not yet cleared by a bill         |
| the 627 overhead pool      | sourced minus absorbed                     |

## The Close checksums this family runs

Twenty-two fixed checks, plus one explainable balance per stock item.
The must-be-zero ones include:

| Code                                              | What it catches                                                                                   |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `WIP-154`                                         | everything that went into WIP came out as finished goods                                          |
| `POOL-627`                                        | the overhead pool was fully absorbed                                                              |
| `GRNI`                                            | goods received not invoiced ties to the receipts not yet billed                                   |
| `TOTAL-154`                                       | WIP value ties to what was declared as production input                                           |
| `NO-NEG-STOCK`                                    | no item has negative stock on hand                                                                |
| `LANDED-COST`                                     | landed-cost shares sum to the source exactly                                                      |
| `PROD-ORPHAN`                                     | no cost tagged to a production order that does not exist                                          |
| `INBOUND-DBLCOUNT`                                | inbound logistics is not double-counted into the production pool                                  |
| `BILL-LINE-FOOT`                                  | bill lines sum to the bill total, per bill                                                        |
| `ITEM-CATALOG`, `GRN-PO-CATALOG`, `PO-SO-CATALOG` | every referenced item, PO and sales order resolves                                                |
| `INVENTORY-GL-TIE`                                | signed native inventory value agrees with its GL control accounts under the same Books definition |

## Where it is enforced

| Concern                       | Code                                    |
| ----------------------------- | --------------------------------------- |
| the operations and their legs | `backend/app/logics/finance/costing.py` |
| the schema                    | migration `0004_inventory_costing`      |
| the derived balances          | `backend/app/logics/finance/derive.py`  |
| the checks                    | `close_checks.run_costing_checks`       |

## The test that would fail if it broke

`backend/tests/test_costing.py` covers the operations.
`backend/tests/test_v12_inventory_parallel_run.py` is the fidelity test: it runs the workbook's worked data through the sub-ledger and asserts the `total_154` figures to four decimal places.

## What goes wrong without it

A live-formula costing model does not produce wrong numbers so much as unfalsifiable ones.
Every reconciliation passes because both sides recompute from the same current price, so the model reports health regardless of whether the underlying data is right.

The specific damage is in margin.
COGS is the largest single number in a manufacturing P\&L, and if it is derived from a cost that moves after the fact, then last quarter's gross margin changes when this quarter's supplier price does.

## Related

* [The seven sub-ledgers](/greatbook/capabilities/subledgers/index) - the costing-to-payroll and costing-to-VAT edges
* [Money is Decimal, always](/greatbook/capabilities/money) - `allocate_exact`, which the landed-cost check rests on
* [Nothing stored that can be derived](/greatbook/capabilities/derived-balances) - the failure class this family illustrates best
* [The Close checksums](/greatbook/capabilities/close-list) - the costing family in the full surface
