c31e23c2b9
- Introduced Playwright configuration for e2e testing. - Implemented authentication tests to validate login functionality. - Created tests for GRN (Goods Receipt Note) to ensure proper stock handling. - Developed production run tests to verify lifecycle and stock posting. - Added stock transfer tests to check movement between warehouses. - Implemented stock adjustment tests for positive and negative adjustments. - Established API seeder for test data setup and verification. - Enhanced utility functions for UI interactions and response handling.
113 lines
4.8 KiB
TypeScript
113 lines
4.8 KiB
TypeScript
import { test, expect, APIRequestContext } from "@playwright/test"
|
|
import { ApiSeeder, newApiContext, Warehouse, Item, Uom, Vendor } from "../support/api"
|
|
import { GrnNewPage, GrnDetailPage } from "../pages/GrnPages"
|
|
import { ProductionRunListPage, ProductionRunDetailPage } from "../pages/ProductionRunPages"
|
|
import { StockTransferNewPage, StockTransferDetailPage } from "../pages/StockPages"
|
|
|
|
// Full cross-module lifecycle: GRN receipt -> Production consumes the received stock and
|
|
// produces a finished good -> Stock Transfer moves the finished good to a second warehouse.
|
|
// All four modules post to the same StockLayer/StockLedger tables (docs/10 C.9), so this is
|
|
// the scenario most likely to catch a regression in one module's ledger posting breaking
|
|
// another's downstream read - the thing the per-module suites (grn.spec.ts,
|
|
// production.spec.ts, stock-transfers.spec.ts) can't see in isolation.
|
|
test.describe("Chained flow: GRN -> Production -> Stock Transfer", () => {
|
|
let api: APIRequestContext
|
|
let seeder: ApiSeeder
|
|
let sourceWarehouse: Warehouse
|
|
let destWarehouse: Warehouse
|
|
let vendor: Vendor
|
|
let uom: Uom
|
|
let rawItem: Item
|
|
let finishedItem: Item
|
|
let templateName: string
|
|
|
|
test.beforeAll(async () => {
|
|
api = await newApiContext()
|
|
seeder = new ApiSeeder(api)
|
|
sourceWarehouse = await seeder.firstWarehouse()
|
|
destWarehouse = await seeder.secondWarehouse()
|
|
uom = await seeder.firstUom()
|
|
vendor = await seeder.createVendor("Chained Flow Vendor")
|
|
rawItem = await seeder.createItem({ namePrefix: "Chained Raw Material" })
|
|
finishedItem = await seeder.createItem({ namePrefix: "Chained Finished Good" })
|
|
|
|
const template = await seeder.createSingleStageTemplate({
|
|
rawItemId: rawItem.itemId,
|
|
finishedItemId: finishedItem.itemId,
|
|
uomId: uom.uomId,
|
|
})
|
|
templateName = template.name
|
|
})
|
|
|
|
test.afterAll(async () => {
|
|
await api.dispose()
|
|
})
|
|
|
|
test("receive raw material, run production, transfer the finished good", async ({ page }) => {
|
|
// --- 1. GRN: receive the raw material into the source warehouse -----------------
|
|
const grnNew = new GrnNewPage(page)
|
|
await grnNew.goto()
|
|
await grnNew.useDirectReceipt()
|
|
await grnNew.selectVendor(vendor.name)
|
|
await grnNew.selectWarehouse(sourceWarehouse.name)
|
|
await grnNew.fillFirstLine({ item: rawItem.name, uom: uom.name, qty: 100, unitCost: 20 })
|
|
await grnNew.submit()
|
|
|
|
await expect(page).toHaveURL(/\/dashboard\/receiving\/grn\/\d+/)
|
|
const grnDetail = new GrnDetailPage(page)
|
|
await grnDetail.expectStatus("Draft")
|
|
await grnDetail.confirm()
|
|
await grnDetail.expectStatus("Confirmed")
|
|
|
|
const rawAfterGrn = await seeder.stockOnHand(rawItem.itemId, sourceWarehouse.warehouseId)
|
|
expect(rawAfterGrn.onHand).toBeCloseTo(100, 4)
|
|
|
|
// --- 2. Production: consume the raw material, produce the finished good ---------
|
|
const runList = new ProductionRunListPage(page)
|
|
await runList.goto()
|
|
await runList.startRun({ template: templateName, targetQty: 20, warehouse: sourceWarehouse.name })
|
|
|
|
await expect(page).toHaveURL(/\/dashboard\/production\/runs\/\d+/)
|
|
const runDetail = new ProductionRunDetailPage(page)
|
|
await runDetail.expectStatus(/in progress/i)
|
|
|
|
await runDetail.openStage("Assemble")
|
|
await runDetail.saveQuantities()
|
|
await runDetail.startStage()
|
|
|
|
const rawAfterStart = await seeder.stockOnHand(rawItem.itemId, sourceWarehouse.warehouseId)
|
|
expect(rawAfterStart.onHand).toBeLessThan(rawAfterGrn.onHand)
|
|
|
|
await runDetail.completeStage({ producedQty: 20 })
|
|
await runDetail.approveAndReceive()
|
|
|
|
const finishedAfterRun = await seeder.stockOnHand(finishedItem.itemId, sourceWarehouse.warehouseId)
|
|
expect(finishedAfterRun.onHand).toBeCloseTo(20, 4)
|
|
|
|
// --- 3. Stock Transfer: move the finished good to a second warehouse ------------
|
|
const transferNew = new StockTransferNewPage(page)
|
|
await transferNew.goto()
|
|
await transferNew.fill({
|
|
fromWarehouse: sourceWarehouse.name,
|
|
toWarehouse: destWarehouse.name,
|
|
item: finishedItem.name,
|
|
qty: 20,
|
|
})
|
|
await transferNew.submit()
|
|
|
|
await expect(page).toHaveURL(/\/dashboard\/stock\/transfers\/\d+/)
|
|
const transferDetail = new StockTransferDetailPage(page)
|
|
await transferDetail.expectStatus("Draft")
|
|
await transferDetail.dispatch()
|
|
await transferDetail.expectStatus("InTransit")
|
|
await transferDetail.receive()
|
|
await transferDetail.expectStatus("Received")
|
|
|
|
// --- 4. Final assertions across the whole chain ----------------------------------
|
|
const finishedAtSource = await seeder.stockOnHand(finishedItem.itemId, sourceWarehouse.warehouseId)
|
|
const finishedAtDest = await seeder.stockOnHand(finishedItem.itemId, destWarehouse.warehouseId)
|
|
expect(finishedAtSource.onHand).toBeCloseTo(0, 4)
|
|
expect(finishedAtDest.onHand).toBeCloseTo(20, 4)
|
|
})
|
|
})
|