feat(core-flows,types,medusa): validate deleting location level when quantities exist (#9086)
what: - adds validation in workflow to prevent deleting a location level when reserved or stocked quantity exists - disabled delete button when quantities exist - consolidate delete workflows <img width="1079" alt="Screenshot 2024-09-10 at 16 39 02" src="https://github.com/user-attachments/assets/cf1f4b2e-75ea-4f7c-9b97-24622396c632"> RESOLVES CC-120
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { IInventoryService } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
export const deleteInventoryLevelsFromItemAndLocationsStepId =
|
||||
"delete-inventory-levels-from-item-and-location-step"
|
||||
/**
|
||||
* This step removes one or more inventory levels by their associated inventory item and location.
|
||||
*/
|
||||
export const deleteInventoryLevelsFromItemAndLocationsStep = createStep(
|
||||
deleteInventoryLevelsFromItemAndLocationsStepId,
|
||||
async (
|
||||
input: { inventory_item_id: string; location_id: string }[],
|
||||
{ container }
|
||||
) => {
|
||||
if (!input.length) {
|
||||
return new StepResponse(void 0, [])
|
||||
}
|
||||
|
||||
const service = container.resolve<IInventoryService>(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
const items = await service.listInventoryLevels({ $or: input }, {})
|
||||
|
||||
if (items.some((i) => i.reserved_quantity > 0)) {
|
||||
const invalidDeletes = items.filter((i) => i.reserved_quantity > 0)
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Cannot remove Inventory Levels for ${invalidDeletes
|
||||
.map((i) => `Inventory Level ${i.id} at Location ${i.location_id}`)
|
||||
.join(
|
||||
", "
|
||||
)} because there are reserved quantities for items at locations`
|
||||
)
|
||||
}
|
||||
|
||||
const deletedIds = items.map((i) => i.id)
|
||||
await service.softDeleteInventoryLevels(deletedIds)
|
||||
|
||||
return new StepResponse(void 0, deletedIds)
|
||||
},
|
||||
async (prevLevelIds, { container }) => {
|
||||
if (!prevLevelIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IInventoryService>(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
await service.restoreInventoryLevels(prevLevelIds)
|
||||
}
|
||||
)
|
||||
@@ -4,7 +4,6 @@ export * from "./create-inventory-items"
|
||||
export * from "./create-inventory-levels"
|
||||
export * from "./delete-inventory-items"
|
||||
export * from "./delete-inventory-levels"
|
||||
export * from "./delete-levels-by-item-and-location"
|
||||
export * from "./update-inventory-items"
|
||||
export * from "./update-inventory-levels"
|
||||
export * from "./validate-inventory-locations"
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { InventoryLevelDTO, InventoryTypes } from "@medusajs/types"
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createWorkflow,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createInventoryLevelsStep,
|
||||
deleteInventoryLevelsFromItemAndLocationsStep,
|
||||
} from "../steps"
|
||||
import { createInventoryLevelsStep } from "../steps"
|
||||
import { deleteInventoryLevelsWorkflow } from "./delete-inventory-levels"
|
||||
|
||||
export interface BulkCreateDeleteLevelsWorkflowInput {
|
||||
creates: InventoryTypes.CreateInventoryLevelInput[]
|
||||
@@ -24,7 +22,11 @@ export const bulkCreateDeleteLevelsWorkflow = createWorkflow(
|
||||
(
|
||||
input: WorkflowData<BulkCreateDeleteLevelsWorkflowInput>
|
||||
): WorkflowResponse<InventoryLevelDTO[]> => {
|
||||
deleteInventoryLevelsFromItemAndLocationsStep(input.deletes)
|
||||
deleteInventoryLevelsWorkflow.runAsStep({
|
||||
input: {
|
||||
$or: input.deletes,
|
||||
},
|
||||
})
|
||||
|
||||
return new WorkflowResponse(createInventoryLevelsStep(input.creates))
|
||||
}
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createWorkflow,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
import { deleteInventoryLevelsStep } from "../steps"
|
||||
import { FilterableInventoryLevelProps } from "@medusajs/types"
|
||||
import {
|
||||
deduplicate,
|
||||
MedusaError,
|
||||
ModuleRegistrationName,
|
||||
} from "@medusajs/utils"
|
||||
import { useRemoteQueryStep } from "../../common"
|
||||
import { deleteEntitiesStep } from "../../common/steps/delete-entities"
|
||||
|
||||
/**
|
||||
* This step validates that inventory levels are deletable.
|
||||
*/
|
||||
export const validateInventoryLevelsDelete = createStep(
|
||||
"validate-inventory-levels-delete",
|
||||
async function ({ inventoryLevels }: { inventoryLevels: any[] }) {
|
||||
const undeleteableItems = inventoryLevels.filter(
|
||||
(i) => i.reserved_quantity > 0 || i.stocked_quantity > 0
|
||||
)
|
||||
|
||||
if (undeleteableItems.length) {
|
||||
const stockLocationIds = deduplicate(
|
||||
undeleteableItems.map((item) => item.location_id)
|
||||
)
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Cannot remove Inventory Levels for ${stockLocationIds} because there are stocked or reserved items at the locations`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export interface DeleteInventoryLevelsWorkflowInput {
|
||||
ids: string[]
|
||||
}
|
||||
export const deleteInventoryLevelsWorkflowId =
|
||||
"delete-inventory-levels-workflow"
|
||||
/**
|
||||
@@ -16,7 +45,28 @@ export const deleteInventoryLevelsWorkflowId =
|
||||
*/
|
||||
export const deleteInventoryLevelsWorkflow = createWorkflow(
|
||||
deleteInventoryLevelsWorkflowId,
|
||||
(input: WorkflowData<DeleteInventoryLevelsWorkflowInput>): WorkflowResponse<string[]> => {
|
||||
return new WorkflowResponse(deleteInventoryLevelsStep(input.ids))
|
||||
(input: WorkflowData<FilterableInventoryLevelProps>) => {
|
||||
const inventoryLevels = useRemoteQueryStep({
|
||||
entry_point: "inventory_levels",
|
||||
fields: ["id", "stocked_quantity", "reserved_quantity", "location_id"],
|
||||
variables: {
|
||||
filters: input,
|
||||
},
|
||||
})
|
||||
|
||||
validateInventoryLevelsDelete({ inventoryLevels })
|
||||
|
||||
const idsToDelete = transform({ inventoryLevels }, ({ inventoryLevels }) =>
|
||||
inventoryLevels.map((il) => il.id)
|
||||
)
|
||||
|
||||
deleteEntitiesStep({
|
||||
moduleRegistrationName: ModuleRegistrationName.INVENTORY,
|
||||
invokeMethod: "softDeleteInventoryLevels",
|
||||
compensateMethod: "restoreInventoryLevels",
|
||||
data: idsToDelete,
|
||||
})
|
||||
|
||||
return new WorkflowResponse(void 0)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user