import React, { useMemo } from "react" import { useTranslation } from "react-i18next" import FeatureToggle from "../../../../components/fundamentals/feature-toggle" import ImagePlaceholder from "../../../../components/fundamentals/image-placeholder" import InputField from "../../../../components/molecules/input" import { LineItem } from "@medusajs/medusa" import clsx from "clsx" import { useAdminVariantsInventory } from "medusa-react" import { useFeatureFlag } from "../../../../providers/feature-flag-provider" export const getFulfillableQuantity = (item: LineItem): number => { return item.quantity - (item.fulfilled_quantity || 0) } const CreateFulfillmentItemsTable = ({ items, quantities, setQuantities, locationId, setErrors, }: { items: LineItem[] quantities: Record setQuantities: (quantities: Record) => void locationId?: string setErrors: (errors: React.SetStateAction<{}>) => void }) => { const handleQuantityUpdate = React.useCallback( (value: number, id: string) => { let newQuantities = { ...quantities } newQuantities = { ...newQuantities, [id]: value, } setQuantities(newQuantities) }, [quantities, setQuantities] ) return (
{items.map((item, idx) => { return ( ) })}
) } const FulfillmentLine = ({ item, locationId, quantities, handleQuantityUpdate, setErrors, }: { locationId?: string item: LineItem quantities: Record handleQuantityUpdate: (value: number, id: string) => void setErrors: (errors: Record) => void }) => { const { t } = useTranslation() const { isFeatureEnabled } = useFeatureFlag() const isLocationFulfillmentEnabled = isFeatureEnabled("inventoryService") && isFeatureEnabled("stockLocationService") const { variant, isLoading, refetch } = useAdminVariantsInventory( item.variant_id as string, { enabled: isLocationFulfillmentEnabled } ) const hasInventoryItem = !!variant?.inventory.length React.useEffect(() => { if (isLocationFulfillmentEnabled) { refetch() } }, [isLocationFulfillmentEnabled, refetch]) const { availableQuantity, inStockQuantity } = useMemo(() => { if (!isLocationFulfillmentEnabled) { return { availableQuantity: item.variant.inventory_quantity, inStockQuantity: item.variant.inventory_quantity, } } if (isLoading || !locationId || !variant) { return {} } const { inventory } = variant const locationInventory = inventory[0]?.location_levels?.find( (inv) => inv.location_id === locationId ) if (!locationInventory) { return {} } return { availableQuantity: locationInventory.available_quantity, inStockQuantity: locationInventory.stocked_quantity, } }, [ isLoading, locationId, variant, item.variant, isLocationFulfillmentEnabled, ]) const validQuantity = !locationId || (locationId && (!availableQuantity || quantities[item.id] <= availableQuantity)) React.useEffect(() => { setErrors((errors) => { if (validQuantity) { delete errors[item.id] return errors } errors[item.id] = t( "create-fulfillment-quantity-is-not-valid", "Quantity is not valid" ) return errors }) }, [validQuantity, setErrors, item.id]) React.useEffect(() => { if (!availableQuantity && hasInventoryItem) { handleQuantityUpdate(0, item.id) } else { handleQuantityUpdate( Math.min( getFulfillableQuantity(item), ...[hasInventoryItem ? availableQuantity : Number.MAX_VALUE] ), item.id ) } // Note: we can't add handleQuantityUpdate to the dependency array as it will cause an infinite loop }, [availableQuantity, item, item.id]) if (getFulfillableQuantity(item) <= 0) { return null } return (
{item.thumbnail ? ( ) : ( )}
{item.title} {item?.variant && ( {`${item.variant.title}${ item.variant.sku ? ` (${item.variant.sku})` : "" }`} )}
{hasInventoryItem && (

{availableQuantity || 0} available

({inStockQuantity || 0} in stock)

)}
{"/"} {getFulfillableQuantity(item)} } value={quantities[item.id]} max={Math.min( getFulfillableQuantity(item), ...[hasInventoryItem ? availableQuantity || 0 : Number.MAX_VALUE] )} onChange={(e) => handleQuantityUpdate(e.target.valueAsNumber, item.id) } errors={ validQuantity ? undefined : { quantity: t( "create-fulfillment-quantity-is-not-valid", "Quantity is not valid" ), } } />
) } export default CreateFulfillmentItemsTable