diff --git a/integration-tests/modules/__tests__/order/order.spec.ts b/integration-tests/modules/__tests__/order/order.spec.ts index cbfb0e3396..861449245d 100644 --- a/integration-tests/modules/__tests__/order/order.spec.ts +++ b/integration-tests/modules/__tests__/order/order.spec.ts @@ -60,7 +60,7 @@ medusaIntegrationTestRunner({ describe("Orders - Admin", () => { it("should get an order", async () => { const created = await orderModule.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { diff --git a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts index 66498be72e..2bdbe15dde 100644 --- a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts +++ b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts @@ -205,7 +205,7 @@ export async function createOrderFixture({ ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts index 22ae02853d..3a9793591e 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts @@ -1,6 +1,7 @@ import { beginClaimOrderWorkflow, createShippingOptionsWorkflow, + orderClaimRequestItemReturnWorkflow, } from "@medusajs/core-flows" import { FulfillmentWorkflow, @@ -232,7 +233,7 @@ async function createOrderFixture({ container, product }) { ModuleRegistrationName.ORDER ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { @@ -382,11 +383,56 @@ medusaIntegrationTestRunner({ fields: ["order_id", "id", "type"], }) - const [returnOrder] = await remoteQuery(remoteQueryObject) + const [claimOrder] = await remoteQuery(remoteQueryObject) - expect(returnOrder.order_id).toEqual(order.id) - expect(returnOrder.type).toEqual("refund") - expect(returnOrder.id).toBeDefined() + expect(claimOrder.order_id).toEqual(order.id) + expect(claimOrder.type).toEqual("refund") + expect(claimOrder.id).toBeDefined() + + // Request itemm to return + const { result: preview } = + await orderClaimRequestItemReturnWorkflow.run({ + throwOnError: true, + container, + input: { + claim_id: claimOrder.id, + items: [ + { + id: order.items![0].id, + quantity: 1, + }, + ], + }, + }) + + expect(preview.items[0]).toEqual( + expect.objectContaining({ + quantity: 1, + id: order.items![0].id, + detail: expect.objectContaining({ + quantity: 1, + fulfilled_quantity: 1, + return_requested_quantity: 1, + }), + actions: [ + expect.objectContaining({ + order_id: order.id, + claim_id: claimOrder.id, + version: 2, + order_change_id: expect.any(String), + reference: "return", + reference_id: expect.stringContaining("return_"), + action: "RETURN_ITEM", + details: expect.objectContaining({ + quantity: 1, + return_id: expect.stringContaining("return_"), + claim_id: claimOrder.id, + reference_id: order.items![0].id, + }), + }), + ], + }) + ) }) }) }, diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts index a79bf41722..2c4a1ac2ec 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts @@ -1,10 +1,10 @@ import { beginExchangeOrderWorkflow, createShippingOptionsWorkflow, + orderExchangeRequestItemReturnWorkflow, } from "@medusajs/core-flows" import { FulfillmentWorkflow, - IOrderModuleService, IRegionModuleService, IStockLocationService, OrderWorkflow, @@ -227,18 +227,16 @@ async function prepareDataFixtures({ container }) { } async function createOrderFixture({ container, product }) { - const orderService: IOrderModuleService = container.resolve( - ModuleRegistrationName.ORDER - ) + const orderService = container.resolve(ModuleRegistrationName.ORDER) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { title: "Custom Item 2", variant_sku: product.variants[0].sku, variant_title: product.variants[0].title, - quantity: 1, + quantity: 5, unit_price: 50, adjustments: [ { @@ -251,12 +249,6 @@ async function createOrderFixture({ container, product }) { ], } as any, ], - transactions: [ - { - amount: 50, - currency_code: "usd", - }, - ], sales_channel_id: "test", shipping_address: { first_name: "Test", @@ -311,7 +303,7 @@ async function createOrderFixture({ container, product }) { reference_id: "fulfill_123", details: { reference_id: order.items![0].id, - quantity: 1, + quantity: 3, }, }, { @@ -356,7 +348,7 @@ medusaIntegrationTestRunner({ product = fixtures.product }) - it("should begin an exchange order", async () => { + it("should begin an exchange order, request item return", async () => { const order = await createOrderFixture({ container, product }) const createExchangeOrderData: OrderWorkflow.beginOrderExchangeWorkflowInput = @@ -369,9 +361,9 @@ medusaIntegrationTestRunner({ }, } - await beginExchangeOrderWorkflow(container).run({ + await beginExchangeOrderWorkflow.run({ + container, input: createExchangeOrderData, - throwOnError: true, }) const remoteQuery = container.resolve( @@ -385,15 +377,119 @@ medusaIntegrationTestRunner({ fields: ["order_id", "id", "metadata"], }) - const [returnOrder] = await remoteQuery(remoteQueryObject) + const [exchangeOrder] = await remoteQuery(remoteQueryObject) - expect(returnOrder.order_id).toEqual(order.id) - expect(returnOrder.metadata).toEqual({ + expect(exchangeOrder.order_id).toEqual(order.id) + expect(exchangeOrder.metadata).toEqual({ reason: "test", extra: "data", value: 1234, }) - expect(returnOrder.id).toBeDefined() + expect(exchangeOrder.id).toBeDefined() + + // Request itemm to return + const { result: preview } = + await orderExchangeRequestItemReturnWorkflow.run({ + throwOnError: true, + container, + input: { + exchange_id: exchangeOrder.id, + items: [ + { + id: order.items![0].id, + quantity: 1, + }, + ], + }, + }) + + expect(preview.items[0]).toEqual( + expect.objectContaining({ + quantity: 5, + id: order.items![0].id, + detail: expect.objectContaining({ + quantity: 5, + fulfilled_quantity: 3, + return_requested_quantity: 1, + }), + actions: [ + expect.objectContaining({ + order_id: order.id, + exchange_id: exchangeOrder.id, + version: 2, + order_change_id: expect.any(String), + reference: "return", + reference_id: expect.stringContaining("return_"), + action: "RETURN_ITEM", + details: expect.objectContaining({ + quantity: 1, + return_id: expect.stringContaining("return_"), + exchange_id: exchangeOrder.id, + reference_id: order.items![0].id, + }), + }), + ], + }) + ) + + const { errors } = await orderExchangeRequestItemReturnWorkflow.run({ + throwOnError: false, + container, + input: { + exchange_id: exchangeOrder.id, + items: [ + { + id: order.items![0].id, + quantity: 3, + }, + ], + }, + }) + expect(errors[0].error.message).toEqual( + `Cannot request to return more items than what was fulfilled for item ${ + order.items![0].id + }.` + ) + + const { result: anotherPreview } = + await orderExchangeRequestItemReturnWorkflow.run({ + container, + input: { + exchange_id: exchangeOrder.id, + items: [ + { + id: order.items![0].id, + quantity: 2, + }, + ], + }, + }) + + expect(anotherPreview.items[0]).toEqual( + expect.objectContaining({ + detail: expect.objectContaining({ + quantity: 5, + fulfilled_quantity: 3, + return_requested_quantity: 3, + }), + actions: [ + expect.objectContaining({ + reference_id: preview.items[0].actions[0].reference_id, + action: "RETURN_ITEM", + details: expect.objectContaining({ + quantity: 1, + }), + }), + expect.objectContaining({ + reference_id: preview.items[0].actions[0].reference_id, + action: "RETURN_ITEM", + details: expect.objectContaining({ + quantity: 2, + }), + }), + ], + }) + ) }) }) }, diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts index cd0b476295..8b8fa369fe 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts @@ -231,7 +231,7 @@ async function createOrderFixture({ container, product }) { ModuleRegistrationName.ORDER ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { diff --git a/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts index 94d5def0db..2b7295712d 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts @@ -235,7 +235,7 @@ async function createOrderFixture({ container, product }) { ModuleRegistrationName.ORDER ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { @@ -434,7 +434,7 @@ medusaIntegrationTestRunner({ expect.objectContaining({ id: expect.any(String), display_id: 1, - region_id: "test_region_idclear", + region_id: "test_region_id", customer_id: "joe", version: 2, sales_channel_id: "test", // TODO: What about order with a sales channel but a shipping option link to a stock from another channel? diff --git a/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts index 8de1b3a82e..5aae561202 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts @@ -217,7 +217,7 @@ async function createOrderFixture({ container, product, location }) { ModuleRegistrationName.ORDER ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { diff --git a/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts index 89c836df22..0c648664cf 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts @@ -220,7 +220,7 @@ async function createOrderFixture({ container, product, location }) { ModuleRegistrationName.ORDER ) let order = await orderService.createOrders({ - region_id: "test_region_idclear", + region_id: "test_region_id", email: "foo@bar.com", items: [ { diff --git a/packages/core/core-flows/src/order/steps/index.ts b/packages/core/core-flows/src/order/steps/index.ts index 74c13e4911..c6a681f339 100644 --- a/packages/core/core-flows/src/order/steps/index.ts +++ b/packages/core/core-flows/src/order/steps/index.ts @@ -16,8 +16,10 @@ export * from "./decline-order-change" export * from "./delete-order-change" export * from "./delete-order-change-actions" export * from "./get-item-tax-lines" +export * from "./preview-order-change" export * from "./register-fulfillment" export * from "./register-shipment" export * from "./set-tax-lines-for-items" export * from "./update-order-change-actions" +export * from "./update-order-exchanges" export * from "./update-tax-lines" diff --git a/packages/core/core-flows/src/order/steps/preview-order-change.ts b/packages/core/core-flows/src/order/steps/preview-order-change.ts new file mode 100644 index 0000000000..23cc0d23af --- /dev/null +++ b/packages/core/core-flows/src/order/steps/preview-order-change.ts @@ -0,0 +1,17 @@ +import { IOrderModuleService } from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +export const previewOrderChangeStepId = "preview-order-change" +export const previewOrderChangeStep = createStep( + previewOrderChangeStepId, + async (orderId: string, { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const preview = await service.previewOrderChange(orderId) + + return new StepResponse(preview) + } +) diff --git a/packages/core/core-flows/src/order/steps/update-order-claims.ts b/packages/core/core-flows/src/order/steps/update-order-claims.ts new file mode 100644 index 0000000000..23ac9b8277 --- /dev/null +++ b/packages/core/core-flows/src/order/steps/update-order-claims.ts @@ -0,0 +1,55 @@ +import { IOrderModuleService, UpdateOrderClaimDTO } from "@medusajs/types" +import { + ModuleRegistrationName, + getSelectsAndRelationsFromObjectArray, +} from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +export const updateOrderClaimsStepId = "update-order-claim" +export const updateOrderClaimsStep = createStep( + updateOrderClaimsStepId, + async (data: UpdateOrderClaimDTO[], { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const { selects, relations } = getSelectsAndRelationsFromObjectArray(data, { + objectFields: ["metadata"], + }) + const dataBeforeUpdate = await service.listOrderClaims( + { id: data.map((d) => d.id) }, + { relations, select: selects } + ) + + const updated = await service.updateOrderClaims( + data.map((dt) => { + const { id, ...rest } = dt + return { + selector: { id }, + data: rest, + } + }) + ) + + return new StepResponse(updated, dataBeforeUpdate) + }, + async (dataBeforeUpdate, { container }) => { + if (!dataBeforeUpdate?.length) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + await service.updateOrderClaims( + dataBeforeUpdate.map((dt) => { + const { id, ...rest } = dt + return { + selector: { id }, + data: rest, + } + }) + ) + } +) diff --git a/packages/core/core-flows/src/order/steps/update-order-exchanges.ts b/packages/core/core-flows/src/order/steps/update-order-exchanges.ts new file mode 100644 index 0000000000..b949b5d860 --- /dev/null +++ b/packages/core/core-flows/src/order/steps/update-order-exchanges.ts @@ -0,0 +1,55 @@ +import { IOrderModuleService, UpdateOrderExchangeDTO } from "@medusajs/types" +import { + ModuleRegistrationName, + getSelectsAndRelationsFromObjectArray, +} from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +export const updateOrderExchangesStepId = "update-order-exchange" +export const updateOrderExchangesStep = createStep( + updateOrderExchangesStepId, + async (data: UpdateOrderExchangeDTO[], { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const { selects, relations } = getSelectsAndRelationsFromObjectArray(data, { + objectFields: ["metadata"], + }) + const dataBeforeUpdate = await service.listOrderExchanges( + { id: data.map((d) => d.id) }, + { relations, select: selects } + ) + + const updated = await service.updateOrderExchanges( + data.map((dt) => { + const { id, ...rest } = dt + return { + selector: { id }, + data: rest, + } + }) + ) + + return new StepResponse(updated, dataBeforeUpdate) + }, + async (dataBeforeUpdate, { container }) => { + if (!dataBeforeUpdate?.length) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + await service.updateOrderExchanges( + dataBeforeUpdate.map((dt) => { + const { id, ...rest } = dt + return { + selector: { id }, + data: rest, + } + }) + ) + } +) diff --git a/packages/core/core-flows/src/order/utils/order-validation.ts b/packages/core/core-flows/src/order/utils/order-validation.ts index a7f24e2a8a..b544b84913 100644 --- a/packages/core/core-flows/src/order/utils/order-validation.ts +++ b/packages/core/core-flows/src/order/utils/order-validation.ts @@ -41,15 +41,14 @@ export function throwIfItemsDoesNotExistsInOrder({ } } -export function throwIfReturnIsCancelled({ - orderReturn, -}: { - orderReturn: ReturnDTO -}) { - if (orderReturn.canceled_at) { +export function throwIfIsCancelled( + obj: unknown & { id: string; canceled_at?: any }, + type: string +) { + if (obj.canceled_at) { throw new MedusaError( MedusaError.Types.INVALID_DATA, - `return with id ${orderReturn.id} has been canceled.` + `${type} with id ${obj.id} has been canceled.` ) } } diff --git a/packages/core/core-flows/src/order/workflows/cancel-return.ts b/packages/core/core-flows/src/order/workflows/cancel-return.ts index 332213642d..f0f82f39e9 100644 --- a/packages/core/core-flows/src/order/workflows/cancel-return.ts +++ b/packages/core/core-flows/src/order/workflows/cancel-return.ts @@ -12,7 +12,7 @@ import { } from "@medusajs/workflows-sdk" import { useRemoteQueryStep } from "../../common" import { cancelOrderReturnStep } from "../steps" -import { throwIfReturnIsCancelled } from "../utils/order-validation" +import { throwIfIsCancelled } from "../utils/order-validation" const validateOrder = createStep( "validate-return", @@ -27,7 +27,7 @@ const validateOrder = createStep( fulfillments: FulfillmentDTO[] } - throwIfReturnIsCancelled({ orderReturn }) + throwIfIsCancelled(orderReturn, "Return") const throwErrorIf = ( arr: unknown[], diff --git a/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts b/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts new file mode 100644 index 0000000000..41991259b5 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts @@ -0,0 +1,163 @@ +import { + OrderChangeDTO, + OrderClaimDTO, + OrderDTO, + OrderWorkflow, + ReturnDTO, +} from "@medusajs/types" +import { ChangeActionType } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + transform, + when, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" +import { createReturnsStep } from "../steps/create-returns" +import { previewOrderChangeStep } from "../steps/preview-order-change" +import { updateOrderClaimsStep } from "../steps/update-order-claims" +import { + throwIfIsCancelled, + throwIfItemsDoesNotExistsInOrder, + throwIfOrderChangeIsNotActive, + throwIfOrderIsCancelled, +} from "../utils/order-validation" + +const validationStep = createStep( + "claim-request-item-return-validation", + async function ({ + order, + orderChange, + orderReturn, + orderClaim, + items, + }: { + order: OrderDTO + orderReturn: ReturnDTO + orderClaim: OrderClaimDTO + orderChange: OrderChangeDTO + items: OrderWorkflow.OrderClaimRequestItemReturnWorkflowInput["items"] + }) { + throwIfOrderIsCancelled({ order }) + throwIfIsCancelled(orderClaim, "Claim") + throwIfIsCancelled(orderReturn, "Return") + throwIfOrderChangeIsNotActive({ orderChange }) + throwIfItemsDoesNotExistsInOrder({ order, inputItems: items }) + } +) + +export const orderClaimRequestItemReturnWorkflowId = "claim-request-item-return" +export const orderClaimRequestItemReturnWorkflow = createWorkflow( + orderClaimRequestItemReturnWorkflowId, + function ( + input: WorkflowData + ): WorkflowData { + const orderClaim = useRemoteQueryStep({ + entry_point: "order_claim", + fields: ["id", "order_id", "return_id"], + variables: { id: input.claim_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "claim-query" }) + + const existingOrderReturn = when({ orderClaim }, ({ orderClaim }) => { + return orderClaim.return_id + }).then(() => { + return useRemoteQueryStep({ + entry_point: "return", + fields: ["id", "status", "order_id"], + variables: { id: orderClaim.return_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "return-query" }) as ReturnDTO + }) + + const createdReturn = when({ orderClaim }, ({ orderClaim }) => { + return !orderClaim.return_id + }).then(() => { + return createReturnsStep([ + { + order_id: orderClaim.order_id, + claim_id: orderClaim.id, + }, + ]) + }) + + const orderReturn: ReturnDTO = transform( + { createdReturn, existingOrderReturn, orderClaim }, + ({ createdReturn, existingOrderReturn, orderClaim }) => { + return existingOrderReturn ?? (createdReturn[0] as ReturnDTO) + } + ) + + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "items.*"], + variables: { id: orderClaim.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status"], + variables: { order_id: orderClaim.order_id }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ + order, + items: input.items, + orderClaim, + orderReturn, + orderChange, + }) + + when({ orderClaim }, ({ orderClaim }) => { + return !orderClaim.return_id + }).then(() => { + const createdReturnId = transform( + { createdReturn }, + ({ createdReturn }) => { + return createdReturn[0]!.id + } + ) + updateOrderClaimsStep([ + { + id: orderClaim.id, + return_id: createdReturnId, + }, + ]) + }) + + const orderChangeActionInput = transform( + { order, orderChange, orderClaim, orderReturn, items: input.items }, + ({ order, orderChange, orderClaim, orderReturn, items }) => { + return items.map((item) => ({ + order_change_id: orderChange.id, + order_id: order.id, + claim_id: orderClaim.id, + return_id: orderReturn.id, + version: orderChange.version, + action: ChangeActionType.RETURN_ITEM, + internal_note: item.internal_note, + reference: "return", + reference_id: orderReturn.id, + details: { + reference_id: item.id, + claim_id: orderClaim.id, + return_id: orderReturn.id, + quantity: item.quantity, + metadata: item.metadata, + }, + })) + } + ) + + createOrderChangeActionsStep(orderChangeActionInput) + + return previewOrderChangeStep(orderClaim.order_id) + } +) diff --git a/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts b/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts new file mode 100644 index 0000000000..45defbe778 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts @@ -0,0 +1,164 @@ +import { + OrderChangeDTO, + OrderDTO, + OrderExchangeDTO, + OrderWorkflow, + ReturnDTO, +} from "@medusajs/types" +import { ChangeActionType } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + transform, + when, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" +import { createReturnsStep } from "../steps/create-returns" +import { previewOrderChangeStep } from "../steps/preview-order-change" +import { updateOrderExchangesStep } from "../steps/update-order-exchanges" +import { + throwIfIsCancelled, + throwIfItemsDoesNotExistsInOrder, + throwIfOrderChangeIsNotActive, + throwIfOrderIsCancelled, +} from "../utils/order-validation" + +const validationStep = createStep( + "exchange-request-item-return-validation", + async function ({ + order, + orderChange, + orderReturn, + orderExchange, + items, + }: { + order: OrderDTO + orderReturn: ReturnDTO + orderExchange: OrderExchangeDTO + orderChange: OrderChangeDTO + items: OrderWorkflow.OrderExchangeRequestItemReturnWorkflowInput["items"] + }) { + throwIfOrderIsCancelled({ order }) + throwIfIsCancelled(orderExchange, "Exchange") + throwIfIsCancelled(orderReturn, "Return") + throwIfOrderChangeIsNotActive({ orderChange }) + throwIfItemsDoesNotExistsInOrder({ order, inputItems: items }) + } +) + +export const orderExchangeRequestItemReturnWorkflowId = + "exchange-request-item-return" +export const orderExchangeRequestItemReturnWorkflow = createWorkflow( + orderExchangeRequestItemReturnWorkflowId, + function ( + input: WorkflowData + ): WorkflowData { + const orderExchange = useRemoteQueryStep({ + entry_point: "order_exchange", + fields: ["id", "order_id", "return_id"], + variables: { id: input.exchange_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "exchange-query" }) + + const existingOrderReturn = when({ orderExchange }, ({ orderExchange }) => { + return orderExchange.return_id + }).then(() => { + return useRemoteQueryStep({ + entry_point: "return", + fields: ["id", "status", "order_id"], + variables: { id: orderExchange.return_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "return-query" }) as ReturnDTO + }) + + const createdReturn = when({ orderExchange }, ({ orderExchange }) => { + return !orderExchange.return_id + }).then(() => { + return createReturnsStep([ + { + order_id: orderExchange.order_id, + exchange_id: orderExchange.id, + }, + ]) + }) + + const orderReturn: ReturnDTO = transform( + { createdReturn, existingOrderReturn, orderExchange }, + ({ createdReturn, existingOrderReturn, orderExchange }) => { + return existingOrderReturn ?? (createdReturn[0] as ReturnDTO) + } + ) + + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "items.*"], + variables: { id: orderExchange.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status"], + variables: { order_id: orderExchange.order_id }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ + order, + items: input.items, + orderExchange, + orderReturn, + orderChange, + }) + + when({ orderExchange }, ({ orderExchange }) => { + return !orderExchange.return_id + }).then(() => { + const createdReturnId = transform( + { createdReturn }, + ({ createdReturn }) => { + return createdReturn[0]!.id + } + ) + updateOrderExchangesStep([ + { + id: orderExchange.id, + return_id: createdReturnId, + }, + ]) + }) + + const orderChangeActionInput = transform( + { order, orderChange, orderExchange, orderReturn, items: input.items }, + ({ order, orderChange, orderExchange, orderReturn, items }) => { + return items.map((item) => ({ + order_change_id: orderChange.id, + order_id: order.id, + exchange_id: orderExchange.id, + return_id: orderReturn.id, + version: orderChange.version, + action: ChangeActionType.RETURN_ITEM, + internal_note: item.internal_note, + reference: "return", + reference_id: orderReturn.id, + details: { + reference_id: item.id, + exchange_id: orderExchange.id, + return_id: orderReturn.id, + quantity: item.quantity, + metadata: item.metadata, + }, + })) + } + ) + + createOrderChangeActionsStep(orderChangeActionInput) + + return previewOrderChangeStep(orderExchange.order_id) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index b3321147d4..edf2121309 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -6,6 +6,7 @@ export * from "./cancel-order" export * from "./cancel-order-change" export * from "./cancel-order-fulfillment" export * from "./cancel-return" +export * from "./claim-request-item-return" export * from "./complete-orders" export * from "./create-complete-return" export * from "./create-fulfillment" @@ -16,6 +17,7 @@ export * from "./create-shipment" export * from "./decline-order-change" export * from "./delete-order-change" export * from "./delete-order-change-actions" +export * from "./exchange-request-item-return" export * from "./get-order-detail" export * from "./get-orders-list" export * from "./receive-return" diff --git a/packages/core/core-flows/src/order/workflows/receive-return.ts b/packages/core/core-flows/src/order/workflows/receive-return.ts index bb6735ce00..ee4dcdb4c5 100644 --- a/packages/core/core-flows/src/order/workflows/receive-return.ts +++ b/packages/core/core-flows/src/order/workflows/receive-return.ts @@ -9,8 +9,8 @@ import { useRemoteQueryStep } from "../../common" import { ReturnDTO } from "@medusajs/types" import { receiveReturnStep } from "../steps/receive-return" import { + throwIfIsCancelled, throwIfItemsDoesNotExistsInReturn, - throwIfReturnIsCancelled, } from "../utils/order-validation" const validationStep = createStep( @@ -25,7 +25,7 @@ const validationStep = createStep( }, context ) { - throwIfReturnIsCancelled({ orderReturn }) + throwIfIsCancelled(orderReturn, "Return") throwIfItemsDoesNotExistsInReturn({ orderReturn, inputItems: input.items }) } ) diff --git a/packages/core/core-flows/src/order/workflows/request-item-return.ts b/packages/core/core-flows/src/order/workflows/request-item-return.ts index 97e494b794..e374bea1c1 100644 --- a/packages/core/core-flows/src/order/workflows/request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/request-item-return.ts @@ -15,10 +15,10 @@ import { import { useRemoteQueryStep } from "../../common" import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" import { + throwIfIsCancelled, throwIfItemsDoesNotExistsInOrder, throwIfOrderChangeIsNotActive, throwIfOrderIsCancelled, - throwIfReturnIsCancelled, } from "../utils/order-validation" const validationStep = createStep( @@ -35,7 +35,7 @@ const validationStep = createStep( items: OrderWorkflow.RequestItemReturnWorkflowInput["items"] }) { throwIfOrderIsCancelled({ order }) - throwIfReturnIsCancelled({ orderReturn }) + throwIfIsCancelled(orderReturn, "Return") throwIfOrderChangeIsNotActive({ orderChange }) throwIfItemsDoesNotExistsInOrder({ order, inputItems: items }) } @@ -49,7 +49,7 @@ export const requestItemReturnWorkflow = createWorkflow( ): WorkflowData { const orderReturn: ReturnDTO = useRemoteQueryStep({ entry_point: "return", - fields: ["id", "status", "refund_amount", "order_id", "items.*"], + fields: ["id", "status", "order_id"], variables: { id: input.return_id }, list: false, throw_if_key_not_found: true, diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index a37be7348e..06ef8740b3 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -326,6 +326,7 @@ export interface CreateOrderChangeActionDTO { export interface UpdateOrderChangeActionDTO { id: string internal_note?: string | null + metadata?: Record | null } /** ORDER TRANSACTION START */ @@ -436,7 +437,8 @@ export interface CreateOrderReturnDTO extends BaseOrderBundledActionsDTO { exchange_id?: string } -export interface UpdateOrderReturnDTO { +export interface UpdateReturnDTO { + id: string refund_amount?: BigNumberInput no_notification?: boolean claim_id?: string @@ -445,6 +447,7 @@ export interface UpdateOrderReturnDTO { } export interface UpdateOrderClaimDTO { + id: string refund_amount?: BigNumberInput no_notification?: boolean return_id?: string @@ -453,6 +456,7 @@ export interface UpdateOrderClaimDTO { } export interface UpdateOrderExchangeDTO { + id: string difference_due?: BigNumberInput no_notification?: boolean return_id?: string @@ -462,7 +466,7 @@ export interface UpdateOrderExchangeDTO { export interface UpdateOrderReturnWithSelectorDTO { selector: Partial - data: Partial + data: Partial } export interface UpdateOrderClaimWithSelectorDTO { diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index 793e462f88..7ad5fb75da 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -75,12 +75,12 @@ import { UpdateOrderLineItemDTO, UpdateOrderLineItemTaxLineDTO, UpdateOrderLineItemWithSelectorDTO, - UpdateOrderReturnDTO, UpdateOrderReturnReasonDTO, UpdateOrderReturnReasonWithSelectorDTO, UpdateOrderReturnWithSelectorDTO, UpdateOrderShippingMethodAdjustmentDTO, UpdateOrderShippingMethodTaxLineDTO, + UpdateReturnDTO, UpsertOrderLineItemAdjustmentDTO, } from "./mutations" @@ -1899,12 +1899,12 @@ export interface IOrderModuleService extends IModuleService { updateReturns( selector: Partial, - data: Partial, + data: Partial, sharedContext?: Context ): Promise updateReturns( id: string, - data: Partial, + data: Partial, sharedContext?: Context ): Promise diff --git a/packages/core/types/src/workflow/order/request-item-return.ts b/packages/core/types/src/workflow/order/request-item-return.ts index c7be7c01dc..18748d4e35 100644 --- a/packages/core/types/src/workflow/order/request-item-return.ts +++ b/packages/core/types/src/workflow/order/request-item-return.ts @@ -4,3 +4,13 @@ export interface RequestItemReturnWorkflowInput { return_id: string items: CreateReturnItem[] } + +export interface OrderExchangeRequestItemReturnWorkflowInput { + exchange_id: string + items: CreateReturnItem[] +} + +export interface OrderClaimRequestItemReturnWorkflowInput { + claim_id: string + items: CreateReturnItem[] +} diff --git a/packages/core/workflows-sdk/src/utils/composer/__tests__/index.spec.ts b/packages/core/workflows-sdk/src/utils/composer/__tests__/index.spec.ts index ed87b950f0..b537f6a70e 100644 --- a/packages/core/workflows-sdk/src/utils/composer/__tests__/index.spec.ts +++ b/packages/core/workflows-sdk/src/utils/composer/__tests__/index.spec.ts @@ -120,9 +120,17 @@ describe("Workflow composer", () => { } ) - const { result } = await workflow.run({ input: { callSubFlow: true } }) + const { result } = await workflow.run({ + input: { callSubFlow: true }, + }) expect(result).toEqual({ result: "hi from outside" }) + + const { result: res2 } = await workflow.run({ + input: { callSubFlow: false }, + }) + + expect(res2).toEqual({ result: "default response" }) }) it("should revert the workflow and sub workflow on failure", async function () { diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index e128dfc97d..93dd17cf0a 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -1932,9 +1932,28 @@ export default class OrderModuleService< return await this.orderChangeService_.create(input, sharedContext) } - async previewOrderChange(orderChangeId: string, sharedContext?: Context) { + @InjectManager("baseRepository_") + async previewOrderChange(orderId: string, sharedContext?: Context) { + const order = await this.retrieveOrder( + orderId, + { + select: ["id", "version", "items.detail", "summary", "total"], + relations: [ + "transactions", + "items", + "items.detail", + "shipping_methods", + ], + }, + sharedContext + ) + + if (!order.order_change) { + return order + } + const orderChange = await super.retrieveOrderChange( - orderChangeId, + order.order_change.id, { relations: ["actions"] }, sharedContext ) @@ -1950,20 +1969,6 @@ export default class OrderModuleService< } }) - const order = await this.retrieveOrder( - orderChange.order_id, - { - select: ["id", "version", "items.detail", "summary", "total"], - relations: [ - "transactions", - "items", - "items.detail", - "shipping_methods", - ], - }, - sharedContext - ) - const calculated = calculateOrderChange({ order: order as any, actions: orderChange.actions, diff --git a/packages/modules/order/src/utils/transform-order.ts b/packages/modules/order/src/utils/transform-order.ts index 71c5f704f1..0ba0aac1cd 100644 --- a/packages/modules/order/src/utils/transform-order.ts +++ b/packages/modules/order/src/utils/transform-order.ts @@ -97,7 +97,7 @@ function cleanNestedRelations(obj) { } function formatOrderReturn(orderReturn, mainOrder) { - orderReturn.items.forEach((orderItem) => { + orderReturn.items?.forEach((orderItem) => { const item = mainOrder.items?.find((item) => item.id === orderItem.item_id) orderItem.detail = item?.detail