feat(core-flows,medusa,utils,types): adds delivered_quantity to order (#9130)
what: - adds delivered_quantity to order https://github.com/user-attachments/assets/709b1727-08ed-4a88-ae29-38f13540e301
This commit is contained in:
@@ -195,5 +195,18 @@ export async function createOrderSeeder({ api, container }) {
|
||||
|
||||
order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data.order
|
||||
|
||||
return order
|
||||
return {
|
||||
order,
|
||||
region,
|
||||
salesChannel,
|
||||
stockLocation,
|
||||
inventoryItem,
|
||||
shippingProfile,
|
||||
product,
|
||||
fulfillmentSets,
|
||||
fulfillmentSet,
|
||||
shippingOption,
|
||||
cart,
|
||||
paymentCollection,
|
||||
}
|
||||
}
|
||||
|
||||
88
integration-tests/http/__tests__/order/admin/order.spec.ts
Normal file
88
integration-tests/http/__tests__/order/admin/order.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/utils"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
import {
|
||||
adminHeaders,
|
||||
createAdminUser,
|
||||
} from "../../../../helpers/create-admin-user"
|
||||
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
|
||||
import { createOrderSeeder } from "../../fixtures/order"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
let order, seeder
|
||||
|
||||
beforeEach(async () => {
|
||||
const container = getContainer()
|
||||
|
||||
await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX))
|
||||
await createAdminUser(dbConnection, adminHeaders, container)
|
||||
seeder = await createOrderSeeder({ api, container })
|
||||
order = seeder.order
|
||||
order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data
|
||||
.order
|
||||
})
|
||||
|
||||
describe("POST /orders/:id/fulfillments/:id/mark-as-delivered", () => {
|
||||
it("should mark fulfillable item as delivered", async () => {
|
||||
let fulfillableItem = order.items.find(
|
||||
(item) => item.detail.fulfilled_quantity < item.detail.quantity
|
||||
)
|
||||
|
||||
await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments`,
|
||||
{
|
||||
location_id: seeder.stockLocation.id,
|
||||
items: [
|
||||
{
|
||||
id: fulfillableItem.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data
|
||||
.order
|
||||
|
||||
expect(order.items[0].detail).toEqual(
|
||||
expect.objectContaining({
|
||||
fulfilled_quantity: 1,
|
||||
delivered_quantity: 0,
|
||||
})
|
||||
)
|
||||
|
||||
await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments/${order.fulfillments[0].id}/mark-as-delivered`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data
|
||||
.order
|
||||
|
||||
expect(order.items[0].detail).toEqual(
|
||||
expect.objectContaining({
|
||||
fulfilled_quantity: 1,
|
||||
delivered_quantity: 1,
|
||||
})
|
||||
)
|
||||
|
||||
const { response } = await api
|
||||
.post(
|
||||
`/admin/orders/${order.id}/fulfillments/${order.fulfillments[0].id}/mark-as-delivered`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(response.data).toEqual({
|
||||
type: "not_allowed",
|
||||
message: "Fulfillment has already been marked delivered",
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -24,7 +24,8 @@ medusaIntegrationTestRunner({
|
||||
|
||||
await setupTaxStructure(container.resolve(Modules.TAX))
|
||||
await createAdminUser(dbConnection, adminHeaders, container)
|
||||
order = await createOrderSeeder({ api, container })
|
||||
const seeders = await createOrderSeeder({ api, container })
|
||||
order = seeders.order
|
||||
|
||||
shippingProfile = (
|
||||
await api.post(
|
||||
@@ -157,7 +158,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
describe("RMA Flows", () => {
|
||||
it.only("should verify order summary at each level", async () => {
|
||||
it("should verify order summary at each level", async () => {
|
||||
/* Case:
|
||||
Purchased:
|
||||
items: {
|
||||
|
||||
@@ -37,7 +37,8 @@ medusaIntegrationTestRunner({
|
||||
beforeEach(async () => {
|
||||
container = getContainer()
|
||||
await createAdminUser(dbConnection, adminHeaders, container)
|
||||
order = await createOrderSeeder({ api, container })
|
||||
const seeders = await createOrderSeeder({ api, container })
|
||||
order = seeders.order
|
||||
|
||||
await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments`,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
QueryKey,
|
||||
useMutation,
|
||||
@@ -5,8 +7,6 @@ import {
|
||||
useQuery,
|
||||
UseQueryOptions,
|
||||
} from "@tanstack/react-query"
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { sdk } from "../../lib/client"
|
||||
import { queryClient } from "../../lib/query-client"
|
||||
import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory"
|
||||
@@ -184,6 +184,33 @@ export const useCreateOrderShipment = (
|
||||
})
|
||||
}
|
||||
|
||||
export const useMarkOrderFulfillmentAsDelivered = (
|
||||
orderId: string,
|
||||
fulfillmentId: string,
|
||||
options?: UseMutationOptions<
|
||||
{ order: HttpTypes.AdminOrder },
|
||||
FetchError,
|
||||
HttpTypes.AdminMarkOrderFulfillmentAsDelivered
|
||||
>
|
||||
) => {
|
||||
return useMutation({
|
||||
mutationFn: (payload: HttpTypes.AdminMarkOrderFulfillmentAsDelivered) =>
|
||||
sdk.admin.order.markAsDelivered(orderId, fulfillmentId, payload),
|
||||
onSuccess: (data: any, variables: any, context: any) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.preview(orderId),
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export const useCancelOrder = (
|
||||
orderId: string,
|
||||
options?: UseMutationOptions<any, FetchError, any>
|
||||
|
||||
@@ -1065,6 +1065,7 @@
|
||||
},
|
||||
"fulfillment": {
|
||||
"cancelWarning": "You are about to cancel a fulfillment. This action cannot be undone.",
|
||||
"markAsDeliveredWarning": "You are about to mark fulfillment as delivered. This action cannot be undone.",
|
||||
"unfulfilledItems": "Unfulfilled Items",
|
||||
"statusLabel": "Fulfillment status",
|
||||
"statusTitle": "Fulfillment Status",
|
||||
@@ -1076,6 +1077,7 @@
|
||||
"available": "Available",
|
||||
"inStock": "In stock",
|
||||
"markAsShipped": "Mark as shipped",
|
||||
"markAsDelivered": "Mark as delivered",
|
||||
"itemsToFulfillDesc": "Choose items and quantities to fulfill",
|
||||
"locationDescription": "Choose which location you want to fulfill items from.",
|
||||
"sendNotificationHint": "Notify customers about the created fulfillment.",
|
||||
@@ -1091,6 +1093,8 @@
|
||||
"fulfilled": "Fulfilled",
|
||||
"partiallyShipped": "Partially shipped",
|
||||
"shipped": "Shipped",
|
||||
"delivered": "Delivered",
|
||||
"partiallyDelivered": "Partially delivered",
|
||||
"partiallyReturned": "Partially returned",
|
||||
"returned": "Returned",
|
||||
"canceled": "Canceled",
|
||||
@@ -1099,7 +1103,8 @@
|
||||
"toast": {
|
||||
"created": "Fulfillment created successfully",
|
||||
"canceled": "Fulfillment successfully canceled",
|
||||
"fulfillmentShipped": "Cannot cancel an already shipped fulfillment"
|
||||
"fulfillmentShipped": "Cannot cancel an already shipped fulfillment",
|
||||
"fulfillmentDelivered": "Fulfillment marked as delivered successfully"
|
||||
},
|
||||
"trackingLabel": "Tracking",
|
||||
"shippingFromLabel": "Shipping from",
|
||||
@@ -1155,6 +1160,7 @@
|
||||
"created": "Items fulfilled",
|
||||
"canceled": "Fulfillment canceled",
|
||||
"shipped": "Items shipped",
|
||||
"delivered": "Items delivered",
|
||||
"items_one": "{{count}} item",
|
||||
"items_other": "{{count}} items"
|
||||
},
|
||||
|
||||
@@ -45,6 +45,11 @@ export const getOrderFulfillmentStatus = (
|
||||
"orange",
|
||||
],
|
||||
shipped: [t("orders.fulfillment.status.shipped"), "green"],
|
||||
delivered: [t("orders.fulfillment.status.delivered"), "green"],
|
||||
partially_delivered: [
|
||||
t("orders.fulfillment.status.partiallyDelivered"),
|
||||
"orange",
|
||||
],
|
||||
partially_returned: [
|
||||
t("orders.fulfillment.status.partiallyReturned"),
|
||||
"orange",
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { AdminOrderLineItem } from "@medusajs/types"
|
||||
import { useOrderChanges } from "../../../../../hooks/api"
|
||||
import { useCancelClaim, useClaims } from "../../../../../hooks/api/claims"
|
||||
import {
|
||||
useCancelExchange,
|
||||
@@ -25,7 +26,6 @@ import { useCancelReturn, useReturns } from "../../../../../hooks/api/returns"
|
||||
import { useDate } from "../../../../../hooks/use-date"
|
||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
||||
import { getPaymentsFromOrder } from "../order-payment-section"
|
||||
import { useOrderChanges } from "../../../../../hooks/api"
|
||||
import ActivityItems from "./activity-items"
|
||||
|
||||
type OrderTimelineProps = {
|
||||
@@ -222,6 +222,16 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
children: <FulfillmentCreatedBody fulfillment={fulfillment} />,
|
||||
})
|
||||
|
||||
if (fulfillment.delivered_at) {
|
||||
items.push({
|
||||
title: t("orders.activity.events.fulfillment.delivered"),
|
||||
timestamp: fulfillment.delivered_at,
|
||||
children: (
|
||||
<FulfillmentCreatedBody fulfillment={fulfillment} />
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
if (fulfillment.shipped_at) {
|
||||
items.push({
|
||||
title: t("orders.activity.events.fulfillment.shipped"),
|
||||
@@ -340,10 +350,10 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
edit.status === "requested"
|
||||
? edit.requested_at
|
||||
: edit.status === "declined"
|
||||
? edit.declined_at
|
||||
: edit.status === "canceled"
|
||||
? edit.canceled_at
|
||||
: edit.created_at,
|
||||
? edit.declined_at
|
||||
: edit.status === "canceled"
|
||||
? edit.canceled_at
|
||||
: edit.created_at,
|
||||
children: isConfirmed ? (
|
||||
<OrderEditBody edit={edit} itemsMap={itemsMap} />
|
||||
) : null,
|
||||
|
||||
@@ -22,7 +22,10 @@ import { Link, useNavigate } from "react-router-dom"
|
||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||
import { Skeleton } from "../../../../../components/common/skeleton"
|
||||
import { Thumbnail } from "../../../../../components/common/thumbnail"
|
||||
import { useCancelOrderFulfillment } from "../../../../../hooks/api/orders"
|
||||
import {
|
||||
useCancelOrderFulfillment,
|
||||
useMarkOrderFulfillmentAsDelivered,
|
||||
} from "../../../../../hooks/api/orders"
|
||||
import { useStockLocation } from "../../../../../hooks/api/stock-locations"
|
||||
import { formatProvider } from "../../../../../lib/format-provider"
|
||||
import { getLocaleAmount } from "../../../../../lib/money-amount-helpers"
|
||||
@@ -183,6 +186,10 @@ const Fulfillment = ({
|
||||
statusText = "Canceled"
|
||||
statusColor = "red"
|
||||
statusTimestamp = fulfillment.canceled_at
|
||||
} else if (fulfillment.delivered_at) {
|
||||
statusText = "Delivered"
|
||||
statusColor = "green"
|
||||
statusTimestamp = fulfillment.delivered_at
|
||||
} else if (fulfillment.shipped_at) {
|
||||
statusText = "Shipped"
|
||||
statusColor = "green"
|
||||
@@ -190,8 +197,41 @@ const Fulfillment = ({
|
||||
}
|
||||
|
||||
const { mutateAsync } = useCancelOrderFulfillment(order.id, fulfillment.id)
|
||||
const { mutateAsync: markAsDelivered } = useMarkOrderFulfillmentAsDelivered(
|
||||
order.id,
|
||||
fulfillment.id
|
||||
)
|
||||
|
||||
const showShippingButton = !fulfillment.canceled_at && !fulfillment.shipped_at
|
||||
const showShippingButton =
|
||||
!fulfillment.canceled_at &&
|
||||
!fulfillment.shipped_at &&
|
||||
!fulfillment.delivered_at
|
||||
const showDeliveryButton =
|
||||
!fulfillment.canceled_at && !fulfillment.delivered_at
|
||||
|
||||
const handleMarkAsDelivered = async () => {
|
||||
const res = await prompt({
|
||||
title: t("general.areYouSure"),
|
||||
description: t("orders.fulfillment.markAsDeliveredWarning"),
|
||||
confirmText: t("actions.continue"),
|
||||
cancelText: t("actions.cancel"),
|
||||
variant: "confirmation",
|
||||
})
|
||||
|
||||
if (res) {
|
||||
await markAsDelivered(
|
||||
{},
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success(t("orders.fulfillment.toast.fulfillmentDelivered"))
|
||||
},
|
||||
onError: (e) => {
|
||||
toast.error(e.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = async () => {
|
||||
if (fulfillment.shipped_at) {
|
||||
@@ -343,14 +383,23 @@ const Fulfillment = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{showShippingButton && (
|
||||
<div className="bg-ui-bg-subtle flex items-center justify-end rounded-b-xl px-4 py-4">
|
||||
<Button
|
||||
onClick={() => navigate(`./${fulfillment.id}/create-shipment`)}
|
||||
variant="secondary"
|
||||
>
|
||||
{t("orders.fulfillment.markAsShipped")}
|
||||
</Button>
|
||||
|
||||
{(showShippingButton || showDeliveryButton) && (
|
||||
<div className="bg-ui-bg-subtle flex items-center justify-end rounded-b-xl px-4 py-4 gap-x-2">
|
||||
{showDeliveryButton && (
|
||||
<Button onClick={handleMarkAsDelivered} variant="secondary">
|
||||
{t("orders.fulfillment.markAsDelivered")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showShippingButton && (
|
||||
<Button
|
||||
onClick={() => navigate(`./${fulfillment.id}/create-shipment`)}
|
||||
variant="secondary"
|
||||
>
|
||||
{t("orders.fulfillment.markAsShipped")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Container>
|
||||
|
||||
@@ -13,6 +13,7 @@ export const updateFulfillmentStep = createStep(
|
||||
{ container }
|
||||
) => {
|
||||
const { id, ...data } = input
|
||||
|
||||
const service = container.resolve<IFulfillmentModuleService>(
|
||||
Modules.FULFILLMENT
|
||||
)
|
||||
|
||||
@@ -9,8 +9,8 @@ export * from "./create-shipping-profiles"
|
||||
export * from "./delete-fulfillment-sets"
|
||||
export * from "./delete-service-zones"
|
||||
export * from "./delete-shipping-options"
|
||||
export * from "./mark-fulfillment-as-delivered"
|
||||
export * from "./update-fulfillment"
|
||||
export * from "./update-service-zones"
|
||||
export * from "./update-shipping-options"
|
||||
export * from "./update-shipping-profiles"
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { FulfillmentDTO } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
StepResponse,
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../common"
|
||||
import { updateFulfillmentWorkflow } from "./update-fulfillment"
|
||||
|
||||
export const validateFulfillmentDeliverabilityStepId =
|
||||
"validate-fulfillment-deliverability"
|
||||
/**
|
||||
* This step validates that if a fulfillment can be marked delivered
|
||||
*/
|
||||
export const validateFulfillmentDeliverabilityStep = createStep(
|
||||
validateFulfillmentDeliverabilityStepId,
|
||||
async (fulfillment: FulfillmentDTO) => {
|
||||
if (fulfillment.canceled_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Cannot deliver an already canceled fulfillment"
|
||||
)
|
||||
}
|
||||
|
||||
if (fulfillment.delivered_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Fulfillment has already been marked delivered"
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
)
|
||||
|
||||
export const markFulfillmentAsDeliveredWorkflowId =
|
||||
"mark-fulfillment-as-delivered-workflow"
|
||||
/**
|
||||
* This workflow marks fulfillment as delivered.
|
||||
*/
|
||||
export const markFulfillmentAsDeliveredWorkflow = createWorkflow(
|
||||
markFulfillmentAsDeliveredWorkflowId,
|
||||
({ id }: WorkflowData<{ id: string }>) => {
|
||||
const fulfillment = useRemoteQueryStep({
|
||||
entry_point: "fulfillment",
|
||||
fields: ["id", "delivered_at", "canceled_at"],
|
||||
variables: { id },
|
||||
throw_if_key_not_found: true,
|
||||
list: false,
|
||||
})
|
||||
|
||||
validateFulfillmentDeliverabilityStep(fulfillment)
|
||||
|
||||
const updateInput = transform({ id }, ({ id }) => ({
|
||||
id,
|
||||
delivered_at: new Date(),
|
||||
}))
|
||||
|
||||
return new WorkflowResponse(
|
||||
updateFulfillmentWorkflow.runAsStep({ input: updateInput })
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { IOrderModuleService, RegisterOrderDeliveryDTO } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const registerOrderDeliveryStepId = "register-order-delivery"
|
||||
/**
|
||||
* This step registers a delivery for an order fulfillment.
|
||||
*/
|
||||
export const registerOrderDeliveryStep = createStep(
|
||||
registerOrderDeliveryStepId,
|
||||
async (data: RegisterOrderDeliveryDTO, { container }) => {
|
||||
const service = container.resolve<IOrderModuleService>(
|
||||
ModuleRegistrationName.ORDER
|
||||
)
|
||||
|
||||
await service.registerDelivery(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)
|
||||
}
|
||||
)
|
||||
@@ -41,6 +41,7 @@ export * from "./exchange/update-exchange-add-item"
|
||||
export * from "./exchange/update-exchange-shipping-method"
|
||||
export * from "./get-order-detail"
|
||||
export * from "./get-orders-list"
|
||||
export * from "./mark-order-fulfillment-as-delivered"
|
||||
export * from "./mark-payment-collection-as-paid"
|
||||
export * from "./order-edit/begin-order-edit"
|
||||
export * from "./order-edit/cancel-begin-order-edit"
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
FulfillmentDTO,
|
||||
OrderDTO,
|
||||
RegisterOrderDeliveryDTO,
|
||||
} from "@medusajs/types"
|
||||
import { FulfillmentEvents, Modules } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { emitEventStep, useRemoteQueryStep } from "../../common"
|
||||
import { markFulfillmentAsDeliveredWorkflow } from "../../fulfillment"
|
||||
import { registerOrderDeliveryStep } from "../steps/register-delivery"
|
||||
import {
|
||||
throwIfItemsDoesNotExistsInOrder,
|
||||
throwIfOrderIsCancelled,
|
||||
} from "../utils/order-validation"
|
||||
|
||||
export const orderFulfillmentDeliverablilityValidationStepId =
|
||||
"order-fulfillment-deliverability-validation"
|
||||
/**
|
||||
* This step validates that order & fulfillment are valid
|
||||
*/
|
||||
export const orderFulfillmentDeliverablilityValidationStep = createStep(
|
||||
orderFulfillmentDeliverablilityValidationStepId,
|
||||
async ({
|
||||
fulfillment,
|
||||
order,
|
||||
}: {
|
||||
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
|
||||
fulfillment: FulfillmentDTO
|
||||
}) => {
|
||||
throwIfOrderIsCancelled({ order })
|
||||
|
||||
const orderFulfillment = order.fulfillments?.find(
|
||||
(f) => f.id === fulfillment.id
|
||||
)
|
||||
|
||||
if (!orderFulfillment) {
|
||||
throw new Error(
|
||||
`Fulfillment with id ${fulfillment.id} not found in the order`
|
||||
)
|
||||
}
|
||||
|
||||
throwIfItemsDoesNotExistsInOrder({
|
||||
order,
|
||||
inputItems: order.items!.map((i) => ({
|
||||
id: i.id,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
function prepareRegisterDeliveryData({
|
||||
fulfillment,
|
||||
order,
|
||||
}: {
|
||||
fulfillment: FulfillmentDTO
|
||||
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
|
||||
}): RegisterOrderDeliveryDTO {
|
||||
const orderFulfillment = order.fulfillments.find(
|
||||
(f) => f.id === fulfillment.id
|
||||
)!
|
||||
|
||||
return {
|
||||
order_id: order.id,
|
||||
reference: Modules.FULFILLMENT,
|
||||
reference_id: orderFulfillment.id,
|
||||
items: orderFulfillment.items!.map((i) => {
|
||||
return {
|
||||
id: i.line_item_id!,
|
||||
quantity: i.quantity!,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export const markOrderFulfillmentAsDeliveredWorkflowId =
|
||||
"mark-order-fulfillment-as-delivered-workflow"
|
||||
/**
|
||||
* This workflow marks a fulfillment in an order as delivered.
|
||||
*/
|
||||
export const markOrderFulfillmentAsDeliveredWorkflow = createWorkflow(
|
||||
markOrderFulfillmentAsDeliveredWorkflowId,
|
||||
(input: WorkflowData<{ orderId: string; fulfillmentId: string }>) => {
|
||||
const { fulfillmentId, orderId } = input
|
||||
const fulfillment = useRemoteQueryStep({
|
||||
entry_point: "fulfillment",
|
||||
fields: ["id"],
|
||||
variables: { id: fulfillmentId },
|
||||
throw_if_key_not_found: true,
|
||||
list: false,
|
||||
})
|
||||
|
||||
const order = useRemoteQueryStep({
|
||||
entry_point: "order",
|
||||
fields: [
|
||||
"id",
|
||||
"summary",
|
||||
"currency_code",
|
||||
"region_id",
|
||||
"fulfillments.id",
|
||||
"fulfillments.items.id",
|
||||
"fulfillments.items.quantity",
|
||||
"fulfillments.items.line_item_id",
|
||||
"items.id",
|
||||
"items.quantity",
|
||||
],
|
||||
variables: { id: orderId },
|
||||
throw_if_key_not_found: true,
|
||||
list: false,
|
||||
}).config({ name: "order-query" })
|
||||
|
||||
orderFulfillmentDeliverablilityValidationStep({ order, fulfillment })
|
||||
|
||||
const deliveryData = transform(
|
||||
{ order, fulfillment },
|
||||
prepareRegisterDeliveryData
|
||||
)
|
||||
|
||||
const [deliveredFulfillment] = parallelize(
|
||||
markFulfillmentAsDeliveredWorkflow.runAsStep({
|
||||
input: { id: fulfillment.id },
|
||||
}),
|
||||
registerOrderDeliveryStep(deliveryData)
|
||||
)
|
||||
|
||||
emitEventStep({
|
||||
eventName: FulfillmentEvents.DELIVERY_CREATED,
|
||||
data: { id: deliveredFulfillment.id },
|
||||
})
|
||||
|
||||
return new WorkflowResponse(void 0)
|
||||
}
|
||||
)
|
||||
@@ -112,6 +112,24 @@ export class Order {
|
||||
)
|
||||
}
|
||||
|
||||
async markAsDelivered(
|
||||
id: string,
|
||||
fulfillmentId: string,
|
||||
body: HttpTypes.AdminMarkOrderFulfillmentAsDelivered,
|
||||
query?: SelectParams,
|
||||
headers?: ClientHeaders
|
||||
) {
|
||||
return await this.client.fetch<{ order: HttpTypes.AdminOrder }>(
|
||||
`/admin/orders/${id}/fulfillments/${fulfillmentId}/mark-as-delivered`,
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body,
|
||||
query,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async listChanges(
|
||||
id: string,
|
||||
queryParams?: FindParams & HttpTypes.AdminOrderChangesFilters,
|
||||
|
||||
@@ -19,3 +19,5 @@ export interface AdminCreateOrderShipment {
|
||||
export interface AdminCancelOrderFulfillment {
|
||||
no_notification?: boolean
|
||||
}
|
||||
|
||||
export interface AdminMarkOrderFulfillmentAsDelivered {}
|
||||
|
||||
@@ -165,6 +165,7 @@ export interface BaseOrderItemDetail {
|
||||
item: BaseOrderLineItem
|
||||
quantity: number
|
||||
fulfilled_quantity: number
|
||||
delivered_quantity: number
|
||||
shipped_quantity: number
|
||||
return_requested_quantity: number
|
||||
return_received_quantity: number
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ClaimReason } from "./mutations"
|
||||
export type ChangeActionType =
|
||||
| "CANCEL_RETURN_ITEM"
|
||||
| "FULFILL_ITEM"
|
||||
| "DELIVER_ITEM"
|
||||
| "CANCEL_ITEM_FULFILLMENT"
|
||||
| "ITEM_ADD"
|
||||
| "ITEM_REMOVE"
|
||||
@@ -899,6 +900,16 @@ export interface OrderItemDTO {
|
||||
*/
|
||||
raw_fulfilled_quantity: BigNumberRawValue
|
||||
|
||||
/**
|
||||
* The delivered quantity of the order line item.
|
||||
*/
|
||||
delivered_quantity: number
|
||||
|
||||
/**
|
||||
* The raw delivered quantity of the order line item.
|
||||
*/
|
||||
raw_delivered_quantity: BigNumberRawValue
|
||||
|
||||
/**
|
||||
* The shipped quantity of the order line item.
|
||||
*/
|
||||
|
||||
@@ -1468,6 +1468,22 @@ export interface RegisterOrderShipmentDTO extends BaseOrderBundledActionsDTO {
|
||||
no_notification?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The details to register a delivery of an order, return, exchange,
|
||||
* or claim.
|
||||
*/
|
||||
export interface RegisterOrderDeliveryDTO extends BaseOrderBundledActionsDTO {
|
||||
/**
|
||||
* The items of the delivery.
|
||||
*/
|
||||
items?: BaseOrderBundledItemActionsDTO[]
|
||||
|
||||
/**
|
||||
* Whether the customer should receive notifications about the delivery.
|
||||
*/
|
||||
no_notification?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The return to be created.
|
||||
*/
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
CreateOrderTransactionDTO,
|
||||
DeclineOrderChangeDTO,
|
||||
ReceiveOrderReturnDTO,
|
||||
RegisterOrderDeliveryDTO,
|
||||
RegisterOrderFulfillmentDTO,
|
||||
RegisterOrderShipmentDTO,
|
||||
UpdateOrderAddressDTO,
|
||||
@@ -4591,6 +4592,29 @@ export interface IOrderModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
/**
|
||||
* This method registers a delivery for an order's fulfillment
|
||||
*
|
||||
* @param {RegisterOrderDeliveryDTO} data - The ordes's delivery data.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<void>} Resolves when the delivery is registered successfully.
|
||||
*
|
||||
* @example
|
||||
* await orderModuleService.registerDelivery({
|
||||
* order_id: "123",
|
||||
* items: [
|
||||
* {
|
||||
* id: "321",
|
||||
* quantity: 1
|
||||
* }
|
||||
* ]
|
||||
* })
|
||||
*/
|
||||
registerDelivery(
|
||||
data: RegisterOrderDeliveryDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
/**
|
||||
* This method creates a return.
|
||||
*
|
||||
|
||||
@@ -16,3 +16,13 @@ export interface CreateOrderShipmentWorkflowInput {
|
||||
no_notification?: boolean
|
||||
metadata?: MetadataType
|
||||
}
|
||||
|
||||
interface CreateOrderDeliveryItem {
|
||||
id: string
|
||||
quantity: BigNumberInput
|
||||
}
|
||||
|
||||
export interface CreateOrderDeliveryWorkflowInput {
|
||||
order_id: string
|
||||
fulfillment_id: string
|
||||
}
|
||||
|
||||
@@ -30,4 +30,5 @@ const eventBaseNames: [
|
||||
export const FulfillmentEvents = {
|
||||
...buildEventNamesFromEntityName(eventBaseNames, Modules.FULFILLMENT),
|
||||
SHIPMENT_CREATED: "shipment.created",
|
||||
DELIVERY_CREATED: "delivery.created",
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum ChangeActionType {
|
||||
FULFILL_ITEM = "FULFILL_ITEM",
|
||||
DELIVER_ITEM = "DELIVER_ITEM",
|
||||
CANCEL_ITEM_FULFILLMENT = "CANCEL_ITEM_FULFILLMENT",
|
||||
ITEM_ADD = "ITEM_ADD",
|
||||
ITEM_REMOVE = "ITEM_REMOVE",
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface GetItemTotalInput {
|
||||
adjustments?: Pick<AdjustmentLineDTO, "amount">[]
|
||||
detail?: {
|
||||
fulfilled_quantity: BigNumber
|
||||
delivered_quantity: BigNumber
|
||||
shipped_quantity: BigNumber
|
||||
return_requested_quantity: BigNumber
|
||||
return_received_quantity: BigNumber
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { markOrderFulfillmentAsDeliveredWorkflow } from "@medusajs/core-flows"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../../types/routing"
|
||||
import { refetchEntity } from "../../../../../../utils/refetch-entity"
|
||||
import { AdminMarkOrderFulfillmentDeliveredType } from "../../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<AdminMarkOrderFulfillmentDeliveredType>,
|
||||
res: MedusaResponse<HttpTypes.AdminOrderResponse>
|
||||
) => {
|
||||
const { id: orderId, fulfillment_id: fulfillmentId } = req.params
|
||||
|
||||
await markOrderFulfillmentAsDeliveredWorkflow(req.scope).run({
|
||||
input: { orderId, fulfillmentId },
|
||||
})
|
||||
|
||||
const order = await refetchEntity(
|
||||
"order",
|
||||
orderId,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
AdminCompleteOrder,
|
||||
AdminGetOrdersOrderParams,
|
||||
AdminGetOrdersParams,
|
||||
AdminMarkOrderFulfillmentDelivered,
|
||||
AdminOrderCancelFulfillment,
|
||||
AdminOrderChanges,
|
||||
AdminOrderCreateFulfillment,
|
||||
@@ -119,4 +120,15 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/orders/:id/fulfillments/:fulfillment_id/mark-as-delivered",
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminMarkOrderFulfillmentDelivered),
|
||||
validateAndTransformQuery(
|
||||
AdminGetOrdersOrderParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -101,3 +101,8 @@ export const AdminOrderChanges = z.object({
|
||||
deleted_at: createOperatorMap().optional(),
|
||||
})
|
||||
export type AdminOrderChangesType = z.infer<typeof AdminOrderChanges>
|
||||
|
||||
export type AdminMarkOrderFulfillmentDeliveredType = z.infer<
|
||||
typeof AdminMarkOrderFulfillmentDelivered
|
||||
>
|
||||
export const AdminMarkOrderFulfillmentDelivered = z.object({})
|
||||
|
||||
@@ -1043,7 +1043,7 @@ export default class InventoryModuleService
|
||||
locationId: string,
|
||||
@MedusaContext() context: Context = {}
|
||||
): Promise<InventoryTypes.InventoryLevelDTO> {
|
||||
const inventoryLevel = await this.listInventoryLevels(
|
||||
const [inventoryLevel] = await this.listInventoryLevels(
|
||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||
{ take: null },
|
||||
context
|
||||
@@ -1056,7 +1056,7 @@ export default class InventoryModuleService
|
||||
)
|
||||
}
|
||||
|
||||
return inventoryLevel[0]
|
||||
return inventoryLevel
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -397,6 +397,13 @@ moduleIntegrationTestRunner<IOrderModuleService>({
|
||||
quantity: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.DELIVER_ITEM,
|
||||
details: {
|
||||
reference_id: createdOrder.items![1].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -421,6 +428,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
|
||||
expect.objectContaining({
|
||||
quantity: 4,
|
||||
fulfilled_quantity: 1,
|
||||
delivered_quantity: 1,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -499,6 +507,30 @@ moduleIntegrationTestRunner<IOrderModuleService>({
|
||||
fulfilled_quantity: 2,
|
||||
})
|
||||
)
|
||||
|
||||
const orderChange5 = await service.createOrderChange({
|
||||
order_id: createdOrder.id,
|
||||
actions: [
|
||||
{
|
||||
action: ChangeActionType.DELIVER_ITEM,
|
||||
details: {
|
||||
reference_id: createdOrder.items![1].id,
|
||||
quantity: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await expect(
|
||||
service.confirmOrderChange({
|
||||
id: orderChange5.id,
|
||||
})
|
||||
).rejects.toThrow(
|
||||
`Cannot deliver more items than what was fulfilled for item ${
|
||||
createdOrder.items![1].id
|
||||
}`
|
||||
)
|
||||
await service.deleteOrderChanges([orderChange5.id])
|
||||
})
|
||||
|
||||
it("should create an order change, add actions to it, confirm the changes, revert all the changes and restore the changes again.", async function () {
|
||||
|
||||
@@ -869,6 +869,25 @@
|
||||
"nullable": false,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"delivered_quantity": {
|
||||
"name": "delivered_quantity",
|
||||
"type": "numeric",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "0",
|
||||
"mappedType": "decimal"
|
||||
},
|
||||
"raw_delivered_quantity": {
|
||||
"name": "raw_delivered_quantity",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"shipped_quantity": {
|
||||
"name": "shipped_quantity",
|
||||
"type": "numeric",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Migration } from "@mikro-orm/migrations"
|
||||
|
||||
export class Migration20240913092514 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
this.addSql(
|
||||
'alter table if exists "order_item" add column if not exists "delivered_quantity" numeric not null default 0, add column if not exists "raw_delivered_quantity" jsonb;'
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
`UPDATE "order_item" SET raw_delivered_quantity = '{"value": "0", "precision": 20}'::jsonb;`
|
||||
)
|
||||
|
||||
this.addSql(
|
||||
'ALTER TABLE IF EXISTS "order_item" ALTER COLUMN "raw_delivered_quantity" SET NOT NULL;'
|
||||
)
|
||||
}
|
||||
|
||||
async down(): Promise<void> {
|
||||
this.addSql(
|
||||
'alter table if exists "order_item" drop column if exists "delivered_quantity";'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table if exists "order_item" drop column if exists "raw_delivered_quantity";'
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,12 @@ export default class OrderItem {
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_fulfilled_quantity: BigNumberRawValue
|
||||
|
||||
@MikroOrmBigNumberProperty()
|
||||
delivered_quantity: BigNumber | number = 0
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_delivered_quantity: BigNumberRawValue
|
||||
|
||||
@MikroOrmBigNumberProperty()
|
||||
shipped_quantity: BigNumber | number = 0
|
||||
|
||||
|
||||
@@ -6,5 +6,6 @@ export * from "./create-claim"
|
||||
export * from "./create-exchange"
|
||||
export * from "./create-return"
|
||||
export * from "./receive-return"
|
||||
export * from "./register-delivery"
|
||||
export * from "./register-fulfillment"
|
||||
export * from "./register-shipment"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Context, OrderTypes } from "@medusajs/types"
|
||||
import { ChangeActionType } from "@medusajs/utils"
|
||||
|
||||
export async function registerDelivery(
|
||||
this: any,
|
||||
data: OrderTypes.RegisterOrderDeliveryDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const items = data.items?.map((item) => {
|
||||
return {
|
||||
action: ChangeActionType.DELIVER_ITEM,
|
||||
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)
|
||||
}
|
||||
@@ -3295,4 +3295,12 @@ export default class OrderModuleService<
|
||||
): Promise<void> {
|
||||
return await BundledActions.registerShipment.bind(this)(data, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async registerDelivery(
|
||||
data: OrderTypes.RegisterOrderDeliveryDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<void> {
|
||||
return await BundledActions.registerDelivery.bind(this)(data, sharedContext)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export type VirtualOrder = {
|
||||
quantity: BigNumberInput
|
||||
shipped_quantity: BigNumberInput
|
||||
fulfilled_quantity: BigNumberInput
|
||||
delivered_quantity: BigNumberInput
|
||||
return_requested_quantity: BigNumberInput
|
||||
return_received_quantity: BigNumberInput
|
||||
return_dismissed_quantity: BigNumberInput
|
||||
|
||||
67
packages/modules/order/src/utils/actions/deliver-item.ts
Normal file
67
packages/modules/order/src/utils/actions/deliver-item.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { ChangeActionType, MathBN, MedusaError } from "@medusajs/utils"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
import { setActionReference } from "../set-action-reference"
|
||||
|
||||
OrderChangeProcessing.registerActionType(ChangeActionType.DELIVER_ITEM, {
|
||||
operation({ action, currentOrder, options }) {
|
||||
const item = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
item.detail.delivered_quantity ??= 0
|
||||
|
||||
item.detail.delivered_quantity = MathBN.add(
|
||||
item.detail.delivered_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
setActionReference(item, action, options)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
|
||||
if (refId == null) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Reference ID is required."
|
||||
)
|
||||
}
|
||||
|
||||
const item = currentOrder.items.find((item) => item.id === refId)
|
||||
|
||||
if (!item) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Item ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
const totalDeliverable = MathBN.convert(item.quantity)
|
||||
const totalDelivered = MathBN.convert(item.detail?.delivered_quantity)
|
||||
const newDelivered = MathBN.convert(action.details?.quantity ?? 0)
|
||||
const newTotalDelivered = MathBN.sum(totalDelivered, newDelivered)
|
||||
|
||||
const totalFulfilled = MathBN.convert(item.detail?.fulfilled_quantity)
|
||||
|
||||
if (MathBN.lte(newDelivered, 0)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
if (MathBN.gt(newTotalDelivered, totalFulfilled)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot deliver more items than what was fulfilled for item ${refId}.`
|
||||
)
|
||||
}
|
||||
|
||||
if (MathBN.gt(newTotalDelivered, totalDeliverable)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot deliver more items than what was ordered for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./cancel-item-fulfillment"
|
||||
export * from "./cancel-return"
|
||||
export * from "./deliver-item"
|
||||
export * from "./fulfill-item"
|
||||
export * from "./item-add"
|
||||
export * from "./item-remove"
|
||||
|
||||
@@ -58,6 +58,7 @@ export function applyChangesToOrder(
|
||||
version,
|
||||
quantity: orderItem.quantity,
|
||||
fulfilled_quantity: orderItem.fulfilled_quantity ?? 0,
|
||||
delivered_quantity: orderItem.delivered_quantity ?? 0,
|
||||
shipped_quantity: orderItem.shipped_quantity ?? 0,
|
||||
return_requested_quantity: orderItem.return_requested_quantity ?? 0,
|
||||
return_received_quantity: orderItem.return_received_quantity ?? 0,
|
||||
|
||||
@@ -148,6 +148,7 @@ export class OrderChangeProcessing {
|
||||
isReplay = false
|
||||
): BigNumberInput | void {
|
||||
const definedType = OrderChangeProcessing.typeDefinition[action.action]
|
||||
|
||||
if (!isPresent(definedType)) {
|
||||
throw new Error(`Action type ${action.action} is not defined`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user