At a Glance
Persona: Requestor · Module: purchase-request · Scenarios: ~36
Categories: Happy Path · Permission · Validation · Edge Case
E2E coverage: maps totests/302-pr-creator-journey.spec.ts,tests/311-pr-returned-flow.spec.ts, andtests/301-pr.spec.ts(requestorTest / noAuthTest fixtures) in../carmen-inventory-frontend-e2e/
This page captures the test scenarios that the Requestor persona directly drives in the purchase-request module. It covers the happy paths described in 03-user-flow-requestor.md Section 2, the RBAC boundaries implied by PR_AUTH_001 and the draft-only edit rule in 02-business-rules.md, the submit-time validation rules PR_VAL_001..PR_VAL_012 that a Requestor can trigger first-hand, and a small set of boundary / concurrency cases that fall out of Decimal(15, 5) / Decimal(20, 5) precision and the send-back loop. Cross-persona handoffs (X-PR-02, X-PR-10) that start at the Requestor and bounce off an Approver live in the parent overview, not here.
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| REQ-HP-01 | Create and submit a minimal PR (single line, in-base currency) | Requestor requestor@blueledgers.com logged in; active tb_workflow for purchase-request scope; at least one active tb_product in the catalogue; base currency THB |
1. From the PR list view click Create New PR. 2. Fill header: pick department_id, set delivery_date ≥ pr_date, leave currency_id at the base default, pick workflow_id, enter description. 3. Add 1 line with requested_qty > 0, valid requested_unit_id, pricelist_price and a future delivery_date. 4. Save header (auto-save also runs on connection loss). 5. Open Review tab, confirm subtotal / discount / tax / grand total. 6. Click Submit. |
New row in tb_purchase_request with pr_status = draft → in_progress; pr_no auto-generated and unique; last_action = submitted; workflow_current_stage = first approval stage; soft budget commitment registered; first-stage approver notified; Requestor sees the PR on the My PRs dashboard. |
| REQ-HP-02 | Create and submit a multi-line PR with attachments | As REQ-HP-01; vendor quotation PDF available to upload; two active products in the catalogue | 1. Create New PR, fill header. 2. Add line 1 (Product A, qty, UoM, line discount 5 %, tax 7 %). 3. Add line 2 (Product B, different store location, FOC qty > 0, discount 0). 4. Open Attachments tab → upload quotation PDF, set description and visibility. 5. Review header totals (PR_CALC_007 roll-up). 6. Submit. |
Two non-deleted rows in tb_purchase_request_detail (PR_VAL_006 satisfied); attachment row persisted with description / visibility; header base_net_amount / base_total_amount equal the line roll-up to 5 dp; PR transitions to in_progress and first approver receives the notification. |
| REQ-HP-03 | Save draft today, resume edit next day, then submit | Requestor logged in; no submit yet | 1. Create New PR, fill header, add 1 line. 2. Click Save (no submit) → PR persisted with pr_status = draft. 3. Log out / close tab. 4. Next day, log back in, open the PR from My PRs. 5. Edit description and qty on line 1 (PR_AUTH_001 allows because still draft and Requestor owns it). 6. Submit. |
Draft visible on next login with last-saved state; edit allowed because pr_status = draft and requestor_id == auth.user.id; on submit doc_version increments per PR_VAL_016; status transitions draft → in_progress. |
| REQ-HP-04 | Respond to send-back and resubmit | PR was submitted by this Requestor and the first-stage approver clicked Send Back with a reason; PR is back to pr_status = in_progress with workflow_current_stage at the prior create stage and edit unlocked for the Requestor |
1. Open the PR from the dashboard or the in-app notification. 2. Read the approver comment in the workflow history. 3. Edit the offending field (e.g. update requested_qty or fix description). 4. Click Submit again. |
Edit succeeds because Requestor is back inside the create stage role for this PR; last_action = submitted re-stamped, workflow_current_stage advances to first approval stage again; soft budget commitment is re-evaluated; revision history preserves the prior submit + send-back comments (PR_POST_008 keeps system comments immutable). |
| REQ-HP-05 | Cancel own draft from the detail page | PR exists in pr_status = draft; Requestor owns it; never submitted |
1. Open the PR detail page. 2. Click Delete / Cancel. 3. Confirm in the modal. | pr_status flips to voided; no workflow stage was advanced; no soft budget commitment was ever created (because it never reached submit); audit log captures the cancel event; PR no longer appears on the active list and edit controls are removed. |
| REQ-HP-06 | Create PR from template, modify, submit | At least one active tb_purchase_request_template with detail rows visible to the Requestor; template carries a workflow_id for purchase-request scope |
1. Create from Template, pick the first template in the picker. 2. The new PR pre-fills header (currency, workflow) and clones template detail rows into tb_purchase_request_detail. 3. Tweak requested_qty and delivery_date on one line. 4. Submit. |
New PR is independent of the template; tb_purchase_request_template_detail rows are not mutated; PR transitions draft → in_progress; workflow follows the template's workflow_id. |
| REQ-HP-07 | Submit a PR with line-level discount and tax adjustments | As REQ-HP-01; tax profile and discount inputs enabled | 1. Add a line with pricelist_price = 185.00000, discount_rate = 5, tax_rate = 7, is_discount_adjustment = false, is_tax_adjustment = false. 2. Verify the live preview shows sub_total_price = 2,220.00000, discount_amount = 111.00000, net_amount = 2,109.00000, tax_amount = 147.63000, total_price = 2,256.63000 for requested_qty = 12. 3. Submit. |
Line persists with the rule-engine values from PR_CALC_001..PR_CALC_005; header roll-up base_total_amount = 2,256.63000 ฿ (THB base, exchange_rate = 1.00000); display layer may truncate to 2 dp but persisted value retains 5 dp. |
| REQ-HP-08 | Submit a foreign-currency PR (USD line, base = THB) | As REQ-HP-01; tb_currency for USD active; effective exchange_rate available on or before pr_date |
1. Add a USD line with pricelist_price = 5.20000, requested_qty = 12, discount_rate = 5, tax_rate = 7, exchange_rate = 35.50000. 2. Verify base preview ≈ base_total_price = 2,251.74180 ฿. 3. Submit. |
exchange_rate is snapshotted on the line at submit and frozen for the life of the document (re-approving does not re-fetch); base_* columns computed per PR_CALC_006; soft budget commitment is registered in base currency. |
| # | Scenario | Expected behaviour (allow/deny + reason) |
|---|---|---|
| REQ-PERM-01 | Requestor opens a PR they own (requestor_id == auth.user.id) in pr_status = draft |
Allow read and edit. PR_AUTH_001 grants the owner exclusive edit rights while the PR is draft. |
| REQ-PERM-02 | Requestor opens a PR owned by another user in pr_status = draft |
Deny edit, read-only (no Edit / Delete / Submit toolbar). PR_AUTH_001 reserves edit to the owner or an explicit delegate. List visibility itself depends on the All Documents / department-wide grant; if the PR is outside the Requestor's department it does not appear on the list at all. |
| REQ-PERM-03 | Requestor edits their own draft (header description and one line qty) | Allow. PR_AUTH_001 is satisfied; doc_version increments on save per PR_VAL_016. |
| REQ-PERM-04 | Requestor tries to edit their own PR after submit (pr_status = in_progress) |
Deny. State-machine lock — edit controls are read-only. The only way back to edit is for an approver to Send Back the PR to draft. |
| REQ-PERM-05 | Requestor cancels / deletes their own draft PR |
Allow. Owner-only action on a not-yet-submitted document; no workflow stage was advanced, no soft commitment to release. |
| REQ-PERM-06 | Requestor tries to cancel their own PR after submit (in_progress or approved) |
Deny. Submitted PRs can only be returned to the chain via approver Send Back, rejected by an approver (PR_AUTH_004 → voided), or voided by Finance / sys-admin (PR_AUTH_007 → voided). The Requestor never sees a Cancel control for a submitted PR. |
| REQ-PERM-07 | Requestor tries to approve their own PR | Deny. Segregation of duties — the create stage role and the downstream approve stage roles are disjoint by workflow definition (see 02-business-rules.md Section 4 default chain). Even if the Requestor were named on the approver stage by misconfig, user_action.execute[] is recomputed per stage and the create-stage owner is filtered out. |
| REQ-PERM-08 | Requestor with noAuth fixture / expired session opens any PR URL |
Deny — redirect to login. Outside the auth context there is no auth.user.id to match against requestor_id; PR_AUTH_001 cannot apply. Covered by the noAuthTest fixture in 301-pr.spec.ts. |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| REQ-VAL-01 | Submit without a pr_no (or duplicate pr_no) |
Header insert raced two clients; or seed test creates a row with a pr_no that collides with an existing non-deleted row |
PR_VAL_001 — reject with "PR reference number is required and must be unique". Unique index PR0_pr_no_u on (pr_no, deleted_at) enforces. |
| REQ-VAL-02 | Submit without a valid requestor_id / requestor_name snapshot |
Requestor account was deactivated mid-edit, or snapshot lost | PR_VAL_002 — reject with "Requestor is required". |
| REQ-VAL-03 | Submit with department_id empty or pointing at a department the Requestor does not belong to |
Header department_id cleared, or set to a value outside user.memberships |
PR_VAL_003 — reject with "Department is required and must match requestor membership". |
| REQ-VAL-04 | Submit without a workflow_id (or pointing at a workflow whose scope ≠ purchase-request) |
Header workflow_id cleared |
PR_VAL_004 — reject with "A valid PR workflow must be selected". |
| REQ-VAL-05 | Submit with pr_date in the future |
Set pr_date to tomorrow |
PR_VAL_005 — reject with "PR date cannot be in the future". |
| REQ-VAL-06 | Submit a PR with zero non-deleted lines | Save header only, then click Submit | PR_VAL_006 — reject with "A PR must contain at least one line item". Submit button is also disabled in the UI per TC-PR-050604. |
| REQ-VAL-07 | Save a line without a product_id (or with an inactive product) |
Free-text description only, no product picked; or product was soft-deleted in the catalogue | PR_VAL_007 — reject with "Product is required on every line". NOT NULL also enforced at the DB layer. |
| REQ-VAL-08 | Save a line with requested_qty = 0 (or negative, or missing requested_unit_id) |
Type 0 in the qty input on a new line |
PR_VAL_008 — reject with "Requested quantity must be greater than zero and have a unit". |
| REQ-VAL-09 | Save a line with delivery_date earlier than pr_date |
Set the line delivery_date to a past date relative to pr_date (covered by TC-PR-050211) |
PR_VAL_009 — reject with "Delivery date cannot be earlier than the PR date". |
| REQ-VAL-10 | Add a second line for the same (product_id, location_id, dimension) already on the PR |
Duplicate line entry within the same PR | PR_VAL_010 — reject with "Same product cannot be requested twice for the same location and dimension". Unique index PR1_purchase_request_product_location_dimension_u enforces. |
| REQ-VAL-11 | Save a line with currency_id missing or exchange_rate ≤ 0 or exchange_rate_date later than pr_date |
Force-clear exchange_rate to 0, or set exchange_rate_date to tomorrow |
PR_VAL_011 — reject with "Currency and exchange rate are required and must be effective on or before the PR date". |
| REQ-VAL-12 | Save a line with tax_rate = 110 (or discount_rate = -5) |
Manually type an out-of-range rate | PR_VAL_012 — reject with "Tax and discount rates must be between 0 and 100". |
| REQ-VAL-13 | Two browser tabs edit the same draft and the second one tries to save | Tab A saves description; Tab B (still on the older doc_version) clicks Save |
PR_VAL_016 — reject with "Document was modified by another user; reload and retry". Successful saves bump doc_version by 1. |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| REQ-EDGE-01 | Zero quantity boundary | Line saved with requested_qty = 0.00000 |
Rejected per PR_VAL_008 — the rule is strict > 0, not ≥ 0; same applies to negative values which the input should also reject at the form layer. |
| REQ-EDGE-02 | Maximum decimal precision | Line entered with requested_qty = 1.99999, pricelist_price = 0.00001, discount_rate = 99.99999, tax_rate = 99.99999, exchange_rate = 35.12345 |
Persisted exactly at 5 decimals on Decimal(15, 5) / Decimal(20, 5) columns; the 6th decimal is rounded half-up before subsequent steps per the rounding policy in 02-business-rules.md Section 3. Display may truncate to 2 dp but the persisted value retains 5 dp. |
| REQ-EDGE-03 | Back-dated requested_date (line delivery_date = pr_date) |
delivery_date == pr_date (same calendar day) |
Accepted — PR_VAL_009 is "on or after pr_date", so equality is valid. |
| REQ-EDGE-04 | Far-future requested_date |
Line delivery_date = 2099-12-31 |
Accepted by PR_VAL_009; downstream stages (Approver, Purchaser) may flag during their own validation but the Requestor's submit is not blocked. |
| REQ-EDGE-05 | Line currency differs from the department's default currency | Header currency_id = USD, but Requestor's department default is THB |
Allowed at the Requestor stage — base-currency conversion happens via PR_CALC_006 using the snapshotted exchange_rate; the soft budget commitment is registered in base (THB). The Approver chain can still send back with a "use department currency" reason but that is downstream. |
| REQ-EDGE-06 | Concurrent edits to the same draft | Same draft opened in two tabs; both edit different fields and click Save in quick succession | First save wins and bumps doc_version; second save is rejected by PR_VAL_016 and the UI prompts a reload. Last-write-wins is not the policy. |
| REQ-EDGE-07 | Send-back received while the Requestor was still editing | An approver clicked Send Back on a PR that was already in in_progress; the Requestor had the PR open in draft view (or was watching the detail page) |
On refresh / next save, the client sees pr_status already back at in_progress with workflow_current_stage rolled back; edit becomes available again (the prior in_progress lock is released). Any unsaved local changes since the send-back are merged into the editable draft (PR_VAL_016 still guards each save). |
X-PR-02, X-PR-09, X-PR-10 touch the Requestor).PR_VAL_001..PR_VAL_012, PR_VAL_016) and Section 4 (PR_AUTH_001, PR_AUTH_004, PR_AUTH_007).../carmen-inventory-frontend-e2e/tests/302-pr-creator-journey.spec.ts (TC-PR-0501NN..TC-PR-0509NN blocks — list, create, template, draft, edit, submit, delete, golden flow). Returned-PR loop is in ../carmen-inventory-frontend-e2e/tests/311-pr-returned-flow.spec.ts. Per-action × per-role permission coverage lives in ../carmen-inventory-frontend-e2e/tests/301-pr.spec.ts (requestorTest, noAuthTest fixtures).../carmen/docs/purchase-request-management/testing.md (testing levels, sample tests), ../carmen/docs/purchase-request-management/troubleshooting.md (Sections 1.1, 2.1, 3.1, 6.2 — known failure modes for form submission, workflow / approval, calculation, and permission).