At a Glance
Persona: Receiver (Store Keeper / Receiving Clerk + Inventory Manager) · Module: good-receive-note · Scenarios: ~31
Categories: Happy Path · Permission · Validation · Edge Case
E2E coverage: maps to501-grn.spec.tsin../carmen-inventory-frontend-e2e/
This page captures the test scenarios that the Receiver persona (the Store Keeper / Receiving Clerk at the dock plus the Store Manager / Inventory Manager who owns the irrevocable saved → committed post) directly drives in the good-receive-note module. The Receiver's involvement begins when a PO arrives at po_status ∈ {sent, partial} (or, for doc_type = manual, when an ad-hoc receipt occurs without an upstream PO) and ends when the GRN leaves the receiver zone — either through committed (hand-off to Finance for the three-way match), voided from draft / saved (no inventory / GL impact), or a flagged variance handed off to the Purchaser (03-user-flow-receiver.md Section 4). Scenarios are grouped into happy paths (the four-state walk from draft → saved → committed, manual GRN, partial GRN, batch commit, lot/expiry capture, and quality-issue accept-with-variance), RBAC (Store Keeper vs Inventory Manager scope, segregation of duties enforced by PO_AUTH_010 carrying over from the upstream PO, and the elevated co-authorisation required for post-commit reversal), validation (negative tests against GRN_VAL_001–GRN_VAL_014 that the Receiver can trigger at save or commit time), and a small set of edge cases around tolerance boundaries, decimal precision on lot quantities, concurrent posts on the same PO, the end-of-period auto-commit sweep catching stale saved GRNs, and multi-line mixed-quality outcomes. Cross-persona handoffs that pivot off the Receiver (Scenarios 1, 2, 3, 4, 7, 9, 10 in the parent overview) live in 04-test-scenarios.md, not here.
| # | Scenario | Pre-condition | Steps | Expected |
|---|---|---|---|---|
| RCV-HP-01 | Create GRN against a single-line sent PO — draft row written |
Source PO PO-X at po_status = sent, line L1: order_qty = 10, received_qty = 0, cancelled_qty = 0; vendor / currency / exchange rate snapshot present on the PO; Store Keeper purchase@blueledgers.com logged in with create-GRN permission; Store Keeper is NOT the PO buyer / transmitter (PO_AUTH_010 satisfied). |
1. Navigate to /procurement/goods-receive-note → click New GRN. 2. Pick the vendor → pick PO-X. 3. Verify GRN header inherits vendor_id, currency_id, exchange_rate from the PO snapshot. 4. Verify line L1 is pre-populated with pending_qty = 10 as the default editable received_qty. 5. Stop without entering any data and back out. |
tb_good_received_note row is persisted at doc_status = draft; doc_type = purchase_order; header fields populated from the PO snapshot; one tb_good_received_note_detail row is pre-staged referencing purchase_order_detail_id = L1; no tb_inventory_transaction row written; no GL or stock impact; GRN appears in the list filtered by draft and is editable by the Store Keeper. Maps to TC-GRN-030001. |
| RCV-HP-02 | Save GRN for review — draft → saved with all line rules passing |
RCV-HP-01 state plus: Receiver has entered received_qty = 10, accepted_qty = 10 on L1, valid received_unit_id and received_unit_conversion_factor > 0, non-negative tax / discount / unit price. |
1. Open the draft GRN detail. 2. Confirm received_qty = 10, accepted_qty = 10. 3. Confirm UoM and conversion factor populated. 4. Click Save (save-for-review). |
All save-time rules pass (GRN_VAL_001 warn-only, GRN_VAL_002, GRN_VAL_003, GRN_VAL_004 with PO ref present since doc_type = purchase_order, GRN_VAL_006, GRN_VAL_007, GRN_VAL_008, GRN_VAL_010, and the line-side GRN_VAL_009 on pending qty); doc_status = draft → saved; document becomes visible to the Inventory Manager / Finance for review while remaining editable by the Receiver; still no inventory / GL impact; activity log records { doc_status: 'saved', action: 'save' }. Maps to the Save step of TC-GRN-030001 / TC-GRN-070001. |
| RCV-HP-03 | Inventory Manager commits GRN — saved → committed with inventory, cost-layer, and PO line effects |
RCV-HP-02 state; Inventory Manager logged in (separate user from PO transmitter and GRN creator); extra-cost allocation, if any extra cost is present, is complete (GRN_VAL_014); product is inventory-type (GRN_VAL_012 applies) and lot data is recorded on the linked tb_inventory_transaction_detail. |
1. Open the saved GRN. 2. Review the variance summary (no short, no over, no quality reject). 3. Click Commit. |
All commit-time rules pass (GRN_VAL_001 block, GRN_VAL_002, GRN_VAL_003, GRN_VAL_004, GRN_VAL_005 invoice-uniqueness, GRN_VAL_011 at-least-one-detail-item, GRN_VAL_012 lot info present on linked inventory transaction, GRN_VAL_013 source PO at sent / partial, GRN_VAL_014 extra-cost allocation done); doc_status = saved → committed; one tb_inventory_transaction row is written per line with a child tb_inventory_transaction_detail carrying lot_no and (if perishable) expiry_date; InventoryStatus.QuantityOnHand incremented by accepted_qty; LastUnitCost and TotalCost refreshed; new FIFO layer / AverageCostTracking recomputed; tb_purchase_order_detail.received_qty advances 0 → 10 on the source line; po_status = sent → completed (since received_qty + cancelled_qty ≥ order_qty); AP accrual raised; GRN locked against further edits. Maps to TC-GRN-110001. |
| RCV-HP-04 | Partial GRN — first half today, remainder later (PO drives sent → partial) |
Source PO PO-Y at po_status = sent, line L1: order_qty = 10, received_qty = 0, cancelled_qty = 0; vendor delivers only 6 of 10 today; tenant permits partial receipt. |
1. Create GRN against PO-Y. 2. Enter received_qty = 6, accepted_qty = 6. 3. Save → review variance (short by 4) → Inventory Manager commits. 4. When vendor delivers the remaining 4 in a follow-up shipment, repeat against the same PO: pending qty pre-populates as 4, enter received_qty = 4, accepted_qty = 4, save and commit. |
Both GRNs persist as separate committed rows against the same PO line; after the first commit, tb_purchase_order_detail.received_qty = 0 → 6, pending = 4, po_status = sent → partial per the upstream module; after the second commit, received_qty = 6 → 10, pending = 0, po_status = partial → completed; inventory on-hand rises by accepted_qty on each commit (6 + 4 = 10); two FIFO layers (or two AverageCostTracking updates) recorded; activity log on the PO shows sent → partial → completed. Maps to TC-GRN-110004 (partial received GRN). |
| RCV-HP-05 | Batch commit at end of shift — N saved GRNs commit in one transaction window |
N = 5 GRNs from the shift sit in doc_status = saved; each independently passes line-level rules; Inventory Manager logged in with batch-commit permission; extra-cost allocation complete on each. |
1. Open the batch-commit screen. 2. Select all 5 saved GRNs. 3. Review the batch summary. 4. Click Commit batch. |
Each GRN in the batch is evaluated against the same commit-time rule set (GRN_VAL_011–GRN_VAL_014); all five transition saved → committed; one tb_inventory_transaction per line per GRN; InventoryStatus.QuantityOnHand incremented per accepted qty; each respective source PO's tb_purchase_order_detail.received_qty advances and po_status may flip; if one GRN in the batch fails (e.g. its PO went voided between save and batch commit), only that GRN rolls back — the other four still commit in the same transaction window; activity log records a single batch event per GRN. Maps to TC-GRN-180001 (Bulk Approval). |
| RCV-HP-06 | Manual GRN — no upstream PO, doc_type = manual |
Tenant permits manual GRN; vendor V-Z active; no PO exists for this receipt (emergency / sample / no-PO supply). |
1. Navigate to GRN list → New GRN → choose Manual. 2. Enter vendor, currency, exchange rate, receipt date manually. 3. Add a line: product (from catalog), location, received_qty, accepted_qty, receiving UoM, conversion factor, unit price. 4. Save (draft → saved). 5. Inventory Manager commits. |
GRN persists at doc_type = manual; no line carries purchase_order_detail_id (GRN_VAL_004 second clause satisfied); save-time and commit-time rules apply identically except GRN_VAL_009 (PO-pending check) and GRN_VAL_013 (PO status check) are inapplicable; on commit, inventory transaction written, on-hand incremented, cost layer created, AP accrual raised; no PO line counter advance and no po_status flip (no PO to advance). Maps to TC-GRN-050001. |
| RCV-HP-07 | Record lot number and expiry via the inventory-transaction linkage | RCV-HP-02 state, but the product is lot-tracked and flagged perishable; receiver has the physical lot label / expiry date from the vendor delivery note. | 1. Open the saved GRN line. 2. Open the lot / expiry sub-form on the line (which writes to tb_inventory_transaction_detail, NOT to a column on the GRN line). 3. Accept the auto-generated lot_no or override manually. 4. Enter expiry_date. 5. For multi-lot deliveries, add additional tb_inventory_transaction_detail rows summing to received_qty. 6. Save the lot data. 7. Inventory Manager commits. |
Lot data is persisted on tb_inventory_transaction_detail (linked via tb_good_received_note_detail_item.inventory_transaction_id), NOT as columns on the GRN detail row; GRN_VAL_012 passes at commit because the linked inventory transaction has non-null lot_no and expiry_date for the perishable product; the recall trace from lot number back to this GRN works post-commit (Scenario 8 of the parent overview). Maps to TC-GRN-140001 (stock movement view post-commit). |
| RCV-HP-08 | Quality issue at receiving — accepted_qty < received_qty accepted with variance |
Source PO PO-Q at po_status = sent, line L1: order_qty = 10, received_qty = 0; vendor delivers 10 but 2 fail dock inspection (damaged cartons). |
1. Create GRN against PO-Q. 2. Enter received_qty = 10, accepted_qty = 8 on L1. 3. Write a variance comment ("2 units damaged on arrival — staged for vendor return"). 4. Attach photo evidence. 5. Save → Inventory Manager commits. |
GRN posts both values; accepted_qty ≤ received_qty enforced (GRN_VAL_007 satisfied: received_qty > 0); inventory on-hand rises by accepted_qty = 8 only on commit; the variance received_qty − accepted_qty = 2 is the quality-rejection quantity tracked on the GRN line and fed to Purchaser for vendor-side return / credit-note resolution; PO received_qty = 0 → 10 advances per the upstream module (line counted as physically received), so po_status = sent → completed; the PO does NOT auto-correct for the 2-unit quality variance — resolution lives on the vendor's response document. Maps to TC-GRN-080001 (Edit Line Item) variant. |
| RCV-HP-09 | Pre-commit void — Receiver / Inventory Manager voids a saved GRN with a reason |
A saved GRN exists from an earlier shift; on review, the entire delivery is rejected on quality grounds (wrong vendor packaging, wrong product, contamination). |
1. Open the saved GRN. 2. Click Void. 3. Enter the required reason text ("entire delivery rejected — vendor packaging contamination"). 4. Confirm in the void dialog. |
doc_status = saved → voided; no inventory transaction written; no PO line counter advance; no AP accrual; PO remains at sent (unchanged); reason text persisted on the GRN; activity log records { doc_status: 'voided', action: 'void' }; the document is terminal; Receiver may raise a fresh GRN later if a replacement delivery arrives. Maps to TC-GRN-120001 (Void GRN — Happy Path). |
| # | Scenario | Expected behaviour (allow/deny + reason) |
|---|---|---|
| RCV-PERM-01 | Store Keeper creates GRN at doc_status = draft and saves to saved |
Allow. Store Keeper owns the editable draft → saved transition. New GRN, line entry, lot data entry, attachment upload, and Save are all enabled on a non-committed GRN. The Inventory Manager and Finance see the document in saved for review but it remains editable by the Store Keeper as owner. Maps to the create / save half of TC-GRN-030001 / TC-GRN-070001. |
| RCV-PERM-02 | Inventory Manager commits a saved GRN (saved → committed) |
Allow. Inventory Manager owns the irrevocable post. The Commit button is enabled on saved rows; all GRN_VAL_011–GRN_VAL_014 evaluations run server-side in one transaction; on success the inventory write, cost-layer write, PO received_qty advance, and AP accrual all execute atomically. Batch commit (RCV-HP-05) shares the same authority. |
| RCV-PERM-03 | Store Keeper attempts to commit a saved GRN |
Deny — Inventory Manager required. The Commit button is hidden / disabled for the Store Keeper role; a direct API call returns "Commit from status saved requires the Inventory Manager role."; no inventory or PO effects; the document stays at saved. The Store Keeper hands off to the Inventory Manager (or, if the Store Keeper and Inventory Manager are the same physical user with both roles, the user must re-authenticate with the IM role context). Maps to TC-GRN-110002 (No Permission to Commit). |
| RCV-PERM-04 | Any user (Store Keeper, Inventory Manager, Receiver) attempts to edit a committed GRN |
Deny — terminal state. Per the lifecycle (Section 4 of 03-user-flow.md), committed is locked against header / line / lot / attachment / comment edits except for the audit-allowed activity-log append. Header and line edit endpoints reject with "GRN at status committed is locked — corrections must be raised via credit note or post-commit reversal."; the UI shows a read-only badge; the user is redirected to either the credit-note flow (Finance) or post-commit reversal (RCV-PERM-06). Maps to TC-GRN-080005 (Edit Line Item in RECEIVED status) and TC-GRN-090003 (delete line in received GRN). |
| RCV-PERM-05 | Segregation of duties — Receiver (GRN poster) ≠ Purchaser (PO buyer / transmitter) | Deny when same user. PO_AUTH_010 carries over from the PO module: the user identified by tb_purchase_order.buyer_id or as last_action_by_id on the sent transition MUST NOT be the same user who creates or commits the GRN against that PO. The GRN create call rejects at the PO-pick step with "You created or transmitted this PO; another user must post the GRN against it."; no tb_good_received_note row is written; PO counters unchanged. Applies equally to Store Keeper draft creation and Inventory Manager commit. |
| RCV-PERM-06 | Post-commit reversal — elevated co-authorisation required | Deny when raised by Inventory Manager alone. Post-commit reversal of a committed GRN (committed → voided) requires co-authorisation by Inventory Manager AND Finance (or System Administrator on audit grounds). The single-user Void action on a committed GRN is blocked with "Post-commit reversal requires co-authorisation from Finance."; only the two-party flow fires the compensating inventory transaction, cost-layer reversal, PO received_qty decrement, and reversing AP entry. The Receiver may then raise a replacement GRN if the physical receipt is to be re-recorded. Maps to TC-GRN-120003 (Void Committed GRN). |
| RCV-PERM-07 | Receiver / Inventory Manager voids a draft or saved GRN with reason |
Allow. Pre-commit void is in scope for the Receiver path (either sub-persona): draft → voided and saved → voided are both permitted with a required reason text and produce no inventory / GL impact (RCV-HP-09). The void button is enabled on draft and saved for the Receiver / Inventory Manager roles and disabled on committed. |
| # | Scenario | Trigger | Expected error |
|---|---|---|---|
| RCV-VAL-01 | Missing vendor at commit (GRN_VAL_001) |
GRN at doc_status = saved with vendor_id IS NULL (left blank during multi-PO consolidation that never resolved a vendor); Inventory Manager clicks Commit. |
Reject at commit — GRN_VAL_001. Server returns "Vendor is required and must be from the approved vendor list." Save-time is warn-only; commit blocks. doc_status stays saved; no inventory write; no PO effect. Maps to TC-GRN-030003 (Create GRN with No Vendor) and TC-GRN-050003 (Manual GRN with Missing Vendor). |
| RCV-VAL-02 | Missing PO reference when doc_type = purchase_order (GRN_VAL_004) |
GRN created via the PO-anchored path so doc_type = purchase_order; user manually clears the line's purchase_order_detail_id while editing; tries to save the line. |
Reject at save — GRN_VAL_004 first clause. Server returns "PO reference is required for PO-sourced GRNs and must be absent for manual GRNs." The line cannot be saved without purchase_order_detail_id. Symmetric failure on the manual side: if doc_type = manual but the user tries to inject a purchase_order_detail_id, the save also fails with the same code. Maps to TC-GRN-030004 (Invalid PO). |
| RCV-VAL-03 | received_qty = 0 AND foc_qty = 0 on a line (GRN_VAL_007) |
Receiver adds a line, leaves both received_qty and foc_qty at zero (or null), clicks Save. |
Reject at save — GRN_VAL_007. Server returns "Each line must record either a received quantity or a free-of-charge quantity greater than zero." The line cannot persist with both values empty / zero; the Receiver must either enter a positive received_qty (paid receipt), a positive foc_qty (sample / promo), or delete the empty line. Maps to TC-GRN-070002 (Empty Product Name and qty variant). |
| RCV-VAL-04 | accepted_qty > received_qty on a line |
Receiver enters received_qty = 8, accepted_qty = 10 on a quality-flagged line (typo / mis-entry); clicks Save. |
Reject at save — GRN-side line invariant accepted_qty ≤ received_qty (companion to GRN_VAL_007, enforced as the cross-field check on the same row). Server returns "Accepted quantity cannot exceed received quantity on any GRN line." The line cannot save or commit; the Receiver must correct one of the two values. Mirrors RCV-VAL-04 in 04-test-scenarios-receiver.md of [purchase-order](/en/inventory/purchase-order). |
| RCV-VAL-05 | Missing lot info for lot-tracked / perishable product at commit (GRN_VAL_012) |
saved GRN with a perishable, inventory-type product on line L1; the linked tb_inventory_transaction_detail for L1 has lot_no IS NULL or expiry_date IS NULL; Inventory Manager clicks Commit. |
Reject at commit — GRN_VAL_012. Server returns "Lot information is required for inventory items at commit; line <seq> is missing lot data on the linked inventory transaction." Commit aborts; no inventory transaction is finalised; the Receiver must return to the line, open the lot sub-form (RCV-HP-07), and populate lot_no (auto or manual) and expiry_date on every tb_inventory_transaction_detail row before re-attempting commit. |
| RCV-VAL-06 | Receive against a voided PO (GRN_VAL_013) |
PO PO-V at po_status = voided (voided by Procurement Manager); Receiver attempts to commit a saved GRN whose line references that PO. |
Reject at commit — GRN_VAL_013. Server returns "Cannot receive against PO <po_no>: PO status voided does not permit receiving. Voided POs are rejected outright." Symmetrically blocked for closed, completed, draft, and in_progress POs. The saved GRN remains; the Receiver must void it (RCV-HP-09 / RCV-PERM-07) and raise a replacement against a valid PO. |
| RCV-VAL-07 | Receipt unit invalid / conversion factor ≤ 0 (GRN_VAL_008) |
Receiver enters received_qty = 10 but the received_unit_id was nulled out by a UoM-master fix-up, or received_unit_conversion_factor = 0 after a unit-cleanup script; tries to save. |
Reject at save — GRN_VAL_008. Server returns "Each receipt event must specify a valid receiving UoM with a positive conversion factor." received_base_qty = Round(received_qty × received_unit_conversion_factor, 3) cannot be computed with a zero / null factor; the line is held; the Receiver re-picks a valid UoM from the active list. |
| RCV-VAL-08 | Over-receipt without tolerance enabled (GRN_VAL_009) |
PO line L1: pending qty = 10; tenant over-receipt tolerance disabled (0%); Receiver enters received_qty = 12 on a PO-anchored line. |
Reject at save — GRN_VAL_009. Server returns "Receipt quantity exceeds the pending quantity on PO line <po_no>:<seq>; over-receipt tolerance not enabled." The line is held; the Receiver either caps received_qty at 10 (per the decision branch in 03-user-flow-receiver.md Section 3, "Over delivery — out of tolerance") and refuses the excess at the dock, or escalates to System Administrator to enable / widen the tolerance, then re-saves. |
| RCV-VAL-09 | Duplicate (invoice_no, vendor_id) across non-soft-deleted GRNs (GRN_VAL_005) |
Receiver enters invoice_no = INV-001 on a GRN whose vendor_id already has a non-voided GRN with the same invoice_no (legacy BR-01 invoice-uniqueness rule); Inventory Manager clicks Commit. |
Reject at commit — GRN_VAL_005. Server returns "An invoice with this number has already been received from this vendor." Save is warn-only on this rule but commit blocks; the Receiver / Finance verify whether the duplicate is a re-key (correct the invoice number) or a true duplicate delivery (void this GRN per RCV-PERM-07). |
| # | Scenario | Condition | Expected |
|---|---|---|---|
| RCV-EDGE-01 | Exact tolerance boundary on over-receipt | PO line L1: order_qty = 10, received_qty = 0, cancelled_qty = 0; tenant over-receipt tolerance = 5%; Receiver enters received_qty = 10.5 (exactly at the upper bound), accepted_qty = 10.5. |
Accepted — GRN_VAL_009 second clause (tenant tolerance relaxes the pending-qty cap). received_qty = 10.5 is within tolerance (10 × 1.05 = 10.5); save and commit both succeed; tb_purchase_order_detail.received_qty = 0 → 10.5; line evaluation received_qty (10.5) ≥ order_qty − cancelled_qty (10) → fully received; po_status = sent → completed per the upstream PO_POST_007. Setting received_qty = 10.51 breaches the bound and is rejected per RCV-VAL-08. |
| RCV-EDGE-02 | Decimal precision on lot quantities | Line allows 3-dp precision; Receiver enters received_qty = 5.123, accepted_qty = 4.998; multi-lot capture sums to received_qty on the linked tb_inventory_transaction_detail rows (e.g. lot A = 3.000, lot B = 2.123); conversion factor 1.234567. |
Accepted at storage. received_qty, accepted_qty, and per-lot quantities all stored at the line's UoM precision (3 dp); received_base_qty = Round(5.123 × 1.234567, 3) computed per GRN_VAL_008; per-lot quantities on tb_inventory_transaction_detail must sum exactly to the line's received_qty (no rounding drift carried into the totals); quality variance received_qty − accepted_qty = 0.125; all stored Decimals fit within Decimal(20, 5) for money and Decimal(15, 5) for rates. |
| RCV-EDGE-03 | Concurrent GRN posts on the same PO from two Receiver sessions | Receiver A (Store Keeper) and Receiver B (separate session) both open PO-X (po_status = sent, received_qty = 0, order_qty = 10); A saves and commits received_qty = 6 at T; B saves and commits received_qty = 4 at T + 500ms against the same line, both with doc_version = N snapshots. |
First commit wins atomically. A's GRN commits, tb_purchase_order_detail.received_qty = 0 → 6, po_status = sent → partial, doc_version → N+1. B's commit is reconciled against the new state — either the GRN module re-reads pending_qty = 4 and B's received_qty = 4 commits cleanly (advancing received_qty → 10, po_status → completed), or the optimistic-concurrency guard rejects B with "This PO was modified by another user. Please refresh and re-apply your changes." and B retries against the fresh snapshot. No double-write, no lost update, no received_qty > order_qty overflow outside the over-receipt tolerance, no double-incremented on-hand. |
| RCV-EDGE-04 | End-of-period auto-commit sweep catches stale saved GRNs |
System Administrator has configured a scheduled sweep that commits saved GRNs older than the tenant grace window (e.g. 24h); two GRNs sit in saved past the threshold — one valid, one with a now-voided source PO. |
Sweep runs the same commit-time rule set. The valid GRN: all GRN_VAL_011–GRN_VAL_014 pass → saved → committed by the sweep; inventory write, cost layer, PO advance, AP accrual all fire under a system / scheduled user context; activity log records { doc_status: 'committed', action: 'auto_commit_sweep' }. The GRN with the voided PO: GRN_VAL_013 blocks → sweep skips, logs the failure, routes the document back to the Inventory Manager queue for manual resolution (void per RCV-PERM-07 or re-anchor to a valid PO); no inventory write. Finance picks up the newly committed GRN for matching; sweep itself is auditable via the activity log per audit trail. |
| RCV-EDGE-05 | Multi-line GRN with mixed quality outcomes | PO PO-M at po_status = sent, three lines: L1 (order_qty = 10), L2 (order_qty = 5), L3 (order_qty = 8). Vendor delivers L1 = 10 (all accepted), L2 = 5 (1 unit damaged → accepted_qty = 4), L3 = 8 (3 units expired on arrival → accepted_qty = 5). |
All three lines commit on the same GRN. Per-line entry: L1 received_qty = 10, accepted_qty = 10; L2 received_qty = 5, accepted_qty = 4; L3 received_qty = 8, accepted_qty = 5. Inventory increments: +10 on L1, +4 on L2, +5 on L3 (total +19, not +23). PO line received_qty advances on all three: L1 0 → 10, L2 0 → 5, L3 0 → 8; all lines satisfy received_qty + cancelled_qty ≥ order_qty → po_status = sent → completed per the upstream PO_POST_007. Variance summary on the GRN shows L2 reject = 1, L3 reject = 3; the Purchaser receives the multi-line variance handoff for vendor-side resolution on L2 and L3 (return / credit note / replacement); the PO does NOT auto-correct for the quality variance. |
| RCV-EDGE-06 | Multi-PO consolidation into a single GRN with one vendor / currency / location | Same vendor, same currency, same delivery location across PO-A (one line, pending 4) and PO-B (two lines, pending 6 each); vendor delivers a single truck covering both POs. |
One GRN with three lines spanning two POs. GRN created via the multi-PO path; header inherits the shared vendor_id, currency_id, exchange_rate; three GRN detail rows each carry their respective purchase_order_detail_id. Save runs GRN_VAL_009 per line against each PO's pending balance independently. Commit advances received_qty on three different tb_purchase_order_detail rows in one transaction; PO-A flips sent → completed, PO-B flips sent → completed (or partial if not all lines fully received); inventory transactions written once per GRN line; one AP accrual covers the consolidated delivery. Maps to TC-GRN-040002, TC-GRN-040004. |
draft → saved → committed), the decision branches (short / over within tolerance / over out of tolerance / quality issue / wrong item / partial-then-remainder / batch commit), and the exit handoffs (Finance for three-way match, Purchaser for variance, elevated co-auth for post-commit reversal).GRN_VAL_001 (vendor required at commit), GRN_VAL_002 (currency / exchange rate), GRN_VAL_003 (receipt date), GRN_VAL_004 (doc_type ↔ PO reference symmetry), GRN_VAL_005 (invoice uniqueness), GRN_VAL_006 (product / location required per line), GRN_VAL_007 (received_qty or foc_qty > 0), GRN_VAL_008 (receiving UoM and conversion factor), GRN_VAL_009 (pending-qty cap with tenant tolerance), GRN_VAL_010 (non-negative monetary fields), GRN_VAL_011 (at-least-one-detail-item at commit), GRN_VAL_012 (lot info on linked inventory transaction at commit), GRN_VAL_013 (source PO at sent / partial at commit), GRN_VAL_014 (extra-cost allocation complete at commit).../carmen-inventory-frontend-e2e/tests/501-grn.spec.ts — canonical Playwright spec for the GRN module. Receiver-relevant test groups: TC-GRN-030001..030005 (Create from Single PO — RCV-HP-01, RCV-VAL-01, RCV-VAL-02), TC-GRN-040002..040004 (Create from Multiple POs — RCV-EDGE-06), TC-GRN-050001..050005 (Manual GRN — RCV-HP-06, RCV-VAL-01), TC-GRN-070001..070004 (Add Line Item — RCV-HP-02, RCV-VAL-03), TC-GRN-080001..080005 (Edit Line Item — RCV-HP-08, RCV-PERM-04), TC-GRN-090001..090005 (Delete Line Item — RCV-PERM-04), TC-GRN-110001..110004 (Commit — RCV-HP-03, RCV-HP-04, RCV-PERM-03), TC-GRN-120001..120004 (Void — RCV-HP-09, RCV-PERM-06), TC-GRN-140001..140003 (Stock Movements — RCV-HP-07 trace), TC-GRN-180001..180004 (Bulk Approval — RCV-HP-05). Permission-denial coverage uses the requestor@blueledgers.com fixture; the happy-path Receiver uses purchase@blueledgers.com.tb_purchase_order_detail.received_qty and flips po_status (sent → partial → completed); see the PO module's 04-test-scenarios-receiver.md for the mirrored PO-side test scenarios (RCV-HP-01..06 on the PO side correspond to the post-commit effects of RCV-HP-03..05 here).tb_inventory_transaction carries lot, expiry, and cost-layer data, and InventoryStatus.QuantityOnHand is incremented by accepted_qty (not received_qty).saved → committed transition (RCV-HP-03, RCV-HP-04, RCV-HP-05).