diff --git a/integration-tests/modules/__tests__/order/workflows/return/create-return-shipping.spec.ts b/integration-tests/modules/__tests__/order/workflows/return/create-return-shipping.spec.ts new file mode 100644 index 0000000000..e59bed552d --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/return/create-return-shipping.spec.ts @@ -0,0 +1,124 @@ +import { + beginReturnOrderWorkflow, + createReturnShippingMethodWorkflow, +} from "@medusajs/core-flows" +import { OrderDTO, ReturnDTO } from "@medusajs/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +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: Create return shipping", () => { + let order: OrderDTO + let fixtures + + let returnOrder: ReturnDTO + + beforeEach(async () => { + fixtures = await prepareDataFixtures({ container }) + + order = await createOrderFixture({ + container, + product: fixtures.product, + location: fixtures.location, + inventoryItem: fixtures.inventoryItem, + }) + + await beginReturnOrderWorkflow(container).run({ + input: { order_id: order.id }, + throwOnError: true, + }) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "return", + variables: { order_id: order.id }, + fields: ["order_id", "id", "status", "order_change_id"], + }) + + ;[returnOrder] = await remoteQuery(remoteQueryObject) + }) + + describe("createReturnShippingMethodWorkflow", () => { + it("should successfully add return shipping to order changes", async () => { + const shippingOptionId = fixtures.shippingOption.id + + const { result } = await createReturnShippingMethodWorkflow( + container + ).run({ + input: { + returnId: returnOrder.id, + shippingOptionId: shippingOptionId, + }, + }) + + const orderChange = result?.[0] + + expect(orderChange).toEqual( + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + details: { + order_id: returnOrder.order_id, + return_id: returnOrder.id, + }, + raw_amount: { value: "10", precision: 20 }, + applied: false, + action: "SHIPPING_ADD", + amount: 10, + }) + ) + }) + + it("should successfully add return shipping with custom price to order changes", async () => { + const shippingOptionId = fixtures.shippingOption.id + + const { result } = await createReturnShippingMethodWorkflow( + container + ).run({ + input: { + returnId: returnOrder.id, + shippingOptionId: shippingOptionId, + customShippingPrice: 20, + }, + }) + + const orderChange = result?.[0] + + expect(orderChange).toEqual( + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + details: { + order_id: returnOrder.order_id, + return_id: returnOrder.id, + }, + raw_amount: { value: "20", precision: 20 }, + applied: false, + action: "SHIPPING_ADD", + amount: 20, + }) + ) + }) + }) + }) + }, +}) diff --git a/packages/core/core-flows/src/order/steps/create-order-shipping-methods.ts b/packages/core/core-flows/src/order/steps/create-order-shipping-methods.ts new file mode 100644 index 0000000000..2394761512 --- /dev/null +++ b/packages/core/core-flows/src/order/steps/create-order-shipping-methods.ts @@ -0,0 +1,37 @@ +import { + CreateOrderShippingMethodDTO, + IOrderModuleService, +} from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/utils" +import { createStep, StepResponse } from "@medusajs/workflows-sdk" + +interface StepInput { + shipping_methods: CreateOrderShippingMethodDTO[] +} + +export const createOrderShippingMethods = createStep( + "create-order-shipping-methods", + async (input: StepInput, { container }) => { + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + const created = await service.createShippingMethods(input.shipping_methods) + + return new StepResponse( + created, + created.map((c) => c.id) + ) + }, + async (createdMethodIds, { container }) => { + if (!createdMethodIds) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + await service.deleteShippingMethods(createdMethodIds) + } +) diff --git a/packages/core/core-flows/src/order/workflows/create-return-shipping-method.ts b/packages/core/core-flows/src/order/workflows/create-return-shipping-method.ts new file mode 100644 index 0000000000..50b7cffa57 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/create-return-shipping-method.ts @@ -0,0 +1,138 @@ +import { + BigNumberInput, + OrderChangeDTO, + OrderDTO, + ReturnDTO, +} from "@medusajs/types" +import { ChangeActionType } from "@medusajs/utils" +import { + createStep, + createWorkflow, + transform, + WorkflowData, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" +import { createOrderShippingMethods } from "../steps/create-order-shipping-methods" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive +} from "../utils/order-validation" + +const validationStep = createStep( + "validate-create-return-shipping-method", + async function ({ + order, + orderChange, + orderReturn, + }: { + order: OrderDTO + orderReturn: ReturnDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(order, "Order") + throwIfIsCancelled(orderReturn, "Return") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const createReturnShippingMethodWorkflowId = + "create-return-shipping-method" +export const createReturnShippingMethodWorkflow = createWorkflow( + createReturnShippingMethodWorkflowId, + function (input: { + returnId: string + shippingOptionId: string + customShippingPrice?: BigNumberInput + }): WorkflowData { + const orderReturn: ReturnDTO = useRemoteQueryStep({ + entry_point: "return", + fields: ["id", "status", "order_id"], + variables: { id: input.returnId }, + list: false, + throw_if_key_not_found: true, + }) + + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "currency_code"], + variables: { id: orderReturn.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const shippingOptions = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: [ + "id", + "name", + "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", + ], + variables: { + id: input.shippingOptionId, + calculated_price: { + context: { currency_code: order.currency_code }, + }, + }, + }).config({ name: "fetch-shipping-option" }) + + const shippingMethodInput = transform( + { orderReturn, shippingOptions }, + (data) => { + const option = data.shippingOptions[0] + + return { + shipping_option_id: option.id, + amount: option.calculated_price.calculated_amount, + is_tax_inclusive: + !!option.calculated_price.is_calculated_price_tax_inclusive, + data: option.data ?? {}, + name: option.name, + order_id: data.orderReturn.order_id, + return_id: data.orderReturn.id, + } + } + ) + + const createdMethods = createOrderShippingMethods({ + shipping_methods: [shippingMethodInput], + }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status"], + variables: { order_id: orderReturn.order_id }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ order, orderReturn, orderChange }) + + const orderChangeActionInput = transform( + { + orderId: order.id, + returnId: orderReturn.id, + shippingOption: shippingOptions[0], + methodId: createdMethods[0].id, + customPrice: input.customShippingPrice, + }, + ({ shippingOption, returnId, orderId, methodId, customPrice }) => { + const methodPrice = + customPrice ?? shippingOption.calculated_price.calculated_amount + + return { + action: ChangeActionType.SHIPPING_ADD, + reference: "order_shipping_method", + reference_id: methodId, + amount: methodPrice, + details: { + order_id: orderId, + return_id: returnId, + }, + } + } + ) + + return createOrderChangeActionsStep([orderChangeActionInput]) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index edf2121309..cab76dd0a7 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -13,6 +13,7 @@ export * from "./create-fulfillment" export * from "./create-order-change" export * from "./create-order-change-actions" export * from "./create-orders" +export * from "./create-return-shipping-method" export * from "./create-shipment" export * from "./decline-order-change" export * from "./delete-order-change" @@ -24,3 +25,4 @@ export * from "./receive-return" export * from "./request-item-return" export * from "./update-order-change-actions" export * from "./update-tax-lines" + diff --git a/packages/core/utils/src/totals/shipping-method/index.ts b/packages/core/utils/src/totals/shipping-method/index.ts index f6c4ccc9b6..ca485111cf 100644 --- a/packages/core/utils/src/totals/shipping-method/index.ts +++ b/packages/core/utils/src/totals/shipping-method/index.ts @@ -34,7 +34,7 @@ export interface GetShippingMethodTotalOutput { export function getShippingMethodsTotals( shippingMethods: GetShippingMethodTotalInput[], context: GetShippingMethodsTotalsContext -) { +): Record { const { includeTax } = context const shippingMethodsTotals = {}