fix(inventory): delete reservation item inventory level required quantity (#13633)
Fixes #13625 --- > [!NOTE] > Ensure `reserved_quantity` is adjusted when deleting reservation items via `deleteReservationItems`, with new integration tests covering deletion and inventory updates. > > - **Inventory Module Service (`packages/modules/inventory/src/services/inventory-module.ts`)**: > - Add `deleteReservationItems` API (and transactional `_` variant) that hard-deletes reservations, then updates related inventory levels via `adjustInventoryLevelsForReservationsDeletion`. > - Wire through event emission/manager decorators consistent with existing patterns. > - **Integration Tests (`packages/modules/inventory/integration-tests/__tests__/inventory-module-service.spec.ts`)**: > - Add tests for `deleteReservationItems` to verify deletion by id and `reserved_quantity` adjustments on inventory levels. > - Minor import update to include `ReservationItemDTO`. > - **Changeset**: > - Patch release note for `@medusajs/inventory` documenting the fix. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ac6641a9ec9543115504407f708f81bd427c3444. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup>
This commit is contained in:
5
.changeset/tiny-trains-drum.md
Normal file
5
.changeset/tiny-trains-drum.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/inventory": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(inventory): Adjust inventory level required_quantity when reservation item is deleted through deleteReservationItems from module service
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IInventoryService, InventoryItemDTO } from "@medusajs/framework/types"
|
import { IInventoryService, InventoryItemDTO, ReservationItemDTO } from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
BigNumber,
|
BigNumber,
|
||||||
CommonEvents,
|
CommonEvents,
|
||||||
@@ -552,6 +552,105 @@ moduleIntegrationTestRunner<IInventoryService>({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("deleteReservationItems", () => {
|
||||||
|
let inventoryItem: InventoryItemDTO
|
||||||
|
let reservationItems: ReservationItemDTO[]
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
inventoryItem = await service.createInventoryItems({
|
||||||
|
sku: "test-sku",
|
||||||
|
origin_country: "test-country",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createInventoryLevels([
|
||||||
|
{
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-1",
|
||||||
|
stocked_quantity: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-2",
|
||||||
|
stocked_quantity: 2,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
reservationItems = await service.createReservationItems([
|
||||||
|
{
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-1",
|
||||||
|
quantity: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-2",
|
||||||
|
quantity: 2,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should delete reservation items by id", async () => {
|
||||||
|
const reservationItemsPreDeleted = await service.listReservationItems(
|
||||||
|
{
|
||||||
|
id: reservationItems.map((item) => item.id),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(reservationItemsPreDeleted).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-1",
|
||||||
|
quantity: 2,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
inventory_item_id: inventoryItem.id,
|
||||||
|
location_id: "location-2",
|
||||||
|
quantity: 2,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(reservationItemsPreDeleted).toHaveLength(2)
|
||||||
|
|
||||||
|
await service.deleteReservationItems(
|
||||||
|
reservationItems.map((item) => item.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
const reservationItemsPostDeleted =
|
||||||
|
await service.listReservationItems({
|
||||||
|
id: reservationItems.map((item) => item.id),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(reservationItemsPostDeleted).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should adjust inventory levels accordingly when removing reservations by id", async () => {
|
||||||
|
const reservationItem = (await service.listReservationItems({ location_id: "location-1"}))[0]
|
||||||
|
|
||||||
|
const inventoryLevelBefore =
|
||||||
|
await service.retrieveInventoryLevelByItemAndLocation(
|
||||||
|
inventoryItem.id,
|
||||||
|
"location-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryLevelBefore).toEqual(
|
||||||
|
expect.objectContaining({ reserved_quantity: 2 })
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.deleteReservationItems(reservationItem.id)
|
||||||
|
|
||||||
|
const inventoryLevelAfter =
|
||||||
|
await service.retrieveInventoryLevelByItemAndLocation(
|
||||||
|
inventoryItem.id,
|
||||||
|
"location-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(inventoryLevelAfter).toEqual(
|
||||||
|
expect.objectContaining({ reserved_quantity: 0 })
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("deleteReservationItemsByLineItem", () => {
|
describe("deleteReservationItemsByLineItem", () => {
|
||||||
let inventoryItem: InventoryItemDTO
|
let inventoryItem: InventoryItemDTO
|
||||||
|
|
||||||
|
|||||||
@@ -711,6 +711,32 @@ export default class InventoryModuleService
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager()
|
||||||
|
@EmitEvents()
|
||||||
|
// @ts-expect-error
|
||||||
|
async deleteReservationItems(
|
||||||
|
ids: string | string[],
|
||||||
|
@MedusaContext() context: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.deleteReservationItems_(ids, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager()
|
||||||
|
protected async deleteReservationItems_(
|
||||||
|
ids: string | string[],
|
||||||
|
@MedusaContext() context: Context = {}
|
||||||
|
): Promise<void> {
|
||||||
|
const reservations: InventoryTypes.ReservationItemDTO[] =
|
||||||
|
await this.reservationItemService_.list({ id: ids }, {}, context)
|
||||||
|
|
||||||
|
await super.deleteReservationItems(ids, context)
|
||||||
|
|
||||||
|
await this.adjustInventoryLevelsForReservationsDeletion(
|
||||||
|
reservations,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@InjectManager()
|
@InjectManager()
|
||||||
@EmitEvents()
|
@EmitEvents()
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
Reference in New Issue
Block a user