feat(core-flows,medusa,order,types): update orders (#10373)
**What** - add order update endpoint - add workflows and steps for updating orders - add `registerChanges` method to Order module + workflow step --- CLOSES CMRC-633
This commit is contained in:
@@ -34,4 +34,5 @@ export * from "./update-order-change-actions"
|
||||
export * from "./update-order-changes"
|
||||
export * from "./update-order-exchanges"
|
||||
export * from "./update-shipping-methods"
|
||||
|
||||
export * from "./update-orders"
|
||||
export * from "./register-order-changes"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
IOrderModuleService,
|
||||
RegisterOrderChangeDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
export const registerOrderChangeStepId = "register-order-change"
|
||||
|
||||
/**
|
||||
* This step registers an order changes.
|
||||
*/
|
||||
export const registerOrderChangesStep = createStep(
|
||||
registerOrderChangeStepId,
|
||||
async (data: RegisterOrderChangeDTO[], { container }) => {
|
||||
const service = container.resolve<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
const orderChanges = await service.registerOrderChange(data)
|
||||
|
||||
return new StepResponse(
|
||||
void 0,
|
||||
orderChanges.map((c) => c.id)
|
||||
)
|
||||
},
|
||||
async (orderChangeIds, { container }) => {
|
||||
if (!orderChangeIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
await service.deleteOrderChanges(orderChangeIds)
|
||||
}
|
||||
)
|
||||
48
packages/core/core-flows/src/order/steps/update-orders.ts
Normal file
48
packages/core/core-flows/src/order/steps/update-orders.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
FilterableOrderProps,
|
||||
IOrderModuleService,
|
||||
UpdateOrderDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import {
|
||||
Modules,
|
||||
getSelectsAndRelationsFromObjectArray,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
export type UpdateOrdersStepInput = {
|
||||
selector: FilterableOrderProps
|
||||
update: UpdateOrderDTO // TODO: Update to UpdateOrderDTO[]
|
||||
}
|
||||
|
||||
export const updateOrdersStepId = "update-orders"
|
||||
/**
|
||||
* This step updates orders matching the specified filters.
|
||||
*/
|
||||
export const updateOrdersStep = createStep(
|
||||
updateOrdersStepId,
|
||||
async (data: UpdateOrdersStepInput, { container }) => {
|
||||
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
|
||||
const prevData = await service.listOrders(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const orders = await service.updateOrders(data.selector, data.update)
|
||||
|
||||
return new StepResponse(orders, prevData)
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IOrderModuleService>(Modules.ORDER)
|
||||
|
||||
await service.updateOrders(prevData as UpdateOrderDTO[])
|
||||
}
|
||||
)
|
||||
@@ -82,3 +82,4 @@ export * from "./transfer/request-order-transfer"
|
||||
export * from "./transfer/accept-order-transfer"
|
||||
export * from "./transfer/cancel-order-transfer"
|
||||
export * from "./transfer/decline-order-transfer"
|
||||
export * from "./update-order"
|
||||
|
||||
173
packages/core/core-flows/src/order/workflows/update-order.ts
Normal file
173
packages/core/core-flows/src/order/workflows/update-order.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { OrderDTO, OrderWorkflow } from "@medusajs/framework/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
OrderPreviewDTO,
|
||||
RegisterOrderChangeDTO,
|
||||
UpdateOrderDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
MedusaError,
|
||||
OrderWorkflowEvents,
|
||||
validateEmail,
|
||||
} from "@medusajs/framework/utils"
|
||||
|
||||
import { throwIfOrderIsCancelled } from "../utils/order-validation"
|
||||
import {
|
||||
previewOrderChangeStep,
|
||||
registerOrderChangesStep,
|
||||
updateOrdersStep,
|
||||
} from "../steps"
|
||||
import { emitEventStep, useQueryGraphStep } from "../../common"
|
||||
|
||||
/**
|
||||
* This step validates that an order can be updated with provided input.
|
||||
*/
|
||||
export const updateOrderValidationStep = createStep(
|
||||
"update-order-validation",
|
||||
async function ({
|
||||
order,
|
||||
input,
|
||||
}: {
|
||||
order: OrderDTO
|
||||
input: OrderWorkflow.UpdateOrderWorkflowInput
|
||||
}) {
|
||||
throwIfOrderIsCancelled({ order })
|
||||
|
||||
if (
|
||||
input.shipping_address?.country_code &&
|
||||
order.shipping_address?.country_code !==
|
||||
input.shipping_address?.country_code
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Country code cannot be changed"
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
input.billing_address?.country_code &&
|
||||
order.billing_address?.country_code !==
|
||||
input.billing_address?.country_code
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Country code cannot be changed"
|
||||
)
|
||||
}
|
||||
|
||||
if (input.email) {
|
||||
validateEmail(input.email)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const updateOrderWorkflowId = "update-order-workflow"
|
||||
/**
|
||||
* Update order workflow.
|
||||
*/
|
||||
export const updateOrderWorkflow = createWorkflow(
|
||||
updateOrderWorkflowId,
|
||||
function (
|
||||
input: WorkflowData<OrderWorkflow.UpdateOrderWorkflowInput>
|
||||
): WorkflowResponse<OrderPreviewDTO> {
|
||||
const orderQuery = useQueryGraphStep({
|
||||
entity: "order",
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"email",
|
||||
"shipping_address.*",
|
||||
"billing_address.*",
|
||||
],
|
||||
filters: { id: input.id },
|
||||
options: { throwIfKeyNotFound: true },
|
||||
}).config({ name: "order-query" })
|
||||
|
||||
const order = transform(
|
||||
{ orderQuery },
|
||||
({ orderQuery }) => orderQuery.data[0]
|
||||
)
|
||||
|
||||
updateOrderValidationStep({ order, input })
|
||||
|
||||
const updateInput = transform({ input, order }, ({ input, order }) => {
|
||||
const update: UpdateOrderDTO = {}
|
||||
|
||||
if (input.shipping_address) {
|
||||
const address = {
|
||||
// we want to create a new 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 }
|
||||
})
|
||||
|
||||
updateOrdersStep({
|
||||
selector: { id: input.id },
|
||||
update: updateInput,
|
||||
})
|
||||
|
||||
const orderChangeInput = transform({ input, order }, ({ input, order }) => {
|
||||
const changes: RegisterOrderChangeDTO[] = []
|
||||
if (input.shipping_address) {
|
||||
changes.push({
|
||||
change_type: "update_order" as const,
|
||||
order_id: input.id,
|
||||
reference: "shipping_address",
|
||||
reference_id: order.shipping_address?.id, // save previous address id as reference
|
||||
details: input.shipping_address as Record<string, unknown>, // save what changed on the address
|
||||
})
|
||||
}
|
||||
|
||||
if (input.billing_address) {
|
||||
changes.push({
|
||||
change_type: "update_order" as const,
|
||||
order_id: input.id,
|
||||
reference: "billing_address",
|
||||
reference_id: order.billing_address?.id,
|
||||
details: input.billing_address as Record<string, unknown>,
|
||||
})
|
||||
}
|
||||
|
||||
if (input.email) {
|
||||
changes.push({
|
||||
change_type: "update_order" as const,
|
||||
order_id: input.id,
|
||||
reference: "email",
|
||||
reference_id: order.email,
|
||||
details: { email: input.email },
|
||||
})
|
||||
}
|
||||
|
||||
return changes
|
||||
})
|
||||
|
||||
registerOrderChangesStep(orderChangeInput)
|
||||
|
||||
emitEventStep({
|
||||
eventName: OrderWorkflowEvents.UPDATED,
|
||||
data: { id: input.id },
|
||||
})
|
||||
|
||||
return new WorkflowResponse(previewOrderChangeStep(input.id))
|
||||
}
|
||||
)
|
||||
@@ -25,6 +25,7 @@ export type ChangeActionType =
|
||||
| "WRITE_OFF_ITEM"
|
||||
| "REINSTATE_ITEM"
|
||||
| "TRANSFER_CUSTOMER"
|
||||
| "UPDATE_ORDER_PROPERTIES"
|
||||
|
||||
export type OrderChangeStatus =
|
||||
| "confirmed"
|
||||
|
||||
@@ -10,6 +10,15 @@ import {
|
||||
ReturnDTO,
|
||||
} from "./common"
|
||||
|
||||
type OrderChangeType =
|
||||
| "return_request"
|
||||
| "return_receive"
|
||||
| "exchange"
|
||||
| "claim"
|
||||
| "edit"
|
||||
| "transfer"
|
||||
| "update_order"
|
||||
|
||||
/** ADDRESS START */
|
||||
/**
|
||||
* The data to create or update in the address.
|
||||
@@ -860,13 +869,7 @@ export interface CreateOrderChangeDTO {
|
||||
/**
|
||||
* The type of the order change.
|
||||
*/
|
||||
change_type?:
|
||||
| "return_request"
|
||||
| "return_receive"
|
||||
| "exchange"
|
||||
| "claim"
|
||||
| "edit"
|
||||
| "transfer"
|
||||
change_type?: OrderChangeType
|
||||
|
||||
/**
|
||||
* The description of the order change.
|
||||
@@ -1049,6 +1052,57 @@ export interface ConfirmOrderChangeDTO {
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The details of the order change registration.
|
||||
*/
|
||||
export interface RegisterOrderChangeDTO {
|
||||
/**
|
||||
* The associated order's ID.
|
||||
*/
|
||||
order_id: string
|
||||
|
||||
/**
|
||||
* The type of the order change.
|
||||
*/
|
||||
change_type?: OrderChangeType
|
||||
|
||||
/**
|
||||
* The description of the order change.
|
||||
*/
|
||||
description?: string
|
||||
|
||||
/**
|
||||
* The internal note of the order change.
|
||||
*/
|
||||
internal_note?: string | null
|
||||
|
||||
/**
|
||||
* The user or customer that requested the order change.
|
||||
*/
|
||||
requested_by?: string
|
||||
|
||||
/**
|
||||
* Holds custom data in key-value pairs.
|
||||
*/
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
/**
|
||||
* The details of the order change action
|
||||
*/
|
||||
details?: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* The name of the data model that this change
|
||||
* references. For example, `shipping_address`.
|
||||
*/
|
||||
reference?: string
|
||||
|
||||
/**
|
||||
* The ID of the data model's record referenced.
|
||||
*/
|
||||
reference_id?: string
|
||||
}
|
||||
|
||||
/** ORDER CHANGE END */
|
||||
/** ORDER CHANGE ACTION START */
|
||||
/**
|
||||
@@ -1124,6 +1178,11 @@ export interface CreateOrderChangeActionDTO {
|
||||
* quantity, based on the type of this action.
|
||||
*/
|
||||
details?: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* Whether the action has been applied.
|
||||
*/
|
||||
applied?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
CreateOrderTransactionDTO,
|
||||
DeclineOrderChangeDTO,
|
||||
ReceiveOrderReturnDTO,
|
||||
RegisterOrderChangeDTO,
|
||||
RegisterOrderDeliveryDTO,
|
||||
RegisterOrderFulfillmentDTO,
|
||||
RegisterOrderShipmentDTO,
|
||||
@@ -2710,6 +2711,44 @@ export interface IOrderModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
/**
|
||||
* This method registers an order change.
|
||||
*
|
||||
* @param {RegisterOrderChangeDTO} data - The register order change details.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<OrderChangeReturn>} The item and shipping method changes made on the order.
|
||||
*
|
||||
* @example
|
||||
* await orderModuleService.registerOrderChange({
|
||||
* order_id: "123",
|
||||
* details: Record<string, unknown>
|
||||
* })
|
||||
*
|
||||
*/
|
||||
registerOrderChange(
|
||||
data: RegisterOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChangeDTO>
|
||||
|
||||
/**
|
||||
* This method registers order changes.
|
||||
*
|
||||
* @param {RegisterOrderChangeDTO[]} data - The register order changes details.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<OrderChangeReturn[]>} The item and shipping method changes made on the orders.
|
||||
*
|
||||
* @example
|
||||
* await orderModuleService.registerOrderChange({
|
||||
* order_id: "123",
|
||||
* details: Record<string, unknown>
|
||||
* })
|
||||
*
|
||||
*/
|
||||
registerOrderChange(
|
||||
data: RegisterOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChangeDTO[]>
|
||||
|
||||
/**
|
||||
* This method soft deletes order changes by their IDs.
|
||||
*
|
||||
|
||||
@@ -19,3 +19,4 @@ export * from "./request-transfer"
|
||||
export * from "./accept-transfer"
|
||||
export * from "./cancel-transfer"
|
||||
export * from "./decline-transfer"
|
||||
export * from "./update-order"
|
||||
|
||||
15
packages/core/types/src/workflow/order/update-order.ts
Normal file
15
packages/core/types/src/workflow/order/update-order.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { UpsertOrderAddressDTO } from "../../order"
|
||||
|
||||
export type UpdateOrderWorkflowInput = {
|
||||
id: string
|
||||
shipping_address?: UpsertOrderAddressDTO
|
||||
billing_address?: UpsertOrderAddressDTO
|
||||
email?: string
|
||||
}
|
||||
|
||||
export type UpdateOrderShippingAddressWorkflowInput = {
|
||||
order_id: string
|
||||
shipping_address: UpsertOrderAddressDTO
|
||||
description?: string
|
||||
internal_note?: string
|
||||
}
|
||||
@@ -12,6 +12,8 @@ export const CustomerWorkflowEvents = {
|
||||
}
|
||||
|
||||
export const OrderWorkflowEvents = {
|
||||
UPDATED: "order.updated",
|
||||
|
||||
PLACED: "order.placed",
|
||||
CANCELED: "order.canceled",
|
||||
COMPLETED: "order.completed",
|
||||
|
||||
@@ -15,4 +15,5 @@ export enum ChangeActionType {
|
||||
WRITE_OFF_ITEM = "WRITE_OFF_ITEM",
|
||||
REINSTATE_ITEM = "REINSTATE_ITEM",
|
||||
TRANSFER_CUSTOMER = "TRANSFER_CUSTOMER",
|
||||
UPDATE_ORDER_PROPERTIES = "UPDATE_ORDER_PROPERTIES",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user