Skip to content

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.

IDTitlePriorityAutomation
E2E-INV-001Add a part code to the catalogueP1Automated (API, partial)
E2E-INV-002SKU is unique per garage; part field validationP2Automated (API, partial)
E2E-INV-003A goods receipt raises stock on hand when it is completedP1Automated (API, partial)
E2E-INV-004The low-stock badge appears at or below the reorder thresholdP2Automated (API, partial)
E2E-INV-005Search parts by SKU or nameP2Automated (API, partial)
E2E-INV-006Edit and delete a part; a deleted part’s past transactions are keptP2Automated (API, partial)
E2E-INV-007The stock movement report, filterable by direction and partP2Automated (API, partial)
E2E-INV-008Closing a repair order with part lines posts its stock issue and deducts stockP1Automated (API, partial)
E2E-INV-009A part line without a catalogue part is not deducted on closeP3Automated (API, partial)
E2E-INV-010Permissions: view-only, and full accessP1Automated
E2E-INV-011A goods receipt with a supplier on each line; completion is blocked while a line has noneP1Automated (API, partial)
E2E-INV-012Completing a receipt updates stock and the weighted-average costP1Automated (API)
E2E-INV-013Cancelling a completed receipt restores stock and cost; refused once the stock is usedP1Automated (API)
E2E-INV-014Part suppliers: the default fills a new line; the last cost is recorded on completionP2Automated (API)
E2E-INV-015Purchases by supplier and stock valuation, on screen and as ExcelP2Automated (API, partial)
E2E-INV-016A technician sees quantities but no costP1Automated (API, partial)
E2E-INV-017A repair order’s stock issue reserves its parts; “issue now” posts itP1Automated (API, partial)
E2E-INV-018Closing the repair order posts its stock issue onceP1Automated (API)
E2E-INV-019With “stock issue posts: when work starts”, the issue posts when the order startsP2Automated (API)
E2E-INV-020Count a shelf: matched, over and short lines; completion makes stock equal the countP1Automated (API)
E2E-INV-021A count typed in a packaging unit; zero is a real count; the system quantity is taken again when typedP2Manual only
E2E-INV-022Two drafts merge into one; uncounted lines are skipped on completionP2Automated (API)
E2E-INV-023Cancelling a draft closes it; cancelling a completed count posts its reversal, refused once the stock is usedP1Automated (API)
E2E-INV-024Count Excel export: value columns only with cost accessP2Manual only
E2E-INV-025A write-off reduces stock at average cost; refused beyond on hand; cancelling puts the parts backP1Automated (API)
E2E-INV-026A return to supplier records the credit; cancelling puts the parts backP1Automated (API)
E2E-INV-027Return of unused parts from a repair order, after its stock issue is postedP1Automated (API)
E2E-INV-028Stock card entries of counts, write-offs and returns: number, reason, user and balanceP2Automated (API, partial)
E2E-INV-029Permissions: counting and adjusting are separate from stock writeP1Automated (API)
E2E-INV-030A part has several barcodes, for the base unit or a case; a code is unique in the garage; lookup finds the part and the unitP1Automated (API)
E2E-INV-031Scan into a goods receipt: a line, one more of the same, cases for a case code; unknown and inactive codes are toldP2Automated (web, typed codes; the camera is manual)
E2E-INV-032Scan into a stock count (web and phone): the part is added and the cursor goes to its counted quantityP2Manual only
E2E-INV-033Import parts: a 500-row file with 5 bad rows lists all 5 and saves nothing; after fixing, parts, units, barcodes and opening stock appearP1Automated (API and web)
E2E-INV-034The same file cannot be imported twice; a changed file can; concurrent confirmations import onceP1Automated (API)
E2E-INV-035Import a goods receipt: one draft, lines found by SKU or barcode, a supplier on each line, the header columns must agreeP1Automated (API)
E2E-INV-036Import a stock count: one draft, a case barcode counts in cases, a part twice or a unit that is not the barcode’s is refusedP2Automated (API)
E2E-INV-037Import permissions: by type and by column; the template is downloadableP1Automated (API)
E2E-INV-038A file that cannot be read, is too large, has too many rows or lacks a column is refused as a wholeP2Automated (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

  1. Catalogue → Units → New unit with the unit’s data. Save.
  2. Catalogue → Parts → New part. Enter the test data. Save.
  3. 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 threshold 5.
  • 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:

CaseSKUNameExpected
aOIL-5W30Different oilRefused: another part already uses this SKU
b(empty)Air filterSKU required
cFILTER-01(empty)Name required
dFILTER-01Air filterSaved

Steps

  1. 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

  1. Inventory → Goods receipts → New goods receipt. Enter a receipt date and a delivery note number DN-01. Add a line: OIL-5W30, quantity 10, unit cost 150000, supplier QA Supplier Phu Tung Sai Gon. Save as draft.
  2. Check Stock on Hand for OIL-5W30.
  3. Open the draft and choose Complete receipt.
  4. Check Stock on Hand again.
  5. New receipt: OIL-5W30, quantity 5, 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 hand 0 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

  1. Catalogue → Parts: edit the part, reorder threshold 20. Save. Look at its row in Inventory.
  2. 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 to 5.

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

  1. In Inventory → Stock on Hand, search OIL, then Engine, then FILTER, then nonexistent.

Expected results

  • OIL and Engine find only the oil; FILTER finds only the filter (SKU and name both match).
  • nonexistent shows “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

  1. Catalogue → Parts: edit, rename to QA Test Part Renamed. Save.
  2. Delete it and confirm.
  3. 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

  1. Inventory → Stock Movements tab, no filter.
  2. 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

  1. Open the repair order. Inventory → Stock on Hand shows OIL-5W30 with 4 L reserved and Available 6 L; on hand is still 10 L.
  2. Complete every line of the repair order (E2E-REP-006), then Close repair order (E2E-REP-008).
  3. Immediately open Inventory → Stock on Hand for OIL-5W30.
  4. 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 exactly 4 (10 L → 6 L), with no page action needed beyond a refresh; the reserved quantity returns to 0 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

  1. Complete every line and close the repair order.
  2. 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

  1. VIEWER_A1: look at the sidebar; type /inventory directly.
  2. TECH_A1A2: open Inventory and Catalogue; look for New goods receipt, Receive stock and New part.
  3. 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; /inventory shows 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

  1. Inventory → Goods receipts → New goods receipt. Set the header supplier to QA Supplier Phu Tung Sai Gon, the delivery note to DN-77 and 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.
  2. Add the third line and clear its supplier. Choose Complete receipt.
  3. Pick a supplier on the third line, then Complete receipt again.
  4. 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

  1. Complete a receipt: OIL-5W30, 10 at 100 per litre, with a supplier.
  2. Open Stock on Hand for OIL-5W30 and read Average cost and Stock value.
  3. Complete a second receipt: OIL-5W30, 10 at 200.
  4. 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

  1. Open R2 and choose Reverse receipt. Read the confirmation, then confirm.
  2. Open Stock on Hand for OIL-5W30.
  3. Create a repair order with OIL-5W30 × 3; complete and close it (E2E-INV-008). Check Stock on Hand again.
  4. 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

  1. 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 code A-1 and mark it Default. Save.
  2. Inventory → Goods receipts → New goods receipt; add OIL-5W30 × 10 at 120 without choosing a supplier. Save, then complete.
  3. Open the part’s Suppliers again.
  4. 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 120 per 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

  1. Inventory → Purchases. Read the total for the whole period, then filter by QA Supplier Lop Xe Viet.
  2. Set From to a date after today and read the list.
  3. Inventory → Stock valuation, valued at today. Then value at a date two days ago.
  4. 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

  1. TECH_A1A2: open Inventory → Stock on Hand and open OIL-5W30.
  2. TECH_A1A2: open Goods receipts and open the completed receipt.
  3. TECH_A1A2: open Purchases and Stock valuation.
  4. 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

  1. ADVISOR_A1: create a repair order with two part lines: OIL-5W30 × 4 and FILTER-01 × 1. Save.
  2. INVENTORY_A1: open Inventory → Stock on Hand; read OIL-5W30 and FILTER-01.
  3. INVENTORY_A1: open the repair order’s Stock issue card (number PX-…, status Draft).
  4. INVENTORY_A1: choose Issue now, then confirm.
  5. Read Stock on Hand and Stock Movements again.
  6. Open the posted stock issue and choose Issue now again.

Expected results

  • Step 2: OIL-5W30 shows 4 L reserved, Available 6 L, On hand 10 L; FILTER-01 shows 1 reserved.
  • 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 L and 4; reserved is 0; 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

  1. Create a repair order with OIL-5W30 × 4. The draft stock issue reserves 4 L.
  2. Complete the line (E2E-REP-006) and close the order (E2E-REP-008).
  3. Open the Stock Movements for OIL-5W30.
  4. Close the order again, if the screen allows it.

Expected results

  • Step 2: on hand becomes 6 L, reserved 0 L; the stock issue shows Posted.
  • Step 3: one Out movement of 4, reference Stock issue, unit cost 100.
  • 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

  1. OWNER_A: Settings → set Stock issue posts to When work starts. Save.
  2. ADVISOR_A1: create a repair order with OIL-5W30 × 4. Check Stock on Hand.
  3. TECH_A1A2: set the order’s first line to In progress.
  4. Check Stock on Hand and the Stock Movements tab.

Expected results

  • Step 2: OIL-5W30 shows 4 L reserved; on hand is still 10 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

  1. Inventory → Stock counts → New stock count. Set the name to Shelf A and the notes to monthly. Save as draft.
  2. Add the three parts by search. Note each line’s system quantity.
  3. Choose Complete count before typing any quantity.
  4. Type the counted quantities above.
  5. Read the summary of the count and the value of each difference.
  6. Choose Export Excel and open the file as MANAGER_A1.
  7. Choose Complete count.
  8. Open Stock on Hand for each part, and the Stock Movements of PLUG-M12.
  9. 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}-0001 for the first count of the month.
  • Step 2: the system quantities are 10, 20 and 5; 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-5W30 is Matched; FILTER-01 is Short by -2 (value -100); PLUG-M12 is 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, 18 and 7; the average cost of PLUG-M12 stays 200.
  • Step 8: the adjustment appears in the stock movements of PLUG-M12 as an In movement of 2 with the count number as its reference, balance 7, 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

  1. Create the draft Shelf B and add OIL-5W30 and FILTER-01.
  2. Type 2 in the unit can on the OIL-5W30 line.
  3. Type 0 on the FILTER-01 line.
  4. Clear the FILTER-01 counted quantity, then type 0 again.
  5. Receive 2 L of OIL-5W30 by a completed goods receipt. Without typing the line again, read its system quantity. Type 2 in 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 L it was counted against. Typing 2 in can again takes a new system quantity of 12 L, and the line is Short by -2 (counted 10 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

  1. Create Shelf C and Shelf D as drafts, and count the lines as above.
  2. Choose Merge with only Shelf C selected. Read the message.
  3. Select Shelf C and Shelf D and merge; Shelf C is the one kept.
  4. Open Shelf D.
  5. Merge Shelf C and Shelf D again.
  6. 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 C is returned with one OIL-5W30 line counted 10, and FILTER-01 still counted 4; the PLUG-M12 line is uncounted and stays on the count.
  • Step 4: Shelf D is Cancelled, has no lines, and its notes end with “Merged into” and the number of Shelf 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-5W30 10 L (matched) and FILTER-01 4 (matched); PLUG-M12 stays 3 on 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

  1. Create a new draft count with FILTER-01 and choose Cancel count.
  2. Cancel the same count again.
  3. ADVISOR_A1: create a repair order with PLUG-M12 × 6; complete and close it (E2E-INV-008). Check Stock on Hand for PLUG-M12.
  4. MANAGER_A1: open Shelf A (completed) and choose Cancel count. Read the confirmation, then confirm.
  5. Check Stock on Hand for PLUG-M12.

Expected results

  • Step 1: the draft is Cancelled; no stock moves, FILTER-01 stays at 18.
  • Step 2: refused with STOCK_COUNT_ALREADY_CANCELLED.
  • Step 3: PLUG-M12 is 1.
  • Step 4: the confirmation says the count’s adjustments are reversed. The reversal is refused with INSUFFICIENT_STOCK_TO_REVERSE: its 2 cannot be taken back from 1. Shelf A stays 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

  1. MANAGER_A1: open the count and choose Export Excel. Open the file.
  2. 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

  1. Inventory → Write-offs → New write-off. Choose the reason Damaged, type the notes, add OIL-5W30 with 3. Save.
  2. Read the write-off’s value and the line’s unit cost.
  3. Open Stock on Hand for OIL-5W30.
  4. Create the second write-off with 70 of OIL-5W30.
  5. Open the stock movements of OIL-5W30.
  6. Choose Cancel write-off on the first write-off and confirm.
  7. 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 is 300.
  • Step 3: On hand 7 L.
  • Step 4: refused with INSUFFICIENT_STOCK; nothing changes.
  • Step 5: an Out movement of 3 L with the write-off number as its reference, the reason Damaged and the balance 7.
  • Step 6: the write-off is Cancelled; the parts go back at 100.
  • Step 7: On hand 10 L, average cost 100. A second cancel is refused with WRITE_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

  1. Create the return: supplier, the linked completed receipt, credit note number CN-1001, OIL-5W30 × 4 with no cost. Save.
  2. Read the credit amount and the line’s cost.
  3. Open Stock on Hand for OIL-5W30.
  4. Create a second return to the same supplier: OIL-5W30 × 1 at the cost 90. Read the credit amount.
  5. Create a return to QA Supplier Lop Xe Viet linked to the same receipt, and a return of OIL-5W30 × 60 to the first supplier.
  6. 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 is 480.
  • Step 3: On hand 6 L.
  • Step 4: the credit is 90; on hand 5 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 60 is refused with INSUFFICIENT_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 with SUPPLIER_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

  1. MANAGER_A1: open the repair order’s Parts returns and try to return 1.5 L before the stock issue is posted.
  2. Post the stock issue (Issue now) for the order. Stock is now 8 L.
  3. Return 1.5 L with the reason customer declined the extra oil.
  4. Read the return’s cost, and Stock on Hand.
  5. Return 1 L more: total returned 2.5 against 2 issued; read the refusal.
  6. Try to return with the reason left empty.
  7. 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 hand 10 L.
  • Step 2: the issue posts; OIL-5W30 is 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 hand 9.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.5 returned 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

  1. Open Stock on Hand for PLUG-M12 and open its Stock Movements tab.
  2. Open the same tab for OIL-5W30.
  3. 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 number KK-… as its reference, the user who completed the count, and a balance of 7 after it.
  • Step 2: the write-off entry (Out, 3) shows the write-off number XH-… and the reason Damaged, and its balance after it. The repair order return (In, 1.5) shows the reason customer declined the extra oil, has no document number, and its balance after it is 9.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

  1. TECH_A1A2: open Stock on Hand and open the draft count.
  2. 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.
  3. INVENTORY_A1: create a count, count OIL-5W30, create a write-off of 1 L and a return to supplier.
  4. MANAGER_A1: open the draft count and the write-off.

Expected results

  • Step 1: TECH_A1A2 sees On hand 10 L and 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

  1. Catalogue → Parts → BEER-330 → Barcodes. Add 8930001 for the can and 8930024 for the carton. Save.
  2. Open the Barcodes of COLA-330 and try to add 8930001 (and 8930001 typed with other case or spaces around it).
  3. Look up 8930024 as MANAGER_A1 (the scan field of a goods receipt, or GET …/barcodes:lookup?barcode=8930024).
  4. Search the parts list for 8930001.
  5. Try to remove the carton packaging unit from BEER-330, then change its factor to 12.
  6. Deactivate BEER-330 and look up 8930001 again. Then create an unused part TEMP-1, give it the code 8930555, delete TEMP-1, and give 8930555 to COLA-330.
  7. In garage A2, add 8930024 to a part of A2 and look it up from A1.

Expected results

  • Step 1: BEER-330 shows both codes, the second marked with carton.
  • Step 2: refused with BARCODE_ALREADY_USED (409) in each form; COLA-330 has no barcode.
  • Step 3: the part is BEER-330 and the code’s unit is the carton; looking up 8930024 in any letter case answers the same.
  • Step 4: only BEER-330 is 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 isActive false; once TEMP-1 is deleted its code is free, and COLA-330 accepts 8930555.
  • 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

  1. Inventory → Goods receipts → New receipt. Choose the supplier.
  2. In Scan a barcode scan (or type and press Enter) 8930001, then 8930001 again.
  3. Scan 8930024.
  4. Scan 0000000, then 8930099.
  5. Scan 8930024 once more and save the receipt.

Expected results

  • Step 2: one line BEER-330 in cans with quantity 2; the field is empty and ready for the next scan.
  • Step 3: a second line BEER-330 in the carton, quantity 1.
  • 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 adds 48 cans in stock for the carton line and 2 for 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

  1. Web: open the draft count; in Scan a barcode scan 8930011.
  2. Type 18 and press Enter.
  3. Scan 8930010.
  4. Phone: open the count; tap the scan button; read the label of FILTER-01 (or type its code on the scanner screen).
  5. Phone: with a count of more than 50 lines, scan a part near the end of the list.
  6. Phone: on the Inventory list tap the scan button and read a label.

Expected results

  • Step 1: FILTER-01 is 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-5W30 is 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

  1. Inventory → Import from Excel → Parts. Download the template and read the Instructions sheet.
  2. Choose the bad file.
  3. Try to confirm (the web offers no button; through the API, call confirm).
  4. Look at Catalogue → Parts and the units.
  5. Fix the five rows and choose the file again.
  6. Read the preview, then Import 500 rows.
  7. 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 Piece or Case, 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; Piece and Case and the category Filters exist; P-0010 has its base code and a second code for the Case.

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

  1. Confirm the goods receipt file.
  2. Confirm the very same file again.
  3. Change a quantity in the file (so its bytes differ) and confirm it.
  4. Cancel the draft receipt made by step 1 and confirm the original file once more.
  5. 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

  1. Import from Excel → Goods receipt. Choose the first file and confirm.
  2. Open the receipt made.
  3. Choose the second file.
  4. 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-77 and the date 2026-10-01; line 1 is 2 cartons (48 cans) from ACME at 12000 per carton; lines 2 and 3 are in cans from supplier1-1-1. Nothing has moved in stock yet.
  • Step 3: the preview lists HEADER_MISMATCH on the second delivery note, UNKNOWN_SUPPLIER, PART_INACTIVE and UNIT_MISMATCH, each at its row.
  • Step 4: completing posts the movements; on hand is 48 + 5 + 1 = 54 cans, 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

  1. Import from Excel → Stock count. Choose the first file and confirm.
  2. Open the count made.
  3. Choose the second file.

Expected results

  • Step 1: one Draft count named Shelf A with 2 lines.
  • Step 2: BEER-330 is counted 3 cartons (72 cans) against 100 on hand (short by 28); FILTER-01 is counted 0 (short by 20). Nothing was posted; completing the count is the user’s step.
  • Step 3: the preview lists SKU_DUPLICATE for the part named twice and UNIT_MISMATCH for 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

  1. TECH_A1A2: open Inventory; look for Import from Excel; call the preview of each type.
  2. CATALOGUE_A1: preview the parts file, then confirm it.
  3. INVENTORY_A1: preview the parts file (it has catalogue.write, inventory.write, cost access by default), the receipt file and the count file.
  4. As a caller with inventory.write but not inventory.cost.read, preview the receipt file that has unit costs.
  5. 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_REQUIRED error and a confirm is refused with PERMISSION_DENIED (403).
  • Step 2: the preview reports PERMISSION_REQUIRED for the Opening quantity and Unit cost columns; 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_REQUIRED on the Unit cost column; a file without costs is accepted.
  • Step 5: three .xlsx workbooks, 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

  1. Choose a .csv, then a text file renamed .xlsx, then an empty workbook.
  2. Choose a parts workbook without the SKU column.
  3. Choose a parts workbook with 5,001 data rows.
  4. Choose a file larger than 5 MB.
  5. 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 .csv before uploading it; the others are reported as INVALID_FILE or FILE_EMPTY, with no row.
  • Step 2: MISSING_COLUMN for SKU, 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.