feat(medusa, types, utils, core-flows, order) request & accept order transfer (#10106)
**What** - add request order transfer workflow - add admin endpoint for transferring an order to a customer - accept order transfer storefront endpoint - accept transfer workflow - changes in the order module to introduce new change and action types --- **Note** - we return 400 instead 409 currently if there is already an active order edit, I will revisit this in a followup - endpoint for requesting order transfer from the storefront will be added in a separate PR --- RESOLVES CMRC-701 RESOLVES CMRC-703 RESOLVES CMRC-704 RESOLVES CMRC-705
This commit is contained in:
@@ -78,3 +78,5 @@ export * from "./return/update-return-shipping-method"
|
||||
export * from "./update-order-change-actions"
|
||||
export * from "./update-order-changes"
|
||||
export * from "./update-tax-lines"
|
||||
export * from "./transfer/request-order-transfer"
|
||||
export * from "./transfer/accept-order-transfer"
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
OrderWorkflow,
|
||||
} from "@medusajs/framework/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { OrderPreviewDTO } from "@medusajs/types"
|
||||
|
||||
import { useRemoteQueryStep } from "../../../common"
|
||||
import { throwIfOrderIsCancelled } from "../../utils/order-validation"
|
||||
import { previewOrderChangeStep } from "../../steps"
|
||||
import {
|
||||
ChangeActionType,
|
||||
MedusaError,
|
||||
OrderChangeStatus,
|
||||
} from "@medusajs/utils"
|
||||
import { confirmOrderChanges } from "../../steps/confirm-order-changes"
|
||||
|
||||
/**
|
||||
* This step validates that an order transfer can be accepted.
|
||||
*/
|
||||
export const acceptOrderTransferValidationStep = createStep(
|
||||
"accept-order-transfer-validation",
|
||||
async function ({
|
||||
token,
|
||||
order,
|
||||
orderChange,
|
||||
}: {
|
||||
token: string
|
||||
order: OrderDTO
|
||||
orderChange: OrderChangeDTO
|
||||
}) {
|
||||
throwIfOrderIsCancelled({ order })
|
||||
|
||||
if (!orderChange || orderChange.change_type !== "transfer") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Order ${order.id} does not have an order transfer request.`
|
||||
)
|
||||
}
|
||||
const transferCustomerAction = orderChange.actions.find(
|
||||
(a) => a.action === ChangeActionType.TRANSFER_CUSTOMER
|
||||
)
|
||||
|
||||
if (!token.length || token !== transferCustomerAction?.details!.token) {
|
||||
throw new MedusaError(MedusaError.Types.NOT_ALLOWED, "Invalid token.")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const acceptOrderTransferWorkflowId = "accept-order-transfer-workflow"
|
||||
/**
|
||||
* This workflow accepts an order transfer.
|
||||
*/
|
||||
export const acceptOrderTransferWorkflow = createWorkflow(
|
||||
acceptOrderTransferWorkflowId,
|
||||
function (
|
||||
input: WorkflowData<OrderWorkflow.AcceptOrderTransferWorkflowInput>
|
||||
): WorkflowResponse<OrderPreviewDTO> {
|
||||
const order: OrderDTO = useRemoteQueryStep({
|
||||
entry_point: "orders",
|
||||
fields: ["id", "email", "status", "customer_id"],
|
||||
variables: { id: input.order_id },
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
const orderChange: OrderChangeDTO = useRemoteQueryStep({
|
||||
entry_point: "order_change",
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"change_type",
|
||||
"actions.id",
|
||||
"actions.order_id",
|
||||
"actions.action",
|
||||
"actions.details",
|
||||
"actions.reference",
|
||||
"actions.reference_id",
|
||||
"actions.internal_note",
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
order_id: input.order_id,
|
||||
status: [OrderChangeStatus.REQUESTED],
|
||||
},
|
||||
},
|
||||
list: false,
|
||||
}).config({ name: "order-change-query" })
|
||||
|
||||
acceptOrderTransferValidationStep({
|
||||
order,
|
||||
orderChange,
|
||||
token: input.token,
|
||||
})
|
||||
|
||||
confirmOrderChanges({
|
||||
changes: [orderChange],
|
||||
orderId: order.id,
|
||||
})
|
||||
|
||||
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,136 @@
|
||||
import { OrderDTO, OrderWorkflow } from "@medusajs/framework/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { CustomerDTO, OrderPreviewDTO } from "@medusajs/types"
|
||||
import { v4 as uid } from "uuid"
|
||||
|
||||
import { emitEventStep, useRemoteQueryStep } from "../../../common"
|
||||
import { createOrderChangeStep } from "../../steps/create-order-change"
|
||||
import { throwIfOrderIsCancelled } from "../../utils/order-validation"
|
||||
import { createOrderChangeActionsWorkflow } from "../create-order-change-actions"
|
||||
import {
|
||||
ChangeActionType,
|
||||
MedusaError,
|
||||
OrderChangeStatus,
|
||||
OrderWorkflowEvents,
|
||||
} from "@medusajs/utils"
|
||||
import { previewOrderChangeStep, updateOrderChangesStep } from "../../steps"
|
||||
|
||||
/**
|
||||
* This step validates that an order transfer can be requested.
|
||||
*/
|
||||
export const requestOrderTransferValidationStep = createStep(
|
||||
"request-order-transfer-validation",
|
||||
async function ({
|
||||
order,
|
||||
customer,
|
||||
}: {
|
||||
order: OrderDTO
|
||||
customer: CustomerDTO
|
||||
}) {
|
||||
throwIfOrderIsCancelled({ order })
|
||||
|
||||
if (!customer.has_account) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot transfer order: ${order.id} to a guest customer account: ${customer.email}`
|
||||
)
|
||||
}
|
||||
|
||||
if (order.customer_id === customer.id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Order: ${order.id} already belongs to customer: ${customer.id}`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const requestOrderTransferWorkflowId = "request-order-transfer-workflow"
|
||||
/**
|
||||
* This workflow requests an order transfer.
|
||||
*/
|
||||
export const requestOrderTransferWorkflow = createWorkflow(
|
||||
requestOrderTransferWorkflowId,
|
||||
function (
|
||||
input: WorkflowData<OrderWorkflow.RequestOrderTransferWorkflowInput>
|
||||
): WorkflowResponse<OrderPreviewDTO> {
|
||||
const order: OrderDTO = useRemoteQueryStep({
|
||||
entry_point: "orders",
|
||||
fields: ["id", "email", "status", "customer_id"],
|
||||
variables: { id: input.order_id },
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
const customer: CustomerDTO = useRemoteQueryStep({
|
||||
entry_point: "customers",
|
||||
fields: ["id", "email", "has_account"],
|
||||
variables: { id: input.customer_id },
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
}).config({ name: "customer-query" })
|
||||
|
||||
requestOrderTransferValidationStep({ order, customer })
|
||||
|
||||
const orderChangeInput = transform({ input }, ({ input }) => {
|
||||
return {
|
||||
change_type: "transfer" as const,
|
||||
order_id: input.order_id,
|
||||
created_by: input.logged_in_user,
|
||||
description: input.description,
|
||||
internal_note: input.internal_note,
|
||||
}
|
||||
})
|
||||
|
||||
const change = createOrderChangeStep(orderChangeInput)
|
||||
|
||||
const actionInput = transform(
|
||||
{ order, input, change },
|
||||
({ order, input, change }) => [
|
||||
{
|
||||
order_change_id: change.id,
|
||||
order_id: input.order_id,
|
||||
action: ChangeActionType.TRANSFER_CUSTOMER,
|
||||
version: change.version,
|
||||
reference: "customer",
|
||||
reference_id: input.customer_id,
|
||||
details: {
|
||||
token: uid(),
|
||||
original_email: order.email,
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
createOrderChangeActionsWorkflow.runAsStep({
|
||||
input: actionInput,
|
||||
})
|
||||
|
||||
const updateOrderChangeInput = transform(
|
||||
{ input, change },
|
||||
({ input, change }) => [
|
||||
{
|
||||
id: change.id,
|
||||
status: OrderChangeStatus.REQUESTED,
|
||||
requested_by: input.logged_in_user,
|
||||
requested_at: new Date(),
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
updateOrderChangesStep(updateOrderChangeInput)
|
||||
|
||||
emitEventStep({
|
||||
eventName: OrderWorkflowEvents.TRANSFER_REQUESTED,
|
||||
data: { id: input.order_id },
|
||||
})
|
||||
|
||||
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user