At a Glance
Module: costing · Personas: Finance · Inventory Controller · Auditor
Workflow lifecycle: Cost-layer row born (inbound) → picked (FIFO/WA outbound) → revalued (credit-note) → period-close anchor → period-open rollforward → period locked. Period valuation:open→closed→locked.
Drill into per-persona views below for action-level detail
This page is the overview entry point for the user-flow set of the costing module. Costing is unusual relative to its sibling document modules — there is no single workflow document on which a draft → saved → committed lifecycle plays out, and there is no separate document tree at all. Costing is a behaviour layer over the inventory cost-layer ledger, so the lifecycle the personas walk is the lifecycle of a unit cost at (location_id, product_id, lot_no): it is born on an inbound tb_inventory_transaction_cost_layer row (a GRN receipt sets cost_per_unit; under WA the running average is recomputed), it is picked by every subsequent outbound (FIFO from oldest lot; WA at the prevailing average), it is revalued by credit-note-amount adjustments (diff_amount), it is rolled forward at period close into the next period's opening cost, and it is locked into the period snapshot as the audit anchor. Each persona owns a different slice of that lifecycle: Finance owns the valuation policy and the period-end sign-off, the Inventory Controller owns the engine's input cleanliness (lot dates, receipt costs, adjustment cost bases, waste write-offs), and the Auditor verifies that costed COGS and ending inventory tie back to source receipts and that the costing method is applied consistently across periods.
Section 2 below describes the cost-flow lifecycle — the canonical set of legal transitions on a unit cost, from inbound creation through outbound consumption through period rollforward, independent of who acts. Each per-persona file (linked from Section 3) describes that persona's path through this state space — their entry point, the actions available to them, the decision branches they face, and the handoff that ends their involvement. Section 4 then summarises the cross-persona handoffs that stitch the individual paths together (Inventory Controller → Finance for variance / credit-note approval, Finance → Finance Manager for period-end valuation lock, Finance Manager → Auditor for audit-cycle handoff). Read this overview first to anchor the lifecycle, then drill into the persona file that matches your role.
Costing has no per-document state machine (no draft → saved → committed lifecycle). The cost engine is invoked by inventory-affecting transactions. The diagram below mirrors the Process Execution Swim Lane from Test_case/System_Process/INDEX.md (version 1.3.0, capture date 2026-04-27) and Test_case/System_Process/proc-03-cost-calculation.md (version 1.1.0, capture date 2026-04-26).
Two state machines coexist in this module: the per-cost-layer-row lifecycle (degenerate — each cost-layer row is written immutable at post time, optionally revalued via a diff_amount row, optionally rolled forward at period close) and the per-period valuation lifecycle that mirrors the inventory module's tb_period.status (open → closed → locked, the substantive lifecycle for valuation reporting). The transitions below cover both.
| From state | Action | To state | Allowed for | Pre-conditions |
|---|---|---|---|---|
(none) |
inbound cost-layer write (engine picks inbound cost) | posted |
Source-document role on the inventory side (GRN-Inventory-Manager-on-commit, SR-Approver-on-issue-transfer-in, Counter-on-count-overage, Stock-Keeper-or-Controller-on-stock-in-approve) | Source document is in its terminal posting state; COST_VAL_001–COST_VAL_005 pass; the business unit's calculation_method resolves. Engine writes cost_per_unit, average_cost_per_unit, lot_seq_no per COST_POST_001. |
(none) |
outbound cost-layer write (engine picks outbound cost via FIFO / WA) | posted |
Source-document role (SR-Approver-on-issue, Controller-on-stock-out, Finance-on-credit-note-quantity) | Source document terminal; COST_VAL_002 / COST_VAL_003 pass (consumable layer exists); FIFO consumes by lot_seq_no asc producing 1+ rows; WA consumes at current average producing 1 row. Engine writes the outbound rows per COST_POST_002. |
posted |
credit-note-amount revaluation | posted (revalued — original row still present, new diff_amount row added) |
Finance (on credit-note approve) | Originating lot identified; COST_VAL_006 passes. Engine writes a new cost-layer row with in_qty = out_qty = 0, diff_amount = signed_amount per COST_POST_003. The originating lot's cost_per_unit is recalculated per COST_CALC_005; downstream consumption from the same lot picks up the revalued cost. Already-consumed portions are not retroactively adjusted. |
posted |
period-close anchor (close_period) |
posted (anchored to period) |
System / scheduled job (period-end run) | Period being closed has all source documents terminal; engine writes close_period cost-layer rows per (period_id, location_id, product_id, lot_no, lot_index) carrying the closing cost; locks the per-lot cost into the period anchor per COST_POST_007. |
posted |
period-open rollforward (open_period) |
posted (rolled forward to next period) |
System / scheduled job (chained with close) | Engine writes open_period cost-layer rows for the next period at cost_per_unit = previous.closing_cost_per_unit, lot_seq_no preserved per COST_POST_008. FIFO sequence carries across the boundary. |
posted |
(no further direct action — terminal under normal flow) | posted |
— | The cost-layer row is immutable; reading by reporting / costing / audit; no edits. Revaluation only via the credit-note path or the period-end rollforward. |
tb_period.status)| From state | Action | To state | Allowed for | Pre-conditions |
|---|---|---|---|---|
(none) |
create period (system / scheduled) | open |
System Administrator | Period definition populated; no overlap with existing period rows (mirrors INV_* period rules). |
open |
accept cost-layer writes | open |
All transactional roles (via source modules) | The engine writes cost-layer rows for any inventory transaction whose date falls within [period.start_at, period.end_at]. Backdated writes into a closed / locked prior period are rejected per COST_VAL_008 / INV_VAL_008. |
open |
run period-end valuation (close + rollforward) | closed |
Finance, with Inventory Controller's variance-review sign-off | Pre-period-end checklist complete (per inventory/03-user-flow § 2.2): all in-flight source documents terminal; counts variance posted; Controller signed off; reconciliation passes. Engine runs COST_POST_007 (write snapshot's closing cost columns) chained with COST_POST_008 (write next period's opening rollforward). Sets tb_period.status = closed. |
closed |
re-open period (exceptional valuation correction) | open |
Finance Manager (elevated; audit-logged) | Audit-grade justification required ("external audit identified a missed credit-note that should have revalued lot X in the closed period"). Re-open invalidates the closing snapshot pro-forma; subsequent re-close re-runs COST_POST_007 / COST_POST_008 with the revised cost-layer state. |
closed |
lock period (valuation immutable) | locked |
Finance Manager | Audit window passed cleanly; reconciliation report clean; Finance Manager signs off. After lock, the tb_period_snapshot.closing_cost_per_unit / closing_total_cost for this period are permanently immutable per COST_AUTH_006. |
locked |
(no further transitions) | locked |
— | Terminal. The period's valuation is the audit-immutable answer; corrections post into the current open period as restatement (a current-period write-down / write-up if needed). |
Each persona below has a dedicated drill-down file describing their entry point, primary flow, decision branches, and exit point. Slugs match the persona role; clicking the link opens the per-persona view.
tb_period.status = closed → locked. Finance is the role that decides what the costing engine should do; the engine itself runs under system context.lot_seq_no which is assigned at inbound), receipt costs reasonable against vendor pricelists (tb_product.price_deviation_limit flags drift), adjustment cost bases sensible (a new-lot stock-in should match the vendor reference; a write-off cost shouldn't be wildly higher than current cost-layer cost). Also investigates valuation variances that Finance surfaces during reconciliation — e.g. a per-lot FIFO consumption that produced unexpected COGS, an outlier on the cost-pick-preview screen during stock-out approval.COST_VAL_009; period rollforward preserves lot_seq_no for FIFO across the boundary; weighted-average reconcile to the running average_cost_per_unit shadow even under FIFO). Runs read-only cost-flow trace queries; deliverable is the audit report, not a write.The table below captures the moments where costing work moves from one persona's responsibility to another's. Each handoff is anchored to the system state at the point of transfer.
| From persona | Trigger | To persona | System state at handoff |
|---|---|---|---|
| Inventory Controller | Anomalous cost-pick preview surfaced on stock-out approval (e.g. FIFO consuming a very-old high-cost lot causing unexpected COGS; or WA average drifted from vendor pricelist by more than tolerance) | Finance | Stock-out document at doc_status = in_progress; Controller has flagged the cost concern but not approved; Finance reviews the cost basis before the document continues. |
| Inventory Controller | Adjustment cost basis exceeds tenant deviation tolerance (tb_product.price_deviation_limit) on a manual stock-in |
Finance | Stock-in document at in_progress; Controller has rejected back to Store Keeper or escalated to Finance for cost confirmation; document awaiting cost verification. |
| Finance | Credit-note-amount adjustment approved (vendor concession on a posted GRN's lot cost) | System / costing engine | Credit-note at approved; the inventory module fires the cost-layer revaluation via INV_POST_007 / COST_POST_003; the originating lot's cost_per_unit updates; downstream consumption picks up the revalued cost. Finance's role on the specific revaluation is done. |
| Finance | Inventory-sub-ledger ↔ GL reconciliation surfaces a costing-side variance (e.g. cost-layer activity sum doesn't match the GL Inventory control account because a credit-note's inventory effect wasn't replicated to GL) | Inventory Controller (investigation) | Period is still open; reconciliation dashboard shows the variance; Controller investigates the offending cost-layer rows; resolution may be a compensating stock-in / stock-out (clerical cost-fix) or a GL-side journal adjustment (integration gap). |
| Finance | Period-end valuation run complete (sub-ledger closing matches GL closing within tolerance) | Finance Manager | Period at tb_period.status = closed; tb_period_snapshot.closing_cost_per_unit / closing_total_cost written for every key; ready for Finance Manager to advance to locked after the audit window. |
| Finance Manager | Period locked | Auditor | Period at tb_period.status = locked; valuation immutable; the period's COGS / ending-inventory figures are the audit-anchored answers; Auditor consumes for external audit, lot-trace queries, and FIFO-vs-WA-shadow consistency reviews. |
| Finance Manager | Configuration change to tb_business_unit.calculation_method proposed (FIFO ↔ average) |
System Administrator (config save) + Finance (drain check) | The Sysadmin attempts the save; COST_VAL_009 blocks if any product has non-zero on-hand at the business unit. Resolution: drain stock (rare) or run an elevated migration. Finance coordinates the migration; Sysadmin executes the configuration once the drain is verified. |
| Auditor | Cost-flow chain-of-custody trace complete (forward from a GRN inbound through all downstream consumption) | Finance (cost-impact review) + Inventory Controller (lot-trace operational review) | No transaction state change. The trace report is the deliverable; operational follow-up (recall write-off, vendor blacklisting, lot-aging policy update) lives on the owning persona's flow (inventory-adjustment for write-off, good-receive-note for vendor handling). |
../carmen/docs/costing/enhanced-costing-engine.md — recipe / portion / dynamic-pricing engine context; outside the cost-flow scope of this page but referenced by Finance when reviewing recipe-driven COGS.tb_inventory_transaction_cost_layer, tb_period_snapshot, tb_business_unit.calculation_method, tb_product.standard_cost, and the five enums (enum_calculation_method, enum_business_unit_config_key, enum_physical_count_costing_method, enum_transaction_type, etc.) used in Section 2's transitions.COST_POST_001–COST_POST_010 for the cost-layer write events, COST_AUTH_001–COST_AUTH_010 for the role gates, COST_VAL_008 for the period-lock guard).COST_POST_001 with the landed-cost-allocated unit price), store-requisition (the primary source of outbound cost-pick — SR issue / transfer invokes COST_POST_002), physical-count / spot-check (count-variance posts use enum_physical_count_costing_method to select the variance cost source per COST_CALC_008), inventory-adjustment (manual tb_stock_in / tb_stock_out — adjustment cost basis flows through COST_POST_001 / COST_POST_002), recipe (downstream consumer of the per-product cost basis), product (carries standard_cost — the reference cost used by enum_physical_count_costing_method = standard and by recipe baseline).