Fix/adjust reservations correctly (#3474)
**What** - Adjust reservations correctly according to the following heuristic: adjustment by addition: (i.e. positive quantity adjustment passed to the adjustment method) - if a reservation for the line-item in the location exists add quantity to that - if not create a new reservation adjustment by subtraction: - if a reservation with the exact quantity exists, delete it and return - if a reservation with a greater quantity exists, subtract from it and return - otherwise delete from reservations until a reservation with greater quantity than the remaining is found and adjust that with the remaining quantity OR there are no more reservations Fixes CORE-1247
This commit is contained in:
@@ -278,7 +278,7 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it.skip("Creates an inventory item using the api", async () => {
|
||||
it("Creates an inventory item using the api", async () => {
|
||||
const product = await simpleProductFactory(dbConnection, {})
|
||||
|
||||
const api = useApi()
|
||||
@@ -316,20 +316,14 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
|
||||
expect(variantInventoryRes.data).toEqual({
|
||||
variant: {
|
||||
variant: expect.objectContaining({
|
||||
id: variantId,
|
||||
inventory: [
|
||||
expect.objectContaining({
|
||||
...inventoryItemCreateRes.data.inventory_item,
|
||||
}),
|
||||
],
|
||||
sales_channel_availability: [
|
||||
expect.objectContaining({
|
||||
available_quantity: 0,
|
||||
channel_name: "Default Sales Channel",
|
||||
}),
|
||||
],
|
||||
},
|
||||
}),
|
||||
})
|
||||
expect(variantInventoryRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
@@ -295,7 +295,7 @@ describe("/store/carts", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Adjusts reservations on successful fulfillment with reservation", async () => {
|
||||
it("Adjusts reservation on successful fulfillment with reservation", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||
@@ -350,6 +350,273 @@ describe("/store/carts", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Deletes multiple reservations on successful fulfillment with reservation", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const a = await inventoryService.updateInventoryLevel(
|
||||
invItemId,
|
||||
locationId,
|
||||
{ stocked_quantity: 2 }
|
||||
)
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||
locationId: locationId,
|
||||
lineItemId: order.items[0].id,
|
||||
})
|
||||
|
||||
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: 2,
|
||||
reserved_quantity: 2,
|
||||
available_quantity: 0,
|
||||
})
|
||||
)
|
||||
|
||||
const fulfillmentRes = await api.post(
|
||||
`/admin/orders/${order.id}/fulfillment`,
|
||||
{
|
||||
items: [{ item_id: lineItemId, quantity: 2 }],
|
||||
location_id: locationId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(fulfillmentRes.status).toBe(200)
|
||||
expect(fulfillmentRes.data.order.fulfillment_status).toBe("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("Deletes single reservation on successful fulfillment with partial reservation", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await inventoryService.updateInventoryLevel(invItemId, locationId, {
|
||||
stocked_quantity: 2,
|
||||
})
|
||||
|
||||
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: 2,
|
||||
reserved_quantity: 1,
|
||||
available_quantity: 1,
|
||||
})
|
||||
)
|
||||
|
||||
const fulfillmentRes = await api.post(
|
||||
`/admin/orders/${order.id}/fulfillment`,
|
||||
{
|
||||
items: [{ item_id: lineItemId, quantity: 2 }],
|
||||
location_id: locationId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(fulfillmentRes.status).toBe(200)
|
||||
expect(fulfillmentRes.data.order.fulfillment_status).toBe("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 single reservation on successful fulfillment with over-reserved line item", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const a = await inventoryService.updateInventoryLevel(
|
||||
invItemId,
|
||||
locationId,
|
||||
{ stocked_quantity: 3 }
|
||||
)
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 3, {
|
||||
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: 3,
|
||||
reserved_quantity: 3,
|
||||
available_quantity: 0,
|
||||
})
|
||||
)
|
||||
|
||||
const fulfillmentRes = await api.post(
|
||||
`/admin/orders/${order.id}/fulfillment`,
|
||||
{
|
||||
items: [{ item_id: lineItemId, quantity: 2 }],
|
||||
location_id: locationId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(fulfillmentRes.status).toBe(200)
|
||||
expect(fulfillmentRes.data.order.fulfillment_status).toBe("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(1)
|
||||
expect(reservations.data.reservations).toEqual([
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
}),
|
||||
])
|
||||
expect(inventoryItem.data.inventory_item.location_levels[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
stocked_quantity: 1,
|
||||
reserved_quantity: 1,
|
||||
available_quantity: 0,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("Prioritizes adjusting reservations at the chosen location", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const sl = await stockLocationService.create({
|
||||
name: "test-location 1",
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: invItemId,
|
||||
location_id: sl.id,
|
||||
stocked_quantity: 3,
|
||||
})
|
||||
|
||||
const a = await inventoryService.updateInventoryLevel(
|
||||
invItemId,
|
||||
locationId,
|
||||
{ stocked_quantity: 3 }
|
||||
)
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||
locationId: locationId,
|
||||
lineItemId: order.items[0].id,
|
||||
})
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 2, {
|
||||
locationId: sl.id,
|
||||
lineItemId: order.items[0].id,
|
||||
})
|
||||
|
||||
let inventoryItem = await api.get(
|
||||
`/admin/inventory-items/${invItemId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(inventoryItem.data.inventory_item.location_levels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
location_id: locationId,
|
||||
stocked_quantity: 3,
|
||||
reserved_quantity: 1,
|
||||
available_quantity: 2,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
location_id: sl.id,
|
||||
stocked_quantity: 3,
|
||||
reserved_quantity: 2,
|
||||
available_quantity: 1,
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const fulfillmentRes = await api.post(
|
||||
`/admin/orders/${order.id}/fulfillment`,
|
||||
{
|
||||
items: [{ item_id: lineItemId, quantity: 2 }],
|
||||
location_id: locationId,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(fulfillmentRes.status).toBe(200)
|
||||
expect(fulfillmentRes.data.order.fulfillment_status).toBe("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(1)
|
||||
expect(reservations.data.reservations).toEqual([
|
||||
expect.objectContaining({
|
||||
quantity: 1,
|
||||
location_id: sl.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("increases stocked quantity when return is received at location", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user