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:
Frane Polić
2024-12-03 18:15:26 +01:00
committed by GitHub
parent 56ae076fd2
commit ab948b7c65
21 changed files with 809 additions and 22 deletions

View File

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

View File

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

View 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[])
}
)

View File

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

View 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))
}
)