feat(orchestration,core-flows,medusa,product,types,utils): product import/export uses workflows (#5811)
This commit is contained in:
@@ -19,7 +19,7 @@ import { pickBy } from "lodash"
|
||||
import { ProductStatus } from "../../../../models"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
import { listAndCountProductWithIsolatedProductModule } from "../products/list-products"
|
||||
import { listProducts } from "../../../../utils"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/price-lists/{id}/products
|
||||
@@ -196,8 +196,8 @@ export default async (req: Request, res) => {
|
||||
}
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
;[products, count] = await listAndCountProductWithIsolatedProductModule(
|
||||
req,
|
||||
;[products, count] = await listProducts(
|
||||
req.scope,
|
||||
filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Workflows, createProducts } from "@medusajs/core-flows"
|
||||
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||
import { createProducts, Workflows } from "@medusajs/core-flows"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
@@ -45,7 +45,7 @@ import { EntityManager } from "typeorm"
|
||||
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
|
||||
import { ProductStatus } from "../../../../models"
|
||||
import { Logger } from "../../../../types/global"
|
||||
import { validator } from "../../../../utils"
|
||||
import { retrieveProduct, validator } from "../../../../utils"
|
||||
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
|
||||
|
||||
/**
|
||||
@@ -257,7 +257,11 @@ export default async (req, res) => {
|
||||
|
||||
let rawProduct
|
||||
if (isMedusaV2Enabled) {
|
||||
rawProduct = await getProductWithIsolatedProductModule(req, product.id)
|
||||
rawProduct = await retrieveProduct(
|
||||
req.scope,
|
||||
product.id,
|
||||
defaultAdminProductRemoteQueryObject
|
||||
)
|
||||
} else {
|
||||
rawProduct = await productService.retrieve(product.id, {
|
||||
select: defaultAdminProductFields,
|
||||
@@ -272,26 +276,6 @@ export default async (req, res) => {
|
||||
res.json({ product: pricedProduct })
|
||||
}
|
||||
|
||||
async function getProductWithIsolatedProductModule(req, id) {
|
||||
// TODO: Add support for fields/expands
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
|
||||
const variables = { id }
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...defaultAdminProductRemoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
const [product] = await remoteQuery(query)
|
||||
|
||||
product.profile_id = product.profile?.id
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
class ProductVariantOptionReq {
|
||||
@IsString()
|
||||
value: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CreateProductVariants } from "@medusajs/core-flows"
|
||||
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { CreateProductVariants } from "@medusajs/core-flows"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
@@ -10,7 +11,12 @@ import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
defaultAdminProductFields,
|
||||
defaultAdminProductRelations,
|
||||
defaultAdminProductRemoteQueryObject,
|
||||
} from "."
|
||||
import {
|
||||
PricingService,
|
||||
ProductService,
|
||||
@@ -21,10 +27,7 @@ import {
|
||||
CreateProductVariantInput,
|
||||
ProductVariantPricesCreateReq,
|
||||
} from "../../../../types/product-variant"
|
||||
import { Type } from "class-transformer"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { getProductWithIsolatedProductModule } from "./get-product"
|
||||
import { retrieveProduct, validator } from "../../../../utils"
|
||||
import { createVariantsTransaction } from "./transaction/create-product-variant"
|
||||
|
||||
/**
|
||||
@@ -157,10 +160,10 @@ export default async (req, res) => {
|
||||
},
|
||||
})
|
||||
|
||||
rawProduct = await getProductWithIsolatedProductModule(
|
||||
req,
|
||||
rawProduct = await retrieveProduct(
|
||||
req.scope,
|
||||
id,
|
||||
req.retrieveConfig
|
||||
defaultAdminProductRemoteQueryObject
|
||||
)
|
||||
} else {
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
|
||||
@@ -5,8 +5,9 @@ import {
|
||||
SalesChannelService,
|
||||
} from "../../../../services"
|
||||
|
||||
import { MedusaError, MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
||||
import { MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
||||
import { FindParams } from "../../../../types/common"
|
||||
import { retrieveProduct } from "../../../../utils"
|
||||
import { defaultAdminProductRemoteQueryObject } from "./index"
|
||||
|
||||
/**
|
||||
@@ -76,10 +77,10 @@ export default async (req, res) => {
|
||||
|
||||
let rawProduct
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
rawProduct = await getProductWithIsolatedProductModule(
|
||||
req,
|
||||
rawProduct = await retrieveProduct(
|
||||
req.scope,
|
||||
id,
|
||||
req.retrieveConfig
|
||||
defaultAdminProductRemoteQueryObject
|
||||
)
|
||||
} else {
|
||||
rawProduct = await productService.retrieve(id, req.retrieveConfig)
|
||||
@@ -118,35 +119,4 @@ export default async (req, res) => {
|
||||
res.json({ product })
|
||||
}
|
||||
|
||||
export async function getProductWithIsolatedProductModule(
|
||||
req,
|
||||
id,
|
||||
retrieveConfig
|
||||
) {
|
||||
// TODO: Add support for fields/expands
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
|
||||
const variables = { id }
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...defaultAdminProductRemoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
const [product] = await remoteQuery(query)
|
||||
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with id: ${id} not found`
|
||||
)
|
||||
}
|
||||
|
||||
product.profile_id = product.profile?.id
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
export class AdminGetProductParams extends FindParams {}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||
import {
|
||||
PriceListService,
|
||||
PricingService,
|
||||
ProductService,
|
||||
ProductVariantInventoryService,
|
||||
SalesChannelService,
|
||||
} from "../../../../services"
|
||||
|
||||
import {
|
||||
IInventoryService,
|
||||
IPricingModuleService,
|
||||
IProductModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
||||
import { listProducts } from "../../../../utils"
|
||||
|
||||
import { IInventoryService } from "@medusajs/types"
|
||||
import { MedusaV2Flag } from "@medusajs/utils"
|
||||
import { Type } from "class-transformer"
|
||||
import { Product } from "../../../../models"
|
||||
import { PricedProduct } from "../../../../types/pricing"
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
import { defaultAdminProductRemoteQueryObject } from "./index"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/products
|
||||
@@ -252,12 +248,11 @@ export default async (req, res) => {
|
||||
let count
|
||||
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
const [products, count_] =
|
||||
await listAndCountProductWithIsolatedProductModule(
|
||||
req,
|
||||
req.filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
const [products, count_] = await listProducts(
|
||||
req.scope,
|
||||
req.filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
|
||||
rawProducts = products
|
||||
count = count_
|
||||
@@ -305,171 +300,6 @@ export default async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
async function getVariantsFromPriceList(req, priceListId) {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const pricingModuleService: IPricingModuleService = req.scope.resolve(
|
||||
"pricingModuleService"
|
||||
)
|
||||
const productModuleService: IProductModuleService = req.scope.resolve(
|
||||
"productModuleService"
|
||||
)
|
||||
|
||||
const [priceList] = await pricingModuleService.listPriceLists(
|
||||
{ id: [priceListId] },
|
||||
{
|
||||
relations: [
|
||||
"price_set_money_amounts",
|
||||
"price_set_money_amounts.price_set",
|
||||
],
|
||||
select: ["price_set_money_amounts.price_set.id"],
|
||||
}
|
||||
)
|
||||
|
||||
const priceSetIds = priceList.price_set_money_amounts?.map(
|
||||
(psma) => psma.price_set?.id
|
||||
)
|
||||
|
||||
const query = {
|
||||
product_variant_price_set: {
|
||||
__args: {
|
||||
price_set_id: priceSetIds,
|
||||
},
|
||||
fields: ["variant_id", "price_set_id"],
|
||||
},
|
||||
}
|
||||
|
||||
const variantPriceSets = await remoteQuery(query)
|
||||
const variantIds = variantPriceSets.map((vps) => vps.variant_id)
|
||||
|
||||
return await productModuleService.listVariants(
|
||||
{
|
||||
id: variantIds,
|
||||
},
|
||||
{
|
||||
select: ["product_id"],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export async function listAndCountProductWithIsolatedProductModule(
|
||||
req,
|
||||
filterableFields,
|
||||
listConfig
|
||||
) {
|
||||
// TODO: Add support for fields/expands
|
||||
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const featureFlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
|
||||
const productIdsFilter: Set<string> = new Set()
|
||||
const variantIdsFilter: Set<string> = new Set()
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
// This is not the best way of handling cross filtering but for now I would say it is fine
|
||||
const salesChannelIdFilter = filterableFields.sales_channel_id
|
||||
delete filterableFields.sales_channel_id
|
||||
|
||||
if (salesChannelIdFilter) {
|
||||
const salesChannelService = req.scope.resolve(
|
||||
"salesChannelService"
|
||||
) as SalesChannelService
|
||||
|
||||
promises.push(
|
||||
salesChannelService
|
||||
.listProductIdsBySalesChannelIds(salesChannelIdFilter)
|
||||
.then((productIdsInSalesChannel) => {
|
||||
let filteredProductIds =
|
||||
productIdsInSalesChannel[salesChannelIdFilter]
|
||||
|
||||
if (filterableFields.id) {
|
||||
filterableFields.id = Array.isArray(filterableFields.id)
|
||||
? filterableFields.id
|
||||
: [filterableFields.id]
|
||||
|
||||
const salesChannelProductIdsSet = new Set(filteredProductIds)
|
||||
|
||||
filteredProductIds = filterableFields.id.filter((productId) =>
|
||||
salesChannelProductIdsSet.has(productId)
|
||||
)
|
||||
}
|
||||
|
||||
filteredProductIds.map((id) => productIdsFilter.add(id))
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const priceListId = filterableFields.price_list_id
|
||||
delete filterableFields.price_list_id
|
||||
|
||||
if (priceListId) {
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
const variants = await getVariantsFromPriceList(req, priceListId)
|
||||
|
||||
variants.forEach((pv) => variantIdsFilter.add(pv.id))
|
||||
} else {
|
||||
// TODO: it is working but validate the behaviour.
|
||||
// e.g pricing context properly set.
|
||||
// At the moment filtering by price list but not having any customer id or
|
||||
// include discount forces the query to filter with price list id is null
|
||||
const priceListService = req.scope.resolve(
|
||||
"priceListService"
|
||||
) as PriceListService
|
||||
promises.push(
|
||||
priceListService
|
||||
.listPriceListsVariantIdsMap(priceListId)
|
||||
.then((priceListVariantIdsMap) => {
|
||||
priceListVariantIdsMap[priceListId].map((variantId) =>
|
||||
variantIdsFilter.add(variantId)
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const discountConditionId = filterableFields.discount_condition_id
|
||||
delete filterableFields.discount_condition_id
|
||||
|
||||
if (discountConditionId) {
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
await promiseAll(promises)
|
||||
|
||||
if (productIdsFilter.size > 0) {
|
||||
filterableFields.id = Array.from(productIdsFilter)
|
||||
}
|
||||
|
||||
if (variantIdsFilter.size > 0) {
|
||||
filterableFields.variants = { id: Array.from(variantIdsFilter) }
|
||||
}
|
||||
|
||||
const variables = {
|
||||
filters: filterableFields,
|
||||
order: listConfig.order,
|
||||
skip: listConfig.skip,
|
||||
take: listConfig.take,
|
||||
}
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...defaultAdminProductRemoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
const {
|
||||
rows: products,
|
||||
metadata: { count },
|
||||
} = await remoteQuery(query)
|
||||
|
||||
products.forEach((product) => {
|
||||
product.profile_id = product.profile?.id
|
||||
})
|
||||
|
||||
return [products, count]
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters used to filter and configure the pagination of the retrieved products.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Workflows, updateProducts } from "@medusajs/core-flows"
|
||||
import { DistributedTransaction } from "@medusajs/orchestration"
|
||||
import {
|
||||
FlagRouter,
|
||||
@@ -5,7 +6,6 @@ import {
|
||||
MedusaV2Flag,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { updateProducts, Workflows } from "@medusajs/core-flows"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
ProductVariantPricesUpdateReq,
|
||||
UpdateProductVariantInput,
|
||||
} from "../../../../types/product-variant"
|
||||
import { retrieveProduct } from "../../../../utils"
|
||||
import {
|
||||
createVariantsTransaction,
|
||||
revertVariantTransaction,
|
||||
@@ -292,7 +293,11 @@ export default async (req, res) => {
|
||||
let rawProduct
|
||||
|
||||
if (isMedusaV2Enabled) {
|
||||
rawProduct = await getProductWithIsolatedProductModule(req, id)
|
||||
rawProduct = await retrieveProduct(
|
||||
req.scope,
|
||||
id,
|
||||
defaultAdminProductRemoteQueryObject
|
||||
)
|
||||
} else {
|
||||
rawProduct = await productService.retrieve(id, {
|
||||
select: defaultAdminProductFields,
|
||||
@@ -305,26 +310,6 @@ export default async (req, res) => {
|
||||
res.json({ product })
|
||||
}
|
||||
|
||||
async function getProductWithIsolatedProductModule(req, id) {
|
||||
// TODO: Add support for fields/expands
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
|
||||
const variables = { id }
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...defaultAdminProductRemoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
const [product] = await remoteQuery(query)
|
||||
|
||||
product.profile_id = product.profile?.id
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
class ProductVariantOptionReq {
|
||||
@IsString()
|
||||
value: string
|
||||
|
||||
Reference in New Issue
Block a user