feat(medusa,core-flows,types,js-sdk): Draft Order workflows and API endpoints (#11805)

This commit is contained in:
Kasper Fabricius Kristensen
2025-04-02 10:23:33 +02:00
committed by GitHub
parent 5f3a82f5c3
commit f441362f4a
75 changed files with 4235 additions and 21 deletions
@@ -102,5 +102,18 @@ medusaIntegrationTestRunner({
expect(response.data.draft_order.email).toBe("test_new@test.com")
})
})
describe("POST /draft-orders/:id/convert-to-order", () => {
it("should convert a draft order to an order", async () => {
const response = await api.post(
`/admin/draft-orders/${testDraftOrder.id}/convert-to-order`,
{},
adminHeaders
)
expect(response.status).toBe(200)
expect(response.data.order.status).toBe("pending")
})
})
},
})
@@ -24,7 +24,7 @@ export const validateLineItemPricesStepId = "validate-line-item-prices"
/**
* This step validates the specified line item objects to ensure they have prices.
* If an item doesn't have a price, the step throws an error.
*
*
* @example
* const data = validateLineItemPricesStep({
* items: [
@@ -0,0 +1,2 @@
export * from "./steps"
export * from "./workflows"
@@ -0,0 +1,66 @@
import { Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import {
CreateLineItemAdjustmentDTO,
IOrderModuleService,
} from "@medusajs/types"
export const createDraftOrderLineItemAdjustmentsStepId =
"create-draft-order-line-item-adjustments"
interface CreateDraftOrderLineItemAdjustmentsStepInput {
order_id: string
lineItemAdjustmentsToCreate: CreateLineItemAdjustmentDTO[]
}
export const createDraftOrderLineItemAdjustmentsStep = createStep(
createDraftOrderLineItemAdjustmentsStepId,
async function (
data: CreateDraftOrderLineItemAdjustmentsStepInput,
{ container }
) {
const { lineItemAdjustmentsToCreate = [], order_id } = data
if (!lineItemAdjustmentsToCreate?.length) {
return new StepResponse(void 0, [])
}
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
/**
* If an items quantity has been changed to 0, it will result in an undefined amount.
* In this case, we don't want to create an adjustment, as the item will be removed,
* and trying to create an adjustment will throw an error.
*/
const filteredAdjustments = lineItemAdjustmentsToCreate.filter(
(adjustment) => {
return !!adjustment.amount
}
)
const lineItemAdjustments = await service.createOrderLineItemAdjustments(
filteredAdjustments.map((adjustment) => ({
...adjustment,
order_id,
}))
)
const createdLineItemAdjustments = lineItemAdjustments.map(
(adjustment) => adjustment.id
)
return new StepResponse(
createdLineItemAdjustments,
createdLineItemAdjustments
)
},
async function (createdLineItemAdjustments, { container }) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
if (!createdLineItemAdjustments?.length) {
return
}
await service.deleteOrderLineItemAdjustments(createdLineItemAdjustments)
}
)
@@ -0,0 +1,54 @@
import { Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import {
CreateShippingMethodAdjustmentDTO,
IOrderModuleService,
} from "@medusajs/types"
export const createDraftOrderShippingMethodAdjustmentsStepId =
"create-draft-order-shipping-method-adjustments"
interface CreateDraftOrderShippingMethodAdjustmentsStepInput {
shippingMethodAdjustmentsToCreate: CreateShippingMethodAdjustmentDTO[]
}
export const createDraftOrderShippingMethodAdjustmentsStep = createStep(
createDraftOrderShippingMethodAdjustmentsStepId,
async function (
data: CreateDraftOrderShippingMethodAdjustmentsStepInput,
{ container }
) {
const { shippingMethodAdjustmentsToCreate = [] } = data
if (!shippingMethodAdjustmentsToCreate?.length) {
return new StepResponse(void 0, [])
}
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
const shippingMethodAdjustments =
await service.createOrderShippingMethodAdjustments(
shippingMethodAdjustmentsToCreate
)
const createdShippingMethodAdjustments = shippingMethodAdjustments.map(
(adjustment) => adjustment.id
)
return new StepResponse(
createdShippingMethodAdjustments,
createdShippingMethodAdjustments
)
},
async function (createdShippingMethodAdjustments, { container }) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
if (!createdShippingMethodAdjustments?.length) {
return
}
await service.deleteOrderShippingMethodAdjustments(
createdShippingMethodAdjustments
)
}
)
@@ -0,0 +1,24 @@
import { Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { IOrderModuleService, OrderDTO } from "@medusajs/types"
interface GetDraftOrderPromotionContextStepInput {
order: OrderDTO
}
export const getDraftOrderPromotionContextStep = createStep(
"get-draft-order-promotion-context",
async ({ order }: GetDraftOrderPromotionContextStepInput, { container }) => {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
const preview = await service.previewOrderChange(order.id)
const orderWithPreviewItemsAndAShipping: OrderDTO = {
...order,
items: preview.items,
shipping_methods: preview.shipping_methods,
}
return new StepResponse(orderWithPreviewItemsAndAShipping)
}
)
@@ -0,0 +1 @@
export * from "./validate-draft-order"
@@ -0,0 +1,46 @@
import { Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { IOrderModuleService } from "@medusajs/types"
export const removeDraftOrderLineItemAdjustmentsStepId =
"remove-draft-order-line-item-adjustments"
interface RemoveDraftOrderLineItemAdjustmentsStepInput {
lineItemAdjustmentIdsToRemove: string[]
}
export const removeDraftOrderLineItemAdjustmentsStep = createStep(
removeDraftOrderLineItemAdjustmentsStepId,
async function (
data: RemoveDraftOrderLineItemAdjustmentsStepInput,
{ container }
) {
const { lineItemAdjustmentIdsToRemove = [] } = data
if (!lineItemAdjustmentIdsToRemove?.length) {
return new StepResponse(void 0, [])
}
const draftOrderModuleService = container.resolve<IOrderModuleService>(
Modules.ORDER
)
await draftOrderModuleService.deleteOrderLineItemAdjustments(
lineItemAdjustmentIdsToRemove
)
return new StepResponse(void 0, lineItemAdjustmentIdsToRemove)
},
async function (lineItemAdjustmentIdsToRemove, { container }) {
const draftOrderModuleService = container.resolve<IOrderModuleService>(
Modules.ORDER
)
if (!lineItemAdjustmentIdsToRemove?.length) {
return
}
await draftOrderModuleService.restoreOrderLineItemAdjustments(
lineItemAdjustmentIdsToRemove
)
}
)
@@ -0,0 +1,43 @@
import { Modules } from "@medusajs/framework/utils"
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
import { IOrderModuleService } from "@medusajs/types"
export const removeDraftOrderShippingMethodAdjustmentsStepId =
"remove-draft-order-shipping-method-adjustments"
interface RemoveDraftOrderShippingMethodAdjustmentsStepInput {
shippingMethodAdjustmentIdsToRemove: string[]
}
export const removeDraftOrderShippingMethodAdjustmentsStep = createStep(
removeDraftOrderShippingMethodAdjustmentsStepId,
async function (
data: RemoveDraftOrderShippingMethodAdjustmentsStepInput,
{ container }
) {
const { shippingMethodAdjustmentIdsToRemove = [] } = data
if (!shippingMethodAdjustmentIdsToRemove?.length) {
return new StepResponse(void 0, [])
}
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
await service.deleteOrderShippingMethodAdjustments(
shippingMethodAdjustmentIdsToRemove
)
return new StepResponse(void 0, shippingMethodAdjustmentIdsToRemove)
},
async function (shippingMethodAdjustmentIdsToRemove, { container }) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
if (!shippingMethodAdjustmentIdsToRemove?.length) {
return
}
await service.restoreOrderShippingMethodAdjustments(
shippingMethodAdjustmentIdsToRemove
)
}
)
@@ -0,0 +1,55 @@
import { Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, IOrderModuleService } from "@medusajs/types"
export const restoreDraftOrderShippingMethodsStepId =
"restore-draft-order-shipping-methods"
interface RestoreDraftOrderShippingMethodsStepInput {
shippingMethods: {
id: string
before: {
shipping_option_id: string
amount: BigNumberInput
}
after: {
shipping_option_id: string
amount: BigNumberInput
}
}[]
}
export const restoreDraftOrderShippingMethodsStep = createStep(
restoreDraftOrderShippingMethodsStepId,
async function (
input: RestoreDraftOrderShippingMethodsStepInput,
{ container }
) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
await service.updateOrderShippingMethods(
input.shippingMethods.map(({ id, before }) => ({
id,
shipping_option_id: before.shipping_option_id,
amount: before.amount,
}))
)
return new StepResponse(void 0, input.shippingMethods)
},
async (input, { container }) => {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
if (!input) {
return
}
await service.updateOrderShippingMethods(
input.map(({ id, after }) => ({
id,
shipping_option_id: after.shipping_option_id,
amount: after.amount,
}))
)
}
)
@@ -0,0 +1,106 @@
import {
ContainerRegistrationKeys,
Modules,
PromotionActions,
} from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { IPromotionModuleService } from "@medusajs/types"
export const updateDraftOrderPromotionsStepId = "update-draft-order-promotions"
interface UpdateDraftOrderPromotionsStepInput {
id: string
promo_codes: string[]
action?: PromotionActions
}
export const updateDraftOrderPromotionsStep = createStep(
updateDraftOrderPromotionsStepId,
async function (data: UpdateDraftOrderPromotionsStepInput, { container }) {
const { id, promo_codes = [], action = PromotionActions.ADD } = data
const remoteLink = container.resolve(ContainerRegistrationKeys.LINK)
const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY
)
const promotionService = container.resolve<IPromotionModuleService>(
Modules.PROMOTION
)
const existingDraftOrderPromotionLinks = await remoteQuery({
entryPoint: "order_promotion",
fields: ["order_id", "promotion_id"],
variables: { order_id: [id] },
})
const promotionLinkMap = new Map<string, any>(
existingDraftOrderPromotionLinks.map((link) => [link.promotion_id, link])
)
const linksToCreate: any[] = []
const linksToDismiss: any[] = []
if (promo_codes?.length) {
const promotions = await promotionService.listPromotions(
{ code: promo_codes },
{ select: ["id"] }
)
for (const promotion of promotions) {
const linkObject = {
[Modules.ORDER]: { order_id: id },
[Modules.PROMOTION]: { promotion_id: promotion.id },
}
if ([PromotionActions.ADD, PromotionActions.REPLACE].includes(action)) {
linksToCreate.push(linkObject)
}
if (action === PromotionActions.REMOVE) {
const link = promotionLinkMap.get(promotion.id)
if (link) {
linksToDismiss.push(linkObject)
}
}
}
}
if (action === PromotionActions.REPLACE) {
for (const link of existingDraftOrderPromotionLinks) {
linksToDismiss.push({
[Modules.ORDER]: { order_id: link.order_id },
[Modules.PROMOTION]: { promotion_id: link.promotion_id },
})
}
}
if (linksToDismiss.length) {
await remoteLink.dismiss(linksToDismiss)
}
const createdLinks = linksToCreate.length
? await remoteLink.create(linksToCreate)
: []
return new StepResponse(null, {
// @ts-expect-error
createdLinkIds: createdLinks.map((link) => link.id),
dismissedLinks: linksToDismiss,
})
},
async function (revertData, { container }) {
const { dismissedLinks, createdLinkIds } = revertData ?? {}
const remoteLink = container.resolve(ContainerRegistrationKeys.LINK)
if (dismissedLinks?.length) {
await remoteLink.create(dismissedLinks)
}
if (createdLinkIds?.length) {
// @ts-expect-error
await remoteLink.delete(createdLinkIds)
}
}
)
@@ -0,0 +1,66 @@
import { MedusaError, Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, IOrderModuleService } from "@medusajs/types"
export const updateDraftOrderShippingMethodStepId =
"update-draft-order-shipping-method"
interface UpdateDraftOrderShippingMethodStepInput {
order_id: string
shipping_method_id: string
shipping_option_id?: string
amount?: BigNumberInput
metadata?: Record<string, unknown> | null
}
export const updateDraftOrderShippingMethodStep = createStep(
updateDraftOrderShippingMethodStepId,
async function (
input: UpdateDraftOrderShippingMethodStepInput,
{ container }
) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
const [beforeUpdate] = await service.listOrderShippingMethods(
{
id: input.shipping_method_id,
},
{
take: 1,
select: ["id", "shipping_option_id", "amount"],
}
)
if (!beforeUpdate) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`A shipping method with id ${input.shipping_method_id} was not found`
)
}
const [updatedMethod] = await service.updateOrderShippingMethods([
{
id: input.shipping_method_id,
shipping_option_id: input.shipping_option_id,
amount: input.amount,
},
])
return new StepResponse(
{
before: beforeUpdate,
after: updatedMethod,
},
beforeUpdate
)
},
(input, { container }) => {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
if (!input) {
return
}
service.updateOrderShippingMethods([input])
}
)
@@ -0,0 +1,19 @@
import { createStep } from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { throwIfOrderChangeIsNotActive } from "../../order/utils/order-validation"
import { throwIfNotDraftOrder } from "../utils/validation"
interface ValidateDraftOrderChangeStepInput {
order: OrderDTO
orderChange: OrderChangeDTO
}
export const validateDraftOrderChangeStepId = "validate-draft-order-change"
export const validateDraftOrderChangeStep = createStep(
validateDraftOrderChangeStepId,
async function ({ order, orderChange }: ValidateDraftOrderChangeStepInput) {
throwIfNotDraftOrder({ order })
throwIfOrderChangeIsNotActive({ orderChange })
}
)
@@ -0,0 +1,40 @@
import { OrderChangeActionDTO } from "@medusajs/types"
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderWorkflow } from "@medusajs/types"
export interface ValidateDraftOrderUpdateActionItemStepInput {
input: OrderWorkflow.DeleteOrderEditItemActionWorkflowInput
orderChange: OrderChangeDTO
}
export const validateDraftOrderRemoveActionItemStep = createStep(
"validate-draft-order-remove-action-item",
async function ({
input,
orderChange,
}: ValidateDraftOrderUpdateActionItemStepInput) {
const associatedAction = (orderChange.actions ?? []).find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
if (!associatedAction) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`No item found for order ${input.order_id} in order change ${orderChange.id}`
)
}
if (
![ChangeActionType.ITEM_ADD, ChangeActionType.ITEM_UPDATE].includes(
associatedAction.action as ChangeActionType
)
) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Action ${associatedAction.id} is not adding or updating an item`
)
}
}
)
@@ -0,0 +1,38 @@
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { createStep } from "@medusajs/framework/workflows-sdk"
import {
OrderChangeActionDTO,
OrderChangeDTO,
OrderWorkflow,
} from "@medusajs/types"
export interface ValidateDraftOrderShippingMethodActionStepInput {
input: OrderWorkflow.DeleteOrderEditShippingMethodWorkflowInput
orderChange: OrderChangeDTO
}
export const validateDraftOrderShippingMethodActionStep = createStep(
"validate-draft-order-shipping-method-action",
async function ({
input,
orderChange,
}: ValidateDraftOrderShippingMethodActionStepInput) {
const associatedAction = (orderChange.actions ?? []).find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
if (!associatedAction) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`No shipping method found for order ${input.order_id} in order change ${orderChange.id}`
)
}
if (associatedAction.action !== ChangeActionType.SHIPPING_ADD) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Action ${associatedAction.id} is not adding a shipping method`
)
}
}
)
@@ -0,0 +1,36 @@
import { OrderChangeActionDTO } from "@medusajs/types"
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderWorkflow } from "@medusajs/types"
export interface ValidateDraftOrderUpdateActionItemStepInput {
input: OrderWorkflow.UpdateOrderEditAddNewItemWorkflowInput
orderChange: OrderChangeDTO
}
export const validateDraftOrderUpdateActionItemStep = createStep(
"validate-draft-order-update-action-item",
async function ({
input,
orderChange,
}: ValidateDraftOrderUpdateActionItemStepInput) {
const associatedAction = (orderChange.actions ?? []).find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
if (!associatedAction) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`No request to add item for order ${input.order_id} in order change ${orderChange.id}`
)
}
if (associatedAction.action !== ChangeActionType.ITEM_ADD) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Action ${associatedAction.id} is not adding an item`
)
}
}
)
@@ -0,0 +1,19 @@
import { MedusaError, OrderStatus } from "@medusajs/framework/utils"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { OrderDTO } from "@medusajs/types"
interface ValidateDraftOrderStepInput {
order: OrderDTO
}
export const validateDraftOrderStep = createStep(
"validate-draft-order",
async function ({ order }: ValidateDraftOrderStepInput) {
if (order.status !== OrderStatus.DRAFT && !order.is_draft_order) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Order ${order.id} is not a draft order`
)
}
}
)
@@ -0,0 +1,23 @@
import { createStep } from "@medusajs/framework/workflows-sdk"
import { PromotionDTO } from "@medusajs/types"
import {
throwIfCodesAreInactive,
throwIfCodesAreMissing,
} from "../utils/validation"
export const validatePromoCodesToAddId = "validate-promo-codes-to-add"
interface ValidatePromoCodesToAddStepInput {
promo_codes: string[]
promotions: PromotionDTO[]
}
export const validatePromoCodesToAddStep = createStep(
validatePromoCodesToAddId,
async function (input: ValidatePromoCodesToAddStepInput) {
const { promo_codes, promotions } = input
throwIfCodesAreMissing(promo_codes, promotions)
throwIfCodesAreInactive(promo_codes, promotions)
}
)
@@ -0,0 +1,19 @@
import { createStep } from "@medusajs/framework/workflows-sdk"
import { PromotionDTO } from "@medusajs/types"
import { throwIfCodesAreMissing } from "../utils/validation"
export const validatePromoCodesToRemoveId = "validate-promo-codes-to-remove"
interface ValidatePromoCodesToRemoveStepInput {
promo_codes: string[]
promotions: PromotionDTO[]
}
export const validatePromoCodesToRemoveStep = createStep(
validatePromoCodesToRemoveId,
async function (input: ValidatePromoCodesToRemoveStepInput) {
const { promo_codes, promotions } = input
throwIfCodesAreMissing(promo_codes, promotions)
}
)
@@ -0,0 +1,38 @@
export const draftOrderFieldsForRefreshSteps = [
"id",
"is_draft_order",
"status",
"currency_code",
"metadata",
"sales_channel_id",
"region_id",
"region.*",
"items.*",
"items.product.id",
"items.product.collection_id",
"items.product.categories.id",
"items.product.tags.id",
"items.variant.id",
"items.variant.product.id",
"items.variant.weight",
"items.variant.length",
"items.variant.height",
"items.variant.width",
"items.variant.material",
"items.adjustments.*",
"items.tax_lines.*",
"shipping_address.*",
"shipping_methods.*",
"shipping_methods.adjustments.*",
"shipping_methods.tax_lines.*",
"customer.*",
"customer.groups.*",
"promotion_link.*",
"promotion_link.promotion",
"promotion_link.promotion.id",
"promotion_link.promotion.code",
"subtotal",
"item_total",
"total",
"item_subtotal",
]
@@ -0,0 +1,66 @@
import {
MedusaError,
OrderStatus,
PromotionStatus,
} from "@medusajs/framework/utils"
import { OrderDTO, PromotionDTO } from "@medusajs/types"
interface ThrowIfNotDraftOrderInput {
order: OrderDTO
}
export function throwIfNotDraftOrder({ order }: ThrowIfNotDraftOrderInput) {
if (order.status !== OrderStatus.DRAFT && !order.is_draft_order) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Order is not a draft"
)
}
}
function getMessageByCount(count: number, singular: string, plural: string) {
return count === 1 ? singular : plural
}
export function throwIfCodesAreMissing(
promo_codes: string[],
promotions: PromotionDTO[]
) {
const missingPromoCodes = promo_codes.filter(
(code) => !promotions.some((promotion) => promotion.code === code)
)
if (missingPromoCodes.length > 0) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
getMessageByCount(
missingPromoCodes.length,
`Promotion code "${missingPromoCodes[0]}" not found`,
`Promotion codes "${missingPromoCodes.join('", "')}" not found`
)
)
}
}
export function throwIfCodesAreInactive(
promo_codes: string[],
promotions: PromotionDTO[]
) {
const inactivePromoCodes = promo_codes.filter((code) =>
promotions.some(
(promotion) =>
promotion.code === code && promotion.status !== PromotionStatus.ACTIVE
)
)
if (inactivePromoCodes.length > 0) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
getMessageByCount(
inactivePromoCodes.length,
`Promotion code "${inactivePromoCodes[0]}" is not active`,
`Promotion codes "${inactivePromoCodes.join('", "')}" are not active`
)
)
}
}
@@ -0,0 +1,128 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO, OrderWorkflow } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
addOrderLineItemsWorkflow,
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
updateOrderTaxLinesWorkflow,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const addDraftOrderItemsWorkflowId = "add-draft-order-items"
export const addDraftOrderItemsWorkflow = createWorkflow(
addDraftOrderItemsWorkflowId,
function (
input: WorkflowData<OrderWorkflow.OrderEditAddNewItemWorkflowInput>
) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
const lineItems = addOrderLineItemsWorkflow.runAsStep({
input: {
order_id: order.id,
items: input.items,
},
})
const lineItemIds = transform(lineItems, (lineItems) => {
return lineItems.map((item) => item.id)
})
updateOrderTaxLinesWorkflow.runAsStep({
input: {
order_id: order.id,
item_ids: lineItemIds,
},
})
const appliedPromoCodes: string[] = transform(order, (order) => {
const promotionLink = (order as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
// If any the order has any promo codes, then we need to refresh the adjustments.
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
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,
compare_at_unit_price:
item.compare_at_unit_price ??
lineItems[index].compare_at_unit_price,
metadata: item.metadata,
},
}))
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: orderChangeActionInput,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,103 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO, PromotionDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validatePromoCodesToAddStep } from "../steps/validate-promo-codes-to-add"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const addDraftOrderPromotionWorkflowId = "add-draft-order-promotion"
interface AddDraftOrderPromotionWorkflowInput {
order_id: string
promo_codes: string[]
}
export const addDraftOrderPromotionWorkflow = createWorkflow(
addDraftOrderPromotionWorkflowId,
function (input: WorkflowData<AddDraftOrderPromotionWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
const promotions: PromotionDTO[] = useRemoteQueryStep({
entry_point: "promotion",
fields: ["id", "code", "status"],
variables: {
filters: {
code: input.promo_codes,
},
},
list: true,
}).config({ name: "promotions-query" })
validatePromoCodesToAddStep({
promo_codes: input.promo_codes,
promotions,
})
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: input.promo_codes,
action: PromotionActions.ADD,
},
})
const orderChangeActionInput = transform(
{ order, orderChange, promotions },
({ order, orderChange, promotions }) => {
return promotions.map((promotion) => ({
action: ChangeActionType.PROMOTION_ADD,
reference: "order_promotion",
order_change_id: orderChange.id,
reference_id: promotion.id,
order_id: order.id,
details: {
added_code: promotion.code,
},
}))
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: orderChangeActionInput,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,184 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
updateOrderTaxLinesWorkflow,
} from "../../order"
import { createOrderShippingMethods } from "../../order/steps/create-order-shipping-methods"
import { prepareShippingMethod } from "../../order/utils/prepare-shipping-method"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const addDraftOrderShippingMethodsWorkflowId =
"add-draft-order-shipping-methods"
interface AddDraftOrderShippingMethodsWorkflowInput {
/**
* The ID of the draft order to add the shipping methods to.
*/
order_id: string
/**
* The ID of the shipping option to add the shipping methods from.
*/
shipping_option_id: string
/**
* The custom amount to add the shipping methods with.
*/
custom_amount?: BigNumberInput | null
}
export const addDraftOrderShippingMethodsWorkflow = createWorkflow(
addDraftOrderShippingMethodsWorkflowId,
function (input: WorkflowData<AddDraftOrderShippingMethodsWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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"],
variables: {
filters: {
order_id: input.order_id,
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
},
},
list: false,
}).config({ name: "order-change-query" })
validateDraftOrderChangeStep({ order, orderChange })
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 shippingMethodInput = transform(
{
relatedEntity: { order_id: order.id },
shippingOptions,
customPrice: input.custom_amount as any, // Need to cast this to any otherwise the type becomes to complex.
orderChange,
input,
},
prepareShippingMethod()
)
const createdMethods = createOrderShippingMethods({
shipping_methods: [shippingMethodInput],
})
const shippingMethodIds = transform(createdMethods, (createdMethods) => {
return createdMethods.map((item) => item.id)
})
updateOrderTaxLinesWorkflow.runAsStep({
input: {
order_id: order.id,
shipping_method_ids: shippingMethodIds,
},
})
const appliedPromoCodes = transform(order, (order) => {
const promotionLink = (order as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
// If any the order has any promo codes, then we need to refresh the adjustments.
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
const refetchedOrder = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
variables: { id: input.order_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "refetched-order-query" })
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order: refetchedOrder,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
const orderChangeActionInput = transform(
{
order,
shippingOptions,
createdMethods,
customPrice: input.custom_amount as any, // Need to cast this to any otherwise the type becomes to complex.
orderChange,
},
({
shippingOptions,
order,
createdMethods,
customPrice,
orderChange,
}) => {
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,
}
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: [orderChangeActionInput],
})
return new WorkflowResponse(previewOrderChangeStep(order.id))
}
)
@@ -0,0 +1,39 @@
import {
createWorkflow,
transform,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderDTO, OrderWorkflow } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import { createOrderChangeStep } from "../../order"
import { validateDraftOrderStep } from "../steps"
export const beginDraftOrderEditWorkflowId = "begin-draft-order-edit"
export const beginDraftOrderEditWorkflow = createWorkflow(
beginDraftOrderEditWorkflowId,
function (input: WorkflowData<OrderWorkflow.BeginorderEditWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
variables: { id: input.order_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "order-query" })
validateDraftOrderStep({ 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))
}
)
@@ -0,0 +1,149 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
parallelize,
transform,
when,
WorkflowData,
} from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import { deleteOrderChangesStep, deleteOrderShippingMethods } from "../../order"
import { restoreDraftOrderShippingMethodsStep } from "../steps/restore-draft-order-shipping-methods"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const cancelDraftOrderEditWorkflowId = "cancel-draft-order-edit"
export interface CancelDraftOrderEditWorkflowInput {
order_id: string
}
export const cancelDraftOrderEditWorkflow = createWorkflow(
cancelDraftOrderEditWorkflowId,
function (input: WorkflowData<CancelDraftOrderEditWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["version", ...draftOrderFieldsForRefreshSteps],
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" })
validateDraftOrderChangeStep({ order, orderChange })
const shippingToRemove = transform(
{ orderChange, input },
({ orderChange }) => {
return (orderChange.actions ?? [])
.filter((a) => a.action === ChangeActionType.SHIPPING_ADD)
.map(({ id }) => id)
}
)
const shippingToRestore = transform(
{ orderChange, input },
({ orderChange }) => {
return (orderChange.actions ?? [])
.filter((a) => a.action === ChangeActionType.SHIPPING_UPDATE)
.map(({ reference_id, details }) => ({
id: reference_id,
before: {
shipping_option_id: details?.old_shipping_option_id,
amount: details?.old_amount,
},
after: {
shipping_option_id: details?.new_shipping_option_id,
amount: details?.new_amount,
},
}))
}
)
const promotionsToRemove = transform(
{ orderChange, input },
({ orderChange }) => {
return (orderChange.actions ?? [])
.filter((a) => a.action === ChangeActionType.PROMOTION_ADD)
.map(({ details }) => details?.added_code)
.filter(Boolean) as string[]
}
)
const promotionsToRestore = transform(
{ orderChange, input },
({ orderChange }) => {
return (orderChange.actions ?? [])
.filter((a) => a.action === ChangeActionType.PROMOTION_REMOVE)
.map(({ details }) => details?.removed_code)
.filter(Boolean) as string[]
}
)
const promotionsToRefresh = transform(
{ order, promotionsToRemove, promotionsToRestore },
({ order, promotionsToRemove, promotionsToRestore }) => {
const promotionLink = (order as any).promotion_link
const codes: Set<string> = new Set()
if (promotionLink) {
if (Array.isArray(promotionLink)) {
promotionLink.forEach((promo) => {
codes.add(promo.promotion.code)
})
} else {
codes.add(promotionLink.promotion.code)
}
}
for (const code of promotionsToRemove) {
codes.delete(code)
}
for (const code of promotionsToRestore) {
codes.add(code)
}
return Array.from(codes)
}
)
parallelize(
deleteOrderChangesStep({ ids: [orderChange.id] }),
deleteOrderShippingMethods({ ids: shippingToRemove })
)
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: promotionsToRefresh,
action: PromotionActions.REPLACE,
},
})
when(shippingToRestore, (methods) => {
return !!methods?.length
}).then(() => {
restoreDraftOrderShippingMethodsStep({
shippingMethods: shippingToRestore as any,
})
})
}
)
@@ -0,0 +1,216 @@
import {
ChangeActionType,
MathBN,
OrderChangeStatus,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { reserveInventoryStep } from "../../cart"
import { prepareConfirmInventoryInput } from "../../cart/utils/prepare-confirm-inventory-input"
import { useRemoteQueryStep } from "../../common"
import {
createOrUpdateOrderPaymentCollectionWorkflow,
previewOrderChangeStep,
} from "../../order"
import { confirmOrderChanges } from "../../order/steps/confirm-order-changes"
import { deleteReservationsByLineItemsStep } from "../../reservation"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
export const confirmDraftOrderEditWorkflowId = "confirm-draft-order-edit"
export interface ConfirmDraftOrderEditWorkflowInput {
/**
* The ID of the draft order to confirm the edit for.
*/
order_id: string
/**
* The ID of the user confirming the edit.
*/
confirmed_by: string
}
export const confirmDraftOrderEditWorkflow = createWorkflow(
confirmDraftOrderEditWorkflowId,
function (input: ConfirmDraftOrderEditWorkflowInput) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: [
"id",
"status",
"is_draft_order",
"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",
"status",
"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" })
validateDraftOrderChangeStep({
order,
orderChange,
})
const orderPreview = previewOrderChangeStep(order.id)
confirmOrderChanges({
changes: [orderChange],
orderId: order.id,
confirmed_by: input.confirmed_by,
})
const orderItems = useRemoteQueryStep({
entry_point: "order",
fields: [
"id",
"version",
"canceled_at",
"sales_channel_id",
"items.*",
"items.variant.manage_inventory",
"items.variant.allow_backorder",
"items.variant.inventory_items.inventory_item_id",
"items.variant.inventory_items.required_quantity",
"items.variant.inventory_items.inventory.location_levels.stock_locations.id",
"items.variant.inventory_items.inventory.location_levels.stock_locations.name",
"items.variant.inventory_items.inventory.location_levels.stock_locations.sales_channels.id",
"items.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-items-query" })
const lineItemIds = transform(
{ orderItems, previousOrderItems: order.items },
(data) => {
const previousItemIds = (data.previousOrderItems || []).map(
({ id }) => id
) // items that have been removed with the change
const newItemIds = data.orderItems.items.map(({ id }) => id)
return [...new Set([...previousItemIds, ...newItemIds])]
}
)
deleteReservationsByLineItemsStep(lineItemIds)
const { variants, items } = transform(
{ orderItems, orderPreview },
({ orderItems, orderPreview }) => {
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 ||
a.action === ChangeActionType.ITEM_UPDATE
)
)
if (!itemAction) {
return
}
const unitPrice: BigNumberInput =
itemAction.raw_unit_price ?? itemAction.unit_price
const compareAtUnitPrice: BigNumberInput | undefined =
itemAction.raw_compare_at_unit_price ??
itemAction.compare_at_unit_price
const updateAction = itemAction.actions!.find(
(a) => a.action === ChangeActionType.ITEM_UPDATE
)
const quantity: BigNumberInput =
itemAction.raw_quantity ?? itemAction.quantity
const newQuantity = updateAction
? MathBN.sub(quantity, ordItem.raw_quantity)
: quantity
if (MathBN.lte(newQuantity, 0)) {
return
}
const reservationQuantity = MathBN.sub(
newQuantity,
ordItem.raw_fulfilled_quantity
)
allItems.push({
id: ordItem.id,
variant_id: ordItem.variant_id,
quantity: reservationQuantity,
unit_price: unitPrice,
compare_at_unit_price: compareAtUnitPrice,
})
allVariants.push(ordItem.variant)
})
return {
variants: allVariants,
items: allItems,
}
}
)
const formatedInventoryItems = transform(
{
input: {
sales_channel_id: (orderItems as any).sales_channel_id,
variants,
items,
},
},
prepareConfirmInventoryInput
)
reserveInventoryStep(formatedInventoryItems)
createOrUpdateOrderPaymentCollectionWorkflow.runAsStep({
input: {
order_id: order.id,
},
})
return new WorkflowResponse(orderPreview)
}
)
@@ -0,0 +1,96 @@
import { Modules, OrderStatus } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
StepResponse,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { IOrderModuleService, OrderDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import { validateDraftOrderStep } from "../steps/validate-draft-order"
const convertDraftOrderWorkflowId = "convert-draft-order"
interface ConvertDraftOrderWorkflowInput {
id: string
}
interface ConvertDraftOrderStepInput {
id: string
}
/**
* This step converts a draft order to a pending order.
*
* @example
* ```typescript
* const order = await convertDraftOrderStep({ id: "order_123" })
* ```
*/
const convertDraftOrderStep = createStep(
"convert-draft-order",
async function ({ id }: ConvertDraftOrderStepInput, { container }) {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
const response = await service.updateOrders([
{
id,
status: OrderStatus.PENDING,
is_draft_order: false,
},
])
const order = response[0]
return new StepResponse(order, {
id,
})
},
async function (prevData, { container }) {
if (!prevData) {
return
}
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
await service.updateOrders([
{
id: prevData.id,
status: OrderStatus.DRAFT,
is_draft_order: true,
},
])
}
)
/**
* This workflow converts a draft order to a pending order.
*
* @example
* ```typescript
* const order = await convertDraftOrderWorkflow({ id: "order_123" })
* ```
*/
export const convertDraftOrderWorkflow = createWorkflow(
convertDraftOrderWorkflowId,
function (
input: WorkflowData<ConvertDraftOrderWorkflowInput>
): WorkflowResponse<OrderDTO> {
const order = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
variables: {
id: input.id,
},
list: false,
throw_if_key_not_found: true,
}).config({ name: "order-query" })
validateDraftOrderStep({ order })
const updatedOrder = convertDraftOrderStep({ id: input.id })
return new WorkflowResponse(updatedOrder)
}
)
@@ -0,0 +1,16 @@
export * from "./add-draft-order-items"
export * from "./add-draft-order-promotions"
export * from "./add-draft-order-shipping-methods"
export * from "./begin-draft-order-edit"
export * from "./cancel-draft-order-edit"
export * from "./confirm-draft-order-edit"
export * from "./convert-draft-order"
export * from "./remove-draft-order-action-item"
export * from "./remove-draft-order-action-shipping-method"
export * from "./remove-draft-order-promotions"
export * from "./request-draft-order-edit"
export * from "./update-draft-order"
export * from "./update-draft-order-action-item"
export * from "./update-draft-order-action-shipping-method"
export * from "./update-draft-order-item"
export * from "./update-draft-order-shipping-method"
@@ -0,0 +1,74 @@
import { PromotionActions } from "@medusajs/framework/utils"
import {
createWorkflow,
parallelize,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderDTO } from "@medusajs/types"
import {
getActionsToComputeFromPromotionsStep,
getPromotionCodesToApply,
prepareAdjustmentsFromPromotionActionsStep,
} from "../../cart"
import { createDraftOrderLineItemAdjustmentsStep } from "../steps/create-draft-order-line-item-adjustments"
import { createDraftOrderShippingMethodAdjustmentsStep } from "../steps/create-draft-order-shipping-method-adjustments"
import { removeDraftOrderLineItemAdjustmentsStep } from "../steps/remove-draft-order-line-item-adjustments"
import { removeDraftOrderShippingMethodAdjustmentsStep } from "../steps/remove-draft-order-shipping-method-adjustments"
import { updateDraftOrderPromotionsStep } from "../steps/update-draft-order-promotions"
export const refreshDraftOrderAdjustmentsWorkflowId =
"refresh-draft-order-adjustments"
interface RefreshDraftOrderAdjustmentsWorkflowInput {
order: OrderDTO
promo_codes: string[]
action: PromotionActions
}
export const refreshDraftOrderAdjustmentsWorkflow = createWorkflow(
refreshDraftOrderAdjustmentsWorkflowId,
function (input: WorkflowData<RefreshDraftOrderAdjustmentsWorkflowInput>) {
const promotionCodesToApply = getPromotionCodesToApply({
cart: input.order,
promo_codes: input.promo_codes,
action: input.action,
})
const actions = getActionsToComputeFromPromotionsStep({
cart: input.order as any,
promotionCodesToApply,
})
const {
lineItemAdjustmentsToCreate,
lineItemAdjustmentIdsToRemove,
shippingMethodAdjustmentsToCreate,
shippingMethodAdjustmentIdsToRemove,
} = prepareAdjustmentsFromPromotionActionsStep({ actions })
parallelize(
removeDraftOrderLineItemAdjustmentsStep({
lineItemAdjustmentIdsToRemove: lineItemAdjustmentIdsToRemove,
}),
removeDraftOrderShippingMethodAdjustmentsStep({
shippingMethodAdjustmentIdsToRemove:
shippingMethodAdjustmentIdsToRemove,
}),
createDraftOrderLineItemAdjustmentsStep({
lineItemAdjustmentsToCreate: lineItemAdjustmentsToCreate,
order_id: input.order.id,
}),
createDraftOrderShippingMethodAdjustmentsStep({
shippingMethodAdjustmentsToCreate: shippingMethodAdjustmentsToCreate,
}),
updateDraftOrderPromotionsStep({
id: input.order.id,
promo_codes: input.promo_codes,
action: input.action,
})
)
return new WorkflowResponse(void 0)
}
)
@@ -0,0 +1,103 @@
import { OrderChangeStatus, PromotionActions } from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
OrderChangeDTO,
OrderDTO,
OrderPreviewDTO,
OrderWorkflow,
} from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
deleteOrderChangeActionsStep,
previewOrderChangeStep,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validateDraftOrderRemoveActionItemStep } from "../steps/validate-draft-order-remove-action-item"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const removeDraftOrderActionItemWorkflowId =
"remove-draft-order-action-item"
export const removeDraftOrderActionItemWorkflow = createWorkflow(
removeDraftOrderActionItemWorkflowId,
function (
input: WorkflowData<OrderWorkflow.DeleteOrderEditItemActionWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order", "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" })
validateDraftOrderChangeStep({ order, orderChange })
validateDraftOrderRemoveActionItemStep({
input,
orderChange,
})
deleteOrderChangeActionsStep({ ids: [input.action_id] })
const refetchedOrder = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
variables: { id: input.order_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "refetched-order-query" })
const appliedPromoCodes: string[] = transform(
refetchedOrder,
(refetchedOrder) => {
const promotionLink = (refetchedOrder as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
}
)
// If any the order has any promo codes, then we need to refresh the adjustments.
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order: refetchedOrder,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,112 @@
import { OrderChangeStatus, PromotionActions } from "@medusajs/framework/utils"
import {
createWorkflow,
parallelize,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
OrderChangeActionDTO,
OrderChangeDTO,
OrderDTO,
OrderPreviewDTO,
OrderWorkflow,
} from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
deleteOrderChangeActionsStep,
deleteOrderShippingMethods,
previewOrderChangeStep,
} from "../../order"
import { getDraftOrderPromotionContextStep } from "../steps/get-draft-order-promotion-context"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validateDraftOrderShippingMethodActionStep } from "../steps/validate-draft-order-shipping-method-action"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const removeDraftOrderActionShippingMethodWorkflowId =
"remove-draft-order-action-shipping-method"
export const removeDraftOrderActionShippingMethodWorkflow = createWorkflow(
removeDraftOrderActionShippingMethodWorkflowId,
function (
input: WorkflowData<OrderWorkflow.DeleteOrderEditShippingMethodWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
validateDraftOrderShippingMethodActionStep({ 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] })
)
const context = getDraftOrderPromotionContextStep({
order,
})
const appliedPromoCodes = transform(context, (context) => {
const promotionLink = (context as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,104 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO, PromotionDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validatePromoCodesToRemoveStep } from "../steps/validate-promo-codes-to-remove"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const removeDraftOrderPromotionsWorkflowId =
"remove-draft-order-promotions"
interface RemoveDraftOrderPromotionsWorkflowInput {
order_id: string
promo_codes: string[]
}
export const removeDraftOrderPromotionsWorkflow = createWorkflow(
removeDraftOrderPromotionsWorkflowId,
function (input: WorkflowData<RemoveDraftOrderPromotionsWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
const promotions: PromotionDTO[] = useRemoteQueryStep({
entry_point: "promotion",
fields: ["id", "code", "status"],
variables: {
filters: {
code: input.promo_codes,
},
},
list: true,
}).config({ name: "promotions-query" })
validatePromoCodesToRemoveStep({
promo_codes: input.promo_codes,
promotions,
})
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: input.promo_codes,
action: PromotionActions.REMOVE,
},
})
const orderChangeActionInput = transform(
{ order, orderChange, promotions },
({ order, orderChange, promotions }) => {
return promotions.map((promotion) => ({
action: ChangeActionType.PROMOTION_REMOVE,
reference: "order_promotion",
order_change_id: orderChange.id,
reference_id: promotion.id,
order_id: order.id,
details: {
removed_code: promotion.code,
},
}))
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: orderChangeActionInput,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,90 @@
import { OrderChangeStatus } from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrUpdateOrderPaymentCollectionWorkflow,
previewOrderChangeStep,
updateOrderChangesStep,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
export const requestDraftOrderEditId = "request-draft-order-edit"
function getOrderChangesData({
input,
orderChange,
}: {
input: { requested_by?: string }
orderChange: { id: string }
}) {
return transform({ input, orderChange }, ({ input, orderChange }) => {
return [
{
id: orderChange.id,
status: OrderChangeStatus.REQUESTED,
requested_at: new Date(),
requested_by: input.requested_by,
},
]
})
}
/**
* The data to request a draft order edit.
*/
export type RequestDraftOrderEditWorkflowInput = {
/**
* The ID of the draft order to request the edit for.
*/
order_id: string
/**
* The ID of the user requesting the edit.
*/
requested_by?: string
}
export const requestDraftOrderEditWorkflow = createWorkflow(
requestDraftOrderEditId,
function (input: RequestDraftOrderEditWorkflowInput) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "version", "status", "is_draft_order", "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", "canceled_at"],
variables: {
filters: {
order_id: input.order_id,
status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED],
},
},
list: false,
}).config({ name: "order-change-query" })
validateDraftOrderChangeStep({
order,
orderChange,
})
const updateOrderChangesData = getOrderChangesData({ input, orderChange })
updateOrderChangesStep(updateOrderChangesData)
createOrUpdateOrderPaymentCollectionWorkflow.runAsStep({
input: {
order_id: order.id,
},
})
return new WorkflowResponse(previewOrderChangeStep(order.id))
}
)
@@ -0,0 +1,122 @@
import { OrderChangeStatus, PromotionActions } from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
OrderChangeActionDTO,
OrderChangeDTO,
OrderDTO,
OrderWorkflow,
} from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
previewOrderChangeStep,
updateOrderChangeActionsStep,
} from "../../order"
import { getDraftOrderPromotionContextStep } from "../steps/get-draft-order-promotion-context"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validateDraftOrderUpdateActionItemStep } from "../steps/validate-draft-order-update-action-item"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const updateDraftOrderActionItemId = "update-draft-order-action-item"
export const updateDraftOrderActionItemWorkflow = createWorkflow(
updateDraftOrderActionItemId,
function (
input: WorkflowData<OrderWorkflow.UpdateOrderEditAddNewItemWorkflowInput>
) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({
order,
orderChange,
})
validateDraftOrderUpdateActionItemStep({
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,
unit_price: data.unit_price ?? originalAction.details?.unit_price,
compare_at_unit_price:
data.compare_at_unit_price ??
originalAction.details?.compare_at_unit_price,
},
internal_note: data.internal_note,
}
}
)
updateOrderChangeActionsStep([updateData])
const context = getDraftOrderPromotionContextStep({
order,
})
const appliedPromoCodes: string[] = transform(context, (context) => {
const promotionLink = (context as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
// If any the order has any promo codes, then we need to refresh the adjustments.
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order: context,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,151 @@
import { OrderChangeStatus, PromotionActions } from "@medusajs/framework/utils"
import {
createWorkflow,
parallelize,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
OrderChangeActionDTO,
OrderChangeDTO,
OrderDTO,
OrderPreviewDTO,
OrderWorkflow,
} from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
previewOrderChangeStep,
updateOrderChangeActionsStep,
updateOrderShippingMethodsStep,
} from "../../order"
import { prepareShippingMethodUpdate } from "../../order/utils/prepare-shipping-method"
import { getDraftOrderPromotionContextStep } from "../steps/get-draft-order-promotion-context"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { validateDraftOrderShippingMethodActionStep } from "../steps/validate-draft-order-shipping-method-action"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const updateDraftOrderActionShippingMethodWorkflowId =
"update-draft-order-action-shipping-method"
export const updateDraftOrderActionShippingMethodWorkflow = createWorkflow(
updateDraftOrderActionShippingMethodWorkflowId,
function (
input: WorkflowData<OrderWorkflow.UpdateOrderEditShippingMethodWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
const shippingOptions = when({ input }, ({ input }) => {
return input.data?.custom_amount === null
}).then(() => {
const action = transform(
{ orderChange, input, order },
({ orderChange, input, order }) => {
const originalAction = (orderChange.actions ?? []).find(
(a) => a.id === input.action_id
) as OrderChangeActionDTO
return {
shipping_method_id: originalAction.reference_id,
currency_code: order.currency_code,
}
}
)
const shippingMethod = useRemoteQueryStep({
entry_point: "order_shipping_method",
fields: ["id", "shipping_option_id"],
variables: {
id: action.shipping_method_id,
},
list: false,
}).config({ name: "fetch-shipping-method" })
return useRemoteQueryStep({
entry_point: "shipping_option",
fields: [
"id",
"name",
"calculated_price.calculated_amount",
"calculated_price.is_calculated_price_tax_inclusive",
],
variables: {
id: shippingMethod.shipping_option_id,
calculated_price: {
context: { currency_code: action.currency_code },
},
},
}).config({ name: "fetch-shipping-option" })
})
validateDraftOrderShippingMethodActionStep({
orderChange,
input,
})
const updateData = transform(
{ orderChange, input, shippingOptions },
prepareShippingMethodUpdate
)
parallelize(
updateOrderChangeActionsStep([updateData.action]),
updateOrderShippingMethodsStep([updateData.shippingMethod!])
)
const context = getDraftOrderPromotionContextStep({
order,
})
const appliedPromoCodes = transform(context, (context) => {
const promotionLink = (context as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,128 @@
import {
BigNumber,
ChangeActionType,
MathBN,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
OrderChangeDTO,
OrderDTO,
OrderPreviewDTO,
OrderWorkflow,
} from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
} from "../../order"
import { getDraftOrderPromotionContextStep } from "../steps/get-draft-order-promotion-context"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const updateDraftOrderItemWorkflowId = "update-draft-order-item"
export const updateDraftOrderItemWorkflow = createWorkflow(
updateDraftOrderItemWorkflowId,
function (
input: WorkflowData<OrderWorkflow.OrderEditUpdateItemQuantityWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
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" })
validateDraftOrderChangeStep({ order, orderChange })
const orderChangeActionInput = transform(
{ order, orderChange, items: input.items },
({ order, orderChange, items }) => {
return items.map((item) => {
const existing = order?.items?.find(
(exItem) => exItem.id === item.id
)!
const quantityDiff = new BigNumber(
MathBN.sub(item.quantity, existing.quantity)
)
return {
order_change_id: orderChange.id,
order_id: order.id,
version: orderChange.version,
action: ChangeActionType.ITEM_UPDATE,
internal_note: item.internal_note,
details: {
reference_id: item.id,
quantity: item.quantity,
unit_price: item.unit_price,
compare_at_unit_price: item.compare_at_unit_price,
quantity_diff: quantityDiff,
},
}
})
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: orderChangeActionInput,
})
const context = getDraftOrderPromotionContextStep({
order,
})
const appliedPromoCodes: string[] = transform(context, (context) => {
const promotionLink = (context as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
// If any the order has any promo codes, then we need to refresh the adjustments.
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order: context,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,154 @@
import {
ChangeActionType,
OrderChangeStatus,
PromotionActions,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
when,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, OrderChangeDTO, OrderDTO } from "@medusajs/types"
import { useRemoteQueryStep } from "../../common"
import {
createOrderChangeActionsWorkflow,
previewOrderChangeStep,
updateOrderTaxLinesWorkflow,
} from "../../order"
import { updateDraftOrderShippingMethodStep } from "../steps/update-draft-order-shipping-metod"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
export const updateDraftOrderShippingMethodWorkflowId =
"update-draft-order-shipping-method"
export interface UpdateDraftOrderShippingMethodWorkflowInput {
/**
* The ID of the order to update the shipping method in its edit.
*/
order_id: string
data: {
/**
* The ID of the shipping method to update.
*/
shipping_method_id: string
/**
* The ID of the shipping option to associate with the shipping method.
*/
shipping_option_id?: string
/**
* Set a custom amount for the shipping method.
*/
custom_amount?: BigNumberInput
/**
* A note viewed by admins only related to the shipping method.
*/
internal_note?: string | null
}
}
export const updateDraftOrderShippingMethodWorkflow = createWorkflow(
updateDraftOrderShippingMethodWorkflowId,
function (input: WorkflowData<UpdateDraftOrderShippingMethodWorkflowInput>) {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
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" })
validateDraftOrderChangeStep({ order, orderChange })
const { before, after } = updateDraftOrderShippingMethodStep({
order_id: input.order_id,
shipping_method_id: input.data.shipping_method_id,
shipping_option_id: input.data.shipping_option_id,
amount: input.data.custom_amount,
})
updateOrderTaxLinesWorkflow.runAsStep({
input: {
order_id: order.id,
shipping_method_ids: [input.data.shipping_method_id],
},
})
const refetchedOrder = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
variables: { id: input.order_id },
list: false,
throw_if_key_not_found: true,
}).config({ name: "refetched-order-query" })
const appliedPromoCodes = transform(refetchedOrder, (refetchedOrder) => {
const promotionLink = (refetchedOrder as any).promotion_link
if (!promotionLink) {
return []
}
if (Array.isArray(promotionLink)) {
return promotionLink.map((promo) => promo.promotion.code)
}
return [promotionLink.promotion.code]
})
when(
appliedPromoCodes,
(appliedPromoCodes) => appliedPromoCodes.length > 0
).then(() => {
refreshDraftOrderAdjustmentsWorkflow.runAsStep({
input: {
order: refetchedOrder,
promo_codes: appliedPromoCodes,
action: PromotionActions.REPLACE,
},
})
})
const orderChangeActionInput = transform(
{ order, orderChange, data: input.data, before, after },
({ order, orderChange, data, before, after }) => {
return {
order_change_id: orderChange.id,
reference: "order_shipping_method",
reference_id: data.shipping_method_id,
order_id: order.id,
version: orderChange.version,
action: ChangeActionType.SHIPPING_UPDATE,
internal_note: data.internal_note,
details: {
old_shipping_option_id: before.shipping_option_id,
new_shipping_option_id: after.shipping_option_id,
old_amount: before.amount,
new_amount: after.amount,
},
}
}
)
createOrderChangeActionsWorkflow.runAsStep({
input: [orderChangeActionInput as any],
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -0,0 +1,258 @@
import { Modules, OrderWorkflowEvents } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
StepResponse,
transform,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
IOrderModuleService,
OrderDTO,
RegisterOrderChangeDTO,
UpdateOrderDTO,
UpsertOrderAddressDTO,
} from "@medusajs/types"
import { emitEventStep, useRemoteQueryStep } from "../../common"
import { previewOrderChangeStep, registerOrderChangesStep } from "../../order"
import { validateDraftOrderStep } from "../steps/validate-draft-order"
export const updateDraftOrderWorkflowId = "update-draft-order"
export interface UpdateDraftOrderWorkflowInput {
/**
* The ID of the draft order to update.
*/
id: string
/**
* The ID of the user updating the draft order.
*/
user_id: string
/**
* Create or update the shipping address of the draft order.
*/
shipping_address?: UpsertOrderAddressDTO
/**
* Create or update the billing address of the draft order.
*/
billing_address?: UpsertOrderAddressDTO
/**
* The ID of the customer to associate the draft order with.
*/
customer_id?: string
/**
* The new email of the draft order.
*/
email?: string
/**
* The ID of the sales channel to associate the draft order with.
*/
sales_channel_id?: string
/**
* The new metadata of the draft order.
*/
metadata?: Record<string, unknown> | null
}
interface UpdateDraftOrderStepInput {
order: OrderDTO
input: UpdateOrderDTO
}
const updateDraftOrderStep = createStep(
"update-draft-order",
async ({ order, input }: UpdateDraftOrderStepInput, { container }) => {
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
const updatedOrders = await service.updateOrders([
{
id: order.id,
...input,
},
])
const updatedOrder = updatedOrders[0]
return new StepResponse(updatedOrder, order)
},
async function (prevData, { container }) {
if (!prevData) {
return
}
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
await service.updateOrders([prevData as UpdateOrderDTO])
}
)
export const updateDraftOrderWorkflow = createWorkflow(
updateDraftOrderWorkflowId,
function (input: WorkflowData<UpdateDraftOrderWorkflowInput>) {
const order = useRemoteQueryStep({
entry_point: "orders",
fields: [
"id",
"customer_id",
"status",
"is_draft_order",
"sales_channel_id",
"email",
"customer_id",
"shipping_address.*",
"billing_address.*",
"metadata",
],
variables: {
id: input.id,
},
list: false,
throw_if_key_not_found: true,
}).config({ name: "order-query" })
validateDraftOrderStep({ order })
const updateInput = transform(
{ input, order },
({
input,
order,
}: {
input: UpdateDraftOrderWorkflowInput
order: OrderDTO
}) => {
const update: UpdateOrderDTO = {}
if (input.shipping_address) {
const address = {
...order.shipping_address,
...input.shipping_address,
}
delete address.id
update.shipping_address = address
}
if (input.billing_address) {
const address = {
...order.billing_address,
...input.billing_address,
}
delete address.id
update.billing_address = address
}
return { ...input, ...update }
}
)
const updatedOrder = updateDraftOrderStep({
order,
input: updateInput,
})
const orderChangeInput = transform(
{ input, updatedOrder, order },
({ input, updatedOrder, order }) => {
const changes: RegisterOrderChangeDTO[] = []
if (input.shipping_address) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "shipping_address",
old: order.shipping_address,
new: updatedOrder.shipping_address,
},
})
}
if (input.billing_address) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "billing_address",
old: order.billing_address,
new: updatedOrder.billing_address,
},
})
}
if (input.customer_id) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "customer_id",
old: order.customer_id,
new: updatedOrder.customer_id,
},
})
}
if (input.email) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "email",
old: order.email,
new: updatedOrder.email,
},
})
}
if (input.sales_channel_id) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "sales_channel_id",
old: order.sales_channel_id,
new: updatedOrder.sales_channel_id,
},
})
}
if (input.metadata) {
changes.push({
change_type: "update_order" as const,
order_id: input.id,
created_by: input.user_id,
confirmed_by: input.user_id,
details: {
type: "metadata",
old: order.metadata,
new: updatedOrder.metadata,
},
})
}
return changes
}
)
registerOrderChangesStep(orderChangeInput)
emitEventStep({
eventName: OrderWorkflowEvents.UPDATED,
data: { id: input.id },
})
const preview = previewOrderChangeStep(input.id)
return new WorkflowResponse(preview)
}
)
+2 -1
View File
@@ -5,11 +5,13 @@ export * from "./common"
export * from "./customer"
export * from "./customer-group"
export * from "./defaults"
export * from "./draft-order"
export * from "./file"
export * from "./fulfillment"
export * from "./inventory"
export * from "./invite"
export * from "./line-item"
export * from "./notification"
export * from "./order"
export * from "./payment"
export * from "./payment-collection"
@@ -28,4 +30,3 @@ export * from "./stock-location"
export * from "./store"
export * from "./tax"
export * from "./user"
export * from "./notification"
@@ -30,7 +30,7 @@ import { pricingContextResult } from "../../cart/utils/schemas"
function prepareLineItems(data) {
const items = (data.input.items ?? []).map((item) => {
const variant = data.variants.find((v) => v.id === item.variant_id)!
const variant = data.variants?.find((v) => v.id === item.variant_id)!
const input: PrepareLineItemDataInput = {
item,
@@ -41,7 +41,6 @@ const conditionallyDeleteProducts = (input: BatchProductWorkflowInput) =>
deleteProductsWorkflow.runAsStep({ input: { ids: input.delete! } })
)
export const batchProductsWorkflowId = "batch-products"
/**
* This workflow creates, updates, or deletes products. It's used by the
@@ -202,4 +202,475 @@ export class DraftOrder {
}
)
}
/**
* This method converts a draft order to an order. It sends a request to the
* [Convert Draft Order to Order](https://docs.medusajs.com/api/admin#draft-orders_postdraftordersidconvert-to-order) API route.
*
* @param id - The draft order's ID.
* @param query - Configure the fields to retrieve in the order.
* @param headers - Headers to pass in the request.
*
* @example
* To convert a draft order to an order:
*
* ```ts
* sdk.admin.draftOrder.convertToOrder("draft_order_123")
* .then(({ order }) => {
* console.log(order)
* })
*/
async convertToOrder(
id: string,
query?: HttpTypes.AdminDraftOrderParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminOrderResponse>(
`/admin/draft-orders/${id}/convert-to-order`,
{
method: "POST",
query,
headers,
}
)
}
/**
* This method adds items to a draft order. It sends a request to the
* [Add Draft Order Items](https://docs.medusajs.com/api/admin#draft-orders_postordereditsiditems) API route.
*
* @param id - The draft order's ID.
* @param body - The data to add the items to the draft order.
* @param headers - Headers to pass in the request.
*
* @example
* To add items to a draft order:
*
* ```ts
* sdk.admin.draftOrder.addItems("order_123", {
* items: [
* {
* variant_id: "variant_123",
* quantity: 1,
* },
* ],
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async addItems(
id: string,
body: HttpTypes.AdminAddDraftOrderItems,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/items`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method updates an item that is part of an action in a draft order. It sends a request to the
* [Update Draft Order Item](https://docs.medusajs.com/api/admin#draft-orders_postordereditsiditemsaction_id) API route.
*
* @param id - The draft order's ID.
* @param actionId - The action ID.
* @param body - The data to update the item.
* @param headers - Headers to pass in the request.
*
* @example
* To update an item that is part of an action in a draft order:
*
* ```ts
* sdk.admin.draftOrder.updateActionItem("order_123", "action_123", {
* quantity: 2,
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async updateActionItem(
id: string,
actionId: string,
body: HttpTypes.AdminUpdateDraftOrderItem,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/items/${actionId}`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method removes an item that is part of an action in a draft order. It sends a request to the
* [Remove Draft Order Item](https://docs.medusajs.com/api/admin#draft-orders_deleteordereditsiditemsaction_id) API route.
*
* @param id - The draft order's ID.
* @param actionId - The action ID.
* @param headers - Headers to pass in the request.
*
* @example
* To remove an item that is part of an action in a draft order:
*
* ```ts
* sdk.admin.draftOrder.removeActionItem("order_123", "action_123")
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async removeActionItem(
id: string,
actionId: string,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/items/${actionId}`,
{
method: "DELETE",
headers,
}
)
}
/**
* This method updates an item in a draft order. It sends a request to the
* [Update Draft Order Item](https://docs.medusajs.com/api/admin#draft-orders_postordereditsiditemsitem_id) API route.
*
* @param id - The draft order's ID.
* @param itemId - The item ID.
* @param body - The data to update the item.
* @param headers - Headers to pass in the request.
*
* @example
* To update an item in a draft order:
*
* ```ts
* sdk.admin.draftOrder.updateItem("order_123", "item_123", {
* quantity: 2,
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async updateItem(
id: string,
itemId: string,
body: HttpTypes.AdminUpdateDraftOrderItem,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/items/item/${itemId}`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method adds promotions to a draft order. It sends a request to the
* [Add Draft Order Promotions](https://docs.medusajs.com/api/admin#draft-orders_postordereditsidpromotions) API route.
*
* @param id - The draft order's ID.
* @param body - The data to add the promotions to the draft order.
* @param headers - Headers to pass in the request.
*
* @example
* To add promotions to a draft order:
*
* ```ts
* sdk.admin.draftOrder.addPromotions("order_123", {
* promo_codes: ["PROMO_CODE_1", "PROMO_CODE_2"],
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async addPromotions(
id: string,
body: HttpTypes.AdminAddDraftOrderPromotions,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/promotions`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method removes promotions from a draft order. It sends a request to the
* [Remove Draft Order Promotions](https://docs.medusajs.com/api/admin#draft-orders_deleteordereditsidpromotions) API route.
*
* @param id - The draft order's ID.
* @param body - The data to remove the promotions from the draft order.
* @param headers - Headers to pass in the request.
*
* @example
* To remove promotions from a draft order:
*
* ```ts
* sdk.admin.draftOrder.removePromotions("order_123", {
* promo_codes: ["PROMO_CODE_1", "PROMO_CODE_2"],
* })
* ```
*/
async removePromotions(
id: string,
body: HttpTypes.AdminRemoveDraftOrderPromotions,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/promotions`,
{
method: "DELETE",
body,
headers,
}
)
}
/**
* This method adds a shipping method to a draft order. It sends a request to the
* [Add Draft Order Shipping Method](https://docs.medusajs.com/api/admin#draft-orders_postordereditsidshipping-methods) API route.
*
* @param id - The draft order's ID.
* @param body - The data to add the shipping method to the draft order.
* @param headers - Headers to pass in the request.
*
* @example
* To add a shipping method to a draft order:
*
* ```ts
* sdk.admin.draftOrder.addShippingMethod("order_123", {
* shipping_option_id: "shipping_option_123",
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async addShippingMethod(
id: string,
body: HttpTypes.AdminAddDraftOrderShippingMethod,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/shipping-methods`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method updates a shipping method in a draft order. It sends a request to the
* [Update Draft Order Shipping Method](https://docs.medusajs.com/api/admin#draft-orders_postordereditsidshipping-methodsaction_id) API route.
*
* @param id - The draft order's ID.
* @param actionId - The action ID.
* @param body - The data to update the shipping method.
* @param headers - Headers to pass in the request.
*
* @example
* To update a shipping method in a draft order:
*
* ```ts
* sdk.admin.draftOrder.updateShippingMethod("order_123", "action_123", {
* shipping_option_id: "shipping_option_123",
* })
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async updateActionShippingMethod(
id: string,
actionId: string,
body: HttpTypes.AdminUpdateDraftOrderActionShippingMethod,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/shipping-methods/${actionId}`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method removes a shipping method from a draft order. It sends a request to the
* [Remove Draft Order Shipping Method](https://docs.medusajs.com/api/admin#draft-orders_deleteordereditsidshipping-methodsaction_id) API route.
*
* @param id - The draft order's ID.
* @param actionId - The action ID.
* @param headers - Headers to pass in the request.
*
* @example
* To remove a shipping method from a draft order:
*
* ```ts
* sdk.admin.draftOrder.removeShippingMethod("order_123", "action_123")
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async removeActionShippingMethod(
id: string,
actionId: string,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/shipping-methods/${actionId}`,
{
method: "DELETE",
headers,
}
)
}
async updateShippingMethod(
id: string,
methodId: string,
body: HttpTypes.AdminUpdateDraftOrderShippingMethod,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/shipping-methods/method/${methodId}`,
{
method: "POST",
body,
headers,
}
)
}
/**
* This method begins an edit to a draft order. It sends a request to the
* [Begin Draft Order Edit](https://docs.medusajs.com/api/admin#draft-orders_postordereditsid) API route.
*
* @param id - The draft order's ID.
* @param headers - Headers to pass in the request.
*
* @example
* To begin an edit to a draft order:
*
* ```ts
* sdk.admin.draftOrder.beginEdit("order_123")
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async beginEdit(id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit`,
{
method: "POST",
headers,
}
)
}
/**
* This method cancels an edit to a draft order. It sends a request to the
* [Cancel Draft Order Edit](https://docs.medusajs.com/api/admin#draft-orders_deleteordereditsid) API route.
*
* @param id - The draft order's ID.
* @param headers - Headers to pass in the request.
*
* @example
* To cancel an edit to a draft order:
*
* ```ts
* sdk.admin.draftOrder.cancelEdit("order_123")
* .then(({ id, object, deleted }) => {
* console.log(id, object, deleted)
* })
* ```
*/
async cancelEdit(id: string, headers?: ClientHeaders) {
return await this.client.fetch<
HttpTypes.DeleteResponse<"draft-order-edit">
>(`/admin/draft-orders/${id}/edit`, {
method: "DELETE",
headers,
})
}
/**
* This method requests an edit to a draft order. It sends a request to the
* [Request Draft Order Edit](https://docs.medusajs.com/api/admin#draft-orders_postordereditsidrequest) API route.
*
* @param id - The draft order's ID.
* @param headers - Headers to pass in the request.
*
* @example
* To request an edit to a draft order:
*
* ```ts
* sdk.admin.draftOrder.requestEdit("order_123")
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async requestEdit(id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/request`,
{
method: "POST",
headers,
}
)
}
/**
* This method confirms an edit to a draft order. It sends a request to the
* [Confirm Draft Order Edit](https://docs.medusajs.com/api/admin#draft-orders_postordereditsidconfirm) API route.
*
* @param id - The draft order's ID.
* @param headers - Headers to pass in the request.
*
* @example
* To confirm an edit to a draft order:
*
* ```ts
* sdk.admin.draftOrder.confirmEdit("order_123")
* .then(({ draft_order_preview }) => {
* console.log(draft_order_preview)
* })
* ```
*/
async confirmEdit(id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminDraftOrderPreviewResponse>(
`/admin/draft-orders/${id}/edit/confirm`,
{
method: "POST",
headers,
}
)
}
}
@@ -1,3 +1,5 @@
import { AdminOrder } from "../../order"
import { AdminOrder, AdminOrderPreview } from "../../order"
export interface AdminDraftOrder extends AdminOrder {}
export interface AdminDraftOrderPreview extends AdminOrderPreview {}
@@ -51,6 +51,10 @@ export interface AdminCreateDraftOrder {
* Either customer_id or email must be provided.
*/
customer_id?: string | null
/**
* The ID of the sales channel to associate the draft order with.
*/
sales_channel_id: string
/**
* The ID of the region to associate the draft order with.
*/
@@ -96,6 +100,14 @@ export interface AdminUpdateDraftOrder {
* The draft order's email.
*/
email?: string
/**
* The ID of the customer to associate the draft order with.
*/
customer_id?: string
/**
* The ID of the sales channel to associate the draft order with.
*/
sales_channel_id?: string
/**
* The draft order's shipping address.
*/
@@ -109,3 +121,134 @@ export interface AdminUpdateDraftOrder {
*/
metadata?: Record<string, unknown> | null
}
export interface AdminUpdateDraftOrderItem {
/**
* The item's quantity.
*/
quantity: number
/**
* The item's unit price.
*/
unit_price?: number | null
/**
* The item's compare at unit price.
*/
compare_at_unit_price?: number | null
/**
* The item's internal note.
*/
internal_note?: string | null
/**
* The item's metadata.
*/
metadata?: Record<string, unknown> | null
}
export interface AdminAddDraftOrderItem {
/**
* The item's variant ID.
*
* Either variant_id or title must be provided.
*/
variant_id?: string
/**
* The item's title.
*
* Either variant_id or title must be provided.
*/
title?: string
/**
* The item's quantity.
*/
quantity: number
/**
* The item's unit price.
*/
unit_price?: number | null
/**
* The item's compare at unit price.
*/
compare_at_unit_price?: number | null
/**
* The item's internal note.
*/
internal_note?: string | null
/**
* The item's metadata.
*/
metadata?: Record<string, unknown> | null
}
export interface AdminAddDraftOrderItems {
items: AdminAddDraftOrderItem[]
}
export interface AdminAddDraftOrderPromotions {
promo_codes: string[]
}
export interface AdminRemoveDraftOrderPromotions {
promo_codes: string[]
}
export interface AdminAddDraftOrderShippingMethod {
/**
* ID of the shipping option to associate with the shipping method.
*/
shipping_option_id: string
/**
* Custom amount for the shipping method.
*/
custom_amount?: number
/**
* Description of the shipping method.
*/
description?: string
/**
* Internal note for the shipping method.
*/
internal_note?: string
/**
* Metadata for the shipping method.
*/
metadata?: Record<string, unknown>
}
export interface AdminUpdateDraftOrderActionShippingMethod {
/**
* ID of the shipping option to associate with the shipping method.
*/
shipping_option_id: string
/**
* Custom amount for the shipping method.
*/
custom_amount?: number | null
/**
* Description of the shipping method.
*/
description?: string | null
/**
* Internal note for the shipping method.
*/
internal_note?: string | null
/**
* Metadata for the shipping method.
*/
metadata?: Record<string, unknown> | null
}
export interface AdminUpdateDraftOrderShippingMethod {
/**
* ID of the shipping option to associate with the shipping method.
*/
shipping_option_id?: string
/**
* Custom amount for the shipping method.
*/
custom_amount?: number
/**
* Internal note for the shipping method.
*/
internal_note?: string | null
}
@@ -1,5 +1,5 @@
import { PaginatedResponse } from "../../common"
import { AdminDraftOrder } from "./entities"
import { AdminDraftOrder, AdminDraftOrderPreview } from "./entities"
export interface AdminDraftOrderResponse {
draft_order: AdminDraftOrder
@@ -9,3 +9,7 @@ export interface AdminDraftOrderListResponse
extends PaginatedResponse<{
draft_orders: AdminDraftOrder[]
}> {}
export interface AdminDraftOrderPreviewResponse {
draft_order_preview: AdminDraftOrderPreview
}
@@ -4,7 +4,7 @@ import { AdminCustomer } from "../../customer"
import { AdminExchange } from "../../exchange"
import { AdminPaymentCollection } from "../../payment/admin"
import { AdminProduct, AdminProductVariant } from "../../product"
import { AdminRegionCountry } from "../../region"
import { AdminRegion, AdminRegionCountry } from "../../region"
import { AdminReturn } from "../../return"
import { AdminSalesChannel } from "../../sales-channel"
import {
@@ -30,6 +30,10 @@ export interface AdminOrder extends Omit<BaseOrder, "items"> {
* The associated sales channel's details.
*/
sales_channel?: AdminSalesChannel
/**
* The order's region.
*/
region?: AdminRegion
/**
* The details of the customer that placed the order.
*/
@@ -939,6 +939,7 @@ export interface BaseOrderChange {
| "edit"
| "return_request"
| "transfer"
| "update_order"
/**
* The ID of the associated order
@@ -13,7 +13,7 @@ export interface AdminGetPromotionsParams
/**
* Filter by promotion code.
*/
code?: string | string[]
code?: string | string[] | OperatorMap<string>
/**
* Filter by campaign ID to retrieve promotions by campaign.
*/
+8
View File
@@ -21,12 +21,15 @@ export type ChangeActionType =
| "RETURN_ITEM"
| "SHIPPING_ADD"
| "SHIPPING_REMOVE"
| "SHIPPING_UPDATE"
| "SHIP_ITEM"
| "WRITE_OFF_ITEM"
| "REINSTATE_ITEM"
| "TRANSFER_CUSTOMER"
| "UPDATE_ORDER_PROPERTIES"
| "CREDIT_LINE_ADD"
| "PROMOTION_ADD"
| "PROMOTION_REMOVE"
export type OrderChangeStatus =
| "confirmed"
@@ -1119,6 +1122,11 @@ export interface OrderDTO {
*/
summary?: OrderSummaryDTO
/**
* Whether the order is a draft order.
*/
is_draft_order?: boolean
/**
* Holds custom data in key-value pairs.
*/
@@ -6,6 +6,7 @@ import {
OrderItemDTO,
OrderLineItemDTO,
OrderReturnReasonDTO,
OrderStatus,
OrderTransactionDTO,
ReturnDTO,
} from "./common"
@@ -223,6 +224,16 @@ export interface UpdateOrderDTO {
*/
sales_channel_id?: string
/**
* The status of the order.
*/
status?: OrderStatus
/**
* Whether the order is a draft order.
*/
is_draft_order?: boolean
/**
* The items of the order.
*/
@@ -8,7 +8,11 @@ interface NewItem {
/**
* The ID of the variant to add to the order.
*/
variant_id: string
variant_id?: string
/**
* The title of the item to add to the order.
*/
title?: string
/**
* The quantity of the item to add to the order.
*/
@@ -211,8 +215,8 @@ export interface UpdateClaimAddNewItemWorkflowInput {
claim_id: string
/**
* The ID of the action associated with the item to update.
* Every item has an `actions` property, whose value is an array of actions.
* You can find the action with the name `ITEM_ADD` using the item's `action` property,
* Every item has an `actions` property, whose value is an array of actions.
* You can find the action with the name `ITEM_ADD` using the item's `action` property,
* and use the value of the action's `id` property.
*/
action_id: string
@@ -256,7 +260,7 @@ export interface OrderClaimItemWorkflowInput {
/**
* The items to add to the claim.
*/
items: (ExistingItem & {
items: (ExistingItem & {
/**
* The reason for adding the item to the claim.
*/
@@ -274,8 +278,8 @@ export interface UpdateClaimItemWorkflowInput {
claim_id: string
/**
* The ID of the action associated with the item to update.
* Every item has an `actions` property, whose value is an array of actions.
* You can find the action with the name `WRITE_OFF_ITEM` using its `action` property,
* Every item has an `actions` property, whose value is an array of actions.
* You can find the action with the name `WRITE_OFF_ITEM` using its `action` property,
* and use the value of its `id` property.
*/
action_id: string
@@ -11,10 +11,13 @@ export enum ChangeActionType {
CANCEL_RETURN_ITEM = "CANCEL_RETURN_ITEM",
SHIPPING_ADD = "SHIPPING_ADD",
SHIPPING_REMOVE = "SHIPPING_REMOVE",
SHIPPING_UPDATE = "SHIPPING_UPDATE",
SHIP_ITEM = "SHIP_ITEM",
WRITE_OFF_ITEM = "WRITE_OFF_ITEM",
REINSTATE_ITEM = "REINSTATE_ITEM",
TRANSFER_CUSTOMER = "TRANSFER_CUSTOMER",
UPDATE_ORDER_PROPERTIES = "UPDATE_ORDER_PROPERTIES",
CREDIT_LINE_ADD = "CREDIT_LINE_ADD",
PROMOTION_ADD = "PROMOTION_ADD",
PROMOTION_REMOVE = "PROMOTION_REMOVE",
}
@@ -78,7 +78,7 @@ export const adminCustomerRoutesMiddlewares: MiddlewareRoute[] = [
middlewares: [
validateAndTransformQuery(
AdminCustomerAddressParams,
QueryConfig.retrieveTransformQueryConfig
QueryConfig.retrieveAddressTransformQueryConfig
),
],
},
@@ -89,7 +89,7 @@ export const adminCustomerRoutesMiddlewares: MiddlewareRoute[] = [
validateAndTransformBody(AdminUpdateCustomerAddress),
validateAndTransformQuery(
AdminCustomerParams,
QueryConfig.retrieveAddressTransformQueryConfig
QueryConfig.retrieveTransformQueryConfig
),
],
},
@@ -22,7 +22,9 @@ export const AdminCustomerGroupInCustomerParams = z.object({
export const AdminCustomersParamsFields = z.object({
q: z.string().optional(),
id: z.union([z.string(), z.array(z.string())]).optional(),
id: z
.union([z.string(), z.array(z.string()), createOperatorMap()])
.optional(),
email: z.union([z.string(), z.array(z.string())]).optional(),
groups: z
.union([
@@ -0,0 +1,22 @@
import { convertDraftOrderWorkflow } from "@medusajs/core-flows"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
import { HttpTypes } from "@medusajs/types"
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
await convertDraftOrderWorkflow(req.scope).run({
input: {
id: req.params.id,
},
})
const result = await query.graph({
entity: "orders",
filters: { id: req.params.id },
fields: req.queryConfig.fields,
})
res.status(200).json({ order: result.data[0] as HttpTypes.AdminOrder })
}
@@ -0,0 +1,24 @@
import { confirmDraftOrderEditWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/types"
export const POST = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id } = req.params
const { result } = await confirmDraftOrderEditWorkflow(req.scope).run({
input: {
order_id: id,
confirmed_by: req.auth_context.actor_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,44 @@
import {
removeDraftOrderActionItemWorkflow,
updateDraftOrderActionItemWorkflow,
} from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import { AdminUpdateDraftOrderItemType } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateDraftOrderItemType>,
res: MedusaResponse
) => {
const { id, action_id } = req.params
const { result } = await updateDraftOrderActionItemWorkflow(req.scope).run({
input: {
data: req.validatedBody,
order_id: id,
action_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id, action_id } = req.params
const { result } = await removeDraftOrderActionItemWorkflow(req.scope).run({
input: {
order_id: id,
action_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,28 @@
import { updateDraftOrderItemWorkflow } from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import { AdminUpdateDraftOrderItemType } from "../../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateDraftOrderItemType>,
res: MedusaResponse
) => {
const { id, item_id } = req.params
const { result } = await updateDraftOrderItemWorkflow(req.scope).run({
input: {
...req.validatedBody,
order_id: id,
items: [
{
...req.validatedBody,
id: item_id,
},
],
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,22 @@
import { addDraftOrderItemsWorkflow } from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import { AdminAddDraftOrderItemsType } from "../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminAddDraftOrderItemsType>,
res: MedusaResponse
) => {
const { id } = req.params
const { result } = await addDraftOrderItemsWorkflow(req.scope).run({
input: {
...req.validatedBody,
order_id: id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,47 @@
import {
addDraftOrderPromotionWorkflow,
removeDraftOrderPromotionsWorkflow,
} from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import {
AdminAddDraftOrderPromotionsType,
AdminRemoveDraftOrderPromotionsType,
} from "../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminAddDraftOrderPromotionsType>,
res: MedusaResponse<HttpTypes.AdminDraftOrderPreviewResponse>
) => {
const { id } = req.params
const { result } = await addDraftOrderPromotionWorkflow(req.scope).run({
input: {
...req.validatedBody,
order_id: id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest<AdminRemoveDraftOrderPromotionsType>,
res: MedusaResponse<HttpTypes.AdminDraftOrderPreviewResponse>
) => {
const { id } = req.params
const { result } = await removeDraftOrderPromotionsWorkflow(req.scope).run({
input: {
...req.validatedBody,
order_id: id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,24 @@
import { requestDraftOrderEditWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/types"
export const POST = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id } = req.params
const { result } = await requestDraftOrderEditWorkflow(req.scope).run({
input: {
order_id: id,
requested_by: req.auth_context.actor_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -0,0 +1,42 @@
import {
beginDraftOrderEditWorkflow,
cancelDraftOrderEditWorkflow,
} from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
export const POST = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id } = req.params
const { result } = await beginDraftOrderEditWorkflow(req.scope).run({
input: {
order_id: id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminDraftOrderPreview,
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id } = req.params
await cancelDraftOrderEditWorkflow(req.scope).run({
input: {
order_id: id,
},
})
res.status(200).json({
id,
object: "draft-order-edit",
deleted: true,
})
}
@@ -0,0 +1,48 @@
import {
removeDraftOrderActionShippingMethodWorkflow,
updateDraftOrderActionShippingMethodWorkflow,
} from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import { AdminUpdateDraftOrderActionShippingMethodType } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateDraftOrderActionShippingMethodType>,
res: MedusaResponse
) => {
const { id, action_id } = req.params
const { result } = await updateDraftOrderActionShippingMethodWorkflow(
req.scope
).run({
input: {
data: { ...req.validatedBody },
order_id: id,
action_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminDraftOrderPreview,
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { id, action_id } = req.params
const { result } = await removeDraftOrderActionShippingMethodWorkflow(
req.scope
).run({
input: {
order_id: id,
action_id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminDraftOrderPreview,
})
}
@@ -0,0 +1,27 @@
import { updateDraftOrderShippingMethodWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/types"
import { AdminUpdateDraftOrderShippingMethodType } from "../../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateDraftOrderShippingMethodType>,
res: MedusaResponse
) => {
const { id, method_id } = req.params
const { result } = await updateDraftOrderShippingMethodWorkflow(
req.scope
).run({
input: {
data: { shipping_method_id: method_id, ...req.validatedBody },
order_id: id,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminDraftOrderPreview,
})
}
@@ -0,0 +1,22 @@
import { addDraftOrderShippingMethodsWorkflow } from "@medusajs/core-flows"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework"
import { HttpTypes } from "@medusajs/types"
import { AdminAddDraftOrderShippingMethodType } from "../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminAddDraftOrderShippingMethodType>,
res: MedusaResponse
) => {
const { id } = req.params
const { result } = await addDraftOrderShippingMethodsWorkflow(req.scope).run({
input: {
order_id: id,
...req.validatedBody,
},
})
res.json({
draft_order_preview: result as unknown as HttpTypes.AdminOrderPreview,
})
}
@@ -1,6 +1,6 @@
import {
getOrderDetailWorkflow,
updateOrderWorkflow,
updateDraftOrderWorkflow,
} from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
@@ -36,7 +36,7 @@ export const POST = async (
) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
await updateOrderWorkflow(req.scope).run({
await updateDraftOrderWorkflow(req.scope).run({
input: {
...req.validatedBody,
user_id: req.auth_context.actor_id,
@@ -5,10 +5,18 @@ import {
import { MiddlewareRoute } from "@medusajs/framework/http"
import * as QueryConfig from "./query-config"
import {
AdminAddDraftOrderItems,
AdminAddDraftOrderPromotions,
AdminAddDraftOrderShippingMethod,
AdminCreateDraftOrder,
AdminGetDraftOrderParams,
AdminGetDraftOrdersParams,
AdminRemoveDraftOrderPromotions,
AdminUpdateDraftOrder,
AdminUpdateDraftOrderActionItem,
AdminUpdateDraftOrderActionShippingMethod,
AdminUpdateDraftOrderItem,
AdminUpdateDraftOrderShippingMethod,
} from "./validators"
export const adminDraftOrderRoutesMiddlewares: MiddlewareRoute[] = [
@@ -54,4 +62,58 @@ export const adminDraftOrderRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/convert-to-order",
middlewares: [
validateAndTransformQuery(
AdminGetDraftOrderParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/items",
middlewares: [validateAndTransformBody(AdminAddDraftOrderItems)],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/items/item/:item_id",
middlewares: [validateAndTransformBody(AdminUpdateDraftOrderItem)],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/items/:action_id",
middlewares: [validateAndTransformBody(AdminUpdateDraftOrderActionItem)],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/promotions",
middlewares: [validateAndTransformBody(AdminAddDraftOrderPromotions)],
},
{
method: ["DELETE"],
matcher: "/admin/draft-orders/:id/edit/promotions",
middlewares: [validateAndTransformBody(AdminRemoveDraftOrderPromotions)],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/shipping-methods",
middlewares: [validateAndTransformBody(AdminAddDraftOrderShippingMethod)],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/shipping-methods/method/:method_id",
middlewares: [
validateAndTransformBody(AdminUpdateDraftOrderShippingMethod),
],
},
{
method: ["POST"],
matcher: "/admin/draft-orders/:id/edit/shipping-methods/:action_id",
middlewares: [
validateAndTransformBody(AdminUpdateDraftOrderActionShippingMethod),
],
},
]
@@ -23,6 +23,7 @@ const AdminGetDraftOrdersParamsFields = z.object({
q: z.string().optional(),
region_id: z.union([z.string(), z.array(z.string())]).optional(),
sales_channel_id: z.array(z.string()).optional(),
customer_id: z.union([z.string(), z.array(z.string())]).optional(),
})
export type AdminGetDraftOrdersParamsType = z.infer<
@@ -105,7 +106,101 @@ export const AdminCreateDraftOrder = WithAdditionalData(
export type AdminUpdateDraftOrderType = z.infer<typeof AdminUpdateDraftOrder>
export const AdminUpdateDraftOrder = z.object({
email: z.string().optional(),
customer_id: z.string().optional(),
sales_channel_id: z.string().optional(),
shipping_address: AddressPayload.optional(),
billing_address: AddressPayload.optional(),
metadata: z.record(z.unknown()).nullish(),
})
export type AdminAddDraftOrderPromotionsType = z.infer<
typeof AdminAddDraftOrderPromotions
>
export const AdminAddDraftOrderPromotions = z.object({
promo_codes: z.array(z.string()),
})
export type AdminRemoveDraftOrderPromotionsType = z.infer<
typeof AdminRemoveDraftOrderPromotions
>
export const AdminRemoveDraftOrderPromotions = z.object({
promo_codes: z.array(z.string()),
})
export type AdminUpdateDraftOrderItemType = z.infer<
typeof AdminUpdateDraftOrderItem
>
export const AdminUpdateDraftOrderItem = z.object({
quantity: z.number(),
unit_price: z.number().nullish(),
compare_at_unit_price: z.number().nullish(),
internal_note: z.string().optional(),
})
export type AdminUpdateDraftOrderActionItemType = z.infer<
typeof AdminUpdateDraftOrderActionItem
>
export const AdminUpdateDraftOrderActionItem = z.object({
quantity: z.number(),
unit_price: z.number().nullish(),
compare_at_unit_price: z.number().nullish(),
internal_note: z.string().optional(),
})
export const AdminAddDraftOrderItems = z.object({
items: z
.array(
z.object({
variant_id: z.string().optional(),
title: z.string().optional(),
quantity: z.number(),
unit_price: z.number().nullish(),
compare_at_unit_price: z.number().nullish(),
internal_note: z.string().nullish(),
allow_backorder: z.boolean().optional(),
metadata: z.record(z.unknown()).optional(),
})
)
.refine(
(items) => {
return items.every((item) => item.variant_id || item.title)
},
{
message: "Items must have either a variant_id or a title",
}
),
})
export type AdminAddDraftOrderItemsType = z.infer<
typeof AdminAddDraftOrderItems
>
export const AdminAddDraftOrderShippingMethod = z.object({
shipping_option_id: z.string(),
custom_amount: z.number().optional(),
description: z.string().optional(),
internal_note: z.string().optional(),
metadata: z.record(z.unknown()).optional(),
})
export type AdminAddDraftOrderShippingMethodType = z.infer<
typeof AdminAddDraftOrderShippingMethod
>
export const AdminUpdateDraftOrderActionShippingMethod = z.object({
shipping_option_id: z.string(),
custom_amount: z.number().nullish(),
description: z.string().nullish(),
internal_note: z.string().nullish(),
metadata: z.record(z.unknown()).nullish(),
})
export type AdminUpdateDraftOrderActionShippingMethodType = z.infer<
typeof AdminUpdateDraftOrderActionShippingMethod
>
export const AdminUpdateDraftOrderShippingMethod = z.object({
shipping_option_id: z.string().optional(),
custom_amount: z.number().optional(),
internal_note: z.string().nullish(),
})
export type AdminUpdateDraftOrderShippingMethodType = z.infer<
typeof AdminUpdateDraftOrderShippingMethod
>
@@ -1,9 +1,9 @@
import { orderEditUpdateItemQuantityWorkflow } from "@medusajs/core-flows"
import { HttpTypes } from "@medusajs/framework/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { AdminPostOrderEditsUpdateItemQuantityReqSchemaType } from "../../../../validators"
export const POST = async (
@@ -23,7 +23,9 @@ export const AdminGetPromotionParams = createSelectParams()
export const AdminGetPromotionsParamsFields = z.object({
q: z.string().optional(),
code: z.union([z.string(), z.array(z.string())]).optional(),
code: z
.union([z.string(), z.array(z.string()), createOperatorMap()])
.optional(),
campaign_id: z.union([z.string(), z.array(z.string())]).optional(),
application_method: z
.object({
@@ -7,6 +7,8 @@ export * from "./fulfill-item"
export * from "./item-add"
export * from "./item-remove"
export * from "./item-update"
export * from "./promotion-add"
export * from "./promotion-remove"
export * from "./receive-damaged-return-item"
export * from "./receive-return-item"
export * from "./reinstate-item"
@@ -14,5 +16,6 @@ export * from "./return-item"
export * from "./ship-item"
export * from "./shipping-add"
export * from "./shipping-remove"
export * from "./shipping-update"
export * from "./transfer-customer"
export * from "./write-off-item"
@@ -0,0 +1,16 @@
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.PROMOTION_ADD, {
operation({ action, currentOrder, options }) {
// no-op
},
validate({ action }) {
if (!action.reference_id) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
},
})
@@ -0,0 +1,16 @@
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.PROMOTION_REMOVE, {
operation({ action, currentOrder, options }) {
// no-op
},
validate({ action }) {
if (!action.reference_id) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
},
})
@@ -0,0 +1,16 @@
import { ChangeActionType, MedusaError } from "@medusajs/framework/utils"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.SHIPPING_UPDATE, {
operation({ action, currentOrder, options }) {
// no-op
},
validate({ action }) {
if (!action.reference_id) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
},
})