From eed784d7d0b58aeddc9f6f5ea56fe80c608b22f5 Mon Sep 17 00:00:00 2001
From: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
Date: Fri, 31 Mar 2023 15:53:56 +0200
Subject: [PATCH] fix(admin-ui, medusa): Allocations and fulfillments for
variants without inventory items (#3660)
* fix fulfillment for order lines without inventory items
* fix summary card on order for variants without inventory items
* add changeset
* minor fixes
* remove variants without inventory items from allocate modal
* naming
* Update .changeset/kind-rings-wave.md
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
* remove line item indicator
* cleanup
---------
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
---
.changeset/kind-rings-wave.md | 6 ++
.../allocations/allocate-items-modal.tsx | 5 ++
.../details/create-fulfillment/item-table.tsx | 32 ++++++----
.../orders/details/detail-cards/summary.tsx | 59 ++++++++++++++++++-
.../orders/details/order-line/index.tsx | 18 +++++-
.../src/services/product-variant-inventory.ts | 6 +-
6 files changed, 111 insertions(+), 15 deletions(-)
create mode 100644 .changeset/kind-rings-wave.md
diff --git a/.changeset/kind-rings-wave.md b/.changeset/kind-rings-wave.md
new file mode 100644
index 0000000000..2a4ce246b3
--- /dev/null
+++ b/.changeset/kind-rings-wave.md
@@ -0,0 +1,6 @@
+---
+"@medusajs/admin-ui": patch
+"@medusajs/medusa": patch
+---
+
+fix(admin-ui, medusa): resolve bugs for orders with variants without inventory items
diff --git a/packages/admin-ui/ui/src/domain/orders/details/allocations/allocate-items-modal.tsx b/packages/admin-ui/ui/src/domain/orders/details/allocations/allocate-items-modal.tsx
index f101f966f0..2af4ac1d9e 100644
--- a/packages/admin-ui/ui/src/domain/orders/details/allocations/allocate-items-modal.tsx
+++ b/packages/admin-ui/ui/src/domain/orders/details/allocations/allocate-items-modal.tsx
@@ -246,6 +246,10 @@ export const AllocationLineItem: React.FC<{
}
}, [variant, locationId, isLoading])
+ if (!variant.inventory?.length) {
+ return null
+ }
+
const lineItemReservationCapacity =
getFulfillableQuantity(item) - (reservedQuantity || 0)
@@ -256,6 +260,7 @@ export const AllocationLineItem: React.FC<{
lineItemReservationCapacity,
inventoryItemReservationCapacity
)
+
return (
diff --git a/packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/item-table.tsx b/packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/item-table.tsx
index 99f32b9806..19593b40fa 100644
--- a/packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/item-table.tsx
+++ b/packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/item-table.tsx
@@ -22,7 +22,7 @@ const CreateFulfillmentItemsTable = ({
items: LineItem[]
quantities: Record
setQuantities: (quantities: Record) => void
- locationId: string
+ locationId?: string
setErrors: (errors: React.SetStateAction<{}>) => void
}) => {
const handleQuantityUpdate = React.useCallback(
@@ -80,6 +80,8 @@ const FulfillmentLine = ({
{ enabled: isLocationFulfillmentEnabled }
)
+ const hasInventoryItem = !!variant?.inventory.length
+
React.useEffect(() => {
if (isLocationFulfillmentEnabled) {
refetch()
@@ -100,7 +102,7 @@ const FulfillmentLine = ({
const { inventory } = variant
- const locationInventory = inventory[0].location_levels?.find(
+ const locationInventory = inventory[0]?.location_levels?.find(
(inv) => inv.location_id === locationId
)
@@ -138,11 +140,14 @@ const FulfillmentLine = ({
}, [validQuantity, setErrors, item.id])
React.useEffect(() => {
- if (!availableQuantity) {
+ if (!availableQuantity && hasInventoryItem) {
handleQuantityUpdate(0, item.id)
} else {
handleQuantityUpdate(
- Math.min(getFulfillableQuantity(item), availableQuantity),
+ Math.min(
+ getFulfillableQuantity(item),
+ ...[hasInventoryItem ? availableQuantity : Number.MAX_VALUE]
+ ),
item.id
)
}
@@ -158,7 +163,9 @@ const FulfillmentLine = ({
className={clsx(
"rounded-rounded hover:bg-grey-5 mx-[-5px] mb-1 flex h-[64px] justify-between py-2 px-[5px]",
{
- "pointer-events-none opacity-50": !availableQuantity,
+ "pointer-events-none opacity-50":
+ (!availableQuantity && hasInventoryItem) ||
+ (!locationId && isLocationFulfillmentEnabled),
}
)}
>
@@ -185,10 +192,12 @@ const FulfillmentLine = ({
-
-
{availableQuantity || 0} available
-
({inStockQuantity || 0} in stock)
-
+ {hasInventoryItem && (
+
+
{availableQuantity || 0} available
+
({inStockQuantity || 0} in stock)
+
+ )}
}
value={quantities[item.id]}
- max={Math.min(availableQuantity || 0, getFulfillableQuantity(item))}
+ max={Math.min(
+ getFulfillableQuantity(item),
+ ...[hasInventoryItem ? availableQuantity || 0 : Number.MAX_VALUE]
+ )}
onChange={(e) =>
handleQuantityUpdate(e.target.valueAsNumber, item.id)
}
diff --git a/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx b/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx
index bce293ea10..eaaff40d69 100644
--- a/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx
+++ b/packages/admin-ui/ui/src/domain/orders/details/detail-cards/summary.tsx
@@ -1,5 +1,9 @@
+import {
+ AdminGetVariantsVariantInventoryRes,
+ Order,
+ VariantInventory,
+} from "@medusajs/medusa"
import { DisplayTotal, PaymentDetails } from "../templates"
-import { Order } from "@medusajs/medusa"
import React, { useContext, useMemo } from "react"
import { ActionType } from "../../../../components/molecules/actionables"
@@ -9,11 +13,13 @@ import BodyCard from "../../../../components/organisms/body-card"
import CopyToClipboard from "../../../../components/atoms/copy-to-clipboard"
import { OrderEditContext } from "../../edit/context"
import OrderLine from "../order-line"
+import { ReservationItemDTO } from "@medusajs/types"
+import { Response } from "@medusajs/medusa-js"
import StatusIndicator from "../../../../components/fundamentals/status-indicator"
import { sum } from "lodash"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
+import { useMedusa } from "medusa-react"
import useToggleState from "../../../../hooks/use-toggle-state"
-import { ReservationItemDTO } from "@medusajs/types"
type SummaryCardProps = {
order: Order
@@ -28,9 +34,49 @@ const SummaryCard: React.FC = ({ order, reservations }) => {
} = useToggleState()
const { showModal } = useContext(OrderEditContext)
+ const { client } = useMedusa()
const { isFeatureEnabled } = useFeatureFlag()
const inventoryEnabled = isFeatureEnabled("inventoryService")
+ const [variantInventoryMap, setVariantInventoryMap] = React.useState<
+ Map
+ >(new Map())
+
+ React.useEffect(() => {
+ if (!inventoryEnabled) {
+ return
+ }
+
+ const fetchInventory = async () => {
+ const inventory = await Promise.all(
+ order.items.map(async (item) => {
+ if (!item.variant_id) {
+ return
+ }
+ return await client.admin.variants.getInventory(item.variant_id)
+ })
+ )
+
+ setVariantInventoryMap(
+ new Map(
+ inventory
+ .filter(
+ (
+ inventoryItem
+ // eslint-disable-next-line max-len
+ ): inventoryItem is Response =>
+ !!inventoryItem
+ )
+ .map((i) => {
+ return [i.variant.id, i.variant]
+ })
+ )
+ )
+ }
+
+ fetchInventory()
+ }, [order.items, inventoryEnabled, client.admin.variants])
+
const reservationItemsMap = useMemo(() => {
if (!reservations?.length || !inventoryEnabled) {
return {}
@@ -52,6 +98,13 @@ const SummaryCard: React.FC = ({ order, reservations }) => {
const allItemsReserved = useMemo(() => {
return order.items.every((item) => {
+ if (
+ !item.variant_id ||
+ !variantInventoryMap.get(item.variant_id)?.inventory.length
+ ) {
+ return true
+ }
+
const reservations = reservationItemsMap[item.id]
return (
@@ -61,7 +114,7 @@ const SummaryCard: React.FC = ({ order, reservations }) => {
item.quantity - (item.fulfilled_quantity || 0))
)
})
- }, [reservationItemsMap, order])
+ }, [order.items, variantInventoryMap, reservationItemsMap])
const { hasMovements, swapAmount, manualRefund, swapRefund, returnRefund } =
useMemo(() => {
diff --git a/packages/admin-ui/ui/src/domain/orders/details/order-line/index.tsx b/packages/admin-ui/ui/src/domain/orders/details/order-line/index.tsx
index 08ffa20d62..bf99b4a8a8 100644
--- a/packages/admin-ui/ui/src/domain/orders/details/order-line/index.tsx
+++ b/packages/admin-ui/ui/src/domain/orders/details/order-line/index.tsx
@@ -1,4 +1,5 @@
import { LineItem, ReservationItemDTO } from "@medusajs/medusa"
+import { useAdminStockLocations, useAdminVariantsInventory } from "medusa-react"
import Button from "../../../../components/fundamentals/button"
import CheckCircleFillIcon from "../../../../components/fundamentals/icons/check-circle-fill-icon"
@@ -10,7 +11,6 @@ import Tooltip from "../../../../components/atoms/tooltip"
import WarningCircleIcon from "../../../../components/fundamentals/icons/warning-circle"
import { formatAmountWithSymbol } from "../../../../utils/prices"
import { sum } from "lodash"
-import { useAdminStockLocations } from "medusa-react"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
type OrderLineProps = {
@@ -84,6 +84,13 @@ const ReservationIndicator = ({
reservations?: ReservationItemDTO[]
lineItem: LineItem
}) => {
+ const { variant, isLoading, isFetching } = useAdminVariantsInventory(
+ lineItem.variant_id!,
+ {
+ enabled: !!lineItem?.variant_id,
+ }
+ )
+
const { stock_locations } = useAdminStockLocations({
id: reservations?.map((r) => r.location_id) || [],
})
@@ -99,6 +106,15 @@ const ReservationIndicator = ({
const awaitingAllocation = allocatableSum - reservationsSum
+ if (
+ isLoading ||
+ isFetching ||
+ !lineItem.variant_id ||
+ !variant?.inventory.length
+ ) {
+ return
+ }
+
return (
item.variant_id)
+ const itemsToValidate = items.filter((item) => !!item.variant_id)
for (const item of itemsToValidate) {
const pvInventoryItems = await this.listByVariant(item.variant_id!)
+ if (!pvInventoryItems.length) {
+ continue
+ }
+
const [inventoryLevels, inventoryLevelCount] =
await this.inventoryService_.listInventoryLevels({
inventory_item_id: pvInventoryItems.map((i) => i.inventory_item_id),