diff --git a/src/api/TransactionService.ts b/src/api/TransactionService.ts index 00b7dd6..8e7a7c1 100644 --- a/src/api/TransactionService.ts +++ b/src/api/TransactionService.ts @@ -45,6 +45,41 @@ class TransactionService extends BaseServiceModel { location — the API counterpart of deleting the top rows on the transaction list page one by one. */ + /** + Deletes every recent transaction that belongs to one of the given orders + (e.g. putaway numbers like "P-887NMH"). Completing a putaway creates a + Transfer Out + Transfer In pair per line, and how many pairs depends on + the putaway lines, so counting "the N newest" rows is fragile; the + transaction details page names its order, which lets us match exactly. + + Deleting one side of a local transfer can remove its pair as well, so a + transaction that no longer shows its order number is simply skipped. + */ + async deleteTransactionsForOrders( + orderNumbers: string[], + scanCount = 30 + ): Promise { + if (orderNumbers.length === 0) { + return; + } + const transactionIds = await this.getRecentTransactionIds(scanCount); + for (const transactionId of transactionIds) { + const response = await this.request.get( + INVENTORY_URL.showTransaction(transactionId) + ); + if (!response.ok()) { + continue; + } + const html = await response.text(); + const belongsToOrder = orderNumbers.some((orderNumber) => + new RegExp(`\\b${orderNumber}\\b`).test(html) + ); + if (belongsToOrder) { + await this.deleteTransaction(transactionId); + } + } + } + async deleteRecentTransactions(count: number): Promise { const transactionIds = await this.getRecentTransactionIds(count); if (transactionIds.length < count) { diff --git a/src/constants/applicationUrls.ts b/src/constants/applicationUrls.ts index c3a218d..8cf8639 100644 --- a/src/constants/applicationUrls.ts +++ b/src/constants/applicationUrls.ts @@ -35,6 +35,8 @@ const INVENTORY_URL = { base: './inventory', listTransactions: (params: { max: number }) => `${INVENTORY_URL.base}/listTransactions?max=${params.max}`, + showTransaction: (id: string) => + `${INVENTORY_URL.base}/showTransaction/${id}`, deleteTransaction: (id: string) => `${INVENTORY_URL.base}/deleteTransaction/${id}`, }; diff --git a/src/pages/receiving/steps/ReceivingStep.ts b/src/pages/receiving/steps/ReceivingStep.ts index ad0069c..d903c43 100644 --- a/src/pages/receiving/steps/ReceivingStep.ts +++ b/src/pages/receiving/steps/ReceivingStep.ts @@ -31,6 +31,19 @@ class ReceivingStep extends BasePageModel { await expect(this.table.table.getByText('Loading...')).toBeHidden({ timeout: 20_000, }); + + // eslint-disable-next-line playwright/no-networkidle + await this.page.waitForLoadState('networkidle'); + } + + async autofillQuantities() { + const autosaveResponse = this.page.waitForResponse( + (response) => + response.url().includes('/items/batch') && + response.request().method() === 'POST' + ); + await this.autofillQuantitiesButton.click(); + await autosaveResponse; } async waitForData() { diff --git a/src/tests/putaway/putawayMoreThan1Item.test.ts b/src/tests/putaway/putawayMoreThan1Item.test.ts index 6614895..5879801 100644 --- a/src/tests/putaway/putawayMoreThan1Item.test.ts +++ b/src/tests/putaway/putawayMoreThan1Item.test.ts @@ -85,15 +85,15 @@ test.describe('Create putaway for more than 1 item, separate putaways', () => { }, testInfo ) => { - const { allPutawaysCompleted } = await cleanupPendingPutaways({ + const { completedPutawayNumbers } = await cleanupPendingPutaways({ putawayService, putawayOrderIds: PUTAWAY_ORDER_IDS, testInfo, }); - if (allPutawaysCompleted) { - await transactionService.deleteRecentTransactions(3); - } + await transactionService.deleteTransactionsForOrders( + completedPutawayNumbers + ); await deleteShipment({ stockMovementService, STOCK_MOVEMENT }); const receivingBin = @@ -345,15 +345,15 @@ test.describe('Putaway 2 items in the same putaway', () => { }, testInfo ) => { - const { allPutawaysCompleted } = await cleanupPendingPutaways({ + const { completedPutawayNumbers } = await cleanupPendingPutaways({ putawayService, putawayOrderIds: PUTAWAY_ORDER_IDS, testInfo, }); - if (allPutawaysCompleted) { - await transactionService.deleteRecentTransactions(2); - } + await transactionService.deleteTransactionsForOrders( + completedPutawayNumbers + ); await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); await stockMovementShowPage.detailsListTable.oldViewShipmentPage.click(); await oldViewShipmentPage.undoStatusChangeButton.click(); diff --git a/src/tests/putaway/putawayToPreferredBin.test.ts b/src/tests/putaway/putawayToPreferredBin.test.ts index 5cec4c7..bc8103f 100644 --- a/src/tests/putaway/putawayToPreferredBin.test.ts +++ b/src/tests/putaway/putawayToPreferredBin.test.ts @@ -109,15 +109,15 @@ test.describe('Putaway to preferred bin and default bin', () => { }, testInfo ) => { - const { allPutawaysCompleted } = await cleanupPendingPutaways({ + const { completedPutawayNumbers } = await cleanupPendingPutaways({ putawayService, putawayOrderIds: PUTAWAY_ORDER_IDS, testInfo, }); - if (allPutawaysCompleted) { - await transactionService.deleteRecentTransactions(2); - } + await transactionService.deleteTransactionsForOrders( + completedPutawayNumbers + ); await deleteShipment({ stockMovementService, STOCK_MOVEMENT }); const product2 = await productService.getProduct(Product.FOUR); diff --git a/src/tests/receiving/assertQtyInputs.test.ts b/src/tests/receiving/assertQtyInputs.test.ts index 2732130..afb409c 100644 --- a/src/tests/receiving/assertQtyInputs.test.ts +++ b/src/tests/receiving/assertQtyInputs.test.ts @@ -83,7 +83,7 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill receiving quantity', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantities(); await expect( receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); diff --git a/src/utils/putawayUtils.ts b/src/utils/putawayUtils.ts index a6e77cc..6a91a17 100644 --- a/src/utils/putawayUtils.ts +++ b/src/utils/putawayUtils.ts @@ -3,20 +3,23 @@ import { TestInfo } from '@playwright/test'; import PutawayService from '@/api/PutawayService'; /** - Deletes the putaway order if it is still in PENDING status. Returns true - when a pending putaway was deleted, false when there was nothing to delete - (the order was completed or no longer exists). + Deletes the putaway order if it is still in PENDING status. Returns whether + a pending putaway was deleted, and the putaway number of an order that was + not pending (completed), so its transactions can be cleaned up. */ async function deletePutawayOrderIfPending( putawayService: PutawayService, putawayOrderId: string -): Promise { +): Promise<{ wasPending: boolean; completedPutawayNumber: string | null }> { const putaway = await putawayService.getPutaway(putawayOrderId); if (putaway?.data?.putawayStatus !== 'PENDING') { - return false; + return { + wasPending: false, + completedPutawayNumber: putaway?.data?.putawayNumber ?? null, + }; } await putawayService.deletePutawayOrder(putawayOrderId); - return true; + return { wasPending: true, completedPutawayNumber: null }; } /** @@ -39,11 +42,15 @@ export async function cleanupPendingPutaways({ }) { testInfo.setTimeout(testInfo.timeout + 60_000); - const deletedPendingPutaways = []; + const deletedPendingPutaways: boolean[] = []; + const completedPutawayNumbers: string[] = []; for (const putawayOrderId of putawayOrderIds) { - deletedPendingPutaways.push( - await deletePutawayOrderIfPending(putawayService, putawayOrderId) - ); + const { wasPending, completedPutawayNumber } = + await deletePutawayOrderIfPending(putawayService, putawayOrderId); + deletedPendingPutaways.push(wasPending); + if (completedPutawayNumber) { + completedPutawayNumbers.push(completedPutawayNumber); + } } return { @@ -53,5 +60,8 @@ export async function cleanupPendingPutaways({ anyPutawayCompleted: deletedPendingPutaways.some( (wasPending) => !wasPending ), + // pass to transactionService.deleteTransactionsForOrders to remove the + // transfer transactions those completed putaways created + completedPutawayNumbers, }; }