feat(medusa, inventory): Inventory Management module (#2956)
* feat: inventory module
This commit is contained in:
1 parent
a2df11fc10
commit
93ee248493
22 files changed
+1735
-14
No files matched your search
@@ -31,12 +31,12 @@ export interface IInventoryService {
|
||||
): Promise<[InventoryLevelDTO[], number]>
|
||||
|
||||
retrieveInventoryItem(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
config?: FindConfig<InventoryItemDTO>
|
||||
): Promise<InventoryItemDTO>
|
||||
|
||||
retrieveInventoryLevel(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationId: string
|
||||
): Promise<InventoryLevelDTO>
|
||||
|
||||
@@ -53,48 +53,56 @@ export interface IInventoryService {
|
||||
): Promise<InventoryLevelDTO>
|
||||
|
||||
updateInventoryLevel(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationId: string,
|
||||
update: UpdateInventoryLevelInput
|
||||
): Promise<InventoryLevelDTO>
|
||||
|
||||
updateInventoryItem(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
input: CreateInventoryItemInput
|
||||
): Promise<InventoryItemDTO>
|
||||
|
||||
updateReservationItem(
|
||||
reservationId: string,
|
||||
update: UpdateReservationItemInput
|
||||
reservationItemId: string,
|
||||
input: UpdateReservationItemInput
|
||||
): Promise<ReservationItemDTO>
|
||||
|
||||
deleteReservationItemsByLineItem(lineItemId: string): Promise<void>
|
||||
|
||||
deleteReservationItem(id: string): Promise<void>
|
||||
deleteReservationItem(reservationItemId: string): Promise<void>
|
||||
|
||||
deleteInventoryItem(itemId: string): Promise<void>
|
||||
deleteInventoryItem(inventoryItemId: string): Promise<void>
|
||||
|
||||
deleteInventoryLevel(itemId: string, locationId: string): Promise<void>
|
||||
deleteInventoryLevel(
|
||||
inventoryLevelId: string,
|
||||
locationId: string
|
||||
): Promise<void>
|
||||
|
||||
adjustInventory(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationId: string,
|
||||
adjustment: number
|
||||
): Promise<InventoryLevelDTO>
|
||||
|
||||
confirmInventory(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationIds: string[],
|
||||
quantity: number
|
||||
): Promise<boolean>
|
||||
|
||||
retrieveAvailableQuantity(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
): Promise<number>
|
||||
|
||||
retrieveStockedQuantity(
|
||||
itemId: string,
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
): Promise<number>
|
||||
|
||||
retrieveReservedQuantity(
|
||||
inventoryItemId: string,
|
||||
locationIds: string[]
|
||||
): Promise<number>
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import dotenv from "dotenv"
|
||||
import { AwilixContainer } from "awilix"
|
||||
import express from "express"
|
||||
|
||||
import {
|
||||
ProductVariantService,
|
||||
ProductVariantInventoryService,
|
||||
} from "../services"
|
||||
import { ProductVariant } from "../models"
|
||||
import { IInventoryService, IStockLocationService } from "../interfaces"
|
||||
import loaders from "../loaders"
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const BATCH_SIZE = 100
|
||||
|
||||
const migrateProductVariant = async (
|
||||
variant: ProductVariant,
|
||||
locationId: string,
|
||||
{ container }: { container: AwilixContainer }
|
||||
) => {
|
||||
const productVariantInventoryService: ProductVariantInventoryService =
|
||||
container.resolve("productVariantInventoryService")
|
||||
const inventoryService: IInventoryService =
|
||||
container.resolve("inventoryService")
|
||||
|
||||
if (!variant.manage_inventory) {
|
||||
return
|
||||
}
|
||||
|
||||
const inventoryItem = await inventoryService.createInventoryItem({
|
||||
sku: variant.sku,
|
||||
material: variant.material,
|
||||
width: variant.width,
|
||||
length: variant.length,
|
||||
height: variant.height,
|
||||
weight: variant.weight,
|
||||
origin_country: variant.origin_country,
|
||||
hs_code: variant.hs_code,
|
||||
mid_code: variant.mid_code,
|
||||
requires_shipping: true,
|
||||
})
|
||||
|
||||
await productVariantInventoryService.attachInventoryItem(
|
||||
variant.id,
|
||||
inventoryItem.id,
|
||||
1
|
||||
)
|
||||
|
||||
await inventoryService.createInventoryLevel({
|
||||
location_id: locationId,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
stocked_quantity: variant.inventory_quantity,
|
||||
incoming_quantity: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const migrateStockLocation = async (container: AwilixContainer) => {
|
||||
const stockLocationService: IStockLocationService = container.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
const existing = await stockLocationService.list({}, { take: 1 })
|
||||
|
||||
if (existing.length) {
|
||||
return existing[0].id
|
||||
}
|
||||
|
||||
const stockLocation = await stockLocationService.create({ name: "Default" })
|
||||
|
||||
return stockLocation.id
|
||||
}
|
||||
|
||||
const processBatch = async (
|
||||
variants: ProductVariant[],
|
||||
locationId: string,
|
||||
container: AwilixContainer
|
||||
) => {
|
||||
await Promise.all(
|
||||
variants.map(async (variant) => {
|
||||
await migrateProductVariant(variant, locationId, { container })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const migrate = async function ({ directory }) {
|
||||
const app = express()
|
||||
const { container } = await loaders({
|
||||
directory,
|
||||
expressApp: app,
|
||||
isTest: false,
|
||||
})
|
||||
|
||||
const variantService: ProductVariantService = await container.resolve(
|
||||
"productVariantService"
|
||||
)
|
||||
|
||||
const defaultLocationId = await migrateStockLocation(container)
|
||||
|
||||
const [variants, totalCount] = await variantService.listAndCount(
|
||||
{},
|
||||
{ take: BATCH_SIZE, order: { id: "ASC" } }
|
||||
)
|
||||
|
||||
await processBatch(variants, defaultLocationId, container)
|
||||
|
||||
let processedCount = variants.length
|
||||
console.log(`Processed ${processedCount} of ${totalCount}`)
|
||||
while (processedCount < totalCount) {
|
||||
const nextBatch = await variantService.list(
|
||||
{},
|
||||
{
|
||||
skip: processedCount,
|
||||
take: BATCH_SIZE,
|
||||
order: { id: "ASC" },
|
||||
}
|
||||
)
|
||||
|
||||
await processBatch(nextBatch, defaultLocationId, container)
|
||||
|
||||
processedCount += nextBatch.length
|
||||
console.log(`Processed ${processedCount} of ${totalCount}`)
|
||||
}
|
||||
|
||||
console.log("Done")
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
migrate({ directory: process.cwd() })
|
||||
@@ -34,6 +34,7 @@ export type InventoryLevelDTO = {
|
||||
inventory_item_id: string
|
||||
location_id: string
|
||||
stocked_quantity: number
|
||||
reserved_quantity: number
|
||||
incoming_quantity: number
|
||||
metadata: Record<string, unknown> | null
|
||||
created_at: string | Date
|
||||
@@ -87,6 +88,7 @@ export type FilterableInventoryLevelProps = {
|
||||
inventory_item_id?: string | string[]
|
||||
location_id?: string | string[]
|
||||
stocked_quantity?: number | NumericalComparisonOperator
|
||||
reserved_quantity?: number | NumericalComparisonOperator
|
||||
incoming_quantity?: number | NumericalComparisonOperator
|
||||
}
|
||||
|
||||
@@ -94,7 +96,8 @@ export type CreateInventoryLevelInput = {
|
||||
inventory_item_id: string
|
||||
location_id: string
|
||||
stocked_quantity: number
|
||||
incoming_quantity: number
|
||||
reserved_quantity?: number
|
||||
incoming_quantity?: number
|
||||
}
|
||||
|
||||
export type UpdateInventoryLevelInput = {
|
||||
|
||||
Reference in new issue
Block a user