At a Glance
Persona: Inventory Controller · Module: costing · Workflow stages: Upstream of Finance — lot-date / receipt-cost / adjustment-cost-basis verification · Cost-pick preview review at stock-out approval · Valuation variance investigation · Key permissions: approve adjustment posts below Finance threshold; cannot approve credit-note revaluation (COST_AUTH_005Finance) or edit cost-layer rows (COST_AUTH_010)
What this persona does: Ensures the costing engine's inputs are clean and defensible, and investigates variances Finance surfaces during reconciliation.
The Inventory Controller persona owns the cleanliness of the costing engine's inputs. The engine itself is a system service that runs under the actor's RBAC on each inventory transaction post (per COST_AUTH_009); the Controller's responsibility is to make sure the inputs flowing into the engine are correct and defensible: lot dates (assigned at inbound, drive the FIFO lot_seq_no — a back-dated GRN can shuffle FIFO order), receipt costs (the inbound unit cost after extra-cost allocation that becomes cost_per_unit on the new layer — out-of-band cost reduces FIFO COGS accuracy and skews WA averages), adjustment cost bases (the cost-per-unit on tb_stock_in rows — a new-lot stock-in at an anomalous cost pollutes downstream FIFO consumption), waste write-off cost bases (tb_stock_out outbound rows — the engine picks the cost via FIFO / WA but the Controller verifies the picked cost is sensible before approval), and the standard cost on tb_product to the extent the Controller has request authority (the actual standard-cost update is Finance / Sysadmin's via COST_AUTH_003). The Controller's other costing-relevant responsibility is valuation variance investigation — Finance surfaces above-tolerance variances during sub-ledger ↔ GL reconciliation, and the Controller drills into the cost-layer ledger to identify the offending row(s): an unexpected FIFO consumption that ate an older lot than expected, an adjustment cost that didn't match the vendor pricelist, a count-variance whose physical_count_costing_method-resolved cost differed from the expected basis. Crucially, the Controller does not approve credit-note-amount revaluations (that is Finance's COST_AUTH_005), does not lock the period (Finance Manager's COST_AUTH_006), does not configure calculation_method (Sysadmin's COST_AUTH_001), and never edits cost-layer rows directly per COST_AUTH_010. Corrections flow through the standard channels: a corrective stock-in / stock-out under their approval authority, a credit-note routed to Finance, or a configuration change request routed to Sysadmin.
The Inventory Controller operates upstream of Finance in the costing module — ensuring the engine's inputs are clean before cost-layer rows become immutable, and investigating cost-layer variances that Finance surfaces during reconciliation.
The Inventory Controller is the primary approval gate for inventory adjustment documents that write cost-layer rows. Costing has no doc-status enum; the Controller's costing authority is expressed through the adjustment approval flow and cost-anomaly investigation. Rows are derived from the primary flow steps in Sections 2.1–2.4 and the authorization rules at costing/02-business-rules § 4.
| Action | Controller authority | Constraint |
|---|---|---|
| View cost-layer ledger (read) | ✅ (COST_AUTH_007) |
Read-only; via cost-pick preview on outbound approval |
View cost-pick preview (FIFO lot walk / AVCO average) on outbound tb_stock_out |
✅ (COST_AUTH_007) |
Preview screen only; cannot override the picked cost |
Approve tb_stock_in (inbound adjustment — new lot cost-basis) |
✅ — within price_deviation_limit tolerance |
Above tolerance: escalate to Finance (COST_AUTH_004) |
Approve tb_stock_out (outbound adjustment — FIFO/AVCO cost-pick fires on approval) |
✅ — within Controller cost-impact threshold | Above threshold: escalate to Finance |
Reject tb_stock_in / tb_stock_out (return to Store Keeper) |
✅ | Cannot edit the cost on behalf of Store Keeper (SoD preservation) |
| Investigate Finance-escalated valuation variance (cost-layer drill) | ✅ (COST_AUTH_007) |
Read-only drill; resolution via compensating adjustment or escalation |
| Draft compensating stock-in / stock-out (corrective cost fix) | ✅ | Routes through normal approval flow; Finance co-approves above threshold |
| Route credit-note revaluation to Finance | ✅ (initiator) | Finance approves (COST_AUTH_005); Controller does not approve credit-notes |
Configure tb_business_unit.calculation_method |
❌ (COST_AUTH_001 — Sysadmin only) |
Controller may request via Finance |
Configure enum_physical_count_costing_method |
❌ (COST_AUTH_002 — Sysadmin only) |
Controller may flag count-costing method concern to Finance |
| Approve credit-note-amount revaluation | ❌ (COST_AUTH_005 — Finance only) |
Controller may initiate; Finance approves |
| Lock period / advance period status | ❌ (COST_AUTH_006 — Finance Manager only) |
Controller signs off cost-side pre-condition; Finance Manager executes |
Edit cost_per_unit or average_cost_per_unit directly on a posted row |
❌ (COST_AUTH_010) |
No role can edit a posted cost-layer row directly |
ℹ️ SR cost-pick is pass-through — not in the Controller's costing queue. Store Requisitions invoke the cost engine (
COST_POST_002,COST_XMOD_003) to pick the existing layer cost at the source location, but AVCO is not re-averaged and no new FIFO layer is created — existing layer is consumed at existing cost. SR cost-pick preview does not appear in the Controller's adjustment queue because no recalculation occurs; the SR moves goods at book value.
Entry points: Four paths, all consequences of upstream activity routing into the Controller's costing-relevant queue or downstream Finance escalation.
tb_stock_in and tb_stock_out documents at doc_status = in_progress with the cost-pick preview displayed for outbound (FIFO from oldest lot vs WA at current average per the business unit's calculation_method). Cost-pick preview is the Controller's primary tool for catching cost anomalies before approval.tb_stock_in documents that create a new lot (current_lot_no not previously seen at (location, product)). These documents introduce a fresh cost_per_unit into the cost-layer ledger; the Controller verifies the cost against the vendor pricelist ([vendor-pricelist](/en/inventory/vendor-pricelist)) and the product's price_deviation_limit (tb_product.price_deviation_limit).average_cost_per_unit jump beyond a threshold (signals a big-cost inbound that may have been miscoded), a FIFO outbound that spanned an unusual number of lots (signals heavily fragmented stock or possible lot-ordering issue).tb_stock_in and tb_stock_out documents at in_progress with the cost-pick preview alongside the standard fields (lines, lot, reason code, cost impact).tb_stock_in): the screen renders the new layer's cost_per_unit (if new-lot) or the existing lot's cost_per_unit it will add to (if same lot re-receiving — rare); for outbound (tb_stock_out): the engine's cost-pick preview — under FIFO, lists the lots being consumed in order with their cost_per_unit and out_qty per lot; under WA, shows the current average_cost_per_unit that will be picked. The Controller's review checklist (cost-specific): (a) for inbound new-lot, does the cost match the vendor pricelist last-price within tolerance? (b) for inbound existing-lot, does the new cost reconcile with the existing lot's cost or does it suggest a discrepancy? (c) for outbound, does the picked cost reflect a reasonable basis or does the FIFO walk surface an unusual old-lot consumption (e.g. a lot_seq_no = 1 lot from 6 months ago when the operational expectation is to consume from recent lots)?BREAKAGE at WA cost looks plausible; a BREAKAGE at standard-cost when the tenant configures last_receiving looks wrong — possible misconfiguration to flag). Escalate to Finance for cost-impacts above the Controller threshold per the standard cost-impact gate (mirrors INV_AUTH_005 for the inventory-side, with the Controller flagging cost concerns as part of the escalation).tb_stock_in.doc_status = completed / tb_stock_out.doc_status = completed; inventory transaction writes per INV_POST_001 / INV_POST_002; the costing engine writes the cost-layer row per COST_POST_001 / COST_POST_002 with the picked cost. The Controller's queue refreshes; the cost is now part of the immutable cost-layer ledger.doc_status = draft with cost-related comment ("Cost ฿50.00 exceeds pricelist ฿15.00 by 233%; verify with vendor pricing reference"); the Store Keeper edits the cost on the document or voids; the Controller does not edit the cost on behalf of the Store Keeper (preserves SoD and originator accountability).(location, product) — typically found-stock recoveries, migration-fix lots, or vendor-replacement stock arriving outside a regular GRN. The cost on these is critical because the new lot's cost_per_unit becomes the FIFO consumption cost for the lot's lifetime.tb_pricelist_detail price for the product at the active vendor; the deviation between the proposed stock-in cost and the pricelist last-price; the product's tb_product.price_deviation_limit tolerance band. If the deviation is within tolerance, the cost is presumed defensible. If above tolerance, the Controller flags for vendor-side confirmation.COST_AUTH_004 / COST_AUTH_005 cost-impact authority decides.cost_per_unit for the new lot is now part of the FIFO sequence — the engine assigns the next lot_seq_no; subsequent outbound at the same (location, product) may consume this lot at the picked cost. For WA, the new lot triggers an average recompute per COST_CALC_003.cost_per_unit, transaction_type, at_period, lot_no, from_lot_no (for outbound), diff_amount (for revaluation rows). Compare against the source-document linkage (GRN with extra-cost allocation, SR-driven outbound, count-variance derivation, credit-note adjustment).cost_per_unit due to vendor-pricelist drift not caught at approval; (b) wrong cost on outbound — under FIFO, the consumption walked an unexpected lot due to wrong lot_seq_no ordering (sometimes from out-of-order receiving / late lot creation); (c) count-variance cost mis-resolved — the physical_count_costing_method picked an unexpected source (e.g. standard when the tenant expected last); (d) credit-note revaluation effect — a diff_amount row drove the lot cost below the running average causing a downstream cost outlier; (e) transfer cost mismatch — transfer_in.cost_per_unit ≠ transfer_out.cost_per_unit (should fail COST_VAL_010 — indicates a bug or rare race).price_deviation_limit, average_cost_per_unit jump beyond a configured threshold, FIFO outbound spanning an unusual number of lots, count-variance whose resolved cost differs from a fallback expected basis.price_deviation_limit — approve under Controller authority. Above tolerance — reject pending vendor verification or escalate to Finance for cost-impact review (this is the same boundary as the inventory adjustment cost-impact gate).The Inventory Controller's involvement on a given costing thread ends at one of four boundaries:
COST_AUTH_004 / COST_AUTH_005. The Controller re-engages if Finance rejects and the document returns to Controller, or if Finance flags a follow-up investigation.[inventory/03-user-flow-inventory-controller](/en/inventory/inventory/03-user-flow-inventory-controller) Section 2 / 3) includes a cost-side gate: no unresolved cost anomalies, no pending corrective adjustments, no above-tolerance reconciliation variance attributed to cost-layer issues. With the sign-off, handoff to Finance for the period-end valuation orchestration.tb_inventory_transaction_cost_layer (the ledger the Controller reads at approval), tb_inventory_transaction_detail.cost_per_unit (the per-line cost they verify), tb_product.standard_cost / tb_product.price_deviation_limit (the deviation band they check against), enum_calculation_method (the configured FIFO / WA they preview).COST_CALC_001 (FIFO outbound — the cost-pick preview the Controller reads), COST_CALC_002 (WA outbound), COST_CALC_003 (WA inbound recompute), COST_CALC_005 (credit-note revaluation — Finance-driven), COST_CALC_008 (count-variance cost source); authorization rules COST_AUTH_007 (read cost-pick previews), COST_AUTH_010 (no direct cost-edit); posting rules COST_POST_001 / COST_POST_002 (engine writes on approval); cross-module rules COST_XMOD_004 (count-variance) / COST_XMOD_005 (manual adjustment).cost_per_unit the engine writes, and the Controller's cost-anomaly checks include verifying the landed-cost is sensible against the vendor pricelist.enum_physical_count_costing_method for valuation; the Controller's cost-pick preview on these reflects the configured source.tb_stock_in / tb_stock_out workflow the Controller approves; cost-pick is part of the approval surface.