feat(medusa, types, utils, workflow): Migrate medusa workflow to the workflow package (#4682)
This commit is contained in:
1 parent
433914ae01
commit
dc46927bc6
68 files changed
+1224
-1064
No files matched your search
@@ -10,7 +10,7 @@ import { simpleSalesChannelFactory } from "../../../../factories"
|
|||||||
import { AxiosInstance } from "axios"
|
import { AxiosInstance } from "axios"
|
||||||
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(5000000)
|
||||||
|
|
||||||
const adminHeaders = {
|
const adminHeaders = {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
"@medusajs/modules-sdk": "^1.9.0",
|
"@medusajs/modules-sdk": "^1.9.0",
|
||||||
"@medusajs/orchestration": "^0.1.0",
|
"@medusajs/orchestration": "^0.1.0",
|
||||||
"@medusajs/utils": "^1.9.3",
|
"@medusajs/utils": "^1.9.3",
|
||||||
|
"@medusajs/workflows": "workspace:^",
|
||||||
"awilix": "^8.0.0",
|
"awilix": "^8.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"boxen": "^5.0.1",
|
"boxen": "^5.0.1",
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ import { DistributedTransaction } from "@medusajs/orchestration"
|
|||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||||
import { FlagRouter } from "../../../../utils/flag-router"
|
import { FlagRouter } from "../../../../utils/flag-router"
|
||||||
import { IInventoryService } from "@medusajs/types"
|
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||||
import { Logger } from "../../../../types/global"
|
import { Logger } from "../../../../types/global"
|
||||||
import { ProductStatus } from "../../../../models"
|
import { ProductStatus } from "../../../../models"
|
||||||
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { createProductsWorkflow } from "../../../../workflows/admin/create-products"
|
import { validator } from "../../../../utils"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { createProducts } from "@medusajs/workflows"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /admin/products
|
* @oas [post] /admin/products
|
||||||
@@ -133,13 +133,26 @@ export default async (req, res) => {
|
|||||||
const productModuleService = req.scope.resolve("productModuleService")
|
const productModuleService = req.scope.resolve("productModuleService")
|
||||||
|
|
||||||
if (productModuleService) {
|
if (productModuleService) {
|
||||||
const products = await createProductsWorkflow(
|
const createProductWorkflow = createProducts(req.scope)
|
||||||
{
|
|
||||||
container: req.scope,
|
const input = {
|
||||||
|
products: [
|
||||||
|
validated,
|
||||||
|
] as WorkflowTypes.ProductWorkflow.CreateProductInputDTO[],
|
||||||
|
config: {
|
||||||
|
listConfig: {
|
||||||
|
select: defaultAdminProductFields,
|
||||||
|
relations: defaultAdminProductRelations,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result: products } = await createProductWorkflow.run({
|
||||||
|
input,
|
||||||
|
context: {
|
||||||
manager: entityManager,
|
manager: entityManager,
|
||||||
},
|
},
|
||||||
[validated]
|
})
|
||||||
)
|
|
||||||
|
|
||||||
return res.json({ product: products[0] })
|
return res.json({ product: products[0] })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,421 +0,0 @@
|
|||||||
import {
|
|
||||||
TransactionHandlerType,
|
|
||||||
TransactionPayload,
|
|
||||||
TransactionStepHandler,
|
|
||||||
TransactionStepsDefinition,
|
|
||||||
} from "@medusajs/orchestration"
|
|
||||||
import {
|
|
||||||
IInventoryService,
|
|
||||||
MedusaContainer,
|
|
||||||
ProductTypes,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import {
|
|
||||||
defaultAdminProductFields,
|
|
||||||
defaultAdminProductRelations,
|
|
||||||
} from "../../../api"
|
|
||||||
import {
|
|
||||||
attachInventoryItems,
|
|
||||||
attachSalesChannelToProducts,
|
|
||||||
attachShippingProfileToProducts,
|
|
||||||
createInventoryItems,
|
|
||||||
createProducts,
|
|
||||||
CreateProductsData,
|
|
||||||
CreateProductsPreparedData,
|
|
||||||
detachInventoryItems,
|
|
||||||
detachSalesChannelFromProducts,
|
|
||||||
detachShippingProfileFromProducts,
|
|
||||||
prepareCreateProductsData,
|
|
||||||
removeInventoryItems,
|
|
||||||
removeProducts,
|
|
||||||
updateProductsVariantsPrices,
|
|
||||||
} from "../../functions"
|
|
||||||
import { PricingService, ProductService } from "../../../services"
|
|
||||||
import { CreateProductsWorkflowInputData, InjectedDependencies } from "./types"
|
|
||||||
|
|
||||||
export enum CreateProductsWorkflowActions {
|
|
||||||
prepare = "prepare",
|
|
||||||
createProducts = "createProducts",
|
|
||||||
attachToSalesChannel = "attachToSalesChannel",
|
|
||||||
attachShippingProfile = "attachShippingProfile",
|
|
||||||
createPrices = "createPrices",
|
|
||||||
createInventoryItems = "createInventoryItems",
|
|
||||||
attachInventoryItems = "attachInventoryItems",
|
|
||||||
result = "result",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const workflowSteps: TransactionStepsDefinition = {
|
|
||||||
next: {
|
|
||||||
action: CreateProductsWorkflowActions.prepare,
|
|
||||||
noCompensation: true,
|
|
||||||
next: {
|
|
||||||
action: CreateProductsWorkflowActions.createProducts,
|
|
||||||
next: [
|
|
||||||
{
|
|
||||||
action: CreateProductsWorkflowActions.attachShippingProfile,
|
|
||||||
saveResponse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: CreateProductsWorkflowActions.attachToSalesChannel,
|
|
||||||
saveResponse: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
action: CreateProductsWorkflowActions.createPrices,
|
|
||||||
next: {
|
|
||||||
action: CreateProductsWorkflowActions.createInventoryItems,
|
|
||||||
next: {
|
|
||||||
action: CreateProductsWorkflowActions.attachInventoryItems,
|
|
||||||
next: {
|
|
||||||
action: CreateProductsWorkflowActions.result,
|
|
||||||
noCompensation: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldSkipInventoryStep = (
|
|
||||||
container: MedusaContainer,
|
|
||||||
stepName: string
|
|
||||||
) => {
|
|
||||||
const inventoryService = container.resolve(
|
|
||||||
"inventoryService"
|
|
||||||
) as IInventoryService
|
|
||||||
if (!inventoryService) {
|
|
||||||
const logger = container.resolve("logger")
|
|
||||||
logger.warn(
|
|
||||||
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The '${stepName}' will be skipped.`
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transactionHandler(
|
|
||||||
dependencies: InjectedDependencies
|
|
||||||
): TransactionStepHandler {
|
|
||||||
const { manager, container } = dependencies
|
|
||||||
|
|
||||||
const command = {
|
|
||||||
[CreateProductsWorkflowActions.prepare]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData
|
|
||||||
) => {
|
|
||||||
return await prepareCreateProductsData({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.createProducts]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsData
|
|
||||||
): Promise<ProductTypes.ProductDTO[]> => {
|
|
||||||
return await createProducts({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
|
|
||||||
if (!products?.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return await removeProducts({
|
|
||||||
container,
|
|
||||||
data: products,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.attachShippingProfile]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
const { productsHandleShippingProfileIdMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
|
|
||||||
return await attachShippingProfileToProducts({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
productsHandleShippingProfileIdMap,
|
|
||||||
products,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
const { productsHandleShippingProfileIdMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
|
|
||||||
return await detachShippingProfileFromProducts({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
productsHandleShippingProfileIdMap,
|
|
||||||
products,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.attachToSalesChannel]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
const { productsHandleSalesChannelsMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
|
|
||||||
return await attachSalesChannelToProducts({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
productsHandleSalesChannelsMap,
|
|
||||||
products,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
const { productsHandleSalesChannelsMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
|
|
||||||
return await detachSalesChannelFromProducts({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
productsHandleSalesChannelsMap,
|
|
||||||
products,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.createInventoryItems]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const shouldSkipStep_ = shouldSkipInventoryStep(
|
|
||||||
container,
|
|
||||||
CreateProductsWorkflowActions.createInventoryItems
|
|
||||||
)
|
|
||||||
if (shouldSkipStep_) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { [CreateProductsWorkflowActions.createProducts]: products } =
|
|
||||||
invoke
|
|
||||||
|
|
||||||
return await createInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: products,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (_, { invoke }) => {
|
|
||||||
const shouldSkipStep_ = shouldSkipInventoryStep(
|
|
||||||
container,
|
|
||||||
CreateProductsWorkflowActions.createInventoryItems
|
|
||||||
)
|
|
||||||
|
|
||||||
const variantInventoryItemsData =
|
|
||||||
invoke[CreateProductsWorkflowActions.createInventoryItems]
|
|
||||||
|
|
||||||
if (shouldSkipStep_ || !variantInventoryItemsData?.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await removeInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: variantInventoryItemsData,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.attachInventoryItems]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const shouldSkipStep_ = shouldSkipInventoryStep(
|
|
||||||
container,
|
|
||||||
CreateProductsWorkflowActions.attachInventoryItems
|
|
||||||
)
|
|
||||||
if (shouldSkipStep_) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
[CreateProductsWorkflowActions.createInventoryItems]:
|
|
||||||
inventoryItemsResult,
|
|
||||||
} = invoke
|
|
||||||
|
|
||||||
return await attachInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: inventoryItemsResult,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const shouldSkipStep_ = shouldSkipInventoryStep(
|
|
||||||
container,
|
|
||||||
CreateProductsWorkflowActions.attachInventoryItems
|
|
||||||
)
|
|
||||||
|
|
||||||
const {
|
|
||||||
[CreateProductsWorkflowActions.createInventoryItems]:
|
|
||||||
inventoryItemsResult,
|
|
||||||
} = invoke
|
|
||||||
|
|
||||||
if (shouldSkipStep_ || !inventoryItemsResult?.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return await detachInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: inventoryItemsResult,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.createPrices]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const { productsHandleVariantsIndexPricesMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
|
|
||||||
return await updateProductsVariantsPrices({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
products,
|
|
||||||
productsHandleVariantsIndexPricesMap,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[TransactionHandlerType.COMPENSATE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const { productsHandleVariantsIndexPricesMap } = invoke[
|
|
||||||
CreateProductsWorkflowActions.prepare
|
|
||||||
] as CreateProductsPreparedData
|
|
||||||
const products = invoke[
|
|
||||||
CreateProductsWorkflowActions.createProducts
|
|
||||||
] as ProductTypes.ProductDTO[]
|
|
||||||
|
|
||||||
if (!productsHandleVariantsIndexPricesMap?.size) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedProductsHandleVariantsIndexPricesMap = new Map()
|
|
||||||
productsHandleVariantsIndexPricesMap.forEach((items, productHandle) => {
|
|
||||||
const existingItems =
|
|
||||||
updatedProductsHandleVariantsIndexPricesMap.get(productHandle) ?? []
|
|
||||||
|
|
||||||
items.forEach(({ index }) => {
|
|
||||||
existingItems.push({
|
|
||||||
index,
|
|
||||||
prices: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
updatedProductsHandleVariantsIndexPricesMap.set(productHandle, items)
|
|
||||||
})
|
|
||||||
|
|
||||||
return await updateProductsVariantsPrices({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data: {
|
|
||||||
products,
|
|
||||||
productsHandleVariantsIndexPricesMap:
|
|
||||||
updatedProductsHandleVariantsIndexPricesMap,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
[CreateProductsWorkflowActions.result]: {
|
|
||||||
[TransactionHandlerType.INVOKE]: async (
|
|
||||||
data: CreateProductsWorkflowInputData,
|
|
||||||
{ invoke }
|
|
||||||
) => {
|
|
||||||
const { [CreateProductsWorkflowActions.createProducts]: products } =
|
|
||||||
invoke
|
|
||||||
|
|
||||||
const productService = container.resolve(
|
|
||||||
"productService"
|
|
||||||
) as ProductService
|
|
||||||
const pricingService = container.resolve(
|
|
||||||
"pricingService"
|
|
||||||
) as PricingService
|
|
||||||
|
|
||||||
const rawProduct = await productService
|
|
||||||
.withTransaction(manager)
|
|
||||||
.retrieve(products[0].id, {
|
|
||||||
select: defaultAdminProductFields,
|
|
||||||
relations: defaultAdminProductRelations,
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await pricingService
|
|
||||||
.withTransaction(manager)
|
|
||||||
.setProductPrices([rawProduct])
|
|
||||||
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
actionId: string,
|
|
||||||
type: TransactionHandlerType,
|
|
||||||
payload: TransactionPayload
|
|
||||||
) => command[actionId][type](payload.data, payload.context)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { ulid } from "ulid"
|
|
||||||
import { MedusaError } from "@medusajs/utils"
|
|
||||||
import {
|
|
||||||
TransactionOrchestrator,
|
|
||||||
TransactionState,
|
|
||||||
} from "@medusajs/orchestration"
|
|
||||||
import { AdminPostProductsReq } from "../../../api"
|
|
||||||
import { Product } from "../../../models"
|
|
||||||
import { PricedProduct } from "../../../types/pricing"
|
|
||||||
import {
|
|
||||||
CreateProductsWorkflowActions,
|
|
||||||
transactionHandler,
|
|
||||||
workflowSteps,
|
|
||||||
} from "./definition"
|
|
||||||
import { InjectedDependencies } from "./types"
|
|
||||||
|
|
||||||
const createProductsOrchestrator = new TransactionOrchestrator(
|
|
||||||
"create-products",
|
|
||||||
workflowSteps
|
|
||||||
)
|
|
||||||
|
|
||||||
export async function createProductsWorkflow(
|
|
||||||
dependencies: InjectedDependencies,
|
|
||||||
input: AdminPostProductsReq[]
|
|
||||||
): Promise<(Product | PricedProduct)[]> {
|
|
||||||
const transaction = await createProductsOrchestrator.beginTransaction(
|
|
||||||
ulid(),
|
|
||||||
transactionHandler(dependencies),
|
|
||||||
input
|
|
||||||
)
|
|
||||||
|
|
||||||
await createProductsOrchestrator.resume(transaction)
|
|
||||||
|
|
||||||
if (transaction.getState() !== TransactionState.DONE) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
transaction
|
|
||||||
.getErrors()
|
|
||||||
.map((err) => err.error?.message)
|
|
||||||
.join("\n")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transaction.getContext().invoke[
|
|
||||||
CreateProductsWorkflowActions.result
|
|
||||||
] as (Product | PricedProduct)[]
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { MedusaContainer } from "@medusajs/types"
|
|
||||||
import { AdminPostProductsReq } from "../../../api"
|
|
||||||
|
|
||||||
export type InjectedDependencies = {
|
|
||||||
manager: EntityManager
|
|
||||||
container: MedusaContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CreateProductsWorkflowInputData = AdminPostProductsReq[]
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import {
|
|
||||||
InventoryItemDTO,
|
|
||||||
MedusaContainer,
|
|
||||||
ProductTypes,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { ProductVariantInventoryService } from "../../services"
|
|
||||||
|
|
||||||
export async function attachInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
manager: EntityManager
|
|
||||||
data: {
|
|
||||||
variant: ProductTypes.ProductVariantDTO
|
|
||||||
inventoryItem: InventoryItemDTO
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const productVariantInventoryService: ProductVariantInventoryService =
|
|
||||||
container.resolve("productVariantInventoryService").withTransaction(manager)
|
|
||||||
|
|
||||||
const inventoryData = data.map(({ variant, inventoryItem }) => ({
|
|
||||||
variantId: variant.id,
|
|
||||||
inventoryItemId: inventoryItem.id,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return await productVariantInventoryService.attachInventoryItem(inventoryData)
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import {
|
|
||||||
IInventoryService,
|
|
||||||
MedusaContainer,
|
|
||||||
ProductTypes,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
export async function createInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
manager: EntityManager
|
|
||||||
data: ProductTypes.ProductDTO[]
|
|
||||||
}) {
|
|
||||||
const inventoryService: IInventoryService =
|
|
||||||
container.resolve("inventoryService")
|
|
||||||
const context = { transactionManager: manager }
|
|
||||||
|
|
||||||
const variants = data.reduce(
|
|
||||||
(
|
|
||||||
acc: ProductTypes.ProductVariantDTO[],
|
|
||||||
product: ProductTypes.ProductDTO
|
|
||||||
) => {
|
|
||||||
return acc.concat(product.variants)
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
variants.map(async (variant) => {
|
|
||||||
if (!variant.manage_inventory) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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!,
|
|
||||||
},
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
return { variant, inventoryItem }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
export type CreateProductsData = ProductTypes.CreateProductDTO[]
|
|
||||||
|
|
||||||
export async function createProducts({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
data: CreateProductsData
|
|
||||||
}): Promise<ProductTypes.ProductDTO[]> {
|
|
||||||
const productModuleService: ProductTypes.IProductModuleService =
|
|
||||||
container.resolve("productModuleService")
|
|
||||||
|
|
||||||
return await productModuleService.create(data)
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import {
|
|
||||||
InventoryItemDTO,
|
|
||||||
MedusaContainer,
|
|
||||||
ProductTypes,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { ProductVariantInventoryService } from "../../services"
|
|
||||||
|
|
||||||
export async function detachInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
manager: EntityManager
|
|
||||||
data: {
|
|
||||||
variant: ProductTypes.ProductVariantDTO
|
|
||||||
inventoryItem: InventoryItemDTO
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const productVariantInventoryService: ProductVariantInventoryService =
|
|
||||||
container.resolve("productVariantInventoryService").withTransaction(manager)
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
data.map(async ({ variant, inventoryItem }) => {
|
|
||||||
return await productVariantInventoryService.detachInventoryItem(
|
|
||||||
inventoryItem.id,
|
|
||||||
variant.id
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { SalesChannelService, ShippingProfileService } from "../../services"
|
|
||||||
import { kebabCase } from "@medusajs/utils"
|
|
||||||
import { FlagRouter } from "../../utils/flag-router"
|
|
||||||
import SalesChannelFeatureFlag from "../../loaders/feature-flags/sales-channels"
|
|
||||||
import { SalesChannel, ShippingProfileType } from "../../models"
|
|
||||||
import { ProductVariantPricesCreateReq } from "../../types/product-variant"
|
|
||||||
import { AdminPostProductsReq } from "../../api"
|
|
||||||
|
|
||||||
type CreateProductsInputData = AdminPostProductsReq[]
|
|
||||||
|
|
||||||
type ShippingProfileId = string
|
|
||||||
type SalesChannelId = string
|
|
||||||
type ProductHandle = string
|
|
||||||
type VariantIndexAndPrices = {
|
|
||||||
index: number
|
|
||||||
prices: ProductVariantPricesCreateReq[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CreateProductsPreparedData = {
|
|
||||||
products: ProductTypes.CreateProductDTO[]
|
|
||||||
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
|
||||||
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
|
||||||
productsHandleVariantsIndexPricesMap: Map<
|
|
||||||
ProductHandle,
|
|
||||||
VariantIndexAndPrices[]
|
|
||||||
>
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function prepareCreateProductsData({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
manager: EntityManager
|
|
||||||
data: CreateProductsInputData
|
|
||||||
}): Promise<CreateProductsPreparedData> {
|
|
||||||
const shippingProfileService: ShippingProfileService = container
|
|
||||||
.resolve("shippingProfileService")
|
|
||||||
.withTransaction(manager)
|
|
||||||
const featureFlagRouter: FlagRouter = container.resolve("featureFlagRouter")
|
|
||||||
const salesChannelService: SalesChannelService = container
|
|
||||||
.resolve("salesChannelService")
|
|
||||||
.withTransaction(manager)
|
|
||||||
|
|
||||||
const shippingProfileServiceTx =
|
|
||||||
shippingProfileService.withTransaction(manager)
|
|
||||||
|
|
||||||
const shippingProfiles = await shippingProfileServiceTx.list({
|
|
||||||
type: [ShippingProfileType.DEFAULT, ShippingProfileType.GIFT_CARD],
|
|
||||||
})
|
|
||||||
const defaultShippingProfile = shippingProfiles.find(
|
|
||||||
(sp) => sp.type === ShippingProfileType.DEFAULT
|
|
||||||
)
|
|
||||||
const gitCardShippingProfile = shippingProfiles.find(
|
|
||||||
(sp) => sp.type === ShippingProfileType.GIFT_CARD
|
|
||||||
)
|
|
||||||
|
|
||||||
let defaultSalesChannel: SalesChannel | undefined
|
|
||||||
if (featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key)) {
|
|
||||||
defaultSalesChannel = await salesChannelService
|
|
||||||
.withTransaction(manager)
|
|
||||||
.retrieveDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const productsHandleShippingProfileIdMap = new Map<
|
|
||||||
ProductHandle,
|
|
||||||
ShippingProfileId
|
|
||||||
>()
|
|
||||||
const productsHandleSalesChannelsMap = new Map<
|
|
||||||
ProductHandle,
|
|
||||||
SalesChannelId[]
|
|
||||||
>()
|
|
||||||
const productsHandleVariantsIndexPricesMap = new Map<
|
|
||||||
ProductHandle,
|
|
||||||
VariantIndexAndPrices[]
|
|
||||||
>()
|
|
||||||
|
|
||||||
for (const product of data) {
|
|
||||||
product.handle ??= kebabCase(product.title)
|
|
||||||
|
|
||||||
if (product.is_giftcard) {
|
|
||||||
productsHandleShippingProfileIdMap.set(
|
|
||||||
product.handle!,
|
|
||||||
gitCardShippingProfile!.id
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
productsHandleShippingProfileIdMap.set(
|
|
||||||
product.handle!,
|
|
||||||
defaultShippingProfile!.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key) &&
|
|
||||||
!product.sales_channels?.length
|
|
||||||
) {
|
|
||||||
productsHandleSalesChannelsMap.set(product.handle!, [
|
|
||||||
defaultSalesChannel!.id,
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
productsHandleSalesChannelsMap.set(
|
|
||||||
product.handle!,
|
|
||||||
product.sales_channels!.map((s) => s.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (product.variants) {
|
|
||||||
const hasPrices = product.variants.some((variant) => {
|
|
||||||
return variant.prices.length > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
if (hasPrices) {
|
|
||||||
const items =
|
|
||||||
productsHandleVariantsIndexPricesMap.get(product.handle!) ?? []
|
|
||||||
|
|
||||||
product.variants.forEach((variant, index) => {
|
|
||||||
items.push({
|
|
||||||
index,
|
|
||||||
prices: variant.prices,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
productsHandleVariantsIndexPricesMap.set(product.handle!, items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data = data.map((productData) => {
|
|
||||||
delete productData.sales_channels
|
|
||||||
return productData
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
products: data as ProductTypes.CreateProductDTO[],
|
|
||||||
productsHandleShippingProfileIdMap,
|
|
||||||
productsHandleSalesChannelsMap,
|
|
||||||
productsHandleVariantsIndexPricesMap,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { InventoryItemDTO, MedusaContainer } from "@medusajs/types"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
export async function removeInventoryItems({
|
|
||||||
container,
|
|
||||||
manager,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
manager: EntityManager
|
|
||||||
data: {
|
|
||||||
inventoryItem: InventoryItemDTO
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const inventoryService = container.resolve("inventoryService")
|
|
||||||
const context = { transactionManager: manager }
|
|
||||||
|
|
||||||
return await inventoryService!.deleteInventoryItem(
|
|
||||||
data.map(({ inventoryItem }) => inventoryItem.id),
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
export async function removeProducts({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
container: MedusaContainer
|
|
||||||
data: ProductTypes.ProductDTO[]
|
|
||||||
}) {
|
|
||||||
const productModuleService: ProductTypes.IProductModuleService =
|
|
||||||
container.resolve("productModuleService")
|
|
||||||
return await productModuleService.softDelete(data.map((p) => p.id))
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,12 @@ import {
|
|||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
import { ProductTypes } from "@medusajs/types"
|
import {
|
||||||
import { DALUtils, generateEntityId, kebabCase } from "@medusajs/utils"
|
DALUtils,
|
||||||
|
generateEntityId,
|
||||||
|
kebabCase,
|
||||||
|
ProductUtils,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import ProductCategory from "./product-category"
|
import ProductCategory from "./product-category"
|
||||||
import ProductCollection from "./product-collection"
|
import ProductCollection from "./product-collection"
|
||||||
import ProductOption from "./product-option"
|
import ProductOption from "./product-option"
|
||||||
@@ -60,8 +64,8 @@ class Product {
|
|||||||
@Property({ columnType: "boolean", default: false })
|
@Property({ columnType: "boolean", default: false })
|
||||||
is_giftcard!: boolean
|
is_giftcard!: boolean
|
||||||
|
|
||||||
@Enum(() => ProductTypes.ProductStatus)
|
@Enum(() => ProductUtils.ProductStatus)
|
||||||
status!: ProductTypes.ProductStatus
|
status!: ProductUtils.ProductStatus
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
thumbnail?: string | null
|
thumbnail?: string | null
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Context,
|
Context,
|
||||||
DAL,
|
DAL,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
ProductStatus,
|
|
||||||
ProductTypes,
|
ProductTypes,
|
||||||
WithRequiredProperty,
|
WithRequiredProperty,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
|
ProductUtils,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { ProductRepository } from "@repositories"
|
import { ProductRepository } from "@repositories"
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ export default class ProductService<TEntity extends Product = Product> {
|
|||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TEntity[]> {
|
): Promise<TEntity[]> {
|
||||||
data.forEach((product) => {
|
data.forEach((product) => {
|
||||||
product.status ??= ProductStatus.DRAFT
|
product.status ??= ProductUtils.ProductStatus.DRAFT
|
||||||
})
|
})
|
||||||
|
|
||||||
return (await (this.productRepository_ as ProductRepository).create(
|
return (await (this.productRepository_ as ProductRepository).create(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ProductStatus, ProductCategoryDTO } from "@medusajs/types"
|
import { ProductUtils } from "@medusajs/utils"
|
||||||
|
|
||||||
export type ProductEventData = {
|
export type ProductEventData = {
|
||||||
id: string
|
id: string
|
||||||
@@ -20,7 +20,7 @@ export interface UpdateProductDTO {
|
|||||||
images?: { id?: string; url: string }[]
|
images?: { id?: string; url: string }[]
|
||||||
thumbnail?: string
|
thumbnail?: string
|
||||||
handle?: string
|
handle?: string
|
||||||
status?: ProductStatus
|
status?: ProductUtils.ProductStatus
|
||||||
collection_id?: string
|
collection_id?: string
|
||||||
width?: number
|
width?: number
|
||||||
height?: number
|
height?: number
|
||||||
|
|||||||
@@ -9,3 +9,6 @@ export * as StockLocationTypes from "./stock-location"
|
|||||||
export * as TransactionBaseTypes from "./transaction-base"
|
export * as TransactionBaseTypes from "./transaction-base"
|
||||||
export * as DAL from "./dal"
|
export * as DAL from "./dal"
|
||||||
export * as LoggerTypes from "./logger"
|
export * as LoggerTypes from "./logger"
|
||||||
|
export * as FeatureFlagTypes from "./feature-flag"
|
||||||
|
export * as SalesChannelTypes from "./sales-channel"
|
||||||
|
export * as WorkflowTypes from "./workflow"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
export interface IFlagRouter {
|
||||||
|
isFeatureEnabled: (key: string) => boolean
|
||||||
|
listFlags: () => FeatureFlagsResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @schema FeatureFlagsResponse
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* type: object
|
||||||
|
* required:
|
||||||
|
* - key
|
||||||
|
* - value
|
||||||
|
* properties:
|
||||||
|
* key:
|
||||||
|
* description: The key of the feature flag.
|
||||||
|
* type: string
|
||||||
|
* value:
|
||||||
|
* description: The value of the feature flag.
|
||||||
|
* type: boolean
|
||||||
|
*/
|
||||||
|
export type FeatureFlagsResponse = {
|
||||||
|
key: string
|
||||||
|
value: boolean
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export type FlagSettings = {
|
||||||
|
key: string
|
||||||
|
description: string
|
||||||
|
env_key: string
|
||||||
|
default_val: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./common"
|
||||||
@@ -13,3 +13,6 @@ export * from "./stock-location"
|
|||||||
export * from "./transaction-base"
|
export * from "./transaction-base"
|
||||||
export * from "./dal"
|
export * from "./dal"
|
||||||
export * from "./logger"
|
export * from "./logger"
|
||||||
|
export * from "./feature-flag"
|
||||||
|
export * from "./sales-channel"
|
||||||
|
export * from "./workflow"
|
||||||
@@ -425,7 +425,7 @@ export interface CreateProductVariantOnlyDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProductVariantOnlyDTO {
|
export interface UpdateProductVariantOnlyDTO {
|
||||||
id: string,
|
id: string
|
||||||
title?: string
|
title?: string
|
||||||
sku?: string
|
sku?: string
|
||||||
barcode?: string
|
barcode?: string
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export interface SalesChannelLocationDTO {
|
||||||
|
sales_channel_id: string
|
||||||
|
location_id: string
|
||||||
|
sales_channel: SalesChannelDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SalesChannelDTO {
|
||||||
|
id: string
|
||||||
|
description: string | null
|
||||||
|
is_disabled: boolean
|
||||||
|
metadata: Record<string, unknown> | null
|
||||||
|
locations?: SalesChannelLocationDTO[]
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./common"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export interface WorkflowInputConfig {
|
||||||
|
listConfig?: {
|
||||||
|
select?: string[]
|
||||||
|
relations?: string[]
|
||||||
|
}
|
||||||
|
retrieveConfig?: {
|
||||||
|
select?: string[]
|
||||||
|
relations?: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * as ProductWorkflow from "./product"
|
||||||
|
export * as CommonWorkflow from "./common"
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { ProductStatus } from "../../product"
|
||||||
|
import { WorkflowInputConfig } from "../common"
|
||||||
|
|
||||||
|
export interface CreateProductTypeInputDTO {
|
||||||
|
id?: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductTagInputDTO {
|
||||||
|
id?: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductSalesChannelInputDTO {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CraeteProductProductCategoryInputDTO {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductOptionInputDTO {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductVariantPricesInputDTO {
|
||||||
|
id?: string
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CraeteProductVariantOptionInputDTO {
|
||||||
|
value: string
|
||||||
|
option_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductVariantInputDTO {
|
||||||
|
id?: string
|
||||||
|
title?: string
|
||||||
|
sku?: string
|
||||||
|
ean?: string
|
||||||
|
upc?: string
|
||||||
|
barcode?: string
|
||||||
|
hs_code?: string
|
||||||
|
inventory_quantity?: number
|
||||||
|
allow_backorder?: boolean
|
||||||
|
manage_inventory?: boolean
|
||||||
|
weight?: number
|
||||||
|
length?: number
|
||||||
|
height?: number
|
||||||
|
width?: number
|
||||||
|
origin_country?: string
|
||||||
|
mid_code?: string
|
||||||
|
material?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
|
||||||
|
prices?: CreateProductVariantPricesInputDTO[]
|
||||||
|
options?: CraeteProductVariantOptionInputDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductInputDTO {
|
||||||
|
title: string
|
||||||
|
subtitle?: string
|
||||||
|
description?: string
|
||||||
|
is_giftcard?: boolean
|
||||||
|
discountable?: boolean
|
||||||
|
images?: string[]
|
||||||
|
thumbnail?: string
|
||||||
|
handle?: string
|
||||||
|
status?: ProductStatus
|
||||||
|
type?: CreateProductTypeInputDTO
|
||||||
|
collection_id?: string
|
||||||
|
tags?: CreateProductTagInputDTO[]
|
||||||
|
categories?: CraeteProductProductCategoryInputDTO[]
|
||||||
|
options?: CreateProductOptionInputDTO[]
|
||||||
|
variants?: CreateProductVariantInputDTO[]
|
||||||
|
weight?: number
|
||||||
|
length?: number
|
||||||
|
height?: number
|
||||||
|
width?: number
|
||||||
|
hs_code?: string
|
||||||
|
origin_country?: string
|
||||||
|
mid_code?: string
|
||||||
|
material?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
|
||||||
|
sales_channels?: CreateProductSalesChannelInputDTO[]
|
||||||
|
listConfig: {
|
||||||
|
select: string[]
|
||||||
|
relations: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProductsWorkflowInputDTO {
|
||||||
|
products: CreateProductInputDTO[]
|
||||||
|
config?: WorkflowInputConfig
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./create-products"
|
||||||
@@ -3,3 +3,6 @@ export * as EventBusUtils from "./event-bus"
|
|||||||
export * as SearchUtils from "./search"
|
export * as SearchUtils from "./search"
|
||||||
export * as ModulesSdkUtils from "./modules-sdk"
|
export * as ModulesSdkUtils from "./modules-sdk"
|
||||||
export * as DALUtils from "./dal"
|
export * as DALUtils from "./dal"
|
||||||
|
export * as FeatureFlagUtils from "./feature-flags"
|
||||||
|
export * as ShippingProfileUtils from "./shipping"
|
||||||
|
export * as ProductUtils from "./product"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const AnalyticsFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "analytics",
|
||||||
|
default_val: true,
|
||||||
|
env_key: "MEDUSA_FF_ANALYTICS",
|
||||||
|
description:
|
||||||
|
"Enable Medusa to collect data on usage, errors and performance for the purpose of improving the product",
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export * from "./analytics"
|
||||||
|
export * from "./order-editing"
|
||||||
|
export * from "./sales-channels"
|
||||||
|
export * from "./product-categories"
|
||||||
|
export * from "./publishable-api-keys"
|
||||||
|
export * from "./tax-inclusive-pricing"
|
||||||
|
export * from "./utils"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const OrderEditingFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "order_editing",
|
||||||
|
default_val: true,
|
||||||
|
env_key: "MEDUSA_FF_ORDER_EDITING",
|
||||||
|
description: "[WIP] Enable the order editing feature",
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const ProductCategoryFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "product_categories",
|
||||||
|
default_val: false,
|
||||||
|
env_key: "MEDUSA_FF_PRODUCT_CATEGORIES",
|
||||||
|
description: "[WIP] Enable the product categories feature",
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const PublishableAPIKeysFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "publishable_api_keys",
|
||||||
|
default_val: true,
|
||||||
|
env_key: "MEDUSA_FF_PUBLISHABLE_API_KEYS",
|
||||||
|
description: "[WIP] Enable the publishable API keys feature",
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const SalesChannelFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "sales_channels",
|
||||||
|
default_val: true,
|
||||||
|
env_key: "MEDUSA_FF_SALES_CHANNELS",
|
||||||
|
description: "[WIP] Enable the sales channels feature",
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const TaxInclusivePricingFeatureFlag: FeatureFlagTypes.FlagSettings = {
|
||||||
|
key: "tax_inclusive_pricing",
|
||||||
|
default_val: false,
|
||||||
|
env_key: "MEDUSA_FF_TAX_INCLUSIVE_PRICING",
|
||||||
|
description: "[WIP] Enable tax inclusive pricing",
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { FeatureFlagTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export class FlagRouter implements FeatureFlagTypes.IFlagRouter {
|
||||||
|
private readonly flags: Record<string, boolean> = {}
|
||||||
|
|
||||||
|
constructor(flags: Record<string, boolean>) {
|
||||||
|
this.flags = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFeatureEnabled(key: string): boolean {
|
||||||
|
return !!this.flags[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
public setFlag(key: string, value = true): void {
|
||||||
|
this.flags[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public listFlags(): FeatureFlagTypes.FeatureFlagsResponse {
|
||||||
|
return Object.entries(this.flags || {}).map(([key, value]) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./flag-router"
|
||||||
@@ -6,3 +6,6 @@ export * from "./event-bus"
|
|||||||
export * from "./search"
|
export * from "./search"
|
||||||
export * from "./modules-sdk"
|
export * from "./modules-sdk"
|
||||||
export * from "./dal"
|
export * from "./dal"
|
||||||
|
export * from "./feature-flags"
|
||||||
|
export * from "./shipping"
|
||||||
|
export * from "./product"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export enum ProductStatus {
|
||||||
|
DRAFT = "draft",
|
||||||
|
PROPOSED = "proposed",
|
||||||
|
PUBLISHED = "published",
|
||||||
|
REJECTED = "rejected",
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum ShippingProfileType {
|
||||||
|
DEFAULT = "default",
|
||||||
|
GIFT_CARD = "gift_card",
|
||||||
|
CUSTOM = "custom",
|
||||||
|
}
|
||||||
@@ -3,46 +3,284 @@ import {
|
|||||||
TransactionStepsDefinition,
|
TransactionStepsDefinition,
|
||||||
WorkflowManager,
|
WorkflowManager,
|
||||||
} from "@medusajs/orchestration"
|
} from "@medusajs/orchestration"
|
||||||
import {
|
|
||||||
createProducts as createProductsHandler,
|
|
||||||
removeProducts,
|
|
||||||
} from "../handlers"
|
|
||||||
import { exportWorkflow, pipe } from "../helper"
|
import { exportWorkflow, pipe } from "../helper"
|
||||||
|
|
||||||
import { ProductTypes } from "@medusajs/types"
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
InventoryHandlers,
|
||||||
|
MiddlewaresHandlers,
|
||||||
|
ProductHandlers,
|
||||||
|
} from "../handlers"
|
||||||
|
import { aggregateData } from "../helper/aggregate"
|
||||||
|
|
||||||
enum Actions {
|
export enum Actions {
|
||||||
|
prepare = "prepare",
|
||||||
createProducts = "createProducts",
|
createProducts = "createProducts",
|
||||||
|
attachToSalesChannel = "attachToSalesChannel",
|
||||||
|
attachShippingProfile = "attachShippingProfile",
|
||||||
|
createPrices = "createPrices",
|
||||||
|
createInventoryItems = "createInventoryItems",
|
||||||
|
attachInventoryItems = "attachInventoryItems",
|
||||||
|
result = "result",
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowSteps: TransactionStepsDefinition = {
|
export const workflowSteps: TransactionStepsDefinition = {
|
||||||
next: {
|
next: {
|
||||||
action: Actions.createProducts,
|
action: Actions.prepare,
|
||||||
|
noCompensation: true,
|
||||||
|
next: {
|
||||||
|
action: Actions.createProducts,
|
||||||
|
next: [
|
||||||
|
{
|
||||||
|
action: Actions.attachShippingProfile,
|
||||||
|
saveResponse: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: Actions.attachToSalesChannel,
|
||||||
|
saveResponse: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: Actions.createPrices,
|
||||||
|
next: {
|
||||||
|
action: Actions.createInventoryItems,
|
||||||
|
next: {
|
||||||
|
action: Actions.attachInventoryItems,
|
||||||
|
next: {
|
||||||
|
action: Actions.result,
|
||||||
|
noCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = new Map([
|
const handlers = new Map([
|
||||||
|
[
|
||||||
|
Actions.prepare,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
inputAlias: InputAlias.ProductsInputData,
|
||||||
|
invoke: {
|
||||||
|
from: InputAlias.ProductsInputData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
MiddlewaresHandlers.createProductsPrepareData
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
[
|
[
|
||||||
Actions.createProducts,
|
Actions.createProducts,
|
||||||
{
|
{
|
||||||
invoke: pipe(
|
invoke: pipe(
|
||||||
{
|
{
|
||||||
inputAlias: InputAlias.Products,
|
|
||||||
invoke: {
|
invoke: {
|
||||||
from: InputAlias.Products,
|
from: Actions.prepare,
|
||||||
alias: InputAlias.Products,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
createProductsHandler
|
aggregateData(),
|
||||||
|
ProductHandlers.createProducts
|
||||||
),
|
),
|
||||||
compensate: pipe(
|
compensate: pipe(
|
||||||
{
|
{
|
||||||
invoke: {
|
invoke: {
|
||||||
from: Actions.createProducts,
|
from: Actions.createProducts,
|
||||||
alias: InputAlias.Products,
|
alias: ProductHandlers.removeProducts.aliases.products,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
removeProducts
|
aggregateData(),
|
||||||
|
ProductHandlers.removeProducts
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.attachShippingProfile,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.attachShippingProfileToProducts.aliases
|
||||||
|
.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.attachShippingProfileToProducts
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.detachShippingProfileFromProducts.aliases
|
||||||
|
.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.detachShippingProfileFromProducts
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.attachToSalesChannel,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.attachSalesChannelToProducts.aliases.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.attachSalesChannelToProducts
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.detachSalesChannelFromProducts.aliases.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.detachSalesChannelFromProducts
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.createInventoryItems,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias: InventoryHandlers.createInventoryItems.aliases.products,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
InventoryHandlers.createInventoryItems
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: Actions.createInventoryItems,
|
||||||
|
alias:
|
||||||
|
InventoryHandlers.removeInventoryItems.aliases.inventoryItems,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
InventoryHandlers.removeInventoryItems
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.attachInventoryItems,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: Actions.createInventoryItems,
|
||||||
|
alias:
|
||||||
|
InventoryHandlers.attachInventoryItems.aliases.inventoryItems,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
InventoryHandlers.attachInventoryItems
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: {
|
||||||
|
from: Actions.createInventoryItems,
|
||||||
|
alias:
|
||||||
|
InventoryHandlers.detachInventoryItems.aliases.inventoryItems,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
InventoryHandlers.detachInventoryItems
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.createPrices,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.updateProductsVariantsPrices.aliases.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.updateProductsVariantsPrices
|
||||||
|
),
|
||||||
|
compensate: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias:
|
||||||
|
ProductHandlers.updateProductsVariantsPrices.aliases.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
MiddlewaresHandlers.createProductsPrepareCreatePricesCompensation,
|
||||||
|
ProductHandlers.updateProductsVariantsPrices
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Actions.result,
|
||||||
|
{
|
||||||
|
invoke: pipe(
|
||||||
|
{
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
from: Actions.prepare,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: Actions.createProducts,
|
||||||
|
alias: ProductHandlers.listProducts.aliases.products,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aggregateData(),
|
||||||
|
ProductHandlers.listProducts
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -51,6 +289,6 @@ const handlers = new Map([
|
|||||||
WorkflowManager.register(Workflows.CreateProducts, workflowSteps, handlers)
|
WorkflowManager.register(Workflows.CreateProducts, workflowSteps, handlers)
|
||||||
|
|
||||||
export const createProducts = exportWorkflow<
|
export const createProducts = exportWorkflow<
|
||||||
ProductTypes.CreateProductDTO[],
|
WorkflowTypes.ProductWorkflow.CreateProductsWorkflowInputDTO,
|
||||||
ProductTypes.ProductDTO[]
|
ProductTypes.ProductDTO[]
|
||||||
>(Workflows.CreateProducts, Actions.createProducts)
|
>(Workflows.CreateProducts, Actions.result)
|
||||||
@@ -4,6 +4,7 @@ export enum Workflows {
|
|||||||
|
|
||||||
export enum InputAlias {
|
export enum InputAlias {
|
||||||
Products = "products",
|
Products = "products",
|
||||||
|
ProductsInputData = "productsInputData",
|
||||||
RemovedProducts = "removedProducts",
|
RemovedProducts = "removedProducts",
|
||||||
|
|
||||||
InventoryItems = "inventoryItems",
|
InventoryItems = "inventoryItems",
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { InventoryItemDTO, ProductTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
import { InputAlias } from "../definitions"
|
|
||||||
import { WorkflowArguments } from "../helper"
|
|
||||||
import { ProductVariantInventoryItem } from "@medusajs/medusa/src"
|
|
||||||
|
|
||||||
export async function attachInventoryItems({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
}: WorkflowArguments & {
|
|
||||||
data: {
|
|
||||||
variant: ProductTypes.ProductVariantDTO
|
|
||||||
[InputAlias.InventoryItems]: InventoryItemDTO
|
|
||||||
}[]
|
|
||||||
}): Promise<ProductVariantInventoryItem[]> {
|
|
||||||
const manager = container.resolve("manager")
|
|
||||||
const productVariantInventoryService = container
|
|
||||||
.resolve("productVariantInventoryService")
|
|
||||||
.withTransaction(manager)
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
data
|
|
||||||
.filter((d) => d)
|
|
||||||
.map(async ({ variant, [InputAlias.InventoryItems]: inventoryItem }) => {
|
|
||||||
return await productVariantInventoryService.attachInventoryItem(
|
|
||||||
variant.id,
|
|
||||||
inventoryItem.id
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { InputAlias } from "../definitions"
|
|
||||||
import { ProductTypes } from "@medusajs/types"
|
|
||||||
import { WorkflowArguments } from "../helper"
|
|
||||||
|
|
||||||
export async function createProducts({
|
|
||||||
container,
|
|
||||||
context,
|
|
||||||
data,
|
|
||||||
}: WorkflowArguments & {
|
|
||||||
data: { [InputAlias.Products]: ProductTypes.CreateProductDTO[] }
|
|
||||||
}): Promise<ProductTypes.ProductDTO[]> {
|
|
||||||
const productModuleService = container.resolve("productModuleService")
|
|
||||||
|
|
||||||
return await productModuleService.create(data[InputAlias.Products], context)
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
export * from "./remove-products"
|
export * as ProductHandlers from "./product"
|
||||||
export * from "./create-products"
|
export * as InventoryHandlers from "./inventory"
|
||||||
export * from "./create-inventory-items"
|
export * as MiddlewaresHandlers from "./middlewares"
|
||||||
export * from "./remove-inventory-items"
|
|
||||||
export * from "./attach-inventory-items"
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { InventoryItemDTO, ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
export async function attachInventoryItems({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
inventoryItems: {
|
||||||
|
variant: ProductTypes.ProductVariantDTO
|
||||||
|
inventoryItem: InventoryItemDTO
|
||||||
|
}[]
|
||||||
|
}>) {
|
||||||
|
const { manager } = context
|
||||||
|
|
||||||
|
const productVariantInventoryService = container
|
||||||
|
.resolve("productVariantInventoryService")
|
||||||
|
.withTransaction(manager)
|
||||||
|
|
||||||
|
if (!data?.inventoryItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const inventoryData = data.inventoryItems.map(
|
||||||
|
({ variant, inventoryItem }) => ({
|
||||||
|
variantId: variant.id,
|
||||||
|
inventoryItemId: inventoryItem.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return await productVariantInventoryService.attachInventoryItem(inventoryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
attachInventoryItems.aliases = {
|
||||||
|
inventoryItems: "inventoryItems",
|
||||||
|
}
|
||||||
+29
-13
@@ -3,25 +3,32 @@ import {
|
|||||||
InventoryItemDTO,
|
InventoryItemDTO,
|
||||||
ProductTypes,
|
ProductTypes,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
import { InputAlias } from "../definitions"
|
type Result = {
|
||||||
import { WorkflowArguments } from "../helper"
|
variant: ProductTypes.ProductVariantDTO
|
||||||
|
inventoryItem: InventoryItemDTO
|
||||||
|
}[]
|
||||||
|
|
||||||
export async function createInventoryItems({
|
export async function createInventoryItems({
|
||||||
container,
|
container,
|
||||||
|
context,
|
||||||
data,
|
data,
|
||||||
}: WorkflowArguments & {
|
}: WorkflowArguments<{
|
||||||
data: {
|
products: ProductTypes.ProductDTO[]
|
||||||
[InputAlias.Products]: ProductTypes.ProductDTO[]
|
}>): Promise<Result | void> {
|
||||||
}
|
|
||||||
}): Promise<{ variant: any; inventoryItem: InventoryItemDTO }[]> {
|
|
||||||
const manager = container.resolve("manager")
|
|
||||||
const inventoryService: IInventoryService =
|
const inventoryService: IInventoryService =
|
||||||
container.resolve("inventoryService")
|
container.resolve("inventoryService")
|
||||||
const context = { transactionManager: manager }
|
|
||||||
|
|
||||||
const products = data[InputAlias.Products]
|
if (!inventoryService) {
|
||||||
const variants = products.reduce(
|
const logger = container.resolve("logger")
|
||||||
|
logger.warn(
|
||||||
|
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The 'createInventoryItems' will be skipped.`
|
||||||
|
)
|
||||||
|
return void 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const variants = data.products.reduce(
|
||||||
(
|
(
|
||||||
acc: ProductTypes.ProductVariantDTO[],
|
acc: ProductTypes.ProductVariantDTO[],
|
||||||
product: ProductTypes.ProductDTO
|
product: ProductTypes.ProductDTO
|
||||||
@@ -31,7 +38,7 @@ export async function createInventoryItems({
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
return await Promise.all(
|
const result = await Promise.all(
|
||||||
variants.map(async (variant) => {
|
variants.map(async (variant) => {
|
||||||
if (!variant.manage_inventory) {
|
if (!variant.manage_inventory) {
|
||||||
return
|
return
|
||||||
@@ -49,10 +56,19 @@ export async function createInventoryItems({
|
|||||||
height: variant.height!,
|
height: variant.height!,
|
||||||
width: variant.width!,
|
width: variant.width!,
|
||||||
},
|
},
|
||||||
context
|
{
|
||||||
|
transactionManager: (context.transactionManager ??
|
||||||
|
context.manager) as any,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return { variant, inventoryItem }
|
return { variant, inventoryItem }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return result.filter(Boolean) as Result
|
||||||
|
}
|
||||||
|
|
||||||
|
createInventoryItems.aliases = {
|
||||||
|
products: "products",
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { InventoryItemDTO, ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
export async function detachInventoryItems({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
inventoryItems: {
|
||||||
|
variant: ProductTypes.ProductVariantDTO
|
||||||
|
inventoryItem: InventoryItemDTO
|
||||||
|
}[]
|
||||||
|
}>): Promise<void> {
|
||||||
|
const { manager } = context
|
||||||
|
|
||||||
|
const productVariantInventoryService = container
|
||||||
|
.resolve("productVariantInventoryService")
|
||||||
|
.withTransaction(manager)
|
||||||
|
|
||||||
|
if (!data?.inventoryItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
data.inventoryItems.map(async ({ variant, inventoryItem }) => {
|
||||||
|
return await productVariantInventoryService.detachInventoryItem(
|
||||||
|
inventoryItem.id,
|
||||||
|
variant.id
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
detachInventoryItems.aliases = {
|
||||||
|
inventoryItems: "inventoryItems",
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./detach-inventory-items"
|
||||||
|
export * from "./attach-inventory-items"
|
||||||
|
export * from "./create-inventory-items"
|
||||||
|
export * from "./remove-inventory-items"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { InventoryItemDTO } from "@medusajs/types"
|
||||||
|
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) {
|
||||||
|
const logger = container.resolve("logger")
|
||||||
|
logger.warn(
|
||||||
|
`Inventory service not found. You should install the @medusajs/inventory package to use inventory. The 'removeInventoryItems' will be skipped.`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return await inventoryService!.deleteInventoryItem(
|
||||||
|
data.inventoryItems.map(({ inventoryItem }) => inventoryItem.id),
|
||||||
|
{ transactionManager: manager }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeInventoryItems.aliases = {
|
||||||
|
inventoryItems: "inventoryItems",
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type ProductHandle = string
|
||||||
|
type VariantIndexAndPrices = {
|
||||||
|
index: number
|
||||||
|
prices: WorkflowTypes.ProductWorkflow.CreateProductVariantPricesInputDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProductsPrepareCreatePricesCompensation({
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
productsHandleVariantsIndexPricesMap: Map<
|
||||||
|
ProductHandle,
|
||||||
|
VariantIndexAndPrices[]
|
||||||
|
>
|
||||||
|
products: ProductTypes.ProductDTO[]
|
||||||
|
}>) {
|
||||||
|
const productsHandleVariantsIndexPricesMap =
|
||||||
|
data.productsHandleVariantsIndexPricesMap
|
||||||
|
const products = data.products
|
||||||
|
|
||||||
|
const updatedProductsHandleVariantsIndexPricesMap = new Map()
|
||||||
|
productsHandleVariantsIndexPricesMap.forEach((items, productHandle) => {
|
||||||
|
const existingItems =
|
||||||
|
updatedProductsHandleVariantsIndexPricesMap.get(productHandle) ?? []
|
||||||
|
|
||||||
|
items.forEach(({ index }) => {
|
||||||
|
existingItems.push({
|
||||||
|
index,
|
||||||
|
prices: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
updatedProductsHandleVariantsIndexPricesMap.set(productHandle, items)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
alias: "",
|
||||||
|
value: {
|
||||||
|
productsHandleVariantsIndexPricesMap:
|
||||||
|
updatedProductsHandleVariantsIndexPricesMap,
|
||||||
|
products,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createProductsPrepareCreatePricesCompensation.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -1,8 +1,154 @@
|
|||||||
import { InputAlias } from "../../definitions"
|
import { ProductTypes, SalesChannelTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
FeatureFlagUtils,
|
||||||
|
kebabCase,
|
||||||
|
ShippingProfileUtils,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
type ShippingProfileId = string
|
||||||
|
type SalesChannelId = string
|
||||||
|
type ProductHandle = string
|
||||||
|
type VariantIndexAndPrices = {
|
||||||
|
index: number
|
||||||
|
prices: {
|
||||||
|
region_id?: string
|
||||||
|
currency_code?: string
|
||||||
|
amount: number
|
||||||
|
min_quantity?: number
|
||||||
|
max_quantity?: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateProductsPreparedData = {
|
||||||
|
products: ProductTypes.CreateProductDTO[]
|
||||||
|
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
||||||
|
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
||||||
|
productsHandleVariantsIndexPricesMap: Map<
|
||||||
|
ProductHandle,
|
||||||
|
VariantIndexAndPrices[]
|
||||||
|
>
|
||||||
|
config?: WorkflowTypes.CommonWorkflow.WorkflowInputConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProductsPrepareData({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<WorkflowTypes.ProductWorkflow.CreateProductsWorkflowInputDTO>): Promise<CreateProductsPreparedData> {
|
||||||
|
const { manager } = context
|
||||||
|
let products = data.products
|
||||||
|
|
||||||
|
const shippingProfileService = container
|
||||||
|
.resolve("shippingProfileService")
|
||||||
|
.withTransaction(manager)
|
||||||
|
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||||
|
const salesChannelService = container
|
||||||
|
.resolve("salesChannelService")
|
||||||
|
.withTransaction(manager)
|
||||||
|
const salesChannelFeatureFlagKey =
|
||||||
|
FeatureFlagUtils.SalesChannelFeatureFlag.key
|
||||||
|
|
||||||
|
const shippingProfileServiceTx =
|
||||||
|
shippingProfileService.withTransaction(manager)
|
||||||
|
|
||||||
|
const shippingProfiles = await shippingProfileServiceTx.list({
|
||||||
|
type: [
|
||||||
|
ShippingProfileUtils.ShippingProfileType.DEFAULT,
|
||||||
|
ShippingProfileUtils.ShippingProfileType.GIFT_CARD,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const defaultShippingProfile = shippingProfiles.find(
|
||||||
|
(sp) => sp.type === ShippingProfileUtils.ShippingProfileType.DEFAULT
|
||||||
|
)
|
||||||
|
const gitCardShippingProfile = shippingProfiles.find(
|
||||||
|
(sp) => sp.type === ShippingProfileUtils.ShippingProfileType.GIFT_CARD
|
||||||
|
)
|
||||||
|
|
||||||
|
let defaultSalesChannel: SalesChannelTypes.SalesChannelDTO | undefined
|
||||||
|
if (featureFlagRouter.isFeatureEnabled(salesChannelFeatureFlagKey)) {
|
||||||
|
defaultSalesChannel = await salesChannelService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieveDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
const productsHandleShippingProfileIdMap = new Map<
|
||||||
|
ProductHandle,
|
||||||
|
ShippingProfileId
|
||||||
|
>()
|
||||||
|
const productsHandleSalesChannelsMap = new Map<
|
||||||
|
ProductHandle,
|
||||||
|
SalesChannelId[]
|
||||||
|
>()
|
||||||
|
const productsHandleVariantsIndexPricesMap = new Map<
|
||||||
|
ProductHandle,
|
||||||
|
VariantIndexAndPrices[]
|
||||||
|
>()
|
||||||
|
|
||||||
|
for (const product of products) {
|
||||||
|
product.handle ??= kebabCase(product.title)
|
||||||
|
|
||||||
|
if (product.is_giftcard) {
|
||||||
|
productsHandleShippingProfileIdMap.set(
|
||||||
|
product.handle!,
|
||||||
|
gitCardShippingProfile!.id
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
productsHandleShippingProfileIdMap.set(
|
||||||
|
product.handle!,
|
||||||
|
defaultShippingProfile!.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
featureFlagRouter.isFeatureEnabled(salesChannelFeatureFlagKey) &&
|
||||||
|
!product.sales_channels?.length
|
||||||
|
) {
|
||||||
|
productsHandleSalesChannelsMap.set(product.handle!, [
|
||||||
|
defaultSalesChannel!.id,
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
productsHandleSalesChannelsMap.set(
|
||||||
|
product.handle!,
|
||||||
|
product.sales_channels!.map((s) => s.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.variants) {
|
||||||
|
const hasPrices = product.variants.some((variant) => {
|
||||||
|
return (variant.prices?.length ?? 0) > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hasPrices) {
|
||||||
|
const items =
|
||||||
|
productsHandleVariantsIndexPricesMap.get(product.handle!) ?? []
|
||||||
|
|
||||||
|
product.variants.forEach((variant, index) => {
|
||||||
|
items.push({
|
||||||
|
index,
|
||||||
|
prices: variant.prices!,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
productsHandleVariantsIndexPricesMap.set(product.handle!, items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
products = products.map((productData) => {
|
||||||
|
delete productData.sales_channels
|
||||||
|
return productData
|
||||||
|
})
|
||||||
|
|
||||||
export function createProductsPrepareData({ data }) {
|
|
||||||
return {
|
return {
|
||||||
alias: InputAlias.Products as string,
|
products: products as ProductTypes.CreateProductDTO[],
|
||||||
value: [{}],
|
productsHandleShippingProfileIdMap,
|
||||||
|
productsHandleSalesChannelsMap,
|
||||||
|
productsHandleVariantsIndexPricesMap,
|
||||||
|
config: data.config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createProductsPrepareData.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./create-products-prepare-create-prices-compensation"
|
||||||
|
export * from "./create-products-prepare-data"
|
||||||
+18
-19
@@ -1,32 +1,27 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
import { ProductTypes } from "@medusajs/types"
|
||||||
import { EntityManager } from "typeorm"
|
import { WorkflowArguments } from "../../helper"
|
||||||
import { SalesChannelService } from "../../services"
|
|
||||||
|
|
||||||
type ProductHandle = string
|
type ProductHandle = string
|
||||||
type SalesChannelId = string
|
type SalesChannelId = string
|
||||||
|
|
||||||
export async function attachSalesChannelToProducts({
|
export async function attachSalesChannelToProducts({
|
||||||
container,
|
container,
|
||||||
manager,
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: WorkflowArguments<{
|
||||||
container: MedusaContainer
|
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
||||||
manager: EntityManager
|
products: ProductTypes.ProductDTO[]
|
||||||
data: {
|
}>): Promise<void> {
|
||||||
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
const { manager } = context
|
||||||
products: ProductTypes.ProductDTO[]
|
const productsHandleSalesChannelsMap = data.productsHandleSalesChannelsMap
|
||||||
}
|
const products = data.products
|
||||||
}): Promise<void> {
|
|
||||||
const salesChannelService: SalesChannelService = container.resolve(
|
const salesChannelService = container.resolve("salesChannelService")
|
||||||
"salesChannelService"
|
|
||||||
)
|
|
||||||
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
|
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
|
||||||
|
|
||||||
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
|
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
|
||||||
data.products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const salesChannelIds = data.productsHandleSalesChannelsMap.get(
|
const salesChannelIds = productsHandleSalesChannelsMap.get(product.handle!)
|
||||||
product.handle!
|
|
||||||
)
|
|
||||||
if (salesChannelIds) {
|
if (salesChannelIds) {
|
||||||
salesChannelIds.forEach((salesChannelId) => {
|
salesChannelIds.forEach((salesChannelId) => {
|
||||||
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
|
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
|
||||||
@@ -47,3 +42,7 @@ export async function attachSalesChannelToProducts({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attachSalesChannelToProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
+20
-19
@@ -1,33 +1,30 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
import { ProductTypes } from "@medusajs/types"
|
||||||
import { EntityManager } from "typeorm"
|
import { WorkflowArguments } from "../../helper"
|
||||||
import { ShippingProfileService } from "../../services"
|
|
||||||
|
|
||||||
type ProductHandle = string
|
type ProductHandle = string
|
||||||
type ShippingProfileId = string
|
type ShippingProfileId = string
|
||||||
|
|
||||||
export async function attachShippingProfileToProducts({
|
export async function attachShippingProfileToProducts({
|
||||||
container,
|
container,
|
||||||
manager,
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: WorkflowArguments<{
|
||||||
container: MedusaContainer
|
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
||||||
manager: EntityManager
|
products: ProductTypes.ProductDTO[]
|
||||||
data: {
|
}>): Promise<void> {
|
||||||
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
const { manager } = context
|
||||||
products: ProductTypes.ProductDTO[]
|
|
||||||
}
|
const productsHandleShippingProfileIdMap =
|
||||||
}): Promise<void> {
|
data.productsHandleShippingProfileIdMap
|
||||||
const shippingProfileService: ShippingProfileService = container.resolve(
|
const products = data.products
|
||||||
"shippingProfileService"
|
|
||||||
)
|
const shippingProfileService = container.resolve("shippingProfileService")
|
||||||
const shippingProfileServiceTx =
|
const shippingProfileServiceTx =
|
||||||
shippingProfileService.withTransaction(manager)
|
shippingProfileService.withTransaction(manager)
|
||||||
|
|
||||||
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
|
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
|
||||||
data.products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const profileId = data.productsHandleShippingProfileIdMap.get(
|
const profileId = productsHandleShippingProfileIdMap.get(product.handle!)
|
||||||
product.handle!
|
|
||||||
)
|
|
||||||
if (profileId) {
|
if (profileId) {
|
||||||
const productIds = profileIdProductIdsMap.get(profileId) || []
|
const productIds = profileIdProductIdsMap.get(profileId) || []
|
||||||
productIds.push(product.id)
|
productIds.push(product.id)
|
||||||
@@ -43,3 +40,7 @@ export async function attachShippingProfileToProducts({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attachShippingProfileToProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
export async function createProducts({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
products: ProductTypes.CreateProductDTO[]
|
||||||
|
}>): Promise<ProductTypes.ProductDTO[]> {
|
||||||
|
const data_ = data.products
|
||||||
|
|
||||||
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
|
return await productModuleService.create(data_)
|
||||||
|
}
|
||||||
|
|
||||||
|
createProducts.aliases = {
|
||||||
|
payload: "payload",
|
||||||
|
}
|
||||||
+18
-19
@@ -1,32 +1,27 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
import { ProductTypes } from "@medusajs/types"
|
||||||
import { EntityManager } from "typeorm"
|
import { WorkflowArguments } from "../../helper"
|
||||||
import { SalesChannelService } from "../../services"
|
|
||||||
|
|
||||||
type ProductHandle = string
|
type ProductHandle = string
|
||||||
type SalesChannelId = string
|
type SalesChannelId = string
|
||||||
|
|
||||||
export async function detachSalesChannelFromProducts({
|
export async function detachSalesChannelFromProducts({
|
||||||
container,
|
container,
|
||||||
manager,
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: WorkflowArguments<{
|
||||||
container: MedusaContainer
|
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
||||||
manager: EntityManager
|
products: ProductTypes.ProductDTO[]
|
||||||
data: {
|
}>): Promise<void> {
|
||||||
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
|
const { manager } = context
|
||||||
products: ProductTypes.ProductDTO[]
|
const productsHandleSalesChannelsMap = data.productsHandleSalesChannelsMap
|
||||||
}
|
const products = data.products
|
||||||
}): Promise<void> {
|
|
||||||
const salesChannelService: SalesChannelService = container.resolve(
|
const salesChannelService = container.resolve("salesChannelService")
|
||||||
"salesChannelService"
|
|
||||||
)
|
|
||||||
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
|
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
|
||||||
|
|
||||||
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
|
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
|
||||||
data.products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const salesChannelIds = data.productsHandleSalesChannelsMap.get(
|
const salesChannelIds = productsHandleSalesChannelsMap.get(product.handle!)
|
||||||
product.handle!
|
|
||||||
)
|
|
||||||
if (salesChannelIds) {
|
if (salesChannelIds) {
|
||||||
salesChannelIds.forEach((salesChannelId) => {
|
salesChannelIds.forEach((salesChannelId) => {
|
||||||
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
|
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
|
||||||
@@ -47,3 +42,7 @@ export async function detachSalesChannelFromProducts({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detachSalesChannelFromProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
+19
-19
@@ -1,33 +1,29 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
import { ProductTypes } from "@medusajs/types"
|
||||||
import { EntityManager } from "typeorm"
|
import { WorkflowArguments } from "../../helper"
|
||||||
import { ShippingProfileService } from "../../services"
|
|
||||||
|
|
||||||
type ProductHandle = string
|
type ProductHandle = string
|
||||||
type ShippingProfileId = string
|
type ShippingProfileId = string
|
||||||
|
|
||||||
export async function detachShippingProfileFromProducts({
|
export async function detachShippingProfileFromProducts({
|
||||||
container,
|
container,
|
||||||
manager,
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: WorkflowArguments<{
|
||||||
container: MedusaContainer
|
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
||||||
manager: EntityManager
|
products: ProductTypes.ProductDTO[]
|
||||||
data: {
|
}>): Promise<void> {
|
||||||
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
|
const { manager } = context
|
||||||
products: ProductTypes.ProductDTO[]
|
const productsHandleShippingProfileIdMap =
|
||||||
}
|
data.productsHandleShippingProfileIdMap
|
||||||
}): Promise<void> {
|
const products = data.products
|
||||||
const shippingProfileService: ShippingProfileService = container.resolve(
|
|
||||||
"shippingProfileService"
|
const shippingProfileService = container.resolve("shippingProfileService")
|
||||||
)
|
|
||||||
const shippingProfileServiceTx =
|
const shippingProfileServiceTx =
|
||||||
shippingProfileService.withTransaction(manager)
|
shippingProfileService.withTransaction(manager)
|
||||||
|
|
||||||
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
|
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
|
||||||
data.products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const profileId = data.productsHandleShippingProfileIdMap.get(
|
const profileId = productsHandleShippingProfileIdMap.get(product.handle!)
|
||||||
product.handle!
|
|
||||||
)
|
|
||||||
if (profileId) {
|
if (profileId) {
|
||||||
const productIds = profileIdProductIdsMap.get(profileId) || []
|
const productIds = profileIdProductIdsMap.get(profileId) || []
|
||||||
productIds.push(product.id)
|
productIds.push(product.id)
|
||||||
@@ -46,3 +42,7 @@ export async function detachShippingProfileFromProducts({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detachShippingProfileFromProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
+6
-10
@@ -1,12 +1,8 @@
|
|||||||
export * from "./create-prducts"
|
export * from "./create-products"
|
||||||
export * from "./remove-products"
|
|
||||||
export * from "./create-inventory-items"
|
|
||||||
export * from "./remove-inventory-items"
|
|
||||||
export * from "./attach-inventory-items"
|
|
||||||
export * from "./prepare-create-products-data"
|
|
||||||
export * from "./attach-shipping-profile-to-products"
|
|
||||||
export * from "./detach-shipping-profile-from-products"
|
|
||||||
export * from "./attach-sales-channel-to-products"
|
|
||||||
export * from "./detach-sales-channel-from-products"
|
export * from "./detach-sales-channel-from-products"
|
||||||
|
export * from "./attach-sales-channel-to-products"
|
||||||
|
export * from "./detach-shipping-profile-from-products"
|
||||||
|
export * from "./remove-products"
|
||||||
|
export * from "./attach-shipping-profile-to-products"
|
||||||
|
export * from "./list-products"
|
||||||
export * from "./update-products-variants-prices"
|
export * from "./update-products-variants-prices"
|
||||||
export * from "./detach-inventory-items"
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
|
export async function listProducts({
|
||||||
|
container,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{
|
||||||
|
products: ProductTypes.ProductDTO[]
|
||||||
|
config?: WorkflowTypes.CommonWorkflow.WorkflowInputConfig
|
||||||
|
}>): Promise<ProductTypes.ProductDTO[]> {
|
||||||
|
const { manager } = context
|
||||||
|
|
||||||
|
const products = data.products
|
||||||
|
const listConfig = data.config?.listConfig ?? {}
|
||||||
|
|
||||||
|
const productService = container.resolve("productService")
|
||||||
|
const pricingService = container.resolve("pricingService")
|
||||||
|
|
||||||
|
const config = {}
|
||||||
|
let shouldUseConfig = false
|
||||||
|
|
||||||
|
if (listConfig.select) {
|
||||||
|
shouldUseConfig = !!listConfig.select.length
|
||||||
|
Object.assign(config, { select: listConfig.select })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listConfig.relations) {
|
||||||
|
shouldUseConfig = shouldUseConfig || !!listConfig.relations.length
|
||||||
|
Object.assign(config, { relations: listConfig.relations })
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawProduct = await productService
|
||||||
|
.withTransaction(manager as any)
|
||||||
|
.retrieve(products[0].id, shouldUseConfig ? config : undefined)
|
||||||
|
|
||||||
|
return await pricingService
|
||||||
|
.withTransaction(manager as any)
|
||||||
|
.setProductPrices([rawProduct])
|
||||||
|
}
|
||||||
|
|
||||||
|
listProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ProductTypes } from "@medusajs/types"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
export async function removeProducts({
|
||||||
|
container,
|
||||||
|
data,
|
||||||
|
}: WorkflowArguments<{ products: ProductTypes.ProductDTO[] }>): Promise<void> {
|
||||||
|
if (!data.products.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const productModuleService: ProductTypes.IProductModuleService =
|
||||||
|
container.resolve(ModulesDefinition[Modules.PRODUCT].registrationName)
|
||||||
|
|
||||||
|
await productModuleService.softDelete(data.products.map((p) => p.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
removeProducts.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
+24
-26
@@ -1,44 +1,38 @@
|
|||||||
import { MedusaContainer, ProductTypes } from "@medusajs/types"
|
import { ProductTypes, WorkflowTypes } from "@medusajs/types"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { ProductVariantService } from "../../services"
|
|
||||||
import {
|
|
||||||
ProductVariantPricesCreateReq,
|
|
||||||
UpdateVariantPricesData,
|
|
||||||
} from "../../types/product-variant"
|
|
||||||
import { MedusaError } from "@medusajs/utils"
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
import { WorkflowArguments } from "../../helper"
|
||||||
|
|
||||||
type ProductHandle = string
|
type ProductHandle = string
|
||||||
type VariantIndexAndPrices = {
|
type VariantIndexAndPrices = {
|
||||||
index: number
|
index: number
|
||||||
prices: ProductVariantPricesCreateReq[]
|
prices: WorkflowTypes.ProductWorkflow.CreateProductVariantPricesInputDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProductsVariantsPrices({
|
export async function updateProductsVariantsPrices({
|
||||||
container,
|
container,
|
||||||
manager,
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: WorkflowArguments<{
|
||||||
container: MedusaContainer
|
productsHandleVariantsIndexPricesMap: Map<
|
||||||
manager: EntityManager
|
ProductHandle,
|
||||||
data: {
|
VariantIndexAndPrices[]
|
||||||
products: ProductTypes.ProductDTO[]
|
>
|
||||||
productsHandleVariantsIndexPricesMap: Map<
|
products: ProductTypes.ProductDTO[]
|
||||||
ProductHandle,
|
}>) {
|
||||||
VariantIndexAndPrices[]
|
const { manager } = context
|
||||||
>
|
const products = data.products
|
||||||
}
|
const productsHandleVariantsIndexPricesMap =
|
||||||
}) {
|
data.productsHandleVariantsIndexPricesMap
|
||||||
const productVariantService: ProductVariantService = container.resolve(
|
|
||||||
"productVariantService"
|
const productVariantService = container.resolve("productVariantService")
|
||||||
)
|
|
||||||
const productVariantServiceTx = productVariantService.withTransaction(manager)
|
const productVariantServiceTx = productVariantService.withTransaction(manager)
|
||||||
|
|
||||||
const variantIdsPricesData: UpdateVariantPricesData[] = []
|
const variantIdsPricesData: any[] = []
|
||||||
const productsMap = new Map<string, ProductTypes.ProductDTO>(
|
const productsMap = new Map<string, ProductTypes.ProductDTO>(
|
||||||
data.products.map((p) => [p.handle!, p])
|
products.map((p) => [p.handle!, p])
|
||||||
)
|
)
|
||||||
|
|
||||||
for (const mapData of data.productsHandleVariantsIndexPricesMap.entries()) {
|
for (const mapData of productsHandleVariantsIndexPricesMap.entries()) {
|
||||||
const [handle, variantData] = mapData
|
const [handle, variantData] = mapData
|
||||||
|
|
||||||
const product = productsMap.get(handle)
|
const product = productsMap.get(handle)
|
||||||
@@ -60,3 +54,7 @@ export async function updateProductsVariantsPrices({
|
|||||||
|
|
||||||
await productVariantServiceTx.updateVariantPrices(variantIdsPricesData)
|
await productVariantServiceTx.updateVariantPrices(variantIdsPricesData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateProductsVariantsPrices.aliases = {
|
||||||
|
products: "products",
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { InventoryItemDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
import { InputAlias } from "../definitions"
|
|
||||||
import { WorkflowArguments } from "../helper"
|
|
||||||
|
|
||||||
export async function removeInventoryItems({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
}: WorkflowArguments & {
|
|
||||||
data: {
|
|
||||||
[InputAlias.InventoryItems]: InventoryItemDTO
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
const manager = container.resolve("manager")
|
|
||||||
const inventoryService = container.resolve("inventoryService")
|
|
||||||
const context = { transactionManager: manager }
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
data.map(async ({ [InputAlias.InventoryItems]: inventoryItem }) => {
|
|
||||||
return await inventoryService!.deleteInventoryItem(
|
|
||||||
inventoryItem.id,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { InputAlias } from "../definitions"
|
|
||||||
import { ProductTypes } from "@medusajs/types"
|
|
||||||
import { WorkflowArguments } from "../helper"
|
|
||||||
|
|
||||||
export async function removeProducts({
|
|
||||||
container,
|
|
||||||
data,
|
|
||||||
}: WorkflowArguments & {
|
|
||||||
data: {
|
|
||||||
[InputAlias.Products]: ProductTypes.ProductDTO[]
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
const productModuleService = container.resolve("productModuleService")
|
|
||||||
return await productModuleService.softDelete(
|
|
||||||
data[InputAlias.Products].map((p) => p.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { aggregateData } from "../aggregate"
|
||||||
|
import { WorkflowStepMiddlewareReturn } from "../pipe"
|
||||||
|
|
||||||
|
describe("aggregate", function () {
|
||||||
|
it("should aggregate a new object from the source into a specify target", async function () {
|
||||||
|
const source = {
|
||||||
|
stringProp: "stringProp",
|
||||||
|
anArray: ["anArray"],
|
||||||
|
input: {
|
||||||
|
test: "test",
|
||||||
|
},
|
||||||
|
another: {
|
||||||
|
anotherTest: "anotherTest",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value: result } = (await aggregateData(
|
||||||
|
["input", "another", "stringProp", "anArray"],
|
||||||
|
"payload"
|
||||||
|
)({ data: source } as any)) as unknown as WorkflowStepMiddlewareReturn
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
payload: {
|
||||||
|
...source.input,
|
||||||
|
...source.another,
|
||||||
|
anArray: source.anArray,
|
||||||
|
stringProp: source.stringProp,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should aggregate a new object from the entire source into the resul object", async function () {
|
||||||
|
const source = {
|
||||||
|
stringProp: "stringProp",
|
||||||
|
anArray: ["anArray"],
|
||||||
|
input: {
|
||||||
|
test: "test",
|
||||||
|
},
|
||||||
|
another: {
|
||||||
|
anotherTest: "anotherTest",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value: result } = (await aggregateData()({
|
||||||
|
data: source,
|
||||||
|
} as any)) as unknown as WorkflowStepMiddlewareReturn
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
...source.input,
|
||||||
|
...source.another,
|
||||||
|
anArray: source.anArray,
|
||||||
|
stringProp: source.stringProp,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { PipelineHandler, WorkflowArguments } from "./pipe"
|
||||||
|
import { isObject } from "@medusajs/utils"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pipe utils that aggregates data from an object into a new object.
|
||||||
|
* The new object will have a target key with the aggregated data from the keys.
|
||||||
|
* @param keys
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
export function aggregateData<
|
||||||
|
T extends Record<string, unknown> = Record<string, unknown>,
|
||||||
|
TKeys extends keyof T = keyof T,
|
||||||
|
Target extends "payload" | string = string
|
||||||
|
>(keys: TKeys[] = [], target?: Target): PipelineHandler {
|
||||||
|
return async function ({ data }: WorkflowArguments<T>) {
|
||||||
|
const workingKeys = (keys.length ? keys : Object.keys(data)) as TKeys[]
|
||||||
|
const value = workingKeys.reduce((acc, key) => {
|
||||||
|
let targetAcc = { ...(target ? acc[target as string] : acc) }
|
||||||
|
targetAcc ??= {}
|
||||||
|
|
||||||
|
if (Array.isArray(data[key as string])) {
|
||||||
|
targetAcc[key as string] = data[key as string]
|
||||||
|
} else if (isObject(data[key as string])) {
|
||||||
|
targetAcc = {
|
||||||
|
...targetAcc,
|
||||||
|
...(data[key as string] as object),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetAcc[key as string] = data[key as string]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
acc[target as string] = {
|
||||||
|
...acc[target as string],
|
||||||
|
...targetAcc,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc = targetAcc
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
alias: target,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,14 +6,14 @@ import {
|
|||||||
|
|
||||||
import { InputAlias } from "../definitions"
|
import { InputAlias } from "../definitions"
|
||||||
|
|
||||||
type WorkflowStepMiddlewareReturn = {
|
export type WorkflowStepMiddlewareReturn = {
|
||||||
alias: string
|
alias?: string
|
||||||
value: any
|
value: any
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowStepMiddlewareInput = {
|
export type WorkflowStepMiddlewareInput = {
|
||||||
from: string
|
from: string
|
||||||
alias: string
|
alias?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PipelineInput {
|
interface PipelineInput {
|
||||||
@@ -32,11 +32,13 @@ export type WorkflowArguments<T = any> = {
|
|||||||
|
|
||||||
export type PipelineHandler<T extends any = undefined> = (
|
export type PipelineHandler<T extends any = undefined> = (
|
||||||
args: WorkflowArguments
|
args: WorkflowArguments
|
||||||
) => T extends undefined
|
) => Promise<
|
||||||
? Promise<WorkflowStepMiddlewareReturn | WorkflowStepMiddlewareReturn[]>
|
T extends undefined
|
||||||
: T
|
? WorkflowStepMiddlewareReturn | WorkflowStepMiddlewareReturn[]
|
||||||
|
: T
|
||||||
|
>
|
||||||
|
|
||||||
export function pipe<T = undefined>(
|
export function pipe<T>(
|
||||||
input: PipelineInput,
|
input: PipelineInput,
|
||||||
...functions: [...PipelineHandler[], PipelineHandler<T>]
|
...functions: [...PipelineHandler[], PipelineHandler<T>]
|
||||||
): WorkflowStepHandler {
|
): WorkflowStepHandler {
|
||||||
@@ -48,7 +50,7 @@ export function pipe<T = undefined>(
|
|||||||
metadata,
|
metadata,
|
||||||
context,
|
context,
|
||||||
}) => {
|
}) => {
|
||||||
const data = {}
|
let data = {}
|
||||||
|
|
||||||
const original = {
|
const original = {
|
||||||
invoke: invoke ?? {},
|
invoke: invoke ?? {},
|
||||||
@@ -60,7 +62,7 @@ export function pipe<T = undefined>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const key in input) {
|
for (const key in input) {
|
||||||
if (!input[key]) {
|
if (!input[key] || key === "inputAlias") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +71,16 @@ export function pipe<T = undefined>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const action of input[key]) {
|
for (const action of input[key]) {
|
||||||
if (action?.alias) {
|
if (action.alias) {
|
||||||
data[action.alias] = original[key][action.from]
|
data[action.alias] = original[key][action.from]
|
||||||
|
} else {
|
||||||
|
data[action.from] = original[key][action.from]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return functions.reduce(async (_, fn) => {
|
let finalResult
|
||||||
|
for (const fn of functions) {
|
||||||
let result = await fn({
|
let result = await fn({
|
||||||
container,
|
container,
|
||||||
payload,
|
payload,
|
||||||
@@ -90,13 +95,22 @@ export function pipe<T = undefined>(
|
|||||||
data[action.alias] = action.value
|
data[action.alias] = action.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((result as WorkflowStepMiddlewareReturn)?.alias) {
|
} else if (
|
||||||
data[(result as WorkflowStepMiddlewareReturn).alias] = (
|
result &&
|
||||||
result as WorkflowStepMiddlewareReturn
|
"alias" in (result as WorkflowStepMiddlewareReturn)
|
||||||
).value
|
) {
|
||||||
|
if ((result as WorkflowStepMiddlewareReturn).alias) {
|
||||||
|
data[(result as WorkflowStepMiddlewareReturn).alias!] = (
|
||||||
|
result as WorkflowStepMiddlewareReturn
|
||||||
|
).value
|
||||||
|
} else {
|
||||||
|
data = (result as WorkflowStepMiddlewareReturn).value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
finalResult = result
|
||||||
}, {})
|
}
|
||||||
|
|
||||||
|
return finalResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6353,6 +6353,7 @@ __metadata:
|
|||||||
"@medusajs/orchestration": ^0.1.0
|
"@medusajs/orchestration": ^0.1.0
|
||||||
"@medusajs/types": ^1.9.0
|
"@medusajs/types": ^1.9.0
|
||||||
"@medusajs/utils": ^1.9.3
|
"@medusajs/utils": ^1.9.3
|
||||||
|
"@medusajs/workflows": "workspace:^"
|
||||||
"@types/express": ^4.17.17
|
"@types/express": ^4.17.17
|
||||||
"@types/ioredis": ^4.28.10
|
"@types/ioredis": ^4.28.10
|
||||||
"@types/jsonwebtoken": ^8.5.9
|
"@types/jsonwebtoken": ^8.5.9
|
||||||
@@ -6577,7 +6578,7 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@medusajs/workflows@workspace:packages/workflows":
|
"@medusajs/workflows@workspace:^, @medusajs/workflows@workspace:packages/workflows":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/workflows@workspace:packages/workflows"
|
resolution: "@medusajs/workflows@workspace:packages/workflows"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in new issue
Block a user