fix(medusa): Account for multiple inventory items in get-inventory (#3094)
* use pvi service method instead of inventory service method to get quantity for sales channel * save a few invocations * rename quantity * omit || 0 * add changeset * extract method for retrieving variant inventory quantity * update pr with feedback
This commit is contained in:
5
.changeset/violet-pans-greet.md
Normal file
5
.changeset/violet-pans-greet.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): Account for multiple inventory items when getting inventory
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from "../../../../types/inventory"
|
||||
import ProductVariantInventoryService from "../../../../services/product-variant-inventory"
|
||||
import {
|
||||
SalesChannelInventoryService,
|
||||
SalesChannelLocationService,
|
||||
SalesChannelService,
|
||||
} from "../../../../services"
|
||||
@@ -75,6 +76,8 @@ export default async (req, res) => {
|
||||
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
const salesChannelInventoryService: SalesChannelInventoryService =
|
||||
req.scope.resolve("salesChannelInventoryService")
|
||||
const channelService: SalesChannelService = req.scope.resolve(
|
||||
"salesChannelService"
|
||||
)
|
||||
@@ -106,11 +109,13 @@ export default async (req, res) => {
|
||||
})
|
||||
)
|
||||
|
||||
const variantInventoryItems =
|
||||
await productVariantInventoryService.listByVariant(variant.id)
|
||||
|
||||
const inventory =
|
||||
await productVariantInventoryService.listInventoryItemsByVariant(variant.id)
|
||||
responseVariant.inventory = await joinLevels(inventory, [], inventoryService)
|
||||
|
||||
// TODO: adjust for required quantity
|
||||
if (inventory.length) {
|
||||
responseVariant.sales_channel_availability = await Promise.all(
|
||||
channels.map(async (channel) => {
|
||||
@@ -122,10 +127,11 @@ export default async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
const quantity = await inventoryService.retrieveAvailableQuantity(
|
||||
inventory[0].id,
|
||||
channel.locations
|
||||
)
|
||||
const quantity =
|
||||
await productVariantInventoryService.getVariantQuantityFromVariantInventoryItems(
|
||||
variantInventoryItems,
|
||||
channel.id
|
||||
)
|
||||
|
||||
return {
|
||||
channel_name: channel.name as string,
|
||||
|
||||
@@ -179,7 +179,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
||||
* @param variantId variant id
|
||||
* @returns variant inventory items for the variant id
|
||||
*/
|
||||
private async listByVariant(
|
||||
public async listByVariant(
|
||||
variantId: string | string[]
|
||||
): Promise<ProductVariantInventoryItem[]> {
|
||||
const variantInventoryRepo = this.activeManager_.getRepository(
|
||||
@@ -615,30 +615,11 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
||||
// first get all inventory items required for a variant
|
||||
const variantInventory = await this.listByVariant(variant.id)
|
||||
|
||||
const salesChannelInventoryServiceTx =
|
||||
this.salesChannelInventoryService_.withTransaction(
|
||||
this.activeManager_
|
||||
variant.inventory_quantity =
|
||||
await this.getVariantQuantityFromVariantInventoryItems(
|
||||
variantInventory,
|
||||
salesChannelId
|
||||
)
|
||||
// the inventory quantity of the variant should be equal to the inventory
|
||||
// item with the smallest stock, adjusted for quantity required to fulfill
|
||||
// the given variant
|
||||
variant.inventory_quantity = Math.min(
|
||||
...(await Promise.all(
|
||||
variantInventory.map(async (variantInventory) => {
|
||||
// get the total available quantity for the given sales channel
|
||||
// divided by the required quantity to account for how many of the
|
||||
// variant we can fulfill at the current time. Take the minimum we
|
||||
// can fulfill and set that as quantity
|
||||
return (
|
||||
// eslint-disable-next-line max-len
|
||||
(await salesChannelInventoryServiceTx.retrieveAvailableItemQuantity(
|
||||
salesChannelId,
|
||||
variantInventory.inventory_item_id
|
||||
)) / variantInventory.required_quantity
|
||||
)
|
||||
})
|
||||
))
|
||||
)
|
||||
|
||||
return variant
|
||||
})
|
||||
@@ -664,6 +645,53 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quantity of a variant from a list of variantInventoryItems
|
||||
* The inventory quantity of the variant should be equal to the inventory
|
||||
* item with the smallest stock, adjusted for quantity required to fulfill
|
||||
* the given variant.
|
||||
*
|
||||
* @param variantInventoryItems List of inventoryItems for a given variant, These must all be for the same variant
|
||||
* @param channelId Sales channel id to fetch availability for
|
||||
* @returns The available quantity of the variant from the inventoryItems
|
||||
*/
|
||||
async getVariantQuantityFromVariantInventoryItems(
|
||||
variantInventoryItems: ProductVariantInventoryItem[],
|
||||
channelId: string
|
||||
): Promise<number> {
|
||||
const variantItemsAreMixed = variantInventoryItems.some(
|
||||
(inventoryItem) =>
|
||||
inventoryItem.variant_id !== variantInventoryItems[0].variant_id
|
||||
)
|
||||
|
||||
if (variantInventoryItems.length && variantItemsAreMixed) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"All variant inventory items must belong to the same variant"
|
||||
)
|
||||
}
|
||||
|
||||
return Math.min(
|
||||
...(await Promise.all(
|
||||
variantInventoryItems.map(async (variantInventory) => {
|
||||
// get the total available quantity for the given sales channel
|
||||
// divided by the required quantity to account for how many of the
|
||||
// variant we can fulfill at the current time. Take the minimum we
|
||||
// can fulfill and set that as quantity
|
||||
return (
|
||||
// eslint-disable-next-line max-len
|
||||
(await this.salesChannelInventoryService_
|
||||
.withTransaction(this.activeManager_)
|
||||
.retrieveAvailableItemQuantity(
|
||||
channelId,
|
||||
variantInventory.inventory_item_id
|
||||
)) / variantInventory.required_quantity
|
||||
)
|
||||
})
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ProductVariantInventoryService
|
||||
|
||||
Reference in New Issue
Block a user