At a Glance
Persona: Inventory Controller (+ Department Manager review) · Module: inventory-adjustment · Scenarios: ~30
Categories: Happy Path · Permission · Validation · Edge Case
E2E coverage: maps to720-stock-issue.spec.ts,900-period-end.spec.tsin../carmen-inventory-frontend-e2e/
This page captures the test scenarios that the Inventory Controller persona directly drives in the inventory-adjustment module. The Controller is the governance authority above the auto-approve threshold and the balance-accuracy owner: they review and approve in_progress documents in the Controller threshold band, validate new-lot stock-ins regardless of cost, commit count-variance lines (which auto-creates and posts rollup documents), forward above-Controller-threshold documents to Finance, cancel pre-post or void post-fact, and monitor variance patterns. The Department Manager review responsibility (read-only cost-centre oversight) is folded into this persona group. Scenarios are grouped into happy paths (review-and-approve above-threshold, new-lot approval, count-variance commit, reject / cancel, forward to Finance), RBAC (Controller scope vs Store Keeper / Finance scope, void authority), validation (re-checked rules at approval time), and edge cases around threshold borderline, count-rollup with mixed outcomes, oversize-variance investigation flags.
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| IC-HP-01 | Approve Store-Keeper-submitted above-threshold stock-out | Inventory Controller ic@blueledgers.com logged in; Store-Keeper-submitted tb_stock_out at doc_status = in_progress, reason BREAKAGE, total ฿2,500 (above auto-approve ฿500, below Controller threshold ฿10,000), evidence attached. |
1. Open Inventory Adjustment module → Pending Approvals. 2. Click into the document. 3. Verify reason matches evidence (BREAKAGE + damage photo). 4. Check context: prior BREAKAGE for this product / location. 5. Review cost preview: FIFO picks LOT-1 at ฿10.00. 6. Click Approve. |
tb_stock_out.doc_status = in_progress → completed per ADJ_POST_002. Outbound tb_inventory_transaction; FIFO cost-layer rows; GL Dr Breakage Expense ฿2,500 / Cr Inventory ฿2,500. workflow_history records {stage: 'completed', action: 'approved', by: ic@blueledgers.com}. Maps to parent Scenario 2. |
| IC-HP-02 | Approve new-lot stock-in (cost defensibility) | Store-Keeper-submitted tb_stock_in at in_progress; reason VENDOR_FREE_REPLACEMENT; new lot_no = LOT-FOC; cost_per_unit = 0; total ฿0 (zero-cost FOC). |
1. Open the document. 2. Verify new-lot identity well-formed per ADJ_VAL_009. 3. Verify cost-defensibility: zero-cost is consistent with VENDOR_FREE_REPLACEMENT reason; vendor RMA attached. 4. Verify the lot label / expiry. 5. Approve. |
Per ADJ_AUTH_003, Controller is the explicit approval gate for new-lot stock-ins. Document doc_status = completed. New lot_no = LOT-FOC created with cost_per_unit = 0, lot_seq_no = max+1. WA recompute dilutes average per ADJ_CALC_005. Maps to parent Scenario 4. |
| IC-HP-03 | Reject a stock-out with mismatched reason / evidence | Store-Keeper-submitted tb_stock_out at in_progress; reason EXPIRY_WRITE_OFF but the lot is not expired (expiry date on the lot label is 6 months out). |
1. Open document. 2. Notice expiry mismatch on lot. 3. Click Reject. 4. Enter rejection reason: "Lot not expired; use correct reason code (e.g. BREAKAGE or DATA_FIX) and resubmit." |
doc_status = in_progress → draft; rejection reason in workflow_history. Store Keeper sees rejection; edits the reason code or cancels. No inventory effect. |
| IC-HP-04 | Commit count-variance — auto-rollup | physical-count completed at LOC-A; variance lines: P-1 over by 2 (cost ฿20), P-2 short by 1 (cost ฿15), P-3 short by 0.5 (cost ฿8.50). |
1. Open completed count document. 2. Review variance lines; confirm real (not counting error). 3. Click Commit Variances. | Per ADJ_POST_006: (a) one tb_stock_in with reason COUNT_OVERAGE, one line for P-1 qty=2; (b) one tb_stock_out with reason COUNT_SHORTAGE, two lines for P-2 qty=1, P-3 qty=0.5. Both auto-advance to completed under Controller authority. info.countId = <count_uuid>. tb_count_stock.status = completed_posted. Maps to parent Scenario 5. |
| IC-HP-05 | Forward above-Controller-threshold document to Finance | Store-Keeper-submitted tb_stock_out at in_progress; reason RECALL_WRITE_OFF; total ฿85,000 (above Controller threshold). |
1. Open document. 2. Verify recall context (recall notice attached). 3. Verify the lots are on the recall notice. 4. Click Forward to Finance with comment "Controller-verified; ready for Finance approval". | workflow_current_stage advances to "finance-review"; document stays in_progress; appears in Finance Approvals queue per 04-test-scenarios-finance.md FN-HP-*. Maps to parent Scenario 3. |
| IC-HP-06 | Void posted document via compensating reversal | A completed tb_stock_in at LOC-A for P-1 qty 10, identified as duplicate-post (the same found stock was reported twice by two SKs). |
1. Open the duplicate tb_stock_in. 2. Click Void → "Create compensating reversal". 3. Form pre-fills a tb_stock_out with the same lines, reason DATA_FIX, info.voidsAdjustmentId = <original_stock_in.id>. 4. Submit (Controller authority — auto-approves regardless of threshold under void-compensation). 5. After compensating posts: original tb_stock_in.doc_status = voided. |
Compensating tb_stock_out.doc_status = completed with info.voidsAdjustmentId. Inventory effect: on-hand at (LOC-A, P-1, LOT-1) reduced by 10 (reversing the original duplicate). Original tb_stock_in.doc_status = voided; original inventory transaction NOT edited per inventory INV_POST_012. Maps to parent Scenario 13. |
| IC-HP-07 | Direct create — Controller raises stock-out for write-off below SoD threshold for SK | SoD blocked the Store Keeper from raising a write-off against a lot they themselves received (parent Scenario 14). Controller raises directly within Controller scope. | 1. Controller opens New Stock-Out. 2. Reason EXPIRY_WRITE_OFF. 3. Lines per the original need. 4. Submit. |
Controller-created direct documents follow the standard flow; below Controller threshold auto-approves (Controller is above SK auto-approve and below Finance threshold); above Controller threshold forwards to Finance. doc_status = completed; inventory effect applied. |
| # | Scenario | Expected behaviour (allow/deny + reason) |
|---|---|---|
| IC-PERM-01 | Approve / reject in_progress documents within Controller threshold band |
Allow per ADJ_AUTH_004. Below auto-approve threshold: Controller doesn't see those (auto-approved). Above Controller threshold: Controller forwards to Finance instead of approving. |
| IC-PERM-02 | Controller approves a document above Controller threshold directly | Deny — Finance required. Per ADJ_AUTH_005, above-Controller-threshold requires Finance. API call returns "Approval above the Controller threshold requires the Finance role; please forward to Finance instead." Document stays in_progress. |
| IC-PERM-03 | Controller approves a new-lot stock-in regardless of cost | Allow per ADJ_AUTH_003. Controller is the explicit gate for new-lot stock-in; below-threshold auto-approve does not apply. |
| IC-PERM-04 | Controller commits count variances | Allow per ADJ_POST_006 / ADJ_XMOD_002. Triggers auto-rollup tb_stock_in / tb_stock_out documents at completed under Controller's signature. |
| IC-PERM-05 | Controller voids a completed document |
Allow per ADJ_AUTH_007 / ADJ_POST_004. Two-step: raises compensating reversal first; original moves to voided after compensating post. |
| IC-PERM-06 | Controller cancels an in_progress document |
Allow per ADJ_AUTH_007. doc_status = cancelled with reason; terminal; no inventory effect. |
| IC-PERM-07 | Controller attempts to configure tb_adjustment_type |
Deny — Sysadmin required. Per ADJ_AUTH_008, master-data CRUD is Sysadmin scope. Controller can request configuration changes via internal process but not execute them. |
| IC-PERM-08 | Controller attempts to edit completed document directly |
Deny per ADJ_VAL_013. Must use void + compensating reversal pattern. |
| IC-PERM-09 | Department Manager (folded persona) attempts to approve a document | Deny — read-only. The Department-Manager review responsibility within this persona group is read-only oversight; approval requires the dedicated Controller role. Notifications and comment / flag-for-escalation are allowed; approval is not. |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| IC-VAL-01 | Approve fails on stale on-hand check (ADJ_VAL_012) |
At Controller approval time, another posting has consumed from the lot since the SK's submit, leaving insufficient on-hand. | Approval rejects with the live recheck per inventory INV_VAL_005 ("Outbound movement would drive on-hand at (..., lot) below zero. Available: , requested: ."). Document returns to draft with system note; SK re-picks lot or reduces qty. |
| IC-VAL-02 | Approve fails on closed period (ADJ_VAL_011) |
The period containing si_date / so_date closed between SK submit and Controller approval. |
Approval rejects with "Cannot post into period <YYMM>: period is closed." Per inventory INV_VAL_008. Controller can request Finance Manager re-open or coordinate a current-period restatement. |
| IC-VAL-03 | Approve fails when reason no longer matches direction (ADJ_VAL_002) |
Sysadmin deactivated / soft-deleted the reason between SK submit and Controller approval. | Approval rejects with "Adjustment reason is no longer valid; please pick an active reason matching the document direction." Document returns to draft. |
| IC-VAL-04 | Approve fails when location becomes inactive (ADJ_VAL_003) |
Sysadmin deactivated the location between SK submit and Controller approval (rare). | Approval rejects with "Cannot post to an inactive or soft-deleted location." Document returns to draft. |
| IC-VAL-05 | Commit count variance fails when count document not at completed |
Attempt to commit variances on a count still at in_progress. |
Reject with "Count must be at completed status before variances can be committed." (Validation in physical-count / spot-check module, called from the adjustment commit path.) |
| IC-VAL-06 | Void without compensating reversal (ADJ_VAL_014) |
Direct API call sets tb_stock_in.doc_status = voided without raising the compensating reversal. |
Reject with "Cannot soft-delete or directly void a posted adjustment without a compensating reversal." per inventory INV_VAL_013. |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| IC-EDGE-01 | Threshold boundary at upper edge (Controller → Finance) | Document cost ฿10,000 exactly (Controller threshold). |
Boundary inclusive — Controller can approve. ฿10,000.01 exceeds and requires Finance. |
| IC-EDGE-02 | Count-rollup with mixed outcomes (overage AND shortage on same product across lots) | Count: P-1 LOT-1 short 1; P-1 LOT-2 over 2. |
Two rollup documents, not netted. Auto-rollup creates one tb_stock_in (LOT-2 overage qty 2) and one tb_stock_out (LOT-1 shortage qty 1); not a single netted tb_stock_in qty 1. Preserves per-lot audit trail. Both auto-advance to completed. |
| IC-EDGE-03 | Oversize variance flag investigation | Count-variance line above ADJ_CALC_008 variance threshold (e.g. > 10% net cost impact). |
Controller may reject the count (request recount) before committing, preventing the auto-rollup from posting. Alternatively, commit with comment flagging for Finance investigation; rollup still auto-posts but the document carries the flag. |
| IC-EDGE-04 | Concurrent approve attempts (two Controllers) | Two Controllers open the same in_progress document simultaneously; both click Approve. |
First commit wins; second sees "Document already approved — refresh" (optimistic-concurrency on doc_version per tb_stock_in.doc_version / tb_stock_out.doc_version). No double-post. |
| IC-EDGE-05 | Forwarding to Finance via direct API bypassing UI | API call moves workflow_current_stage to "finance-review" without comment. |
Reject — Forward action requires comment per workflow rule (tb_workflow definition). Returns "Forward action requires a comment." |
| IC-EDGE-06 | New-lot approval with cost outlier | SK-submitted new-lot stock-in cost_per_unit = ฿100.00 for product with [vendor-pricelist](/en/inventory/vendor-pricelist) last-price ฿10.00. |
Controller sees 10× outlier flag; expected to reject pending defensibility (vendor-RMA reference, attached invoice). If approved without justification, audit-trail flag triggers Auditor review per ADJ_AUTH_009. |
| IC-EDGE-07 | Compensating-reversal of compensating-reversal | After void via compensating reversal, identify that the original void was wrong and re-void the compensating. | Allowed — compensating documents are normal tb_stock_in / tb_stock_out and can themselves be voided per ADJ_POST_004. The void chain is two-deep (original → comp → comp-of-comp); Auditor inspects per ADJ_AUTH_009. The on-hand effect nets to the original state. |
| IC-EDGE-08 | Department Manager flag-for-escalation | DM (read-only sub-persona) flags a posted adjustment as anomalous via comment. | Comment recorded on tb_stock_in_comment / tb_stock_out_comment; notification fires to Finance and Auditor. No state change; Finance / Auditor follow-up. |
ADJ_AUTH_003 (new-lot gate), ADJ_AUTH_004 (Controller approval), ADJ_AUTH_007 (cancel / void), ADJ_POST_002 (post fan-out), ADJ_POST_004 (void via compensating), ADJ_POST_006 (count-rollup), ADJ_CALC_008 (variance %), validations re-checked at approval (ADJ_VAL_002, ADJ_VAL_003, ADJ_VAL_009, ADJ_VAL_011, ADJ_VAL_012).../carmen-inventory-frontend-e2e/tests/720-stock-issue.spec.ts — outbound approval pattern shared with adjustment stock-out; 900-period-end.spec.ts — period-end variance-hold and approval-gate patterns. Controller-fixture user inventory@blueledgers.com (typical multi-role).INV_AUTH_003 (Controller as second signature in inventory hierarchy), INV_POST_001 / INV_POST_002 (post effects), INV_VAL_005 / INV_VAL_008 (re-check rules).