E2E SCENARIOS — INVENTORY
VietGara — Release 7.2 · Version 2.2 — October 19, 2026
Covers FR-INV-01, 02, 03 and 05 (Release 1.3), the goods receipt document, cost and valuation, part suppliers, cost visibility and the stock issue of a repair order (FR-INV-06, 07, 08, 15, 19 and 20, Release 7.0), the stock counts, write-offs, returns to supplier and repair order part returns, with their stock card entries (FR-INV-09 to FR-INV-12, Release 7.1; the count’s Excel export is FR-DOC-02), barcodes and the Excel import of parts, goods receipts and stock counts (FR-INV-13 and FR-INV-14, Release 7.2), and the low-stock badge (the display half of FR-INV-04; the notification is Release 10.1). The part itself (base unit, price, category, brand, images) belongs to the Catalogue module and its scenarios are in 09_Catalogue.md; creating a part here also needs a base unit. Records are in 00_Test_Data.md §3.3. Each scenario creates the parts it uses (OIL-5W30, FILTER-01, and so on), so none depends on the seeded parts of 00_Test_Data.md §3.4.
Blackbox. The API rules of E2E-INV-001 to 029 and E2E-INV-030, 033 to 038 run in the backend’s blackbox suite (vietgara-backend/testing/blackbox/scenarios/inventory), named in each scenario. The scenarios here cover the owner web screens and the behaviour a user sees; the API suite covers the rest.
| ID | Title | Priority | Automation |
|---|---|---|---|
| E2E-INV-001 | Add a part code to the catalogue | P1 | Automated (API, partial) |
| E2E-INV-002 | SKU is unique per garage; part field validation | P2 | Automated (API, partial) |
| E2E-INV-003 | A goods receipt raises stock on hand when it is completed | P1 | Automated (API, partial) |
| E2E-INV-004 | The low-stock badge appears at or below the reorder threshold | P2 | Automated (API, partial) |
| E2E-INV-005 | Search parts by SKU or name | P2 | Automated (API, partial) |
| E2E-INV-006 | Edit and delete a part; a deleted part’s past transactions are kept | P2 | Automated (API, partial) |
| E2E-INV-007 | The stock movement report, filterable by direction and part | P2 | Automated (API, partial) |
| E2E-INV-008 | Closing a repair order with part lines posts its stock issue and deducts stock | P1 | Automated (API, partial) |
| E2E-INV-009 | A part line without a catalogue part is not deducted on close | P3 | Automated (API, partial) |
| E2E-INV-010 | Permissions: view-only, and full access | P1 | Automated |
| E2E-INV-011 | A goods receipt with a supplier on each line; completion is blocked while a line has none | P1 | Automated (API, partial) |
| E2E-INV-012 | Completing a receipt updates stock and the weighted-average cost | P1 | Automated (API) |
| E2E-INV-013 | Cancelling a completed receipt restores stock and cost; refused once the stock is used | P1 | Automated (API) |
| E2E-INV-014 | Part suppliers: the default fills a new line; the last cost is recorded on completion | P2 | Automated (API) |
| E2E-INV-015 | Purchases by supplier and stock valuation, on screen and as Excel | P2 | Automated (API, partial) |
| E2E-INV-016 | A technician sees quantities but no cost | P1 | Automated (API, partial) |
| E2E-INV-017 | A repair order’s stock issue reserves its parts; “issue now” posts it | P1 | Automated (API, partial) |
| E2E-INV-018 | Closing the repair order posts its stock issue once | P1 | Automated (API) |
| E2E-INV-019 | With “stock issue posts: when work starts”, the issue posts when the order starts | P2 | Automated (API) |
| E2E-INV-020 | Count a shelf: matched, over and short lines; completion makes stock equal the count | P1 | Automated (API) |
| E2E-INV-021 | A count typed in a packaging unit; zero is a real count; the system quantity is taken again when typed | P2 | Manual only |
| E2E-INV-022 | Two drafts merge into one; uncounted lines are skipped on completion | P2 | Automated (API) |
| E2E-INV-023 | Cancelling a draft closes it; cancelling a completed count posts its reversal, refused once the stock is used | P1 | Automated (API) |
| E2E-INV-024 | Count Excel export: value columns only with cost access | P2 | Manual only |
| E2E-INV-025 | A write-off reduces stock at average cost; refused beyond on hand; cancelling puts the parts back | P1 | Automated (API) |
| E2E-INV-026 | A return to supplier records the credit; cancelling puts the parts back | P1 | Automated (API) |
| E2E-INV-027 | Return of unused parts from a repair order, after its stock issue is posted | P1 | Automated (API) |
| E2E-INV-028 | Stock card entries of counts, write-offs and returns: number, reason, user and balance | P2 | Automated (API, partial) |
| E2E-INV-029 | Permissions: counting and adjusting are separate from stock write | P1 | Automated (API) |
| E2E-INV-030 | A part has several barcodes, for the base unit or a case; a code is unique in the garage; lookup finds the part and the unit | P1 | Automated (API) |
| E2E-INV-031 | Scan into a goods receipt: a line, one more of the same, cases for a case code; unknown and inactive codes are told | P2 | Automated (web, typed codes; the camera is manual) |
| E2E-INV-032 | Scan into a stock count (web and phone): the part is added and the cursor goes to its counted quantity | P2 | Manual only |
| E2E-INV-033 | Import parts: a 500-row file with 5 bad rows lists all 5 and saves nothing; after fixing, parts, units, barcodes and opening stock appear | P1 | Automated (API and web) |
| E2E-INV-034 | The same file cannot be imported twice; a changed file can; concurrent confirmations import once | P1 | Automated (API) |
| E2E-INV-035 | Import a goods receipt: one draft, lines found by SKU or barcode, a supplier on each line, the header columns must agree | P1 | Automated (API) |
| E2E-INV-036 | Import a stock count: one draft, a case barcode counts in cases, a part twice or a unit that is not the barcode’s is refused | P2 | Automated (API) |
| E2E-INV-037 | Import permissions: by type and by column; the template is downloadable | P1 | Automated (API) |
| E2E-INV-038 | A file that cannot be read, is too large, has too many rows or lacks a column is refused as a whole | P2 | Automated (API) |
E2E-INV-001 · Add a part code to the catalogue
Priority: P1 · Covers: FR-INV-01 · Automation: Automated (API, partial)
Blackbox: TestAddCatalogueItem.
Preconditions: baseline. Signed in as MANAGER_A1 (or OWNER_A), active garage A1.
Test data: unit Litre (L); SKU OIL-5W30, name “Engine oil 5W-30”, base unit Litre, reorder threshold 5.
Steps
- Catalogue → Units → New unit with the unit’s data. Save.
- Catalogue → Parts → New part. Enter the test data. Save.
- Open Inventory → Stock on Hand.
Expected results
- The part is listed in Catalogue → Parts and, in Inventory, with SKU, name and On hand
0 L(no receipt yet), reorder threshold5. - It does not appear when A2 is the active garage.
E2E-INV-002 · SKU is unique per garage; part field validation
Priority: P2 · Covers: FR-INV-01 · Automation: Automated (API, partial)
Blackbox: TestItemSKUUniqueAndValidation.
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 exists (E2E-INV-001).
Test data:
| Case | SKU | Name | Expected |
|---|---|---|---|
| a | OIL-5W30 | Different oil | Refused: another part already uses this SKU |
| b | (empty) | Air filter | SKU required |
| c | FILTER-01 | (empty) | Name required |
| d | FILTER-01 | Air filter | Saved |
Steps
- For each case, Catalogue → Parts → New part with the row’s data and save.
Expected results
- a–c are refused; the form shows “Please check the highlighted information.”
- d is saved. Clean-up: delete it.
- Step 1’s SKU is free again in A2 (each garage has its own catalogue) — not re-tested here, same pattern as E2E-CUS-003/E2E-CUS-006.
E2E-INV-003 · A goods receipt raises stock on hand when it is completed
Priority: P1 · Covers: FR-INV-01, FR-INV-06 · Automation: Automated (API, partial)
Blackbox: TestGoodsReceiptIncreasesStock (receipt of 10 and 5, the stock on hand after each completion).
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30, currently 0 L on hand.
Test data: supplier QA Supplier Phu Tung Sai Gon (supplier1-1-1) on the part’s line.
Steps
- Inventory → Goods receipts → New goods receipt. Enter a receipt date and a delivery note number
DN-01. Add a line:OIL-5W30, quantity10, unit cost150000, supplier QA Supplier Phu Tung Sai Gon. Save as draft. - Check Stock on Hand for
OIL-5W30. - Open the draft and choose Complete receipt.
- Check Stock on Hand again.
- New receipt:
OIL-5W30, quantity5, no unit cost, the same supplier. Complete it.
Expected results
- Step 1: the receipt is listed with a number from the garage’s goods receipt template (
PN-…) and status Draft. Step 2: On hand0 L: a draft changes no stock. - Step 4: On hand
10 L. - Step 5: On hand
15 L. Unit cost is optional per line; a line without it is recorded as “not recorded”. - The stock movements show both receipts as direction In with reference Goods receipt.
E2E-INV-004 · The low-stock badge appears at or below the reorder threshold
Priority: P2 · Covers: FR-INV-03, FR-INV-04 (display) · Automation: Automated (API, partial)
Blackbox: TestLowStockThreshold.
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30, reorder threshold 5, currently 15 L on hand (E2E-INV-003).
Steps
- Catalogue → Parts: edit the part, reorder threshold
20. Save. Look at its row in Inventory. - Edit again: reorder threshold
15. Save. Look at the row.
Expected results
- Step 1 (
15 ≤ 20): the row shows the Low stock badge, and its background is subtly highlighted (not colour alone). - Step 2 (
15 ≤ 15, boundary): still Low stock — the threshold is inclusive. Clean-up: set the threshold back to5.
E2E-INV-005 · Search parts by SKU or name
Priority: P2 · Covers: FR-INV-03 · Automation: Automated (API, partial)
Blackbox: TestSearchItems.
Preconditions: baseline. MANAGER_A1, A1 active. Parts OIL-5W30 “Engine oil 5W-30” and FILTER-01 “Air filter” (E2E-INV-001/002).
Steps
- In Inventory → Stock on Hand, search
OIL, thenEngine, thenFILTER, thennonexistent.
Expected results
OILandEnginefind only the oil;FILTERfinds only the filter (SKU and name both match).nonexistentshows “No parts yet.” or an empty result.
E2E-INV-006 · Edit and delete a part; a deleted part’s past transactions are kept
Priority: P2 · Covers: FR-INV-01, FR-CAT-07 · Automation: Automated (API, partial)
Blackbox: TestEditAndDeleteItem (a part with history is deactivated, its transactions stay).
Preconditions: baseline. MANAGER_A1, A1 active. A fresh part QA Test Part (TEST-PART-01) with one completed goods receipt of 3 units.
Steps
- Catalogue → Parts: edit, rename to QA Test Part Renamed. Save.
- Delete it and confirm.
- Inventory → Stock Movements → look for the earlier receipt.
Expected results
- Step 1 saves; the row shows the new name.
- Step 2: the part has stock history, so the confirmation says it will be deactivated (an unused part would be deleted); it leaves Stock on Hand and the part pickers, and can be reactivated.
- Step 3: the receipt transaction is still listed, with the part’s SKU and name as they were.
E2E-INV-007 · The stock movement report, filterable by direction and part
Priority: P2 · Covers: FR-INV-05 · Automation: Automated (API, partial)
Blackbox: TestStockMovementReportFilters.
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with at least one In movement (E2E-INV-003) and one Out movement (E2E-INV-008 provides one; run that scenario first, or accept In-only coverage).
Steps
- Inventory → Stock Movements tab, no filter.
- Filter direction = In, then Out.
Expected results
- No filter: every movement of A1’s parts, newest first, with part, direction, quantity, reference (Goods receipt, or Stock issue for a repair order) and date.
- In shows only receipts; Out shows only issues.
E2E-INV-008 · Closing a repair order with part lines posts its stock issue and deducts stock
Priority: P1 · Covers: FR-INV-02, FR-INV-20 · Automation: Automated (API, partial)
Blackbox: TestCloseDeductsStock, TestClosingTheOrderPostsTheStockIssue (E2E-INV-018).
Preconditions: baseline. ADVISOR_A1/TECH_A1A2, A1 active. Part OIL-5W30 with 10 L on hand (E2E-INV-003, adjusted as needed). A new ad-hoc repair order for any A1 vehicle with a Part line: description “Engine oil 5W-30”, quantity 4, part OIL-5W30 picked from the line’s part search.
Steps
- Open the repair order. Inventory → Stock on Hand shows
OIL-5W30with4 Lreserved and Available6 L; on hand is still10 L. - Complete every line of the repair order (E2E-REP-006), then Close repair order (E2E-REP-008).
- Immediately open Inventory → Stock on Hand for
OIL-5W30. - Open Stock Movements.
Expected results
- Step 1: the reservation shows before any stock moves.
- Within a couple of seconds of closing (the outbox relay’s interval),
OIL-5W30’s on-hand quantity drops by exactly4(10 L→6 L), with no page action needed beyond a refresh; the reserved quantity returns to0 L. - A new Out movement appears, quantity
4, reference Stock issue with the issue’s number, and a cost. No manual stock action taken by anyone. - This is the one place where closing a repair order (Repair Order module) and stock on hand (Inventory module) cross — confirm both screens agree.
E2E-INV-009 · A part line without a catalogue part is not deducted on close
Priority: P3 · Covers: FR-INV-02 · Automation: Automated (API, partial)
Blackbox: TestCloseWithoutCatalogueItemDeductsNothing.
Preconditions: baseline. ADVISOR_A1, A1 active. A repair order with a Part line that has no catalogue part picked (free-text description only, e.g. E2E-REP-001’s “Brake pad set”).
Steps
- Complete every line and close the repair order.
- Check Stock Movements for any new movement referencing this order.
Expected results
- Closing succeeds normally (a part not carried in stock must not block closing).
- No stock movement is created for that line — nothing to deduct without a catalogue part. The order’s stock issue has no line for it.
E2E-INV-010 · Permissions: view-only, and full access
Priority: P1 · Covers: FR-IAM-03, FR-INV-01, FR-INV-02 · Automation: Automated
Blackbox: TestInventoryPermissionsNoAccess, TestInventoryPermissionsReadWrite, TestInventoryPermissionsOwner.
Preconditions: baseline. A1 active. Part OIL-5W30 exists.
Test data: VIEWER_A1 (no inventory access at all), TECH_A1A2 (inventory view only, per the Technician role), MANAGER_A1 (everything).
Steps
- VIEWER_A1: look at the sidebar; type
/inventorydirectly. - TECH_A1A2: open Inventory and Catalogue; look for New goods receipt, Receive stock and New part.
- MANAGER_A1: add a part and create and complete a goods receipt on it.
Expected results
- VIEWER_A1 has no Inventory entry in the sidebar;
/inventoryshows no data or “You are not allowed to do this.” - TECH_A1A2 sees the stock levels and the catalogue (the default Technician role views both) but no New goods receipt action and no New part button.
- MANAGER_A1 can fully manage parts, receipts and stock.
E2E-INV-011 · A goods receipt with a supplier on each line; completion is blocked while a line has none
Priority: P1 · Covers: FR-INV-06, FR-INV-19 · Automation: Automated (API, partial)
Blackbox: TestGoodsReceiptSeveralSuppliers (the header default is inherited, a line can override it, a line without a supplier blocks completion, a completed receipt cannot be edited).
Preconditions: baseline. MANAGER_A1, A1 active. Parts OIL-5W30 and FILTER-01 (E2E-INV-001/002), suppliers QA Supplier Phu Tung Sai Gon (supplier1-1-1) and QA Supplier Lop Xe Viet (supplier1-1-2).
Test data: delivery note DN-77; lines: OIL-5W30 × 10 at 100, FILTER-01 × 4 at 2500, and a third line OIL-5W30 × 1 with no supplier.
Steps
- Inventory → Goods receipts → New goods receipt. Set the header supplier to QA Supplier Phu Tung Sai Gon, the delivery note to
DN-77and the notes to “morning delivery”. Add the first two lines: the first inherits the header supplier, and on the second choose QA Supplier Lop Xe Viet. Save as draft. - Add the third line and clear its supplier. Choose Complete receipt.
- Pick a supplier on the third line, then Complete receipt again.
- Open the completed receipt and try to change its notes.
Expected results
- Step 1: the draft shows the running total of its lines; each line shows its supplier, the first one inherited.
- Step 2: completion is refused with “Every line needs a supplier before the receipt can be completed.”; the draft stays a draft and stock is unchanged.
- Step 3: the receipt is Completed, and the stock and cost of each part are updated (E2E-INV-012).
- Step 4: the receipt cannot be edited: its fields are read-only.
- The receipt’s Print or Export PDF action produces the goods receipt PDF with each line’s supplier.
E2E-INV-012 · Completing a receipt updates stock and the weighted-average cost
Priority: P1 · Covers: FR-INV-07 · Automation: Automated (API)
Blackbox: TestGoodsReceiptSeveralSuppliers (average 100, then 150 after a second receipt of 10 at 200), TestPurchasesAndValuationReports.
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with no stock.
Steps
- Complete a receipt:
OIL-5W30,10at100per litre, with a supplier. - Open Stock on Hand for
OIL-5W30and read Average cost and Stock value. - Complete a second receipt:
OIL-5W30,10at200. - Read the same fields again.
Expected results
- Step 2: On hand
10 L, Average cost 100, Stock value 1,000. - Step 4: On hand
20 L, Average cost 150 ((10 × 100 + 10 × 200) ÷ 20), Stock value 3,000. - The movements show each receipt with its own unit cost, unchanged by the later receipt.
E2E-INV-013 · Cancelling a completed receipt restores stock and cost; refused once the stock is used
Priority: P1 · Covers: FR-INV-06 · Automation: Automated (API)
Blackbox: TestGoodsReceiptSeveralSuppliers (a cancelled receipt restores the previous stock and average; a second cancel is refused with GOODS_RECEIPT_ALREADY_CANCELLED), TestCancelReceiptWithUsedStockIsRefused (INSUFFICIENT_STOCK_TO_REVERSE).
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with two completed receipts: R1 of 10 at 100, then R2 of 10 at 200 (E2E-INV-012). On hand 20 L, average 150.
Steps
- Open R2 and choose Reverse receipt. Read the confirmation, then confirm.
- Open Stock on Hand for
OIL-5W30. - Create a repair order with
OIL-5W30×3; complete and close it (E2E-INV-008). Check Stock on Hand again. - Open R1 and choose Reverse receipt, then confirm.
Expected results
- Step 1: the confirmation says the stock and cost are taken back, and that the reversal is refused when the stock has been used. R2 is Cancelled, and its movements show as reversals.
- Step 2: On hand
10 L, Average cost 100. - Step 3: On hand
7 L. - Step 4: the reversal is refused with “Part of this delivery has already been used, so the receipt cannot be reversed.” Nothing changes: On hand
7 L, R1 stays Completed. - Cancelling an already cancelled receipt is not offered.
E2E-INV-014 · Part suppliers: the default fills a new line; the last cost is recorded on completion
Priority: P2 · Covers: FR-INV-19 · Automation: Automated (API)
Blackbox: TestPartSuppliersDefaultAndLastCost (the default comes first, a line without a supplier takes it, the last cost is recorded, two defaults are refused).
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30; suppliers supplier1-1-1 and supplier1-1-2.
Steps
- Open Catalogue → Parts → OIL-5W30 → Suppliers. Add QA Supplier Lop Xe Viet with code
B-1, then add QA Supplier Phu Tung Sai Gon with codeA-1and mark it Default. Save. - Inventory → Goods receipts → New goods receipt; add
OIL-5W30×10at120without choosing a supplier. Save, then complete. - Open the part’s Suppliers again.
- Try to mark the second supplier as Default as well, and save.
Expected results
- Step 1: the default is listed first.
- Step 2: the new line is filled with QA Supplier Phu Tung Sai Gon.
- Step 3: Last cost paid for QA Supplier Phu Tung Sai Gon is
120per litre; the other supplier shows no last cost (“never paid”). - Step 4: refused; only one supplier can be the default.
E2E-INV-015 · Purchases by supplier and stock valuation, on screen and as Excel
Priority: P2 · Covers: FR-INV-07, FR-INV-19, FR-DOC-02 · Automation: Automated (API, partial)
Blackbox: TestPurchasesAndValuationReports (totals, the supplier filter, an empty future period, the valuation at a date with no stock). The Excel workbooks are checked by hand.
Preconditions: baseline. MANAGER_A1, A1 active. E2E-INV-011 and E2E-INV-012 done: two suppliers have completed receipts.
Steps
- Inventory → Purchases. Read the total for the whole period, then filter by QA Supplier Lop Xe Viet.
- Set From to a date after today and read the list.
- Inventory → Stock valuation, valued at today. Then value at a date two days ago.
- Choose Export Excel on each of the two reports, and open the files.
Expected results
- Step 1: the total is the sum of the amounts of every completed line in the period; the filter keeps only that supplier’s lines.
- Step 2: “No completed goods receipts in this period.”
- Step 3: the valuation lists each part with quantity, average cost and value, grouped by category, with a total. At the earlier date: “Nothing was in stock on this date.”
- Step 4: each workbook matches the screen: the same lines and totals, and the valuation date in its title.
E2E-INV-016 · A technician sees quantities but no cost
Priority: P1 · Covers: FR-INV-08 · Automation: Automated (API, partial)
Blackbox: TestCostIsHiddenFromTechnicians (stock level, receipt, and the purchases and valuation reports for a Technician member).
Preconditions: baseline. MANAGER_A1 and TECH_A1A2, A1 active. Part OIL-5W30 with 10 L on hand and a completed receipt.
Steps
- TECH_A1A2: open Inventory → Stock on Hand and open
OIL-5W30. - TECH_A1A2: open Goods receipts and open the completed receipt.
- TECH_A1A2: open Purchases and Stock valuation.
- MANAGER_A1: open the same screens.
Expected results
- Step 1: On hand
10 L; no Average cost or Stock value is shown. - Step 2: the receipt shows its lines and quantities, without the unit cost, line amount and total.
- Step 3: the Purchases and Stock valuation tabs are not offered, or show “You are not allowed to do this.”
- Step 4: the manager sees the cost, the average and the totals.
E2E-INV-017 · A repair order’s stock issue reserves its parts; “issue now” posts it
Priority: P1 · Covers: FR-INV-15, FR-INV-20 · Automation: Automated (API, partial)
Blackbox: TestStockIssueReservesThenIssuesNow (the draft reserves 4 of oil and 1 of filter; available drops; on hand does not; Issue now posts it; a second post is refused with STOCK_ISSUE_NOT_DRAFT).
Preconditions: baseline. ADVISOR_A1 and INVENTORY_A1, A1 active. Parts OIL-5W30 (10 L) and FILTER-01 (5) on hand.
Steps
- ADVISOR_A1: create a repair order with two part lines:
OIL-5W30×4andFILTER-01×1. Save. - INVENTORY_A1: open Inventory → Stock on Hand; read
OIL-5W30andFILTER-01. - INVENTORY_A1: open the repair order’s Stock issue card (number
PX-…, status Draft). - INVENTORY_A1: choose Issue now, then confirm.
- Read Stock on Hand and Stock Movements again.
- Open the posted stock issue and choose Issue now again.
Expected results
- Step 2:
OIL-5W30shows4 Lreserved, Available6 L, On hand10 L;FILTER-01shows1reserved. - Step 3: the draft lists both lines, with the note that the parts are reserved for the order.
- Step 4: the stock issue is Posted, and “Stock issue posted.” appears.
- Step 5: on hand is
6 Land4; reserved is0; two Out movements reference Stock issue with a cost each. - Step 6: nothing happens: “This stock issue has already been posted.”
E2E-INV-018 · Closing the repair order posts its stock issue once
Priority: P1 · Covers: FR-INV-02, FR-INV-20 · Automation: Automated (API)
Blackbox: TestClosingTheOrderPostsTheStockIssue (one movement per part, the reservation released, the movement stamped with the average cost).
Preconditions: baseline. ADVISOR_A1, A1 active. Part OIL-5W30 with 10 L on hand at average cost 100.
Steps
- Create a repair order with
OIL-5W30×4. The draft stock issue reserves4 L. - Complete the line (E2E-REP-006) and close the order (E2E-REP-008).
- Open the Stock Movements for
OIL-5W30. - Close the order again, if the screen allows it.
Expected results
- Step 2: on hand becomes
6 L, reserved0 L; the stock issue shows Posted. - Step 3: one Out movement of
4, reference Stock issue, unit cost100. - Step 4: no second movement is written.
E2E-INV-019 · With “stock issue posts: when work starts”, the issue posts when the order starts
Priority: P2 · Covers: FR-INV-20 · Automation: Automated (API)
Blackbox: TestStockIssueModeOnStart (the garage setting stock_issue_mode = on_start; the stock issue posts at the first line move out of open).
Preconditions: baseline. OWNER_A, A1 active. Part OIL-5W30 with 10 L on hand.
Test data: setting Stock issue posts = When work starts (the garage’s settings).
Steps
- OWNER_A: Settings → set Stock issue posts to When work starts. Save.
- ADVISOR_A1: create a repair order with
OIL-5W30×4. Check Stock on Hand. - TECH_A1A2: set the order’s first line to In progress.
- Check Stock on Hand and the Stock Movements tab.
Expected results
- Step 2:
OIL-5W30shows4 Lreserved; on hand is still10 L. - Step 3: the stock issue posts when the first line leaves To Do.
- Step 4: on hand
6 L; one Out movement, reference Stock issue. Clean-up: set the stock issue setting back to When the repair order closes.
E2E-INV-020 · Count a shelf: matched, over and short lines; completion makes stock equal the count
Priority: P1 · Covers: FR-INV-12 · Automation: Automated (API)
Blackbox: TestStockCountMakesStockEqualTheCount (the uncounted refusal, the three result kinds, the difference value at average cost, the stock card entry, the refusal to edit a completed count, and the cancel reversal of E2E-INV-023).
Preconditions: baseline. MANAGER_A1, A1 active. Parts OIL-5W30 (litre, 10 L on hand at an average cost of 100), FILTER-01 (piece, 20 on hand at 50) and PLUG-M12 (piece, 5 on hand at 200), each received by a completed goods receipt (E2E-INV-012 pattern).
Test data: count name Shelf A, notes monthly. Counted quantities: OIL-5W30 10, FILTER-01 18, PLUG-M12 7.
Steps
- Inventory → Stock counts → New stock count. Set the name to
Shelf Aand the notes tomonthly. Save as draft. - Add the three parts by search. Note each line’s system quantity.
- Choose Complete count before typing any quantity.
- Type the counted quantities above.
- Read the summary of the count and the value of each difference.
- Choose Export Excel and open the file as MANAGER_A1.
- Choose Complete count.
- Open Stock on Hand for each part, and the Stock Movements of
PLUG-M12. - Try to change the counted quantity of
OIL-5W30.
Expected results
- Step 1: the count is a draft with a number from the stock count template,
KK-{YYYY}{MM}-0001for the first count of the month. - Step 2: the system quantities are
10,20and5; the three lines show Uncounted. - Step 3: completion is refused with
STOCK_COUNT_HAS_UNCOUNTED_LINES; the count stays a draft and stock does not change. - Step 4:
OIL-5W30is Matched;FILTER-01is Short by-2(value-100);PLUG-M12is Over by+2(value+400). - Step 5: the summary shows 1 matched, 1 over and 1 short line, and a total difference of
+300. - Step 6: the workbook lists each line with its system and counted quantities, result and difference, and the average cost and value columns (MANAGER_A1 has cost access).
- Step 7: the count is Completed. Stock is
10 L,18and7; the average cost ofPLUG-M12stays200. - Step 8: the adjustment appears in the stock movements of
PLUG-M12as an In movement of2with the count number as its reference, balance7, and the user’s name. - Step 9: the change is refused with
STOCK_COUNT_NOT_DRAFT; the completed count cannot be edited.
E2E-INV-021 · A count typed in a packaging unit; zero is a real count; the system quantity is taken again when typed
Priority: P2 · Covers: FR-INV-12, FR-CAT-02 · Automation: Manual only
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with base unit litre, a packaging unit can of 5 L, and 10 L on hand (E2E-INV-020). Part FILTER-01 with 4 on hand.
Test data: count name Shelf B. Typed counts: OIL-5W30 2 in can (the same as 10 L); FILTER-01 0.
Steps
- Create the draft
Shelf Band addOIL-5W30andFILTER-01. - Type
2in the unit can on theOIL-5W30line. - Type
0on theFILTER-01line. - Clear the
FILTER-01counted quantity, then type0again. - Receive
2 LofOIL-5W30by a completed goods receipt. Without typing the line again, read its system quantity. Type2in can again and read the system quantity.
Expected results
- Step 2: the counted quantity reads
10 L(2 can); the line is Matched. - Step 3: the line shows counted
0, not Uncounted; the result is Short by-4. - Step 4: the line returns to Uncounted while empty.
- Step 5: after the receipt the line still shows the system quantity
10 Lit was counted against. Typing2in can again takes a new system quantity of12 L, and the line is Short by-2(counted10 L). The system quantity is taken again at each typing, so the difference compares with the stock on hand at that time.
E2E-INV-022 · Two drafts merge into one; uncounted lines are skipped on completion
Priority: P2 · Covers: FR-INV-12 · Automation: Automated (API)
Blackbox: TestStockCountDraftsMerge (the merge of two drafts with a shared part counted in different places, the refusal of fewer than two counts and of a count that is not a draft, the cancelled absorbed count, and the skip of uncounted lines on completion).
Preconditions: baseline. MANAGER_A1, A1 active. Parts OIL-5W30 (10 L on hand), FILTER-01 (4 on hand) and PLUG-M12 (3 on hand).
Test data: drafts Shelf C and Shelf D. Shelf C: OIL-5W30 4, FILTER-01 4. Shelf D: OIL-5W30 6, and PLUG-M12 added but not counted.
Steps
- Create
Shelf CandShelf Das drafts, and count the lines as above. - Choose Merge with only
Shelf Cselected. Read the message. - Select
Shelf CandShelf Dand merge;Shelf Cis the one kept. - Open
Shelf D. - Merge
Shelf CandShelf Dagain. - Complete the merged count without skipping the uncounted line, then choose to skip the uncounted lines and complete.
Expected results
- Step 2: the merge is refused with a validation error (at least two drafts are needed); nothing changes.
- Step 3:
Shelf Cis returned with oneOIL-5W30line counted10, andFILTER-01still counted4; thePLUG-M12line is uncounted and stays on the count. - Step 4:
Shelf Dis Cancelled, has no lines, and its notes end with “Merged into” and the number ofShelf C. - Step 5: refused with
STOCK_COUNT_NOT_DRAFT. - Step 6: the first completion is refused with
STOCK_COUNT_HAS_UNCOUNTED_LINES. After skipping, the count is Completed with two lines:OIL-5W3010 L(matched) andFILTER-014(matched);PLUG-M12stays3on hand, untouched.
E2E-INV-023 · Cancelling a draft closes it; cancelling a completed count posts its reversal, refused once the stock is used
Priority: P1 · Covers: FR-INV-12 · Automation: Automated (API)
Blackbox: TestStockCountMakesStockEqualTheCount (the reversal of a completed count on cancel, and the refusal of a second cancel with STOCK_COUNT_ALREADY_CANCELLED), TestStockCountReversalNeedsTheStock (INSUFFICIENT_STOCK_TO_REVERSE).
Preconditions: baseline. MANAGER_A1 and ADVISOR_A1, A1 active. E2E-INV-020 done: count Shelf A is Completed and added 2 of PLUG-M12 (on hand 7). Part PLUG-M12 is used on a repair order in step 3.
Steps
- Create a new draft count with
FILTER-01and choose Cancel count. - Cancel the same count again.
- ADVISOR_A1: create a repair order with
PLUG-M12×6; complete and close it (E2E-INV-008). Check Stock on Hand forPLUG-M12. - MANAGER_A1: open
Shelf A(completed) and choose Cancel count. Read the confirmation, then confirm. - Check Stock on Hand for
PLUG-M12.
Expected results
- Step 1: the draft is Cancelled; no stock moves,
FILTER-01stays at18. - Step 2: refused with
STOCK_COUNT_ALREADY_CANCELLED. - Step 3:
PLUG-M12is1. - Step 4: the confirmation says the count’s adjustments are reversed. The reversal is refused with
INSUFFICIENT_STOCK_TO_REVERSE: its2cannot be taken back from1.Shelf Astays Completed. - Step 5: On hand
1, unchanged by the refused reversal. - Note: when the stock the count added is still on hand, the same cancel succeeds, reverses each line and sets the count to Cancelled (blackbox as above). Clean-up: none needed; the repair order stays closed.
E2E-INV-024 · Count Excel export: value columns only with cost access
Priority: P2 · Covers: FR-INV-08, FR-DOC-02, FR-INV-12 · Automation: Manual only
Preconditions: baseline. MANAGER_A1 and INVENTORY_A1 (Inventory Staff, cost access), TECH_A1A2 (no cost access), A1 active. A draft count with two counted lines (E2E-INV-020 pattern).
Steps
- MANAGER_A1: open the count and choose Export Excel. Open the file.
- TECH_A1A2: open the same count and choose Export Excel. Open the file.
Expected results
- Step 1: the workbook lists each line with system and counted quantities, result and difference; the average cost and the difference value columns are filled.
- Step 2: TECH_A1A2 can export the workbook, because the count is a read of inventory. It has the quantities, the results and the differences only: no average cost and no value columns. The technician never sees a cost.
E2E-INV-025 · A write-off reduces stock at average cost; refused beyond on hand; cancelling puts the parts back
Priority: P1 · Covers: FR-INV-09 · Automation: Automated (API)
Blackbox: TestWriteOffReducesStockAtAverageCost (the line value at the average cost of the moment, the refusal beyond on hand with INSUFFICIENT_STOCK, the stock card entry with the reason, the list filter by reason, and the cancel that restores stock at the same average cost).
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with 10 L on hand at an average cost of 100 (E2E-INV-012 pattern).
Test data: write-off with reason Damaged, notes dropped in transit, line OIL-5W30 3; a second write-off with reason Expired, line OIL-5W30 70.
Steps
- Inventory → Write-offs → New write-off. Choose the reason Damaged, type the notes, add
OIL-5W30with3. Save. - Read the write-off’s value and the line’s unit cost.
- Open Stock on Hand for
OIL-5W30. - Create the second write-off with
70ofOIL-5W30. - Open the stock movements of
OIL-5W30. - Choose Cancel write-off on the first write-off and confirm.
- Check Stock on Hand again, and try to cancel the same write-off a second time.
Expected results
- Step 1: the write-off is created Completed (posted) with a number from the write-off template,
XH-{YYYY}{MM}-…. - Step 2: the unit cost is
100(the average cost at that moment); the value is300. - Step 3: On hand
7 L. - Step 4: refused with
INSUFFICIENT_STOCK; nothing changes. - Step 5: an Out movement of
3 Lwith the write-off number as its reference, the reason Damaged and the balance7. - Step 6: the write-off is Cancelled; the parts go back at
100. - Step 7: On hand
10 L, average cost100. A second cancel is refused withWRITE_OFF_ALREADY_CANCELLED.
E2E-INV-026 · A return to supplier records the credit; cancelling puts the parts back
Priority: P1 · Covers: FR-INV-10, FR-INV-19 · Automation: Automated (API)
Blackbox: TestSupplierReturnRecordsTheCredit (the default credit from the linked receipt, a typed credit, the refusal beyond on hand, the refusal of a receipt of another supplier, the list by supplier, the credit note number, and the cancel).
Preconditions: baseline. MANAGER_A1, A1 active. Part OIL-5W30 with a completed receipt from QA Supplier Phu Tung Sai Gon (supplier1-1-1): 10 at 120 (E2E-INV-011 pattern), and 10 L on hand. Supplier QA Supplier Lop Xe Viet (supplier1-1-2) with no receipt of OIL-5W30.
Test data: return to QA Supplier Phu Tung Sai Gon, linked to the receipt, credit note number CN-1001, line OIL-5W30 × 4 with no typed cost; a second return to the same supplier with OIL-5W30 × 1 at a typed cost of 90.
Steps
- Create the return: supplier, the linked completed receipt, credit note number
CN-1001,OIL-5W30×4with no cost. Save. - Read the credit amount and the line’s cost.
- Open Stock on Hand for
OIL-5W30. - Create a second return to the same supplier:
OIL-5W30×1at the cost90. Read the credit amount. - Create a return to QA Supplier Lop Xe Viet linked to the same receipt, and a return of
OIL-5W30×60to the first supplier. - Cancel the first return.
Expected results
- Step 1: the return is Completed with a number from the return template,
TN-{YYYY}{MM}-…, and the linked receipt number. - Step 2: the line costs
120(the cost of the same part on the linked receipt); the credit amount is480. - Step 3: On hand
6 L. - Step 4: the credit is
90; on hand5 L. - Step 5: the return to the other supplier is refused with the validation error (the receipt has no line from that supplier); the return of
60is refused withINSUFFICIENT_STOCK. - Step 6: the return is Cancelled; on hand is back to
9 L(after step 4), at the cost the parts left with. A second cancel is refused withSUPPLIER_RETURN_ALREADY_CANCELLED.
E2E-INV-027 · Return of unused parts from a repair order, after its stock issue is posted
Priority: P1 · Covers: FR-INV-11, FR-INV-20 · Automation: Automated (API)
Blackbox: TestUnusedRepairOrderPartsReturnToStock (refused until the stock issue is posted with STOCK_ISSUE_NOT_POSTED, a return with a reason restores stock and cost, the cap RETURN_EXCEEDS_ISSUED, the stock card entry, and the list of returns).
Preconditions: baseline. ADVISOR_A1 and MANAGER_A1, A1 active. Part OIL-5W30 with 10 L on hand at 100. A repair order with OIL-5W30 × 2 and its stock issue, not yet posted (E2E-INV-017 pattern).
Test data: return 1.5 litre of OIL-5W30 with the reason customer declined the extra oil; a second return of 1 litre (to pass the cap); a return with an empty reason.
Steps
- MANAGER_A1: open the repair order’s Parts returns and try to return
1.5L before the stock issue is posted. - Post the stock issue (Issue now) for the order. Stock is now
8 L. - Return
1.5L with the reasoncustomer declined the extra oil. - Read the return’s cost, and Stock on Hand.
- Return
1L more: total returned2.5against2issued; read the refusal. - Try to return with the reason left empty.
- Open the repair order’s Stock issue and the stock movements of
OIL-5W30.
Expected results
- Step 1: refused with
STOCK_ISSUE_NOT_POSTED; the draft is unchanged and on hand10 L. - Step 2: the issue posts;
OIL-5W30is 8 L. - Step 3: the return is recorded with its reason.
- Step 4: the return is restored at
100, the cost the line left with; On hand9.5 L. The order’s lines and what the customer is billed are unchanged. - Step 5: refused with
RETURN_EXCEEDS_ISSUED; nothing changes. - Step 6: refused as a validation error; the reason is required.
- Step 7: the stock issue shows
1.5returned for that line; the movements show an In movement with the reason and the balance after it.
E2E-INV-028 · Stock card entries of counts, write-offs and returns: number, reason, user and balance
Priority: P2 · Covers: FR-INV-05, FR-INV-09, FR-INV-10, FR-INV-11, FR-INV-12 · Automation: Automated (API, partial)
Blackbox: TestStockCountMakesStockEqualTheCount (the count entry: its number, the quantity, the balance after it, the user and the value at average cost), TestWriteOffReducesStockAtAverageCost (the reason on the entry), TestUnusedRepairOrderPartsReturnToStock (the reason and the balance after the return). The screens are not covered by the blackbox suite.
Preconditions: E2E-INV-020, E2E-INV-025 and E2E-INV-027 done. MANAGER_A1, A1 active.
Steps
- Open Stock on Hand for
PLUG-M12and open its Stock Movements tab. - Open the same tab for
OIL-5W30. - Open the write-off entry and the repair order return entry.
Expected results
- Step 1: the entries are in the order they were made. The count entry (
+2, In) shows the count numberKK-…as its reference, the user who completed the count, and a balance of7after it. - Step 2: the write-off entry (Out,
3) shows the write-off numberXH-…and the reason Damaged, and its balance after it. The repair order return (In,1.5) shows the reasoncustomer declined the extra oil, has no document number, and its balance after it is9.5. - Each entry that a user made shows that user’s name; an automatic posting, such as a repair order’s stock issue, shows none.
E2E-INV-029 · Permissions: counting and adjusting are separate from stock write
Priority: P1 · Covers: FR-IAM-03, FR-INV-08, FR-INV-09, FR-INV-10, FR-INV-11, FR-INV-12 · Automation: Automated (API)
Blackbox: TestAdjustmentPermissions (a technician reads stock but cannot count, write off, return to supplier or return repair order parts; inventory staff can; the count’s cost fields follow inventory.cost.read).
Preconditions: baseline. MANAGER_A1, INVENTORY_A1 (group Inventory Staff), TECH_A1A2 (group Technician), A1 active. Part OIL-5W30 with 10 L on hand at an average cost of 100, and a draft count with OIL-5W30 counted.
Test data: INVENTORY_A1 holds inventory.count and inventory.adjust by its default role; TECH_A1A2 holds neither.
Steps
- TECH_A1A2: open Stock on Hand and open the draft count.
- TECH_A1A2: look for New stock count, New write-off, New return to supplier and Return part on a repair order; try to add a line to the draft count.
- INVENTORY_A1: create a count, count
OIL-5W30, create a write-off of1L and a return to supplier. - MANAGER_A1: open the draft count and the write-off.
Expected results
- Step 1: TECH_A1A2 sees On hand
10 Land the draft count with its quantities; no Average cost, Stock value, difference value or unit cost is shown. - Step 2: none of the four actions is offered. A direct request to add a line is refused with
PERMISSION_DENIED. - Step 3: every action succeeds. The write-off is Completed, and the count’s adjustments are written only when the count is completed.
- Step 4: MANAGER_A1 sees the cost fields: the average cost, the difference value and the write-off’s value.
E2E-INV-030 · A part has several barcodes, for the base unit or a case; a code is unique in the garage; lookup finds the part and the unit
Priority: P1 · Covers: FR-INV-13, FR-CAT-02 · Automation: Automated (API)
Blackbox: TestPartBarcodes (several codes, the case unit, the uniqueness without regard to case, the lookup of an active and an inactive part, the search by exact code, and freeing the codes when the part is deleted).
Preconditions: baseline. MANAGER_A1, A1 and A2 active. Part BEER-330 (can) with a packaging unit carton = 24 cans in A1, and a second part COLA-330 in A1; no barcodes yet.
Test data: codes 8930001 (base unit) and 8930024 (carton) for BEER-330; 8930001 again for COLA-330; 8930024 typed as 8930024 in garage A2 for its own part.
Steps
- Catalogue → Parts → BEER-330 → Barcodes. Add
8930001for the can and8930024for the carton. Save. - Open the Barcodes of
COLA-330and try to add8930001(and8930001typed with other case or spaces around it). - Look up
8930024as MANAGER_A1 (the scan field of a goods receipt, orGET …/barcodes:lookup?barcode=8930024). - Search the parts list for
8930001. - Try to remove the
cartonpackaging unit fromBEER-330, then change its factor to12. - Deactivate
BEER-330and look up8930001again. Then create an unused partTEMP-1, give it the code8930555, deleteTEMP-1, and give8930555toCOLA-330. - In garage A2, add
8930024to a part of A2 and look it up from A1.
Expected results
- Step 1:
BEER-330shows both codes, the second marked withcarton. - Step 2: refused with
BARCODE_ALREADY_USED(409) in each form;COLA-330has no barcode. - Step 3: the part is
BEER-330and the code’s unit is the carton; looking up8930024in any letter case answers the same. - Step 4: only
BEER-330is listed (an exact code matches; a fragment of it does not). - Step 5: removing the carton is refused with
PACKAGING_UNIT_HAS_BARCODE(409); changing its factor is accepted and the codes are kept. - Step 6: an inactive part is still found, with
isActivefalse; onceTEMP-1is deleted its code is free, andCOLA-330accepts8930555. - Step 7: the code is accepted in A2 (uniqueness is per garage) and A1 still answers with its own part; A1 never finds A2’s part.
E2E-INV-031 · Scan into a goods receipt: a line, one more of the same, cases for a case code; unknown and inactive codes are told
Priority: P2 · Covers: FR-INV-13, FR-INV-06 · Automation: Automated (web, typed codes); the phone camera is manual
Preconditions: baseline. MANAGER_A1, A1 active. Part BEER-330 with codes 8930001 (can) and 8930024 (carton, 24 cans), part OLD-1 (inactive) with code 8930099, supplier supplier1-1-1.
Steps
- Inventory → Goods receipts → New receipt. Choose the supplier.
- In Scan a barcode scan (or type and press Enter)
8930001, then8930001again. - Scan
8930024. - Scan
0000000, then8930099. - Scan
8930024once more and save the receipt.
Expected results
- Step 2: one line
BEER-330in cans with quantity2; the field is empty and ready for the next scan. - Step 3: a second line
BEER-330in the carton, quantity1. - Step 4: the message “No part has the code 0000000.” and then “Part OLD-1 is inactive.”; no line is added.
- Step 5: the carton line has quantity
2; after saving, completing it adds48cans in stock for the carton line and2for the can line.
E2E-INV-032 · Scan into a stock count (web and phone): the part is added and the cursor goes to its counted quantity
Priority: P2 · Covers: FR-INV-13, FR-INV-12 · Automation: Manual only (needs the camera for the phone)
Preconditions: baseline. MANAGER_A1, A1 active; the owner app signed in as MANAGER_A1. Parts OIL-5W30 and FILTER-01 with barcodes 8930010 and 8930011; a draft count Shelf A with OIL-5W30 only.
Steps
- Web: open the draft count; in Scan a barcode scan
8930011. - Type
18and press Enter. - Scan
8930010. - Phone: open the count; tap the scan button; read the label of
FILTER-01(or type its code on the scanner screen). - Phone: with a count of more than 50 lines, scan a part near the end of the list.
- Phone: on the Inventory list tap the scan button and read a label.
Expected results
- Step 1:
FILTER-01is added with its system quantity and the cursor is in its counted quantity. - Step 2: the count is saved on the line.
- Step 3:
OIL-5W30is already there: nothing is added and the cursor goes to its counted quantity. - Step 4: the line is focused (it is already on the count).
- Step 5: the list scrolls to the line and the cursor is in its counted quantity.
- Step 6: the part’s detail opens. A code no part has says “No part has the code …” and an inactive part says so; neither adds or opens anything.
E2E-INV-033 · Import parts: a 500-row file with 5 bad rows lists all 5 and saves nothing; after fixing, parts, units, barcodes and opening stock appear
Priority: P1 · Covers: FR-INV-14, FR-INV-13, FR-INV-06, FR-CAT-01 · Automation: Automated (API and web)
Blackbox: TestImportPartsAllOrNothing (the preview of the bad file lists exactly the five problems with their rows, columns and codes; confirm writes nothing, not even a unit; the fixed file creates the 500 parts, the units, the barcodes of the base and of the case, and one completed opening-stock receipt whose lines each carry their supplier; the average cost and stock follow).
Preconditions: baseline. MANAGER_A1, A1 active; supplier ACME in A1. No parts. A parts workbook from the template with 500 rows: P-0000 to P-0499, base unit Piece (pc), category Filters, brand Bosch, a barcode on each row, every tenth row with Packaging units Case=12 and a case barcode Code@Case, and every row except each seventh with an opening quantity, a unit cost and the supplier ACME.
Test data: five bad rows: a missing Name (row 5), a repeated SKU (row 22), the supplier Nobody with an opening quantity (row 42), a barcode bad_code (row 62) and an opening quantity many (row 83).
Steps
- Inventory → Import from Excel → Parts. Download the template and read the Instructions sheet.
- Choose the bad file.
- Try to confirm (the web offers no button; through the API, call confirm).
- Look at Catalogue → Parts and the units.
- Fix the five rows and choose the file again.
- Read the preview, then Import 500 rows.
- Open the document named in the result; look at Stock on Hand, the units, the categories and the barcodes of
P-0010.
Expected results
- Step 1: the Data sheet has only the header row; the Instructions sheet explains each column in English and Vietnamese.
- Step 2: the preview shows 500 rows, 495 without problems and exactly five problems listed with rows 5, 22, 42, 62 and 83, each with its column and a reason (
REQUIRED_VALUE_MISSING,SKU_DUPLICATE,UNKNOWN_SUPPLIER,BARCODE_INVALID,INVALID_QUANTITY); there is no confirm button. - Step 3: refused with
IMPORT_FILE_INVALID(422). - Step 4: no part, no unit
PieceorCase, no category was created. - Step 5: the preview shows 500 rows, no problem, the first 20 rows and what will be created (500 parts, 1 category, 2 units, 550 barcodes, 428 lines of opening stock).
- Step 6: the result says 500 rows imported and links a Completed goods receipt.
- Step 7: the receipt has 428 lines, each with its supplier
ACME; on hand and the average cost follow the file;PieceandCaseand the categoryFiltersexist;P-0010has its base code and a second code for theCase.
E2E-INV-034 · The same file cannot be imported twice; a changed file can; concurrent confirmations import once
Priority: P1 · Covers: FR-INV-14 · Automation: Automated (API)
Blackbox: TestImportIsIdempotent (the repeat is refused with IMPORT_FILE_ALREADY_IMPORTED and creates nothing; changing one cell makes a new file; two confirmations of one file at the same time import once and the other is refused; the history keeps one row per import).
Preconditions: baseline. MANAGER_A1, A1 active; supplier ACME. A goods receipt workbook with two lines.
Steps
- Confirm the goods receipt file.
- Confirm the very same file again.
- Change a quantity in the file (so its bytes differ) and confirm it.
- Cancel the draft receipt made by step 1 and confirm the original file once more.
- Send two confirmations of a fresh file at the same time.
Expected results
- Step 1: one draft receipt is created.
- Step 2: refused with
IMPORT_FILE_ALREADY_IMPORTED(409); no second receipt exists. - Step 3: a second draft receipt is created (the content is new).
- Step 4: still refused: the file was imported once; changing it is how it is imported again.
- Step 5: exactly one succeeds and creates one receipt; the other is refused with
IMPORT_FILE_ALREADY_IMPORTED.
E2E-INV-035 · Import a goods receipt: one draft, lines found by SKU or barcode, a supplier on each line, the header columns must agree
Priority: P1 · Covers: FR-INV-14, FR-INV-06, FR-INV-13 · Automation: Automated (API)
Blackbox: TestImportGoodsReceipt (the draft, the lines found by SKU, by base code and by case code with the carton as its unit, the supplier on every line, the receipt date and delivery note taken from the rows that fill them, the header mismatch, the unknown supplier, the inactive part and the unit that does not match the code).
Preconditions: baseline. MANAGER_A1, A1 active. Part BEER-330 with a carton of 24 and codes 8930001 (can) and 8930024 (carton); part OLD-1 (inactive); suppliers supplier1-1-1 and ACME.
Test data: rows: 8930024, quantity 2, ACME, unit cost 12000, delivery note DN-77, date 2026-10-01; BEER-330, quantity 5, supplier1-1-1; 8930001, quantity 1, supplier1-1-1, unit can. A second file with delivery notes DN-1 and DN-2 on two rows, a supplier Nobody, the part OLD-1, and the code 8930024 with the unit can.
Steps
- Import from Excel → Goods receipt. Choose the first file and confirm.
- Open the receipt made.
- Choose the second file.
- Complete the receipt of step 2.
Expected results
- Step 1: the preview has no problem; the result links one Draft receipt with 3 lines.
- Step 2: the delivery note is
DN-77and the date2026-10-01; line 1 is2cartons (48cans) fromACMEat12000per carton; lines 2 and 3 are in cans fromsupplier1-1-1. Nothing has moved in stock yet. - Step 3: the preview lists
HEADER_MISMATCHon the second delivery note,UNKNOWN_SUPPLIER,PART_INACTIVEandUNIT_MISMATCH, each at its row. - Step 4: completing posts the movements; on hand is
48 + 5 + 1 = 54cans, and the average cost follows the unit costs typed in the file.
E2E-INV-036 · Import a stock count: one draft, a case barcode counts in cases, a part twice or a unit that is not the barcode’s is refused
Priority: P2 · Covers: FR-INV-14, FR-INV-12, FR-INV-13 · Automation: Automated (API)
Blackbox: TestImportStockCount (the draft count with its counted lines, the case barcode counted in cartons and converted, zero as a real count, the duplicate part by SKU and by barcode, the unit mismatch, and a 5,000-line file inside a request).
Preconditions: baseline. MANAGER_A1, A1 active. Parts BEER-330 (carton of 24, codes 8930001 and 8930024) with 100 cans on hand and FILTER-01 with 20.
Test data: a file named Shelf A with the rows 8930024 counted 3, FILTER-01 counted 0. A second file with BEER-330 and 8930001 (the same part twice), and 8930024 with the unit can.
Steps
- Import from Excel → Stock count. Choose the first file and confirm.
- Open the count made.
- Choose the second file.
Expected results
- Step 1: one Draft count named
Shelf Awith 2 lines. - Step 2:
BEER-330is counted3cartons (72cans) against100on hand (short by28);FILTER-01is counted0(short by20). Nothing was posted; completing the count is the user’s step. - Step 3: the preview lists
SKU_DUPLICATEfor the part named twice andUNIT_MISMATCHfor the unit that is not the code’s.
E2E-INV-037 · Import permissions: by type and by column; the template is downloadable
Priority: P1 · Covers: FR-INV-14, FR-IAM-03, FR-INV-08 · Automation: Automated (API)
Blackbox: TestImportPermissions (each type with and without its permission, the opening-stock and cost columns of a parts file, a caller with only catalogue.write, and the template for each type).
Preconditions: baseline. MANAGER_A1, INVENTORY_A1 (group Inventory Staff), TECH_A1A2 (group Technician), A1 active. A parts file with opening quantities and costs, a goods receipt file with a unit cost, and a count file.
Test data: a custom role with catalogue.write only (no inventory permission), assigned to a staff member CATALOGUE_A1.
Steps
- TECH_A1A2: open Inventory; look for Import from Excel; call the preview of each type.
- CATALOGUE_A1: preview the parts file, then confirm it.
- INVENTORY_A1: preview the parts file (it has
catalogue.write,inventory.write, cost access by default), the receipt file and the count file. - As a caller with
inventory.writebut notinventory.cost.read, preview the receipt file that has unit costs. - Download the template of each type as MANAGER_A1.
Expected results
- Step 1: no import entry is offered; the previews report a row-0
PERMISSION_REQUIREDerror and a confirm is refused withPERMISSION_DENIED(403). - Step 2: the preview reports
PERMISSION_REQUIREDfor theOpening quantityandUnit costcolumns; the confirm is refused and nothing is created. - Step 3: all three previews have no problem; the confirms succeed.
- Step 4: the preview reports
PERMISSION_REQUIREDon theUnit costcolumn; a file without costs is accepted. - Step 5: three
.xlsxworkbooks, each with the header row of its type on the Data sheet and the Instructions sheet.
E2E-INV-038 · A file that cannot be read, is too large, has too many rows or lacks a column is refused as a whole
Priority: P2 · Covers: FR-INV-14 · Automation: Automated (API)
Blackbox: TestImportRefusesBadFiles (not an .xlsx, an empty file, a missing required column, 5,001 rows, a file over 5 MB; each is a row-0 error, canConfirm is false and confirm writes nothing).
Preconditions: baseline. MANAGER_A1, A1 active.
Steps
- Choose a
.csv, then a text file renamed.xlsx, then an empty workbook. - Choose a parts workbook without the
SKUcolumn. - Choose a parts workbook with 5,001 data rows.
- Choose a file larger than 5 MB.
- Choose a valid file whose header names are written in other case and spacing, with an extra column and blank rows between the data.
Expected results
- Step 1: the web refuses the
.csvbefore uploading it; the others are reported asINVALID_FILEorFILE_EMPTY, with no row. - Step 2:
MISSING_COLUMNforSKU, and no row is checked. - Step 3:
TOO_MANY_ROWS; no row is checked. - Step 4:
FILE_TOO_LARGE; the web refuses it before uploading. - Step 5: the preview accepts it: headers match without regard to case and spacing, the extra column is ignored and the blank rows are skipped; the rows are numbered as the sheet shows them.