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:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa-js": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix(medusa): Adjust reservations correctly
|
||||||
@@ -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 product = await simpleProductFactory(dbConnection, {})
|
||||||
|
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
@@ -316,20 +316,14 @@ describe("Inventory Items endpoints", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(variantInventoryRes.data).toEqual({
|
expect(variantInventoryRes.data).toEqual({
|
||||||
variant: {
|
variant: expect.objectContaining({
|
||||||
id: variantId,
|
id: variantId,
|
||||||
inventory: [
|
inventory: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
...inventoryItemCreateRes.data.inventory_item,
|
...inventoryItemCreateRes.data.inventory_item,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
sales_channel_availability: [
|
}),
|
||||||
expect.objectContaining({
|
|
||||||
available_quantity: 0,
|
|
||||||
channel_name: "Default Sales Channel",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
expect(variantInventoryRes.status).toEqual(200)
|
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()
|
const api = useApi()
|
||||||
|
|
||||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
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 () => {
|
it("increases stocked quantity when return is received at location", async () => {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ export default class InventoryService
|
|||||||
* Deletes a reservation item
|
* Deletes a reservation item
|
||||||
* @param reservationItemId - the id of the reservation item to delete
|
* @param reservationItemId - the id of the reservation item to delete
|
||||||
*/
|
*/
|
||||||
async deleteReservationItem(reservationItemId: string): Promise<void> {
|
async deleteReservationItem(reservationItemId: string | string[]): Promise<void> {
|
||||||
return await this.reservationItemService_
|
return await this.reservationItemService_
|
||||||
.withTransaction(this.activeManager_)
|
.withTransaction(this.activeManager_)
|
||||||
.delete(reservationItemId)
|
.delete(reservationItemId)
|
||||||
|
|||||||
@@ -277,21 +277,29 @@ export default class ReservationItemService extends TransactionBaseService {
|
|||||||
* Deletes a reservation item by id.
|
* Deletes a reservation item by id.
|
||||||
* @param reservationItemId - the id of the reservation item to delete.
|
* @param reservationItemId - the id of the reservation item to delete.
|
||||||
*/
|
*/
|
||||||
async delete(reservationItemId: string): Promise<void> {
|
async delete(reservationItemId: string | string[]): Promise<void> {
|
||||||
await this.atomicPhase_(async (manager) => {
|
const ids = Array.isArray(reservationItemId)
|
||||||
|
? reservationItemId
|
||||||
|
: [reservationItemId]
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
const itemRepository = manager.getRepository(ReservationItem)
|
const itemRepository = manager.getRepository(ReservationItem)
|
||||||
const item = await this.retrieve(reservationItemId)
|
|
||||||
|
|
||||||
await Promise.all([
|
const items = await this.list({ id: ids })
|
||||||
itemRepository.softRemove({ id: reservationItemId }),
|
|
||||||
this.inventoryLevelService_
|
await itemRepository.softRemove(items)
|
||||||
.withTransaction(manager)
|
|
||||||
.adjustReservedQuantity(
|
const inventoryServiceTx =
|
||||||
|
this.inventoryLevelService_.withTransaction(manager)
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
items.map(async (item) => {
|
||||||
|
return inventoryServiceTx.adjustReservedQuantity(
|
||||||
item.inventory_item_id,
|
item.inventory_item_id,
|
||||||
item.location_id,
|
item.location_id,
|
||||||
item.quantity * -1
|
item.quantity * -1
|
||||||
),
|
)
|
||||||
])
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await this.eventBusService_
|
await this.eventBusService_
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
|
|||||||
@@ -74,8 +74,6 @@ export const createVariantTransaction = async (
|
|||||||
productVariantInventoryService,
|
productVariantInventoryService,
|
||||||
} = dependencies
|
} = dependencies
|
||||||
|
|
||||||
const inventoryServiceTx = inventoryService?.withTransaction(manager)
|
|
||||||
|
|
||||||
const productVariantInventoryServiceTx =
|
const productVariantInventoryServiceTx =
|
||||||
productVariantInventoryService.withTransaction(manager)
|
productVariantInventoryService.withTransaction(manager)
|
||||||
|
|
||||||
@@ -96,7 +94,7 @@ export const createVariantTransaction = async (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return await inventoryServiceTx!.createInventoryItem({
|
return await inventoryService!.createInventoryItem({
|
||||||
sku: variant.sku,
|
sku: variant.sku,
|
||||||
origin_country: variant.origin_country,
|
origin_country: variant.origin_country,
|
||||||
hs_code: variant.hs_code,
|
hs_code: variant.hs_code,
|
||||||
@@ -111,7 +109,7 @@ export const createVariantTransaction = async (
|
|||||||
|
|
||||||
async function removeInventoryItem(inventoryItem: InventoryItemDTO) {
|
async function removeInventoryItem(inventoryItem: InventoryItemDTO) {
|
||||||
if (inventoryItem) {
|
if (inventoryItem) {
|
||||||
await inventoryServiceTx!.deleteInventoryItem(inventoryItem.id)
|
await inventoryService!.deleteInventoryItem(inventoryItem.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
} from "../../../../types/inventory"
|
} from "../../../../types/inventory"
|
||||||
import ProductVariantInventoryService from "../../../../services/product-variant-inventory"
|
import ProductVariantInventoryService from "../../../../services/product-variant-inventory"
|
||||||
import {
|
import {
|
||||||
SalesChannelInventoryService,
|
|
||||||
SalesChannelLocationService,
|
SalesChannelLocationService,
|
||||||
SalesChannelService,
|
SalesChannelService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
@@ -76,8 +75,7 @@ export default async (req, res) => {
|
|||||||
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||||
"salesChannelLocationService"
|
"salesChannelLocationService"
|
||||||
)
|
)
|
||||||
const salesChannelInventoryService: SalesChannelInventoryService =
|
|
||||||
req.scope.resolve("salesChannelInventoryService")
|
|
||||||
const channelService: SalesChannelService = req.scope.resolve(
|
const channelService: SalesChannelService = req.scope.resolve(
|
||||||
"salesChannelService"
|
"salesChannelService"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export interface IInventoryService {
|
|||||||
|
|
||||||
deleteReservationItemsByLineItem(lineItemId: string): Promise<void>
|
deleteReservationItemsByLineItem(lineItemId: string): Promise<void>
|
||||||
|
|
||||||
deleteReservationItem(reservationItemId: string): Promise<void>
|
deleteReservationItem(reservationItemId: string | string[]): Promise<void>
|
||||||
|
|
||||||
deleteInventoryItem(inventoryItemId: string): Promise<void>
|
deleteInventoryItem(inventoryItemId: string): Promise<void>
|
||||||
|
|
||||||
|
|||||||
@@ -115,13 +115,11 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
const hasInventory = await Promise.all(
|
const hasInventory = await Promise.all(
|
||||||
variantInventory.map(async (inventoryPart) => {
|
variantInventory.map(async (inventoryPart) => {
|
||||||
const itemQuantity = inventoryPart.required_quantity * quantity
|
const itemQuantity = inventoryPart.required_quantity * quantity
|
||||||
return await this.inventoryService_
|
return await this.inventoryService_.confirmInventory(
|
||||||
.withTransaction(this.activeManager_)
|
inventoryPart.inventory_item_id,
|
||||||
.confirmInventory(
|
locationIds,
|
||||||
inventoryPart.inventory_item_id,
|
itemQuantity
|
||||||
locationIds,
|
)
|
||||||
itemQuantity
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -253,11 +251,9 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Verify that item exists
|
// Verify that item exists
|
||||||
await this.inventoryService_
|
await this.inventoryService_.retrieveInventoryItem(inventoryItemId, {
|
||||||
.withTransaction(this.activeManager_)
|
select: ["id"],
|
||||||
.retrieveInventoryItem(inventoryItemId, {
|
})
|
||||||
select: ["id"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const variantInventoryRepo = this.activeManager_.getRepository(
|
const variantInventoryRepo = this.activeManager_.getRepository(
|
||||||
ProductVariantInventoryItem
|
ProductVariantInventoryItem
|
||||||
@@ -392,14 +388,12 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
variantInventory.map(async (inventoryPart) => {
|
variantInventory.map(async (inventoryPart) => {
|
||||||
const itemQuantity = inventoryPart.required_quantity * quantity
|
const itemQuantity = inventoryPart.required_quantity * quantity
|
||||||
return await this.inventoryService_
|
return await this.inventoryService_.createReservationItem({
|
||||||
.withTransaction(this.activeManager_)
|
...toReserve,
|
||||||
.createReservationItem({
|
location_id: locationId as string,
|
||||||
...toReserve,
|
inventory_item_id: inventoryPart.inventory_item_id,
|
||||||
location_id: locationId as string,
|
quantity: itemQuantity,
|
||||||
inventory_item_id: inventoryPart.inventory_item_id,
|
})
|
||||||
quantity: itemQuantity,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -435,9 +429,15 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [reservations, reservationCount] = await this.inventoryService_
|
if (quantity > 0) {
|
||||||
.withTransaction(this.activeManager_)
|
throw new MedusaError(
|
||||||
.listReservationItems(
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
"You can only reduce reservation quantities using adjustReservationsQuantityByLineItem. If you wish to reserve more use update or create."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [reservations, reservationCount] =
|
||||||
|
await this.inventoryService_.listReservationItems(
|
||||||
{
|
{
|
||||||
line_item_id: lineItemId,
|
line_item_id: lineItemId,
|
||||||
},
|
},
|
||||||
@@ -446,33 +446,56 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
reservations.sort((a, _) => {
|
||||||
|
if (a.location_id === locationId) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
if (reservationCount) {
|
if (reservationCount) {
|
||||||
let reservation = reservations[0]
|
const inventoryItems = await this.listByVariant(variantId)
|
||||||
|
const productVariantInventory = inventoryItems[0]
|
||||||
|
|
||||||
reservation =
|
const deltaUpdate = Math.abs(
|
||||||
reservations.find(
|
quantity * productVariantInventory.required_quantity
|
||||||
(r) => r.location_id === locationId && r.quantity >= quantity
|
|
||||||
) ?? reservation
|
|
||||||
|
|
||||||
const productVariantInventory = await this.retrieve(
|
|
||||||
reservation.inventory_item_id,
|
|
||||||
variantId
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const reservationQtyUpdate =
|
const exactReservation = reservations.find(
|
||||||
reservation.quantity +
|
(r) => r.quantity === deltaUpdate && r.location_id === locationId
|
||||||
quantity * productVariantInventory.required_quantity
|
)
|
||||||
|
if (exactReservation) {
|
||||||
|
await this.inventoryService_.deleteReservationItem(exactReservation.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (reservationQtyUpdate === 0) {
|
let remainingQuantity = deltaUpdate
|
||||||
await this.inventoryService_
|
|
||||||
.withTransaction(this.activeManager_)
|
const reservationsToDelete: ReservationItemDTO[] = []
|
||||||
.deleteReservationItem(reservation.id)
|
let reservationToUpdate: ReservationItemDTO | null = null
|
||||||
} else {
|
for (const reservation of reservations) {
|
||||||
await this.inventoryService_
|
if (reservation.quantity <= remainingQuantity) {
|
||||||
.withTransaction(this.activeManager_)
|
remainingQuantity -= reservation.quantity
|
||||||
.updateReservationItem(reservation.id, {
|
reservationsToDelete.push(reservation)
|
||||||
quantity: reservationQtyUpdate,
|
} else {
|
||||||
})
|
reservationToUpdate = reservation
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reservationsToDelete.length) {
|
||||||
|
await this.inventoryService_.deleteReservationItem(
|
||||||
|
reservationsToDelete.map((r) => r.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reservationToUpdate) {
|
||||||
|
await this.inventoryService_.updateReservationItem(
|
||||||
|
reservationToUpdate.id,
|
||||||
|
{
|
||||||
|
quantity: reservationToUpdate.quantity - remainingQuantity,
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,9 +575,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.inventoryService_
|
await this.inventoryService_.deleteReservationItemsByLineItem(lineItemId)
|
||||||
.withTransaction(this.activeManager_)
|
|
||||||
.deleteReservationItemsByLineItem(lineItemId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -584,26 +605,24 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
inventory_quantity: variant.inventory_quantity + quantity,
|
inventory_quantity: variant.inventory_quantity + quantity,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
const variantInventory = await this.listByVariant(variantId)
|
|
||||||
|
|
||||||
if (variantInventory.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
variantInventory.map(async (inventoryPart) => {
|
|
||||||
const itemQuantity = inventoryPart.required_quantity * quantity
|
|
||||||
return await this.inventoryService_
|
|
||||||
.withTransaction(this.activeManager_)
|
|
||||||
.adjustInventory(
|
|
||||||
inventoryPart.inventory_item_id,
|
|
||||||
locationId,
|
|
||||||
itemQuantity
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const variantInventory = await this.listByVariant(variantId)
|
||||||
|
|
||||||
|
if (variantInventory.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
variantInventory.map(async (inventoryPart) => {
|
||||||
|
const itemQuantity = inventoryPart.required_quantity * quantity
|
||||||
|
return await this.inventoryService_.adjustInventory(
|
||||||
|
inventoryPart.inventory_item_id,
|
||||||
|
locationId,
|
||||||
|
itemQuantity
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setVariantAvailability(
|
async setVariantAvailability(
|
||||||
@@ -685,6 +704,9 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const salesChannelInventoryServiceTx =
|
||||||
|
this.salesChannelInventoryService_.withTransaction(this.activeManager_)
|
||||||
|
|
||||||
return Math.min(
|
return Math.min(
|
||||||
...(await Promise.all(
|
...(await Promise.all(
|
||||||
variantInventoryItems.map(async (variantInventory) => {
|
variantInventoryItems.map(async (variantInventory) => {
|
||||||
@@ -694,12 +716,10 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
// can fulfill and set that as quantity
|
// can fulfill and set that as quantity
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
(await this.salesChannelInventoryService_
|
(await salesChannelInventoryServiceTx.retrieveAvailableItemQuantity(
|
||||||
.withTransaction(this.activeManager_)
|
channelId,
|
||||||
.retrieveAvailableItemQuantity(
|
variantInventory.inventory_item_id
|
||||||
channelId,
|
)) / variantInventory.required_quantity
|
||||||
variantInventory.inventory_item_id
|
|
||||||
)) / variantInventory.required_quantity
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user