feat: move create inventory to @medusajs/workflows (#5301)

**Why**
- We have some workflow-like flows in @medusajs/medusa. These should be moved over to the workflows package.
- Inventory Items <> Variant currently assume a 1-1 mapping. There should be support for a many-to-many mapping.

**What**
- PR introduces a feature flag for supporting many-to-many mappings for inventory and variants.
- Deletes legacy transaction handler in @medusajs/medusa.
- Adjusts existing createInventoryItems handler to remove dependency on variant data.

**Unkowns**
~~1. Couldn't find an existing test for the CreateProduct workflow. It should be tested that this still works as expected.~~
2. Have removed transaction managers as we should move to handling consistency through orchestration tooling. Are we ready for that?
This commit is contained in:
Sebastian Rindom
2023-10-11 11:01:56 -07:00
committed by GitHub
parent bbd9dd408f
commit 66413d094e
25 changed files with 480 additions and 281 deletions

View File

@@ -1,2 +1,3 @@
export * from "./cart"
export * from "./product"
export * from "./inventory"

View File

@@ -0,0 +1,64 @@
import { Workflows } from "../../definitions"
import {
TransactionStepsDefinition,
WorkflowManager,
} from "@medusajs/orchestration"
import { exportWorkflow, pipe } from "../../helper"
import { InventoryTypes, WorkflowTypes } from "@medusajs/types"
import { InventoryHandlers } from "../../handlers"
export enum CreateInventoryItemActions {
prepare = "prepare",
createInventoryItems = "createInventoryItems",
}
const workflowSteps: TransactionStepsDefinition = {
next: {
action: CreateInventoryItemActions.createInventoryItems,
},
}
const handlers = new Map([
[
CreateInventoryItemActions.createInventoryItems,
{
invoke: pipe(
{
inputAlias: CreateInventoryItemActions.prepare,
merge: true,
invoke: {
from: CreateInventoryItemActions.prepare,
},
},
InventoryHandlers.createInventoryItems
),
compensate: pipe(
{
merge: true,
invoke: {
from: CreateInventoryItemActions.createInventoryItems,
alias:
InventoryHandlers.removeInventoryItems.aliases.inventoryItems,
},
},
InventoryHandlers.removeInventoryItems
),
},
],
])
WorkflowManager.register(
Workflows.CreateInventoryItems,
workflowSteps,
handlers
)
export const createInventoryItems = exportWorkflow<
WorkflowTypes.InventoryWorkflow.CreateInventoryItemsWorkflowInputDTO,
{ tag: string; inventoryItem: InventoryTypes.InventoryItemDTO }[]
>(
Workflows.CreateInventoryItems,
CreateInventoryItemActions.createInventoryItems,
async (data) => data
)

View File

@@ -0,0 +1 @@
export * from "./create-inventory-item"

View File

@@ -11,6 +11,7 @@ import {
MiddlewaresHandlers,
ProductHandlers,
} from "../../handlers"
import { prepareCreateInventoryItems } from "./prepare-create-inventory-items"
export enum CreateProductsActions {
prepare = "prepare",
@@ -175,9 +176,10 @@ const handlers = new Map([
merge: true,
invoke: {
from: CreateProductsActions.createProducts,
alias: InventoryHandlers.createInventoryItems.aliases.products,
alias: prepareCreateInventoryItems.aliases.products,
},
},
prepareCreateInventoryItems,
InventoryHandlers.createInventoryItems
),
compensate: pipe(

View File

@@ -0,0 +1,46 @@
import { ProductTypes } from "@medusajs/types"
import { WorkflowArguments } from "../../helper"
type AssociationTaggedVariant = ProductTypes.ProductVariantDTO & {
_associationTag?: string
}
type ObjectWithVariant = { variants: ProductTypes.ProductVariantDTO[] }
export async function prepareCreateInventoryItems({
data,
}: WorkflowArguments<{
products: ObjectWithVariant[]
}>) {
const taggedVariants = data.products.reduce<AssociationTaggedVariant[]>(
(acc, product: ObjectWithVariant) => {
const cleanVariants = product.variants.reduce<AssociationTaggedVariant[]>(
(acc, variant: AssociationTaggedVariant) => {
if (!variant.manage_inventory) {
return acc
}
variant._associationTag = variant.id
acc.push(variant)
return acc
},
[]
)
return acc.concat(cleanVariants)
},
[]
)
return {
alias: prepareCreateInventoryItems.aliases.output,
value: {
inventoryItems: taggedVariants,
},
}
}
prepareCreateInventoryItems.aliases = {
products: "products",
output: "prepareCreateInventoryItemsOutput",
}

View File

@@ -4,6 +4,8 @@ export enum Workflows {
// Cart workflows
CreateCart = "create-cart",
CreateInventoryItems = "create-inventory-items",
}
export enum InputAlias {
@@ -16,4 +18,6 @@ export enum InputAlias {
AttachedInventoryItems = "attachedInventoryItems",
DetachedInventoryItems = "detachedInventoryItems",
InventoryItemsInputData = "inventoryItemsInputData",
}

View File

@@ -1,4 +1,4 @@
import { InventoryItemDTO, ProductTypes } from "@medusajs/types"
import { InventoryItemDTO } from "@medusajs/types"
import { WorkflowArguments } from "../../helper"
export async function attachInventoryItems({
@@ -7,12 +7,11 @@ export async function attachInventoryItems({
data,
}: WorkflowArguments<{
inventoryItems: {
variant: ProductTypes.ProductVariantDTO
tag: string
inventoryItem: InventoryItemDTO
}[]
}>) {
const { manager } = context
const productVariantInventoryService = container
.resolve("productVariantInventoryService")
.withTransaction(manager)
@@ -21,12 +20,10 @@ export async function attachInventoryItems({
return
}
const inventoryData = data.inventoryItems.map(
({ variant, inventoryItem }) => ({
variantId: variant.id,
inventoryItemId: inventoryItem.id,
})
)
const inventoryData = data.inventoryItems.map(({ tag, inventoryItem }) => ({
variantId: tag,
inventoryItemId: inventoryItem.id,
}))
return await productVariantInventoryService.attachInventoryItem(inventoryData)
}

View File

@@ -1,21 +1,16 @@
import {
IInventoryService,
InventoryItemDTO,
ProductTypes,
} from "@medusajs/types"
import { IInventoryService, InventoryItemDTO } from "@medusajs/types"
import { WorkflowArguments } from "../../helper"
type Result = {
variant: ProductTypes.ProductVariantDTO
tag: string
inventoryItem: InventoryItemDTO
}[]
export async function createInventoryItems({
container,
context,
data,
}: WorkflowArguments<{
products: ProductTypes.ProductDTO[]
inventoryItems: (InventoryItemDTO & { _associationTag?: string })[]
}>): Promise<Result | void> {
const inventoryService: IInventoryService =
container.resolve("inventoryService")
@@ -28,47 +23,27 @@ export async function createInventoryItems({
return void 0
}
const variants = data.products.reduce(
(
acc: ProductTypes.ProductVariantDTO[],
product: ProductTypes.ProductDTO
) => {
return acc.concat(product.variants)
},
[]
)
const result = await Promise.all(
variants.map(async (variant) => {
if (!variant.manage_inventory) {
return
}
data.inventoryItems.map(async (item) => {
const inventoryItem = await inventoryService!.createInventoryItem({
sku: item.sku!,
origin_country: item.origin_country!,
hs_code: item.hs_code!,
mid_code: item.mid_code!,
material: item.material!,
weight: item.weight!,
length: item.length!,
height: item.height!,
width: item.width!,
})
const inventoryItem = await inventoryService!.createInventoryItem(
{
sku: variant.sku!,
origin_country: variant.origin_country!,
hs_code: variant.hs_code!,
mid_code: variant.mid_code!,
material: variant.material!,
weight: variant.weight!,
length: variant.length!,
height: variant.height!,
width: variant.width!,
},
{
transactionManager: (context.transactionManager ??
context.manager) as any,
}
)
return { variant, inventoryItem }
return { tag: item._associationTag ?? inventoryItem.id, inventoryItem }
})
)
return result.filter(Boolean) as Result
return result
}
createInventoryItems.aliases = {
products: "products",
payload: "payload",
}

View File

@@ -1,4 +1,4 @@
import { InventoryItemDTO, ProductTypes } from "@medusajs/types"
import { InventoryItemDTO } from "@medusajs/types"
import { WorkflowArguments } from "../../helper"
export async function detachInventoryItems({
@@ -7,7 +7,7 @@ export async function detachInventoryItems({
data,
}: WorkflowArguments<{
inventoryItems: {
variant: ProductTypes.ProductVariantDTO
tag: string
inventoryItem: InventoryItemDTO
}[]
}>): Promise<void> {
@@ -22,10 +22,10 @@ export async function detachInventoryItems({
}
await Promise.all(
data.inventoryItems.map(async ({ variant, inventoryItem }) => {
data.inventoryItems.map(async ({ tag, inventoryItem }) => {
return await productVariantInventoryService.detachInventoryItem(
inventoryItem.id,
variant.id
tag
)
})
)

View File

@@ -3,12 +3,10 @@ import { WorkflowArguments } from "../../helper"
export async function removeInventoryItems({
container,
context,
data,
}: WorkflowArguments<{
inventoryItems: { inventoryItem: InventoryItemDTO }[]
}>) {
const { manager } = context
const inventoryService = container.resolve("inventoryService")
if (!inventoryService) {
@@ -20,8 +18,7 @@ export async function removeInventoryItems({
}
return await inventoryService!.deleteInventoryItem(
data.inventoryItems.map(({ inventoryItem }) => inventoryItem.id),
{ transactionManager: manager }
data.inventoryItems.map(({ inventoryItem }) => inventoryItem.id)
)
}