At a Glance
Persona: Procurement Manager (high-value approval + Allocate Vendor configuration) · Module: purchase-request · Scenarios: ~29
Categories: Happy Path · Permission · Validation · Edge Case
E2E coverage: no dedicated Procurement Manager spec yet; escalation paths exercised viagmTestblocks intests/301-pr.spec.tsin../carmen-inventory-frontend-e2e/
This page captures the test scenarios that the Procurement Manager persona directly drives in the purchase-request module. The Procurement Manager owns two surfaces described in 03-user-flow-procurement-manager.md: a transactional surface — the final escalated approval stage that fires when base_total_amount breaches a configured high-value threshold per PR_AUTH_005 and uses the same review-and-decide UI as the base Approver chain with broader scope (including override of a prior Approver send-back) — and a configurational surface — the Allocate Vendor rule set (vendor ranking weights, per-vendor priority overrides, and Stuck PR Oversight bulk actions) that drives every Purchaser's downstream vendor-selection flow. Scenarios are grouped into the happy paths from 03-user-flow-procurement-manager.md Section 2 (escalated high-value approve / reject / send-back, adjust ranking weights, tune Allocate Vendor priority, bulk-action on stuck PRs, override delegation rules), the RBAC boundaries from PR_AUTH_002–PR_AUTH_008 in 02-business-rules.md Section 4, the validation rules that fire on each surface (PR_AUTH_005 threshold semantics, PR_VAL_013 on approved_qty, snapshot-preservation per PR_CALC_006), and a small set of boundary / concurrency cases unique to having both transactional and configurational rights. Cross-persona handoffs that pivot off the Procurement Manager (X-PR-05 threshold escalation, X-PR-10 returned-PR round trip when an escalated PR is sent back to draft) live in the parent overview, not here.
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| PM-HP-01 | Receive an escalated high-value PR in the queue | PR pr_status = in_progress, workflow_current_stage routed to the escalated approve stage because base_total_amount > tb_workflow.escalation_threshold per PR_AUTH_005; Procurement Manager pm@blueledgers.com logged in and present in user_action.execute[] for the escalated stage; notification "Purchase Request [PR-ID] Escalated for Procurement Manager Review" delivered |
1. Click the deep link in the notification (or Sidebar → Purchase Request → Escalated PRs queue). 2. The queue shows pr_no, requestor, department, base_total_amount in transaction and base currency, originating stage, threshold band that fired, and elapsed wait time at this stage. 3. Open the PR by clicking the pr_no row. 4. Confirm the detail page loads in read-mostly mode (header non-editable; only approved_qty and per-line decision flags interactive). |
PR detail page loads with the full upstream Activity Log (Department Head, Budget Controller, Finance prior decisions) plus the escalation annotation; the action bar shows Approve, Send Back, Reject; user_action.execute[] contains the Procurement Manager; no write side-effects on tb_purchase_request; queue row remains until the decision is committed. |
| PM-HP-02 | Approve an escalated PR — state transitions to approved |
PR in_progress at the escalated stage; Procurement Manager has walked the Items tab, Budget Impact panel, and Activity Log; no quantity adjustments needed; optional approval comment ready |
1. Click Approve in the action bar. 2. Confirm in the dialog (optional comment "Cleared — strategic vendor confirmed via email evidence attached"). 3. Server runs PR_AUTH_002 against user_action.execute[] for the escalated stage. |
PR_POST_005 fires because the escalated stage is the final approve stage in the chain by definition: pr_status transitions in_progress → approved; workflow_history records the final approve with the Procurement Manager's user_id; the workflow stepper marks the chain complete; the Requestor is notified ("Approved"); the PR drops into the Purchaser's Approved PRs queue and becomes eligible for PO conversion (PR_AUTH_008); soft budget commitment persists until PO creation hardens it; type = system comment captures the action and the optional comment (PR_POST_008); the PR drops from the Escalated PRs queue. |
| PM-HP-03 | Reject an escalated PR — state transitions to voided |
PR in_progress at the escalated stage; the PR overlaps a duplicate purchase already in approved on PR-202605-0099; reason text ready |
1. Click Reject in the action bar. 2. Mandatory-reason dialog appears — type "Duplicate of PR-202605-0099 (same vendor, same product, same delivery window)". 3. Confirm. |
PR_AUTH_004 + PR_POST_006 apply: pr_status flips from in_progress to voided (terminal); soft budget commitment released; workflow_history appended with the reject action and last_action_by_id = Procurement Manager; type = system comment captures the rejection reason (PR_POST_008); no subsequent stages run; the Requestor sees "Rejected" in My PRs; the Auditor can read the void / cancel reason post-hoc. |
| PM-HP-04 | Send-back an escalated PR — state returns to draft |
PR in_progress at the escalated stage; the justification narrative is insufficient for a high-value commitment; workflow configured so that send-back from the escalated stage rolls all the way back to the create stage; reason text ready |
1. Click Send Back in the action bar. 2. Mandatory-reason dialog appears — type "Strategic-spend justification missing — provide three-quote evidence and management endorsement before resubmitting.". 3. Confirm. |
PR_POST_003 fires: workflow_current_stage rolls back to the Requestor's create stage; pr_status returns to draft; last_action = reviewed; soft budget commitment released (rollback reached the create stage); Requestor notified with the reason text; type = system comment written (PR_POST_008); the PR drops from the Escalated PRs queue and re-enters once the Requestor resubmits and the chain re-clears. |
| PM-HP-05 | Adjust vendor ranking weights and save (configurational) | Procurement Manager has enum_stage_role = purchase per PR_AUTH_008; current Allocate Vendor rule set has scoring weights { vendor_rank: 50, lowest_price: 30, last_receiving: 20 }; no active configuration lock |
1. Sidebar → Procurement → Vendor Allocation Rules. 2. Open the Scoring Weights panel. 3. Shift weights to { vendor_rank: 40, lowest_price: 25, last_receiving: 35 } (sum still = 100). 4. Preview the change in the simulation panel against a representative product — observe ranking re-order for the candidate vendors. 5. Click Save. 6. Confirm in the dialog. |
New rule set written with a new effective-from timestamp; old rule set archived with its effective-to timestamp; Purchaser team notified that Allocate Vendor configuration changed; new PRs created after the timestamp use the new weights; PRs already in in_progress retain their snapshotted vendor allocation per PR_CALC_006-style snapshot semantics; the change is logged in the configuration audit trail with the Procurement Manager's user_id. |
| PM-HP-06 | Tune Allocate Vendor priority — pin and blacklist (configurational) | Allocate Vendor rule set has no per-vendor overrides currently active; vendor V_STRATEGIC should be pinned to top rank for product family BEVERAGE; vendor V_BAD_HISTORY should be excluded from auto-allocation for product family MEAT |
1. Open Vendor Allocation Rules → Per-Vendor Priority Overrides. 2. Add override: V_STRATEGIC pinned to top rank for BEVERAGE. 3. Add override: V_BAD_HISTORY excluded from auto-allocation (still manually selectable) for MEAT. 4. Preview in the simulation panel against BEVERAGE and MEAT representative products. 5. Click Save. 6. Confirm. |
Two per-vendor override rows written with the new effective-from timestamp; simulation panel reflects the override (V_STRATEGIC at rank 1 for BEVERAGE; V_BAD_HISTORY absent from auto-allocation results for MEAT but visible in the manual picker); the saved rule set takes effect for new PRs; in-flight PRs retain their snapshotted allocations; configuration audit trail records both override changes with the Procurement Manager's user_id. |
| PM-HP-07 | Bulk-action on multiple stuck PRs (configurational oversight) | Five PRs are stuck in in_progress past the configured SLA window (visible in the Stuck PR Oversight view); two are blocked solely on vendor allocation (no current pricelist match for the snapshotted vendor); three are blocked at an intermediate approve stage; Procurement Manager is authorised on all five |
1. Open Procurement → Stuck PR Oversight. 2. Filter to age > SLA; five rows appear. 3. Multi-select the two vendor-allocation-blocked PRs → choose One-off Re-allocation against the just-saved rule set. 4. Multi-select the three approval-stuck PRs → choose Escalation Ping with templated reason. 5. Confirm the bulk dialog (preview of impacted PRs and per-PR action shown). |
Per PR_AUTH_002 is re-evaluated per PR; all five pass for this Procurement Manager. The two re-allocated PRs receive updated vendor_id / pricelist_* snapshots on their detail rows and a type = system comment per PR recording the bulk re-allocation source (PR_POST_008); the three approval-stuck PRs receive a ping notification routed to their current stage's executors with a type = system comment recording the bulk-action origin; bulk-action audit log shows five impacted PRs and the per-action breakdown; no PR's pr_status changes (re-allocation is on the detail snapshot; ping is just a notification). |
| PM-HP-08 | Override delegation rules — Procurement Manager acts directly while a delegate is also configured | Procurement Manager had previously delegated their escalated-stage authority to pm-delegate@blueledgers.com per PR_AUTH_006 for a date range that is still active; an escalated PR sits in the queue; the Procurement Manager has returned early and chooses to act personally rather than wait for the delegate |
1. Procurement Manager logs in and opens Escalated PRs — the PR appears in their own queue (delegation does not remove the original from user_action.execute[]; it adds the delegate). 2. Open the PR → review → Approve. 3. Confirm. |
PR_AUTH_002 passes because the Procurement Manager remains in user_action.execute[] for the escalated stage even with delegation active; PR_AUTH_006 permits both the original and the delegate to act during the window; last_action_by_id reflects the Procurement Manager directly (not the delegate); PR_POST_005 runs normally; the delegate's queue refreshes and the PR is no longer visible to them (first-write-wins, same path as APP-EDGE-03). |
| # | Scenario | Expected behaviour (allow/deny + reason) |
|---|---|---|
| PM-PERM-01 | Procurement Manager opens an escalated PR (current user is in user_action.execute[] for the escalated stage) |
Allow read and the full action bar (Approve / Send Back / Reject / Split-Reject). PR_AUTH_002 is satisfied for the escalated stage; the escalated stage is itself an approve-role stage by PR_AUTH_005 routing, so PR_AUTH_003 and PR_AUTH_004 grant the same line-level and header-level rights as the base Approver chain. |
| PM-PERM-02 | Procurement Manager opens a PR that is not escalated to them (PR currently at the Budget Controller stage, which is earlier in the chain) | Deny action, read-only. PR_AUTH_002 recomputes user_action.execute[] for the current stage on every transition; the Procurement Manager is not in the Stage 2 executor list. List visibility may still allow the row to appear under All Documents depending on grant, but Approve / Send Back / Reject buttons are disabled with an inline explanation; a direct API call is rejected at the server with "You are not authorised to act on this PR at the current stage". |
| PM-PERM-03 | Procurement Manager edits Allocate Vendor rules (configurational surface) | Allow. Rule maintenance is restricted to enum_stage_role = purchase per PR_AUTH_008; the Procurement Manager holds that role on the configurational surface. The configuration audit trail records every edit with the Procurement Manager's user_id. |
| PM-PERM-04 | Procurement Manager attempts to edit Allocate Vendor rules while a system-wide conversion lock is active (PR_AUTH_007 analog — a Finance / sys-admin has temporarily frozen the rule set during a year-end conversion window) |
Deny. The configurational surface is locked while the conversion-lock flag is on; the rule editor goes read-only with a banner explaining the lock window; a direct API call to update the rule set is rejected with "Allocate Vendor rules are locked during the active conversion window — contact System Administrator.". The lock is itself maintained by a PR_AUTH_007-style admin scope (Finance / sys-admin only). |
| PM-PERM-05 | Procurement Manager runs a bulk action on multiple PRs from the Stuck PR Oversight view | Allow per-PR. Bulk actions still pass through per-PR PR_AUTH_002 checks; authorised PRs proceed and write per-PR audit comments (PR_POST_008); PRs the Procurement Manager is not authorised on (e.g. a different business unit's PR scoped out of their user_action.execute[]) are silently excluded from the bulk result with an explanatory line in the bulk-action audit log. The result dialog shows the count of impacted vs excluded PRs. |
| PM-PERM-06 | Non-purchase user (Requestor / HOD / Finance) opens Vendor Allocation Rules and attempts to save a weight change | Deny — wrong role. PR_AUTH_008 restricts rule maintenance to enum_stage_role = purchase. UI hides or disables the Save button for non-purchase roles; a direct API call to update the rule set is rejected with "You are not authorised to edit Allocate Vendor rules". |
| PM-PERM-07 | Configurational delegation — Procurement Manager attempts to delegate rule-maintenance rights to another user | Deny by default. PR_AUTH_006 covers delegation of transactional approval rights only; configurational rights (rule maintenance) are not delegable per 03-user-flow-procurement-manager.md Section 3. The delegation UI scopes the delegable rights to the transactional surface and disables / hides the configurational toggle; a direct API call to extend delegation to rule maintenance is rejected with "Allocate Vendor rule maintenance cannot be delegated". |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| PM-VAL-01 | Approval-threshold mismatch — PR is not actually above the Procurement Manager's threshold | A misrouted PR with base_total_amount = 50,000 ฿ landed in the Escalated PRs queue but the configured escalation threshold is > 100,000 ฿ per PR_AUTH_005; Procurement Manager clicks Approve |
Reject — PR_AUTH_005 re-validates the threshold at action time. Server rejects with "This PR does not meet the escalated-stage threshold and should not be at this stage"; the action is blocked and the workflow engine is flagged for stage-routing repair. The PR remains in in_progress at whichever earlier stage it should have stopped. |
| PM-VAL-02 | Rule change with no diff (no-op save) | Open Vendor Allocation Rules → click Save without changing any weight or override | Reject — the configuration writer requires a non-empty diff against the active rule set. Server rejects with "No changes detected — nothing to save". UI also disables the Save button until at least one staged change is present. No new effective-from row is written and no notification is fired. |
| PM-VAL-03 | Rule change committed while an in-flight PR still depends on the old rules — snapshot preservation per PR_CALC_006 |
PR in_progress already has vendor_id, vendor_name, pricelist_detail_id, pricelist_price snapshotted from PR submit under the old rule set; Procurement Manager saves a new rule set five minutes later; an Approver then approves the PR |
Allowed save, but snapshot is preserved. The configurational save is not blocked by in-flight PRs (per 03-user-flow-procurement-manager.md Section 3); however the in-flight PR keeps its old snapshotted allocation through to PO conversion. New rules only apply to: (a) PRs created after the effective-from timestamp, (b) PRs returned to draft via send-back, and (c) PRs run through the one-off bulk re-allocation. Any attempt to retroactively re-rank an in_progress PR via direct API call is rejected with "Cannot re-allocate vendor on an in-flight PR — use Stuck PR Oversight bulk re-allocation". |
| PM-VAL-04 | Vendor priority weights do not sum to 100 % | Open Scoring Weights and enter { vendor_rank: 50, lowest_price: 30, last_receiving: 30 } (sum = 110) |
Reject — scoring weights must sum to exactly 100 across the three criteria. Server rejects with "Scoring weights must sum to 100". UI also surfaces a live Σ = 110 ≠ 100 indicator and disables the Save button until the sum is corrected. The simulation panel does not re-rank until the validation passes. |
| PM-VAL-05 | Invalid scoring weight — negative value | Enter { vendor_rank: -10, lowest_price: 60, last_receiving: 50 } |
Reject — each scoring weight must satisfy 0 ≤ weight ≤ 100. Server rejects with "Scoring weight must be a non-negative percentage (0–100)"; UI flags the negative input with an inline error and disables Save. The same path rejects values above 100 with the same operator. |
| PM-VAL-06 | Approve / Reject / Send Back without a reason where reason is mandatory | Open the Reject or Send Back dialog, leave the reason textarea empty, click Confirm | Reject — reason is mandatory per the dialog contract for Reject (PR_AUTH_004 + PR_POST_006) and Send Back (PR_POST_003); both require the immutable system comment payload (PR_POST_008). UI disables the Confirm button when reason is blank; a direct API call is rejected with "A reason is required to reject this PR" (Reject) or "A reason is required to send back this PR" (Send Back). Approve allows an optional comment. |
| PM-VAL-07 | Edit approved_qty at the escalated stage but violate PR_VAL_013 |
On a line with requested_qty = 100, Procurement Manager types approved_qty = 120 then clicks Approve |
PR_VAL_013 — reject with "Approved quantity must be positive and may not exceed requested quantity". The escalated stage inherits all base-Approver line-level validation rules; the rule is strict > 0 and ≤ requested_qty evaluated after UoM conversion using approved_unit_conversion_factor. |
| PM-VAL-08 | Approver-issued send-back override attempted on a PR already terminated | A prior Approver had already issued Send Back and the PR returned to draft; the Requestor has not yet resubmitted; Procurement Manager attempts to override the send-back from a stale UI by clicking Approve |
Reject — state-machine guard: the PR is no longer in_progress at the escalated stage. PR_AUTH_002 re-evaluates against the current workflow_current_stage (the Requestor's create stage) and finds the Procurement Manager not on user_action.execute[] for that stage. Server rejects with "This PR has been sent back to the Requestor and cannot be approved without resubmission". The Procurement Manager's UI prompts a refresh. |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| PM-EDGE-01 | In-flight PR vs new rules snapshot boundary | PR-A is submitted at T-0 under rule set R1; vendor snapshot taken at submit. Procurement Manager saves rule set R2 at T+5m. PR-B is created at T+6m and submitted at T+7m |
PR-A retains R1 snapshot through approval and into PO conversion — vendor_id, vendor_name, pricelist_* columns on tb_purchase_request_detail are unchanged. PR-B's vendor snapshot at submit is taken under R2 — new weights, new per-vendor overrides applied. The boundary is the effective-from timestamp of R2, not the PR's approval time. This mirrors the PR_CALC_006 exchange-rate snapshot pattern. |
| PM-EDGE-02 | Exact threshold boundary — base_total_amount lands exactly at the Procurement Manager's lower bound |
Escalation threshold configured at base_total_amount > 100,000.00000 ฿; Approver adjusts approved_qty such that the new base_total_amount = 100,000.00000 ฿ (exact) |
Threshold comparison is strict > per PR_AUTH_005, so the exact boundary value does not trigger escalation; the PR does not route to the Procurement Manager and is finalised at the normal Finance stage instead. A value of 100,000.00001 ฿ would escalate. (Specific threshold numbers are configurable per organisation; the operator, not the number, is the invariant — same pattern as APP-EDGE-05.) |
| PM-EDGE-03 | Bulk-action partial failure — some PRs succeed, some fail | Procurement Manager selects ten PRs from Stuck PR Oversight for an Escalation Ping. Seven pass PR_AUTH_002 for the Procurement Manager and have a valid current stage; two are scoped to a business unit the Procurement Manager is not authorised on; one was just voided by Finance (PR_AUTH_007) in the past minute |
First-pass aggregation: PR_AUTH_002 and state-machine guards run per PR. The seven authorised, valid PRs receive the ping and a type = system comment recording the bulk source (PR_POST_008). The two unauthorised PRs are silently excluded with an explanatory log line. The one voided PR is rejected at the state-machine guard with "This PR is voided and cannot be acted on" and listed in the failure breakdown. The result dialog shows 7 succeeded / 3 failed (2 unauthorised, 1 voided). No partial transactional rollback applies — successful PRs keep their ping; failed PRs are left untouched. |
| PM-EDGE-04 | Delegation chain to Procurement Manager expires mid-decision | Procurement Manager A is on leave and has delegated their escalated-stage authority to Procurement Manager B per PR_AUTH_006 for the window [T-2d, T]; B opens an escalated PR at T-1m, walks the review, and clicks Approve at T+30s (after delegation expiry) |
Reject at the server. PR_AUTH_002 re-evaluates user_action.execute[] against the current clock; the expired delegate B is no longer in the list. Server returns "Your delegation has expired — please refresh". UI surfaces the message and prompts a refresh. The original Procurement Manager A (or a new delegate) must act. Same pattern as APP-EDGE-04 in the Approver scenarios. |
| PM-EDGE-05 | Configurational save vs concurrent in-flight one-off re-allocation | Procurement Manager opens Stuck PR Oversight and starts a one-off re-allocation for three PRs at T; before the bulk job commits, another Procurement Manager B saves a new rule set at T+1s |
The one-off re-allocation reads the rule set at the moment each per-PR re-allocation runs. Per-PR re-allocations that ran before T+1s apply R_old; per-PR re-allocations that ran after T+1s apply R_new. There is no transactional guarantee that all three PRs use the same rule set in a concurrent-write window. The bulk-action audit log records the rule-set timestamp used for each PR so the divergence is observable. Operationally rare — Stuck PR Oversight and the rule editor are documented to be used by one Procurement Manager at a time. |
| PM-EDGE-06 | Rule-set save lands exactly at the moment a new PR is being submitted | At T, Requestor clicks Submit on PR-X (which triggers the vendor-snapshot routine); at the same instant T, Procurement Manager saves rule set R2 (effective-from T) |
Tie-break is by the system clock at write-time. If PR-X's snapshot routine resolves the rule set at T-1ms, it reads R1; if at T+1ms, it reads R2. Both outcomes are valid — the contract is "use whichever rule set is effective at the moment of snapshot," not "give PR-X a preferred rule set." The audit trail on both the configuration row and the PR detail row records the timestamps so the choice is observable. |
X-PR-05 (threshold escalation to the Procurement Manager), X-PR-10 (returned-PR round trip when an escalated PR is sent back).PR_AUTH_002 per-stage executors, PR_AUTH_003 line-level actions and split-reject, PR_AUTH_004 header reject, PR_AUTH_005 amount-threshold routing and escalation, PR_AUTH_006 delegation, PR_AUTH_007 void scope analog for the configuration lock, PR_AUTH_008 enum_stage_role = purchase owns vendor allocation rules), Section 2 (PR_VAL_013 on approved_qty), Section 5 (PR_POST_003 send-back, PR_POST_005 final approve → approved, PR_POST_006 reject / void / cancel, PR_POST_008 immutable audit comments), Section 6 (vendor / pricelist snapshot semantics, snapshot-preservation behaviour during rule changes — PR_CALC_006 analog).PR_VAL_013 validation, delegation patterns (APP-EDGE-03, APP-EDGE-04), and threshold-boundary semantics (APP-EDGE-05) are inherited verbatim on the transactional surface.30X-pr-procurement-manager-journey.spec.ts exists yet. Escalation paths (X-PR-05) and high-value override are partly exercised via gmTest blocks in ../carmen-inventory-frontend-e2e/tests/301-pr.spec.ts (per-action × per-role permission coverage). Add a dedicated persona-journey spec once a stable Allocate Vendor Rules admin surface and Stuck PR Oversight view are wired into the frontend.../carmen/docs/purchase-request-management/PR-Module-Structure.md (Allocate Vendor module surface, vendor-allocation configuration shape, threshold-routing configuration), ../carmen/docs/purchase-request-management/PR-User-Experience.md (review-and-decide UI shared with the base Approver chain, escalation procedures), ../carmen/docs/purchase-request-management/PR-Overview.md (Procurement Manager stakeholder role, workflow engine integration, vendor management), ../carmen/docs/purchase-request-management/purchase-request-module-prd.md (product requirements for threshold-based routing and Allocate Vendor rule configuration), ../carmen/docs/purchase-request-management/testing.md (testing levels — escalation and configuration test patterns), ../carmen/docs/purchase-request-management/troubleshooting.md (Section 2.1 stuck-stage problems, Section 2.2 workflow transition issues, threshold-routing misconfigurations).