diff --git a/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts b/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts index 6bcc1e90bd9d5..5e581778c29f2 100644 --- a/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts +++ b/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts @@ -33,9 +33,15 @@ medusaIntegrationTestRunner({ ) expect(response.status).toEqual(200) - expect(response.data.fulfillment_providers).toEqual([ - { id: "manual_test-provider", is_enabled: true }, - ]) + expect(response.data.fulfillment_providers).toEqual( + expect.arrayContaining([ + { id: "manual_test-provider", is_enabled: true }, + { + id: "manual-calculated_test-provider-calculated", + is_enabled: true, + }, + ]) + ) }) }) }, diff --git a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts index 3abdec9f34fc6..11d6c8a783e15 100644 --- a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts +++ b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts @@ -13,14 +13,22 @@ import { } from "@medusajs/utils" const providerId = "manual_test-provider" +const providerIdCalculated = "manual-calculated_test-provider-calculated" + export async function prepareDataFixtures({ container }) { const fulfillmentService = container.resolve(Modules.FULFILLMENT) const salesChannelService = container.resolve(Modules.SALES_CHANNEL) const stockLocationModule: IStockLocationService = container.resolve( Modules.STOCK_LOCATION ) + const pricingModule = container.resolve(Modules.PRICING) const productModule = container.resolve(Modules.PRODUCT) const inventoryModule = container.resolve(Modules.INVENTORY) + const customerService = container.resolve(Modules.CUSTOMER) + + const customer = await customerService.createCustomers({ + email: "foo@bar.com", + }) const shippingProfile = await fulfillmentService.createShippingProfiles({ name: "test", @@ -71,6 +79,18 @@ export async function prepareDataFixtures({ container }) { }, }) + const priceSets = await pricingModule.createPriceSets([ + { + prices: [ + { + amount: 10, + region_id: region.id, + currency_code: "usd", + }, + ], + }, + ]) + const [product] = await productModule.createProducts([ { title: "Test product", @@ -91,7 +111,7 @@ export async function prepareDataFixtures({ container }) { { inventory_item_id: inventoryItem.id, location_id: location.id, - stocked_quantity: 2, + stocked_quantity: 10, reserved_quantity: 0, }, ]) @@ -123,6 +143,14 @@ export async function prepareDataFixtures({ container }) { inventory_item_id: inventoryItem.id, }, }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[0].id, + }, + [Modules.PRICING]: { + price_set_id: priceSets[0].id, + }, + }, ]) await remoteLink.create([ @@ -131,7 +159,16 @@ export async function prepareDataFixtures({ container }) { stock_location_id: location.id, }, [Modules.FULFILLMENT]: { - fulfillment_provider_id: "manual_test-provider", + fulfillment_provider_id: providerId, + }, + }, + + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: providerIdCalculated, }, }, ]) @@ -160,14 +197,29 @@ export async function prepareDataFixtures({ container }) { ], } + const shippingOptionCalculatedData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = + { + name: "Calculated shipping option", + service_zone_id: serviceZone.id, + shipping_profile_id: shippingProfile.id, + provider_id: providerIdCalculated, + price_type: "calculated", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + rules: [], + } + const { result } = await createShippingOptionsWorkflow(container).run({ - input: [shippingOptionData], + input: [shippingOptionData, shippingOptionCalculatedData], }) const remoteQueryObject = remoteQueryObjectFromString({ entryPoint: "shipping_option", variables: { - id: result[0].id, + id: result.map((r) => r.id), }, fields: [ "id", @@ -189,13 +241,17 @@ export async function prepareDataFixtures({ container }) { const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const [createdShippingOption] = await remoteQuery(remoteQueryObject) + const shippingOptions = await remoteQuery(remoteQueryObject) return { - shippingOption: createdShippingOption, + shippingOption: shippingOptions.find((s) => s.price_type === "flat"), + shippingOptionCalculated: shippingOptions.find( + (s) => s.price_type === "calculated" + ), region, salesChannel, location, product, + customer, inventoryItem, } } @@ -205,18 +261,31 @@ export async function createOrderFixture({ product, location, inventoryItem, + region, + salesChannel, + customer, + overrides, +}: { + container: any + product: any + location: any + inventoryItem: any + salesChannel?: any + customer?: any + region?: any + overrides?: { quantity?: number } }) { const orderService: IOrderModuleService = container.resolve(Modules.ORDER) let order = await orderService.createOrders({ - region_id: "test_region_id", - email: "foo@bar.com", + region_id: region?.id || "test_region_id", + email: customer?.email || "foo@bar.com", items: [ { title: "Custom Item 2", variant_sku: product.variants[0].sku, variant_title: product.variants[0].title, - quantity: 1, + quantity: overrides?.quantity ?? 1, unit_price: 50, adjustments: [ { @@ -235,7 +304,7 @@ export async function createOrderFixture({ currency_code: "usd", }, ], - sales_channel_id: "test", + sales_channel_id: salesChannel?.id || "test", shipping_address: { first_name: "Test", last_name: "Test", @@ -277,7 +346,7 @@ export async function createOrderFixture({ }, ], currency_code: "usd", - customer_id: "joe", + customer_id: customer?.id || "joe", }) const inventoryModule = container.resolve(Modules.INVENTORY) diff --git a/integration-tests/modules/__tests__/order/workflows/claim/claim-shipping.spec.ts b/integration-tests/modules/__tests__/order/workflows/claim/claim-shipping.spec.ts new file mode 100644 index 0000000000000..94a11870f6f3f --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/claim/claim-shipping.spec.ts @@ -0,0 +1,212 @@ +import { + beginClaimOrderWorkflow, + createClaimShippingMethodWorkflow, + createOrderFulfillmentWorkflow, + orderClaimAddNewItemWorkflow, + orderClaimRequestItemReturnWorkflow, + updateClaimAddItemWorkflow, +} from "@medusajs/core-flows" +import { IFulfillmentModuleService, OrderDTO } from "@medusajs/types" +import { + ContainerRegistrationKeys, + Modules, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { medusaIntegrationTestRunner } from "@medusajs/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: Claim shipping", () => { + let order: OrderDTO + let service: IFulfillmentModuleService + let fixtures + + let claimOrder: OrderDTO + + beforeEach(async () => { + fixtures = await prepareDataFixtures({ container }) + + order = await createOrderFixture({ + container, + product: fixtures.product, + location: fixtures.location, + inventoryItem: fixtures.inventoryItem, + salesChannel: fixtures.salesChannel, + customer: fixtures.customer, + region: fixtures.region, + overrides: { quantity: 2 }, + }) + + await createOrderFulfillmentWorkflow(container).run({ + input: { + order_id: order.id, + items: [ + { + quantity: 2, + id: order.items![0].id, + }, + ], + }, + }) + + await beginClaimOrderWorkflow(container).run({ + input: { order_id: order.id, type: "replace" }, + throwOnError: true, + }) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "order_claim", + variables: { order_id: order.id }, + fields: ["order_id", "id", "status", "order_change_id"], + }) + + service = container.resolve(Modules.FULFILLMENT) + ;[claimOrder] = await remoteQuery(remoteQueryObject) + }) + + describe("createClaimShippingMethodWorkflow", () => { + it("should successfully add caluclated inbound and outbound shipping to order changes", async () => { + const { result } = await orderClaimAddNewItemWorkflow(container).run({ + input: { + claim_id: claimOrder.id, + items: [ + { + variant_id: fixtures.product.variants[0].id, + quantity: 1, + internal_note: "test", + }, + ], + }, + }) + + const shippingOptionId = fixtures.shippingOptionCalculated.id + + const { result: orderChangePreview } = + await createClaimShippingMethodWorkflow(container).run({ + input: { + claim_id: claimOrder.id, + shipping_option_id: shippingOptionId, + }, + }) + + // Original shipping + outbound + expect(orderChangePreview.shipping_methods).toHaveLength(2) + + const outboundShippingMethod = + orderChangePreview.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + expect((outboundShippingMethod as any).actions).toEqual([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "2.5", precision: 20 }, + return_id: null, + claim_id: claimOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 2.5, + }), + ]) + + const { result: orderChangePreview2 } = + await orderClaimRequestItemReturnWorkflow.run({ + container, + input: { + claim_id: claimOrder.id, + items: [ + { + id: result.items[0].id, + quantity: 1, + }, + ], + }, + }) + + const associatedReturnId = orderChangePreview2.order_change.return_id + + const { result: orderChangePreview3 } = + await createClaimShippingMethodWorkflow(container).run({ + input: { + claim_id: claimOrder.id, + return_id: associatedReturnId, + shipping_option_id: shippingOptionId, + }, + }) + + expect(orderChangePreview3.shipping_methods).toHaveLength(3) + + const inboundShippingMethod = + orderChangePreview3.shipping_methods?.find( + (sm) => + sm.shipping_option_id === shippingOptionId && + sm.actions?.find( + (a) => + a.action === "SHIPPING_ADD" && + a.return_id === associatedReturnId + ) + ) + + expect(inboundShippingMethod!.actions![0]).toEqual( + expect.objectContaining({ + return_id: associatedReturnId, + claim_id: claimOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 2, + }) + ) + + // Update outbound quantity to test refresh caluclation + + const { result: orderChangePreview4 } = + await updateClaimAddItemWorkflow(container).run({ + input: { + claim_id: claimOrder.id, + action_id: result.items.find( + (i) => i.variant_id === fixtures.product.variants[0].id + )?.actions?.[0]?.id as string, + data: { + quantity: 2, + }, + }, + }) + + const outboundShippingMethod2 = + orderChangePreview4.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + expect((outboundShippingMethod2 as any).actions).toEqual([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "5", precision: 20 }, + return_id: null, + claim_id: claimOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 5, + }), + ]) + }) + }) + }) + }, +}) diff --git a/integration-tests/modules/__tests__/order/workflows/exchange/exchange-shipping.spec.ts b/integration-tests/modules/__tests__/order/workflows/exchange/exchange-shipping.spec.ts new file mode 100644 index 0000000000000..b91900d67b10a --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/exchange/exchange-shipping.spec.ts @@ -0,0 +1,214 @@ +import { + beginExchangeOrderWorkflow, + createExchangeShippingMethodWorkflow, + createOrderFulfillmentWorkflow, + orderExchangeAddNewItemWorkflow, + orderExchangeRequestItemReturnWorkflow, + updateExchangeAddItemWorkflow, +} from "@medusajs/core-flows" +import { IFulfillmentModuleService, OrderDTO } from "@medusajs/types" +import { + ContainerRegistrationKeys, + Modules, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { medusaIntegrationTestRunner } from "@medusajs/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: Exchange shipping", () => { + let order: OrderDTO + let service: IFulfillmentModuleService + let fixtures + + let exchangeOrder: OrderDTO + + beforeEach(async () => { + fixtures = await prepareDataFixtures({ container }) + + order = await createOrderFixture({ + container, + product: fixtures.product, + location: fixtures.location, + inventoryItem: fixtures.inventoryItem, + salesChannel: fixtures.salesChannel, + customer: fixtures.customer, + region: fixtures.region, + overrides: { quantity: 2 }, + }) + + await createOrderFulfillmentWorkflow(container).run({ + input: { + order_id: order.id, + items: [ + { + quantity: 2, + id: order.items![0].id, + }, + ], + }, + }) + + await beginExchangeOrderWorkflow(container).run({ + input: { order_id: order.id }, + throwOnError: true, + }) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + + const remoteQueryObject = remoteQueryObjectFromString({ + entryPoint: "order_exchange", + variables: { order_id: order.id }, + fields: ["order_id", "id", "status", "order_change_id"], + }) + + service = container.resolve(Modules.FULFILLMENT) + ;[exchangeOrder] = await remoteQuery(remoteQueryObject) + }) + + describe("createExchangeShippingMethodWorkflow", () => { + it("should successfully add caluclated inbound and outbound shipping to order changes", async () => { + const { result } = await orderExchangeAddNewItemWorkflow( + container + ).run({ + input: { + exchange_id: exchangeOrder.id, + items: [ + { + variant_id: fixtures.product.variants[0].id, + quantity: 1, + internal_note: "test", + }, + ], + }, + }) + + const shippingOptionId = fixtures.shippingOptionCalculated.id + + const { result: orderChangePreview } = + await createExchangeShippingMethodWorkflow(container).run({ + input: { + exchange_id: exchangeOrder.id, + shipping_option_id: shippingOptionId, + }, + }) + + // Original shipping + outbound + expect(orderChangePreview.shipping_methods).toHaveLength(2) + + const outboundShippingMethod = + orderChangePreview.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + expect((outboundShippingMethod as any).actions).toEqual([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "2.5", precision: 20 }, + return_id: null, + exchange_id: exchangeOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 2.5, + }), + ]) + + const { result: orderChangePreview2 } = + await orderExchangeRequestItemReturnWorkflow.run({ + container, + input: { + exchange_id: exchangeOrder.id, + items: [ + { + id: result.items[0].id, + quantity: 1, + }, + ], + }, + }) + + const associatedReturnId = orderChangePreview2.order_change.return_id + + const { result: orderChangePreview3 } = + await createExchangeShippingMethodWorkflow(container).run({ + input: { + exchange_id: exchangeOrder.id, + return_id: associatedReturnId, + shipping_option_id: shippingOptionId, + }, + }) + + expect(orderChangePreview3.shipping_methods).toHaveLength(3) + + const inboundShippingMethod = + orderChangePreview3.shipping_methods?.find( + (sm) => + sm.shipping_option_id === shippingOptionId && + sm.actions?.find( + (a) => + a.action === "SHIPPING_ADD" && + a.return_id === associatedReturnId + ) + ) + + expect(inboundShippingMethod!.actions![0]).toEqual( + expect.objectContaining({ + return_id: associatedReturnId, + exchange_id: exchangeOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 2, + }) + ) + + // Update outbound quantity to test refresh caluclation + + const { result: orderChangePreview4 } = + await updateExchangeAddItemWorkflow(container).run({ + input: { + exchange_id: exchangeOrder.id, + action_id: result.items.find( + (i) => i.variant_id === fixtures.product.variants[0].id + )?.actions?.[0]?.id as string, + data: { + quantity: 2, + }, + }, + }) + + const outboundShippingMethod2 = + orderChangePreview4.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + expect((outboundShippingMethod2 as any).actions).toEqual([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "5", precision: 20 }, + return_id: null, + exchange_id: exchangeOrder.id, + applied: false, + action: "SHIPPING_ADD", + amount: 5, + }), + ]) + }) + }) + }) + }, +}) 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 index 401e9ac54bc2b..7ace4ba1fcb78 100644 --- 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 @@ -1,6 +1,9 @@ import { beginReturnOrderWorkflow, + createOrderFulfillmentWorkflow, createReturnShippingMethodWorkflow, + requestItemReturnWorkflow, + updateRequestItemReturnWorkflow, } from "@medusajs/core-flows" import { IFulfillmentModuleService, OrderDTO, ReturnDTO } from "@medusajs/types" import { @@ -10,7 +13,6 @@ import { } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { createOrderFixture, prepareDataFixtures } from "../__fixtures__" - jest.setTimeout(50000) medusaIntegrationTestRunner({ @@ -37,6 +39,19 @@ medusaIntegrationTestRunner({ product: fixtures.product, location: fixtures.location, inventoryItem: fixtures.inventoryItem, + overrides: { quantity: 2 }, + }) + + await createOrderFulfillmentWorkflow(container).run({ + input: { + order_id: order.id, + items: [ + { + quantity: 2, + id: order.items![0].id, + }, + ], + }, }) await beginReturnOrderWorkflow(container).run({ @@ -115,6 +130,121 @@ medusaIntegrationTestRunner({ }), ]) }) + + it("should successfully add calculated return shipping to order changes", async () => { + const shippingOptionId = fixtures.shippingOptionCalculated.id + + const { result: orderChangePreview } = + await createReturnShippingMethodWorkflow(container).run({ + input: { + return_id: returnOrder.id, + shipping_option_id: shippingOptionId, + }, + }) + + const shippingMethod = orderChangePreview.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + /** + * Shipping is 0 because the shipping option is calculated based on the return items + * and currently there are no return items. + */ + + expect((shippingMethod as any).actions).toEqual([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "0", precision: 20 }, + applied: false, + action: "SHIPPING_ADD", + amount: 0, + }), + ]) + + const { result } = await requestItemReturnWorkflow(container).run({ + input: { + return_id: returnOrder.id, + items: [ + { + id: order.items![0].id, + quantity: 1, + internal_note: "test", + }, + ], + }, + }) + + console.log(result.items[0].actions) + + let updatedShippingMethod = result.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + /** + * Caluclated shipping is 2$ per return item. + */ + expect(updatedShippingMethod).toEqual( + expect.objectContaining({ + id: expect.any(String), + shipping_option_id: shippingOptionId, + amount: 2, + actions: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "2", precision: 20 }, + applied: false, + action: "SHIPPING_ADD", + amount: 2, + }), + ]), + }) + ) + /** + * Update the return item quantity to 2. + */ + + const { result: updatedResult } = + await updateRequestItemReturnWorkflow(container).run({ + input: { + return_id: returnOrder.id, + action_id: result.items + .find((i) => + i.actions?.find((a) => a.action === "RETURN_ITEM") + ) + ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id!, + data: { + quantity: 2, + }, + }, + }) + + updatedShippingMethod = updatedResult.shipping_methods?.find( + (sm) => sm.shipping_option_id === shippingOptionId + ) + + expect(updatedShippingMethod).toEqual( + expect.objectContaining({ + id: expect.any(String), + shipping_option_id: shippingOptionId, + amount: 4, + actions: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + reference: "order_shipping_method", + reference_id: expect.any(String), + raw_amount: { value: "4", precision: 20 }, + applied: false, + action: "SHIPPING_ADD", + amount: 4, + }), + ]), + }) + ) + }) }) }) }, diff --git a/integration-tests/modules/medusa-config.js b/integration-tests/modules/medusa-config.js index 81ce1c7557bf1..021234b59c33c 100644 --- a/integration-tests/modules/medusa-config.js +++ b/integration-tests/modules/medusa-config.js @@ -22,6 +22,12 @@ const customFulfillmentProvider = { id: "test-provider", } +const customFulfillmentProviderCalculated = { + resolve: require("./dist/utils/providers/fulfillment-manual-calculated") + .default, + id: "test-provider-calculated", +} + module.exports = { admin: { disable: true, @@ -96,7 +102,10 @@ module.exports = { [Modules.FULFILLMENT]: { /** @type {import('@medusajs/fulfillment').FulfillmentModuleOptions} */ options: { - providers: [customFulfillmentProvider], + providers: [ + customFulfillmentProvider, + customFulfillmentProviderCalculated, + ], }, }, [Modules.NOTIFICATION]: { diff --git a/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/index.ts b/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/index.ts new file mode 100644 index 0000000000000..93b20068a8207 --- /dev/null +++ b/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/index.ts @@ -0,0 +1,8 @@ +import { ModuleProvider, Modules } from "@medusajs/framework/utils" +import { ManualFulfillmentService } from "./services/manual-fulfillment" + +const services = [ManualFulfillmentService] + +export default ModuleProvider(Modules.FULFILLMENT, { + services, +}) diff --git a/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/services/manual-fulfillment.ts b/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/services/manual-fulfillment.ts new file mode 100644 index 0000000000000..fc704d79a9ac2 --- /dev/null +++ b/integration-tests/modules/src/utils/providers/fulfillment-manual-calculated/services/manual-fulfillment.ts @@ -0,0 +1,80 @@ +import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils" + +export class ManualFulfillmentService extends AbstractFulfillmentProviderService { + static identifier = "manual-calculated" + + constructor() { + super() + } + + async getFulfillmentOptions() { + return [ + { + id: "manual-fulfillment-calculated", + }, + { + id: "manual-fulfillment-return-calculated", + is_return: true, + }, + ] + } + + async validateFulfillmentData(optionData, data, context) { + return data + } + + async calculatePrice(optionData, data, context) { + if (context.exchange_id) { + return { + calculated_amount: + context.exchange_items.reduce((acc, i) => acc + i.quantity, 0) * 2.5, // mock return cost as 2 per item + is_calculated_price_tax_inclusive: false, + } + } + if (context.claim_id) { + return { + calculated_amount: + context.claim_items.reduce((acc, i) => acc + i.quantity, 0) * 2.5, // mock return cost as 2 per item + is_calculated_price_tax_inclusive: false, + } + } + + if (context.return_id) { + return { + calculated_amount: + context.return_items.reduce((acc, i) => acc + i.quantity, 0) * 2, // mock return cost as 2 per item + is_calculated_price_tax_inclusive: false, + } + } + + return { + calculated_amount: + context.items.reduce((acc, i) => acc + i.quantity, 0) * 1.5, // mock caluclation as 1.5 per item + is_calculated_price_tax_inclusive: false, + } + } + + async canCalculate() { + return true + } + + async validateOption(data) { + return true + } + + async createFulfillment() { + // No data is being sent anywhere + return { + data: {}, + labels: [], + } + } + + async cancelFulfillment() { + return {} + } + + async createReturnFulfillment() { + return { data: {}, labels: [] } + } +} diff --git a/packages/core/core-flows/src/cart/workflows/add-shipping-method-to-cart.ts b/packages/core/core-flows/src/cart/workflows/add-shipping-method-to-cart.ts index dfb991dedba71..99fe13df2f539 100644 --- a/packages/core/core-flows/src/cart/workflows/add-shipping-method-to-cart.ts +++ b/packages/core/core-flows/src/cart/workflows/add-shipping-method-to-cart.ts @@ -39,7 +39,7 @@ export interface AddShippingMethodToCartWorkflowInput { id: string /** * Custom data useful for the fulfillment provider processing the shipping option or method. - * + * * Learn more in [this documentation](https://docs.medusajs.com/resources/commerce-modules/fulfillment/shipping-option#data-property). */ data?: Record @@ -48,11 +48,11 @@ export interface AddShippingMethodToCartWorkflowInput { export const addShippingMethodToCartWorkflowId = "add-shipping-method-to-cart" /** - * This workflow adds a shipping method to a cart. It's executed by the + * This workflow adds a shipping method to a cart. It's executed by the * [Add Shipping Method Store API Route](https://docs.medusajs.com/api/store#carts_postcartsidshippingmethods). - * + * * You can use this workflow within your own customizations or custom workflows, allowing you to wrap custom logic around adding a shipping method to the cart. - * + * * @example * const { result } = await addShippingMethodToCartWorkflow(container) * .run({ @@ -71,11 +71,11 @@ export const addShippingMethodToCartWorkflowId = "add-shipping-method-to-cart" * ] * } * }) - * + * * @summary - * + * * Add a shipping method to a cart. - * + * * @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution. */ export const addShippingMethodToCartWorkflow = createWorkflow( diff --git a/packages/core/core-flows/src/fulfillment/steps/calculate-shipping-options-prices.ts b/packages/core/core-flows/src/fulfillment/steps/calculate-shipping-options-prices.ts index ebdec56bbde70..cff5d880ef1a8 100644 --- a/packages/core/core-flows/src/fulfillment/steps/calculate-shipping-options-prices.ts +++ b/packages/core/core-flows/src/fulfillment/steps/calculate-shipping-options-prices.ts @@ -8,13 +8,14 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk" /** * The data to calculate the prices for one or more shipping options. */ -export type CalculateShippingOptionsPriceStepInput = CalculateShippingOptionPriceDTO[] +export type CalculateShippingOptionsPriceStepInput = + CalculateShippingOptionPriceDTO[] export const calculateShippingOptionsPricesStepId = "calculate-shipping-options-prices" /** * This step calculates the prices for one or more shipping options. - * + * * @example * const data = calculateShippingOptionsPricesStep([{ * id: "so_123", diff --git a/packages/core/core-flows/src/fulfillment/steps/validate-shipping-option-prices.ts b/packages/core/core-flows/src/fulfillment/steps/validate-shipping-option-prices.ts index a3548054084cf..be6aca6fa3f5c 100644 --- a/packages/core/core-flows/src/fulfillment/steps/validate-shipping-option-prices.ts +++ b/packages/core/core-flows/src/fulfillment/steps/validate-shipping-option-prices.ts @@ -22,9 +22,9 @@ export const validateShippingOptionPricesStepId = * * For flat rate prices, it validates that regions exist for the shipping option prices. * For calculated prices, it validates with the fulfillment provider if the price can be calculated. - * + * * If not valid, the step throws an error. - * + * * @example * const data = validateShippingOptionPricesStep([ * { diff --git a/packages/core/core-flows/src/order/utils/fetch-shipping-option.ts b/packages/core/core-flows/src/order/utils/fetch-shipping-option.ts new file mode 100644 index 0000000000000..199f228735a89 --- /dev/null +++ b/packages/core/core-flows/src/order/utils/fetch-shipping-option.ts @@ -0,0 +1,210 @@ +import { + BigNumberInput, + CalculatedRMAShippingContext, + CalculateShippingOptionPriceDTO, + ShippingOptionDTO, +} from "@medusajs/framework/types" +import { + WorkflowResponse, + createWorkflow, + transform, + when, +} from "@medusajs/framework/workflows-sdk" +import { BigNumber, ShippingOptionPriceType } from "@medusajs/framework/utils" +import { calculateShippingOptionsPricesStep } from "../../fulfillment/steps" +import { useRemoteQueryStep } from "../../common" + +const COMMON_OPTIONS_FIELDS = [ + "id", + "name", + "price_type", + "service_zone_id", + "service_zone.fulfillment_set_id", + "service_zone.fulfillment_set.type", + "service_zone.fulfillment_set.location.*", + "service_zone.fulfillment_set.location.address.*", + "shipping_profile_id", + "provider_id", + "data", + + "type.id", + "type.label", + "type.description", + "type.code", + + "provider.id", + "provider.is_enabled", + + "rules.attribute", + "rules.value", + "rules.operator", +] + +/** + * The data to create a shipping method for an order edit. + */ +export type FetchShippingOptionForOrderWorkflowInput = { + /** + * The ID of the shipping option to create the shipping method from. + */ + shipping_option_id: string + /** + * The custom amount to create the shipping method with. + * If not provided, the shipping option's amount is used. + */ + custom_amount?: BigNumberInput | null + /** + * The currency code of the order. + */ + currency_code: string + /** + * The ID of the order. + */ + order_id: string + /** + * The context of the order. + */ + context: CalculatedRMAShippingContext +} + +/** + * The output of the fetch shipping option for order workflow. + */ +export type FetchShippingOptionForOrderWorkflowOutput = ShippingOptionDTO & { + calculated_price: { + calculated_amount: BigNumber + is_calculated_price_tax_inclusive: boolean + } +} +export const createOrderEditShippingMethodWorkflowId = "fetch-shipping-option" +/** + * This workflows fetches a shipping option for an order (used in RMA flows). + * + * There can be 3 cases: + * 1. The shipping option is a flat rate shipping option. + * In this case, pricing calculation context is not used. + * 2. The shipping option is a calculated shipping option. + * In this case, calculate shipping price method from the provider is called. + * 3. The shipping option is a custom shipping option. -- TODO + * In this case, we don't need to do caluclations above and just return the shipping option with the custom amount. + */ +export const fetchShippingOptionForOrderWorkflow = createWorkflow( + createOrderEditShippingMethodWorkflowId, + function ( + input: FetchShippingOptionForOrderWorkflowInput + ): WorkflowResponse { + const initialOption = useRemoteQueryStep({ + entry_point: "shipping_option", + variables: { id: input.shipping_option_id }, + fields: ["id", "price_type"], + list: false, + }).config({ name: "shipping-option-query" }) + + const isCalculatedPriceShippingOption = transform( + initialOption, + (option) => option.price_type === ShippingOptionPriceType.CALCULATED + ) + + const calculatedPriceShippingOption = when( + "option-calculated", + { isCalculatedPriceShippingOption }, + ({ isCalculatedPriceShippingOption }) => isCalculatedPriceShippingOption + ).then(() => { + const order = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "shipping_address", "items.*", "items.variant.*"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const shippingOption = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: [...COMMON_OPTIONS_FIELDS], + variables: { id: input.shipping_option_id }, + list: false, + }).config({ name: "calculated-option" }) + + const calculateShippingOptionsPricesData = transform( + { + shippingOption, + order, + input, + }, + ({ shippingOption, order, input }) => { + return [ + { + id: shippingOption.id as string, + optionData: shippingOption.data, + context: { + ...order, + ...input.context, + from_location: + shippingOption.service_zone.fulfillment_set.location, + }, + // data: {}, // TODO: add data + provider_id: shippingOption.provider_id, + } as CalculateShippingOptionPriceDTO, + ] + } + ) + + const prices = calculateShippingOptionsPricesStep( + calculateShippingOptionsPricesData + ) + + const shippingOptionsWithPrice = transform( + { + shippingOption, + prices, + }, + ({ shippingOption, prices }) => { + return { + id: shippingOption.id, + name: shippingOption.name, + calculated_price: prices[0], + } + } + ) + + return shippingOptionsWithPrice + }) + + const flatRateShippingOption = when( + "option-flat", + { isCalculatedPriceShippingOption }, + ({ isCalculatedPriceShippingOption }) => !isCalculatedPriceShippingOption + ).then(() => { + const shippingOption = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: [ + "id", + "name", + "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", + ], + variables: { + id: input.shipping_option_id, + calculated_price: { + context: { currency_code: input.currency_code }, + }, + }, + list: false, + }).config({ name: "flat-reate-option" }) + + return shippingOption + }) + + const result = transform( + { + calculatedPriceShippingOption, + flatRateShippingOption, + }, + ({ calculatedPriceShippingOption, flatRateShippingOption }) => { + return calculatedPriceShippingOption ?? flatRateShippingOption + } + ) + + return new WorkflowResponse(result) + } +) diff --git a/packages/core/core-flows/src/order/utils/maybe-refresh-shipping-methods.ts b/packages/core/core-flows/src/order/utils/maybe-refresh-shipping-methods.ts new file mode 100644 index 0000000000000..e89e4b453cba7 --- /dev/null +++ b/packages/core/core-flows/src/order/utils/maybe-refresh-shipping-methods.ts @@ -0,0 +1,170 @@ +import { + CalculatedRMAShippingContext, + CalculateShippingOptionPriceDTO, +} from "@medusajs/framework/types" +import { + WorkflowResponse, + createWorkflow, + parallelize, + transform, + when, +} from "@medusajs/framework/workflows-sdk" +import { ShippingOptionPriceType } from "@medusajs/framework/utils" +import { calculateShippingOptionsPricesStep } from "../../fulfillment/steps" +import { + updateOrderChangeActionsStep, + updateOrderShippingMethodsStep, +} from "../steps" +import { useRemoteQueryStep } from "../../common" + +const COMMON_OPTIONS_FIELDS = [ + "id", + "name", + "price_type", + "service_zone_id", + "service_zone.fulfillment_set_id", + "service_zone.fulfillment_set.type", + "service_zone.fulfillment_set.location.*", + "service_zone.fulfillment_set.location.address.*", + "shipping_profile_id", + "provider_id", + "data", + + "type.id", + "type.label", + "type.description", + "type.code", + + "provider.id", + "provider.is_enabled", + + "rules.attribute", + "rules.value", + "rules.operator", +] + +/** + * The data to create a shipping method for an order edit. + */ +export type MaybeRefreshShippingMethodsWorkflowInput = { + /** + * The ID of the shipping method to refresh. + */ + shipping_method_id: string + /** + * The ID of the order. + */ + order_id: string + /** + * The ID of the ADD SHIPPING action to update. + */ + action_id: string + /** + * Data to pass for the shipping calculation. + */ + context: CalculatedRMAShippingContext +} + +export const maybeRefreshShippingMethodsWorkflowId = + "maybe-refresh-shipping-methods" +/** + * This workflows refreshes shipping method an order (used in RMA flows). + * The shipping method and the action is updated if the shipping option is calculated. + */ +export const maybeRefreshShippingMethodsWorkflow = createWorkflow( + maybeRefreshShippingMethodsWorkflowId, + function ( + input: MaybeRefreshShippingMethodsWorkflowInput + ): WorkflowResponse { + const shippingMethod = useRemoteQueryStep({ + entry_point: "order_shipping_method", + fields: ["id", "shipping_option_id"], + variables: { + id: input.shipping_method_id, + }, + list: false, + }).config({ name: "fetch-shipping-method" }) + + const shippingOption = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: [...COMMON_OPTIONS_FIELDS], + variables: { id: shippingMethod.shipping_option_id }, + list: false, + }).config({ name: "calculated-option" }) + + const isCalculatedPriceShippingOption = transform( + shippingOption, + (option) => option?.price_type === ShippingOptionPriceType.CALCULATED + ) + + when( + { isCalculatedPriceShippingOption, shippingOption }, + ({ isCalculatedPriceShippingOption, shippingOption }) => + isCalculatedPriceShippingOption + ).then(() => { + const order = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "shipping_address", "items.*", "items.variant.*"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const calculateShippingOptionsPricesData = transform( + { + shippingOption, + order, + input, + }, + ({ shippingOption, order, input }) => { + return [ + { + id: shippingOption.id as string, + optionData: shippingOption.data, + context: { + ...order, + ...input.context, + from_location: + shippingOption.service_zone.fulfillment_set.location, + }, + // data: {}, // TODO: add data + provider_id: shippingOption.provider_id, + } as CalculateShippingOptionPriceDTO, + ] + } + ) + + const prices = calculateShippingOptionsPricesStep( + calculateShippingOptionsPricesData + ) + + const updateData = transform( + { + shippingOption, + prices, + input, + }, + ({ prices, input }) => { + return [ + { + id: input.action_id, + amount: prices[0].calculated_amount, + }, + { + id: input.shipping_method_id, + amount: prices[0].calculated_amount, + is_custom_amount: false, + }, + ] + } + ) + + parallelize( + updateOrderChangeActionsStep([updateData[0]]), + updateOrderShippingMethodsStep([updateData[1]!]) + ) + }) + + return new WorkflowResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/order/workflows/claim/claim-add-new-item.ts b/packages/core/core-flows/src/order/workflows/claim/claim-add-new-item.ts index d46d36661210a..585bafdaf872d 100644 --- a/packages/core/core-flows/src/order/workflows/claim/claim-add-new-item.ts +++ b/packages/core/core-flows/src/order/workflows/claim/claim-add-new-item.ts @@ -22,6 +22,7 @@ import { import { addOrderLineItemsWorkflow } from "../add-line-items" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" import { updateOrderTaxLinesWorkflow } from "../update-tax-lines" +import { refreshClaimShippingWorkflow } from "./refresh-shipping" /** * The data to validate adding new items to a claim. @@ -42,16 +43,16 @@ export type OrderClaimAddNewItemValidationStepInput = { } /** - * This step validates that new items can be added to the claim. If the + * This step validates that new items can be added to the claim. If the * order or claim is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order claim, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = orderClaimAddNewItemValidationStep({ * order: { @@ -85,10 +86,10 @@ export const orderClaimAddNewItemWorkflowId = "claim-add-new-item" /** * This workflow adds outbound (or new) items to a claim. It's used by the * [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#claims_postclaimsidoutbounditems). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to add outbound items to a claim * in your custom flows. - * + * * @example * const { result } = await orderClaimAddNewItemWorkflow(container) * .run({ @@ -102,9 +103,9 @@ export const orderClaimAddNewItemWorkflowId = "claim-add-new-item" * ] * } * }) - * + * * @summary - * + * * Add outbound or new items to a claim. */ export const orderClaimAddNewItemWorkflow = createWorkflow( @@ -191,6 +192,21 @@ export const orderClaimAddNewItemWorkflow = createWorkflow( input: orderChangeActionInput, }) + const refreshArgs = transform( + { orderChange, orderClaim }, + ({ orderChange, orderClaim }) => { + return { + order_change_id: orderChange.id, + claim_id: orderClaim.id, + order_id: orderClaim.order_id, + } + } + ) + + refreshClaimShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(orderClaim.order_id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts b/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts index 449e422983794..f95c7c3f3ddd2 100644 --- a/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts @@ -26,6 +26,7 @@ import { throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" +import { refreshClaimShippingWorkflow } from "./refresh-shipping" /** * The data to validate that items can be requested to return as part of a claim. @@ -56,14 +57,14 @@ export type OrderClaimRequestItemReturnValidationStepInput = { /** * This step validates that items can be requested to return as part of a claim. * If the order, claim, or return is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order claim, order return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = orderClaimRequestItemReturnValidationStep({ * order: { @@ -112,10 +113,10 @@ export const orderClaimRequestItemReturnWorkflowId = "claim-request-item-return" * This workflow requests one or more items to be returned as part of a claim. The * items are added to the claim as inbound items. The workflow is used by the * [Add Inbound Items to Claim Admin API Route](https://docs.medusajs.com/api/admin#claims_postclaimsidinbounditems). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to request items to be returned * as part of a claim in your custom flows. - * + * * @example * const { result } = await orderClaimRequestItemReturnWorkflow(container) * .run({ @@ -130,9 +131,9 @@ export const orderClaimRequestItemReturnWorkflowId = "claim-request-item-return" * ] * } * }) - * + * * @summary - * + * * Request one or more items to be returned as part of a claim. */ export const orderClaimRequestItemReturnWorkflow = createWorkflow( @@ -258,6 +259,21 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow( input: orderChangeActionInput, }) + const refreshArgs = transform( + { orderChange, orderClaim }, + ({ orderChange, orderClaim }) => { + return { + order_change_id: orderChange.id, + claim_id: orderClaim.id, + order_id: orderClaim.order_id, + } + } + ) + + refreshClaimShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(orderClaim.order_id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts b/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts index eaf268332528a..82ae6f09ed511 100644 --- a/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts @@ -1,5 +1,6 @@ import { BigNumberInput, + CalculatedRMAShippingContext, OrderChangeDTO, OrderClaimDTO, OrderDTO, @@ -22,6 +23,7 @@ import { import { prepareShippingMethod } from "../../utils/prepare-shipping-method" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" import { updateOrderTaxLinesWorkflow } from "../update-tax-lines" +import { fetchShippingOptionForOrderWorkflow } from "../../utils/fetch-shipping-option" /** * The data to validate that a shipping method can be created for a claim. @@ -44,14 +46,14 @@ export type CreateClaimShippingMethodValidationStepInput = { /** * This step confirms that a shipping method can be created for a claim. * If the order or claim is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order claim, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = createClaimShippingMethodValidationStep({ * order: { @@ -111,13 +113,13 @@ export const createClaimShippingMethodWorkflowId = * This workflow creates an inbound (return) or outbound (delivering new items) shipping method for a claim. * It's used by the [Add Inbound Shipping Admin API Route](https://docs.medusajs.com/api/admin#claims_postclaimsidinboundshippingmethod), * and the [Add Outbound Shipping Admin API Route](https://docs.medusajs.com/api/admin#claims_postclaimsidoutboundshippingmethod). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to create a shipping method * for a claim in your custom flows. - * + * * @example * To create an outbound shipping method for a claim: - * + * * ```ts * const { result } = await createClaimShippingMethodWorkflow(container) * .run({ @@ -127,9 +129,9 @@ export const createClaimShippingMethodWorkflowId = * } * }) * ``` - * + * * To create an inbound shipping method for a claim, specify the ID of the return associated with the claim: - * + * * ```ts * const { result } = await createClaimShippingMethodWorkflow(container) * .run({ @@ -140,14 +142,16 @@ export const createClaimShippingMethodWorkflowId = * } * }) * ``` - * + * * @summary - * + * * Create an inbound or outbound shipping method for a claim. */ export const createClaimShippingMethodWorkflow = createWorkflow( createClaimShippingMethodWorkflowId, - function (input: CreateClaimShippingMethodWorkflowInput): WorkflowResponse { + function ( + input: CreateClaimShippingMethodWorkflowInput + ): WorkflowResponse { const orderClaim: OrderClaimDTO = useRemoteQueryStep({ entry_point: "order_claim", fields: ["id", "status", "order_id", "canceled_at"], @@ -164,25 +168,9 @@ export const createClaimShippingMethodWorkflow = createWorkflow( 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.shipping_option_id, - calculated_price: { - context: { currency_code: order.currency_code }, - }, - }, - }).config({ name: "fetch-shipping-option" }) - const orderChange: OrderChangeDTO = useRemoteQueryStep({ entry_point: "order_change", - fields: ["id", "status", "version"], + fields: ["id", "status", "version", "actions.*"], variables: { filters: { order_id: orderClaim.order_id, @@ -193,6 +181,52 @@ export const createClaimShippingMethodWorkflow = createWorkflow( list: false, }).config({ name: "order-change-query" }) + const isReturn = transform(input, (data) => { + return !!data.return_id + }) + + const fetchShippingOptionInput = transform( + { input, isReturn, orderChange, order }, + (data) => { + const changeActionType = data.isReturn + ? ChangeActionType.RETURN_ITEM + : ChangeActionType.ITEM_ADD + + const items = data.orderChange.actions + .filter((action) => action.action === changeActionType) + .map((a) => ({ + id: a.details?.reference_id, + quantity: a.details?.quantity, + })) + + const context = data.isReturn + ? { + return_id: data.input.return_id, + return_items: items, + } + : { + claim_id: data.input.claim_id, + claim_items: items, + } + + return { + order_id: data.order.id, + currency_code: data.order.currency_code, + shipping_option_id: data.input.shipping_option_id, + custom_amount: data.input.custom_amount, + context: context as CalculatedRMAShippingContext, + } + } + ) + + const shippingOption = fetchShippingOptionForOrderWorkflow.runAsStep({ + input: fetchShippingOptionInput, + }) + + const shippingOptions = transform(shippingOption, (shippingOption) => { + return [shippingOption] + }) + createClaimShippingMethodValidationStep({ order, orderClaim, orderChange }) const shippingMethodInput = transform( @@ -214,10 +248,6 @@ export const createClaimShippingMethodWorkflow = createWorkflow( return createdMethods.map((item) => item.id) }) - const isReturn = transform(input, (data) => { - return !!data.return_id - }) - updateOrderTaxLinesWorkflow.runAsStep({ input: { order_id: order.id, diff --git a/packages/core/core-flows/src/order/workflows/claim/refresh-shipping.ts b/packages/core/core-flows/src/order/workflows/claim/refresh-shipping.ts new file mode 100644 index 0000000000000..eb23c47ef0bca --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/claim/refresh-shipping.ts @@ -0,0 +1,150 @@ +import { OrderChangeDTO } from "@medusajs/framework/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, + transform, + when, +} from "@medusajs/framework/workflows-sdk" + +import { maybeRefreshShippingMethodsWorkflow } from "../../utils/maybe-refresh-shipping-methods" +import { useRemoteQueryStep } from "../../../common" + +/** + * The data to refresh the shipping methods for an claim. + */ +export type RefreshClaimShippingWorkflowInput = { + /** + * The order change's ID. + */ + order_change_id: string + /** + * The claim's details. + */ + claim_id: string + /** + * The order's ID. + */ + order_id: string +} + +export const refreshClaimShippingWorkflowId = "refresh-claim-shipping" +/** + * This workflow refreshes the shipping methods for an claim in case the shipping option is calculated. + * It refreshes both inbound and outbound shipping methods. + * + * @summary + * + * Refresh claim shipping. + */ +export const refreshClaimShippingWorkflow = createWorkflow( + refreshClaimShippingWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: [ + "id", + "status", + "order_id", + "claim_id", + "return_id", + "actions.*", + ], + variables: { + filters: { + order_id: input.order_id, + claim_id: input.claim_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + const refreshArgs = transform( + { input, orderChange }, + ({ input, orderChange }) => { + const result: Record = [] + + const inboundShippingAction = orderChange.actions.find( + (action) => + action.action === ChangeActionType.SHIPPING_ADD && + !!action.return_id + ) + + const outboundShippingAction = orderChange.actions.find( + (action) => + action.action === ChangeActionType.SHIPPING_ADD && !action.return_id + ) + + if (inboundShippingAction) { + const items = orderChange.actions + .filter((action) => action.action === ChangeActionType.RETURN_ITEM) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })) + + result.push({ + shipping_method_id: inboundShippingAction.reference_id, + order_id: orderChange.order_id, + action_id: inboundShippingAction.id, + context: { + return_id: inboundShippingAction.return_id, + return_items: items, + }, + }) + } else { + result.push(null) + } + + if (outboundShippingAction) { + const items = orderChange.actions + .filter((action) => action.action === ChangeActionType.ITEM_ADD) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })) + + result.push({ + shipping_method_id: outboundShippingAction.reference_id, + order_id: orderChange.order_id, + action_id: outboundShippingAction.id, + context: { + claim_id: outboundShippingAction.claim_id, + claim_items: items, + }, + }) + } else { + result.push(null) + } + + return result + } + ) + + // Refresh inbound shipping method + when({ refreshArgs }, ({ refreshArgs }) => refreshArgs[0] !== null).then( + () => + maybeRefreshShippingMethodsWorkflow + .runAsStep({ + input: refreshArgs[0], + }) + .config({ name: "refresh-inbound-shipping-method" }) + ) + + // Refresh outbound shipping method + when({ refreshArgs }, ({ refreshArgs }) => refreshArgs[1] !== null).then( + () => + maybeRefreshShippingMethodsWorkflow + .runAsStep({ + input: refreshArgs[1], + }) + .config({ name: "refresh-outbound-shipping-method" }) + ) + + return new WorkflowResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/order/workflows/claim/remove-claim-item-action.ts b/packages/core/core-flows/src/order/workflows/claim/remove-claim-item-action.ts index 48a4449a20d85..615bc7c7260bc 100644 --- a/packages/core/core-flows/src/order/workflows/claim/remove-claim-item-action.ts +++ b/packages/core/core-flows/src/order/workflows/claim/remove-claim-item-action.ts @@ -12,6 +12,7 @@ import { WorkflowResponse, createStep, createWorkflow, + transform, } from "@medusajs/framework/workflows-sdk" import { useRemoteQueryStep } from "../../../common" import { @@ -22,6 +23,7 @@ import { throwIfIsCancelled, throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" +import { refreshClaimShippingWorkflow } from "./refresh-shipping" /** * The data to validate that claim items can be removed. @@ -48,14 +50,14 @@ export type RemoveClaimItemActionValidationStepInput = { /** * This step confirms that a claim's items, added as order items, can be removed. * If the order, claim, or order change is canceled, or the action is not claiming an item, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order claim, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = removeClaimItemActionValidationStep({ * order: { @@ -104,22 +106,23 @@ export const removeClaimItemActionValidationStep = createStep( /** * The data to remove order items from a claim. - * + * * @property action_id - The ID of the action associated with the outbound items. - * Every item has an `actions` property, whose value is an array of actions. - * You can find the action name `WRITE_OFF_ITEM` using its `action` property, + * Every item has an `actions` property, whose value is an array of actions. + * You can find the action name `WRITE_OFF_ITEM` using its `action` property, * and use the value of its `id` property. */ -export type RemoveItemClaimActionWorkflowInput = OrderWorkflow.DeleteOrderClaimItemActionWorkflowInput +export type RemoveItemClaimActionWorkflowInput = + OrderWorkflow.DeleteOrderClaimItemActionWorkflowInput export const removeItemClaimActionWorkflowId = "remove-item-claim-action" /** * This workflow removes order items from a claim. It's used by the * [Remove Claim Item Admin API Route](https://docs.medusajs.com/api/admin#claims_deleteclaimsidclaimitemsaction_id). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to remove order items from a claim * in your custom flows. - * + * * @example * const { result } = await removeItemClaimActionWorkflow(container) * .run({ @@ -128,9 +131,9 @@ export const removeItemClaimActionWorkflowId = "remove-item-claim-action" * action_id: "orchact_123", * } * }) - * + * * @summary - * + * * Remove order items from a claim. */ export const removeItemClaimActionWorkflow = createWorkflow( @@ -176,6 +179,21 @@ export const removeItemClaimActionWorkflow = createWorkflow( deleteOrderChangeActionsStep({ ids: [input.action_id] }) + const refreshArgs = transform( + { orderChange, orderClaim }, + ({ orderChange, orderClaim }) => { + return { + order_change_id: orderChange.id, + claim_id: orderClaim.id, + order_id: orderClaim.order_id, + } + } + ) + + refreshClaimShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/claim/update-claim-add-item.ts b/packages/core/core-flows/src/order/workflows/claim/update-claim-add-item.ts index fa3468de65d48..bf49ffce06908 100644 --- a/packages/core/core-flows/src/order/workflows/claim/update-claim-add-item.ts +++ b/packages/core/core-flows/src/order/workflows/claim/update-claim-add-item.ts @@ -23,6 +23,7 @@ import { throwIfIsCancelled, throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" +import { refreshClaimShippingWorkflow } from "./refresh-shipping" /** * The data to validate that a claim's outbound item can be updated. @@ -50,14 +51,14 @@ export type UpdateClaimAddNewItemValidationStepInput = { * This step validates that a claim's new or outbound item can be updated. * If the order, claim, or order change is canceled, no action is adding the item, * or the action is not adding an outbound item, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order claim, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = updateClaimAddItemValidationStep({ * order: { @@ -114,10 +115,10 @@ export const updateClaimAddItemWorkflowId = "update-claim-add-item" /** * This workflow updates a claim's new or outbound item. It's used by the * [Update Outbound Item API Route](https://docs.medusajs.com/api/admin#claims_postclaimsidoutbounditemsaction_id). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to update a claim's new or outbound item * in your custom flows. - * + * * @example * const { result } = await updateClaimAddItemWorkflow(container) * .run({ @@ -129,9 +130,9 @@ export const updateClaimAddItemWorkflowId = "update-claim-add-item" * } * } * }) - * + * * @summary - * + * * Update a claim's new or outbound item. */ export const updateClaimAddItemWorkflow = createWorkflow( @@ -190,6 +191,21 @@ export const updateClaimAddItemWorkflow = createWorkflow( updateOrderChangeActionsStep([updateData]) + const refreshArgs = transform( + { orderChange, orderClaim }, + ({ orderChange, orderClaim }) => { + return { + order_change_id: orderChange.id, + claim_id: orderClaim.id, + order_id: orderClaim.order_id, + } + } + ) + + refreshClaimShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/exchange/begin-order-exchange.ts b/packages/core/core-flows/src/order/workflows/exchange/begin-order-exchange.ts index 3ea521f7a68c7..d39c561d5c59c 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/begin-order-exchange.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/begin-order-exchange.ts @@ -28,14 +28,14 @@ export type BeginOrderExchangeValidationStepInput = { /** * This step validates that an exchange can be requested for an order. * If the order is canceled, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order's details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = beginOrderExchangeValidationStep({ * order: { @@ -53,12 +53,12 @@ export const beginOrderExchangeValidationStep = createStep( export const beginExchangeOrderWorkflowId = "begin-exchange-order" /** - * This workflow requests an order exchange. It's used by the + * This workflow requests an order exchange. It's used by the * [Create Exchange Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchanges). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to request an exchange * for an order in your custom flow. - * + * * @example * const { result } = await beginExchangeOrderWorkflow(container) * .run({ @@ -66,9 +66,9 @@ export const beginExchangeOrderWorkflowId = "begin-exchange-order" * order_id: "order_123", * } * }) - * + * * @summary - * + * * Request an order exchange. */ export const beginExchangeOrderWorkflow = createWorkflow( diff --git a/packages/core/core-flows/src/order/workflows/exchange/create-exchange-shipping-method.ts b/packages/core/core-flows/src/order/workflows/exchange/create-exchange-shipping-method.ts index 62bfc3dd18689..b96cd6f28fe0d 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/create-exchange-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/create-exchange-shipping-method.ts @@ -1,5 +1,6 @@ import { BigNumberInput, + CalculatedRMAShippingContext, OrderChangeDTO, OrderDTO, OrderExchangeDTO, @@ -22,6 +23,7 @@ import { import { prepareShippingMethod } from "../../utils/prepare-shipping-method" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" import { updateOrderTaxLinesWorkflow } from "../update-tax-lines" +import { fetchShippingOptionForOrderWorkflow } from "../../utils/fetch-shipping-option" /** * The data to validate that a shipping method can be created for an exchange. @@ -44,14 +46,14 @@ export type CreateExchangeShippingMethodValidationStepInput = { /** * This step validates that an inbound or outbound shipping method can be created for an exchange. * If the order or exchange is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order exchange, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = createExchangeShippingMethodValidationStep({ * order: { @@ -112,13 +114,13 @@ export const createExchangeShippingMethodWorkflowId = * This workflow creates an inbound (return) or outbound (delivery of new items) shipping method for an exchange. * It's used by the [Add Inbound Shipping Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinboundshippingmethod) * and the [Add Outbound Shipping Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutboundshippingmethod). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to create a shipping method * for an exchange in your custom flow. - * + * * @example * To create an outbound shipping method for the exchange: - * + * * ```ts * const { result } = await createExchangeShippingMethodWorkflow(container) * .run({ @@ -128,9 +130,9 @@ export const createExchangeShippingMethodWorkflowId = * } * }) * ``` - * + * * To create an inbound shipping method, pass the ID of the return associated with the exchange: - * + * * ```ts * const { result } = await createExchangeShippingMethodWorkflow(container) * .run({ @@ -141,14 +143,16 @@ export const createExchangeShippingMethodWorkflowId = * } * }) * ``` - * + * * @summary - * + * * Create an inbound or outbound shipping method for an exchange. */ export const createExchangeShippingMethodWorkflow = createWorkflow( createExchangeShippingMethodWorkflowId, - function (input: CreateExchangeShippingMethodWorkflowInput): WorkflowResponse { + function ( + input: CreateExchangeShippingMethodWorkflowInput + ): WorkflowResponse { const orderExchange: OrderExchangeDTO = useRemoteQueryStep({ entry_point: "order_exchange", fields: ["id", "status", "order_id", "canceled_at"], @@ -165,25 +169,13 @@ export const createExchangeShippingMethodWorkflow = createWorkflow( 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.shipping_option_id, - calculated_price: { - context: { currency_code: order.currency_code }, - }, - }, - }).config({ name: "fetch-shipping-option" }) + const isReturn = transform(input, (data) => { + return !!data.return_id + }) const orderChange: OrderChangeDTO = useRemoteQueryStep({ entry_point: "order_change", - fields: ["id", "status", "version"], + fields: ["id", "status", "version", "actions.*"], variables: { filters: { order_id: orderExchange.order_id, @@ -194,6 +186,48 @@ export const createExchangeShippingMethodWorkflow = createWorkflow( list: false, }).config({ name: "order-change-query" }) + const fetchShippingOptionInput = transform( + { input, isReturn, orderChange, order }, + (data) => { + const changeActionType = data.isReturn + ? ChangeActionType.RETURN_ITEM + : ChangeActionType.ITEM_ADD + + const items = data.orderChange.actions + .filter((action) => action.action === changeActionType) + .map((a) => ({ + id: a.details?.reference_id, + quantity: a.details?.quantity, + })) + + const context = data.isReturn + ? { + return_id: data.input.return_id, + return_items: items, + } + : { + exchange_id: data.input.exchange_id, + exchange_items: items, + } + + return { + order_id: data.order.id, + currency_code: data.order.currency_code, + shipping_option_id: data.input.shipping_option_id, + custom_amount: data.input.custom_amount, + context: context as CalculatedRMAShippingContext, + } + } + ) + + const shippingOption = fetchShippingOptionForOrderWorkflow.runAsStep({ + input: fetchShippingOptionInput, + }) + + const shippingOptions = transform(shippingOption, (shippingOption) => { + return [shippingOption] + }) + createExchangeShippingMethodValidationStep({ order, orderExchange, @@ -219,10 +253,6 @@ export const createExchangeShippingMethodWorkflow = createWorkflow( return createdMethods.map((item) => item.id) }) - const isReturn = transform(input, (data) => { - return !!data.return_id - }) - updateOrderTaxLinesWorkflow.runAsStep({ input: { order_id: order.id, diff --git a/packages/core/core-flows/src/order/workflows/exchange/exchange-add-new-item.ts b/packages/core/core-flows/src/order/workflows/exchange/exchange-add-new-item.ts index 1bd3518e47a5c..91bfae38feef5 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/exchange-add-new-item.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/exchange-add-new-item.ts @@ -22,6 +22,7 @@ import { import { addOrderLineItemsWorkflow } from "../add-line-items" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" import { updateOrderTaxLinesWorkflow } from "../update-tax-lines" +import { refreshExchangeShippingWorkflow } from "./refresh-shipping" /** * The data to validate that new or outbound items can be added to an exchange. @@ -44,14 +45,14 @@ export type ExchangeAddNewItemValidationStepInput = { /** * This step validates that new or outbound items can be added to an exchange. * If the order or exchange is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order exchange, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = exchangeAddNewItemValidationStep({ * order: { @@ -85,10 +86,10 @@ export const orderExchangeAddNewItemWorkflowId = "exchange-add-new-item" /** * This workflow adds new or outbound items to an exchange. It's used by the * [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to add new or outbound items * to an exchange in your custom flow. - * + * * @example * const { result } = await orderExchangeAddNewItemWorkflow(container) * .run({ @@ -102,9 +103,9 @@ export const orderExchangeAddNewItemWorkflowId = "exchange-add-new-item" * ] * } * }) - * + * * @summary - * + * * Add new or outbound items to an exchange. */ export const orderExchangeAddNewItemWorkflow = createWorkflow( @@ -191,6 +192,21 @@ export const orderExchangeAddNewItemWorkflow = createWorkflow( input: orderChangeActionInput, }) + const refreshArgs = transform( + { orderChange, orderExchange }, + ({ orderChange, orderExchange }) => { + return { + order_change_id: orderChange.id, + exchange_id: orderExchange.id, + order_id: orderExchange.order_id, + } + } + ) + + refreshExchangeShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(orderExchange.order_id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/exchange/exchange-request-item-return.ts b/packages/core/core-flows/src/order/workflows/exchange/exchange-request-item-return.ts index edc02bfe6607b..e2f449c23ac27 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/exchange-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/exchange-request-item-return.ts @@ -26,6 +26,7 @@ import { throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" +import { refreshExchangeShippingWorkflow } from "./refresh-shipping" /** * The data to validate that items can be returned as part of an exchange. @@ -55,16 +56,16 @@ export type ExchangeRequestItemReturnValidationStepInput = { /** * This step validates that items can be returned as part of an exchange. - * If the order, exchange, or return is canceled, the order change is not active, + * If the order, exchange, or return is canceled, the order change is not active, * or the item doesn't exist in the order, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order exchange, and order return details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = exchangeRequestItemReturnValidationStep({ * order: { @@ -113,10 +114,10 @@ export const orderExchangeRequestItemReturnWorkflowId = /** * This workflow adds inbound items to be retuned as part of the exchange. It's used * by the [Add Inbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinbounditems). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to add inbound items * to be returned as part of an exchange in your custom flow. - * + * * @example * const { result } = await orderExchangeRequestItemReturnWorkflow(container) * .run({ @@ -131,9 +132,9 @@ export const orderExchangeRequestItemReturnWorkflowId = * ] * } * }) - * + * * @summary - * + * * Add inbound items to be returned as part of the exchange. */ export const orderExchangeRequestItemReturnWorkflow = createWorkflow( @@ -260,6 +261,21 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow( input: orderChangeActionInput, }) + const refreshArgs = transform( + { orderChange, orderExchange }, + ({ orderChange, orderExchange }) => { + return { + order_change_id: orderChange.id, + exchange_id: orderExchange.id, + order_id: orderExchange.order_id, + } + } + ) + + refreshExchangeShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(orderExchange.order_id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/exchange/refresh-shipping.ts b/packages/core/core-flows/src/order/workflows/exchange/refresh-shipping.ts new file mode 100644 index 0000000000000..5777ce6bd5e5f --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/exchange/refresh-shipping.ts @@ -0,0 +1,150 @@ +import { OrderChangeDTO } from "@medusajs/framework/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, + transform, + when, +} from "@medusajs/framework/workflows-sdk" + +import { maybeRefreshShippingMethodsWorkflow } from "../../utils/maybe-refresh-shipping-methods" +import { useRemoteQueryStep } from "../../../common" + +/** + * The data to refresh the shipping methods for an exchange. + */ +export type RefreshExchangeShippingWorkflowInput = { + /** + * The order change's ID. + */ + order_change_id: string + /** + * The exchange's details. + */ + exchange_id: string + /** + * The order's ID. + */ + order_id: string +} + +export const refreshExchangeShippingWorkflowId = "refresh-exchange-shipping" +/** + * This workflow refreshes the shipping methods for an exchange in case the shipping option is calculated. + * It refreshes both inbound and outbound shipping methods. + * + * @summary + * + * Refresh exchange shipping. + */ +export const refreshExchangeShippingWorkflow = createWorkflow( + refreshExchangeShippingWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: [ + "id", + "status", + "order_id", + "exchange_id", + "return_id", + "actions.*", + ], + variables: { + filters: { + order_id: input.order_id, + exchange_id: input.exchange_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + const refreshArgs = transform( + { input, orderChange }, + ({ input, orderChange }) => { + const result: Record = [] + + const inboundShippingAction = orderChange.actions.find( + (action) => + action.action === ChangeActionType.SHIPPING_ADD && + !!action.return_id + ) + + const outboundShippingAction = orderChange.actions.find( + (action) => + action.action === ChangeActionType.SHIPPING_ADD && !action.return_id + ) + + if (inboundShippingAction) { + const items = orderChange.actions + .filter((action) => action.action === ChangeActionType.RETURN_ITEM) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })) + + result.push({ + shipping_method_id: inboundShippingAction.reference_id, + order_id: orderChange.order_id, + action_id: inboundShippingAction.id, + context: { + return_id: inboundShippingAction.return_id, + return_items: items, + }, + }) + } else { + result.push(null) + } + + if (outboundShippingAction) { + const items = orderChange.actions + .filter((action) => action.action === ChangeActionType.ITEM_ADD) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })) + + result.push({ + shipping_method_id: outboundShippingAction.reference_id, + order_id: orderChange.order_id, + action_id: outboundShippingAction.id, + context: { + exchange_id: outboundShippingAction.exchange_id, + exchange_items: items, + }, + }) + } else { + result.push(null) + } + + return result + } + ) + + // Refresh inbound shipping method + when({ refreshArgs }, ({ refreshArgs }) => refreshArgs[0] !== null).then( + () => + maybeRefreshShippingMethodsWorkflow + .runAsStep({ + input: refreshArgs[0], + }) + .config({ name: "refresh-inbound-shipping-method" }) + ) + + // Refresh outbound shipping method + when({ refreshArgs }, ({ refreshArgs }) => refreshArgs[1] !== null).then( + () => + maybeRefreshShippingMethodsWorkflow + .runAsStep({ + input: refreshArgs[1], + }) + .config({ name: "refresh-outbound-shipping-method" }) + ) + + return new WorkflowResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/order/workflows/exchange/remove-exchange-item-action.ts b/packages/core/core-flows/src/order/workflows/exchange/remove-exchange-item-action.ts index 33c8634c122c9..643cdddc44cbe 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/remove-exchange-item-action.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/remove-exchange-item-action.ts @@ -25,6 +25,7 @@ import { throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" import { removeExchangeShippingMethodWorkflow } from "./remove-exchange-shipping-method" +import { refreshExchangeShippingWorkflow } from "./refresh-shipping" /** * The data to validate that an outbound item can be removed from an exchange. @@ -52,14 +53,14 @@ export type RemoveExchangeItemActionValidationStepInput = { * This step validates that an outbound item can be removed from an exchange. * If the order or exchange is canceled, the item is not found, * or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order exchange, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = removeExchangeItemActionValidationStep({ * order: { @@ -79,7 +80,7 @@ export type RemoveExchangeItemActionValidationStepInput = { * action_id: "orchact_123", * } * }) - * + * */ export const removeExchangeItemActionValidationStep = createStep( "remove-item-exchange-action-validation", @@ -111,10 +112,10 @@ export const removeItemExchangeActionWorkflowId = "remove-item-exchange-action" /** * This workflow removes an outbound or new item from an exchange. It's used by * the [Remove Outbound Item API Route](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidoutbounditemsaction_id). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to remove an outbound or new item * from an exchange in your custom flow. - * + * * @example * const { result } = await removeItemExchangeActionWorkflow(container) * .run({ @@ -123,9 +124,9 @@ export const removeItemExchangeActionWorkflowId = "remove-item-exchange-action" * action_id: "orchact_123", * } * }) - * + * * @summary - * + * * Remove an outbound or new item from an exchange. */ export const removeItemExchangeActionWorkflow = createWorkflow( @@ -227,6 +228,28 @@ export const removeItemExchangeActionWorkflow = createWorkflow( }) }) + when( + { actionIdToDelete, orderExchange, orderChange }, + ({ actionIdToDelete }) => { + return !actionIdToDelete + } + ).then(() => { + const refreshArgs = transform( + { orderChange, orderExchange }, + ({ orderChange, orderExchange }) => { + return { + order_change_id: orderChange.id, + exchange_id: orderExchange.id, + order_id: orderExchange.order_id, + } + } + ) + + refreshExchangeShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/exchange/update-exchange-add-item.ts b/packages/core/core-flows/src/order/workflows/exchange/update-exchange-add-item.ts index 8b5b2c785ba87..87579a356c1b1 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/update-exchange-add-item.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/update-exchange-add-item.ts @@ -23,6 +23,7 @@ import { throwIfIsCancelled, throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" +import { refreshExchangeShippingWorkflow } from "./refresh-shipping" /** * The data to validate that an outbound or new item in an exchange can be updated. @@ -50,14 +51,14 @@ export type UpdateExchangeAddItemValidationStepInput = { * This step validates that an outbound or new item can be removed from an exchange. * If the order or exchange is canceled, the item is not found in the exchange, * or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, order exchange, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = updateExchangeAddItemValidationStep({ * order: { @@ -114,10 +115,10 @@ export const updateExchangeAddItemWorkflowId = "update-exchange-add-item" /** * This workflow updates an outbound or new item in the exchange. It's used by the * [Update Outbound Item Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditemsaction_id). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to update an outbound or new item * in an exchange in your custom flow. - * + * * @example * const { result } = await updateExchangeAddItemWorkflow(container) * .run({ @@ -129,9 +130,9 @@ export const updateExchangeAddItemWorkflowId = "update-exchange-add-item" * } * } * }) - * + * * @summary - * + * * Update an outbound or new item in an exchange. */ export const updateExchangeAddItemWorkflow = createWorkflow( @@ -195,6 +196,21 @@ export const updateExchangeAddItemWorkflow = createWorkflow( updateOrderChangeActionsStep([updateData]) + const refreshArgs = transform( + { orderChange, orderExchange }, + ({ orderChange, orderExchange }) => { + return { + order_change_id: orderChange.id, + exchange_id: orderExchange.id, + order_id: orderExchange.order_id, + } + } + ) + + refreshExchangeShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts b/packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts index a98c740b5546b..0dbd73e6eb43f 100644 --- a/packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts @@ -185,7 +185,6 @@ export const createOrderEditShippingMethodWorkflow = createWorkflow( createdMethods, customPrice: input.custom_amount, orderChange, - input, }, ({ shippingOptions, @@ -193,7 +192,6 @@ export const createOrderEditShippingMethodWorkflow = createWorkflow( createdMethods, customPrice, orderChange, - input, }) => { const shippingOption = shippingOptions[0] const createdMethod = createdMethods[0] diff --git a/packages/core/core-flows/src/order/workflows/return/create-return-shipping-method.ts b/packages/core/core-flows/src/order/workflows/return/create-return-shipping-method.ts index 8e46c3fe46553..4c698cd606315 100644 --- a/packages/core/core-flows/src/order/workflows/return/create-return-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/return/create-return-shipping-method.ts @@ -22,6 +22,7 @@ import { import { prepareShippingMethod } from "../../utils/prepare-shipping-method" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" import { updateOrderTaxLinesWorkflow } from "../update-tax-lines" +import { fetchShippingOptionForOrderWorkflow } from "../../utils/fetch-shipping-option" /** * The data to validate that a shipping method can be created for a return. @@ -44,14 +45,14 @@ export type CreateReturnShippingMethodValidationStepInput = { /** * This step validates that a shipping method can be created for a return. * If the order or return is canceled, or the order change is not active, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = createReturnShippingMethodValidationStep({ * order: { @@ -113,10 +114,10 @@ export const createReturnShippingMethodWorkflowId = /** * This workflow creates a shipping method for a return. It's used by the * [Add Shipping Method Store API Route](https://docs.medusajs.com/api/admin#returns_postreturnsidshippingmethod). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you * to create a shipping method for a return in your custom flows. - * + * * @example * const { result } = await createReturnShippingMethodWorkflow(container) * .run({ @@ -125,14 +126,16 @@ export const createReturnShippingMethodWorkflowId = * shipping_option_id: "so_123", * } * }) - * + * * @summary - * + * * Create a shipping method for a return. */ export const createReturnShippingMethodWorkflow = createWorkflow( createReturnShippingMethodWorkflowId, - function (input: CreateReturnShippingMethodWorkflowInput): WorkflowResponse { + function ( + input: CreateReturnShippingMethodWorkflowInput + ): WorkflowResponse { const orderReturn: ReturnDTO = useRemoteQueryStep({ entry_point: "return", fields: [ @@ -142,6 +145,7 @@ export const createReturnShippingMethodWorkflow = createWorkflow( "claim_id", "exchange_id", "canceled_at", + "items.*", ], variables: { id: input.return_id }, list: false, @@ -156,25 +160,9 @@ export const createReturnShippingMethodWorkflow = createWorkflow( 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.shipping_option_id, - calculated_price: { - context: { currency_code: order.currency_code }, - }, - }, - }).config({ name: "fetch-shipping-option" }) - const orderChange: OrderChangeDTO = useRemoteQueryStep({ entry_point: "order_change", - fields: ["id", "status", "version"], + fields: ["id", "status", "version", "actions.*"], variables: { filters: { order_id: orderReturn.order_id, @@ -185,6 +173,36 @@ export const createReturnShippingMethodWorkflow = createWorkflow( list: false, }).config({ name: "order-change-query" }) + const shippingOptionFetchInput = transform( + { orderChange, input, order, orderReturn }, + ({ orderChange, input, order, orderReturn }) => { + return { + order_id: order.id, + shipping_option_id: input.shipping_option_id, + currency_code: order.currency_code, + context: { + return_id: orderReturn.id, + return_items: orderChange.actions + .filter( + (action) => action.action === ChangeActionType.RETURN_ITEM + ) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })), + }, + } + } + ) + + const shippingOption = fetchShippingOptionForOrderWorkflow.runAsStep({ + input: shippingOptionFetchInput, + }) + + const shippingOptions = transform(shippingOption, (shippingOption) => { + return [shippingOption] + }) + createReturnShippingMethodValidationStep({ order, orderReturn, diff --git a/packages/core/core-flows/src/order/workflows/return/refresh-shipping.ts b/packages/core/core-flows/src/order/workflows/return/refresh-shipping.ts new file mode 100644 index 0000000000000..beba7c3489fd1 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/return/refresh-shipping.ts @@ -0,0 +1,96 @@ +import { OrderChangeDTO } from "@medusajs/framework/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, + transform, + when, +} from "@medusajs/framework/workflows-sdk" + +import { maybeRefreshShippingMethodsWorkflow } from "../../utils/maybe-refresh-shipping-methods" +import { useRemoteQueryStep } from "../../../common" + +/** + * The data to validate that items can be added to a return. + */ +export type RequestItemReturnValidationStepInput = { + /** + * The order change's ID. + */ + order_change_id: string + /** + * The return's details. + */ + return_id: string + /** + * The order's ID. + */ + order_id: string +} + +export const refreshReturnShippingWorkflowId = "refresh-return-shipping" +/** + * This workflow refreshes the shipping method for a return in case the shipping option is calculated. + * + * @summary + * + * Refresh return shipping. + */ +export const refreshReturnShippingWorkflow = createWorkflow( + refreshReturnShippingWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "order_id", "return_id", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + return_id: input.return_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + const refreshArgs = transform( + { input, orderChange }, + ({ input, orderChange }) => { + const shippingAction = orderChange.actions.find( + (action) => action.action === ChangeActionType.SHIPPING_ADD + ) + + const items = orderChange.actions + .filter((action) => action.action === ChangeActionType.RETURN_ITEM) + .map((a) => ({ + id: a.details?.reference_id as string, + quantity: a.details?.quantity as number, + })) + + if (shippingAction) { + return { + shipping_method_id: shippingAction.reference_id, + order_id: orderChange.order_id, + action_id: shippingAction.id, + context: { + return_id: input.return_id, + return_items: items, + }, + } + } + + return null + } + ) + + when({ refreshArgs }, ({ refreshArgs }) => refreshArgs !== null).then(() => + maybeRefreshShippingMethodsWorkflow.runAsStep({ + input: refreshArgs, + }) + ) + + return new WorkflowResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts b/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts index 1e051c4c66dc0..f0e3e38d3089a 100644 --- a/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts +++ b/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts @@ -26,6 +26,7 @@ import { } from "../../utils/order-validation" import { removeReturnShippingMethodWorkflow } from "./remove-return-shipping-method" import { updateReturnWorkflow } from "./update-return" +import { refreshReturnShippingWorkflow } from "./refresh-shipping" /** * The data to validate that a return item can be removed. @@ -54,14 +55,14 @@ export type RemoveItemReturnActionValidationStepInput = { * If the order or return is canceled, the order change is not active, * the return request is not found, * or the action is not a request return action, the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = removeReturnItemActionValidationStep({ * order: { @@ -114,10 +115,10 @@ export const removeItemReturnActionWorkflowId = "remove-item-return-action" /** * This workflow removes a return item. It's used by the * [Remove Item from Return Admin API Route](https://docs.medusajs.com/api/admin#returns_deletereturnsidrequestitemsaction_id). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you * to remove an item from a return request in your custom flows. - * + * * @example * const { result } = await removeItemReturnActionWorkflow(container) * .run({ @@ -126,9 +127,9 @@ export const removeItemReturnActionWorkflowId = "remove-item-return-action" * action_id: "orchac_123", * } * }) - * + * * @summary - * + * * Remove an item from a return. */ export const removeItemReturnActionWorkflow = createWorkflow( @@ -249,6 +250,25 @@ export const removeItemReturnActionWorkflow = createWorkflow( }) }) + when({ actionIdToDelete }, ({ actionIdToDelete }) => { + return !actionIdToDelete + }).then(() => { + const refreshArgs = transform( + { orderChange, orderReturn }, + ({ orderChange, orderReturn }) => { + return { + order_change_id: orderChange.id, + return_id: orderReturn.id, + order_id: orderReturn.order_id, + } + } + ) + + refreshReturnShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/return/request-item-return.ts b/packages/core/core-flows/src/order/workflows/return/request-item-return.ts index f6ce9ecf78df6..6452c89650968 100644 --- a/packages/core/core-flows/src/order/workflows/return/request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/return/request-item-return.ts @@ -22,7 +22,7 @@ import { } from "../../utils/order-validation" import { validateReturnReasons } from "../../utils/validate-return-reason" import { createOrderChangeActionsWorkflow } from "../create-order-change-actions" - +import { refreshReturnShippingWorkflow } from "./refresh-shipping" /** * The data to validate that items can be added to a return. */ @@ -50,14 +50,14 @@ export type RequestItemReturnValidationStepInput = { * If the order or return is canceled, the order change is not active, * the items do not exist in the order, or the return reasons are invalid, * the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = requestItemReturnValidationStep({ * order: { @@ -110,12 +110,12 @@ export const requestItemReturnValidationStep = createStep( export const requestItemReturnWorkflowId = "request-item-return" /** - * This workflow adds items to a return. It's used by the + * This workflow adds items to a return. It's used by the * [Add Requested Items to Return Admin API Route](https://docs.medusajs.com/api/admin#returns_postreturnsidrequestitems). - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to add items to a return * in your custom flows. - * + * * @example * const { result } = await requestItemReturnWorkflow(container) * .run({ @@ -129,9 +129,9 @@ export const requestItemReturnWorkflowId = "request-item-return" * ] * } * }) - * + * * @summary - * + * * Add items to a return. */ export const requestItemReturnWorkflow = createWorkflow( @@ -201,6 +201,21 @@ export const requestItemReturnWorkflow = createWorkflow( input: orderChangeActionInput, }) + const refreshArgs = transform( + { orderChange, orderReturn }, + ({ orderChange, orderReturn }) => { + return { + order_change_id: orderChange.id, + return_id: orderReturn.id, + order_id: orderReturn.order_id, + } + } + ) + + refreshReturnShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/order/workflows/return/update-request-item-return.ts b/packages/core/core-flows/src/order/workflows/return/update-request-item-return.ts index 543d88d89868b..c8ff64c569559 100644 --- a/packages/core/core-flows/src/order/workflows/return/update-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/return/update-request-item-return.ts @@ -28,6 +28,7 @@ import { throwIfOrderChangeIsNotActive, } from "../../utils/order-validation" import { validateReturnReasons } from "../../utils/validate-return-reason" +import { refreshReturnShippingWorkflow } from "./refresh-shipping" /** * The data to validate that an item in a return can be updated. @@ -56,14 +57,14 @@ export type UpdateRequestItemReturnValidationStepInput = { * If the order or return is canceled, the order change is not active, * the return request is not found, or the action is not requesting an item return, * the step will throw an error. - * + * * :::note - * + * * You can retrieve an order, return, and order change details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query), * or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep). - * + * * ::: - * + * * @example * const data = updateRequestItemReturnValidationStep({ * order: { @@ -130,12 +131,12 @@ export const updateRequestItemReturnValidationStep = createStep( export const updateRequestItemReturnWorkflowId = "update-request-item-return" /** - * This workflow updates a requested item in a return. It's used by the + * This workflow updates a requested item in a return. It's used by the * [Update Requested Item in Return Admin API Route](https://docs.medusajs.com/api/admin#returns_postreturnsidrequestitemsaction_id). - * - * You can use this workflow within your customizations or your own custom workflows, allowing you to update an + * + * You can use this workflow within your customizations or your own custom workflows, allowing you to update an * item in a return in your custom flows. - * + * * @example * const { result } = await updateRequestItemReturnWorkflow(container) * .run({ @@ -147,9 +148,9 @@ export const updateRequestItemReturnWorkflowId = "update-request-item-return" * } * } * }) - * + * * @summary - * + * * Update a requested item in a return. */ export const updateRequestItemReturnWorkflow = createWorkflow( @@ -216,6 +217,21 @@ export const updateRequestItemReturnWorkflow = createWorkflow( updateOrderChangeActionsStep([updateData]) + const refreshArgs = transform( + { orderChange, orderReturn }, + ({ orderChange, orderReturn }) => { + return { + order_change_id: orderChange.id, + return_id: orderReturn.id, + order_id: orderReturn.order_id, + } + } + ) + + refreshReturnShippingWorkflow.runAsStep({ + input: refreshArgs, + }) + return new WorkflowResponse(previewOrderChangeStep(order.id)) } ) diff --git a/packages/core/core-flows/src/payment/steps/refund-payments.ts b/packages/core/core-flows/src/payment/steps/refund-payments.ts index 7b1654477787b..78e033213a8f0 100644 --- a/packages/core/core-flows/src/payment/steps/refund-payments.ts +++ b/packages/core/core-flows/src/payment/steps/refund-payments.ts @@ -21,7 +21,7 @@ export type RefundPaymentsStepInput = { */ payment_id: string /** - * The amount to refund. + * The amount to refund. */ amount: BigNumberInput /** @@ -36,10 +36,7 @@ export const refundPaymentsStepId = "refund-payments-step" */ export const refundPaymentsStep = createStep( refundPaymentsStepId, - async ( - input: RefundPaymentsStepInput, - { container } - ) => { + async (input: RefundPaymentsStepInput, { container }) => { const logger = container.resolve(ContainerRegistrationKeys.LOGGER) const paymentModule = container.resolve( Modules.PAYMENT diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index bdeeb429c75e2..e9e7f39a2eb44 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -120,6 +120,22 @@ export interface UpdateShippingOptionDTO { */ export interface UpsertShippingOptionDTO extends UpdateShippingOptionDTO {} +type CalculateShippingItems = { + /** + * The ID of the order item. Lookup in context.items for the details about variant / product. + */ + id: string + /** + * The quantity to ship. + */ + quantity: number +} + +export type CalculatedRMAShippingContext = + | { return_id: string; return_items: CalculateShippingItems[] } + | { exchange_id: string; exchange_items: CalculateShippingItems[] } + | { claim_id: string; claim_items: CalculateShippingItems[] } + /** * The data needed for the associated fulfillment provider to calculate the price of a shipping option. */ @@ -149,11 +165,12 @@ export interface CalculateShippingOptionPriceDTO { */ context: CartPropsForFulfillment & { /** - * The location that the items will be shipped from. + * The location that the items will be shipped from (or to if it is a return). */ from_location?: StockLocationDTO + [k: string]: unknown - } + } & CalculatedRMAShippingContext } /**