At a Glance
Persona: Inventory Controller · Module: costing · Scenarios: ~27
Categories: Happy Path · Permission · Validation · Edge Case
E2E coverage: maps to501-grn.spec.ts,701-sr.spec.ts,720-stock-issue.spec.tsin../carmen-inventory-frontend-e2e/
This page captures the test scenarios that the Inventory Controller persona directly drives in the costing module. The Controller owns engine-input cleanliness: lot dates and lot_seq_no assignment correctness, receipt costs (the cost_per_unit written on inbound layers), adjustment cost bases (cost on tb_stock_in for new lots, picked cost on tb_stock_out for outbound), and waste write-off cost basis. They review the cost-pick preview on every adjustment approval (FIFO walk vs WA current average per COST_AUTH_007), verify new-lot cost basis against vendor pricelists / tb_product.price_deviation_limit, investigate valuation variances Finance surfaces during reconciliation, and proactively triage cost anomalies on the cost-layer ledger. The Controller does not approve credit-note revaluations (Finance per COST_AUTH_005), does not advance period state (Finance Manager per COST_AUTH_006), does not configure calculation_method (Sysadmin per COST_AUTH_001), and never edits cost-layer rows directly per COST_AUTH_010. Their costing-module ownership begins when (a) a Store Keeper raises an adjustment with cost implications, (b) a new-lot stock-in lands, (c) Finance escalates a variance, or (d) the cost-anomaly sweep surfaces an outlier, and ends when (a) the document posts at the picked cost, (b) the document escalates to Finance for above-threshold cost-impact or credit-note path, (c) the variance is resolved via compensating adjustment, or (d) the anomaly is dismissed or routed. Scenarios are grouped into happy paths (cost-pick preview matches, new-lot cost approved within tolerance, variance investigation resolved by compensating adjustment, anomaly dismissed), RBAC (Controller cannot edit cost-layer; Controller cannot approve credit-note), validation (cost-pick preview drives negative balance, new-lot cost above tolerance), and edge cases (FIFO consumption spanning many lots; WA average drift at boundary; concurrent approval on documents affecting the same lot).
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| IC-HP-01 | Approve FIFO outbound spanning two lots — cost-pick preview matches | FIFO business unit; product P-1 at LOC-A with two lots: LOT-1 (lot_seq_no=1, cost ฿10, remaining 20), LOT-2 (lot_seq_no=2, cost ฿14, remaining 50); Store Keeper raises a tb_stock_out for qty = 30 (breakage write-off, BREAKAGE reason). Cost-pick preview: 20 units from LOT-1 at ฿10 + 10 units from LOT-2 at ฿14 = ฿340 total. Controller controller@blueledgers.com logged in. |
1. Open the adjustment approval queue. 2. Open the document; review the cost-pick preview (two-row FIFO walk). 3. Verify the walk matches expected lot rotation (oldest first). 4. Click Approve. | tb_stock_out.doc_status = completed; one outbound tb_inventory_transaction; two outbound cost-layer rows per COST_POST_002 / COST_CALC_001: row 1 (out_qty=20, cost=฿10, from_lot_no=LOT-1), row 2 (out_qty=10, cost=฿14, from_lot_no=LOT-2). LOT-1 fully drained; LOT-2 remaining = 40. GL: Dr Department Expense ฿340.00 / Cr Inventory ฿340.00. Maps to parent Scenario 1. |
| IC-HP-02 | Approve WA outbound at current running average | WA business unit; product P-1 at LOC-A with on-hand 100 units, average_cost_per_unit = ฿11.33333; Store Keeper raises stock-out for 30 units. Cost-pick preview: cost_per_unit = ฿11.33333 (single row). |
1. Open document; review cost-pick preview. 2. Verify average matches recent inbound history. 3. Click Approve. | tb_stock_out.doc_status = completed; one outbound cost-layer row per COST_POST_002 / COST_CALC_002: out_qty = 30, cost_per_unit = ฿11.33333, total_cost = ฿340.00. Average unchanged (outbound doesn't re-blend). On-hand reduced to 70 units at ฿11.33333. |
| IC-HP-03 | Approve new-lot stock-in — cost within vendor-pricelist tolerance | FIFO business unit; new-lot stock-in for LOT-NEW at LOC-A, P-1, qty 10, cost ฿15.50; vendor pricelist last-price ฿15.00; product price_deviation_limit = 10% (i.e. ฿1.50). Deviation +฿0.50 within tolerance. |
1. Open new-lot review queue. 2. Open document; cross-reference vendor pricelist. 3. Confirm ฿15.50 / ฿15.00 = 1.033 ≤ 1.10. 4. Click Approve. |
tb_stock_in.doc_status = completed; inbound cost-layer row per COST_POST_001: in_qty = 10, cost_per_unit = ฿15.50, lot_no = LOT-NEW, lot_index = 1, lot_seq_no = (max + 1). average_cost_per_unit shadow recomputed per COST_CALC_003. New lot enters FIFO sequence. |
| IC-HP-04 | Reject anomalous-cost stock-out (FIFO stale-lot consumption) | FIFO business unit; product P-1 at LOC-A has a 6-month-old LOT-1 (lot_seq_no=1, cost ฿20) plus recent lots at ฿15; Store Keeper raises stock-out for breakage; cost-pick preview shows FIFO consuming LOT-1 at ฿20 (stale high-cost lot). Vendor pricelist current ฿15. Controller suspects rotation issue. |
1. Open document; review cost-pick preview. 2. Identify LOT-1 as stale high-cost; rotation should have consumed it earlier. 3. Click Reject with comment: "FIFO consumes stale LOT-1 at ฿20 (current market ฿15); rotation issue. Review on-hand aging before write-off." |
Document doc_status = in_progress → draft; Controller's comment on activity log; no cost-layer row written; Store Keeper sees rejection. Operational follow-up: review lot rotation; possibly age-based write-off via a separate EXPIRY_WRITE_OFF workflow. Maps to parent Scenario 10. |
| IC-HP-05 | Investigate Finance-flagged variance — corrective adjustment posted | Finance reconciliation flagged a ฿200 variance at LOC-A; drill identifies a stock-in row posted with cost_per_unit = ฿20 that should have been ฿15 (typo at Store Keeper entry, missed at Controller approval); investigation traces it back. Controller drafts a compensating adjustment to correct the cost. |
1. Receive escalation. 2. Drill into cost-layer; identify the wrong-cost row LAYER-X (cost_per_unit = ฿20, qty 40, total ฿800). 3. Draft a compensating: stock-out of the wrong-cost lot's remaining qty plus stock-in at the correct cost (฿15). 4. Route through normal approval (which may escalate to Finance for cost-impact). |
After approvals (Controller and/or Finance depending on threshold): compensating cost-layer rows written; original wrong-cost row stays (immutable); net effect is the cost corrected to ฿15. Reconciliation variance drops. Maps to parent Scenario 14 cost-side resolution. |
| IC-HP-06 | Triage cost anomaly — dismiss legitimate variation | Cost-anomaly dashboard surfaces a cost_per_unit = ฿5 outbound on P-1 at WA business unit; current average_cost_per_unit = ฿10. Drill reveals: a one-off promotional vendor discount on a recent inbound dropped the running average; outbound at the new average is legitimate. |
1. Open the cost-anomaly dashboard. 2. Drill into the outlier. 3. Verify the recent inbound that drove the average down (vendor promo invoice attached). 4. Click Dismiss with comment. | Anomaly dismissed; activity log records the dismissal with the operational context; no cost-layer adjustment. Future anomaly checks ignore this dismissed row. |
| # | Scenario | Expected behaviour (allow/deny + reason) |
|---|---|---|
| IC-PERM-01 | Controller reads cost-pick previews on adjustment documents | Allow per COST_AUTH_007. Read scope on tb_inventory_transaction_cost_layer + tb_inventory_transaction_detail.cost_per_unit; cost-pick preview computed via the engine's strategy resolver under read context. |
| IC-PERM-02 | Controller approves adjustment at picked cost | Allow per inventory INV_AUTH_003. The picked cost is part of the document; approval commits the document and fires the cost-layer write. Controller is the actor; engine is the writer. |
| IC-PERM-03 | Controller attempts to edit cost_per_unit directly on a posted cost-layer row |
Deny — terminal per COST_AUTH_010. API returns "Cost-layer rows are immutable." Controller's resolution path is compensating adjustment, credit-note routing (via Finance), or configuration fix (via Sysadmin). |
| IC-PERM-04 | Controller attempts to approve a credit-note-amount revaluation | Deny — Finance only per COST_AUTH_005. The credit-note approval queue is not visible to the Controller role. Controller can flag cost concerns that suggest a credit-note is needed; Finance owns the approval. |
| IC-PERM-05 | Controller attempts to change tb_business_unit.calculation_method |
Deny — Sysadmin only per COST_AUTH_001. Controller can request the change (e.g. via service ticket); Sysadmin executes; Finance coordinates the drain pre-condition. |
| IC-PERM-06 | Controller attempts to lock the period | Deny — Finance Manager only per COST_AUTH_006. Same gate as inventory module. |
| IC-PERM-07 | Controller edits tb_product.standard_cost |
Deny by default — Finance / cost-control authority per COST_AUTH_003. Controller can request a standard-cost refresh (typically routed via Finance during the monthly cadence). |
| IC-PERM-08 | Controller approves adjustment whose cost-impact exceeds Controller threshold | Allow Controller's first signature; Finance-pending sub-state per inventory INV_AUTH_005. Controller's approval flags the document Finance-pending; Finance is the role that fires the cost-layer write at completion. |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| IC-VAL-01 | Approve outbound when no cost-layer available (FIFO) | FIFO business unit; product P-FRESH at a new location LOC-NEW with no prior inbound; Store Keeper attempts a stock-out by mistake. |
Reject at cost-pick per COST_VAL_002: server returns "FIFO: no available cost layer at (location, product) to consume." Document cannot proceed; inventory INV_VAL_005 also fires (no-negative-balance). |
| IC-VAL-02 | Approve outbound when no average initialised (WA) | WA business unit; same setup as IC-VAL-01. | Reject at cost-pick per COST_VAL_003: server returns "Weighted Average: no prior inbound layer at (location, product) to read average from." |
| IC-VAL-03 | Approve new-lot stock-in with cost above price-deviation-limit | New-lot stock-in for P-1 at LOC-A, qty 10, cost ฿30; vendor pricelist last-price ฿15; price_deviation_limit = 10%. Deviation +฿15 (100%) above tolerance. |
Reject at approve by Controller's deviation check (logic implemented at the application layer reading tb_product.price_deviation_limit): server returns "Cost ฿30 exceeds pricelist last-price ฿15 by 100% (tolerance 10%); verify vendor pricing or escalate to Finance." Controller may reject to Store Keeper or escalate. |
| IC-VAL-04 | Approve adjustment with negative cost on inbound | Store Keeper enters cost_per_unit = −฿5 on a stock-in document; Controller attempts to approve. |
Reject at cost-pick per COST_VAL_004: server returns "Cost-pick produced an invalid cost_per_unit (negative or non-finite): −5.0." Document cannot proceed. |
| IC-VAL-05 | Approve transfer where source and destination cost differ | Manual override attempt: source location FIFO produces transfer_out.cost_per_unit = ฿10; destination override sets transfer_in.cost_per_unit = ฿12. |
Reject at post per COST_VAL_010: server returns "Transfer cost mismatch: transfer_in.cost_per_unit must equal transfer_out.cost_per_unit." Maps to parent Scenario 16. |
| IC-VAL-06 | Count-variance cost cannot be resolved (configured source missing) | Tenant enum_physical_count_costing_method = standard; product P-NEW has standard_cost = NULL or 0; count completes with variance line. |
Reject at count-rollup post per COST_VAL_007: server returns "Count-variance valuation: configured count-costing-method standard cannot resolve a cost (missing standard_cost on product)." Rollup cannot post; routes back to Controller for source fix (set standard_cost first or change method). |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| IC-EDGE-01 | FIFO consumption spanning many lots | FIFO product with 5 partial lots, each at different cost_per_unit; outbound qty consumes all 5. |
Five outbound cost-layer rows under one detail line. Each row carries its consumed lot's cost_per_unit and the consumed out_qty. Sum of out_qty equals total outbound qty per COST_CALC_001. Cost-pick preview shows all 5 rows pre-approve; Controller reviews and approves. |
| IC-EDGE-02 | WA average exactly at column precision boundary | WA product; recompute produces new_average = ฿11.333335; stored at 5dp rounds to ฿11.33334 (half-up). Subsequent outbound qty = 1 at ฿11.33334 = ฿11.33. |
Half-up rounding consistent per COST_CALC_010. Stored and displayed values match the rounding policy. Cumulative drift over many movements stays within 5dp precision; reconciliation tolerance absorbs aggregate rounding. |
| IC-EDGE-03 | Concurrent approval on documents affecting the same lot | Controller A approves stock-out from LOT-X at T; Controller B (peer) approves a different stock-out from LOT-X at T+100ms. |
Both succeed if balance suffices; first-fail-fast otherwise. Both attempts read remaining qty; FIFO cost-pick produces non-overlapping cost-layer rows; if combined out_qty > LOT-X.remaining, the second post fails COST_VAL_002 / INV_VAL_005. Defensive race handled at write. |
| IC-EDGE-04 | Cost-pick preview at threshold boundary | Adjustment whose aggregate cost impact = Controller threshold exactly. | Boundary inclusive — Controller approval terminal. ≤ threshold is Controller-final; > threshold routes to Finance. Mirrors inventory IC-EDGE-01. |
| IC-EDGE-05 | New-lot cost exactly at deviation tolerance | New-lot cost ฿16.50; vendor pricelist last ฿15.00; price_deviation_limit = 10% (tolerance ฿1.50). Deviation +฿1.50 = 10% exactly at tolerance. |
Boundary inclusive — approve at tolerance. ≤ tolerance passes; > tolerance rejects. Controller approves; cost-layer row writes. |
| IC-EDGE-06 | FIFO outbound when newest layer was created same minute as request | A GRN committed at T, creating LOT-NEW with lot_seq_no = max + 1; SR-driven outbound at T+200ms consumes from LOT-NEW. |
FIFO walks lot_seq_no ascending — consumes older lots first, not LOT-NEW. Unless older lots are exhausted, LOT-NEW is untouched. The Controller's preview correctly shows older-lot consumption. |
| IC-EDGE-07 | WA average drift after credit-note revaluation | WA business unit; lot LOT-X revalued via credit-note-amount diff_amount = −฿100; lot's cost_per_unit reduced; the running average_cost_per_unit shadow should refresh on the next inbound (revaluation alone doesn't refresh the average — only inbound does). |
Average refresh deferred to next inbound per COST_CALC_003. Until next inbound, the running average reflects the pre-revaluation state. This is a deliberate design choice — revaluation affects only the specific lot's cost; the running average tracks future inbound. Document this gap to avoid surprise. |
COST_AUTH_007 (cost-pick preview read), COST_AUTH_010 (no direct cost-edit), COST_VAL_002 (FIFO no-available-layer — IC-VAL-01), COST_VAL_003 (WA no-prior-inbound — IC-VAL-02), COST_VAL_004 (cost non-negative — IC-VAL-04), COST_VAL_007 (count-variance source — IC-VAL-06), COST_VAL_010 (transfer cost coherence — IC-VAL-05), COST_CALC_001 (FIFO multi-row outbound — IC-HP-01 / IC-EDGE-01), COST_CALC_002 (WA single-row outbound — IC-HP-02), COST_CALC_003 (WA inbound recompute — IC-EDGE-07), COST_CALC_010 (rounding — IC-EDGE-02), COST_POST_001 / COST_POST_002 (engine writes on approval — IC-HP-01 / IC-HP-03), COST_XMOD_004 (count-variance), COST_XMOD_005 (manual adjustment).720-stock-issue.spec.ts (stock-out approval surface — IC-HP-01 / IC-HP-04); 701-sr.spec.ts (SR-driven outbound — IC-HP-01); 501-grn.spec.ts (inbound cost-layer write — IC-HP-03 indirect). Cost-anomaly dashboard and new-lot review flows are likely manual / planned.enum_physical_count_costing_method.tb_stock_in / tb_stock_out workflow; cost-pick preview is part of the Controller's approval surface.