feat: carry over promotions toggle on exchanges (#14128)
* feat: carry over promotions toggle on exchanges * fix: inital flag value, return the flag on preview * fix: validation of allocation type * fix: revert client changes * fix: invert condition * feat: recompute adjustments when outbound item is updated * fix: condition again * fix: display more accurate inbound/outbound totals for exchanges * fix: make exchanges specs green * feat: more testing cases * wip: pr feedback * fix: use plural for the flag on Admin * fix: schema test, route refactor * feat: tooltip * feat: refactor to use update workflow * feat: display applied promotion per item on order details, show copy sku on hover * feat: refactor edits and exchanges to have common flag toggle flow * fix: delete empty file * fix: exchange_id param query
This commit is contained in:
@@ -17,6 +17,7 @@ export * from "./delete-order-change-actions"
|
||||
export * from "./delete-order-changes"
|
||||
export * from "./delete-order-shipping-methods"
|
||||
export * from "./exchange/cancel-exchange"
|
||||
export * from "./list-order-change-actions-by-type"
|
||||
export * from "./exchange/create-exchange"
|
||||
export * from "./exchange/create-exchange-items-from-actions"
|
||||
export * from "./exchange/delete-exchanges"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { IOrderModuleService } from "@medusajs/framework/types"
|
||||
import { ChangeActionType, Modules } from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
/**
|
||||
* This step lists order change actions filtered by action type.
|
||||
*/
|
||||
export const listOrderChangeActionsByTypeStep = createStep(
|
||||
"list-order-change-actions-by-type",
|
||||
async function (
|
||||
{
|
||||
order_change_id,
|
||||
action_type,
|
||||
}: {
|
||||
order_change_id: string
|
||||
action_type: ChangeActionType
|
||||
},
|
||||
{ container }
|
||||
) {
|
||||
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
|
||||
|
||||
const actions = await service.listOrderChangeActions(
|
||||
{
|
||||
order_change_id,
|
||||
},
|
||||
{
|
||||
select: ["id", "action"],
|
||||
}
|
||||
)
|
||||
|
||||
const filteredActions = actions.filter(
|
||||
(action) => action.action === action_type
|
||||
)
|
||||
|
||||
return new StepResponse(filteredActions.map((action) => action.id))
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,166 @@
|
||||
import {
|
||||
ComputeActionContext,
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import { ChangeActionType } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
when,
|
||||
WorkflowData,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
getActionsToComputeFromPromotionsStep,
|
||||
prepareAdjustmentsFromPromotionActionsStep,
|
||||
} from "../../cart"
|
||||
import { previewOrderChangeStep } from "../steps/preview-order-change"
|
||||
import { createOrderChangeActionsWorkflow } from "./create-order-change-actions"
|
||||
import {
|
||||
deleteOrderChangeActionsStep,
|
||||
listOrderChangeActionsByTypeStep,
|
||||
} from "../steps"
|
||||
|
||||
/**
|
||||
* The data to compute adjustments for an order edit, exchange, claim, or return.
|
||||
*/
|
||||
export type ComputeAdjustmentsForPreviewWorkflowInput = {
|
||||
/**
|
||||
* The order's details.
|
||||
*/
|
||||
order: OrderDTO & { promotions: PromotionDTO[] }
|
||||
/**
|
||||
* The order change's details.
|
||||
*/
|
||||
orderChange: OrderChangeDTO
|
||||
}
|
||||
|
||||
export const computeAdjustmentsForPreviewWorkflowId =
|
||||
"compute-adjustments-for-preview"
|
||||
/**
|
||||
* This workflow computes adjustments for an order change if the carry over promotions flag is true on the order change.
|
||||
* If the flag is false, it deletes the existing adjustments replacement actions.
|
||||
*
|
||||
* It is currently used as a part of the order edit and exchange flows.
|
||||
* It's used by the [Add Items to Order Edit Admin API Route](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditems),
|
||||
* [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems),
|
||||
* and [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 compute adjustments
|
||||
* in your custom flows.
|
||||
*
|
||||
* @example
|
||||
* const { result } = await computeAdjustmentsForPreviewWorkflow(container)
|
||||
* .run({
|
||||
* input: {
|
||||
* order: {
|
||||
* id: "order_123",
|
||||
* // other order details...
|
||||
* },
|
||||
* orderChange: {
|
||||
* id: "orch_123",
|
||||
* // other order change details...
|
||||
* },
|
||||
* exchange_id: "exchange_123", // optional, for exchanges
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
* Compute adjustments for an order edit, exchange, claim, or return.
|
||||
*/
|
||||
export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
|
||||
computeAdjustmentsForPreviewWorkflowId,
|
||||
function (input: WorkflowData<ComputeAdjustmentsForPreviewWorkflowInput>) {
|
||||
const previewedOrder = previewOrderChangeStep(input.order.id)
|
||||
|
||||
when(
|
||||
{ order: input.order },
|
||||
({ order }) =>
|
||||
/**
|
||||
* Compute adjustments only if the flag on the order change is true
|
||||
*/
|
||||
!!order.promotions.length && !!input.orderChange.carry_over_promotions
|
||||
).then(() => {
|
||||
const actionsToComputeItemsInput = transform(
|
||||
{ previewedOrder, order: input.order },
|
||||
({ previewedOrder, order }) => {
|
||||
return {
|
||||
currency_code: order.currency_code,
|
||||
items: previewedOrder.items.map((item) => ({
|
||||
...item,
|
||||
// Buy-Get promotions rely on the product ID, so we need to manually set it before refreshing adjustments
|
||||
product: { id: item.product_id },
|
||||
})),
|
||||
} as ComputeActionContext
|
||||
}
|
||||
)
|
||||
|
||||
const orderPromotions = transform({ order: input.order }, ({ order }) => {
|
||||
return order.promotions
|
||||
.map((p) => p.code)
|
||||
.filter((p) => p !== undefined)
|
||||
})
|
||||
|
||||
const actions = getActionsToComputeFromPromotionsStep({
|
||||
computeActionContext: actionsToComputeItemsInput,
|
||||
promotionCodesToApply: orderPromotions,
|
||||
})
|
||||
|
||||
const { lineItemAdjustmentsToCreate } =
|
||||
prepareAdjustmentsFromPromotionActionsStep({ actions })
|
||||
|
||||
const orderChangeActionAdjustmentsInput = transform(
|
||||
{
|
||||
order: input.order,
|
||||
previewedOrder,
|
||||
orderChange: input.orderChange,
|
||||
lineItemAdjustmentsToCreate,
|
||||
},
|
||||
({
|
||||
order,
|
||||
previewedOrder,
|
||||
orderChange,
|
||||
lineItemAdjustmentsToCreate,
|
||||
}) => {
|
||||
return previewedOrder.items.map((item) => {
|
||||
const itemAdjustments = lineItemAdjustmentsToCreate.filter(
|
||||
(adjustment) => adjustment.item_id === item.id
|
||||
)
|
||||
|
||||
return {
|
||||
order_change_id: orderChange.id,
|
||||
order_id: order.id,
|
||||
exchange_id: orderChange.exchange_id ?? undefined,
|
||||
claim_id: orderChange.claim_id ?? undefined,
|
||||
return_id: orderChange.return_id ?? undefined,
|
||||
version: orderChange.version,
|
||||
action: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
||||
details: {
|
||||
reference_id: item.id,
|
||||
adjustments: itemAdjustments,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
createOrderChangeActionsWorkflow
|
||||
.runAsStep({ input: orderChangeActionAdjustmentsInput })
|
||||
.config({ name: "order-change-action-adjustments-input" })
|
||||
})
|
||||
|
||||
when(
|
||||
{ order: previewedOrder },
|
||||
({ order }) => !order.order_change.carry_over_promotions
|
||||
).then(() => {
|
||||
const actionIds = listOrderChangeActionsByTypeStep({
|
||||
order_change_id: input.orderChange.id,
|
||||
action_type: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
||||
})
|
||||
|
||||
deleteOrderChangeActionsStep({ ids: actionIds })
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "../../utils/order-validation"
|
||||
import { addOrderLineItemsWorkflow } from "../add-line-items"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../order-edit/compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||
|
||||
@@ -140,7 +140,13 @@ export const orderExchangeAddNewItemWorkflow = createWorkflow(
|
||||
|
||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||
entry_point: "order_change",
|
||||
fields: ["id", "status", "version"],
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"version",
|
||||
"exchange_id",
|
||||
"carry_over_promotions",
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
order_id: orderExchange.order_id,
|
||||
@@ -212,7 +218,6 @@ export const orderExchangeAddNewItemWorkflow = createWorkflow(
|
||||
input: {
|
||||
order: orderWithPromotions,
|
||||
orderChange,
|
||||
exchange_id: orderExchange.id,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../order-edit/compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||
|
||||
/**
|
||||
@@ -194,7 +194,13 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
|
||||
|
||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||
entry_point: "order_change",
|
||||
fields: ["id", "status", "version"],
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"version",
|
||||
"exchange_id",
|
||||
"carry_over_promotions",
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
order_id: orderExchange.order_id,
|
||||
@@ -321,7 +327,6 @@ export const orderExchangeRequestItemReturnWorkflow = createWorkflow(
|
||||
input: {
|
||||
order: orderWithPromotions,
|
||||
orderChange,
|
||||
exchange_id: orderExchange.id,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
OrderExchangeDTO,
|
||||
PromotionDTO,
|
||||
OrderPreviewDTO,
|
||||
OrderWorkflow,
|
||||
} from "@medusajs/framework/types"
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { refreshExchangeShippingWorkflow } from "./refresh-shipping"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
|
||||
/**
|
||||
* The data to validate that an outbound or new item in an exchange can be updated.
|
||||
@@ -150,7 +152,7 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
||||
|
||||
const order: OrderDTO = useRemoteQueryStep({
|
||||
entry_point: "orders",
|
||||
fields: ["id", "status", "canceled_at", "items.*"],
|
||||
fields: ["id", "status", "canceled_at", "items.*", "promotions.*"],
|
||||
variables: { id: orderExchange.order_id },
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
@@ -158,7 +160,14 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
||||
|
||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||
entry_point: "order_change",
|
||||
fields: ["id", "status", "version", "actions.*"],
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"version",
|
||||
"exchange_id",
|
||||
"actions.*",
|
||||
"carry_over_promotions",
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
order_id: orderExchange.order_id,
|
||||
@@ -207,6 +216,20 @@ export const updateExchangeAddItemWorkflow = createWorkflow(
|
||||
}
|
||||
)
|
||||
|
||||
const orderWithPromotions = transform({ order }, ({ order }) => {
|
||||
return {
|
||||
...order,
|
||||
promotions: (order as any).promotions ?? [],
|
||||
} as OrderDTO & { promotions: PromotionDTO[] }
|
||||
})
|
||||
|
||||
computeAdjustmentsForPreviewWorkflow.runAsStep({
|
||||
input: {
|
||||
order: orderWithPromotions,
|
||||
orderChange,
|
||||
},
|
||||
})
|
||||
|
||||
refreshExchangeShippingWorkflow.runAsStep({
|
||||
input: refreshArgs,
|
||||
})
|
||||
|
||||
@@ -50,7 +50,7 @@ export * from "./mark-payment-collection-as-paid"
|
||||
export * from "./maybe-refresh-shipping-methods"
|
||||
export * from "./order-edit/begin-order-edit"
|
||||
export * from "./order-edit/cancel-begin-order-edit"
|
||||
export * from "./order-edit/compute-adjustments-for-preview"
|
||||
export * from "./compute-adjustments-for-preview"
|
||||
export * from "./order-edit/confirm-order-edit-request"
|
||||
export * from "./order-edit/create-order-edit-shipping-method"
|
||||
export * from "./order-edit/order-edit-add-new-item"
|
||||
@@ -81,11 +81,13 @@ export * from "./return/update-receive-item-return-request"
|
||||
export * from "./return/update-request-item-return"
|
||||
export * from "./return/update-return"
|
||||
export * from "./return/update-return-shipping-method"
|
||||
export * from "./on-carry-promotions-flag-set"
|
||||
export * from "./transfer/accept-order-transfer"
|
||||
export * from "./transfer/cancel-order-transfer"
|
||||
export * from "./transfer/decline-order-transfer"
|
||||
export * from "./transfer/request-order-transfer"
|
||||
export * from "./update-order"
|
||||
export * from "./update-order-change"
|
||||
export * from "./update-order-change-actions"
|
||||
export * from "./update-order-changes"
|
||||
export * from "./update-tax-lines"
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
import {
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import {
|
||||
ApplicationMethodAllocation,
|
||||
MedusaError,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../common"
|
||||
import { throwIfOrderChangeIsNotActive } from "../utils/order-validation"
|
||||
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
|
||||
/**
|
||||
* The data to set the carry over promotions flag for an order change.
|
||||
*/
|
||||
export type OnCarryPromotionsFlagSetWorkflowInput = {
|
||||
/**
|
||||
* The order change's ID.
|
||||
*/
|
||||
order_change_id: string
|
||||
/**
|
||||
* Whether to carry over promotions to outbound exchange items.
|
||||
*/
|
||||
carry_over_promotions: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* This step validates that the order change is an exchange and validates promotion allocation.
|
||||
*/
|
||||
export const validateCarryPromotionsFlagStep = createStep(
|
||||
"validate-carry-promotions-flag",
|
||||
async function ({
|
||||
orderChange,
|
||||
order,
|
||||
input,
|
||||
}: {
|
||||
orderChange: OrderChangeDTO
|
||||
order: OrderDTO & { promotions?: PromotionDTO[] }
|
||||
input: OnCarryPromotionsFlagSetWorkflowInput
|
||||
}) {
|
||||
// Validate order change is active
|
||||
throwIfOrderChangeIsNotActive({ orderChange })
|
||||
|
||||
// we don't need to validate promotion since we will be resetting the adjustments
|
||||
if (!input.carry_over_promotions) {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate promotion allocation if promotions exist
|
||||
if (order.promotions && order.promotions.length > 0) {
|
||||
const invalidPromotions: string[] = []
|
||||
|
||||
for (const promotion of order.promotions) {
|
||||
const applicationMethod = (promotion as any).application_method
|
||||
|
||||
if (!applicationMethod) {
|
||||
continue
|
||||
}
|
||||
|
||||
const allocation = applicationMethod.allocation
|
||||
const type = applicationMethod.type
|
||||
|
||||
if (
|
||||
allocation !== ApplicationMethodAllocation.ACROSS &&
|
||||
allocation !== ApplicationMethodAllocation.EACH
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Promotion ${
|
||||
promotion.code || promotion.id
|
||||
} has invalid allocation. Only promotions with EACH or ACROSS allocation can be carried over to outbound exchange items.`
|
||||
)
|
||||
}
|
||||
|
||||
// For fixed promotions, allocation must be EACH
|
||||
if (
|
||||
type === "fixed" &&
|
||||
allocation !== ApplicationMethodAllocation.EACH
|
||||
) {
|
||||
invalidPromotions.push(promotion.code || promotion.id)
|
||||
}
|
||||
|
||||
// For percentage promotions, allocation must be EACH or ACROSS
|
||||
if (
|
||||
type === "percentage" &&
|
||||
allocation !== ApplicationMethodAllocation.EACH &&
|
||||
allocation !== ApplicationMethodAllocation.ACROSS
|
||||
) {
|
||||
invalidPromotions.push(promotion.code || promotion.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidPromotions.length > 0) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Promotions with codes ${invalidPromotions.join(
|
||||
", "
|
||||
)} have invalid allocation. Fixed promotions must have EACH allocation, and percentage promotions must have EACH or ACROSS allocation.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const onCarryPromotionsFlagSetId = "on-carry-promotions-flag-set"
|
||||
|
||||
/**
|
||||
* This workflow sets the carry over promotions flag for an order change.
|
||||
* It validates that the order change is active and is an exchange, validates promotion allocation,
|
||||
* and either applies or removes promotion adjustments based on the flag value.
|
||||
*
|
||||
* @example
|
||||
* const { result } = await onCarryPromotionsFlagSet(container)
|
||||
* .run({
|
||||
* input: {
|
||||
* order_change_id: "orch_123",
|
||||
* carry_over_promotions: true,
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
* Set the carry over promotions flag for an order change.
|
||||
*/
|
||||
export const onCarryPromotionsFlagSet = createWorkflow(
|
||||
onCarryPromotionsFlagSetId,
|
||||
function (
|
||||
input: WorkflowData<OnCarryPromotionsFlagSetWorkflowInput>
|
||||
): WorkflowResponse<void> {
|
||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||
entry_point: "order_change",
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"version",
|
||||
"exchange_id",
|
||||
"claim_id",
|
||||
"return_id",
|
||||
"order_id",
|
||||
"canceled_at",
|
||||
"confirmed_at",
|
||||
"declined_at",
|
||||
"carry_over_promotions",
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
id: input.order_change_id,
|
||||
},
|
||||
},
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
}).config({ name: "order-change-query" })
|
||||
|
||||
const order: OrderDTO & { promotions?: PromotionDTO[] } =
|
||||
useRemoteQueryStep({
|
||||
entry_point: "orders",
|
||||
fields: [
|
||||
"id",
|
||||
"currency_code",
|
||||
"promotions.*",
|
||||
"promotions.application_method.*",
|
||||
],
|
||||
variables: {
|
||||
id: orderChange.order_id,
|
||||
},
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
}).config({ name: "order-query" })
|
||||
|
||||
validateCarryPromotionsFlagStep({
|
||||
orderChange,
|
||||
order,
|
||||
input,
|
||||
})
|
||||
|
||||
const orderWithPromotions = transform({ order }, ({ order }) => {
|
||||
return {
|
||||
...order,
|
||||
promotions: (order as any).promotions ?? [],
|
||||
} as OrderDTO & { promotions: PromotionDTO[] }
|
||||
})
|
||||
|
||||
computeAdjustmentsForPreviewWorkflow.runAsStep({
|
||||
input: {
|
||||
orderChange,
|
||||
order: orderWithPromotions,
|
||||
},
|
||||
})
|
||||
|
||||
return new WorkflowResponse(void 0)
|
||||
}
|
||||
)
|
||||
@@ -1,163 +0,0 @@
|
||||
import {
|
||||
ComputeActionContext,
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
PromotionDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import { ChangeActionType } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
when,
|
||||
WorkflowData,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
getActionsToComputeFromPromotionsStep,
|
||||
prepareAdjustmentsFromPromotionActionsStep,
|
||||
} from "../../../cart"
|
||||
import { previewOrderChangeStep } from "../../steps/preview-order-change"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
|
||||
/**
|
||||
* The data to compute adjustments for an order edit, exchange, claim, or return.
|
||||
*/
|
||||
export type ComputeAdjustmentsForPreviewWorkflowInput = {
|
||||
/**
|
||||
* The order's details.
|
||||
*/
|
||||
order: OrderDTO & { promotions: PromotionDTO[] }
|
||||
/**
|
||||
* The order change's details.
|
||||
*/
|
||||
orderChange: OrderChangeDTO
|
||||
/**
|
||||
* Optional exchange ID to include in the order change action.
|
||||
*/
|
||||
exchange_id?: string
|
||||
/**
|
||||
* Optional claim ID to include in the order change action.
|
||||
*/
|
||||
claim_id?: string
|
||||
/**
|
||||
* Optional return ID to include in the order change action.
|
||||
*/
|
||||
return_id?: string
|
||||
}
|
||||
|
||||
export const computeAdjustmentsForPreviewWorkflowId =
|
||||
"compute-adjustments-for-preview"
|
||||
/**
|
||||
* This workflow computes adjustments for an order edit, exchange, claim, or return.
|
||||
* It's used by the [Add Items to Order Edit Admin API Route](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditems),
|
||||
* [Add Outbound Items Admin API Route](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems),
|
||||
* and [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 compute adjustments
|
||||
* in your custom flows.
|
||||
*
|
||||
* @example
|
||||
* const { result } = await computeAdjustmentsForPreviewWorkflow(container)
|
||||
* .run({
|
||||
* input: {
|
||||
* order: {
|
||||
* id: "order_123",
|
||||
* // other order details...
|
||||
* },
|
||||
* orderChange: {
|
||||
* id: "orch_123",
|
||||
* // other order change details...
|
||||
* },
|
||||
* exchange_id: "exchange_123", // optional, for exchanges
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
* Compute adjustments for an order edit, exchange, claim, or return.
|
||||
*/
|
||||
export const computeAdjustmentsForPreviewWorkflow = createWorkflow(
|
||||
computeAdjustmentsForPreviewWorkflowId,
|
||||
function (input: WorkflowData<ComputeAdjustmentsForPreviewWorkflowInput>) {
|
||||
const previewedOrder = previewOrderChangeStep(input.order.id)
|
||||
|
||||
when({ order: input.order }, ({ order }) => !!order.promotions.length).then(
|
||||
() => {
|
||||
const actionsToComputeItemsInput = transform(
|
||||
{ previewedOrder, order: input.order },
|
||||
({ previewedOrder, order }) => {
|
||||
return {
|
||||
currency_code: order.currency_code,
|
||||
items: previewedOrder.items.map((item) => ({
|
||||
...item,
|
||||
// Buy-Get promotions rely on the product ID, so we need to manually set it before refreshing adjustments
|
||||
product: { id: item.product_id },
|
||||
})),
|
||||
} as ComputeActionContext
|
||||
}
|
||||
)
|
||||
|
||||
const orderPromotions = transform(
|
||||
{ order: input.order },
|
||||
({ order }) => {
|
||||
return order.promotions
|
||||
.map((p) => p.code)
|
||||
.filter((p) => p !== undefined)
|
||||
}
|
||||
)
|
||||
|
||||
const actions = getActionsToComputeFromPromotionsStep({
|
||||
computeActionContext: actionsToComputeItemsInput,
|
||||
promotionCodesToApply: orderPromotions,
|
||||
})
|
||||
|
||||
const { lineItemAdjustmentsToCreate } =
|
||||
prepareAdjustmentsFromPromotionActionsStep({ actions })
|
||||
|
||||
const orderChangeActionAdjustmentsInput = transform(
|
||||
{
|
||||
order: input.order,
|
||||
previewedOrder,
|
||||
orderChange: input.orderChange,
|
||||
lineItemAdjustmentsToCreate,
|
||||
exchangeId: input.exchange_id,
|
||||
claimId: input.claim_id,
|
||||
returnId: input.return_id,
|
||||
},
|
||||
({
|
||||
order,
|
||||
previewedOrder,
|
||||
orderChange,
|
||||
lineItemAdjustmentsToCreate,
|
||||
exchangeId,
|
||||
claimId,
|
||||
returnId,
|
||||
}) => {
|
||||
return previewedOrder.items.map((item) => {
|
||||
const itemAdjustments = lineItemAdjustmentsToCreate.filter(
|
||||
(adjustment) => adjustment.item_id === item.id
|
||||
)
|
||||
|
||||
return {
|
||||
order_change_id: orderChange.id,
|
||||
order_id: order.id,
|
||||
...(exchangeId && { exchange_id: exchangeId }),
|
||||
...(claimId && { claim_id: claimId }),
|
||||
...(returnId && { return_id: returnId }),
|
||||
version: orderChange.version,
|
||||
action: ChangeActionType.ITEM_ADJUSTMENTS_REPLACE,
|
||||
details: {
|
||||
reference_id: item.id,
|
||||
adjustments: itemAdjustments,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
createOrderChangeActionsWorkflow
|
||||
.runAsStep({ input: orderChangeActionAdjustmentsInput })
|
||||
.config({ name: "order-change-action-adjustments-input" })
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -22,7 +22,7 @@ import { addOrderLineItemsWorkflow } from "../add-line-items"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
import { updateOrderTaxLinesWorkflow } from "../update-tax-lines"
|
||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
|
||||
/**
|
||||
* The data to validate that new items can be added to an order edit.
|
||||
@@ -118,7 +118,7 @@ export const orderEditAddNewItemWorkflow = createWorkflow(
|
||||
|
||||
const orderChangeResult = useQueryGraphStep({
|
||||
entity: "order_change",
|
||||
fields: ["id", "status", "version", "actions.*"],
|
||||
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||
filters: {
|
||||
order_id: input.order_id,
|
||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||
|
||||
/**
|
||||
@@ -128,7 +128,7 @@ export const orderEditUpdateItemQuantityWorkflow = createWorkflow(
|
||||
|
||||
const orderChangeResult = useQueryGraphStep({
|
||||
entity: "order_change",
|
||||
fields: ["id", "status", "version", "actions.*"],
|
||||
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||
filters: {
|
||||
order_id: input.order_id,
|
||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
throwIfIsCancelled,
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||
|
||||
/**
|
||||
@@ -143,7 +143,7 @@ export const removeItemOrderEditActionWorkflow = createWorkflow(
|
||||
|
||||
const orderChangeResult = useQueryGraphStep({
|
||||
entity: "order_change",
|
||||
fields: ["id", "status", "version", "actions.*"],
|
||||
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||
filters: {
|
||||
order_id: input.order_id,
|
||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
throwIfIsCancelled,
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||
|
||||
/**
|
||||
@@ -142,7 +142,7 @@ export const updateOrderEditAddItemWorkflow = createWorkflow(
|
||||
|
||||
const orderChangeResult = useQueryGraphStep({
|
||||
entity: "order_change",
|
||||
fields: ["id", "status", "version", "actions.*"],
|
||||
fields: ["id", "status", "version", "actions.*", "carry_over_promotions"],
|
||||
filters: {
|
||||
order_id: input.order_id,
|
||||
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
throwIfIsCancelled,
|
||||
throwIfOrderChangeIsNotActive,
|
||||
} from "../../utils/order-validation"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "./compute-adjustments-for-preview"
|
||||
import { computeAdjustmentsForPreviewWorkflow } from "../compute-adjustments-for-preview"
|
||||
import { fieldsToRefreshOrderEdit } from "./utils/fields"
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { OrderChangeDTO, UpdateOrderChangeDTO } from "@medusajs/framework/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createWorkflow,
|
||||
transform,
|
||||
when,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { updateOrderChangesStep } from "../steps/update-order-changes"
|
||||
import { onCarryPromotionsFlagSet } from "./on-carry-promotions-flag-set"
|
||||
|
||||
export const updateOrderChangeWorkflowId = "update-order-change-workflow"
|
||||
|
||||
/**
|
||||
* This workflow updates an order change.
|
||||
* If the carry_over_promotions flag is provided, it calls onCarryPromotionsFlagSet
|
||||
* to handle the promotion logic. Otherwise, it updates the order change directly.
|
||||
*
|
||||
* @example
|
||||
* const { result } = await updateOrderChangeWorkflow(container)
|
||||
* .run({
|
||||
* input: {
|
||||
* id: "orch_123",
|
||||
* carry_over_promotions: true,
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
* Update an order change, conditionally handling promotion carry-over if specified.
|
||||
*/
|
||||
export const updateOrderChangeWorkflow = createWorkflow(
|
||||
updateOrderChangeWorkflowId,
|
||||
function (
|
||||
input: WorkflowData<UpdateOrderChangeDTO>
|
||||
): WorkflowResponse<OrderChangeDTO> {
|
||||
const updatedOrderChange = updateOrderChangesStep([input])
|
||||
|
||||
when(
|
||||
"should-call-carry-over-promotion-workflow",
|
||||
input,
|
||||
({ carry_over_promotions }) => typeof carry_over_promotions === "boolean"
|
||||
).then(() => {
|
||||
return onCarryPromotionsFlagSet.runAsStep({
|
||||
input: {
|
||||
order_change_id: input.id,
|
||||
carry_over_promotions: input.carry_over_promotions!,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
return new WorkflowResponse(
|
||||
transform(
|
||||
{ updatedOrderChange },
|
||||
({ updatedOrderChange }) => updatedOrderChange?.[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user