fix(medusa): update create fulfillment flow (#3172)
* update create fulfillment flow * move transaction service creation close to where it's used * integration tests * fix feedback * use transformBody * add changeset
This commit is contained in:
1 parent
7738525401
commit
5eb61fa0ef
6 files changed
+367
-46
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add inventory management to create-fulfillment flow
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
const { bootstrapApp } = require("../../../../helpers/bootstrap-app")
|
||||||
|
const { initDb, useDb } = require("../../../../helpers/use-db")
|
||||||
|
const { setPort, useApi } = require("../../../../helpers/use-api")
|
||||||
|
|
||||||
|
const adminSeeder = require("../../../helpers/admin-seeder")
|
||||||
|
const cartSeeder = require("../../../helpers/cart-seeder")
|
||||||
|
const { simpleProductFactory } = require("../../../../api/factories")
|
||||||
|
const { simpleSalesChannelFactory } = require("../../../../api/factories")
|
||||||
|
const {
|
||||||
|
simpleOrderFactory,
|
||||||
|
simpleRegionFactory,
|
||||||
|
} = require("../../../factories")
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
const adminHeaders = { headers: { Authorization: "Bearer test_token" } }
|
||||||
|
|
||||||
|
describe("/store/carts", () => {
|
||||||
|
let express
|
||||||
|
let appContainer
|
||||||
|
let dbConnection
|
||||||
|
|
||||||
|
const doAfterEach = async () => {
|
||||||
|
const db = useDb()
|
||||||
|
return await db.teardown()
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd })
|
||||||
|
const { container, app, port } = await bootstrapApp({ cwd })
|
||||||
|
appContainer = container
|
||||||
|
|
||||||
|
setPort(port)
|
||||||
|
express = app.listen(port, (err) => {
|
||||||
|
process.send(port)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
express.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
const db = useDb()
|
||||||
|
return await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("POST /store/carts/:id", () => {
|
||||||
|
let order
|
||||||
|
let locationId
|
||||||
|
let invItemId
|
||||||
|
let variantId
|
||||||
|
let prodVarInventoryService
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
prodVarInventoryService = appContainer.resolve(
|
||||||
|
"productVariantInventoryService"
|
||||||
|
)
|
||||||
|
const inventoryService = appContainer.resolve("inventoryService")
|
||||||
|
const stockLocationService = appContainer.resolve("stockLocationService")
|
||||||
|
const salesChannelLocationService = appContainer.resolve(
|
||||||
|
"salesChannelLocationService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const r = await simpleRegionFactory(dbConnection, {})
|
||||||
|
await simpleSalesChannelFactory(dbConnection, {
|
||||||
|
id: "test-channel",
|
||||||
|
is_default: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
const product = await simpleProductFactory(dbConnection, {
|
||||||
|
id: "product1",
|
||||||
|
sales_channels: [{ id: "test-channel" }],
|
||||||
|
})
|
||||||
|
variantId = product.variants[0].id
|
||||||
|
|
||||||
|
const sl = await stockLocationService.create({ name: "test-location" })
|
||||||
|
|
||||||
|
locationId = sl.id
|
||||||
|
|
||||||
|
await salesChannelLocationService.associateLocation(
|
||||||
|
"test-channel",
|
||||||
|
locationId
|
||||||
|
)
|
||||||
|
|
||||||
|
const invItem = await inventoryService.createInventoryItem({
|
||||||
|
sku: "test-sku",
|
||||||
|
})
|
||||||
|
invItemId = invItem.id
|
||||||
|
|
||||||
|
await prodVarInventoryService.attachInventoryItem(variantId, invItem.id)
|
||||||
|
|
||||||
|
await inventoryService.createInventoryLevel({
|
||||||
|
inventory_item_id: invItem.id,
|
||||||
|
location_id: locationId,
|
||||||
|
stocked_quantity: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { id: orderId } = await simpleOrderFactory(dbConnection, {
|
||||||
|
sales_channel: "test-channel",
|
||||||
|
line_items: [
|
||||||
|
{
|
||||||
|
variant_id: variantId,
|
||||||
|
quantity: 2,
|
||||||
|
id: "line-item-id",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
shipping_option: {
|
||||||
|
region_id: r.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderRes = await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||||
|
order = orderRes.data.order
|
||||||
|
|
||||||
|
const inventoryItem = await api.get(
|
||||||
|
`/admin/inventory-items/${invItem.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
stocked_quantity: 1,
|
||||||
|
reserved_quantity: 0,
|
||||||
|
available_quantity: 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fulfillments", () => {
|
||||||
|
const lineItemId = "line-item-id"
|
||||||
|
it("Adjusts reservations on successful fulfillment with reservation", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||||
|
locationId: locationId,
|
||||||
|
lineItemId: order.items[0].id,
|
||||||
|
})
|
||||||
|
|
||||||
|
let inventoryItem = await api.get(
|
||||||
|
`/admin/inventory-items/${invItemId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
stocked_quantity: 1,
|
||||||
|
reserved_quantity: 1,
|
||||||
|
available_quantity: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const fulfillmentRes = await api.post(
|
||||||
|
`/admin/orders/${order.id}/fulfillment`,
|
||||||
|
{
|
||||||
|
items: [{ item_id: lineItemId, quantity: 1 }],
|
||||||
|
location_id: locationId,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(fulfillmentRes.status).toBe(200)
|
||||||
|
expect(fulfillmentRes.data.order.fulfillment_status).toBe(
|
||||||
|
"partially_fulfilled"
|
||||||
|
)
|
||||||
|
|
||||||
|
inventoryItem = await api.get(
|
||||||
|
`/admin/inventory-items/${invItemId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const reservations = await api.get(
|
||||||
|
`/admin/reservations?inventory_item_id[]=${invItemId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(reservations.data.reservations.length).toBe(0)
|
||||||
|
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
stocked_quantity: 0,
|
||||||
|
reserved_quantity: 0,
|
||||||
|
available_quantity: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adjusts inventory levels on successful fulfillment without reservation", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const fulfillmentRes = await api.post(
|
||||||
|
`/admin/orders/${order.id}/fulfillment`,
|
||||||
|
{
|
||||||
|
items: [{ item_id: lineItemId, quantity: 1 }],
|
||||||
|
location_id: locationId,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
expect(fulfillmentRes.status).toBe(200)
|
||||||
|
expect(fulfillmentRes.data.order.fulfillment_status).toBe(
|
||||||
|
"partially_fulfilled"
|
||||||
|
)
|
||||||
|
|
||||||
|
const inventoryItem = await api.get(
|
||||||
|
`/admin/inventory-items/${invItemId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
stocked_quantity: 0,
|
||||||
|
reserved_quantity: 0,
|
||||||
|
available_quantity: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Fails to create fulfillment if there is not enough inventory at the fulfillment location", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const err = await api
|
||||||
|
.post(
|
||||||
|
`/admin/orders/${order.id}/fulfillment`,
|
||||||
|
{
|
||||||
|
items: [{ item_id: lineItemId, quantity: 2 }],
|
||||||
|
location_id: locationId,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err.response.status).toBe(400)
|
||||||
|
expect(err.response.data).toEqual({
|
||||||
|
type: "not_allowed",
|
||||||
|
message: `Insufficient stock for item: ${order.items[0].title}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
const inventoryItem = await api.get(
|
||||||
|
`/admin/inventory-items/${invItemId}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
stocked_quantity: 1,
|
||||||
|
reserved_quantity: 0,
|
||||||
|
available_quantity: 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -5,6 +5,9 @@ import {
|
|||||||
Order,
|
Order,
|
||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
FulfillmentStatus,
|
FulfillmentStatus,
|
||||||
|
SalesChannel,
|
||||||
|
Discount,
|
||||||
|
isString,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -24,6 +27,11 @@ import {
|
|||||||
ShippingMethodFactoryData,
|
ShippingMethodFactoryData,
|
||||||
simpleShippingMethodFactory,
|
simpleShippingMethodFactory,
|
||||||
} from "./simple-shipping-method-factory"
|
} from "./simple-shipping-method-factory"
|
||||||
|
import {
|
||||||
|
SalesChannelFactoryData,
|
||||||
|
simpleSalesChannelFactory,
|
||||||
|
} from "../../api/factories"
|
||||||
|
import { isDefined } from "medusa-core-utils"
|
||||||
|
|
||||||
export type OrderFactoryData = {
|
export type OrderFactoryData = {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -33,6 +41,7 @@ export type OrderFactoryData = {
|
|||||||
email?: string | null
|
email?: string | null
|
||||||
currency_code?: string
|
currency_code?: string
|
||||||
tax_rate?: number | null
|
tax_rate?: number | null
|
||||||
|
sales_channel?: string | SalesChannelFactoryData
|
||||||
line_items?: LineItemFactoryData[]
|
line_items?: LineItemFactoryData[]
|
||||||
discounts?: DiscountFactoryData[]
|
discounts?: DiscountFactoryData[]
|
||||||
shipping_address?: AddressFactoryData
|
shipping_address?: AddressFactoryData
|
||||||
@@ -72,15 +81,14 @@ export const simpleOrderFactory = async (
|
|||||||
})
|
})
|
||||||
const customer = await manager.save(customerToSave)
|
const customer = await manager.save(customerToSave)
|
||||||
|
|
||||||
let discounts = []
|
let discounts: Discount[] = []
|
||||||
if (typeof data.discounts !== "undefined") {
|
if (typeof data.discounts !== "undefined") {
|
||||||
discounts = await Promise.all(
|
discounts = await Promise.all(
|
||||||
data.discounts.map((d) => simpleDiscountFactory(connection, d, seed))
|
data.discounts.map((d) => simpleDiscountFactory(connection, d, seed))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = data.id || `simple-order-${Math.random() * 1000}`
|
const id = data.id || `simple-order-${Math.random() * 1000}`
|
||||||
const toSave = manager.create(Order, {
|
const toCreate: Partial<Order> = {
|
||||||
id,
|
id,
|
||||||
discounts,
|
discounts,
|
||||||
payment_status: data.payment_status ?? PaymentStatus.AWAITING,
|
payment_status: data.payment_status ?? PaymentStatus.AWAITING,
|
||||||
@@ -92,16 +100,44 @@ export const simpleOrderFactory = async (
|
|||||||
currency_code: currencyCode,
|
currency_code: currencyCode,
|
||||||
tax_rate: taxRate,
|
tax_rate: taxRate,
|
||||||
shipping_address_id: address.id,
|
shipping_address_id: address.id,
|
||||||
})
|
}
|
||||||
|
|
||||||
const order = await manager.save(toSave)
|
let sc_id
|
||||||
|
if (isDefined(data.sales_channel)) {
|
||||||
|
let sc
|
||||||
|
|
||||||
|
if (isString(data.sales_channel)) {
|
||||||
|
sc = await manager.findOne(SalesChannel, {
|
||||||
|
where: { id: data.sales_channel },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sc) {
|
||||||
|
sc = await simpleSalesChannelFactory(
|
||||||
|
connection,
|
||||||
|
isString(data.sales_channel)
|
||||||
|
? { id: data.sales_channel }
|
||||||
|
: data.sales_channel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_id = sc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sc_id) {
|
||||||
|
toCreate.sales_channel_id = sc_id
|
||||||
|
}
|
||||||
|
|
||||||
|
const toSave = manager.create(Order, toCreate)
|
||||||
|
|
||||||
|
const order = await manager.save(Order, toSave)
|
||||||
|
|
||||||
const shippingMethods = data.shipping_methods || []
|
const shippingMethods = data.shipping_methods || []
|
||||||
for (const sm of shippingMethods) {
|
for (const sm of shippingMethods) {
|
||||||
await simpleShippingMethodFactory(connection, { ...sm, order_id: order.id })
|
await simpleShippingMethodFactory(connection, { ...sm, order_id: order.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = data.line_items
|
const items = data.line_items || []
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
await simpleLineItemFactory(connection, { ...item, order_id: id })
|
await simpleLineItemFactory(connection, { ...item, order_id: id })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import regionRoutes from "./regions"
|
|||||||
import reservationRoutes from "./reservations"
|
import reservationRoutes from "./reservations"
|
||||||
import returnReasonRoutes from "./return-reasons"
|
import returnReasonRoutes from "./return-reasons"
|
||||||
import returnRoutes from "./returns"
|
import returnRoutes from "./returns"
|
||||||
|
import reservationRoutes from "./reservations"
|
||||||
import salesChannelRoutes from "./sales-channels"
|
import salesChannelRoutes from "./sales-channels"
|
||||||
import shippingOptionRoutes from "./shipping-options"
|
import shippingOptionRoutes from "./shipping-options"
|
||||||
import shippingProfileRoutes from "./shipping-profiles"
|
import shippingProfileRoutes from "./shipping-profiles"
|
||||||
@@ -101,6 +102,7 @@ export default (app, container, config) => {
|
|||||||
reservationRoutes(route)
|
reservationRoutes(route)
|
||||||
returnReasonRoutes(route)
|
returnReasonRoutes(route)
|
||||||
returnRoutes(route)
|
returnRoutes(route)
|
||||||
|
reservationRoutes(route)
|
||||||
salesChannelRoutes(route)
|
salesChannelRoutes(route)
|
||||||
shippingOptionRoutes(route, featureFlagRouter)
|
shippingOptionRoutes(route, featureFlagRouter)
|
||||||
shippingProfileRoutes(route)
|
shippingProfileRoutes(route)
|
||||||
|
|||||||
@@ -97,39 +97,49 @@ import { FindParams } from "../../../../types/common"
|
|||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
|
||||||
const validated = req.validatedBody
|
const { validatedBody } = req as {
|
||||||
|
validatedBody: AdminPostOrdersOrderFulfillmentsReq
|
||||||
|
}
|
||||||
|
|
||||||
const orderService: OrderService = req.scope.resolve("orderService")
|
const orderService: OrderService = req.scope.resolve("orderService")
|
||||||
const pvInventoryService: ProductVariantInventoryService = req.scope.resolve(
|
const pvInventoryService: ProductVariantInventoryService = req.scope.resolve(
|
||||||
"productVariantInventoryService"
|
"productVariantInventoryService"
|
||||||
)
|
)
|
||||||
|
|
||||||
const manager: EntityManager = req.scope.resolve("manager")
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
await manager.transaction(async (transactionManager) => {
|
await manager.transaction(async (transactionManager) => {
|
||||||
const { fulfillments: existingFulfillments } = await orderService
|
const orderServiceTx = orderService.withTransaction(transactionManager)
|
||||||
.withTransaction(transactionManager)
|
|
||||||
.retrieve(id, {
|
const { fulfillments: existingFulfillments } =
|
||||||
|
await orderServiceTx.retrieve(id, {
|
||||||
relations: ["fulfillments"],
|
relations: ["fulfillments"],
|
||||||
})
|
})
|
||||||
const existingFulfillmentMap = new Map(
|
const existingFulfillmentMap = new Map(
|
||||||
existingFulfillments.map((fulfillment) => [fulfillment.id, fulfillment])
|
existingFulfillments.map((fulfillment) => [fulfillment.id, fulfillment])
|
||||||
)
|
)
|
||||||
|
|
||||||
const { fulfillments } = await orderService
|
await orderServiceTx.createFulfillment(id, validatedBody.items, {
|
||||||
.withTransaction(transactionManager)
|
metadata: validatedBody.metadata,
|
||||||
.createFulfillment(id, validated.items, {
|
no_notification: validatedBody.no_notification,
|
||||||
metadata: validated.metadata,
|
})
|
||||||
no_notification: validated.no_notification,
|
|
||||||
|
if (validatedBody.location_id) {
|
||||||
|
const { fulfillments } = await orderServiceTx.retrieve(id, {
|
||||||
|
relations: [
|
||||||
|
"fulfillments",
|
||||||
|
"fulfillments.items",
|
||||||
|
"fulfillments.items.item",
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const pvInventoryServiceTx =
|
const pvInventoryServiceTx =
|
||||||
pvInventoryService.withTransaction(transactionManager)
|
pvInventoryService.withTransaction(transactionManager)
|
||||||
|
|
||||||
if (validated.location_id) {
|
|
||||||
await updateInventoryAndReservations(
|
await updateInventoryAndReservations(
|
||||||
fulfillments.filter((f) => !existingFulfillmentMap[f.id]),
|
fulfillments.filter((f) => !existingFulfillmentMap[f.id]),
|
||||||
{
|
{
|
||||||
inventoryService: pvInventoryServiceTx,
|
inventoryService: pvInventoryServiceTx,
|
||||||
locationId: validated.location_id,
|
locationId: validatedBody.location_id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -151,33 +161,35 @@ const updateInventoryAndReservations = async (
|
|||||||
) => {
|
) => {
|
||||||
const { inventoryService, locationId } = context
|
const { inventoryService, locationId } = context
|
||||||
|
|
||||||
fulfillments.map(async ({ items }) => {
|
await Promise.all(
|
||||||
await inventoryService.validateInventoryAtLocation(
|
fulfillments.map(async ({ items }) => {
|
||||||
items.map(({ item, quantity }) => ({ ...item, quantity } as LineItem)),
|
await inventoryService.validateInventoryAtLocation(
|
||||||
locationId
|
items.map(({ item, quantity }) => ({ ...item, quantity } as LineItem)),
|
||||||
)
|
locationId
|
||||||
|
)
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
items.map(async ({ item, quantity }) => {
|
items.map(async ({ item, quantity }) => {
|
||||||
if (!item.variant_id) {
|
if (!item.variant_id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await inventoryService.adjustReservationsQuantityByLineItem(
|
await inventoryService.adjustReservationsQuantityByLineItem(
|
||||||
item.id,
|
item.id,
|
||||||
item.variant_id,
|
item.variant_id,
|
||||||
locationId,
|
locationId,
|
||||||
-quantity
|
-quantity
|
||||||
)
|
)
|
||||||
|
|
||||||
await inventoryService.adjustInventory(
|
await inventoryService.adjustInventory(
|
||||||
item.variant_id,
|
item.variant_id,
|
||||||
locationId,
|
locationId,
|
||||||
-quantity
|
-quantity
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const reservationQtyUpdate =
|
const reservationQtyUpdate =
|
||||||
reservation.quantity -
|
reservation.quantity +
|
||||||
quantity * productVariantInventory.required_quantity
|
quantity * productVariantInventory.required_quantity
|
||||||
|
|
||||||
if (reservationQtyUpdate === 0) {
|
if (reservationQtyUpdate === 0) {
|
||||||
@@ -494,11 +494,11 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (const inventoryLevel of inventoryLevels) {
|
for (const inventoryLevel of inventoryLevels) {
|
||||||
const pvInventoryItem = pviMap[inventoryLevel.inventory_item_id]
|
const pvInventoryItem = pviMap.get(inventoryLevel.inventory_item_id)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!pvInventoryItem ||
|
!pvInventoryItem ||
|
||||||
pvInventoryItem.quantity * item.quantity >
|
pvInventoryItem.required_quantity * item.quantity >
|
||||||
inventoryLevel.stocked_quantity
|
inventoryLevel.stocked_quantity
|
||||||
) {
|
) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
|
|||||||
Reference in new issue
Block a user