diff --git a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts new file mode 100644 index 0000000000..66498be72e --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts @@ -0,0 +1,294 @@ +import { createShippingOptionsWorkflow } from "@medusajs/core-flows" +import { + FulfillmentWorkflow, + IOrderModuleService, + IRegionModuleService, + IStockLocationService, + StockLocationDTO, +} from "@medusajs/types" +import { + ContainerRegistrationKeys, + ModuleRegistrationName, + Modules, + remoteQueryObjectFromString, +} from "@medusajs/utils" + +const providerId = "manual_test-provider" +export async function prepareDataFixtures({ container }) { + const fulfillmentService = container.resolve( + ModuleRegistrationName.FULFILLMENT + ) + const salesChannelService = container.resolve( + ModuleRegistrationName.SALES_CHANNEL + ) + const stockLocationModule: IStockLocationService = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + const productModule = container.resolve(ModuleRegistrationName.PRODUCT) + const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) + + const shippingProfile = await fulfillmentService.createShippingProfiles({ + name: "test", + type: "default", + }) + + const fulfillmentSet = await fulfillmentService.createFulfillmentSets({ + name: "Test fulfillment set", + type: "manual_test", + }) + + const serviceZone = await fulfillmentService.createServiceZones({ + name: "Test service zone", + fulfillment_set_id: fulfillmentSet.id, + geo_zones: [ + { + type: "country", + country_code: "US", + }, + ], + }) + + const regionService = container.resolve( + ModuleRegistrationName.REGION + ) as IRegionModuleService + + const [region] = await regionService.createRegions([ + { + name: "Test region", + currency_code: "eur", + countries: ["fr"], + }, + ]) + + const salesChannel = await salesChannelService.createSalesChannels({ + name: "Webshop", + }) + + const location: StockLocationDTO = + await stockLocationModule.createStockLocations({ + name: "Warehouse", + address: { + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }, + }) + + const [product] = await productModule.createProducts([ + { + title: "Test product", + variants: [ + { + title: "Test variant", + sku: "test-variant", + }, + ], + }, + ]) + + const inventoryItem = await inventoryModule.createInventoryItems({ + sku: "inv-1234", + }) + + await inventoryModule.createInventoryLevels([ + { + inventory_item_id: inventoryItem.id, + location_id: location.id, + stocked_quantity: 2, + reserved_quantity: 0, + }, + ]) + + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.SALES_CHANNEL]: { + sales_channel_id: salesChannel.id, + }, + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[0].id, + }, + [Modules.INVENTORY]: { + inventory_item_id: inventoryItem.id, + }, + }, + ]) + + const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = + { + name: "Shipping option", + price_type: "flat", + service_zone_id: serviceZone.id, + shipping_profile_id: shippingProfile.id, + provider_id: providerId, + type: { + code: "manual-type", + label: "Manual Type", + description: "Manual Type Description", + }, + prices: [ + { + currency_code: "usd", + amount: 10, + }, + { + region_id: region.id, + amount: 100, + }, + ], + } + + const { result } = await createShippingOptionsWorkflow(container).run({ + input: [shippingOptionData], + }) + + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "shipping_option", + variables: { + id: result[0].id, + }, + fields: [ + "id", + "name", + "price_type", + "service_zone_id", + "shipping_profile_id", + "provider_id", + "data", + "metadata", + "type.*", + "created_at", + "updated_at", + "deleted_at", + "shipping_option_type_id", + "prices.*", + ], + }) + + const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + + const [createdShippingOption] = await remoteQuery(remoteQueryObject) + return { + shippingOption: createdShippingOption, + region, + salesChannel, + location, + product, + inventoryItem, + } +} + +export async function createOrderFixture({ + container, + product, + location, + inventoryItem, +}) { + const orderService: IOrderModuleService = container.resolve( + ModuleRegistrationName.ORDER + ) + + let order = await orderService.createOrders({ + region_id: "test_region_idclear", + email: "foo@bar.com", + items: [ + { + title: "Custom Item 2", + variant_sku: product.variants[0].sku, + variant_title: product.variants[0].title, + quantity: 1, + unit_price: 50, + adjustments: [ + { + code: "VIP_25 ETH", + amount: "0.000000000000000005", + description: "VIP discount", + promotion_id: "prom_123", + provider_id: "coupon_kings", + }, + ], + } as any, + ], + transactions: [ + { + amount: 50, + currency_code: "usd", + }, + ], + sales_channel_id: "test", + shipping_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + phone: "12345", + }, + billing_address: { + first_name: "Test", + last_name: "Test", + address_1: "Test", + city: "Test", + country_code: "US", + postal_code: "12345", + }, + shipping_methods: [ + { + name: "Test shipping method", + amount: 10, + data: {}, + tax_lines: [ + { + description: "shipping Tax 1", + tax_rate_id: "tax_usa_shipping", + code: "code", + rate: 10, + }, + ], + adjustments: [ + { + code: "VIP_10", + amount: 1, + description: "VIP discount", + promotion_id: "prom_123", + }, + ], + }, + ], + currency_code: "usd", + customer_id: "joe", + }) + + const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) + await inventoryModule.createReservationItems([ + { + line_item_id: order.items![0].id, + inventory_item_id: inventoryItem.id, + location_id: location.id, + quantity: order.items![0].quantity, + }, + ]) + + order = await orderService.retrieveOrder(order.id, { + relations: ["items"], + }) + + return order +} diff --git a/integration-tests/modules/__tests__/order/workflows/cancel-order.spec.ts b/integration-tests/modules/__tests__/order/workflows/cancel-order.spec.ts index 6bab3d77d5..8ce44a9791 100644 --- a/integration-tests/modules/__tests__/order/workflows/cancel-order.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/cancel-order.spec.ts @@ -2,13 +2,10 @@ import { cancelOrderFulfillmentWorkflow, cancelOrderWorkflow, createOrderFulfillmentWorkflow, - createShippingOptionsWorkflow, } from "@medusajs/core-flows" import { - FulfillmentWorkflow, + InventoryItemDTO, IOrderModuleService, - IRegionModuleService, - IStockLocationService, OrderWorkflow, ProductDTO, RegionDTO, @@ -18,288 +15,14 @@ import { import { ContainerRegistrationKeys, ModuleRegistrationName, - Modules, remoteQueryObjectFromString, } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { createOrderFixture, prepareDataFixtures } from "./__fixtures__" jest.setTimeout(500000) const env = { MEDUSA_FF_MEDUSA_V2: true } -const providerId = "manual_test-provider" -let inventoryItem - -async function prepareDataFixtures({ container }) { - const fulfillmentService = container.resolve( - ModuleRegistrationName.FULFILLMENT - ) - const salesChannelService = container.resolve( - ModuleRegistrationName.SALES_CHANNEL - ) - const stockLocationModule: IStockLocationService = container.resolve( - ModuleRegistrationName.STOCK_LOCATION - ) - const productModule = container.resolve(ModuleRegistrationName.PRODUCT) - const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) - - const shippingProfile = await fulfillmentService.createShippingProfiles({ - name: "test", - type: "default", - }) - - const fulfillmentSet = await fulfillmentService.createFulfillmentSets({ - name: "Test fulfillment set", - type: "manual_test", - }) - - const serviceZone = await fulfillmentService.createServiceZones({ - name: "Test service zone", - fulfillment_set_id: fulfillmentSet.id, - geo_zones: [ - { - type: "country", - country_code: "US", - }, - ], - }) - - const regionService = container.resolve( - ModuleRegistrationName.REGION - ) as IRegionModuleService - - const [region] = await regionService.createRegions([ - { - name: "Test region", - currency_code: "eur", - countries: ["fr"], - }, - ]) - - const salesChannel = await salesChannelService.createSalesChannels({ - name: "Webshop", - }) - - const location: StockLocationDTO = - await stockLocationModule.createStockLocations({ - name: "Warehouse", - address: { - address_1: "Test", - city: "Test", - country_code: "US", - postal_code: "12345", - phone: "12345", - }, - }) - - const [product] = await productModule.createProducts([ - { - title: "Test product", - variants: [ - { - title: "Test variant", - sku: "test-variant", - }, - ], - }, - ]) - - inventoryItem = await inventoryModule.createInventoryItems({ - sku: "inv-1234", - }) - - await inventoryModule.createInventoryLevels([ - { - inventory_item_id: inventoryItem.id, - location_id: location.id, - stocked_quantity: 2, - reserved_quantity: 0, - }, - ]) - - const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) - - await remoteLink.create([ - { - [Modules.STOCK_LOCATION]: { - stock_location_id: location.id, - }, - [Modules.FULFILLMENT]: { - fulfillment_set_id: fulfillmentSet.id, - }, - }, - { - [Modules.SALES_CHANNEL]: { - sales_channel_id: salesChannel.id, - }, - [Modules.STOCK_LOCATION]: { - stock_location_id: location.id, - }, - }, - { - [Modules.PRODUCT]: { - variant_id: product.variants[0].id, - }, - [Modules.INVENTORY]: { - inventory_item_id: inventoryItem.id, - }, - }, - ]) - - const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = - { - name: "Shipping option", - price_type: "flat", - service_zone_id: serviceZone.id, - shipping_profile_id: shippingProfile.id, - provider_id: providerId, - type: { - code: "manual-type", - label: "Manual Type", - description: "Manual Type Description", - }, - prices: [ - { - currency_code: "usd", - amount: 10, - }, - { - region_id: region.id, - amount: 100, - }, - ], - } - - const { result } = await createShippingOptionsWorkflow(container).run({ - input: [shippingOptionData], - }) - - const remoteQueryObject = remoteQueryObjectFromString({ - entryPoint: "shipping_option", - variables: { - id: result[0].id, - }, - fields: [ - "id", - "name", - "price_type", - "service_zone_id", - "shipping_profile_id", - "provider_id", - "data", - "metadata", - "type.*", - "created_at", - "updated_at", - "deleted_at", - "shipping_option_type_id", - "prices.*", - ], - }) - - const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const [createdShippingOption] = await remoteQuery(remoteQueryObject) - return { - shippingOption: createdShippingOption, - region, - salesChannel, - location, - product, - } -} - -async function createOrderFixture({ container, product, location }) { - const orderService: IOrderModuleService = container.resolve( - ModuleRegistrationName.ORDER - ) - let order = await orderService.createOrders({ - region_id: "test_region_idclear", - email: "foo@bar.com", - items: [ - { - title: "Custom Item 2", - variant_sku: product.variants[0].sku, - variant_title: product.variants[0].title, - quantity: 1, - unit_price: 50, - adjustments: [ - { - code: "VIP_25 ETH", - amount: "0.000000000000000005", - description: "VIP discount", - promotion_id: "prom_123", - provider_id: "coupon_kings", - }, - ], - } as any, - ], - transactions: [ - { - amount: 50, - currency_code: "usd", - }, - ], - sales_channel_id: "test", - shipping_address: { - first_name: "Test", - last_name: "Test", - address_1: "Test", - city: "Test", - country_code: "US", - postal_code: "12345", - phone: "12345", - }, - billing_address: { - first_name: "Test", - last_name: "Test", - address_1: "Test", - city: "Test", - country_code: "US", - postal_code: "12345", - }, - shipping_methods: [ - { - name: "Test shipping method", - amount: 10, - data: {}, - tax_lines: [ - { - description: "shipping Tax 1", - tax_rate_id: "tax_usa_shipping", - code: "code", - rate: 10, - }, - ], - adjustments: [ - { - code: "VIP_10", - amount: 1, - description: "VIP discount", - promotion_id: "prom_123", - }, - ], - }, - ], - currency_code: "usd", - customer_id: "joe", - }) - - const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) - const reservation = await inventoryModule.createReservationItems([ - { - line_item_id: order.items![0].id, - inventory_item_id: inventoryItem.id, - location_id: location.id, - quantity: order.items![0].quantity, - }, - ]) - - order = await orderService.retrieveOrder(order.id, { - relations: ["items"], - }) - - return order -} medusaIntegrationTestRunner({ env, @@ -315,7 +38,7 @@ medusaIntegrationTestRunner({ let region: RegionDTO let location: StockLocationDTO let product: ProductDTO - + let inventoryItem: InventoryItemDTO let orderService: IOrderModuleService beforeEach(async () => { @@ -327,12 +50,18 @@ medusaIntegrationTestRunner({ region = fixtures.region location = fixtures.location product = fixtures.product + inventoryItem = fixtures.inventoryItem orderService = container.resolve(ModuleRegistrationName.ORDER) }) it("should cancel an order", async () => { - const order = await createOrderFixture({ container, product, location }) + const order = await createOrderFixture({ + container, + product, + location, + inventoryItem, + }) // Create a fulfillment const createOrderFulfillmentData: OrderWorkflow.CreateOrderFulfillmentWorkflowInput = @@ -355,7 +84,12 @@ medusaIntegrationTestRunner({ }) it("should fail to cancel an order that has fulfilled items", async () => { - const order = await createOrderFixture({ container, product, location }) + const order = await createOrderFixture({ + container, + product, + location, + inventoryItem, + }) // Create a fulfillment const createOrderFulfillmentData: OrderWorkflow.CreateOrderFulfillmentWorkflowInput = diff --git a/integration-tests/modules/__tests__/order/workflows/create-order-change.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-order-change.spec.ts new file mode 100644 index 0000000000..faa216ba7c --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/create-order-change.spec.ts @@ -0,0 +1,74 @@ +import { createOrderChangeWorkflow } from "@medusajs/core-flows" +import { OrderDTO } from "@medusajs/types" +import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { createOrderFixture, prepareDataFixtures } from "./__fixtures__" + +jest.setTimeout(50000) + +medusaIntegrationTestRunner({ + env: { MEDUSA_FF_MEDUSA_V2: true }, + testSuite: ({ getContainer }) => { + let container + + beforeAll(() => { + container = getContainer() + }) + + describe("Order change workflows", () => { + let order: OrderDTO + + describe("createOrderChangeWorkflow", () => { + beforeEach(async () => { + const fixtures = await prepareDataFixtures({ + container, + }) + + order = await createOrderFixture({ + container, + product: fixtures.product, + location: fixtures.location, + inventoryItem: fixtures.inventoryItem, + }) + }) + + it("should successfully create an order change", async () => { + const { result } = await createOrderChangeWorkflow(container).run({ + input: { + order_id: order.id, + }, + }) + + expect(result).toEqual( + expect.objectContaining({ + id: expect.any(String), + order_id: order.id, + }) + ) + }) + + it("should throw an error when creating an order change when an active one already exists", async () => { + await createOrderChangeWorkflow(container).run({ + input: { + order_id: order.id, + }, + }) + + const { + errors: [error], + } = await createOrderChangeWorkflow(container).run({ + input: { + order_id: order.id, + }, + throwOnError: false, + }) + + expect(error.error).toEqual( + expect.objectContaining({ + message: `Order (${order.id}) already has an existing active order change`, + }) + ) + }) + }) + }) + }, +}) diff --git a/integration-tests/modules/jest.config.js b/integration-tests/modules/jest.config.js index e8d2a012fa..753eeca7b0 100644 --- a/integration-tests/modules/jest.config.js +++ b/integration-tests/modules/jest.config.js @@ -12,6 +12,7 @@ module.exports = { `__tests__/fixtures`, `__testfixtures__`, `.cache`, + "__fixtures__", ], transformIgnorePatterns: ["/dist", "/node_modules/"], transform: { diff --git a/packages/core/core-flows/src/order/steps/create-order-change.ts b/packages/core/core-flows/src/order/steps/create-order-change.ts new file mode 100644 index 0000000000..0b49372097 --- /dev/null +++ b/packages/core/core-flows/src/order/steps/create-order-change.ts @@ -0,0 +1,28 @@ +import { CreateOrderChangeDTO, IOrderModuleService } from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +export const createOrderChangeStepId = "create-order-change" +export const createOrderChangeStep = createStep( + createOrderChangeStepId, + async (data: CreateOrderChangeDTO, { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const created = await service.createOrderChange(data) + + return new StepResponse(created, created.id) + }, + async (id, { container }) => { + if (!id) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + await service.deleteOrderChanges(id) + } +) diff --git a/packages/core/core-flows/src/order/steps/index.ts b/packages/core/core-flows/src/order/steps/index.ts index 2f64e2d4d6..299b735019 100644 --- a/packages/core/core-flows/src/order/steps/index.ts +++ b/packages/core/core-flows/src/order/steps/index.ts @@ -4,6 +4,7 @@ export * from "./cancel-exchange" export * from "./cancel-orders" export * from "./cancel-return" export * from "./complete-orders" +export * from "./create-order-change" export * from "./create-orders" export * from "./get-item-tax-lines" export * from "./register-fulfillment" diff --git a/packages/core/core-flows/src/order/workflows/create-order-change.ts b/packages/core/core-flows/src/order/workflows/create-order-change.ts new file mode 100644 index 0000000000..9d76397e6a --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/create-order-change.ts @@ -0,0 +1,11 @@ +import { CreateOrderChangeDTO, OrderChangeDTO } from "@medusajs/types" +import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" +import { createOrderChangeStep } from "../steps" + +export const createOrderChangeWorkflowId = "create-order-change" +export const createOrderChangeWorkflow = createWorkflow( + createOrderChangeWorkflowId, + (input: WorkflowData): WorkflowData => { + return createOrderChangeStep(input) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index 98a565d75b..2d1790b63e 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -4,6 +4,7 @@ export * from "./cancel-order-fulfillment" export * from "./cancel-return" export * from "./complete-orders" export * from "./create-fulfillment" +export * from "./create-order-change" export * from "./create-orders" export * from "./create-return" export * from "./create-shipment" diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index 8122c2bdad..5875e725c7 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -1127,6 +1127,42 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise + /** + * This method deletes order change by its ID. + * + * @param {string[]} orderChangeId - The list of {summary} + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} Resolves when {summary} + * + * @example + * ```typescript + * await orderModuleService.deleteOrderChanges(["orderChangeId1", "orderChangeId2"]); + * ``` + * + */ + deleteOrderChanges( + orderChangeId: string[], + sharedContext?: Context + ): Promise + + /** + * This method deletes order change by its ID. + * + * @param {string} orderChangeId - The order's ID. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} Resolves when {summary} + * + * @example + * ```typescript + * await orderModuleService.deleteOrderChanges("orderChangeId"); + * ``` + * + */ + deleteOrderChanges( + orderChangeId: string, + sharedContext?: Context + ): Promise + /** * This method Represents the completion of an asynchronous operation * diff --git a/packages/modules/order/integration-tests/__tests__/order-edit.ts b/packages/modules/order/integration-tests/__tests__/order-edit.ts index c62e727a73..1f9c632244 100644 --- a/packages/modules/order/integration-tests/__tests__/order-edit.ts +++ b/packages/modules/order/integration-tests/__tests__/order-edit.ts @@ -547,7 +547,7 @@ moduleIntegrationTestRunner({ ]) }) - it("should create order changes, cancel and reject them.", async function () { + it("should create order change, cancel and reject them.", async function () { const createdOrder = await service.createOrders(input) const orderChange = await service.createOrderChange({ @@ -556,6 +556,14 @@ moduleIntegrationTestRunner({ internal_note: "changing the order to version 2", created_by: "user_123", }) + await service.cancelOrderChange({ + id: orderChange.id, + canceled_by: "cx_agent_123", + }) + + await expect(service.cancelOrderChange(orderChange.id)).rejects.toThrow( + "Order Change cannot be modified" + ) const orderChange2 = await service.createOrderChange({ order_id: createdOrder.id, @@ -578,15 +586,6 @@ moduleIntegrationTestRunner({ ], } as CreateOrderChangeDTO) - await service.cancelOrderChange({ - id: orderChange.id, - canceled_by: "cx_agent_123", - }) - - await expect(service.cancelOrderChange(orderChange.id)).rejects.toThrow( - "Order Change cannot be modified" - ) - await service.declineOrderChange({ id: orderChange2.id, declined_by: "user_123", diff --git a/packages/modules/order/src/services/index.ts b/packages/modules/order/src/services/index.ts index cf72196234..7f8cd01cd5 100644 --- a/packages/modules/order/src/services/index.ts +++ b/packages/modules/order/src/services/index.ts @@ -1,3 +1,2 @@ -export { default as OrderChangeService } from "./order-change-service" export { default as OrderModuleService } from "./order-module-service" export { default as OrderService } from "./order-service" diff --git a/packages/modules/order/src/services/order-change-service.ts b/packages/modules/order/src/services/order-change-service.ts deleted file mode 100644 index aea0a85005..0000000000 --- a/packages/modules/order/src/services/order-change-service.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - Context, - DAL, - FindConfig, - OrderTypes, - RepositoryService, -} from "@medusajs/types" -import { - deduplicate, - InjectManager, - InjectTransactionManager, - MedusaContext, - MedusaError, - ModulesSdkUtils, -} from "@medusajs/utils" -import { OrderChange } from "@models" -import { OrderChangeStatus } from "@types" - -type InjectedDependencies = { - orderChangeRepository: DAL.RepositoryService -} - -export default class OrderChangeService extends ModulesSdkUtils.MedusaInternalService< - InjectedDependencies, - OrderChange ->(OrderChange) { - protected readonly orderChangeRepository_: RepositoryService - - constructor(container: InjectedDependencies) { - // @ts-ignore - super(...arguments) - this.orderChangeRepository_ = container.orderChangeRepository - } - - @InjectManager("orderChangeRepository_") - async listCurrentOrderChange( - orderId: string | string[], - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - const allChanges = await super.list( - { order_id: orderId }, - config ?? { - select: ["order_id", "status", "version"], - order: { - order_id: "ASC", - version: "DESC", - }, - } - ) - if (!allChanges.length) { - return [] - } - - const lastChanges: string[] = [] - - const seen = new Set() - for (let i = 0; i < allChanges.length; i++) { - if (seen.has(allChanges[i].order_id)) { - continue - } - seen.add(allChanges[i].order_id) - - if (this.isActive(allChanges[i])) { - lastChanges.push(allChanges[i].id) - } - } - - let orderChange!: OrderChange - if (allChanges?.length > 0) { - if (this.isActive(allChanges[0])) { - orderChange = allChanges[0] - } - } - - if (!orderChange) { - return [] - } - - const relations = deduplicate([...(config.relations ?? []), "actions"]) - config.relations = relations - - const queryConfig = ModulesSdkUtils.buildQuery( - { - id: lastChanges, - order: { - items: { - version: orderChange.version, - }, - }, - }, - config - ) - - return await this.orderChangeRepository_.find(queryConfig, sharedContext) - } - - isActive(orderChange: OrderChange): boolean { - return ( - orderChange.status === OrderChangeStatus.PENDING || - orderChange.status === OrderChangeStatus.REQUESTED - ) - } - - async create( - data: Partial[], - sharedContext?: Context - ): Promise - - async create( - data: Partial, - sharedContext?: Context - ): Promise - - @InjectTransactionManager("orderChangeRepository_") - async create( - data: Partial[] | Partial, - @MedusaContext() sharedContext: Context = {} - ): Promise { - const dataArr = Array.isArray(data) ? data : [data] - const activeOrderEdit = await this.listCurrentOrderChange( - dataArr.map((d) => d.order_id!), - {}, - sharedContext - ) - - if (activeOrderEdit.length > 0) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `An active order change already exists for the order(s) ${activeOrderEdit - .map((a) => a.order_id) - .join(",")}` - ) - } - - return await super.create(dataArr, sharedContext) - } -} diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 78e4cf2525..e6703687d7 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -71,7 +71,6 @@ import { formatOrder, } from "../utils" import * as BundledActions from "./actions" -import OrderChangeService from "./order-change-service" import OrderService from "./order-service" type InjectedDependencies = { @@ -85,7 +84,7 @@ type InjectedDependencies = { lineItemTaxLineService: ModulesSdkTypes.IMedusaInternalService shippingMethodTaxLineService: ModulesSdkTypes.IMedusaInternalService transactionService: ModulesSdkTypes.IMedusaInternalService - orderChangeService: OrderChangeService + orderChangeService: ModulesSdkTypes.IMedusaInternalService orderChangeActionService: ModulesSdkTypes.IMedusaInternalService orderItemService: ModulesSdkTypes.IMedusaInternalService orderSummaryService: ModulesSdkTypes.IMedusaInternalService @@ -176,7 +175,7 @@ export default class OrderModuleService< protected lineItemTaxLineService_: ModulesSdkTypes.IMedusaInternalService protected shippingMethodTaxLineService_: ModulesSdkTypes.IMedusaInternalService protected transactionService_: ModulesSdkTypes.IMedusaInternalService - protected orderChangeService_: OrderChangeService + protected orderChangeService_: ModulesSdkTypes.IMedusaInternalService protected orderChangeActionService_: ModulesSdkTypes.IMedusaInternalService protected orderItemService_: ModulesSdkTypes.IMedusaInternalService protected orderSummaryService_: ModulesSdkTypes.IMedusaInternalService @@ -1741,21 +1740,30 @@ export default class OrderModuleService< @MedusaContext() sharedContext?: Context ): Promise { const dataArr = Array.isArray(data) ? data : [data] - const orderIds: string[] = [] const dataMap: Record = {} + + const orderChanges = await this.listOrderChanges( + { + order_id: dataArr.map((data) => data.order_id), + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + {}, + sharedContext + ) + + const orderChangesMap = new Map( + orderChanges.map((item) => [item.order_id, item]) + ) + for (const change of dataArr) { orderIds.push(change.order_id) dataMap[change.order_id] = change } const orders = await this.listOrders( - { - id: orderIds, - }, - { - select: ["id", "version"], - }, + { id: orderIds }, + { select: ["id", "version"] }, sharedContext ) @@ -1769,6 +1777,15 @@ export default class OrderModuleService< } const input = orders.map((order) => { + const existingOrderChange = orderChangesMap.get(order.id) + + if (existingOrderChange) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Order (${order.id}) already has an existing active order change` + ) + } + return { ...dataMap[order.id], version: order.version + 1,