At a Glance
Persona: Vendor (external party — no Carmen login) · Module: purchase-order · Scenarios: ~15
Categories: Happy Path · Permission (N/A) · Validation · Edge Case
E2E coverage: no dedicated vendor spec (no Carmen login); vendor-driven events exercised indirectly viatests/401-po.spec.ts(TC-PO-030001..030004 send-to-vendor, TC-PO-340001..340003 GRN-sync) in../carmen-inventory-frontend-e2e/
This page captures the test scenarios that exercise the Vendor persona's interaction with the purchase-order module. Unlike the other persona files, the Vendor is an external party with no Carmen system login (03-user-flow-vendor.md Section 1) — every system-side effect of a vendor action is recorded by an internal persona (the Purchaser for acknowledgement and amendment, the Receiver for GRN postings against PO_POST_006 / PO_POST_007, the Finance persona for invoice capture and three-way match). The scenarios below therefore describe vendor-driven events (transmitted PO received, acknowledgement returned, shipment dispatched, invoice issued) together with the system effects that the internal personas record on the vendor's behalf. Because the vendor has no in-system role, the file is intentionally shorter than the other persona scenario files — the Permission / Authorization section is reduced to a single N/A row, and cross-persona handoffs (X-PO-01, X-PO-05, three-way-match) live in the parent overview, not here.
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| VND-HP-01 | Receive a transmitted PO (email PDF or vendor-portal callback) | PO at po_status = sent after Purchaser's Send to Vendor action; tb_purchase_order.email set and approval_date = now(); vendor's email or portal-callback endpoint configured on tb_vendor |
1. Carmen transmits the PO via the configured channel (email PDF, EDI feed, or vendor-portal link). 2. Vendor's mailbox / portal receives the document referencing po_no, lines, qty, price, delivery date, and payment terms. |
The PO is delivered to the vendor; po_status remains sent (transmission does not move status — PO_POST_004 already fired at final approval); on the Carmen side tb_purchase_order.email is populated and the activity log records the transmission event. From this point the soft budget commitment is a vendor-facing liability. |
| VND-HP-02 | Acknowledge acceptance of the PO (entered by Purchaser as a comment) | PO at po_status = sent; vendor confirms acceptance of price, quantity, delivery date, and payment terms through the original transmission channel |
1. Vendor replies (email / portal / phone) confirming acceptance. 2. Purchaser opens the PO detail and adds a comment to tb_purchase_order_comment recording the acknowledgement, the confirmation date, and the vendor's reference. (Where a vendor portal is configured, the portal callback writes the same comment automatically.) |
A comment row is appended to tb_purchase_order_comment with type system (or user if entered manually by the Purchaser), the confirmation date, and the vendor's reference; po_status stays sent; no cancelled_qty or quantity adjustment is written; the activity log shows the acknowledgement event. The PO is now considered "confirmed" by the vendor for fulfilment monitoring. |
| VND-HP-03 | Full shipment on the agreed delivery date (Receiver records matching GRN) | PO at po_status = sent with line L1 : order_qty = 10, received_qty = 0, cancelled_qty = 0; vendor dispatches 10 units against the agreed delivery_date |
1. Vendor's logistics partner delivers the full 10 units to the receiving location with a delivery note referencing po_no. 2. Receiver opens the PO in good-receive-note and posts a GRN of qty 10 against L1. |
GRN posting flips po_status from sent → completed via PO_POST_007 because received_qty + cancelled_qty = order_qty on every line (L1: 10 + 0 = 10); tb_purchase_order_detail.received_qty = 10 on L1; inventory on-hand is incremented in the inventory module per PO_XMOD_008; AP module raises the inventory-accrual liability per PO_XMOD_007 against the GRN; the PO reaches the terminal receipt state completed. |
| VND-HP-04 | Partial shipment then second delivery (Receiver records partial then full) | PO at po_status = sent with L1 : order_qty = 10; vendor can supply 6 today and 4 next week |
1. Vendor ships 6 units with the delivery note marked partial. 2. Receiver posts GRN #1 of qty 6 against L1. 3. Vendor ships the remaining 4 units a week later. 4. Receiver posts GRN #2 of qty 4 against L1. |
After GRN #1: received_qty = 6, po_status flips sent → partial per PO_POST_006 because received_qty < order_qty − cancelled_qty on L1; after GRN #2: received_qty = 10, po_status flips partial → completed per PO_POST_007 because every line satisfies received_qty + cancelled_qty = order_qty; both GRN events appear on the activity log; AP-side accruals are raised per-GRN under PO_XMOD_007; inventory increments twice. |
| VND-HP-05 | Issue invoice matching the PO and the GRN (Finance three-way match passes) | PO at po_status = completed (or partial for a partial invoice); GRN(s) recorded as in VND-HP-03 / VND-HP-04; vendor's AP invoice references po_no and the delivered quantities |
1. Vendor sends the AP invoice (paper / PDF / EDI). 2. Finance persona captures the invoice referencing the PO and the linked GRN(s). 3. Finance runs three-way match (PO ↔ GRN ↔ invoice) against the canonical match rule (PO_POST_008 + PO_XMOD_007). 4. On match success Finance posts the AP liability. |
Three-way match succeeds — Σ invoice.qty equals Σ GRN.received_qty on each line and Σ invoice.amount equals Σ matched (qty × price) within tenant tolerance; AP-side inventory-accrual liability raised at GRN posting is cleared and replaced by the vendor invoice liability per PO_XMOD_007; po_status is not changed by the match (the PO status is set by GRN posting, not by the match); the invoice is posted and the vendor enters the payment cycle. |
| # | Scenario | Expected behaviour |
|---|---|---|
| VND-PERM-01 | Vendor is an external party with no system login; in-system permissions are N/A. All vendor responses are recorded by internal personas (Purchaser / Receiver / Finance). | N/A — there is no Carmen login for the vendor and no UI surface to test. Every effect of a vendor action enters the system through the Purchaser (acknowledgement / amendment comments on tb_purchase_order_comment), the Receiver (GRN postings driving PO_POST_006 / PO_POST_007), or the Finance persona (invoice capture and three-way match per PO_POST_008 / PO_XMOD_007). Where a vendor portal is configured, the portal acts as the vendor's authenticated channel into Carmen but is a separate concern from the PO module's RBAC matrix (PO_AUTH_001–PO_AUTH_011). |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| VND-VAL-01 | Invoice received for a non-existent PO | Finance attempts to capture a vendor invoice that references po_no not present in tb_purchase_order (typo or fabricated) |
Reject at invoice capture — server returns "Referenced PO not found. Please verify the PO number with the vendor." No invoice row is written; no three-way match is attempted; an audit entry is recorded for the failed capture. Finance contacts the vendor via the Purchaser to clarify the correct po_no. |
| VND-VAL-02 | Invoice received before the PO was transmitted (order-of-operations violation) | Vendor issues an invoice while the PO is still at po_status ∈ {draft, in_progress} (the Send to Vendor transmission has not yet fired) |
Reject at invoice capture per the cross-module precondition that the PO must be at po_status ∈ {sent, partial, completed} for any vendor-side document to attach. Server returns "Invoice cannot be matched — PO has not been transmitted to the vendor." This catches premature invoicing where the vendor pre-empted the PO or used the wrong po_no for a different document. |
| VND-VAL-03 | Invoice quantity exceeds the recorded GRN quantity (flagged for three-way match) | Vendor invoices 12 units against L1 whose received_qty = 10 (vendor over-billed); Finance captures the invoice and runs three-way match per PO_POST_008 and PO_XMOD_007 |
Three-way match fails — Σ invoice.qty (12) > Σ GRN.received_qty (10) on L1. Match result is recorded as failed with reason "Invoice qty exceeds matched GRN qty on line L1 (12 > 10)"; AP-accrual liability is not cleared; the invoice is parked in a match-exception queue; Finance escalates to the Purchaser to confirm whether the excess 2 units constitute (a) a vendor error to be credit-noted, (b) a missed GRN to be posted, or (c) an agreed over-receipt within tenant tolerance per PO_XMOD_004. The PO status is not changed by the match failure. |
| VND-VAL-04 | Invoice received for an already-completed PO that has been fully invoiced |
Vendor re-sends an invoice (duplicate or error) against a PO at po_status = completed whose matched invoice has already been posted |
Reject at invoice capture — server returns "PO is already fully invoiced and matched. Reject the duplicate or raise as a credit / debit memo." The duplicate-invoice guard checks the AP module's prior-match registry for the po_no and the line-level invoiced totals. The activity log records the duplicate-capture attempt; Finance notifies the Purchaser, who confirms the rejection with the vendor through the original transmission channel. |
| VND-VAL-05 | Wrong-currency invoice versus the PO's base currency (FX adjustment) | PO at currency_id = USD, exchange_rate = 35.50 against base currency THB; vendor issues the invoice in EUR (different from both the transaction and base currency on the PO) |
Reject by default — server returns "Invoice currency (EUR) does not match the PO transaction currency (USD)." The three-way match operates in the PO's transaction currency; cross-currency invoices require an FX adjustment captured at invoice time, which is governed by PO_CALC_005 (FX rounding to base currency) and PO_CALC_009 (header rolls maintained in both transaction and base currency). Where tenant policy permits a single-step FX conversion, the invoice is captured with an explicit exchange_rate_invoice value and the match is re-run against the converted amount; otherwise Finance returns the invoice to the vendor for re-issue in the PO's currency. |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| VND-EDGE-01 | Vendor declines the PO after acknowledgement (bounce-back via Purchaser) | PO at po_status = sent; vendor initially acknowledged (VND-HP-02) then a stock-out / price-disagreement forces a refusal |
Purchaser logs the refusal in tb_purchase_order_comment (type system) and either re-routes for amendment within the post-sent restrictions of PO_VAL_016 (only cancelled_qty and per-line notes may be mutated) or escalates to the Procurement Manager for a void under PO_AUTH_007 / PO_POST_010. If voided, po_status transitions sent → voided and the soft commitment is reversed; vendor-side notification is sent back through the original channel where supported. The vendor's prior acknowledgement comment is preserved for audit. |
| VND-EDGE-02 | Vendor partial-ships wrong items (Receiver records discrepancy) | PO line L1 : order_qty = 10 (product P1); vendor delivers 8 units of P1 and 2 units of an unrelated product P_wrong (substitution / packing error) |
Receiver opens the GRN against L1 and records 8 received against L1 (flipping po_status → partial per PO_POST_006) plus a variance comment in tb_purchase_order_comment referencing P_wrong and the unrelated 2 units. The wrong-item delivery does not post to inventory under L1; the Purchaser is notified to initiate a return / replacement / credit note loop with the vendor through the amendment surface. Any agreed write-off of the remaining 2 units of P1 is written to cancelled_qty on L1 (closing the line at partial → closed under PO_POST_011 when there is no further fulfilment), and the wrong items are returned to the vendor on a separate document. |
| VND-EDGE-03 | Vendor goes out of business mid-PO (Procurement Manager voids per PO_AUTH_007) |
PO at po_status = partial with received_qty = 6 on L1 : order_qty = 10; vendor ceases trading before delivering the balance of 4 units |
Procurement Manager opens the PO detail and executes Void under PO_AUTH_007 (the action the Purchaser is denied per PUR-PERM-07). po_status transitions partial → voided via PO_POST_010; is_active = false; the 6 units already received are preserved on tb_purchase_order_detail.received_qty and their AP-accrual liability remains under AP's responsibility per PO_XMOD_007; the unfulfilled 4 units are reversed from the soft commitment; an audit comment captures the reason "Vendor out of business". Where many POs are affected, the Manager may use the bulk-void path (covered as PM-HP-09 in 04-test-scenarios-procurement-manager.md). |
| VND-EDGE-04 | Vendor confirms the wrong quantity in the initial acknowledgement (correction via Purchaser amendment) | Vendor acknowledges in VND-HP-02 but states they can supply only 8 units against L1 : order_qty = 10; PO at po_status = sent, received_qty = 0 on L1 |
Per PO_VAL_016, only cancelled_qty and per-line notes are mutable post-sent. The Purchaser executes the amendment loop (covered as PUR-HP-05 in 04-test-scenarios-purchaser.md): sets cancelled_qty = 2 on L1 so received_qty + cancelled_qty = 0 + 2 = 2 and the effective open qty becomes order_qty − received_qty − cancelled_qty = 10 − 0 − 2 = 8; records a comment in tb_purchase_order_comment with the vendor's reference; po_status stays sent. Downstream GRN against L1 may now receive up to 8. The Purchaser need not void the PO — the amendment surface is sufficient for a quantity-down correction acknowledged by the vendor. |
X-PO-01 (full happy path from PR through transmission to receipt), X-PO-05 (amendment cycle on Sent PO), and the three-way-match scenarios that integrate the GRN and Finance personas.sent, partial, completed, voided).tb_purchase_order_comment, and runs the amendment loop on the vendor's behalf (relevant for VND-EDGE-04 quantity-down correction).PO_AUTH_007 void authority used in VND-EDGE-03 (vendor out of business) and PO_AUTH_008 early-close used when vendor cannot supply the remainder of a partial PO.po_status from sent → partial → completed per PO_POST_006 / PO_POST_007 in response to vendor deliveries (VND-HP-03, VND-HP-04, VND-EDGE-02).PO_POST_008 / PO_XMOD_007 referenced by VND-HP-05 and VND-VAL-03, VND-VAL-04, VND-VAL-05.PO_XMOD_003 (GRN may only be created against po_status ∈ {sent, partial}), PO_XMOD_004 (over-receipt tolerance referenced in VND-VAL-03), PO_XMOD_007 (three-way-match interaction with AP-accrual liability); Section 3 — PO_CALC_005 and PO_CALC_009 (FX handling referenced in VND-VAL-05); Section 5 — PO_POST_004 (transmit on final approval), PO_POST_006 / PO_POST_007 (GRN-driven receipt transitions), PO_POST_008 (three-way match), PO_POST_010 (void from non-terminal state), PO_POST_011 (early-close).../carmen-inventory-frontend-e2e/tests/401-po.spec.ts — shared / mixed-persona spec; there is no dedicated vendor E2E because the vendor has no Carmen login. Vendor-driven events are exercised indirectly through the Purchaser send-to-vendor flow (TC-PO-030001..TC-PO-030004 send to vendor), the GRN-sync scenarios (TC-PO-340001..TC-PO-340003), the change-order / cancel paths (TC-PO-040001..TC-PO-050004), and the three-way-match negatives in the backend / calc bracket (TC-PO-310001..TC-PO-340003).