diff --git a/.changeset/nasty-fans-suffer.md b/.changeset/nasty-fans-suffer.md new file mode 100644 index 0000000000..c3b1525166 --- /dev/null +++ b/.changeset/nasty-fans-suffer.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix(medusa): Only set product availability if variants are requested diff --git a/packages/medusa/src/api/routes/admin/products/get-product.ts b/packages/medusa/src/api/routes/admin/products/get-product.ts index 6d9febb403..f3f7fcbde2 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -59,7 +59,16 @@ export default async (req, res) => { const rawProduct = await productService.retrieve(id, req.retrieveConfig) - const [product] = await pricingService.setProductPrices([rawProduct]) + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every((relation) => + req.retrieveConfig.relations?.includes(relation) + ) + + const product = rawProduct + + if (!shouldSetPricing) { + await pricingService.setProductPrices([product]) + } res.json({ product }) } diff --git a/packages/medusa/src/api/routes/admin/products/list-products.ts b/packages/medusa/src/api/routes/admin/products/list-products.ts index abfdea4d0c..592e9d4b63 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.ts +++ b/packages/medusa/src/api/routes/admin/products/list-products.ts @@ -6,11 +6,11 @@ import { SalesChannelService, } from "../../../../services" -import { FilterableProductProps } from "../../../../types/product" import { IInventoryService } from "@medusajs/types" -import { PricedProduct } from "../../../../types/pricing" -import { Product } from "../../../../models" import { Type } from "class-transformer" +import { Product } from "../../../../models" +import { PricedProduct } from "../../../../types/pricing" +import { FilterableProductProps } from "../../../../types/product" /** * @oas [get] /admin/products @@ -241,16 +241,21 @@ export default async (req, res) => { let products: (Product | PricedProduct)[] = rawProducts - const includesPricing = ["variants", "variants.prices"].every( + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every( (relation) => relations?.includes(relation) ) - if (includesPricing) { + + if (shouldSetPricing) { products = await pricingService .withTransaction(transactionManager) .setProductPrices(rawProducts) } - if (inventoryService) { + // We only set availability if variants are requested + const shouldSetAvailability = relations?.includes("variants") + + if (inventoryService && shouldSetAvailability) { const [salesChannelsIds] = await salesChannelService .withTransaction(transactionManager) .listAndCount({}, { select: ["id"] }) diff --git a/packages/medusa/src/api/routes/store/products/get-product.ts b/packages/medusa/src/api/routes/store/products/get-product.ts index c6c31410d3..1883b1f916 100644 --- a/packages/medusa/src/api/routes/store/products/get-product.ts +++ b/packages/medusa/src/api/routes/store/products/get-product.ts @@ -101,24 +101,46 @@ export default async (req, res) => { currencyCode = region.currency_code } - const pricedProductArray = await pricingService.setProductPrices( - [rawProduct], - { - cart_id: validated.cart_id, - customer_id: customer_id, - region_id: regionId, - currency_code: currencyCode, - include_discount_prices: true, - } + const decoratedProduct = rawProduct + + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every((relation) => + req.retrieveConfig.relations?.includes(relation) ) - const [product] = await productVariantInventoryService.setProductAvailability( - pricedProductArray, - sales_channel_id - ) + // We only set availability if variants are requested + const shouldSetAvailability = + req.retrieveConfig.relations?.includes("variants") + + const decoratePromises: Promise[] = [] + + if (shouldSetPricing) { + decoratePromises.push( + pricingService.setProductPrices([decoratedProduct], { + cart_id: validated.cart_id, + customer_id: customer_id, + region_id: regionId, + currency_code: currencyCode, + include_discount_prices: true, + }) + ) + } + + if (shouldSetAvailability) { + decoratePromises.push( + productVariantInventoryService.setProductAvailability( + [decoratedProduct], + sales_channel_id + ) + ) + } + + // We can run them concurrently as the new properties are assigned to the references + // of the appropriate entity + await Promise.all(decoratePromises) res.json({ - product: cleanResponseData(product, req.allowedProperties || []), + product: cleanResponseData(decoratedProduct, req.allowedProperties || []), }) } diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index f13d81a836..b2bebf54e8 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -7,19 +7,19 @@ import { IsString, ValidateNested, } from "class-validator" +import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" import { CartService, ProductService, ProductVariantInventoryService, } from "../../../../services" -import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" import PricingService from "../../../../services/pricing" import { DateComparisonOperator } from "../../../../types/common" import { PriceSelectionParams } from "../../../../types/price-selection" +import { cleanResponseData } from "../../../../utils/clean-response-data" import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators" import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean" import { IsType } from "../../../../utils/validators/is-type" -import { cleanResponseData } from "../../../../utils/clean-response-data" import { defaultStoreCategoryScope } from "../product-categories" /** @@ -249,25 +249,44 @@ export default async (req, res) => { // Create a new reference just for naming purpose const computedProducts = rawProducts + // We only set prices if variants.prices are requested + const shouldSetPricing = ["variants", "variants.prices"].every( + (relation) => listConfig.relations?.includes(relation) + ) + + // We only set availability if variants are requested + const shouldSetAvailability = listConfig.relations?.includes("variants") + + const decoratePromises: Promise[] = [] + + if (shouldSetPricing) { + decoratePromises.push( + pricingService + .withTransaction(transactionManager) + .setProductPrices(computedProducts, { + cart_id: cart_id, + region_id: regionId, + currency_code: currencyCode, + customer_id: req.user?.customer_id, + include_discount_prices: true, + }) + ) + } + + if (shouldSetAvailability) { + decoratePromises.push( + productVariantInventoryService + .withTransaction(transactionManager) + .setProductAvailability( + computedProducts, + filterableFields.sales_channel_id + ) + ) + } + // We can run them concurrently as the new properties are assigned to the references // of the appropriate entity - await Promise.all([ - pricingService - .withTransaction(transactionManager) - .setProductPrices(computedProducts, { - cart_id: cart_id, - region_id: regionId, - currency_code: currencyCode, - customer_id: req.user?.customer_id, - include_discount_prices: true, - }), - productVariantInventoryService - .withTransaction(transactionManager) - .setProductAvailability( - computedProducts, - filterableFields.sales_channel_id - ), - ]) + await Promise.all(decoratePromises) return [computedProducts, count] }