Feat(core-flows, medusa, types): Add delete location level api-v2 endpoint (#6727)
* add delete inventory level endpoint * add changeset * rename step * rename step
This commit is contained in:
7
.changeset/shaggy-suits-drop.md
Normal file
7
.changeset/shaggy-suits-drop.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@medusajs/core-flows": patch
|
||||
"@medusajs/medusa": patch
|
||||
"@medusajs/types": patch
|
||||
---
|
||||
|
||||
feat(medusa, core-flows, types): Add delete-location-level api-v2 endpoints
|
||||
@@ -229,6 +229,61 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
|
||||
describe("Delete inventory levels", () => {
|
||||
const locationId = "loc_1"
|
||||
let inventoryItem
|
||||
|
||||
beforeEach(async () => {
|
||||
inventoryItem = await service.create({
|
||||
sku: "MY_SKU",
|
||||
})
|
||||
|
||||
await service.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
stocked_quantity: 10,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should delete an inventory location level without reservations", async () => {
|
||||
const result = await api.delete(
|
||||
`/admin/inventory-items/${inventoryItem.id}/location-levels/${locationId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(result.status).toEqual(200)
|
||||
expect(result.data).toEqual({
|
||||
id: expect.any(String),
|
||||
object: "inventory-level",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
|
||||
it("should fail delete an inventory location level with reservations", async () => {
|
||||
await service.createReservationItems({
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: locationId,
|
||||
quantity: 5,
|
||||
})
|
||||
|
||||
let error
|
||||
|
||||
await api
|
||||
.delete(
|
||||
`/admin/inventory-items/${inventoryItem.id}/location-levels/${locationId}`,
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => (error = e))
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data).toEqual({
|
||||
type: "not_allowed",
|
||||
message: `Cannot remove Inventory Level ${inventoryItem.id} at Location ${locationId} because there are reservations at location`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Retrieve inventory item", () => {
|
||||
let location1 = "loc_1"
|
||||
let location2 = "loc_2"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ICustomerModuleService, IInventoryServiceNext } from "@medusajs/types"
|
||||
import { StepResponse, WorkflowData, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const deleteInventoryLevelsStepId = "delete-inventory-levels-step"
|
||||
export const deleteInventoryLevelsStep = createStep(
|
||||
deleteInventoryLevelsStepId,
|
||||
async (ids: string[], { container }) => {
|
||||
const service = container.resolve<IInventoryServiceNext>(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
await service.softDeleteInventoryLevels(ids)
|
||||
|
||||
return new StepResponse(void 0, ids)
|
||||
},
|
||||
async (prevLevelIds, { container }) => {
|
||||
if (!prevLevelIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IInventoryServiceNext>(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
await service.restoreInventoryLevels(prevLevelIds)
|
||||
}
|
||||
)
|
||||
@@ -3,3 +3,4 @@ export * from "./create-inventory-items"
|
||||
export * from "./validate-singular-inventory-items-for-tags"
|
||||
export * from "./create-inventory-levels"
|
||||
export * from "./validate-inventory-locations"
|
||||
export * from "./delete-inventory-levels"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { deleteInventoryLevelsStep } from "../steps"
|
||||
|
||||
interface WorkflowInput {
|
||||
ids: string[]
|
||||
}
|
||||
export const deleteInventoryLevelsWorkflowId =
|
||||
"delete-inventory-levels-workflow"
|
||||
export const deleteInventoryLevelsWorkflow = createWorkflow(
|
||||
deleteInventoryLevelsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<string[]> => {
|
||||
return deleteInventoryLevelsStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./create-inventory-items"
|
||||
export * from "./create-inventory-levels"
|
||||
export * from "./delete-inventory-levels"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../../../types/routing"
|
||||
|
||||
import { deleteInventoryLevelsWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { id, location_id } = req.params
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const [{ id: levelId, reserved_quantity: reservedQuantity }] =
|
||||
await remoteQuery(
|
||||
remoteQueryObjectFromString({
|
||||
entryPoint: "inventory_level",
|
||||
variables: {
|
||||
inventory_item_id: id,
|
||||
location_id,
|
||||
},
|
||||
fields: ["id", "reserved_quantity"],
|
||||
})
|
||||
)
|
||||
|
||||
if (reservedQuantity > 0) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Cannot remove Inventory Level ${id} at Location ${location_id} because there are reservations at location`
|
||||
)
|
||||
}
|
||||
|
||||
const deleteInventoryLevelWorkflow = deleteInventoryLevelsWorkflow(req.scope)
|
||||
|
||||
await deleteInventoryLevelWorkflow.run({
|
||||
input: {
|
||||
ids: [levelId],
|
||||
},
|
||||
})
|
||||
|
||||
res.status(200).json({
|
||||
id: levelId,
|
||||
object: "inventory-level",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
import { InventoryNext } from "@medusajs/types"
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export const defaultAdminLocationLevelFields: (keyof InventoryNext.InventoryLevelDTO)[] =
|
||||
[
|
||||
"id",
|
||||
"inventory_item_id",
|
||||
"location_id",
|
||||
"stocked_quantity",
|
||||
"reserved_quantity",
|
||||
"incoming_quantity",
|
||||
"available_quantity",
|
||||
"metadata",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
export const defaultAdminLocationLevelFields = [
|
||||
"id",
|
||||
"inventory_item_id",
|
||||
"location_id",
|
||||
"stocked_quantity",
|
||||
"reserved_quantity",
|
||||
"incoming_quantity",
|
||||
"available_quantity",
|
||||
"metadata",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export const defaultAdminInventoryItemFields = [
|
||||
"id",
|
||||
|
||||
@@ -795,6 +795,17 @@ export interface IInventoryServiceNext extends IModuleService {
|
||||
context?: Context
|
||||
): Promise<void>
|
||||
|
||||
softDeleteInventoryLevels<TReturnableLinkableKeys extends string = string>(
|
||||
inventoryLevelIds: string[],
|
||||
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
|
||||
restoreInventoryLevels<TReturnableLinkableKeys extends string = string>(
|
||||
inventoryLevelIds: string[],
|
||||
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]> | void>
|
||||
/**
|
||||
* This method is used to adjust the inventory level's stocked quantity. The inventory level is identified by the IDs of its associated inventory item and location.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user