Files
ERP-core/Testing/e2e/specs/grn.spec.ts
T
ImanThiyanga 15ddac178c Refactor production and stock tests to remove UOM dependency
- Updated production.spec.ts, stock-adjustments.spec.ts, and stock-transfers.spec.ts to eliminate UOM references in API seeder and test cases.
- Adjusted ApiSeeder methods to remove UOM parameters from stock receiving and production template creation.
- Revised documentation to reflect changes in UOM handling, emphasizing that stock is counted in base UOM only.
- Introduced new enums for MeasureUnit and StageQtyUnit to clarify content size and stage input quantities.
- Implemented ItemContent service to validate and normalize content sizes.
- Updated smoke tests to validate production stage inputs expressed in content units, ensuring correct consumption calculations.
- Modified frontend UOM label handling to reflect the removal of per-line UOMs in document lines.
2026-08-11 11:32:40 +05:30

124 lines
4.1 KiB
TypeScript

import { test, expect, APIRequestContext } from "@playwright/test"
import { ApiSeeder, newApiContext, Vendor, Warehouse, Item } from "../support/api"
import { GrnNewPage, GrnDetailPage } from "../pages/GrnPages"
// GRN receiving flow (Backend/ERPCore/Controllers/GrnsController.cs, Frontend
// app/dashboard/receiving/grn/*). Covers direct + against-PO receipts, confirm posting to
// the stock ledger, and per-line hold-status actions.
test.describe("GRN", () => {
let api: APIRequestContext
let seeder: ApiSeeder
let warehouse: Warehouse
let vendor: Vendor
let item: Item
test.beforeAll(async () => {
api = await newApiContext()
seeder = new ApiSeeder(api)
warehouse = await seeder.firstWarehouse()
vendor = await seeder.createVendor()
item = await seeder.createItem({ namePrefix: "GRN Test Item" })
})
test.afterAll(async () => {
await api.dispose()
})
test("direct receipt creates a Draft GRN, confirm posts stock", async ({ page }) => {
const grnNew = new GrnNewPage(page)
await grnNew.goto()
await grnNew.useDirectReceipt()
await grnNew.selectVendor(vendor.name)
await grnNew.selectWarehouse(warehouse.name)
await grnNew.fillFirstLine({ item: item.name, qty: 10, unitCost: 50 })
await grnNew.submit()
await expect(page).toHaveURL(/\/dashboard\/receiving\/grn\/\d+/)
const grnDetail = new GrnDetailPage(page)
await grnDetail.expectStatus("Draft")
const before = await seeder.stockOnHand(item.itemId, warehouse.warehouseId)
await grnDetail.confirm()
await grnDetail.expectStatus("Confirmed")
const after = await seeder.stockOnHand(item.itemId, warehouse.warehouseId)
expect(after.onHand).toBeCloseTo(before.onHand + 10, 4)
})
test("against-PO receipt pre-fills vendor/warehouse from the PO", async ({ page }) => {
const po = await seeder.createPurchaseOrder({
vendorId: vendor.vendorId,
warehouseId: warehouse.warehouseId,
itemId: item.itemId,
qty: 5,
unitPrice: 40,
})
const grnNew = new GrnNewPage(page)
await grnNew.goto()
await grnNew.useAgainstPo()
await grnNew.selectPurchaseOrder(po.docNo)
await grnNew.fillFirstLine({ item: item.name, qty: 5 })
await grnNew.submit()
await expect(page).toHaveURL(/\/dashboard\/receiving\/grn\/\d+/)
const grnDetail = new GrnDetailPage(page)
await grnDetail.expectStatus("Draft")
})
test("rejecting a Draft GRN's blocked submit (missing warehouse) keeps the user on the form", async ({ page }) => {
const grnNew = new GrnNewPage(page)
await grnNew.goto()
await grnNew.useDirectReceipt()
await grnNew.selectVendor(vendor.name)
// Warehouse intentionally left unselected.
await grnNew.fillFirstLine({ item: item.name, qty: 1, unitCost: 10 })
await grnNew.submit()
await expect(page).toHaveURL(/\/dashboard\/receiving\/grn\/new/)
})
test("releasing an on-hold line clears the hold and makes stock available", async ({ page }) => {
const grn = await seeder.receiveStockOnHold({
warehouseId: warehouse.warehouseId,
vendorId: vendor.vendorId,
itemId: item.itemId,
qty: 8,
unitCost: 12,
})
const grnDetail = new GrnDetailPage(page)
await grnDetail.gotoById(grn.grnId)
await grnDetail.expectStatus("Confirmed")
const before = await seeder.stockOnHand(item.itemId, warehouse.warehouseId)
expect(before.available).toBeLessThan(before.onHand) // held stock is on-hand but not available
await grnDetail.releaseFirstOnHoldLine()
const after = await seeder.stockOnHand(item.itemId, warehouse.warehouseId)
expect(after.available).toBeCloseTo(before.available + 8, 4)
})
test("rejecting an on-hold line surfaces a Create Return link", async ({ page }) => {
const grn = await seeder.receiveStockOnHold({
warehouseId: warehouse.warehouseId,
vendorId: vendor.vendorId,
itemId: item.itemId,
qty: 3,
unitCost: 12,
})
const grnDetail = new GrnDetailPage(page)
await grnDetail.gotoById(grn.grnId)
await grnDetail.expectStatus("Confirmed")
await grnDetail.rejectFirstOnHoldLine()
await grnDetail.expectCreateReturnLink()
})
})