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
@@ -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
>