fix(core-flows, dashboard): adjust stock levels when doing partial fulfilments (#9736)
* fix: correctly update stock location when partial fulfillemnt is created * fix: update test * fix: count reserved quantity of the item as available quantity for fulfillment * fix: refresh reservations when order fulfillment is created * feat: add check for reservation quantity * feat: add a test case
This commit is contained in:
1 parent
a0034a0fef
commit
1889180319
6 files changed
+205
-18
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
import { AdminOrderItemsFilters, HttpTypes } from "@medusajs/types"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
QueryKey,
|
||||
useMutation,
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
import { sdk } from "../../lib/client"
|
||||
import { queryClient } from "../../lib/query-client"
|
||||
import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory"
|
||||
import { inventoryItemsQueryKeys } from "./inventory"
|
||||
import { reservationItemsQueryKeys } from "./reservations"
|
||||
|
||||
const ORDERS_QUERY_KEY = "orders" as const
|
||||
const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey<"orders"> & {
|
||||
@@ -156,6 +158,14 @@ export const useCreateOrderFulfillment = (
|
||||
queryKey: ordersQueryKeys.preview(orderId),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: reservationItemsQueryKeys.lists(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryItemsQueryKeys.details(),
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
|
||||
+15
-3
@@ -1,5 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import * as zod from "zod"
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Alert, Button, Select, Switch, toast } from "@medusajs/ui"
|
||||
import { useForm, useWatch } from "react-hook-form"
|
||||
|
||||
import { OrderLineItemDTO } from "@medusajs/types"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import {
|
||||
RouteFocusModal,
|
||||
@@ -20,6 +19,7 @@ import { useStockLocations } from "../../../../../hooks/api/stock-locations"
|
||||
import { getFulfillableQuantity } from "../../../../../lib/order-item"
|
||||
import { CreateFulfillmentSchema } from "./constants"
|
||||
import { OrderCreateFulfillmentItem } from "./order-create-fulfillment-item"
|
||||
import { useReservationItems } from "../../../../../hooks/api"
|
||||
|
||||
type OrderCreateFulfillmentFormProps = {
|
||||
order: AdminOrder
|
||||
@@ -32,11 +32,20 @@ export function OrderCreateFulfillmentForm({
|
||||
}: OrderCreateFulfillmentFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const { mutateAsync: createOrderFulfillment, isPending: isMutating } =
|
||||
useCreateOrderFulfillment(order.id)
|
||||
|
||||
const { reservations } = useReservationItems({
|
||||
line_item_id: order.items.map((i) => i.id),
|
||||
})
|
||||
|
||||
const itemReservedQuantitiesMap = useMemo(
|
||||
() =>
|
||||
new Map((reservations || []).map((r) => [r.line_item_id, r.quantity])),
|
||||
[reservations]
|
||||
)
|
||||
|
||||
const [fulfillableItems, setFulfillableItems] = useState(() =>
|
||||
(order.items || []).filter(
|
||||
(item) =>
|
||||
@@ -246,6 +255,9 @@ export function OrderCreateFulfillmentForm({
|
||||
form={form}
|
||||
item={item}
|
||||
locationId={selectedLocationId}
|
||||
itemReservedQuantitiesMap={
|
||||
itemReservedQuantitiesMap
|
||||
}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
+8
-2
@@ -10,12 +10,14 @@ import { Thumbnail } from "../../../../../components/common/thumbnail/index"
|
||||
import { useProductVariant } from "../../../../../hooks/api/products"
|
||||
import { getFulfillableQuantity } from "../../../../../lib/order-item"
|
||||
import { CreateFulfillmentSchema } from "./constants"
|
||||
import { useReservationItems } from "../../../../../hooks/api/reservations"
|
||||
|
||||
type OrderEditItemProps = {
|
||||
item: HttpTypes.AdminOrderLineItem
|
||||
currencyCode: string
|
||||
locationId?: string
|
||||
onItemRemove: (itemId: string) => void
|
||||
itemReservedQuantitiesMap: Map<string, number>
|
||||
form: UseFormReturn<zod.infer<typeof CreateFulfillmentSchema>>
|
||||
}
|
||||
|
||||
@@ -23,6 +25,7 @@ export function OrderCreateFulfillmentItem({
|
||||
item,
|
||||
form,
|
||||
locationId,
|
||||
itemReservedQuantitiesMap,
|
||||
}: OrderEditItemProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -49,11 +52,14 @@ export function OrderCreateFulfillmentItem({
|
||||
return {}
|
||||
}
|
||||
|
||||
const reservedQuantityForItem = itemReservedQuantitiesMap.get(item.id) ?? 0
|
||||
|
||||
return {
|
||||
availableQuantity: locationInventory.available_quantity,
|
||||
availableQuantity:
|
||||
locationInventory.available_quantity + reservedQuantityForItem,
|
||||
inStockQuantity: locationInventory.stocked_quantity,
|
||||
}
|
||||
}, [variant, locationId])
|
||||
}, [variant, locationId, itemReservedQuantitiesMap])
|
||||
|
||||
const minValue = 0
|
||||
const maxValue = Math.min(
|
||||
|
||||
@@ -202,20 +202,27 @@ function prepareInventoryUpdate({
|
||||
|
||||
const inputQuantity = inputItemsMap[item.id]?.quantity ?? item.quantity
|
||||
|
||||
const quantity = reservation.quantity - inputQuantity
|
||||
if (MathBN.gt(inputQuantity, reservation.quantity)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity to fulfill exceeds the reserved quantity for the item: ${item.id}`
|
||||
)
|
||||
}
|
||||
|
||||
const remainingReservationQuantity = reservation.quantity - inputQuantity
|
||||
|
||||
inventoryAdjustment.push({
|
||||
inventory_item_id: reservation.inventory_item_id,
|
||||
location_id: input.location_id ?? reservation.location_id,
|
||||
adjustment: MathBN.mult(item.quantity, -1),
|
||||
adjustment: MathBN.mult(inputQuantity, -1),
|
||||
})
|
||||
|
||||
if (quantity === 0) {
|
||||
if (remainingReservationQuantity === 0) {
|
||||
toDelete.push(reservation.id)
|
||||
} else {
|
||||
toUpdate.push({
|
||||
id: reservation.id,
|
||||
quantity: quantity,
|
||||
quantity: remainingReservationQuantity,
|
||||
location_id: input.location_id ?? reservation.location_id,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user