From 1e13878a8f41ead8b6dd5bf075e61d7aaf35f23b Mon Sep 17 00:00:00 2001 From: itariv Date: Wed, 29 Oct 2025 10:29:00 +0100 Subject: [PATCH 1/2] chore: added possibility to remove location from product on vendor panel --- .../location-levels/[location_id]/route.ts | 89 ++++++++++++++++++- .../api/vendor/inventory-items/middlewares.ts | 12 ++- .../api/vendor/inventory-items/validators.ts | 3 + 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts index f63424451..6dfdd221d 100644 --- a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts +++ b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts @@ -1,10 +1,12 @@ import { AuthenticatedMedusaRequest, MedusaResponse } from '@medusajs/framework' -import { ContainerRegistrationKeys, Modules } from '@medusajs/framework/utils' -import { updateInventoryLevelsWorkflow } from '@medusajs/medusa/core-flows' +import { ContainerRegistrationKeys, Modules, MedusaError } from '@medusajs/framework/utils' +import { updateInventoryLevelsWorkflow, deleteInventoryLevelsWorkflow } from '@medusajs/medusa/core-flows' import { IntermediateEvents } from '@mercurjs/framework' import { VendorUpdateInventoryLevelType } from '../../../validators' +import { VendorInventoryLevelDeleteResponse } from '../../../validators' +import { refetchInventoryItem } from '@medusajs/medusa/api/admin/inventory-items/helpers' /** * @oas [post] /vendor/inventory-items/{id}/location-levels/{location_id} @@ -128,3 +130,86 @@ export const GET = async ( location_level }) } + +/** + * @oas [delete] /vendor/inventory-items/{id}/location-levels/{location_id} + * operationId: "VendorDeleteInventoryLevel" + * summary: "Delete inventory level" + * description: "Deletes inventory level of the InventoryItem in the specified location" + * x-authenticated: true + * parameters: + * - in: path + * name: id + * required: true + * description: The ID of the InventoryItem. + * schema: + * type: string + * - in: path + * name: location_id + * required: true + * description: The ID of the Stock Location. + * schema: + * type: string + * responses: + * "200": + * description: Inventory level + * tags: + * - Vendor Inventory Items + * security: + * - api_token: [] + * - cookie_auth: [] + */ + +export const DELETE = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const { id, location_id } = req.params + + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) + + const result = await query.graph({ + entity: "inventory_level", + filters: { inventory_item_id: id, location_id }, + fields: ["id", "reserved_quantity"], + }) + + if (!result.data.length) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Inventory Level for Item ${id} at Location ${location_id} not found` + ) + } + + const { id: levelId, reserved_quantity: reservedQuantity } = result.data[0] + + 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: { + id: [levelId], + }, + }) + + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.queryConfig.fields + ) + + res.status(200).json({ + id: levelId, + object: "inventory-level", + deleted: true, + parent: inventoryItem, + }) +} + + diff --git a/packages/modules/b2c-core/src/api/vendor/inventory-items/middlewares.ts b/packages/modules/b2c-core/src/api/vendor/inventory-items/middlewares.ts index 16dc5544e..3805a5b78 100644 --- a/packages/modules/b2c-core/src/api/vendor/inventory-items/middlewares.ts +++ b/packages/modules/b2c-core/src/api/vendor/inventory-items/middlewares.ts @@ -165,5 +165,15 @@ export const vendorInventoryItemsMiddlewares: MiddlewareRoute[] = [ }) ) ] - } + }, + { + method: ["DELETE"], + matcher: "/vendor/inventory-items/:id/location-levels/:location_id", + middlewares: [ + validateAndTransformQuery( + VendorGetInventoryItemsParams, + vendorInventoryLevelQueryConfig.retrieve + ), + ], + }, ] diff --git a/packages/modules/b2c-core/src/api/vendor/inventory-items/validators.ts b/packages/modules/b2c-core/src/api/vendor/inventory-items/validators.ts index 9c9f393bf..525450532 100644 --- a/packages/modules/b2c-core/src/api/vendor/inventory-items/validators.ts +++ b/packages/modules/b2c-core/src/api/vendor/inventory-items/validators.ts @@ -2,6 +2,7 @@ import { z } from 'zod' import { applyAndAndOrOperators } from '@medusajs/medusa/api/utils/common-validators/common' import { createFindParams } from '@medusajs/medusa/api/utils/validators' +import { HttpTypes } from '@medusajs/framework/types' export const VendorGetInventoryItemsParamsFields = z.object({ q: z.string().optional(), @@ -240,3 +241,5 @@ export const VendorBatchInventoryItemLocationsLevel = z.object({ delete: z.array(z.string()).optional(), force: z.boolean().optional() }) + +export type VendorInventoryLevelDeleteResponse = HttpTypes.AdminInventoryLevelDeleteResponse From 86527f537bc93b47e6b985c3d02a9ef5f6ce6475 Mon Sep 17 00:00:00 2001 From: itariv Date: Thu, 30 Oct 2025 14:05:34 +0100 Subject: [PATCH 2/2] chore: added possibility to delete inventory level with stocked items at the locations --- .../location-levels/[location_id]/route.ts | 64 +++++++++++++++++++ .../location-levels/[location_id]/route.ts | 1 + .../[id]/location-levels/batch/route.ts | 2 +- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 packages/modules/b2c-core/src/api/admin/inventory-items/[id]/location-levels/[location_id]/route.ts diff --git a/packages/modules/b2c-core/src/api/admin/inventory-items/[id]/location-levels/[location_id]/route.ts b/packages/modules/b2c-core/src/api/admin/inventory-items/[id]/location-levels/[location_id]/route.ts new file mode 100644 index 000000000..bd9b1efda --- /dev/null +++ b/packages/modules/b2c-core/src/api/admin/inventory-items/[id]/location-levels/[location_id]/route.ts @@ -0,0 +1,64 @@ +import { + ContainerRegistrationKeys, + MedusaError, +} from "@medusajs/framework/utils"; +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"; + +import { deleteInventoryLevelsWorkflow } from "@medusajs/core-flows"; +import { HttpTypes } from "@medusajs/framework/types"; +import { refetchInventoryItem } from "@medusajs/medusa/api/admin/inventory-items/helpers"; + +// Overridden admin default delete inventory level route to force delete the inventory level with stocked items at the locations. + +export const DELETE = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const { id, location_id } = req.params; + + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY); + + const result = await query.graph({ + entity: "inventory_level", + filters: { inventory_item_id: id, location_id }, + fields: ["id", "reserved_quantity"], + }); + + if (!result.data.length) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Inventory Level for Item ${id} at Location ${location_id} not found` + ); + } + + const { id: levelId, reserved_quantity: reservedQuantity } = result.data[0]; + + 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: { + id: [levelId], + force: true, + }, + }); + + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.queryConfig.fields + ); + + res.status(200).json({ + id: levelId, + object: "inventory-level", + deleted: true, + parent: inventoryItem, + }); +}; diff --git a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts index 6dfdd221d..ff9c9ed20 100644 --- a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts +++ b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/[location_id]/route.ts @@ -195,6 +195,7 @@ export const DELETE = async ( await deleteInventoryLevelWorkflow.run({ input: { id: [levelId], + force: true, }, }) diff --git a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/batch/route.ts b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/batch/route.ts index c7a05a0c8..e1b589ee1 100644 --- a/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/batch/route.ts +++ b/packages/modules/b2c-core/src/api/vendor/inventory-items/[id]/location-levels/batch/route.ts @@ -64,7 +64,7 @@ export const POST = async ( ...u, inventory_item_id: id })) ?? [], - force: req.validatedBody.force ?? false + force: req.validatedBody.force ?? true } }