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:
@@ -22,7 +22,7 @@ export async function createOrderSeeder({
|
||||
container: MedusaContainer
|
||||
productOverride?: AdminProduct
|
||||
stockChannelOverride?: AdminStockLocation
|
||||
additionalProducts?: AdminProduct[]
|
||||
additionalProducts?: { variant_id: string; quantity: number }[]
|
||||
inventoryItemOverride?: AdminInventoryItem
|
||||
}) {
|
||||
const publishableKey = await generatePublishableKey(container)
|
||||
@@ -195,10 +195,7 @@ export async function createOrderSeeder({
|
||||
sales_channel_id: salesChannel.id,
|
||||
items: [
|
||||
{ quantity: 1, variant_id: product.variants[0].id },
|
||||
...(additionalProducts || []).map((p) => ({
|
||||
quantity: 1,
|
||||
variant_id: p.variants?.[0]?.id,
|
||||
})),
|
||||
...(additionalProducts || []),
|
||||
],
|
||||
},
|
||||
storeHeaders
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
|
||||
import { createOrderSeeder } from "../../fixtures/order"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
jest.setTimeout(300000)
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
let order, seeder
|
||||
let order, seeder, inventoryItemOverride3, productOverride3
|
||||
|
||||
beforeEach(async () => {
|
||||
const container = getContainer()
|
||||
@@ -82,6 +82,14 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
inventoryItemOverride3 = (
|
||||
await api.post(
|
||||
`/admin/inventory-items`,
|
||||
{ sku: "test-variant-3", requires_shipping: false },
|
||||
adminHeaders
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
await api.post(
|
||||
`/admin/inventory-items/${inventoryItemOverride2.id}/location-levels`,
|
||||
{
|
||||
@@ -91,6 +99,15 @@ medusaIntegrationTestRunner({
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
await api.post(
|
||||
`/admin/inventory-items/${inventoryItemOverride3.id}/location-levels`,
|
||||
{
|
||||
location_id: stockChannelOverride.id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const productOverride2 = (
|
||||
await api.post(
|
||||
"/admin/products",
|
||||
@@ -127,11 +144,50 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
).data.product
|
||||
|
||||
productOverride3 = (
|
||||
await api.post(
|
||||
"/admin/products",
|
||||
{
|
||||
title: `Test fixture 3`,
|
||||
options: [
|
||||
{ title: "size", values: ["large", "small"] },
|
||||
{ title: "color", values: ["green"] },
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant 3",
|
||||
sku: "test-variant-3",
|
||||
inventory_items: [
|
||||
{
|
||||
inventory_item_id: inventoryItemOverride3.id,
|
||||
required_quantity: 1,
|
||||
},
|
||||
],
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
},
|
||||
],
|
||||
options: {
|
||||
size: "small",
|
||||
color: "green",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.product
|
||||
|
||||
seeder = await createOrderSeeder({
|
||||
api,
|
||||
container: getContainer(),
|
||||
productOverride,
|
||||
additionalProducts: [productOverride2],
|
||||
additionalProducts: [
|
||||
{ variant_id: productOverride2.variants[0].id, quantity: 1 },
|
||||
{ variant_id: productOverride3.variants[0].id, quantity: 3 },
|
||||
],
|
||||
stockChannelOverride,
|
||||
inventoryItemOverride,
|
||||
})
|
||||
@@ -157,6 +213,105 @@ medusaIntegrationTestRunner({
|
||||
expect(response2.orders[0].email).toEqual(userEmail)
|
||||
})
|
||||
|
||||
it("should update stock levels correctly when creating partial fulfillment on an order", async () => {
|
||||
const orderItemId = order.items.find(
|
||||
(i) => i.variant_id === productOverride3.variants[0].id
|
||||
).id
|
||||
|
||||
let iitem = (
|
||||
await api.get(
|
||||
`/admin/inventory-items/${inventoryItemOverride3.id}?fields=stocked_quantity,reserved_quantity`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
expect(iitem.stocked_quantity).toBe(10)
|
||||
expect(iitem.reserved_quantity).toBe(3)
|
||||
|
||||
await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments`,
|
||||
{
|
||||
location_id: seeder.stockLocation.id,
|
||||
items: [{ id: orderItemId, quantity: 1 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
iitem = (
|
||||
await api.get(
|
||||
`/admin/inventory-items/${inventoryItemOverride3.id}?fields=stocked_quantity,reserved_quantity`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
expect(iitem.stocked_quantity).toBe(9)
|
||||
expect(iitem.reserved_quantity).toBe(2)
|
||||
|
||||
await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments`,
|
||||
{
|
||||
location_id: seeder.stockLocation.id,
|
||||
items: [{ id: orderItemId, quantity: 1 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
iitem = (
|
||||
await api.get(
|
||||
`/admin/inventory-items/${inventoryItemOverride3.id}?fields=stocked_quantity,reserved_quantity`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
expect(iitem.stocked_quantity).toBe(8)
|
||||
expect(iitem.reserved_quantity).toBe(1)
|
||||
|
||||
const {
|
||||
data: { order: fulfillableOrder },
|
||||
} = await api.post(
|
||||
`/admin/orders/${order.id}/fulfillments?fields=fulfillments.id`,
|
||||
{
|
||||
location_id: seeder.stockLocation.id,
|
||||
items: [{ id: orderItemId, quantity: 1 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(fulfillableOrder.fulfillments).toHaveLength(3)
|
||||
|
||||
iitem = (
|
||||
await api.get(
|
||||
`/admin/inventory-items/${inventoryItemOverride3.id}?fields=stocked_quantity,reserved_quantity`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.inventory_item
|
||||
|
||||
expect(iitem.stocked_quantity).toBe(7)
|
||||
expect(iitem.reserved_quantity).toBe(0)
|
||||
})
|
||||
|
||||
it("should throw if trying to fulfillment more items than it is reserved", async () => {
|
||||
const orderItemId = order.items.find(
|
||||
(i) => i.variant_id === productOverride3.variants[0].id
|
||||
).id
|
||||
|
||||
const res = await api
|
||||
.post(
|
||||
`/admin/orders/${order.id}/fulfillments`,
|
||||
{
|
||||
location_id: seeder.stockLocation.id,
|
||||
items: [{ id: orderItemId, quantity: 5 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(res.response.status).toBe(400)
|
||||
expect(res.response.data.message).toBe(
|
||||
`Quantity to fulfill exceeds the reserved quantity for the item: ${orderItemId}`
|
||||
)
|
||||
})
|
||||
|
||||
it("should only create fulfillments grouped by shipping requirement", async () => {
|
||||
const {
|
||||
response: { data },
|
||||
|
||||
Reference in New Issue
Block a user