feat(medusa, types, utils, workflow): Migrate medusa workflow to the workflow package (#4682)

This commit is contained in:
Adrien de Peretti authored and GitHub committed 2023-08-05 16:03:45 +02:00
1 parent 433914ae01
commit dc46927bc6
68 files changed
+1224 -1064

No files matched your search

+1
View File
@@ -51,6 +51,7 @@
"@medusajs/modules-sdk": "^1.9.0",
"@medusajs/orchestration": "^0.1.0",
"@medusajs/utils": "^1.9.3",
"@medusajs/workflows": "workspace:^",
"awilix": "^8.0.0",
"body-parser": "^1.19.0",
"boxen": "^5.0.1",
@@ -36,13 +36,13 @@ import { DistributedTransaction } from "@medusajs/orchestration"
import { EntityManager } from "typeorm"
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
import { FlagRouter } from "../../../../utils/flag-router"
import { IInventoryService } from "@medusajs/types"
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
import { Logger } from "../../../../types/global"
import { ProductStatus } from "../../../../models"
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
import { Type } from "class-transformer"
import { createProductsWorkflow } from "../../../../workflows/admin/create-products"
import { validator } from "../../../../utils/validator"
import { validator } from "../../../../utils"
import { createProducts } from "@medusajs/workflows"
/**
* @oas [post] /admin/products
@@ -133,13 +133,26 @@ export default async (req, res) => {
const productModuleService = req.scope.resolve("productModuleService")
if (productModuleService) {
const products = await createProductsWorkflow(
{
container: req.scope,
const createProductWorkflow = createProducts(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,
},
[validated]
)
})
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,49 +0,0 @@
import { MedusaContainer, ProductTypes } from "@medusajs/types"
import { EntityManager } from "typeorm"
import { SalesChannelService } from "../../services"
type ProductHandle = string
type SalesChannelId = string
export async function attachSalesChannelToProducts({
container,
manager,
data,
}: {
container: MedusaContainer
manager: EntityManager
data: {
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
products: ProductTypes.ProductDTO[]
}
}): Promise<void> {
const salesChannelService: SalesChannelService = container.resolve(
"salesChannelService"
)
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
data.products.forEach((product) => {
const salesChannelIds = data.productsHandleSalesChannelsMap.get(
product.handle!
)
if (salesChannelIds) {
salesChannelIds.forEach((salesChannelId) => {
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
productIds.push(product.id)
salesChannelIdProductIdsMap.set(salesChannelId, productIds)
})
}
})
await Promise.all(
Array.from(salesChannelIdProductIdsMap.entries()).map(
async ([salesChannelId, productIds]) => {
return await salesChannelServiceTx.addProducts(
salesChannelId,
productIds
)
}
)
)
}
@@ -1,45 +0,0 @@
import { MedusaContainer, ProductTypes } from "@medusajs/types"
import { EntityManager } from "typeorm"
import { ShippingProfileService } from "../../services"
type ProductHandle = string
type ShippingProfileId = string
export async function attachShippingProfileToProducts({
container,
manager,
data,
}: {
container: MedusaContainer
manager: EntityManager
data: {
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
products: ProductTypes.ProductDTO[]
}
}): Promise<void> {
const shippingProfileService: ShippingProfileService = container.resolve(
"shippingProfileService"
)
const shippingProfileServiceTx =
shippingProfileService.withTransaction(manager)
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
data.products.forEach((product) => {
const profileId = data.productsHandleShippingProfileIdMap.get(
product.handle!
)
if (profileId) {
const productIds = profileIdProductIdsMap.get(profileId) || []
productIds.push(product.id)
profileIdProductIdsMap.set(profileId, productIds)
}
})
await Promise.all(
Array.from(profileIdProductIdsMap.entries()).map(
async ([profileId, productIds]) => {
return await shippingProfileServiceTx.addProducts(profileId, productIds)
}
)
)
}
@@ -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,49 +0,0 @@
import { MedusaContainer, ProductTypes } from "@medusajs/types"
import { EntityManager } from "typeorm"
import { SalesChannelService } from "../../services"
type ProductHandle = string
type SalesChannelId = string
export async function detachSalesChannelFromProducts({
container,
manager,
data,
}: {
container: MedusaContainer
manager: EntityManager
data: {
productsHandleSalesChannelsMap: Map<ProductHandle, SalesChannelId[]>
products: ProductTypes.ProductDTO[]
}
}): Promise<void> {
const salesChannelService: SalesChannelService = container.resolve(
"salesChannelService"
)
const salesChannelServiceTx = salesChannelService.withTransaction(manager)
const salesChannelIdProductIdsMap = new Map<ProductHandle, SalesChannelId[]>()
data.products.forEach((product) => {
const salesChannelIds = data.productsHandleSalesChannelsMap.get(
product.handle!
)
if (salesChannelIds) {
salesChannelIds.forEach((salesChannelId) => {
const productIds = salesChannelIdProductIdsMap.get(salesChannelId) || []
productIds.push(product.id)
salesChannelIdProductIdsMap.set(salesChannelId, productIds)
})
}
})
await Promise.all(
Array.from(salesChannelIdProductIdsMap.entries()).map(
async ([salesChannelId, productIds]) => {
return await salesChannelServiceTx.removeProducts(
salesChannelId,
productIds
)
}
)
)
}
@@ -1,48 +0,0 @@
import { MedusaContainer, ProductTypes } from "@medusajs/types"
import { EntityManager } from "typeorm"
import { ShippingProfileService } from "../../services"
type ProductHandle = string
type ShippingProfileId = string
export async function detachShippingProfileFromProducts({
container,
manager,
data,
}: {
container: MedusaContainer
manager: EntityManager
data: {
productsHandleShippingProfileIdMap: Map<ProductHandle, ShippingProfileId>
products: ProductTypes.ProductDTO[]
}
}): Promise<void> {
const shippingProfileService: ShippingProfileService = container.resolve(
"shippingProfileService"
)
const shippingProfileServiceTx =
shippingProfileService.withTransaction(manager)
const profileIdProductIdsMap = new Map<ShippingProfileId, ProductHandle[]>()
data.products.forEach((product) => {
const profileId = data.productsHandleShippingProfileIdMap.get(
product.handle!
)
if (profileId) {
const productIds = profileIdProductIdsMap.get(profileId) || []
productIds.push(product.id)
profileIdProductIdsMap.set(profileId, productIds)
}
})
await Promise.all(
Array.from(profileIdProductIdsMap.entries()).map(
async ([profileId, productIds]) => {
return await shippingProfileServiceTx.removeProducts(
profileId,
productIds
)
}
)
)
}
@@ -1,12 +0,0 @@
export * from "./create-prducts"
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 "./update-products-variants-prices"
export * from "./detach-inventory-items"
@@ -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))
}
@@ -1,62 +0,0 @@
import { MedusaContainer, ProductTypes } from "@medusajs/types"
import { EntityManager } from "typeorm"
import { ProductVariantService } from "../../services"
import {
ProductVariantPricesCreateReq,
UpdateVariantPricesData,
} from "../../types/product-variant"
import { MedusaError } from "@medusajs/utils"
type ProductHandle = string
type VariantIndexAndPrices = {
index: number
prices: ProductVariantPricesCreateReq[]
}
export async function updateProductsVariantsPrices({
container,
manager,
data,
}: {
container: MedusaContainer
manager: EntityManager
data: {
products: ProductTypes.ProductDTO[]
productsHandleVariantsIndexPricesMap: Map<
ProductHandle,
VariantIndexAndPrices[]
>
}
}) {
const productVariantService: ProductVariantService = container.resolve(
"productVariantService"
)
const productVariantServiceTx = productVariantService.withTransaction(manager)
const variantIdsPricesData: UpdateVariantPricesData[] = []
const productsMap = new Map<string, ProductTypes.ProductDTO>(
data.products.map((p) => [p.handle!, p])
)
for (const mapData of data.productsHandleVariantsIndexPricesMap.entries()) {
const [handle, variantData] = mapData
const product = productsMap.get(handle)
if (!product) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Product with handle ${handle} not found`
)
}
variantData.forEach((item, index) => {
const variant = product.variants[index]
variantIdsPricesData.push({
variantId: variant.id,
prices: item.prices,
})
})
}
await productVariantServiceTx.updateVariantPrices(variantIdsPricesData)
}