From 413a0da26c7e9acffe9cc087fd77500efc76191a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Sun, 13 Apr 2025 17:40:16 +0200 Subject: [PATCH] fix(core-flows): draft order reservations (#12115) * fix: draft order reservations * feat: add test case * fix: assert item ids --- .changeset/eleven-readers-film.md | 5 + .../draft-order/admin/draft-order.spec.ts | 173 +++++++++++++++++- .../workflows/confirm-draft-order-edit.ts | 18 +- .../workflows/convert-draft-order.ts | 13 +- 4 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 .changeset/eleven-readers-film.md diff --git a/.changeset/eleven-readers-film.md b/.changeset/eleven-readers-film.md new file mode 100644 index 0000000000..d18ea0a331 --- /dev/null +++ b/.changeset/eleven-readers-film.md @@ -0,0 +1,5 @@ +--- +"@medusajs/core-flows": patch +--- + +fix(core-flows): draft order reservations + emit order placedevent on convert diff --git a/integration-tests/http/__tests__/draft-order/admin/draft-order.spec.ts b/integration-tests/http/__tests__/draft-order/admin/draft-order.spec.ts index b5523f09cb..0efbc0d44f 100644 --- a/integration-tests/http/__tests__/draft-order/admin/draft-order.spec.ts +++ b/integration-tests/http/__tests__/draft-order/admin/draft-order.spec.ts @@ -12,6 +12,8 @@ jest.setTimeout(300000) medusaIntegrationTestRunner({ testSuite: ({ dbConnection, getContainer, api }) => { let region: HttpTypes.AdminRegion + let salesChannel: HttpTypes.AdminSalesChannel + let stockLocation: HttpTypes.AdminStockLocation let testDraftOrder: HttpTypes.AdminDraftOrder beforeEach(async () => { @@ -32,10 +34,32 @@ medusaIntegrationTestRunner({ ) ).data.region + salesChannel = ( + await api.post("/admin/sales-channels", { name: "test" }, adminHeaders) + ).data.sales_channel + + stockLocation = ( + await api.post( + `/admin/stock-locations`, + { name: "test location" }, + adminHeaders + ) + ).data.stock_location + + await api.post( + `/admin/stock-locations/${stockLocation.id}/sales-channels`, + { add: [salesChannel.id] }, + adminHeaders + ) + testDraftOrder = ( await api.post( "/admin/draft-orders", - { email: "test@test.com", region_id: region.id }, + { + email: "test@test.com", + region_id: region.id, + sales_channel_id: salesChannel.id, + }, adminHeaders ) ).data.draft_order @@ -115,5 +139,152 @@ medusaIntegrationTestRunner({ expect(response.data.order.status).toBe("pending") }) }) + + describe("POST /draft-orders/:id/edit/items/:item_id", () => { + let product + + beforeEach(async () => { + const inventoryItem = ( + await api.post( + `/admin/inventory-items`, + { sku: "shirt" }, + adminHeaders + ) + ).data.inventory_item + + await api.post( + `/admin/inventory-items/${inventoryItem.id}/location-levels`, + { + location_id: stockLocation.id, + stocked_quantity: 10, + }, + adminHeaders + ) + + product = ( + await api.post( + "/admin/products", + { + title: "Shirt", + options: [{ title: "size", values: ["large", "small"] }], + variants: [ + { + title: "L shirt", + options: { size: "large" }, + inventory_items: [ + { + inventory_item_id: inventoryItem.id, + required_quantity: 1, + }, + ], + prices: [ + { + currency_code: "usd", + amount: 10, + }, + ], + }, + { + title: "S shirt", + options: { size: "small" }, + inventory_items: [ + { + inventory_item_id: inventoryItem.id, + required_quantity: 1, + }, + ], + prices: [ + { + currency_code: "usd", + amount: 10, + }, + ], + }, + ], + }, + adminHeaders + ) + ).data.product + }) + + it("should create reservations for added items", async () => { + // 1. Create first edit and add items to it + let edit = ( + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit`, + {}, + adminHeaders + ) + ).data.draft_order_preview + + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit/items`, + { + items: [{ variant_id: product.variants[0].id, quantity: 1 }], + }, + adminHeaders + ) + + edit = ( + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit/confirm`, + {}, + adminHeaders + ) + ).data.draft_order_preview + + // Create second edit and add items to it + edit = ( + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit`, + {}, + adminHeaders + ) + ).data.draft_order_preview + + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit/items`, + { + items: [{ variant_id: product.variants[1].id, quantity: 2 }], + }, + adminHeaders + ) + + edit = ( + await api.post( + `/admin/draft-orders/${testDraftOrder.id}/edit/confirm`, + {}, + adminHeaders + ) + ).data.draft_order_preview + + const reservations = ( + await api.get(`/admin/reservations`, adminHeaders) + ).data.reservations + + const lineItem1Id = edit.items.find( + (item) => item.variant_id === product.variants[0].id + )?.id + + const lineItem2Id = edit.items.find( + (item) => item.variant_id === product.variants[1].id + )?.id + + // second edit didn't override the reservations for the first edit + expect(reservations.length).toBe(2) + expect(reservations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + line_item_id: lineItem1Id, + quantity: 1, + }), + expect.objectContaining({ + line_item_id: lineItem2Id, + quantity: 2, + }), + ]) + ) + }) + }) }, }) diff --git a/packages/core/core-flows/src/draft-order/workflows/confirm-draft-order-edit.ts b/packages/core/core-flows/src/draft-order/workflows/confirm-draft-order-edit.ts index 2a74e810ab..e1f5d9a62e 100644 --- a/packages/core/core-flows/src/draft-order/workflows/confirm-draft-order-edit.ts +++ b/packages/core/core-flows/src/draft-order/workflows/confirm-draft-order-edit.ts @@ -114,19 +114,25 @@ export const confirmDraftOrderEditWorkflow = createWorkflow( throw_if_key_not_found: true, }).config({ name: "order-items-query" }) - const lineItemIds = transform( + const { removedLineItemIds } = transform( { orderItems, previousOrderItems: order.items }, - (data) => { const previousItemIds = (data.previousOrderItems || []).map( ({ id }) => id - ) // items that have been removed with the change - const newItemIds = data.orderItems.items.map(({ id }) => id) - return [...new Set([...previousItemIds, ...newItemIds])] + ) + const currentItemIds = data.orderItems.items.map(({ id }) => id) + + const removedItemIds = previousItemIds.filter( + (id) => !currentItemIds.includes(id) + ) + + return { + removedLineItemIds: removedItemIds, + } } ) - deleteReservationsByLineItemsStep(lineItemIds) + deleteReservationsByLineItemsStep(removedLineItemIds) const { variants, items } = transform( { orderItems, orderPreview }, diff --git a/packages/core/core-flows/src/draft-order/workflows/convert-draft-order.ts b/packages/core/core-flows/src/draft-order/workflows/convert-draft-order.ts index b9cda1b4fa..a2285e089b 100644 --- a/packages/core/core-flows/src/draft-order/workflows/convert-draft-order.ts +++ b/packages/core/core-flows/src/draft-order/workflows/convert-draft-order.ts @@ -1,4 +1,8 @@ -import { Modules, OrderStatus } from "@medusajs/framework/utils" +import { + Modules, + OrderStatus, + OrderWorkflowEvents, +} from "@medusajs/framework/utils" import { createStep, createWorkflow, @@ -7,7 +11,7 @@ import { WorkflowResponse, } from "@medusajs/framework/workflows-sdk" import { IOrderModuleService, OrderDTO } from "@medusajs/types" -import { useRemoteQueryStep } from "../../common" +import { emitEventStep, useRemoteQueryStep } from "../../common" import { validateDraftOrderStep } from "../steps/validate-draft-order" const convertDraftOrderWorkflowId = "convert-draft-order" @@ -91,6 +95,11 @@ export const convertDraftOrderWorkflow = createWorkflow( const updatedOrder = convertDraftOrderStep({ id: input.id }) + emitEventStep({ + eventName: OrderWorkflowEvents.PLACED, + data: { id: updatedOrder.id }, + }) + return new WorkflowResponse(updatedOrder) } )