From 8d530aa7f2612bfdf52a729f7d366f7f266ef490 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Mon, 15 Jul 2024 08:08:43 -0300 Subject: [PATCH] Chore(core-flows,order): exchange/claim add item (#8126) --- .../order/workflows/begin-order-claim.spec.ts | 4 +- .../workflows/begin-order-exchange.spec.ts | 131 +++++++++++++--- .../workflows/begin-order-return.spec.ts | 2 +- .../src/order/steps/create-line-items.ts | 33 ++++ .../core/core-flows/src/order/steps/index.ts | 1 + .../src/order/workflows/add-line-items.ts | 148 ++++++++++++++++++ .../src/order/workflows/begin-order-claim.ts | 2 +- .../order/workflows/begin-order-exchange.ts | 2 +- .../src/order/workflows/begin-return.ts | 2 +- .../src/order/workflows/claim-add-new-item.ts | 109 +++++++++++++ .../workflows/claim-request-item-return.ts | 2 - .../src/order/workflows/create-orders.ts | 3 +- .../order/workflows/exchange-add-new-item.ts | 109 +++++++++++++ .../workflows/exchange-request-item-return.ts | 2 - .../core-flows/src/order/workflows/index.ts | 2 + packages/core/types/src/order/service.ts | 9 +- .../types/src/workflow/order/add-new-item.ts | 34 ++++ .../src/workflow/order/begin-claim-order.ts | 2 +- .../workflow/order/begin-exchange-order.ts | 2 +- .../src/workflow/order/begin-return-order.ts | 2 +- .../core/types/src/workflow/order/index.ts | 1 + .../__tests__/util/actions/exchanges.ts | 3 - .../src/services/order-module-service.ts | 59 +++++-- .../order/src/utils/actions/item-add.ts | 6 +- .../order/src/utils/apply-order-changes.ts | 20 ++- .../order/src/utils/set-action-reference.ts | 7 - .../order/src/utils/transform-order.ts | 9 ++ 27 files changed, 636 insertions(+), 70 deletions(-) create mode 100644 packages/core/core-flows/src/order/steps/create-line-items.ts create mode 100644 packages/core/core-flows/src/order/workflows/add-line-items.ts create mode 100644 packages/core/core-flows/src/order/workflows/claim-add-new-item.ts create mode 100644 packages/core/core-flows/src/order/workflows/exchange-add-new-item.ts create mode 100644 packages/core/types/src/workflow/order/add-new-item.ts diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts index 3a9793591e..0cdc305595 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts @@ -361,7 +361,7 @@ medusaIntegrationTestRunner({ it("should begin a claim order", async () => { const order = await createOrderFixture({ container, product }) - const createClaimOrderData: OrderWorkflow.beginOrderClaimWorkflowInput = + const createClaimOrderData: OrderWorkflow.BeginOrderClaimWorkflowInput = { type: ClaimType.REFUND, order_id: order.id, @@ -425,8 +425,6 @@ medusaIntegrationTestRunner({ action: "RETURN_ITEM", details: expect.objectContaining({ quantity: 1, - return_id: expect.stringContaining("return_"), - claim_id: claimOrder.id, reference_id: order.items![0].id, }), }), diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts index 2c4a1ac2ec..b28de4aec7 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts @@ -1,6 +1,7 @@ import { beginExchangeOrderWorkflow, createShippingOptionsWorkflow, + orderExchangeAddNewItemWorkflow, orderExchangeRequestItemReturnWorkflow, } from "@medusajs/core-flows" import { @@ -9,7 +10,10 @@ import { IStockLocationService, OrderWorkflow, ProductDTO, + RegionDTO, + SalesChannelDTO, StockLocationDTO, + UserDTO, } from "@medusajs/types" import { ContainerRegistrationKeys, @@ -38,6 +42,12 @@ async function prepareDataFixtures({ container }) { const productModule = container.resolve(ModuleRegistrationName.PRODUCT) const pricingModule = container.resolve(ModuleRegistrationName.PRICING) const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) + const customerService = container.resolve(ModuleRegistrationName.CUSTOMER) + + const customer = await customerService.createCustomers({ + first_name: "joe", + email: "joe@gmail.com", + }) const shippingProfile = await fulfillmentService.createShippingProfiles({ name: "test", @@ -96,17 +106,44 @@ async function prepareDataFixtures({ container }) { title: "Test variant", sku: "test-variant", }, + { + title: "another product variant", + sku: "another-variant", + }, ], }, ]) - const inventoryItem = await inventoryModule.createInventoryItems({ - sku: "inv-1234", - }) + const priceSets = await pricingModule.createPriceSets([ + { + prices: [ + { + amount: 45.987, + region_id: region.id, + currency_code: "eur", + }, + ], + }, + ]) + + const inventoryItems = await inventoryModule.createInventoryItems([ + { + sku: "inv-1234", + }, + { + sku: "another-inv-987", + }, + ]) await inventoryModule.createInventoryLevels([ { - inventory_item_id: inventoryItem.id, + inventory_item_id: inventoryItems[0].id, + location_id: location.id, + stocked_quantity: 2, + reserved_quantity: 0, + }, + { + inventory_item_id: inventoryItems[1].id, location_id: location.id, stocked_quantity: 2, reserved_quantity: 0, @@ -137,17 +174,28 @@ async function prepareDataFixtures({ container }) { variant_id: product.variants[0].id, }, [Modules.INVENTORY]: { - inventory_item_id: inventoryItem.id, + inventory_item_id: inventoryItems[0].id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[1].id, + }, + [Modules.INVENTORY]: { + inventory_item_id: inventoryItems[1].id, + }, + }, + { + [Modules.PRODUCT]: { + variant_id: product.variants[1].id, + }, + [Modules.PRICING]: { + price_set_id: priceSets[0].id, }, }, ]) await pricingModule.createPricePreferences([ - { - attribute: "currency_code", - value: "usd", - is_tax_inclusive: true, - }, { attribute: "region_id", value: region.id, @@ -223,13 +271,20 @@ async function prepareDataFixtures({ container }) { location, product, fulfillmentSet, + customer, } } -async function createOrderFixture({ container, product }) { +async function createOrderFixture({ + container, + product, + salesChannel, + customer, + region, +}) { const orderService = container.resolve(ModuleRegistrationName.ORDER) let order = await orderService.createOrders({ - region_id: "test_region_id", + region_id: region.id, email: "foo@bar.com", items: [ { @@ -249,7 +304,7 @@ async function createOrderFixture({ container, product }) { ], } as any, ], - sales_channel_id: "test", + sales_channel_id: salesChannel.id, shipping_address: { first_name: "Test", last_name: "Test", @@ -290,8 +345,8 @@ async function createOrderFixture({ container, product }) { ], }, ], - currency_code: "usd", - customer_id: "joe", + currency_code: "eur", + customer_id: customer.id, }) await orderService.addOrderAction([ @@ -339,6 +394,9 @@ medusaIntegrationTestRunner({ describe("Begin exchange order workflow", () => { let product: ProductDTO + let salesChannel: SalesChannelDTO + let customer: UserDTO + let region: RegionDTO beforeEach(async () => { const fixtures = await prepareDataFixtures({ @@ -346,12 +404,21 @@ medusaIntegrationTestRunner({ }) product = fixtures.product + salesChannel = fixtures.salesChannel + customer = fixtures.customer + region = fixtures.region }) it("should begin an exchange order, request item return", async () => { - const order = await createOrderFixture({ container, product }) + const order = await createOrderFixture({ + container, + product, + salesChannel, + customer, + region, + }) - const createExchangeOrderData: OrderWorkflow.beginOrderExchangeWorkflowInput = + const createExchangeOrderData: OrderWorkflow.BeginOrderExchangeWorkflowInput = { order_id: order.id, metadata: { @@ -423,8 +490,6 @@ medusaIntegrationTestRunner({ action: "RETURN_ITEM", details: expect.objectContaining({ quantity: 1, - return_id: expect.stringContaining("return_"), - exchange_id: exchangeOrder.id, reference_id: order.items![0].id, }), }), @@ -490,6 +555,34 @@ medusaIntegrationTestRunner({ ], }) ) + + const { result: addItemPreview } = + await orderExchangeAddNewItemWorkflow.run({ + container, + input: { + exchange_id: exchangeOrder.id, + items: [ + { + variant_id: product.variants[1].id, + quantity: 2, + unit_price: 14.786, + }, + ], + }, + }) + + expect(addItemPreview.items).toHaveLength(2) + expect(addItemPreview.items[1].actions).toEqual([ + expect.objectContaining({ + action: "ITEM_ADD", + reference: "order_exchange", + reference_id: exchangeOrder.id, + details: expect.objectContaining({ + quantity: 2, + unit_price: 14.786, + }), + }), + ]) }) }) }, diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts index 8b8fa369fe..cc5d006400 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts @@ -359,7 +359,7 @@ medusaIntegrationTestRunner({ it("should begin a return order", async () => { const order = await createOrderFixture({ container, product }) - const createReturnOrderData: OrderWorkflow.beginOrderReturnWorkflowInput = + const createReturnOrderData: OrderWorkflow.BeginOrderReturnWorkflowInput = { order_id: order.id, } diff --git a/packages/core/core-flows/src/order/steps/create-line-items.ts b/packages/core/core-flows/src/order/steps/create-line-items.ts new file mode 100644 index 0000000000..de2dafbb11 --- /dev/null +++ b/packages/core/core-flows/src/order/steps/create-line-items.ts @@ -0,0 +1,33 @@ +import { CreateOrderLineItemDTO } from "@medusajs/types" +import { ModuleRegistrationName } from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +interface StepInput { + items: CreateOrderLineItemDTO[] +} + +export const createOrderLineItemsStepId = "create-line-items-step" +export const createOrderLineItemsStep = createStep( + createOrderLineItemsStepId, + async (input: StepInput, { container }) => { + const orderModule = container.resolve(ModuleRegistrationName.ORDER) + + const createdItems = input.items.length + ? await orderModule.createLineItems(input.items) + : [] + + return new StepResponse( + createdItems, + createdItems.map((c) => c.id) + ) + }, + async (itemIds, { container }) => { + if (!itemIds?.length) { + return + } + + const orderModule = container.resolve(ModuleRegistrationName.ORDER) + + await orderModule.deleteLineItems(itemIds) + } +) diff --git a/packages/core/core-flows/src/order/steps/index.ts b/packages/core/core-flows/src/order/steps/index.ts index c6a681f339..994047a744 100644 --- a/packages/core/core-flows/src/order/steps/index.ts +++ b/packages/core/core-flows/src/order/steps/index.ts @@ -8,6 +8,7 @@ export * from "./complete-orders" export * from "./create-claims" export * from "./create-complete-return" export * from "./create-exchanges" +export * from "./create-line-items" export * from "./create-order-change" export * from "./create-order-change-actions" export * from "./create-orders" diff --git a/packages/core/core-flows/src/order/workflows/add-line-items.ts b/packages/core/core-flows/src/order/workflows/add-line-items.ts new file mode 100644 index 0000000000..c1daeec864 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/add-line-items.ts @@ -0,0 +1,148 @@ +import { OrderLineItemDTO, OrderWorkflow } from "@medusajs/types" +import { MathBN, MedusaError } from "@medusajs/utils" +import { + WorkflowData, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { findOneOrAnyRegionStep } from "../../definition/cart/steps/find-one-or-any-region" +import { findOrCreateCustomerStep } from "../../definition/cart/steps/find-or-create-customer" +import { findSalesChannelStep } from "../../definition/cart/steps/find-sales-channel" +import { getVariantPriceSetsStep } from "../../definition/cart/steps/get-variant-price-sets" +import { validateVariantPricesStep } from "../../definition/cart/steps/validate-variant-prices" +import { prepareLineItemData } from "../../definition/cart/utils/prepare-line-item-data" +import { confirmVariantInventoryWorkflow } from "../../definition/cart/workflows/confirm-variant-inventory" +import { createOrderLineItemsStep } from "../steps" +import { productVariantsFields } from "../utils/fields" +import { prepareCustomLineItemData } from "../utils/prepare-custom-line-item-data" + +function prepareLineItems(data) { + const items = (data.input.items ?? []).map((item) => { + const variant = data.variants.find((v) => v.id === item.variant_id)! + + if (!variant) { + return prepareCustomLineItemData({ + variant: { + ...item, + }, + unitPrice: MathBN.max(0, item.unit_price), + isTaxInclusive: item.is_tax_inclusive, + quantity: item.quantity as number, + metadata: item?.metadata ?? {}, + }) + } + + return prepareLineItemData({ + variant: variant, + unitPrice: MathBN.max( + 0, + item.unit_price ?? + data.priceSets[item.variant_id!]?.raw_calculated_amount + ), + isTaxInclusive: + item.is_tax_inclusive ?? + data.priceSets[item.variant_id!]?.is_calculated_price_tax_inclusive, + quantity: item.quantity as number, + metadata: item?.metadata ?? {}, + taxLines: item.tax_lines || [], + adjustments: item.adjustments || [], + }) + }) + + return items +} + +export const addOrderLineItemsWorkflowId = "order-add-line-items" +export const addOrderLineItemsWorkflow = createWorkflow( + addOrderLineItemsWorkflowId, + ( + input: WorkflowData + ): WorkflowData => { + const order = useRemoteQueryStep({ + entry_point: "orders", + fields: [ + "id", + "sales_channel_id", + "region_id", + "customer_id", + "email", + "currency_code", + ], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const variantIds = transform({ input }, (data) => { + return (data.input.items ?? []) + .map((item) => item.variant_id) + .filter(Boolean) as string[] + }) + + const [salesChannel, region, customerData] = parallelize( + findSalesChannelStep({ + salesChannelId: order.sales_channel_id, + }), + findOneOrAnyRegionStep({ + regionId: order.region_id, + }), + findOrCreateCustomerStep({ + customerId: order.customer_id, + email: order.email, + }) + ) + + const pricingContext = transform( + { input, region, customerData, order }, + (data) => { + if (!data.region) { + throw new MedusaError(MedusaError.Types.NOT_FOUND, "Region not found") + } + + return { + currency_code: data.order.currency_code ?? data.region.currency_code, + region_id: data.region.id, + customer_id: data.customerData.customer?.id, + } + } + ) + + const variants = useRemoteQueryStep({ + entry_point: "variants", + fields: productVariantsFields, + variables: { + id: variantIds, + calculated_price: { + context: pricingContext, + }, + }, + throw_if_key_not_found: true, + }).config({ name: "variants-query" }) + + validateVariantPricesStep({ variants }) + + confirmVariantInventoryWorkflow.runAsStep({ + input: { + sales_channel_id: salesChannel.id, + variants, + items: input.items!, + }, + }) + + const priceSets = getVariantPriceSetsStep({ + variantIds, + context: pricingContext, + }) + + const lineItems = transform( + { priceSets, input, variants }, + prepareLineItems + ) + + return createOrderLineItemsStep({ + items: lineItems, + }) + } +) diff --git a/packages/core/core-flows/src/order/workflows/begin-order-claim.ts b/packages/core/core-flows/src/order/workflows/begin-order-claim.ts index c02813f4fc..4991f166e3 100644 --- a/packages/core/core-flows/src/order/workflows/begin-order-claim.ts +++ b/packages/core/core-flows/src/order/workflows/begin-order-claim.ts @@ -21,7 +21,7 @@ export const beginClaimOrderWorkflowId = "begin-claim-order" export const beginClaimOrderWorkflow = createWorkflow( beginClaimOrderWorkflowId, function ( - input: WorkflowData + input: WorkflowData ): WorkflowData { const order: OrderDTO = useRemoteQueryStep({ entry_point: "orders", diff --git a/packages/core/core-flows/src/order/workflows/begin-order-exchange.ts b/packages/core/core-flows/src/order/workflows/begin-order-exchange.ts index 45183b1523..3258c369b5 100644 --- a/packages/core/core-flows/src/order/workflows/begin-order-exchange.ts +++ b/packages/core/core-flows/src/order/workflows/begin-order-exchange.ts @@ -21,7 +21,7 @@ export const beginExchangeOrderWorkflowId = "begin-exchange-order" export const beginExchangeOrderWorkflow = createWorkflow( beginExchangeOrderWorkflowId, function ( - input: WorkflowData + input: WorkflowData ): WorkflowData { const order: OrderDTO = useRemoteQueryStep({ entry_point: "orders", diff --git a/packages/core/core-flows/src/order/workflows/begin-return.ts b/packages/core/core-flows/src/order/workflows/begin-return.ts index 3d4629bee3..9dfefaef3b 100644 --- a/packages/core/core-flows/src/order/workflows/begin-return.ts +++ b/packages/core/core-flows/src/order/workflows/begin-return.ts @@ -21,7 +21,7 @@ export const beginReturnOrderWorkflowId = "begin-return-order" export const beginReturnOrderWorkflow = createWorkflow( beginReturnOrderWorkflowId, function ( - input: WorkflowData + input: WorkflowData ): WorkflowData { const order: OrderDTO = useRemoteQueryStep({ entry_point: "orders", diff --git a/packages/core/core-flows/src/order/workflows/claim-add-new-item.ts b/packages/core/core-flows/src/order/workflows/claim-add-new-item.ts new file mode 100644 index 0000000000..302ef11c29 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/claim-add-new-item.ts @@ -0,0 +1,109 @@ +import { + OrderChangeDTO, + OrderClaimDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" +import { previewOrderChangeStep } from "../steps/preview-order-change" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, + throwIfOrderIsCancelled, +} from "../utils/order-validation" +import { addOrderLineItemsWorkflow } from "./add-line-items" + +const validationStep = createStep( + "claim-add-new-item-validation", + async function ({ + order, + orderChange, + orderClaim, + }: { + order: OrderDTO + orderClaim: OrderClaimDTO + orderChange: OrderChangeDTO + }) { + throwIfOrderIsCancelled({ order }) + throwIfIsCancelled(orderClaim, "Claim") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const orderClaimAddNewItemWorkflowId = "claim-add-new-item" +export const orderClaimAddNewItemWorkflow = createWorkflow( + orderClaimAddNewItemWorkflowId, + function ( + input: WorkflowData + ): WorkflowData { + const orderClaim = useRemoteQueryStep({ + entry_point: "order_claim", + fields: ["id", "order_id"], + variables: { id: input.claim_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "claim-query" }) + + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "items.*"], + variables: { id: orderClaim.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status"], + variables: { order_id: orderClaim.order_id }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ + order, + orderClaim, + orderChange, + }) + + const lineItems = addOrderLineItemsWorkflow.runAsStep({ + input: { + order_id: order.id, + items: input.items, + }, + }) + + const orderChangeActionInput = transform( + { order, orderChange, orderClaim, items: input.items, lineItems }, + ({ order, orderChange, orderClaim, items, lineItems }) => { + return items.map((item, index) => ({ + order_change_id: orderChange.id, + order_id: order.id, + claim_id: orderClaim.id, + version: orderChange.version, + action: ChangeActionType.ITEM_ADD, + internal_note: item.internal_note, + reference: "order_claim", + reference_id: orderClaim.id, + details: { + reference_id: lineItems[index].id, + quantity: item.quantity, + unit_price: item.unit_price, + metadata: item.metadata, + }, + })) + } + ) + + createOrderChangeActionsStep(orderChangeActionInput) + + return previewOrderChangeStep(orderClaim.order_id) + } +) diff --git a/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts b/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts index 41991259b5..98e5b424e9 100644 --- a/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/claim-request-item-return.ts @@ -147,8 +147,6 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow( reference_id: orderReturn.id, details: { reference_id: item.id, - claim_id: orderClaim.id, - return_id: orderReturn.id, quantity: item.quantity, metadata: item.metadata, }, diff --git a/packages/core/core-flows/src/order/workflows/create-orders.ts b/packages/core/core-flows/src/order/workflows/create-orders.ts index 3a81a8ed4d..c2273832fc 100644 --- a/packages/core/core-flows/src/order/workflows/create-orders.ts +++ b/packages/core/core-flows/src/order/workflows/create-orders.ts @@ -38,7 +38,8 @@ function prepareLineItems(data) { variant: variant, unitPrice: MathBN.max( 0, - item.unit_price ?? data.priceSets[item.variant_id!]?.calculated_amount + item.unit_price ?? + data.priceSets[item.variant_id!]?.raw_calculated_amount ), isTaxInclusive: item.is_tax_inclusive ?? diff --git a/packages/core/core-flows/src/order/workflows/exchange-add-new-item.ts b/packages/core/core-flows/src/order/workflows/exchange-add-new-item.ts new file mode 100644 index 0000000000..288a3126ac --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/exchange-add-new-item.ts @@ -0,0 +1,109 @@ +import { + OrderChangeDTO, + OrderDTO, + OrderExchangeDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../common" +import { createOrderChangeActionsStep } from "../steps/create-order-change-actions" +import { previewOrderChangeStep } from "../steps/preview-order-change" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, + throwIfOrderIsCancelled, +} from "../utils/order-validation" +import { addOrderLineItemsWorkflow } from "./add-line-items" + +const validationStep = createStep( + "exchange-add-new-item-validation", + async function ({ + order, + orderChange, + orderExchange, + }: { + order: OrderDTO + orderExchange: OrderExchangeDTO + orderChange: OrderChangeDTO + }) { + throwIfOrderIsCancelled({ order }) + throwIfIsCancelled(orderExchange, "Exchange") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const orderExchangeAddNewItemWorkflowId = "exchange-add-new-item" +export const orderExchangeAddNewItemWorkflow = createWorkflow( + orderExchangeAddNewItemWorkflowId, + function ( + input: WorkflowData + ): WorkflowData { + const orderExchange = useRemoteQueryStep({ + entry_point: "order_exchange", + fields: ["id", "order_id"], + variables: { id: input.exchange_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "exchange-query" }) + + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "items.*"], + variables: { id: orderExchange.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status"], + variables: { order_id: orderExchange.order_id }, + list: false, + }).config({ name: "order-change-query" }) + + validationStep({ + order, + orderExchange, + orderChange, + }) + + const lineItems = addOrderLineItemsWorkflow.runAsStep({ + input: { + order_id: order.id, + items: input.items, + }, + }) + + const orderChangeActionInput = transform( + { order, orderChange, orderExchange, items: input.items, lineItems }, + ({ order, orderChange, orderExchange, items, lineItems }) => { + return items.map((item, index) => ({ + order_change_id: orderChange.id, + order_id: order.id, + exchange_id: orderExchange.id, + version: orderChange.version, + action: ChangeActionType.ITEM_ADD, + internal_note: item.internal_note, + reference: "order_exchange", + reference_id: orderExchange.id, + details: { + reference_id: lineItems[index].id, + quantity: item.quantity, + unit_price: item.unit_price, + metadata: item.metadata, + }, + })) + } + ) + + createOrderChangeActionsStep(orderChangeActionInput) + + return previewOrderChangeStep(orderExchange.order_id) + } +) diff --git a/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts b/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts index 45defbe778..583ac6b53d 100644 --- a/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/exchange-request-item-return.ts @@ -148,8 +148,6 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow( reference_id: orderReturn.id, details: { reference_id: item.id, - exchange_id: orderExchange.id, - return_id: orderReturn.id, quantity: item.quantity, metadata: item.metadata, }, diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index cab76dd0a7..1d85e1d748 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -1,3 +1,4 @@ +export * from "./add-line-items" export * from "./archive-orders" export * from "./begin-order-claim" export * from "./begin-order-exchange" @@ -18,6 +19,7 @@ export * from "./create-shipment" export * from "./decline-order-change" export * from "./delete-order-change" export * from "./delete-order-change-actions" +export * from "./exchange-add-new-item" export * from "./exchange-request-item-return" export * from "./get-order-detail" export * from "./get-orders-list" diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index 7ad5fb75da..3e57f59fba 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -50,7 +50,6 @@ import { CreateOrderDTO, CreateOrderExchangeDTO, CreateOrderLineItemDTO, - CreateOrderLineItemForOrderDTO, CreateOrderLineItemTaxLineDTO, CreateOrderReturnDTO, CreateOrderReturnReasonDTO, @@ -559,12 +558,8 @@ export interface IOrderModuleService extends IModuleService { sharedContext?: Context ): Promise - createLineItems( - data: CreateOrderLineItemForOrderDTO - ): Promise - createLineItems( - data: CreateOrderLineItemForOrderDTO[] - ): Promise + createLineItems(data: CreateOrderLineItemDTO): Promise + createLineItems(data: CreateOrderLineItemDTO[]): Promise createLineItems( orderId: string, items: CreateOrderLineItemDTO[], diff --git a/packages/core/types/src/workflow/order/add-new-item.ts b/packages/core/types/src/workflow/order/add-new-item.ts new file mode 100644 index 0000000000..6a95efaa31 --- /dev/null +++ b/packages/core/types/src/workflow/order/add-new-item.ts @@ -0,0 +1,34 @@ +import { BigNumberInput } from "../../totals" + +export interface OrderExchangeAddNewItemWorkflowInput { + exchange_id: string + items: { + variant_id: string + quantity: BigNumberInput + unit_price?: BigNumberInput + internal_note?: string + metadata?: Record | null + }[] +} + +export interface OrderClaimAddNewItemWorkflowInput { + claim_id: string + items: { + variant_id: string + quantity: BigNumberInput + unit_price?: BigNumberInput + internal_note?: string + metadata?: Record | null + }[] +} + +export interface OrderAddLineItemWorkflowInput { + order_id: string + items: { + variant_id: string + quantity: BigNumberInput + unit_price?: BigNumberInput + internal_note?: string + metadata?: Record | null + }[] +} diff --git a/packages/core/types/src/workflow/order/begin-claim-order.ts b/packages/core/types/src/workflow/order/begin-claim-order.ts index f5c0721488..c2b0c37326 100644 --- a/packages/core/types/src/workflow/order/begin-claim-order.ts +++ b/packages/core/types/src/workflow/order/begin-claim-order.ts @@ -1,6 +1,6 @@ import { OrderClaimType } from "../../order/mutations" -export interface beginOrderClaimWorkflowInput { +export interface BeginOrderClaimWorkflowInput { type: OrderClaimType order_id: string created_by?: string diff --git a/packages/core/types/src/workflow/order/begin-exchange-order.ts b/packages/core/types/src/workflow/order/begin-exchange-order.ts index aa03e074c6..aa3e121a18 100644 --- a/packages/core/types/src/workflow/order/begin-exchange-order.ts +++ b/packages/core/types/src/workflow/order/begin-exchange-order.ts @@ -1,4 +1,4 @@ -export interface beginOrderExchangeWorkflowInput { +export interface BeginOrderExchangeWorkflowInput { order_id: string created_by?: string internal_note?: string diff --git a/packages/core/types/src/workflow/order/begin-return-order.ts b/packages/core/types/src/workflow/order/begin-return-order.ts index 45b3aaecf9..ca90b0efb1 100644 --- a/packages/core/types/src/workflow/order/begin-return-order.ts +++ b/packages/core/types/src/workflow/order/begin-return-order.ts @@ -1,4 +1,4 @@ -export interface beginOrderReturnWorkflowInput { +export interface BeginOrderReturnWorkflowInput { order_id: string created_by?: string internal_note?: string diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts index d59380f4f4..b03d27c074 100644 --- a/packages/core/types/src/workflow/order/index.ts +++ b/packages/core/types/src/workflow/order/index.ts @@ -1,3 +1,4 @@ +export * from "./add-new-item" export * from "./begin-claim-order" export * from "./begin-exchange-order" export * from "./begin-return-order" diff --git a/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts b/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts index bb80c469ad..d62b722cc1 100644 --- a/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts +++ b/packages/modules/order/src/services/__tests__/util/actions/exchanges.ts @@ -171,7 +171,6 @@ describe("Order Exchange - Actions", function () { id: "item_555", unit_price: 50, quantity: 1, - detail: {}, actions: [ { action: "ITEM_ADD", @@ -194,7 +193,6 @@ describe("Order Exchange - Actions", function () { { id: "shipping_345", price: 5, - detail: {}, actions: [ { action: "SHIPPING_ADD", @@ -206,7 +204,6 @@ describe("Order Exchange - Actions", function () { { id: "return_shipping_345", price: 7.5, - detail: {}, actions: [ { action: "SHIPPING_ADD", diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 93dd17cf0a..0dcc4f560c 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -1969,18 +1969,57 @@ export default class OrderModuleService< } }) - const calculated = calculateOrderChange({ - order: order as any, - actions: orderChange.actions, - transactions: order.transactions ?? [], - options: { - addActionReferenceToObject: true, - }, - }) + const { itemsToUpsert, shippingMethodsToUpsert, calculatedOrders } = + applyChangesToOrder( + [order], + { [order.id]: orderChange.actions }, + { addActionReferenceToObject: true } + ) - createRawPropertiesFromBigNumber(calculated) + const calculated = calculatedOrders[order.id] + + const addedItems = {} + for (const item of calculated.order.items) { + const isExistingItem = item.id === item.detail?.item_id + + if (!isExistingItem) { + addedItems[item.id] = item + } + } + if (Object.keys(addedItems).length > 0) { + const addedItemDetails = await this.listLineItems( + { id: Object.keys(addedItems) }, + { + relations: ["adjustments", "tax_lines"], + }, + sharedContext + ) + + calculated.order.items.forEach((item, idx) => { + if (addedItems[item.id]) { + const lineItem = addedItemDetails.find((d) => d.id === item.id) as any + + const actions = item.actions + delete item.actions + + const newItem = itemsToUpsert.find((d) => d.item_id === item.id)! + calculated.order.items[idx] = { + ...lineItem, + actions, + quantity: newItem.quantity, + detail: { + ...newItem, + ...item, + }, + } + } + }) + } + + // TODO - add new shipping methods + + const calcOrder = calculated.order - const calcOrder = calculated.order as any decorateCartTotals(calcOrder as DecorateCartLikeInputDTO) calcOrder.summary = calculated.summary diff --git a/packages/modules/order/src/utils/actions/item-add.ts b/packages/modules/order/src/utils/actions/item-add.ts index 56788b5766..e0e3b6a5c3 100644 --- a/packages/modules/order/src/utils/actions/item-add.ts +++ b/packages/modules/order/src/utils/actions/item-add.ts @@ -30,9 +30,9 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, { existing = { id: action.details.reference_id!, order_id: currentOrder.id, - return_id: action.details.return_id, - claim_id: action.details.claim_id, - exchange_id: action.details.exchange_id, + return_id: action.return_id, + claim_id: action.claim_id, + exchange_id: action.exchange_id, unit_price: action.details.unit_price, quantity: action.details.quantity, diff --git a/packages/modules/order/src/utils/apply-order-changes.ts b/packages/modules/order/src/utils/apply-order-changes.ts index a5b1e9f792..f3f3dafe1d 100644 --- a/packages/modules/order/src/utils/apply-order-changes.ts +++ b/packages/modules/order/src/utils/apply-order-changes.ts @@ -12,22 +12,29 @@ export interface ApplyOrderChangeDTO extends OrderChangeActionDTO { export function applyChangesToOrder( orders: any[], - actionsMap: Record + actionsMap: Record, + options?: { + addActionReferenceToObject?: boolean + } ) { const itemsToUpsert: OrderItem[] = [] const shippingMethodsToUpsert: OrderShippingMethod[] = [] const summariesToUpsert: any[] = [] const orderToUpdate: any[] = [] + const calculatedOrders = {} for (const order of orders) { const calculated = calculateOrderChange({ order: order as any, actions: actionsMap[order.id], transactions: order.transactions ?? [], + options, }) createRawPropertiesFromBigNumber(calculated) + calculatedOrders[order.id] = calculated + const version = actionsMap[order.id][0].version ?? 1 for (const item of calculated.order.items) { @@ -41,11 +48,11 @@ export function applyChangesToOrder( order_id: order.id, version, quantity: orderItem.quantity, - fulfilled_quantity: orderItem.fulfilled_quantity, - shipped_quantity: orderItem.shipped_quantity, - return_requested_quantity: orderItem.return_requested_quantity, - return_received_quantity: orderItem.return_received_quantity, - return_dismissed_quantity: orderItem.return_dismissed_quantity, + fulfilled_quantity: orderItem.fulfilled_quantity ?? 0, + shipped_quantity: orderItem.shipped_quantity ?? 0, + return_requested_quantity: orderItem.return_requested_quantity ?? 0, + return_received_quantity: orderItem.return_received_quantity ?? 0, + return_dismissed_quantity: orderItem.return_dismissed_quantity ?? 0, written_off_quantity: orderItem.written_off_quantity, metadata: orderItem.metadata, } as OrderItem) @@ -90,5 +97,6 @@ export function applyChangesToOrder( shippingMethodsToUpsert, summariesToUpsert, orderToUpdate, + calculatedOrders, } } diff --git a/packages/modules/order/src/utils/set-action-reference.ts b/packages/modules/order/src/utils/set-action-reference.ts index be8c8cdeed..be68189aad 100644 --- a/packages/modules/order/src/utils/set-action-reference.ts +++ b/packages/modules/order/src/utils/set-action-reference.ts @@ -1,11 +1,4 @@ export function setActionReference(existing, action, options) { - existing.detail ??= {} - - existing.detail.order_id ??= action.order_id - existing.detail.return_id ??= action.return_id - existing.detail.claim_id ??= action.claim_id - existing.detail.exchange_id ??= action.exchange_id - if (options?.addActionReferenceToObject) { existing.actions ??= [] existing.actions.push(action) diff --git a/packages/modules/order/src/utils/transform-order.ts b/packages/modules/order/src/utils/transform-order.ts index 0ba0aac1cd..b37c90411d 100644 --- a/packages/modules/order/src/utils/transform-order.ts +++ b/packages/modules/order/src/utils/transform-order.ts @@ -33,6 +33,11 @@ export function formatOrder( } mainOrder.items = mainOrder.items?.map((orderItem) => { + const isFormatted = isDefined(orderItem.detail?.fulfilled_quantity) + if (isFormatted) { + return orderItem + } + const detail = { ...orderItem } delete detail.order delete detail.item @@ -62,6 +67,10 @@ export function formatOrder( if (order.shipping_methods) { order.shipping_methods = order.shipping_methods?.map((shippingMethod) => { + if (shippingMethod.detail) { + return shippingMethod + } + const sm = { ...shippingMethod.shipping_method } delete shippingMethod.shipping_method