From 1ff988c3f8b8cef45200374ae5f15d098f97f421 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Tue, 13 Aug 2024 06:47:01 -0300 Subject: [PATCH] feat(core-flows): order edit flows (#8555) What: - Order edit workflows Tests of the flows will come in a next PR together with http endpoints --- .../update-exchange-shipping-method.ts | 6 +- .../workflows/order-edit/begin-order-edit.ts | 53 ++++++ .../order-edit/cancel-begin-order-edit.ts | 81 +++++++++ .../order-edit/confirm-order-edit-request.ts | 166 ++++++++++++++++++ .../create-order-edit-shipping-method.ts | 148 ++++++++++++++++ .../order-edit/order-edit-add-new-item.ts | 100 +++++++++++ .../remove-order-edit-item-action.ts | 95 ++++++++++ .../remove-order-edit-shipping-method.ts | 100 +++++++++++ .../order-edit/update-order-edit-add-item.ts | 116 ++++++++++++ .../update-order-edit-shipping-method.ts | 115 ++++++++++++ packages/core/types/src/order/common.ts | 9 + packages/core/types/src/order/service.ts | 3 +- .../src/workflow/order/begin-order-edit.ts | 7 + .../core/types/src/workflow/order/index.ts | 1 + .../core/types/src/workflow/order/items.ts | 13 +- .../src/workflow/order/shipping-method.ts | 15 ++ 16 files changed, 1024 insertions(+), 4 deletions(-) create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/begin-order-edit.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/confirm-order-edit-request.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/order-edit-add-new-item.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-item-action.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-shipping-method.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-add-item.ts create mode 100644 packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-shipping-method.ts create mode 100644 packages/core/types/src/workflow/order/begin-order-edit.ts diff --git a/packages/core/core-flows/src/order/workflows/exchange/update-exchange-shipping-method.ts b/packages/core/core-flows/src/order/workflows/exchange/update-exchange-shipping-method.ts index 44a2379466..4b5345c396 100644 --- a/packages/core/core-flows/src/order/workflows/exchange/update-exchange-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/exchange/update-exchange-shipping-method.ts @@ -89,7 +89,11 @@ export const updateExchangeShippingMethodWorkflow = createWorkflow( list: false, }).config({ name: "order-change-query" }) - updateExchangeShippingMethodValidationStep({ orderExchange, orderChange, input }) + updateExchangeShippingMethodValidationStep({ + orderExchange, + orderChange, + input, + }) const updateData = transform( { orderChange, input }, diff --git a/packages/core/core-flows/src/order/workflows/order-edit/begin-order-edit.ts b/packages/core/core-flows/src/order/workflows/order-edit/begin-order-edit.ts new file mode 100644 index 0000000000..280481a682 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/begin-order-edit.ts @@ -0,0 +1,53 @@ +import { OrderChangeDTO, OrderDTO, OrderWorkflow } from "@medusajs/types" +import { + WorkflowData, + WorkflowResponse, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { createOrderChangeStep } from "../../steps/create-order-change" +import { throwIfOrderIsCancelled } from "../../utils/order-validation" + +/** + * This step validates that an order-edit can be requested for an order. + */ +export const beginOrderOrderEditValidationStep = createStep( + "begin-order-edit-validation", + async function ({ order }: { order: OrderDTO }) { + throwIfOrderIsCancelled({ order }) + } +) + +export const beginOrderEditOrderWorkflowId = "begin-order-edit-order" +/** + * This workflow requests an order order-edit. + */ +export const beginOrderEditOrderWorkflow = createWorkflow( + beginOrderEditOrderWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }) + + beginOrderOrderEditValidationStep({ order }) + + const orderChangeInput = transform({ input }, ({ input }) => { + return { + change_type: "edit" as const, + order_id: input.order_id, + created_by: input.created_by, + description: input.description, + internal_note: input.internal_note, + } + }) + return new WorkflowResponse(createOrderChangeStep(orderChangeInput)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts b/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts new file mode 100644 index 0000000000..d4be4a1377 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/cancel-begin-order-edit.ts @@ -0,0 +1,81 @@ +import { OrderChangeDTO, OrderDTO } from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + createStep, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { deleteOrderChangesStep, deleteOrderShippingMethods } from "../../steps" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +export type CancelBeginOrderEditWorkflowInput = { + order_id: string +} + +/** + * This step validates that a requested order edit can be canceled. + */ +export const cancelBeginOrderEditValidationStep = createStep( + "validate-cancel-begin-order-edit", + async function ({ + order, + orderChange, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const cancelBeginOrderEditWorkflowId = "cancel-begin-order-edit" +/** + * This workflow cancels a requested order edit. + */ +export const cancelBeginOrderEditWorkflow = createWorkflow( + cancelBeginOrderEditWorkflowId, + function (input: CancelBeginOrderEditWorkflowInput): WorkflowData { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "version", "canceled_at"], + variables: { id: input.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", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + cancelBeginOrderEditValidationStep({ order, orderChange }) + + const shippingToRemove = transform( + { orderChange, input }, + ({ orderChange, input }) => { + return (orderChange.actions ?? []) + .filter((a) => a.action === ChangeActionType.SHIPPING_ADD) + .map(({ id }) => id) + } + ) + + parallelize( + deleteOrderChangesStep({ ids: [orderChange.id] }), + deleteOrderShippingMethods({ ids: shippingToRemove }) + ) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/confirm-order-edit-request.ts b/packages/core/core-flows/src/order/workflows/order-edit/confirm-order-edit-request.ts new file mode 100644 index 0000000000..238b9a38f9 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/confirm-order-edit-request.ts @@ -0,0 +1,166 @@ +import { OrderChangeDTO, OrderDTO } from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowResponse, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { reserveInventoryStep } from "../../../definition/cart/steps/reserve-inventory" +import { prepareConfirmInventoryInput } from "../../../definition/cart/utils/prepare-confirm-inventory-input" +import { previewOrderChangeStep } from "../../steps" +import { confirmOrderChanges } from "../../steps/confirm-order-changes" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +export type ConfirmOrderEditRequestWorkflowInput = { + order_id: string +} + +/** + * This step validates that a requested order edit can be confirmed. + */ +export const confirmOrderEditRequestValidationStep = createStep( + "validate-confirm-order-edit-request", + async function ({ + order, + orderChange, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const confirmOrderEditRequestWorkflowId = "confirm-order-edit-request" +/** + * This workflow confirms an order edit request. + */ +export const confirmOrderEditRequestWorkflow = createWorkflow( + confirmOrderEditRequestWorkflowId, + function ( + input: ConfirmOrderEditRequestWorkflowInput + ): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: [ + "id", + "version", + "canceled_at", + "items.id", + "items.title", + "items.variant_title", + "items.variant_sku", + "items.variant_barcode", + "shipping_address.*", + ], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: [ + "id", + "actions.id", + "actions.order_id", + "actions.return_id", + "actions.action", + "actions.details", + "actions.reference", + "actions.reference_id", + "actions.internal_note", + ], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + confirmOrderEditRequestValidationStep({ + order, + orderChange, + }) + + const orderPreview = previewOrderChangeStep(order.id) + + confirmOrderChanges({ changes: [orderChange], orderId: order.id }) + + const orderItems = useRemoteQueryStep({ + entry_point: "order", + fields: [ + "id", + "version", + "canceled_at", + "sales_channel_id", + "items.quantity", + "items.raw_quantity", + "items.item.id", + "items.item.variant.manage_inventory", + "items.item.variant.allow_backorder", + "items.item.variant.inventory_items.inventory_item_id", + "items.item.variant.inventory_items.required_quantity", + "items.item.variant.inventory_items.inventory.location_levels.stock_locations.id", + "items.item.variant.inventory_items.inventory.location_levels.stock_locations.name", + "items.item.variant.inventory_items.inventory.location_levels.stock_locations.sales_channels.id", + "items.item.variant.inventory_items.inventory.location_levels.stock_locations.sales_channels.name", + ], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const { variants, items } = transform({ orderItems }, ({ orderItems }) => { + const allItems: any[] = [] + const allVariants: any[] = [] + orderItems.items.forEach((ordItem) => { + const itemAction = orderPreview.items?.find( + (item) => + item.id === ordItem.id && + item.actions?.find((a) => a.action === ChangeActionType.ITEM_ADD) + ) + + if (!itemAction) { + return + } + + const item = ordItem.item + allItems.push({ + id: item.id, + variant_id: item.variant_id, + quantity: itemAction.raw_quantity ?? itemAction.quantity, + }) + allVariants.push(item.variant) + }) + + return { + variants: allVariants, + items: allItems, + } + }) + + const formatedInventoryItems = transform( + { + input: { + sales_channel_id: (orderItems as any).order.sales_channel_id, + variants, + items, + }, + }, + prepareConfirmInventoryInput + ) + + reserveInventoryStep(formatedInventoryItems) + + return new WorkflowResponse(orderPreview) + } +) 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 new file mode 100644 index 0000000000..99f1f57d60 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/create-order-edit-shipping-method.ts @@ -0,0 +1,148 @@ +import { BigNumberInput, OrderChangeDTO, OrderDTO } from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowResponse, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { previewOrderChangeStep } from "../../steps" +import { createOrderChangeActionsStep } from "../../steps/create-order-change-actions" +import { createOrderShippingMethods } from "../../steps/create-order-shipping-methods" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +/** + * This step validates that a shipping method can be created for an order edit. + */ +export const createOrderEditShippingMethodValidationStep = createStep( + "validate-create-order-edit-shipping-method", + async function ({ + order, + orderChange, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const createOrderEditShippingMethodWorkflowId = + "create-order-edit-shipping-method" +/** + * This workflow creates a shipping method for an order edit. + */ +export const createOrderEditShippingMethodWorkflow = createWorkflow( + createOrderEditShippingMethodWorkflowId, + function (input: { + order_id: string + shipping_option_id: string + custom_price?: BigNumberInput + }): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "currency_code", "canceled_at"], + variables: { id: input.order_id }, + list: false, + throw_if_key_not_found: true, + }).config({ name: "order-query" }) + + const shippingOptions = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: [ + "id", + "name", + "calculated_price.calculated_amount", + "calculated_price.is_calculated_price_tax_inclusive", + ], + variables: { + id: input.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"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + const shippingMethodInput = transform( + { + shippingOptions, + customPrice: input.custom_price, + orderChange, + input, + }, + (data) => { + const option = data.shippingOptions[0] + const orderChange = data.orderChange + + return { + shipping_option_id: option.id, + amount: data.customPrice ?? option.calculated_price.calculated_amount, + is_tax_inclusive: + !!option.calculated_price.is_calculated_price_tax_inclusive, + data: option.data ?? {}, + name: option.name, + version: orderChange.version, + order_id: data.input.order_id, + } + } + ) + + const createdMethods = createOrderShippingMethods({ + shipping_methods: [shippingMethodInput], + }) + + const orderChangeActionInput = transform( + { + order, + shippingOptions, + createdMethods, + customPrice: input.custom_price, + orderChange, + input, + }, + ({ + shippingOptions, + order, + createdMethods, + customPrice, + orderChange, + input, + }) => { + const shippingOption = shippingOptions[0] + const createdMethod = createdMethods[0] + const methodPrice = + customPrice ?? shippingOption.calculated_price.calculated_amount + + return { + action: ChangeActionType.SHIPPING_ADD, + reference: "order_shipping_method", + order_change_id: orderChange.id, + reference_id: createdMethod.id, + amount: methodPrice, + order_id: order.id, + } + } + ) + + createOrderChangeActionsStep([orderChangeActionInput]) + + return new WorkflowResponse(previewOrderChangeStep(order.id)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/order-edit-add-new-item.ts b/packages/core/core-flows/src/order/workflows/order-edit/order-edit-add-new-item.ts new file mode 100644 index 0000000000..d100436002 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/order-edit-add-new-item.ts @@ -0,0 +1,100 @@ +import { OrderChangeDTO, OrderDTO, OrderWorkflow } from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + 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, +} from "../../utils/order-validation" +import { addOrderLineItemsWorkflow } from "../add-line-items" + +/** + * This step validates that new items can be added to an order edit. + */ +export const orderEditAddNewItemValidationStep = createStep( + "order-edit-add-new-item-validation", + async function ({ + order, + orderChange, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + } +) + +export const orderOrderEditAddNewItemWorkflowId = "order-edit-add-new-item" +/** + * This workflow adds new items to an order edit. + */ +export const orderOrderEditAddNewItemWorkflow = createWorkflow( + orderOrderEditAddNewItemWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "canceled_at", "items.*"], + variables: { id: input.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: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + orderEditAddNewItemValidationStep({ + order, + orderChange, + }) + + const lineItems = addOrderLineItemsWorkflow.runAsStep({ + input: { + order_id: order.id, + items: input.items, + }, + }) + + const orderChangeActionInput = transform( + { order, orderChange, items: input.items, lineItems }, + ({ order, orderChange, items, lineItems }) => { + return items.map((item, index) => ({ + order_change_id: orderChange.id, + order_id: order.id, + version: orderChange.version, + action: ChangeActionType.ITEM_ADD, + internal_note: item.internal_note, + details: { + reference_id: lineItems[index].id, + quantity: item.quantity, + unit_price: item.unit_price ?? lineItems[index].unit_price, + metadata: item.metadata, + }, + })) + } + ) + + createOrderChangeActionsStep(orderChangeActionInput) + + return new WorkflowResponse(previewOrderChangeStep(input.order_id)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-item-action.ts b/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-item-action.ts new file mode 100644 index 0000000000..beaa2113d0 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-item-action.ts @@ -0,0 +1,95 @@ +import { + OrderChangeActionDTO, + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createStep, + createWorkflow, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { + deleteOrderChangeActionsStep, + previewOrderChangeStep, +} from "../../steps" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +/** + * This step validates that a new item can be removed from an order edit. + */ +export const removeOrderEditItemActionValidationStep = createStep( + "remove-item-order-edit-action-validation", + async function ({ + order, + orderChange, + input, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + input: OrderWorkflow.DeleteOrderEditItemActionWorkflowInput + }) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + + const associatedAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + if (!associatedAction) { + throw new Error( + `No item found for order ${input.order_id} in order change ${orderChange.id}` + ) + } else if (associatedAction.action !== ChangeActionType.ITEM_ADD) { + throw new Error(`Action ${associatedAction.id} is not adding an item`) + } + } +) + +export const removeItemOrderEditActionWorkflowId = + "remove-item-order edit-action" +/** + * This workflow removes a new item in an order edit. + */ +export const removeItemOrderEditActionWorkflow = createWorkflow( + removeItemOrderEditActionWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "canceled_at", "items.*"], + variables: { id: input.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", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + removeOrderEditItemActionValidationStep({ + order, + input, + orderChange, + }) + + deleteOrderChangeActionsStep({ ids: [input.action_id] }) + + return new WorkflowResponse(previewOrderChangeStep(order.id)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-shipping-method.ts b/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-shipping-method.ts new file mode 100644 index 0000000000..e303626589 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/remove-order-edit-shipping-method.ts @@ -0,0 +1,100 @@ +import { + OrderChangeActionDTO, + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createStep, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { deleteOrderShippingMethods } from "../../steps" +import { deleteOrderChangeActionsStep } from "../../steps/delete-order-change-actions" +import { previewOrderChangeStep } from "../../steps/preview-order-change" +import { throwIfOrderChangeIsNotActive } from "../../utils/order-validation" + +/** + * This step validates that a shipping method can be removed from an order edit. + */ +export const removeOrderEditShippingMethodValidationStep = createStep( + "validate-remove-order-edit-shipping-method", + async function ({ + orderChange, + input, + }: { + input: { order_id: string; action_id: string } + orderChange: OrderChangeDTO + }) { + throwIfOrderChangeIsNotActive({ orderChange }) + + const associatedAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + if (!associatedAction) { + throw new Error( + `No shipping method found for order ${input.order_id} in order change ${orderChange.id}` + ) + } else if (associatedAction.action !== ChangeActionType.SHIPPING_ADD) { + throw new Error( + `Action ${associatedAction.id} is not adding a shipping method` + ) + } + } +) + +export const removeOrderEditShippingMethodWorkflowId = + "remove-order-edit-shipping-method" +/** + * This workflow removes a shipping method of an order edit. + */ +export const removeOrderEditShippingMethodWorkflow = createWorkflow( + removeOrderEditShippingMethodWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + removeOrderEditShippingMethodValidationStep({ + orderChange, + input, + }) + + const dataToRemove = transform( + { orderChange, input }, + ({ orderChange, input }) => { + const associatedAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + return { + actionId: associatedAction.id, + shippingMethodId: associatedAction.reference_id, + } + } + ) + + parallelize( + deleteOrderChangeActionsStep({ ids: [dataToRemove.actionId] }), + deleteOrderShippingMethods({ ids: [dataToRemove.shippingMethodId] }) + ) + + return new WorkflowResponse(previewOrderChangeStep(input.order_id)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-add-item.ts b/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-add-item.ts new file mode 100644 index 0000000000..6cb81c839d --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-add-item.ts @@ -0,0 +1,116 @@ +import { + OrderChangeActionDTO, + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createStep, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { + previewOrderChangeStep, + updateOrderChangeActionsStep, +} from "../../steps" +import { + throwIfIsCancelled, + throwIfOrderChangeIsNotActive, +} from "../../utils/order-validation" + +/** + * This step validates that a new item can be removed from an order edit. + */ +export const updateOrderEditAddItemValidationStep = createStep( + "update-order-edit-add-item-validation", + async function ( + { + order, + orderChange, + input, + }: { + order: OrderDTO + orderChange: OrderChangeDTO + input: OrderWorkflow.UpdateOrderEditAddNewItemWorkflowInput + }, + context + ) { + throwIfIsCancelled(order, "Order") + throwIfOrderChangeIsNotActive({ orderChange }) + + const associatedAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + if (!associatedAction) { + throw new Error( + `No request to add item for order ${input.order_id} in order change ${orderChange.id}` + ) + } else if (associatedAction.action !== ChangeActionType.ITEM_ADD) { + throw new Error(`Action ${associatedAction.id} is not adding an item`) + } + } +) + +export const updateOrderEditAddItemWorkflowId = "update-order-edit-add-item" +/** + * This workflow updates a new item in the order edit. + */ +export const updateOrderEditAddItemWorkflow = createWorkflow( + updateOrderEditAddItemWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const order: OrderDTO = useRemoteQueryStep({ + entry_point: "orders", + fields: ["id", "status", "canceled_at", "items.*"], + variables: { id: input.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", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + updateOrderEditAddItemValidationStep({ + order, + input, + orderChange, + }) + + const updateData = transform( + { orderChange, input }, + ({ input, orderChange }) => { + const originalAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + const data = input.data + return { + id: input.action_id, + details: { + quantity: data.quantity ?? originalAction.details?.quantity, + }, + internal_note: data.internal_note, + } + } + ) + + updateOrderChangeActionsStep([updateData]) + + return new WorkflowResponse(previewOrderChangeStep(order.id)) + } +) diff --git a/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-shipping-method.ts b/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-shipping-method.ts new file mode 100644 index 0000000000..2c0eff92fc --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/order-edit/update-order-edit-shipping-method.ts @@ -0,0 +1,115 @@ +import { + OrderChangeActionDTO, + OrderChangeDTO, + OrderDTO, + OrderWorkflow, +} from "@medusajs/types" +import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createStep, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common" +import { + updateOrderChangeActionsStep, + updateOrderShippingMethodsStep, +} from "../../steps" +import { previewOrderChangeStep } from "../../steps/preview-order-change" +import { throwIfOrderChangeIsNotActive } from "../../utils/order-validation" + +/** + * This step validates that an order edit's shipping method can be updated. + */ +export const updateOrderEditShippingMethodValidationStep = createStep( + "validate-update-order-edit-shipping-method", + async function ({ + orderChange, + input, + }: { + input: { order_id: string; action_id: string } + orderChange: OrderChangeDTO + }) { + throwIfOrderChangeIsNotActive({ orderChange }) + + const associatedAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + if (!associatedAction) { + throw new Error( + `No shipping method found for order ${input.order_id} in order change ${orderChange.id}` + ) + } else if (associatedAction.action !== ChangeActionType.SHIPPING_ADD) { + throw new Error( + `Action ${associatedAction.id} is not adding a shipping method` + ) + } + } +) + +export const updateOrderEditShippingMethodWorkflowId = + "update-order-edit-shipping-method" +/** + * This workflow updates an order edit's shipping method. + */ +export const updateOrderEditShippingMethodWorkflow = createWorkflow( + updateOrderEditShippingMethodWorkflowId, + function ( + input: WorkflowData + ): WorkflowResponse { + const orderChange: OrderChangeDTO = useRemoteQueryStep({ + entry_point: "order_change", + fields: ["id", "status", "version", "actions.*"], + variables: { + filters: { + order_id: input.order_id, + status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], + }, + }, + list: false, + }).config({ name: "order-change-query" }) + + updateOrderEditShippingMethodValidationStep({ + orderChange, + input, + }) + + const updateData = transform( + { orderChange, input }, + ({ input, orderChange }) => { + const originalAction = (orderChange.actions ?? []).find( + (a) => a.id === input.action_id + ) as OrderChangeActionDTO + + const data = input.data + + const action = { + id: originalAction.id, + internal_note: data.internal_note, + } + + const shippingMethod = { + id: originalAction.reference_id, + amount: data.custom_price, + metadata: data.metadata, + } + + return { + action, + shippingMethod, + } + } + ) + + parallelize( + updateOrderChangeActionsStep([updateData.action]), + updateOrderShippingMethodsStep([updateData.shippingMethod!]) + ) + + return new WorkflowResponse(previewOrderChangeStep(input.order_id)) + } +) diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index 8094cb3010..a32b130da7 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -2656,3 +2656,12 @@ export interface OrderChangeReturn { */ shippingMethods: any[] } + +export interface OrderPreviewDTO + extends Omit { + order_change: OrderChangeDTO + items: (OrderLineItemDTO & { actions?: OrderChangeActionDTO[] })[] + shipping_methods: (OrderShippingMethodDTO & { + actions?: OrderChangeActionDTO[] + })[] +} diff --git a/packages/core/types/src/order/service.ts b/packages/core/types/src/order/service.ts index b125e3e1bb..ec7c6cbc90 100644 --- a/packages/core/types/src/order/service.ts +++ b/packages/core/types/src/order/service.ts @@ -31,6 +31,7 @@ import { OrderLineItemAdjustmentDTO, OrderLineItemDTO, OrderLineItemTaxLineDTO, + OrderPreviewDTO, OrderReturnItemDTO, OrderReturnReasonDTO, OrderShippingMethodAdjustmentDTO, @@ -2520,7 +2521,7 @@ export interface IOrderModuleService extends IModuleService { previewOrderChange( orderId: string, sharedContext?: Context - ): Promise + ): Promise /** * This method cancels an order's change, providing cancelation details. diff --git a/packages/core/types/src/workflow/order/begin-order-edit.ts b/packages/core/types/src/workflow/order/begin-order-edit.ts new file mode 100644 index 0000000000..4013db766b --- /dev/null +++ b/packages/core/types/src/workflow/order/begin-order-edit.ts @@ -0,0 +1,7 @@ +export interface BeginOrderOrderEditWorkflowInput { + order_id: string + created_by?: string + internal_note?: string + description?: string + metadata?: Record | null +} diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts index 91adb48a81..bc63385aab 100644 --- a/packages/core/types/src/workflow/order/index.ts +++ b/packages/core/types/src/workflow/order/index.ts @@ -1,5 +1,6 @@ export * from "./begin-claim-order" export * from "./begin-exchange-order" +export * from "./begin-order-edit" export * from "./begin-return-order" export * from "./cancel-claim" export * from "./cancel-exchange" diff --git a/packages/core/types/src/workflow/order/items.ts b/packages/core/types/src/workflow/order/items.ts index 21a6e45bfb..a691a5ff35 100644 --- a/packages/core/types/src/workflow/order/items.ts +++ b/packages/core/types/src/workflow/order/items.ts @@ -49,6 +49,15 @@ export interface UpdateExchangeAddNewItemWorkflowInput { } } +export interface UpdateOrderEditAddNewItemWorkflowInput { + order_id: string + action_id: string + data: { + quantity?: BigNumberInput + internal_note?: string | null + } +} + export interface UpdateClaimAddNewItemWorkflowInput { claim_id: string action_id: string @@ -106,7 +115,7 @@ export interface DeleteOrderClaimItemActionWorkflowInput { action_id: string } -export interface DeleteOrderExchangeItemActionWorkflowInput { - exchange_id: string +export interface DeleteOrderEditItemActionWorkflowInput { + order_id: string action_id: string } diff --git a/packages/core/types/src/workflow/order/shipping-method.ts b/packages/core/types/src/workflow/order/shipping-method.ts index 7d74ee9139..bfef3b58ea 100644 --- a/packages/core/types/src/workflow/order/shipping-method.ts +++ b/packages/core/types/src/workflow/order/shipping-method.ts @@ -40,7 +40,22 @@ export interface UpdateExchangeShippingMethodWorkflowInput { } } +export interface UpdateOrderEditShippingMethodWorkflowInput { + order_id: string + action_id: string + data: { + custom_price?: BigNumberInput + internal_note?: string | null + metadata?: Record | null + } +} + export interface DeleteExchangeShippingMethodWorkflowInput { exchange_id: string action_id: string } + +export interface DeleteOrderEditShippingMethodWorkflowInput { + order_id: string + action_id: string +}