feat(order): cancel fulfillment (#7573)
This commit is contained in:
committed by
GitHub
parent
4e04214612
commit
af0140d317
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
cancelOrderFulfillmentWorkflow,
|
||||
createOrderFulfillmentWorkflow,
|
||||
createShippingOptionsWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
@@ -314,7 +315,7 @@ medusaIntegrationTestRunner({
|
||||
container = getContainer()
|
||||
})
|
||||
|
||||
describe("Create order fulfillment workflow", () => {
|
||||
describe("Order fulfillment workflow", () => {
|
||||
let shippingOption: ShippingOptionDTO
|
||||
let region: RegionDTO
|
||||
let location: StockLocationDTO
|
||||
@@ -335,9 +336,11 @@ medusaIntegrationTestRunner({
|
||||
orderService = container.resolve(ModuleRegistrationName.ORDER)
|
||||
})
|
||||
|
||||
it("should create a order fulfillment", async () => {
|
||||
it("should create a order fulfillment and cancel it", async () => {
|
||||
const order = await createOrderFixture({ container, product, location })
|
||||
const createReturnOrderData: OrderWorkflow.CreateOrderFulfillmentWorkflowInput =
|
||||
|
||||
// Create a fulfillment
|
||||
const createOrderFulfillmentData: OrderWorkflow.CreateOrderFulfillmentWorkflowInput =
|
||||
{
|
||||
order_id: order.id,
|
||||
created_by: "user_1",
|
||||
@@ -352,7 +355,7 @@ medusaIntegrationTestRunner({
|
||||
}
|
||||
|
||||
await createOrderFulfillmentWorkflow(container).run({
|
||||
input: createReturnOrderData,
|
||||
input: createOrderFulfillmentData,
|
||||
})
|
||||
|
||||
const remoteQuery = container.resolve(
|
||||
@@ -391,6 +394,48 @@ medusaIntegrationTestRunner({
|
||||
[location.id]
|
||||
)
|
||||
expect(stockAvailability).toEqual(1)
|
||||
|
||||
// Cancel the fulfillment
|
||||
const cancelFulfillmentData: OrderWorkflow.CancelOrderFulfillmentWorkflowInput =
|
||||
{
|
||||
order_id: order.id,
|
||||
fulfillment_id: orderFulfill.fulfillments[0].id,
|
||||
no_notification: false,
|
||||
}
|
||||
|
||||
await cancelOrderFulfillmentWorkflow(container).run({
|
||||
input: cancelFulfillmentData,
|
||||
})
|
||||
|
||||
const remoteQueryObjectFulfill = remoteQueryObjectFromString({
|
||||
entryPoint: "order",
|
||||
variables: {
|
||||
id: order.id,
|
||||
},
|
||||
fields: [
|
||||
"*",
|
||||
"items.*",
|
||||
"shipping_methods.*",
|
||||
"total",
|
||||
"item_total",
|
||||
"fulfillments.*",
|
||||
],
|
||||
})
|
||||
|
||||
const [orderFulfillAfterCancelled] = await remoteQuery(
|
||||
remoteQueryObjectFulfill
|
||||
)
|
||||
|
||||
expect(orderFulfillAfterCancelled.fulfillments).toHaveLength(1)
|
||||
expect(
|
||||
orderFulfillAfterCancelled.items[0].detail.fulfilled_quantity
|
||||
).toEqual(0)
|
||||
|
||||
const stockAvailabilityAfterCancelled =
|
||||
await inventoryModule.retrieveStockedQuantity(inventoryItem.id, [
|
||||
location.id,
|
||||
])
|
||||
expect(stockAvailabilityAfterCancelled).toEqual(2)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export type ChangeActionType =
|
||||
| "CANCEL"
|
||||
| "CANCEL_RETURN"
|
||||
| "FULFILL_ITEM"
|
||||
| "CANCEL_ITEM_FULFILLMENT"
|
||||
| "ITEM_ADD"
|
||||
| "ITEM_REMOVE"
|
||||
| "RECEIVE_DAMAGED_RETURN_ITEM"
|
||||
|
||||
@@ -368,7 +368,7 @@ export interface UpdateOrderItemWithSelectorDTO {
|
||||
|
||||
/** ORDER bundled action flows */
|
||||
|
||||
export interface RegisterOrderFulfillmentDTO {
|
||||
interface BaseOrderBundledActionsDTO {
|
||||
order_id: string
|
||||
description?: string
|
||||
internal_note?: string
|
||||
@@ -384,54 +384,18 @@ export interface RegisterOrderFulfillmentDTO {
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export interface RegisterOrderShipmentDTO {
|
||||
order_id: string
|
||||
description?: string
|
||||
internal_note?: string
|
||||
reference?: string
|
||||
reference_id?: string
|
||||
created_by?: string
|
||||
items: {
|
||||
id: string
|
||||
quantity: BigNumberInput
|
||||
internal_note?: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
}[]
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
export interface RegisterOrderFulfillmentDTO
|
||||
extends BaseOrderBundledActionsDTO {}
|
||||
|
||||
export interface CreateOrderReturnDTO {
|
||||
order_id: string
|
||||
description?: string
|
||||
reference?: string
|
||||
reference_id?: string
|
||||
internal_note?: string
|
||||
created_by?: string
|
||||
export interface CancelOrderFulfillmentDTO extends BaseOrderBundledActionsDTO {}
|
||||
|
||||
export interface RegisterOrderShipmentDTO extends BaseOrderBundledActionsDTO {}
|
||||
|
||||
export interface CreateOrderReturnDTO extends BaseOrderBundledActionsDTO {
|
||||
shipping_method: Omit<CreateOrderShippingMethodDTO, "order_id"> | string
|
||||
items: {
|
||||
id: string
|
||||
quantity: BigNumberInput
|
||||
internal_note?: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
}[]
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export interface ReceiveOrderReturnDTO {
|
||||
order_id: string
|
||||
description?: string
|
||||
internal_note?: string
|
||||
reference?: string
|
||||
reference_id?: string
|
||||
created_by?: string
|
||||
items: {
|
||||
id: string
|
||||
quantity: BigNumberInput
|
||||
internal_note?: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
}[]
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
export interface ReceiveOrderReturnDTO extends BaseOrderBundledActionsDTO {}
|
||||
|
||||
/** ORDER bundled action flows */
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
} from "./common"
|
||||
import {
|
||||
CancelOrderChangeDTO,
|
||||
CancelOrderFulfillmentDTO,
|
||||
ConfirmOrderChangeDTO,
|
||||
CreateOrderAddressDTO,
|
||||
CreateOrderAdjustmentDTO,
|
||||
@@ -1496,6 +1497,11 @@ export interface IOrderModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
cancelFulfillment(
|
||||
data: CancelOrderFulfillmentDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
registerShipment(
|
||||
data: RegisterOrderShipmentDTO,
|
||||
sharedContext?: Context
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface CancelOrderFulfillmentWorkflowInput {
|
||||
order_id: string
|
||||
fulfillment_id: string
|
||||
no_notification?: boolean
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./cancel-fulfillment"
|
||||
export * from "./create-fulfillment"
|
||||
export * from "./create-return-order"
|
||||
export * from "./create-shipment"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cancelOrderFulfillmentWorkflow } from "@medusajs/core-flows"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
@@ -6,16 +7,29 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../../types/routing"
|
||||
import { AdminOrderCancelFulfillmentType } from "../../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
req: AuthenticatedMedusaRequest<AdminOrderCancelFulfillmentType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const variables = { id: req.params.id }
|
||||
|
||||
// TODO: Workflow to cancel fulfillment + adjust inventory
|
||||
const input = {
|
||||
...req.validatedBody,
|
||||
order_id: req.params.id,
|
||||
}
|
||||
|
||||
const { errors } = await cancelOrderFulfillmentWorkflow(req.scope).run({
|
||||
input,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "order",
|
||||
|
||||
@@ -79,3 +79,12 @@ export const AdminOrderCreateShipment = z.object({
|
||||
export type AdminOrderCreateShipmentType = z.infer<
|
||||
typeof AdminOrderCreateShipment
|
||||
>
|
||||
|
||||
export const AdminOrderCancelFulfillment = z.object({
|
||||
fulfillment_id: z.string(),
|
||||
no_notification: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type AdminOrderCancelFulfillmentType = z.infer<
|
||||
typeof AdminOrderCancelFulfillment
|
||||
>
|
||||
|
||||
@@ -418,208 +418,6 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
expect(orders4.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("should create an order, fulfill, ship and return the items", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
|
||||
// Fullfilment
|
||||
await service.registerFulfillment({
|
||||
order_id: createdOrder.id,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
let getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
let serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 2,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
// Shipment
|
||||
await service.registerShipment({
|
||||
order_id: createdOrder.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 3,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 2,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
// Return
|
||||
await service.createReturn({
|
||||
order_id: createdOrder.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
description: "Return all the items",
|
||||
internal_note: "user wants to return all items",
|
||||
shipping_method: createdOrder.shipping_methods![0].id,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
"items.detail.return_requested_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 4,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 2,
|
||||
return_requested_quantity: 2,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { CreateOrderDTO, IOrderModuleService } from "@medusajs/types"
|
||||
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.ORDER,
|
||||
testSuite: ({ service }: SuiteOptions<IOrderModuleService>) => {
|
||||
describe("Order Module Service - Return flows", () => {
|
||||
const input = {
|
||||
email: "foo@bar.com",
|
||||
items: [
|
||||
{
|
||||
title: "Item 1",
|
||||
subtitle: "Subtitle 1",
|
||||
thumbnail: "thumbnail1.jpg",
|
||||
quantity: 1,
|
||||
product_id: "product1",
|
||||
product_title: "Product 1",
|
||||
product_description: "Description 1",
|
||||
product_subtitle: "Product Subtitle 1",
|
||||
product_type: "Type 1",
|
||||
product_collection: "Collection 1",
|
||||
product_handle: "handle1",
|
||||
variant_id: "variant1",
|
||||
variant_sku: "SKU1",
|
||||
variant_barcode: "Barcode1",
|
||||
variant_title: "Variant 1",
|
||||
variant_option_values: {
|
||||
color: "Red",
|
||||
size: "Large",
|
||||
},
|
||||
requires_shipping: true,
|
||||
is_discountable: true,
|
||||
is_tax_inclusive: true,
|
||||
compare_at_unit_price: 10,
|
||||
unit_price: 8,
|
||||
tax_lines: [
|
||||
{
|
||||
description: "Tax 1",
|
||||
tax_rate_id: "tax_usa",
|
||||
code: "code",
|
||||
rate: 0.1,
|
||||
provider_id: "taxify_master",
|
||||
},
|
||||
],
|
||||
adjustments: [
|
||||
{
|
||||
code: "VIP_10",
|
||||
amount: 10,
|
||||
description: "VIP discount",
|
||||
promotion_id: "prom_123",
|
||||
provider_id: "coupon_kings",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Item 2",
|
||||
quantity: 2,
|
||||
unit_price: 5,
|
||||
},
|
||||
{
|
||||
title: "Item 3",
|
||||
quantity: 1,
|
||||
unit_price: 30,
|
||||
},
|
||||
],
|
||||
sales_channel_id: "test",
|
||||
shipping_address: {
|
||||
first_name: "Test",
|
||||
last_name: "Test",
|
||||
address_1: "Test",
|
||||
city: "Test",
|
||||
country_code: "US",
|
||||
postal_code: "12345",
|
||||
phone: "12345",
|
||||
},
|
||||
billing_address: {
|
||||
first_name: "Test",
|
||||
last_name: "Test",
|
||||
address_1: "Test",
|
||||
city: "Test",
|
||||
country_code: "US",
|
||||
postal_code: "12345",
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
name: "Test shipping method",
|
||||
amount: 10,
|
||||
},
|
||||
],
|
||||
transactions: [
|
||||
{
|
||||
amount: 58,
|
||||
currency_code: "USD",
|
||||
reference: "payment",
|
||||
reference_id: "pay_123",
|
||||
},
|
||||
],
|
||||
currency_code: "usd",
|
||||
customer_id: "joe",
|
||||
} as CreateOrderDTO
|
||||
|
||||
it("should create an order, fulfill, ship and return the items and cancel some item return", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
|
||||
// Fullfilment
|
||||
await service.registerFulfillment({
|
||||
order_id: createdOrder.id,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
let getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
let serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 2,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
// Shipment
|
||||
await service.registerShipment({
|
||||
order_id: createdOrder.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 3,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 2,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 3,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
// Return
|
||||
await service.createReturn({
|
||||
order_id: createdOrder.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
description: "Return all the items",
|
||||
internal_note: "user wants to return all items",
|
||||
shipping_method: createdOrder.shipping_methods![0].id,
|
||||
items: createdOrder.items!.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
"items.detail.quantity",
|
||||
"items.detail.shipped_quantity",
|
||||
"items.detail.fulfilled_quantity",
|
||||
"items.detail.return_requested_quantity",
|
||||
],
|
||||
relations: ["items", "items.detail"],
|
||||
})
|
||||
|
||||
serializedOrder = JSON.parse(JSON.stringify(getOrder))
|
||||
|
||||
expect(serializedOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 4,
|
||||
items: [
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 2,
|
||||
shipped_quantity: 2,
|
||||
return_requested_quantity: 2,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 4,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -95,7 +95,7 @@ describe("Order Return - Actions", function () {
|
||||
order: originalOrder,
|
||||
actions,
|
||||
})
|
||||
}).toThrow(`Reference ID "333" not found.`)
|
||||
}).toThrow(`Item ID "333" not found.`)
|
||||
})
|
||||
|
||||
it("should validate return received", function () {
|
||||
|
||||
@@ -2142,6 +2142,40 @@ export default class OrderModuleService<
|
||||
await this.confirmOrderChange(change[0].id, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async cancelFulfillment(
|
||||
data: OrderTypes.CancelOrderFulfillmentDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const items = data.items.map((item) => {
|
||||
return {
|
||||
action: ChangeActionType.CANCEL_ITEM_FULFILLMENT,
|
||||
internal_note: item.internal_note,
|
||||
reference: data.reference,
|
||||
reference_id: data.reference_id,
|
||||
details: {
|
||||
reference_id: item.id,
|
||||
quantity: item.quantity,
|
||||
metadata: item.metadata,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const change = await this.createOrderChange_(
|
||||
{
|
||||
order_id: data.order_id,
|
||||
description: data.description,
|
||||
internal_note: data.internal_note,
|
||||
created_by: data.created_by,
|
||||
metadata: data.metadata,
|
||||
actions: items,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.confirmOrderChange(change[0].id, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async registerShipment(
|
||||
data: OrderTypes.RegisterOrderShipmentDTO,
|
||||
|
||||
@@ -2,6 +2,7 @@ export enum ChangeActionType {
|
||||
CANCEL = "CANCEL",
|
||||
CANCEL_RETURN = "CANCEL_RETURN",
|
||||
FULFILL_ITEM = "FULFILL_ITEM",
|
||||
CANCEL_ITEM_FULFILLMENT = "CANCEL_ITEM_FULFILLMENT",
|
||||
ITEM_ADD = "ITEM_ADD",
|
||||
ITEM_REMOVE = "ITEM_REMOVE",
|
||||
RECEIVE_DAMAGED_RETURN_ITEM = "RECEIVE_DAMAGED_RETURN_ITEM",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
OrderChangeProcessing.registerActionType(
|
||||
ChangeActionType.CANCEL_ITEM_FULFILLMENT,
|
||||
{
|
||||
operation({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.fulfilled_quantity ??= 0
|
||||
|
||||
existing.detail.fulfilled_quantity = MathBN.sub(
|
||||
existing.detail.fulfilled_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.fulfilled_quantity = MathBN.add(
|
||||
existing.detail.fulfilled_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
if (!isDefined(refId)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Reference ID is required."
|
||||
)
|
||||
}
|
||||
|
||||
const existing = currentOrder.items.find((item) => item.id === refId)
|
||||
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity to cancel item fulfillment ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity to cancel item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const greater = MathBN.gt(
|
||||
action.details?.quantity,
|
||||
existing.detail?.fulfilled_quantity
|
||||
)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot cancel more items than what was fulfilled for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -48,7 +48,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./cancel"
|
||||
export * from "./cancel-item-fulfillment"
|
||||
export * from "./cancel-return"
|
||||
export * from "./fulfill-item"
|
||||
export * from "./item-add"
|
||||
|
||||
@@ -58,7 +58,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ OrderChangeProcessing.registerActionType(
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.SHIP_ITEM, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user