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:
Frane Polić
2024-11-04 09:04:58 +01:00
committed by GitHub
parent a0034a0fef
commit 1889180319
6 changed files with 205 additions and 18 deletions
@@ -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 },