import { test, expect, APIRequestContext } from "@playwright/test" import { ApiSeeder, newApiContext, Warehouse, Item, 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 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() 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, }) 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, 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) }) })