Feat/bulk operations for inventory service (#4503)
* initial push * bulk delete reservations by location ids * add method to interface (not implemented yet) * bulk update * delete reservations by location id bulk * add create bulk for inventory item * refactor attach inventory item method * add changeset * verbose false * method override instead of multiple methods * change up method signature * redo changes when updating interface * update createInventoryLevel method * rename variables * fix feedback * return correct string array when emitting event * refactor inventory service * redo order changes * snapshot * move prep methods
This commit is contained in:
@@ -8,7 +8,10 @@ const adminSeeder = require("../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { simpleProductFactory } = require("../../../factories")
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleOrderFactory,
|
||||
} = require("../../../factories")
|
||||
const adminHeaders = { headers: { Authorization: "Bearer test_token" } }
|
||||
|
||||
describe("Inventory Items endpoints", () => {
|
||||
@@ -127,7 +130,7 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
it("Create, update and delete inventory location level", async () => {
|
||||
it("should create, update and delete the inventory location levels", async () => {
|
||||
const api = useApi()
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
|
||||
@@ -181,7 +184,7 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Update inventory item", async () => {
|
||||
it("should update the inventory item", async () => {
|
||||
const api = useApi()
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
|
||||
@@ -207,7 +210,7 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("fails to update location level to negative quantity", async () => {
|
||||
it("should fail to update the location level to negative quantity", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
@@ -241,7 +244,7 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("Retrieve an inventory item", async () => {
|
||||
it("should retrieve the inventory item", async () => {
|
||||
const api = useApi()
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
|
||||
@@ -312,7 +315,7 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("Creates an inventory item using the api", async () => {
|
||||
it("should create the inventory item using the api", async () => {
|
||||
const product = await simpleProductFactory(dbConnection, {})
|
||||
|
||||
const api = useApi()
|
||||
@@ -362,7 +365,7 @@ describe("Inventory Items endpoints", () => {
|
||||
expect(variantInventoryRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("lists location levels based on id param constraint", async () => {
|
||||
it("should list the location levels based on id param constraint", async () => {
|
||||
const api = useApi()
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
|
||||
@@ -397,8 +400,9 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe("List inventory items", () => {
|
||||
it("Lists inventory items with location", async () => {
|
||||
it("should list inventory items with location", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await api.post(
|
||||
@@ -451,7 +455,7 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Lists inventory items", async () => {
|
||||
it("should list the inventory items", async () => {
|
||||
const api = useApi()
|
||||
const inventoryItemId = inventoryItems[0].id
|
||||
|
||||
@@ -539,21 +543,21 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Lists inventory items searching by title, description and sku", async () => {
|
||||
it("should list the inventory items searching by title, description and sku", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
await Promise.all([
|
||||
inventoryService.createInventoryItem({
|
||||
await inventoryService.createInventoryItems([
|
||||
{
|
||||
title: "Test Item",
|
||||
}),
|
||||
inventoryService.createInventoryItem({
|
||||
},
|
||||
{
|
||||
description: "Test Desc",
|
||||
}),
|
||||
inventoryService.createInventoryItem({
|
||||
},
|
||||
{
|
||||
sku: "Test Sku",
|
||||
}),
|
||||
},
|
||||
])
|
||||
|
||||
const response = await api.get(
|
||||
@@ -585,7 +589,7 @@ describe("Inventory Items endpoints", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("When deleting an inventory item it removes associated levels and reservations", async () => {
|
||||
it("should remove associated levels and reservations when deleting an inventory item", async () => {
|
||||
const api = useApi()
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
@@ -650,7 +654,7 @@ describe("Inventory Items endpoints", () => {
|
||||
expect(inventoryLevelCountPostDelete).toEqual(0)
|
||||
})
|
||||
|
||||
it("When deleting an inventory item it removes the product variants associated to it", async () => {
|
||||
it("should remove the product variant associations when deleting an inventory item", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await simpleProductFactory(
|
||||
@@ -727,5 +731,344 @@ describe("Inventory Items endpoints", () => {
|
||||
)
|
||||
).toHaveLength(1)
|
||||
})
|
||||
|
||||
describe("inventory service", () => {
|
||||
let inventoryService
|
||||
let productInventoryService
|
||||
|
||||
beforeAll(() => {
|
||||
inventoryService = appContainer.resolve("inventoryService")
|
||||
productInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
})
|
||||
|
||||
it("should bulk remove the inventory items", async () => {
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const ids = items.map((item) => item.id)
|
||||
|
||||
expect(ids).not.toBeFalsy()
|
||||
|
||||
await inventoryService.deleteInventoryItem(ids)
|
||||
|
||||
const [emptyItems] = await inventoryService.listInventoryItems()
|
||||
expect(emptyItems).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should bulk create the inventory levels", async () => {
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
])
|
||||
|
||||
const [levels] = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(levels).toHaveLength(2)
|
||||
expect(levels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should bulk create the inventory items", async () => {
|
||||
const items = [
|
||||
{
|
||||
sku: "sku-1",
|
||||
},
|
||||
{
|
||||
sku: "sku-2",
|
||||
},
|
||||
]
|
||||
const createdItems = await inventoryService.createInventoryItems(items)
|
||||
|
||||
expect(createdItems).toHaveLength(2)
|
||||
expect(createdItems).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
sku: "sku-1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sku: "sku-2",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should bulk delete the inventory levels by location id", async () => {
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
])
|
||||
|
||||
await inventoryService.deleteInventoryItemLevelByLocationId([
|
||||
locationId,
|
||||
location2Id,
|
||||
])
|
||||
|
||||
const [levels] = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(levels).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should bulk delete the inventory levels by location id", async () => {
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
])
|
||||
|
||||
await inventoryService.deleteInventoryItemLevelByLocationId([
|
||||
locationId,
|
||||
location2Id,
|
||||
])
|
||||
|
||||
const [levels] = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(levels).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should fail to create the reservations with invalid configuration", async () => {
|
||||
const order = await simpleOrderFactory(dbConnection, {
|
||||
line_items: [
|
||||
{ id: "line-item-1", quantity: 1 },
|
||||
{ id: "line-item-2", quantity: 1 },
|
||||
],
|
||||
})
|
||||
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
const error = await inventoryService
|
||||
.createReservationItems([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
line_item_id: "line-item-1",
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
line_item_id: "line-item-2",
|
||||
quantity: 1,
|
||||
},
|
||||
])
|
||||
.catch((err) => err)
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`Item ${itemId} is not stocked at location ${locationId}, Item ${itemId} is not stocked at location ${locationId}`
|
||||
)
|
||||
})
|
||||
|
||||
it("should bulk delete the reservations by their line item ids", async () => {
|
||||
const order = await simpleOrderFactory(dbConnection, {
|
||||
line_items: [
|
||||
{ id: "line-item-1", quantity: 1 },
|
||||
{ id: "line-item-2", quantity: 1 },
|
||||
],
|
||||
})
|
||||
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
await inventoryService.createReservationItems([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
line_item_id: "line-item-1",
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
line_item_id: "line-item-2",
|
||||
quantity: 1,
|
||||
},
|
||||
])
|
||||
|
||||
const [reservations] = await inventoryService.listReservationItems({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(reservations).toHaveLength(2)
|
||||
|
||||
await inventoryService.deleteReservationItemsByLineItem([
|
||||
"line-item-1",
|
||||
"line-item-2",
|
||||
])
|
||||
|
||||
const [deletedReservations] =
|
||||
await inventoryService.listReservationItems({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(deletedReservations).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should bulk delete the reservations by their location id", async () => {
|
||||
const order = await simpleOrderFactory(dbConnection, {
|
||||
line_items: [
|
||||
{ id: "line-item-1", quantity: 1 },
|
||||
{ id: "line-item-2", quantity: 1 },
|
||||
],
|
||||
})
|
||||
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
await inventoryService.createReservationItems([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
line_item_id: "line-item-1",
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
line_item_id: "line-item-2",
|
||||
quantity: 1,
|
||||
},
|
||||
])
|
||||
|
||||
const [reservations] = await inventoryService.listReservationItems({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(reservations).toHaveLength(2)
|
||||
|
||||
await inventoryService.deleteReservationItemByLocationId([
|
||||
location2Id,
|
||||
locationId,
|
||||
])
|
||||
|
||||
const [deletedReservations] =
|
||||
await inventoryService.listReservationItems({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(deletedReservations).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should bulk update the inventory levels", async () => {
|
||||
const [items] = await inventoryService.listInventoryItems()
|
||||
|
||||
const itemId = items[0].id
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 10,
|
||||
})
|
||||
|
||||
const levels = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
expect(levels).toHaveLength(2)
|
||||
|
||||
await inventoryService.updateInventoryLevels([
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 20,
|
||||
},
|
||||
{
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 25,
|
||||
},
|
||||
])
|
||||
|
||||
const [updatedLevels] = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: itemId,
|
||||
})
|
||||
|
||||
expect(updatedLevels).toHaveLength(2)
|
||||
expect(updatedLevels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: itemId,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 20,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
inventory_item_id: itemId,
|
||||
location_id: location2Id,
|
||||
stocked_quantity: 25,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -119,7 +119,7 @@ describe("/store/carts", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("creates an order from a draft order and doesn't adjust reservations", async () => {
|
||||
it("should create the order from a draft order and shouldn't adjust reservations", async () => {
|
||||
const api = useApi()
|
||||
let inventoryItem = await api.get(
|
||||
`/admin/inventory-items/${invItemId}`,
|
||||
|
||||
@@ -542,11 +542,9 @@ 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 inventoryService.updateInventoryLevel(invItemId, locationId, {
|
||||
stocked_quantity: 2,
|
||||
})
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||
locationId: locationId,
|
||||
@@ -663,11 +661,9 @@ describe("/store/carts", () => {
|
||||
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 inventoryService.updateInventoryLevel(invItemId, locationId, {
|
||||
stocked_quantity: 3,
|
||||
})
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 3, {
|
||||
locationId: locationId,
|
||||
@@ -737,11 +733,9 @@ describe("/store/carts", () => {
|
||||
stocked_quantity: 3,
|
||||
})
|
||||
|
||||
const a = await inventoryService.updateInventoryLevel(
|
||||
invItemId,
|
||||
locationId,
|
||||
{ stocked_quantity: 3 }
|
||||
)
|
||||
await inventoryService.updateInventoryLevel(invItemId, locationId, {
|
||||
stocked_quantity: 3,
|
||||
})
|
||||
|
||||
await prodVarInventoryService.reserveQuantity(variantId, 1, {
|
||||
locationId: locationId,
|
||||
|
||||
@@ -71,6 +71,7 @@ describe("Get products", () => {
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem.id
|
||||
|
||||
@@ -71,6 +71,7 @@ describe("Get variant", () => {
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variantId,
|
||||
invItem.id
|
||||
|
||||
@@ -82,6 +82,7 @@ describe("List Variants", () => {
|
||||
invItem = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku",
|
||||
})
|
||||
|
||||
const invItemId = invItem.id
|
||||
|
||||
await prodVarInventoryService.attachInventoryItem(variantId, invItem.id)
|
||||
|
||||
@@ -296,6 +296,104 @@ describe("Inventory Module", () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe("updateInventoryLevel", () => {
|
||||
it("should pass along the correct context when doing a bulk update", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
let error
|
||||
try {
|
||||
await inventoryService.updateInventoryLevels(
|
||||
[
|
||||
{
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 25,
|
||||
reserved_quantity: 4,
|
||||
incoming_quantity: 10,
|
||||
},
|
||||
],
|
||||
{
|
||||
transactionManager: {},
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toEqual("manager.getRepository is not a function")
|
||||
})
|
||||
|
||||
it("should pass along the correct context when doing a single update", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: "sku_1",
|
||||
origin_country: "CH",
|
||||
mid_code: "mid code",
|
||||
material: "lycra",
|
||||
weight: 100,
|
||||
length: 200,
|
||||
height: 50,
|
||||
width: 50,
|
||||
metadata: {
|
||||
abc: 123,
|
||||
},
|
||||
hs_code: "hs_code 123",
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: "location_123",
|
||||
stocked_quantity: 50,
|
||||
reserved_quantity: 0,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
|
||||
let error
|
||||
try {
|
||||
await inventoryService.updateInventoryLevel(
|
||||
inventoryItem.id,
|
||||
"location_123",
|
||||
{
|
||||
stocked_quantity: 25,
|
||||
reserved_quantity: 4,
|
||||
incoming_quantity: 10,
|
||||
},
|
||||
{
|
||||
transactionManager: {},
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toEqual("manager.getRepository is not a function")
|
||||
})
|
||||
})
|
||||
|
||||
it("deleteInventoryLevel", async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
const path = require("path")
|
||||
|
||||
const { bootstrapApp } = require("../../../helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
const {
|
||||
simpleProductVariantFactory,
|
||||
simpleProductFactory,
|
||||
} = require("../../factories")
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
describe("Inventory Module", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let express
|
||||
|
||||
let invItem1
|
||||
let invItem2
|
||||
let variant1
|
||||
let variant2
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
const { container, app, port } = await bootstrapApp({ cwd, verbose: false })
|
||||
appContainer = container
|
||||
|
||||
express = app.listen(port, (err) => {
|
||||
process.send(port)
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
express.close()
|
||||
})
|
||||
|
||||
describe("ProductVariantInventoryService", () => {
|
||||
describe("attachInventoryItem", () => {
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
|
||||
const { variants } = await simpleProductFactory(dbConnection, {
|
||||
variants: [{}, {}],
|
||||
})
|
||||
|
||||
variant1 = variants[0]
|
||||
variant2 = variants[1]
|
||||
|
||||
invItem1 = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-1",
|
||||
})
|
||||
|
||||
invItem2 = await inventoryService.createInventoryItem({
|
||||
sku: "test-sku-2",
|
||||
})
|
||||
})
|
||||
|
||||
it("should attach the single item with spread params", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
const variantItems = await pviService.listByVariant(variant1.id)
|
||||
expect(variantItems.length).toEqual(1)
|
||||
expect(variantItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should attach multiple inventory items and variants at once", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
|
||||
const variantItems = await pviService.listByVariant([
|
||||
variant1.id,
|
||||
variant2.id,
|
||||
])
|
||||
expect(variantItems.length).toEqual(2)
|
||||
expect(variantItems).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: variant2.id,
|
||||
inventory_item_id: invItem2.id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should skip existing attachments when attaching a singular inventory item", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
const variantItems = await pviService.listByVariant(variant1.id)
|
||||
expect(variantItems.length).toEqual(1)
|
||||
expect(variantItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should skip existing attachments when attaching multiple inventory items in bulk", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id)
|
||||
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
|
||||
const variantItems = await pviService.listByVariant([
|
||||
variant1.id,
|
||||
variant2.id,
|
||||
])
|
||||
expect(variantItems.length).toEqual(2)
|
||||
expect(variantItems).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
inventory_item_id: invItem1.id,
|
||||
variant_id: variant1.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: variant2.id,
|
||||
inventory_item_id: invItem2.id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to attach items when a single item has a required_quantity below 1", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem(variant1.id, invItem1.id, 0)
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`"requiredQuantity" must be greater than 0, the following entries are invalid: ${JSON.stringify(
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
requiredQuantity: 0,
|
||||
}
|
||||
)}`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
requiredQuantity: 0,
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`"requiredQuantity" must be greater than 0, the following entries are invalid: ${JSON.stringify(
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
requiredQuantity: 0,
|
||||
}
|
||||
)}`
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to attach items when attaching to a non-existing variant", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem("variant1.id", invItem1.id)
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Variants not found for the following ids: variant1.id`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: "variant1.id",
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: invItem2.id,
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Variants not found for the following ids: variant1.id`
|
||||
)
|
||||
})
|
||||
it("should fail to attach items when attaching to a non-existing inventory item", async () => {
|
||||
const pviService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
let e
|
||||
try {
|
||||
await pviService.attachInventoryItem(variant1.id, "invItem1.id")
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Inventory items not found for the following ids: invItem1.id`
|
||||
)
|
||||
|
||||
try {
|
||||
await pviService.attachInventoryItem([
|
||||
{
|
||||
variantId: variant1.id,
|
||||
inventoryItemId: invItem1.id,
|
||||
},
|
||||
{
|
||||
variantId: variant2.id,
|
||||
inventoryItemId: "invItem2.id",
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
e = err
|
||||
}
|
||||
|
||||
expect(e.message).toEqual(
|
||||
`Inventory items not found for the following ids: invItem2.id`
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user