diff --git a/.changeset/friendly-maps-share.md b/.changeset/friendly-maps-share.md new file mode 100644 index 0000000000..b471a4033b --- /dev/null +++ b/.changeset/friendly-maps-share.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix(medusa): decorate inventory for variants returned through carts diff --git a/integration-tests/plugins/__tests__/inventory/cart/cart.js b/integration-tests/plugins/__tests__/inventory/cart/cart.js index 7d4115c092..bd8058dc1c 100644 --- a/integration-tests/plugins/__tests__/inventory/cart/cart.js +++ b/integration-tests/plugins/__tests__/inventory/cart/cart.js @@ -234,6 +234,54 @@ describe("/store/carts", () => { expect(count).toEqual(0) }) + it("should decorate line item variant inventory_quantity when creating a line-item", async () => { + const api = useApi() + + const cartId = "test-cart" + + // Add standard line item to cart + const addCart = await api + .post( + `/store/carts/${cartId}/line-items`, + { + variant_id: variantId, + quantity: 3, + }, + { withCredentials: true } + ) + .catch((e) => e) + + expect(addCart.status).toEqual(200) + expect(addCart.data.cart.items[0].variant.inventory_quantity).toEqual(5) + }) + + it("should decorate line item variant inventory_quantity when getting cart", async () => { + const api = useApi() + + const cartId = "test-cart" + + // Add standard line item to cart + await api + .post( + `/store/carts/${cartId}/line-items`, + { + variant_id: variantId, + quantity: 3, + }, + { withCredentials: true } + ) + .catch((e) => e) + + const cartResponse = await api + .get(`/store/carts/${cartId}`, { withCredentials: true }) + .catch((e) => e) + + expect(cartResponse.status).toEqual(200) + expect( + cartResponse.data.cart.items[0].variant.inventory_quantity + ).toEqual(5) + }) + it("fails to add a item on the cart if the inventory isn't enough", async () => { const api = useApi() diff --git a/integration-tests/plugins/__tests__/inventory/products/get-product.js b/integration-tests/plugins/__tests__/inventory/products/get-product.js index 1519b6c6b0..57ba9cbe30 100644 --- a/integration-tests/plugins/__tests__/inventory/products/get-product.js +++ b/integration-tests/plugins/__tests__/inventory/products/get-product.js @@ -10,7 +10,10 @@ const adminSeeder = require("../../../../helpers/admin-seeder") jest.setTimeout(30000) -const { simpleProductFactory } = require("../../../../factories") +const { + simpleProductFactory, + simpleSalesChannelFactory, +} = require("../../../../factories") const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } } @@ -53,7 +56,17 @@ describe("Get products", () => { "productVariantInventoryService" ) const inventoryService = appContainer.resolve("inventoryService") + const locationService = appContainer.resolve("stockLocationService") + const salesChannelService = appContainer.resolve("salesChannelService") + const salesChannelLocationService = appContainer.resolve( + "salesChannelLocationService" + ) + const salesChannel = await simpleSalesChannelFactory(dbConnection, { + is_default: true, + }) + + const location = await locationService.create({ name: "test-location" }) await simpleProductFactory( dbConnection, { @@ -63,7 +76,11 @@ describe("Get products", () => { }, 100 ) - + await salesChannelService.addProducts(salesChannel.id, [productId]) + await salesChannelLocationService.associateLocation( + salesChannel.id, + location.id + ) invItem = await inventoryService.createInventoryItem({ sku: "test-sku", }) @@ -72,33 +89,62 @@ describe("Get products", () => { variantId, invItem.id ) + + await inventoryService.createInventoryLevel({ + inventory_item_id: invItem.id, + location_id: location.id, + stocked_quantity: 100, + }) }) - it("Expands inventory items when getting product with expand parameters", async () => { - const api = useApi() + describe("/store/products/:id", () => { + it("Expands inventory items when getting product with expand parameters", async () => { + const api = useApi() - const res = await api.get( - `/store/products/${productId}?expand=variants,variants.inventory_items`, - adminHeaders - ) + const res = await api.get( + `/store/products/${productId}?expand=variants,variants.inventory_items` + ) - expect(res.status).toEqual(200) - expect(res.data.product).toEqual( - expect.objectContaining({ - id: productId, - variants: [ - expect.objectContaining({ - id: variantId, - inventory_items: [ - expect.objectContaining({ - inventory_item_id: invItem.id, - variant_id: variantId, - }), - ], - }), - ], - }), - expect.objectContaining({}) - ) + expect(res.status).toEqual(200) + expect(res.data.product).toEqual( + expect.objectContaining({ + id: productId, + variants: [ + expect.objectContaining({ + id: variantId, + inventory_items: [ + expect.objectContaining({ + inventory_item_id: invItem.id, + variant_id: variantId, + }), + ], + }), + ], + }), + expect.objectContaining({}) + ) + }) + }) + + describe("/admin/products/:id", () => { + it("should get inventory quantity for products fetched through the admin api", async () => { + const api = useApi() + + const res = await api.get(`/admin/products/${productId}`, adminHeaders) + + expect(res.status).toEqual(200) + expect(res.data.product).toEqual( + expect.objectContaining({ + id: productId, + variants: [ + expect.objectContaining({ + id: variantId, + inventory_quantity: 100, + }), + ], + }), + expect.objectContaining({}) + ) + }) }) }) diff --git a/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts b/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts index 69387aa48d..5fa882358d 100644 --- a/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts +++ b/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts @@ -11,6 +11,7 @@ import path from "path" import adminSeeder from "../../../../helpers/admin-seeder" import { createDefaultRuleTypes } from "../../../helpers/create-default-rule-types" import { createVariantPriceSet } from "../../../helpers/create-variant-price-set" +import { AxiosInstance } from "axios" jest.setTimeout(50000) @@ -80,7 +81,7 @@ describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () }) it("should create product variant price sets and prices", async () => { - const api = useApi() + const api = useApi()! as AxiosInstance const data = { title: "test variant update", prices: [ @@ -144,7 +145,7 @@ describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () const moneyAmountToUpdate = priceSet.money_amounts?.[0] - const api = useApi() + const api = useApi()! as AxiosInstance const data = { title: "test variant update", prices: [ @@ -202,7 +203,7 @@ describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () prices: [], }) - const api = useApi() + const api = useApi()! as AxiosInstance const data = { title: "test variant update", prices: [ 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 87f653567f..fc3d3b8135 100644 --- a/packages/medusa/src/api/routes/admin/products/get-product.ts +++ b/packages/medusa/src/api/routes/admin/products/get-product.ts @@ -1,6 +1,12 @@ -import { MedusaError } from "@medusajs/utils" +import { + PricingService, + ProductService, + ProductVariantInventoryService, + SalesChannelService, +} from "../../../../services" + import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain" -import { PricingService, ProductService } from "../../../../services" +import { MedusaError } from "@medusajs/utils" import { FindParams } from "../../../../types/common" import { defaultAdminProductRemoteQueryObject } from "./index" @@ -63,6 +69,12 @@ export default async (req, res) => { const pricingService: PricingService = req.scope.resolve("pricingService") const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + const salesChannelService: SalesChannelService = req.scope.resolve( + "salesChannelService" + ) + let rawProduct if (featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)) { rawProduct = await getProductWithIsolatedProductModule( @@ -81,10 +93,29 @@ export default async (req, res) => { const product = rawProduct + const decoratePromises: Promise[] = [] if (shouldSetPricing) { - await pricingService.setAdminProductPricing([product]) + decoratePromises.push(pricingService.setAdminProductPricing([product])) } + const shouldSetAvailability = + req.retrieveConfig.relations?.includes("variants") + + if (shouldSetAvailability) { + const [salesChannelsIds] = await salesChannelService.listAndCount( + {}, + { select: ["id"] } + ) + + decoratePromises.push( + productVariantInventoryService.setProductAvailability( + [product], + salesChannelsIds.map((salesChannel) => salesChannel.id) + ) + ) + } + await Promise.all(decoratePromises) + res.json({ product }) } diff --git a/packages/medusa/src/api/routes/store/carts/__tests__/create-cart.js b/packages/medusa/src/api/routes/store/carts/__tests__/create-cart.js index 2d04beb96a..0cb66d6023 100644 --- a/packages/medusa/src/api/routes/store/carts/__tests__/create-cart.js +++ b/packages/medusa/src/api/routes/store/carts/__tests__/create-cart.js @@ -1,7 +1,7 @@ -import { IdMap } from "medusa-test-utils" -import { request } from "../../../../../helpers/test-request" import { CartServiceMock } from "../../../../../services/__mocks__/cart" +import { IdMap } from "medusa-test-utils" import { LineItemServiceMock } from "../../../../../services/__mocks__/line-item" +import { request } from "../../../../../helpers/test-request" describe("POST /store/carts", () => { describe("successfully creates a cart", () => { diff --git a/packages/medusa/src/api/routes/store/carts/add-shipping-method.ts b/packages/medusa/src/api/routes/store/carts/add-shipping-method.ts index 76f9bfa327..7caa5c30c4 100644 --- a/packages/medusa/src/api/routes/store/carts/add-shipping-method.ts +++ b/packages/medusa/src/api/routes/store/carts/add-shipping-method.ts @@ -1,7 +1,10 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { IsOptional, IsString } from "class-validator" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" import { EntityManager } from "typeorm" import { cleanResponseData } from "../../../../utils/clean-response-data" @@ -66,6 +69,8 @@ export default async (req, res) => { const manager: EntityManager = req.scope.resolve("manager") const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") await manager.transaction(async (m) => { const txCartService = cartService.withTransaction(m) @@ -91,6 +96,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/create-cart.ts b/packages/medusa/src/api/routes/store/carts/create-cart.ts index b187a9c871..f562767a6b 100644 --- a/packages/medusa/src/api/routes/store/carts/create-cart.ts +++ b/packages/medusa/src/api/routes/store/carts/create-cart.ts @@ -1,9 +1,9 @@ -import { MedusaContainer } from "@medusajs/modules-sdk" import { - createCart as createCartWorkflow, - Workflows, -} from "@medusajs/workflows" -import { Type } from "class-transformer" + CartService, + LineItemService, + ProductVariantInventoryService, + RegionService, +} from "../../../../services" import { IsArray, IsInt, @@ -12,21 +12,23 @@ import { IsString, ValidateNested, } from "class-validator" -import { isDefined, MedusaError } from "medusa-core-utils" -import reqIp from "request-ip" -import { EntityManager } from "typeorm" -import { FlagRouter } from "@medusajs/utils" -import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" -import { LineItem } from "../../../../models" +import { MedusaError, isDefined } from "medusa-core-utils" import { - CartService, - LineItemService, - RegionService, -} from "../../../../services" + Workflows, + createCart as createCartWorkflow, +} from "@medusajs/workflows" +import { defaultStoreCartFields, defaultStoreCartRelations } from "." + import { CartCreateProps } from "../../../../types/cart" -import { cleanResponseData } from "../../../../utils/clean-response-data" +import { EntityManager } from "typeorm" import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators" +import { FlagRouter } from "@medusajs/utils" +import { LineItem } from "../../../../models" +import { MedusaContainer } from "@medusajs/modules-sdk" +import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" +import { Type } from "class-transformer" +import { cleanResponseData } from "../../../../utils/clean-response-data" +import reqIp from "request-ip" /** * @oas [post] /store/carts @@ -82,6 +84,8 @@ export default async (req, res) => { const entityManager: EntityManager = req.scope.resolve("manager") const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter") const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") const validated = req.validatedBody as StorePostCartReq @@ -219,6 +223,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + cart.items.map((i) => i.variant), + cart.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(cart, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/create-line-item/utils/handler-steps.ts b/packages/medusa/src/api/routes/store/carts/create-line-item/utils/handler-steps.ts index 2c9dfd2974..5f203b9d31 100644 --- a/packages/medusa/src/api/routes/store/carts/create-line-item/utils/handler-steps.ts +++ b/packages/medusa/src/api/routes/store/carts/create-line-item/utils/handler-steps.ts @@ -2,10 +2,16 @@ import { FlagRouter } from "@medusajs/utils" import { AwilixContainer } from "awilix" import { EntityManager } from "typeorm" import { Cart } from "../../../../../../models" -import { CartService, LineItemService } from "../../../../../../services" +import { + CartService, + LineItemService, + ProductVariantInventoryService, +} from "../../../../../../services" import { WithRequiredProperty } from "../../../../../../types/common" import { IdempotencyCallbackResult } from "../../../../../../types/idempotency-key" import { defaultStoreCartFields, defaultStoreCartRelations } from "../../index" +import SalesChannelFeatureFlag from "../../../../../../loaders/feature-flags/sales-channels" +import { MedusaError } from "medusa-core-utils" export const CreateLineItemSteps = { STARTED: "started", @@ -26,6 +32,9 @@ export async function handleAddOrUpdateLineItem( const lineItemService: LineItemService = container.resolve("lineItemService") const featureFlagRouter: FlagRouter = container.resolve("featureFlagRouter") + const productVariantInventoryService: ProductVariantInventoryService = + container.resolve("productVariantInventoryService") + const txCartService = cartService.withTransaction(manager) let cart = await txCartService.retrieve(cartId, { @@ -43,17 +52,30 @@ export async function handleAddOrUpdateLineItem( validateSalesChannels: featureFlagRouter.isFeatureEnabled("sales_channels"), }) + const relations = [ + ...defaultStoreCartRelations, + "billing_address", + "region.payment_providers", + "payment_sessions", + "customer", + ] + + const shouldSetAvailability = + relations?.some((rel) => rel.includes("variant")) && + featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key) + cart = await txCartService.retrieveWithTotals(cart.id, { select: defaultStoreCartFields, - relations: [ - ...defaultStoreCartRelations, - "billing_address", - "region.payment_providers", - "payment_sessions", - "customer", - ], + relations, }) + if (shouldSetAvailability) { + await productVariantInventoryService.setVariantAvailability( + cart.items.map((i) => i.variant), + cart.sales_channel_id! + ) + } + if (cart.payment_sessions?.length) { await txCartService.setPaymentSessions( cart as WithRequiredProperty diff --git a/packages/medusa/src/api/routes/store/carts/create-payment-sessions.ts b/packages/medusa/src/api/routes/store/carts/create-payment-sessions.ts index 0e9f9b330d..67bf8cd56a 100644 --- a/packages/medusa/src/api/routes/store/carts/create-payment-sessions.ts +++ b/packages/medusa/src/api/routes/store/carts/create-payment-sessions.ts @@ -1,5 +1,9 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" + import { EntityManager } from "typeorm" import IdempotencyKeyService from "../../../../services/idempotency-key" import { cleanResponseData } from "../../../../utils/clean-response-data" @@ -55,6 +59,10 @@ export default async (req, res) => { const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve( "idempotencyKeyService" ) + + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + const manager: EntityManager = req.scope.resolve("manager") const headerKey = req.get("Idempotency-Key") || "" @@ -98,6 +106,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + cart.items.map((i) => i.variant), + cart.sales_channel_id! + ) + return { response_code: 200, response_body: { cart }, diff --git a/packages/medusa/src/api/routes/store/carts/delete-discount.ts b/packages/medusa/src/api/routes/store/carts/delete-discount.ts index 764bc4a54f..f940eb3b55 100644 --- a/packages/medusa/src/api/routes/store/carts/delete-discount.ts +++ b/packages/medusa/src/api/routes/store/carts/delete-discount.ts @@ -1,6 +1,10 @@ -import { EntityManager } from "typeorm" +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" + +import { EntityManager } from "typeorm" import { cleanResponseData } from "../../../../utils/clean-response-data" /** @@ -53,6 +57,8 @@ export default async (req, res) => { const manager: EntityManager = req.scope.resolve("manager") const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") await manager.transaction(async (m) => { // Remove the discount @@ -73,5 +79,10 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/delete-line-item.ts b/packages/medusa/src/api/routes/store/carts/delete-line-item.ts index 85a47d7405..0d31b1295a 100644 --- a/packages/medusa/src/api/routes/store/carts/delete-line-item.ts +++ b/packages/medusa/src/api/routes/store/carts/delete-line-item.ts @@ -1,6 +1,10 @@ -import { EntityManager } from "typeorm" +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" + +import { EntityManager } from "typeorm" import { cleanResponseData } from "../../../../utils/clean-response-data" /** @@ -53,6 +57,9 @@ export default async (req, res) => { const manager: EntityManager = req.scope.resolve("manager") const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + await manager.transaction(async (m) => { const cartServiceTx = cartService.withTransaction(m) @@ -74,5 +81,10 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/delete-payment-session.ts b/packages/medusa/src/api/routes/store/carts/delete-payment-session.ts index bf3c3465c0..0cca8e5783 100644 --- a/packages/medusa/src/api/routes/store/carts/delete-payment-session.ts +++ b/packages/medusa/src/api/routes/store/carts/delete-payment-session.ts @@ -1,5 +1,9 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" + import { EntityManager } from "typeorm" import { cleanResponseData } from "../../../../utils/clean-response-data" @@ -52,6 +56,9 @@ export default async (req, res) => { const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + const manager: EntityManager = req.scope.resolve("manager") await manager.transaction(async (transactionManager) => { return await cartService @@ -64,5 +71,10 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/get-cart.ts b/packages/medusa/src/api/routes/store/carts/get-cart.ts index d7aa8f4ec1..053315df43 100644 --- a/packages/medusa/src/api/routes/store/carts/get-cart.ts +++ b/packages/medusa/src/api/routes/store/carts/get-cart.ts @@ -1,5 +1,10 @@ -import { CartService } from "../../../../services" +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" + import { EntityManager } from "typeorm" +import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" import { cleanResponseData } from "../../../../utils/clean-response-data" /** @@ -50,6 +55,9 @@ export default async (req, res) => { const cartService: CartService = req.scope.resolve("cartService") const manager: EntityManager = req.scope.resolve("manager") + const featureFlagRouter = req.scope.resolve("featureFlagRouter") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") const cart = await cartService.retrieve(id, { select: ["id", "customer_id"], @@ -69,7 +77,27 @@ export default async (req, res) => { }) } } + const shouldSetAvailability = req.retrieveConfig.relations?.some((rel) => + rel.includes("variant") + ) + + const select = [...req.retrieveConfig.select] + const salesChannelsEnabled = featureFlagRouter.isFeatureEnabled( + SalesChannelFeatureFlag.key + ) + + if (salesChannelsEnabled) { + select.push("sales_channel_id") + } const data = await cartService.retrieveWithTotals(id, req.retrieveConfig) + + if (shouldSetAvailability) { + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + } + res.json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/set-payment-session.ts b/packages/medusa/src/api/routes/store/carts/set-payment-session.ts index 49d0ddd25d..a830ff22b9 100644 --- a/packages/medusa/src/api/routes/store/carts/set-payment-session.ts +++ b/packages/medusa/src/api/routes/store/carts/set-payment-session.ts @@ -1,6 +1,9 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" import { EntityManager } from "typeorm" import { IsString } from "class-validator" import { cleanResponseData } from "../../../../utils/clean-response-data" @@ -67,6 +70,9 @@ export default async (req, res) => { const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + const manager: EntityManager = req.scope.resolve("manager") await manager.transaction(async (transactionManager) => { return await cartService @@ -79,6 +85,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/update-cart.ts b/packages/medusa/src/api/routes/store/carts/update-cart.ts index 5d59b65cd2..6f08ccd76f 100644 --- a/packages/medusa/src/api/routes/store/carts/update-cart.ts +++ b/packages/medusa/src/api/routes/store/carts/update-cart.ts @@ -1,3 +1,7 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { IsArray, IsEmail, @@ -7,15 +11,14 @@ import { } from "class-validator" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { Type } from "class-transformer" -import { EntityManager } from "typeorm" -import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" -import { CartService } from "../../../../services" import { AddressPayload } from "../../../../types/common" +import { EntityManager } from "typeorm" import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators" import { IsType } from "../../../../utils/validators/is-type" -import { cleanResponseData } from "../../../../utils/clean-response-data" import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain" +import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels" +import { Type } from "class-transformer" +import { cleanResponseData } from "../../../../utils/clean-response-data" /** * @oas [post] /store/carts/{id} @@ -79,6 +82,9 @@ export default async (req, res) => { const featureFlagRouter = req.scope.resolve("featureFlagRouter") const manager: EntityManager = req.scope.resolve("manager") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + if (req.user?.customer_id) { validated.customer_id = req.user.customer_id } @@ -111,6 +117,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/update-line-item.ts b/packages/medusa/src/api/routes/store/carts/update-line-item.ts index 40a3c3b956..a107760f26 100644 --- a/packages/medusa/src/api/routes/store/carts/update-line-item.ts +++ b/packages/medusa/src/api/routes/store/carts/update-line-item.ts @@ -1,7 +1,10 @@ +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { IsInt, IsOptional } from "class-validator" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" import { EntityManager } from "typeorm" import { MedusaError } from "medusa-core-utils" import { cleanResponseData } from "../../../../utils/clean-response-data" @@ -70,6 +73,9 @@ export default async (req, res) => { const manager: EntityManager = req.scope.resolve("manager") const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") + await manager.transaction(async (m) => { // If the quantity is 0 that is effectively deletion if (validated.quantity === 0) { @@ -115,6 +121,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/api/routes/store/carts/update-payment-session.ts b/packages/medusa/src/api/routes/store/carts/update-payment-session.ts index d00907c540..ecfd9757cc 100644 --- a/packages/medusa/src/api/routes/store/carts/update-payment-session.ts +++ b/packages/medusa/src/api/routes/store/carts/update-payment-session.ts @@ -1,7 +1,11 @@ -import { IsObject } from "class-validator" +import { + CartService, + ProductVariantInventoryService, +} from "../../../../services" import { defaultStoreCartFields, defaultStoreCartRelations } from "." -import { CartService } from "../../../../services" + import { EntityManager } from "typeorm" +import { IsObject } from "class-validator" import { cleanResponseData } from "../../../../utils/clean-response-data" /** @@ -68,6 +72,8 @@ export default async (req, res) => { const validated = req.validatedBody const cartService: CartService = req.scope.resolve("cartService") + const productVariantInventoryService: ProductVariantInventoryService = + req.scope.resolve("productVariantInventoryService") const manager: EntityManager = req.scope.resolve("manager") await manager.transaction(async (transactionManager) => { @@ -84,6 +90,11 @@ export default async (req, res) => { relations: defaultStoreCartRelations, }) + await productVariantInventoryService.setVariantAvailability( + data.items.map((i) => i.variant), + data.sales_channel_id! + ) + res.status(200).json({ cart: cleanResponseData(data, []) }) } diff --git a/packages/medusa/src/services/__mocks__/cart.js b/packages/medusa/src/services/__mocks__/cart.js index fbf729d466..c4b1bc5749 100644 --- a/packages/medusa/src/services/__mocks__/cart.js +++ b/packages/medusa/src/services/__mocks__/cart.js @@ -1,5 +1,5 @@ -import { MedusaError } from "medusa-core-utils" import { IdMap } from "medusa-test-utils" +import { MedusaError } from "medusa-core-utils" export const carts = { emptyCart: { @@ -66,6 +66,7 @@ export const carts = { id: IdMap.getId("regionCart"), name: "Product 1", region_id: IdMap.getId("testRegion"), + items: [], }, frCart: { id: IdMap.getId("fr-cart"),