feat(inventory,dashboard,types,core-flows,js-sdk,medusa): Improve inventory UX (#10630)
* feat(dashboard): Add UI for bulk editing inventory stock (#10556) * progress * cleanup types * add changeset * fix 0 values * format schema * add delete event and allow copy/pasting enabled for some fields * add response types * add tests * work on fixing setValue behaviour * cleanup toggle logic * add loading state * format schema * add support for bidirectional actions in DataGrid and update Checkbox and RadioGroup * update lock * lint * fix 404 * address feedback * update cursor on bidirectional select
This commit is contained in:
@@ -12,7 +12,7 @@ medusaIntegrationTestRunner({
|
||||
let inventoryItem2
|
||||
let stockLocation1
|
||||
let stockLocation2
|
||||
|
||||
let stockLocation3
|
||||
beforeEach(async () => {
|
||||
await createAdminUser(dbConnection, adminHeaders, getContainer())
|
||||
|
||||
@@ -24,6 +24,10 @@ medusaIntegrationTestRunner({
|
||||
await api.post(`/admin/stock-locations`, { name: "loc2" }, adminHeaders)
|
||||
).data.stock_location
|
||||
|
||||
stockLocation3 = (
|
||||
await api.post(`/admin/stock-locations`, { name: "loc3" }, adminHeaders)
|
||||
).data.stock_location
|
||||
|
||||
inventoryItem1 = (
|
||||
await api.post(
|
||||
`/admin/inventory-items`,
|
||||
@@ -122,9 +126,152 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/inventory-items/:id/location-levels/batch", () => {
|
||||
describe("POST /admin/inventory-items/location-levels/batch", () => {
|
||||
let locationLevel1
|
||||
let locationLevel2
|
||||
|
||||
beforeEach(async () => {
|
||||
await api.post(
|
||||
const seed = await api.post(
|
||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
|
||||
{
|
||||
create: [
|
||||
{
|
||||
location_id: stockLocation1.id,
|
||||
stocked_quantity: 0,
|
||||
},
|
||||
{
|
||||
location_id: stockLocation2.id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
locationLevel1 = seed.data.created[0]
|
||||
locationLevel2 = seed.data.created[1]
|
||||
})
|
||||
|
||||
it("should batch update the inventory levels", async () => {
|
||||
const result = await api.post(
|
||||
`/admin/inventory-items/location-levels/batch`,
|
||||
{
|
||||
update: [
|
||||
{
|
||||
location_id: stockLocation1.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
{
|
||||
location_id: stockLocation2.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data).toEqual(
|
||||
expect.objectContaining({
|
||||
updated: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
location_id: stockLocation1.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 10,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
location_id: stockLocation2.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 20,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should batch create the inventory levels", async () => {
|
||||
const result = await api.post(
|
||||
`/admin/inventory-items/location-levels/batch`,
|
||||
{
|
||||
create: [
|
||||
{
|
||||
location_id: stockLocation3.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data).toEqual(
|
||||
expect.objectContaining({
|
||||
created: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
location_id: stockLocation3.id,
|
||||
inventory_item_id: inventoryItem1.id,
|
||||
stocked_quantity: 10,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should batch delete the inventory levels when stocked quantity is 0 and force is false", async () => {
|
||||
const result = await api.post(
|
||||
`/admin/inventory-items/location-levels/batch`,
|
||||
{ delete: [locationLevel1.id] },
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data).toEqual(
|
||||
expect.objectContaining({
|
||||
deleted: [locationLevel1.id],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should not delete the inventory levels when stocked quantity is greater than 0 and force is false", async () => {
|
||||
const error = await api
|
||||
.post(
|
||||
`/admin/inventory-items/location-levels/batch`,
|
||||
{ delete: [locationLevel2.id] },
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data).toEqual({
|
||||
type: "not_allowed",
|
||||
message: `Cannot remove Inventory Levels for ${stockLocation2.id} because there are stocked items at the locations. Use force flag to delete anyway.`,
|
||||
})
|
||||
})
|
||||
|
||||
it("should delete the inventory levels when stocked quantity is greater than 0 and force is true", async () => {
|
||||
const result = await api.post(
|
||||
`/admin/inventory-items/location-levels/batch`,
|
||||
{ delete: [locationLevel2.id], force: true },
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data).toEqual(
|
||||
expect.objectContaining({
|
||||
deleted: [locationLevel2.id],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/inventory-items/:id/location-levels/batch", () => {
|
||||
let locationLevel1
|
||||
|
||||
beforeEach(async () => {
|
||||
const seed = await api.post(
|
||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels`,
|
||||
{
|
||||
location_id: stockLocation1.id,
|
||||
@@ -132,6 +279,8 @@ medusaIntegrationTestRunner({
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
locationLevel1 = seed.data.inventory_item.location_levels[0]
|
||||
})
|
||||
|
||||
it("should delete an inventory location level and create a new one", async () => {
|
||||
@@ -139,7 +288,8 @@ medusaIntegrationTestRunner({
|
||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
|
||||
{
|
||||
create: [{ location_id: "location_2" }],
|
||||
delete: [stockLocation1.id],
|
||||
delete: [locationLevel1.id],
|
||||
force: true,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
@@ -154,7 +304,7 @@ medusaIntegrationTestRunner({
|
||||
expect(levelsListResult.data.inventory_levels).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("should not delete an inventory location level when there is stocked items", async () => {
|
||||
it("should not delete an inventory location level when there is stocked items without force", async () => {
|
||||
await api.post(
|
||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels/${stockLocation1.id}`,
|
||||
{ stocked_quantity: 10 },
|
||||
@@ -164,7 +314,7 @@ medusaIntegrationTestRunner({
|
||||
const { response } = await api
|
||||
.post(
|
||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
|
||||
{ delete: [stockLocation1.id] },
|
||||
{ delete: [locationLevel1.id] },
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e)
|
||||
@@ -172,7 +322,7 @@ medusaIntegrationTestRunner({
|
||||
expect(response.status).toEqual(400)
|
||||
expect(response.data).toEqual({
|
||||
type: "not_allowed",
|
||||
message: `Cannot remove Inventory Levels for ${stockLocation1.id} because there are stocked or reserved items at the locations`,
|
||||
message: `Cannot remove Inventory Levels for ${stockLocation1.id} because there are stocked items at the locations. Use force flag to delete anyway.`,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user