feat(order): cancel fulfillment (#7573)
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { LinkDefinition, RemoteLink } from "@medusajs/modules-sdk"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
type CreateRemoteLinksStepInput = LinkDefinition[]
|
||||
|
||||
export const createLinksStepId = "create-links"
|
||||
export const createLinkStep = createStep(
|
||||
export const createLinksStepId = "create-remote-links"
|
||||
export const createRemoteLinkStep = createStep(
|
||||
createLinksStepId,
|
||||
async (data: CreateRemoteLinksStepInput, { container }) => {
|
||||
const link = container.resolve<RemoteLink>(
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CancelOrderFulfillmentDTO, IOrderModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type CancelOrderFulfillmentStepInput = CancelOrderFulfillmentDTO
|
||||
|
||||
export const cancelOrderFulfillmentStepId = "cancel-order-fullfillment"
|
||||
export const cancelOrderFulfillmentStep = createStep(
|
||||
cancelOrderFulfillmentStepId,
|
||||
async (data: CancelOrderFulfillmentStepInput, { container }) => {
|
||||
const service = container.resolve<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
await service.cancelFulfillment(data)
|
||||
return new StepResponse(void 0, data.order_id)
|
||||
},
|
||||
async (orderId, { container }) => {
|
||||
if (!orderId) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
await service.revertLastVersion(orderId)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,148 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { FulfillmentDTO, OrderDTO, OrderWorkflow } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../common"
|
||||
import { cancelFulfillmentWorkflow } from "../../fulfillment"
|
||||
import { adjustInventoryLevelsStep } from "../../inventory"
|
||||
import { cancelOrderFulfillmentStep } from "../steps/cancel-fulfillment"
|
||||
import {
|
||||
throwIfItemsDoesNotExistsInOrder,
|
||||
throwIfOrderIsCancelled,
|
||||
} from "../utils/order-validation"
|
||||
|
||||
const validateOrder = createStep(
|
||||
"validate-order",
|
||||
({
|
||||
order,
|
||||
input,
|
||||
}: {
|
||||
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
|
||||
input: OrderWorkflow.CancelOrderFulfillmentWorkflowInput
|
||||
}) => {
|
||||
throwIfOrderIsCancelled({ order })
|
||||
|
||||
const fulfillment = order.fulfillments.find(
|
||||
(f) => f.id === input.fulfillment_id
|
||||
)
|
||||
if (!fulfillment) {
|
||||
throw new Error(
|
||||
`Fulfillment with id ${input.fulfillment_id} not found in the order`
|
||||
)
|
||||
}
|
||||
|
||||
if (fulfillment.shipped_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`The fulfillment has already been shipped. Shipped fulfillments cannot be canceled`
|
||||
)
|
||||
}
|
||||
|
||||
throwIfItemsDoesNotExistsInOrder({
|
||||
order,
|
||||
inputItems: fulfillment.items.map((i) => ({
|
||||
id: i.line_item_id as string,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
function prepareCancelOrderFulfillmentData({
|
||||
order,
|
||||
fulfillment,
|
||||
}: {
|
||||
order: OrderDTO
|
||||
fulfillment: FulfillmentDTO
|
||||
}) {
|
||||
return {
|
||||
order_id: order.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
reference_id: fulfillment.id,
|
||||
items: fulfillment.items!.map((i) => {
|
||||
return {
|
||||
id: i.line_item_id as string,
|
||||
quantity: i.quantity,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function prepareInventoryUpdate({
|
||||
fulfillment,
|
||||
}: {
|
||||
order: OrderDTO
|
||||
fulfillment: FulfillmentDTO
|
||||
}) {
|
||||
const inventoryAdjustment: {
|
||||
inventory_item_id: string
|
||||
location_id: string
|
||||
adjustment: number // TODO: BigNumberInput
|
||||
}[] = []
|
||||
|
||||
for (const item of fulfillment.items) {
|
||||
inventoryAdjustment.push({
|
||||
inventory_item_id: item.inventory_item_id as string,
|
||||
location_id: fulfillment.location_id,
|
||||
adjustment: item.quantity,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
inventoryAdjustment,
|
||||
}
|
||||
}
|
||||
|
||||
export const cancelOrderFulfillmentWorkflowId = "cancel-order-fulfillment"
|
||||
export const cancelOrderFulfillmentWorkflow = createWorkflow(
|
||||
cancelOrderFulfillmentWorkflowId,
|
||||
(
|
||||
input: WorkflowData<OrderWorkflow.CancelOrderFulfillmentWorkflowInput>
|
||||
): WorkflowData<void> => {
|
||||
const order: OrderDTO & { fulfillments: FulfillmentDTO[] } =
|
||||
useRemoteQueryStep({
|
||||
entry_point: "orders",
|
||||
fields: [
|
||||
"id",
|
||||
"status",
|
||||
"items.*",
|
||||
"fulfillments.*",
|
||||
"fulfillments.items.*",
|
||||
],
|
||||
variables: { id: input.order_id },
|
||||
list: false,
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
validateOrder({ order, input })
|
||||
|
||||
const fulfillment = transform({ input, order }, ({ input, order }) => {
|
||||
return order.fulfillments.find((f) => f.id === input.fulfillment_id)!
|
||||
})
|
||||
|
||||
cancelFulfillmentWorkflow.runAsStep({
|
||||
input: {
|
||||
id: input.fulfillment_id,
|
||||
},
|
||||
})
|
||||
|
||||
const cancelOrderFulfillmentData = transform(
|
||||
{ order, fulfillment },
|
||||
prepareCancelOrderFulfillmentData
|
||||
)
|
||||
|
||||
cancelOrderFulfillmentStep(cancelOrderFulfillmentData)
|
||||
|
||||
const { inventoryAdjustment } = transform(
|
||||
{ order, fulfillment },
|
||||
prepareInventoryUpdate
|
||||
)
|
||||
|
||||
adjustInventoryLevelsStep(inventoryAdjustment)
|
||||
}
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
FulfillmentWorkflow,
|
||||
OrderDTO,
|
||||
OrderWorkflow,
|
||||
ReservationItemDTO,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { createLinkStep, useRemoteQueryStep } from "../../common"
|
||||
import { createRemoteLinkStep, useRemoteQueryStep } from "../../common"
|
||||
import { createFulfillmentWorkflow } from "../../fulfillment"
|
||||
import { adjustInventoryLevelsStep } from "../../inventory"
|
||||
import {
|
||||
@@ -70,6 +71,7 @@ function prepareFulfillmentData({
|
||||
order,
|
||||
input,
|
||||
shippingOption,
|
||||
reservations,
|
||||
}: {
|
||||
order: OrderDTO
|
||||
input: OrderWorkflow.CreateOrderFulfillmentWorkflowInput
|
||||
@@ -78,15 +80,21 @@ function prepareFulfillmentData({
|
||||
provider_id: string
|
||||
service_zone: { fulfillment_set: { location?: { id: string } } }
|
||||
}
|
||||
reservations: ReservationItemDTO[]
|
||||
}) {
|
||||
const inputItems = input.items
|
||||
const orderItemsMap = new Map<string, Required<OrderDTO>["items"][0]>(
|
||||
order.items!.map((i) => [i.id, i])
|
||||
)
|
||||
const reservationItemMap = new Map<string, ReservationItemDTO>(
|
||||
reservations.map((r) => [r.line_item_id as string, r])
|
||||
)
|
||||
const fulfillmentItems = inputItems.map((i) => {
|
||||
const orderItem = orderItemsMap.get(i.id)!
|
||||
const reservation = reservationItemMap.get(i.id)!
|
||||
return {
|
||||
line_item_id: i.id,
|
||||
inventory_item_id: reservation.inventory_item_id,
|
||||
quantity: i.quantity,
|
||||
title: orderItem.variant_title ?? orderItem.title,
|
||||
sku: orderItem.variant_sku || "",
|
||||
@@ -118,7 +126,7 @@ function prepareFulfillmentData({
|
||||
}
|
||||
}
|
||||
|
||||
function prepareInventoryReservations({ reservations, order, input }) {
|
||||
function prepareInventoryUpdate({ reservations, order, input }) {
|
||||
if (!reservations || !reservations.length) {
|
||||
throw new Error(
|
||||
`No stock reservation found for items ${input.items.map((i) => i.id)}`
|
||||
@@ -215,8 +223,27 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
||||
throw_if_key_not_found: true,
|
||||
}).config({ name: "get-shipping-option" })
|
||||
|
||||
const lineItemIds = transform({ order }, ({ order }) => {
|
||||
return order.items?.map((i) => i.id)
|
||||
})
|
||||
const reservations = useRemoteQueryStep({
|
||||
entry_point: "reservations",
|
||||
fields: [
|
||||
"id",
|
||||
"line_item_id",
|
||||
"quantity",
|
||||
"inventory_item_id",
|
||||
"location_id",
|
||||
],
|
||||
variables: {
|
||||
filter: {
|
||||
line_item_id: lineItemIds,
|
||||
},
|
||||
},
|
||||
}).config({ name: "get-reservations" })
|
||||
|
||||
const fulfillmentData = transform(
|
||||
{ order, input, shippingOption },
|
||||
{ order, input, shippingOption, reservations },
|
||||
prepareFulfillmentData
|
||||
)
|
||||
|
||||
@@ -240,30 +267,11 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
||||
]
|
||||
}
|
||||
)
|
||||
createLinkStep(link)
|
||||
|
||||
const lineItemIds = transform({ order }, ({ order }) => {
|
||||
return order.items?.map((i) => i.id)
|
||||
})
|
||||
const reservations = useRemoteQueryStep({
|
||||
entry_point: "reservations",
|
||||
fields: [
|
||||
"id",
|
||||
"line_item_id",
|
||||
"quantity",
|
||||
"inventory_item_id",
|
||||
"location_id",
|
||||
],
|
||||
variables: {
|
||||
filter: {
|
||||
line_item_id: lineItemIds,
|
||||
},
|
||||
},
|
||||
}).config({ name: "get-reservations" })
|
||||
createRemoteLinkStep(link)
|
||||
|
||||
const { toDelete, toUpdate, inventoryAdjustment } = transform(
|
||||
{ order, reservations, input },
|
||||
prepareInventoryReservations
|
||||
prepareInventoryUpdate
|
||||
)
|
||||
|
||||
parallelize(
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { createLinkStep, useRemoteQueryStep } from "../../common"
|
||||
import { createRemoteLinkStep, useRemoteQueryStep } from "../../common"
|
||||
import { createReturnFulfillmentWorkflow } from "../../fulfillment"
|
||||
import { updateOrderTaxLinesStep } from "../steps"
|
||||
import { createReturnStep } from "../steps/create-return"
|
||||
@@ -326,6 +326,6 @@ export const createReturnOrderWorkflow = createWorkflow(
|
||||
]
|
||||
}
|
||||
)
|
||||
createLinkStep(link)
|
||||
createRemoteLinkStep(link)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./archive-orders"
|
||||
export * from "./cancel-order-fulfillment"
|
||||
export * from "./complete-orders"
|
||||
export * from "./create-fulfillment"
|
||||
export * from "./create-orders"
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { updateProductsStep } from "../steps/update-products"
|
||||
|
||||
import {
|
||||
dismissRemoteLinkStep,
|
||||
createLinkStep,
|
||||
useRemoteQueryStep,
|
||||
} from "../../common"
|
||||
import { arrayDifference } from "@medusajs/utils"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { arrayDifference } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createRemoteLinkStep,
|
||||
dismissRemoteLinkStep,
|
||||
useRemoteQueryStep,
|
||||
} from "../../common"
|
||||
|
||||
type UpdateProductsStepInputSelector = {
|
||||
selector: ProductTypes.FilterableProductProps
|
||||
@@ -161,7 +161,7 @@ export const updateProductsWorkflow = createWorkflow(
|
||||
prepareSalesChannelLinks
|
||||
)
|
||||
|
||||
createLinkStep(salesChannelLinks)
|
||||
createRemoteLinkStep(salesChannelLinks)
|
||||
|
||||
return updatedProducts
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user