Example #1
0
        public async Task <ActionResult> ProductDetails(string productId)
        {
            var product = (await _catalogSearchService.GetProductsAsync(new[] { productId }, Model.Catalog.ItemResponseGroup.ItemInfo | Model.Catalog.ItemResponseGroup.ItemWithPrices)).FirstOrDefault();

            WorkContext.CurrentProduct = product;

            WorkContext.CurrentCatalogSearchCriteria.CategoryId = product.CategoryId;
            WorkContext.CurrentCatalogSearchResult = await _catalogSearchService.SearchAsync(WorkContext.CurrentCatalogSearchCriteria);

            return(View("product", WorkContext));
        }
        public async Task <ActionResult> Add(string id, int quantity)
        {
            EnsureThatCartExist();
            using (await AsyncLock.GetLockByKey(GetAsyncLockCartKey(_workContext.CurrentCart)).LockAsync())
            {
                var product = (await _catalogService.GetProductsAsync(new string[] { id }, Model.Catalog.ItemResponseGroup.ItemLarge)).FirstOrDefault();
                if (product != null)
                {
                    await _cartBuilder.AddItemAsync(product, quantity);

                    await _cartBuilder.SaveAsync();
                }
            }
            return(StoreFrontRedirect("~/cart"));
        }
        public async Task <ActionResult> Add(string id, int quantity)
        {
            await _cartBuilder.GetOrCreateNewTransientCartAsync(WorkContext.CurrentStore, WorkContext.CurrentCustomer, WorkContext.CurrentLanguage, WorkContext.CurrentCurrency);

            var product = (await _catalogService.GetProductsAsync(new string[] { id }, Model.Catalog.ItemResponseGroup.ItemLarge)).FirstOrDefault();

            if (product != null)
            {
                await _cartBuilder.AddItemAsync(product, quantity);

                await _cartBuilder.SaveAsync();
            }

            return(StoreFrontRedirect("~/cart"));
        }
        public async Task <ActionResult> ProductDetails(string productId)
        {
            var product = (await _catalogSearchService.GetProductsAsync(new[] { productId }, WorkContext.CurrentProductResponseGroup)).FirstOrDefault();

            WorkContext.CurrentProduct = product;

            if (product != null)
            {
                WorkContext.CurrentPageSeo = product.SeoInfo;

                if (product.CategoryId != null)
                {
                    var category = (await _catalogSearchService.GetCategoriesAsync(new[] { product.CategoryId }, CategoryResponseGroup.Full)).FirstOrDefault();
                    WorkContext.CurrentCategory = category;

                    if (category != null)
                    {
                        category.Products = new MutablePagedList <Product>((pageNumber, pageSize, sortInfos) =>
                        {
                            var criteria        = WorkContext.CurrentCatalogSearchCriteria.Clone();
                            criteria.CategoryId = product.CategoryId;
                            criteria.PageNumber = pageNumber;
                            criteria.PageSize   = pageSize;
                            if (string.IsNullOrEmpty(criteria.SortBy) && !sortInfos.IsNullOrEmpty())
                            {
                                criteria.SortBy = SortInfo.ToString(sortInfos);
                            }
                            return(_catalogSearchService.SearchProducts(criteria).Products);
                        });
                    }
                }
            }

            return(View("product", WorkContext));
        }
Example #5
0
        public async Task <ActionResult> ProductDetails(string productId)
        {
            var product = (await _catalogSearchService.GetProductsAsync(new[] { productId },
                                                                        Model.Catalog.ItemResponseGroup.Variations |
                                                                        Model.Catalog.ItemResponseGroup.ItemProperties |
                                                                        Model.Catalog.ItemResponseGroup.ItemSmall |
                                                                        Model.Catalog.ItemResponseGroup.ItemWithPrices)).FirstOrDefault();

            WorkContext.CurrentProduct = product;
            if (product.CategoryId != null)
            {
                var category = (await _catalogSearchService.GetCategoriesAsync(new[] { product.CategoryId }, Model.Catalog.CategoryResponseGroup.Full)).FirstOrDefault();
                WorkContext.CurrentCategory = category;
                category.Products           = new MutablePagedList <Product>((pageNumber, pageSize) =>
                {
                    var criteria        = WorkContext.CurrentCatalogSearchCriteria.Clone();
                    criteria.CategoryId = product.CategoryId;
                    criteria.PageNumber = pageNumber;
                    criteria.PageSize   = pageSize;
                    return(_catalogSearchService.SearchProducts(criteria).Products);
                });
            }

            return(View("product", WorkContext));
        }
Example #6
0
        public async Task <Product[]> GetRecommendationsAsync(RecommendationEvalContext context)
        {
            Product[] products = await _catalogSearchService.GetProductsAsync(context.ProductIds.ToArray(), ItemResponseGroup.ItemAssociations);

            //Need to load related products from associated product and categories
            var retVal = products.SelectMany(p => p.Associations.OfType <ProductAssociation>().OrderBy(x => x.Priority))
                         .Select(a => a.Product).ToList();

            retVal.AddRange(products.SelectMany(p => p.Associations.OfType <CategoryAssociation>().OrderBy(x => x.Priority))
                            .SelectMany(a => a.Category.Products.ToArray()));

            return(retVal.Take(context.Take).ToArray());
        }
        public void CheckoutQuoteRequest()
        {
            WorkContext          workContext         = GetTestWorkContext();
            IQuoteRequestBuilder quoteRequestBuilder = GetQuoteRequestBuilder();
            var customer = new CustomerInfo
            {
                Id = Guid.NewGuid().ToString(),
                IsRegisteredUser = false
            };

            workContext.CurrentCustomer = customer;

            QuoteRequest quoteRequest = quoteRequestBuilder.GetOrCreateNewTransientQuoteRequestAsync(workContext.CurrentStore, customer, workContext.CurrentLanguage, workContext.CurrentCurrency).Result.QuoteRequest;

            ICatalogSearchService catalogSearchService = GetCatalogSearchService();

            Product[] searchResult = catalogSearchService.GetProductsAsync(new[] { "217be9f3d9064075821f6785dca658b9" }, ItemResponseGroup.ItemLarge).Result;
            quoteRequestBuilder.AddItem(searchResult.First(), 1);
            quoteRequestBuilder.SaveAsync().Wait();

            var quoteItem = quoteRequestBuilder.QuoteRequest.Items.First();

            quoteItem.ProposalPrices.Add(new TierPrice(quoteItem.SalePrice * 0.7, 5));
            quoteItem.ProposalPrices.Add(new TierPrice(quoteItem.SalePrice * 0.5, 10));
            quoteRequestBuilder.SaveAsync().Wait();
            quoteRequest = quoteRequestBuilder.GetOrCreateNewTransientQuoteRequestAsync(workContext.CurrentStore, customer, workContext.CurrentLanguage, workContext.CurrentCurrency).Result.QuoteRequest;

            Assert.NotNull(quoteRequest.Items);
            Assert.Equal(quoteRequest.Items.Count, 1);

            var requestItem = quoteRequest.Items.First();

            foreach (var proposalPrice in requestItem.ProposalPrices)
            {
                requestItem.SelectedTierPrice = proposalPrice;

                ICartBuilder cartBuilder = GetCartBuilder();
                cartBuilder.LoadOrCreateNewTransientCartAsync("default", workContext.CurrentStore, customer, workContext.CurrentLanguage, workContext.CurrentCurrency).Wait();
                cartBuilder.FillFromQuoteRequestAsync(quoteRequest).Wait();
                cartBuilder.SaveAsync().Wait();

                cartBuilder.LoadOrCreateNewTransientCartAsync("default", workContext.CurrentStore, customer, workContext.CurrentLanguage, workContext.CurrentCurrency).Wait();
                var cart = cartBuilder.Cart;
                var item = cart.Items.First();

                Assert.Equal(requestItem.SelectedTierPrice.ActualPrice, item.SalePrice);
                Assert.Equal(requestItem.SelectedTierPrice.Quantity, item.Quantity);
            }
        }
        public async Task <MenuLinkList[]> LoadAllStoreLinkListsAsync(string storeId)
        {
            var retVal    = new List <MenuLinkList>();
            var linkLists = await _cmsApi.Menu.GetListsAsync(storeId);

            if (linkLists != null)
            {
                retVal.AddRange(linkLists.Select(x => x.ToMenuLinkList()));

                var allMenuLinks  = retVal.SelectMany(x => x.MenuLinks).ToList();
                var productLinks  = allMenuLinks.OfType <ProductMenuLink>().ToList();
                var categoryLinks = allMenuLinks.OfType <CategoryMenuLink>().ToList();

                Task <Product[]>  productsLoadingTask   = null;
                Task <Category[]> categoriesLoadingTask = null;

                //Parallel loading associated objects
                var productIds = productLinks.Select(x => x.AssociatedObjectId).ToArray();
                if (productIds.Any())
                {
                    productsLoadingTask = _catalogSearchService.GetProductsAsync(productIds, ItemResponseGroup.ItemSmall | ItemResponseGroup.Seo | ItemResponseGroup.Outlines);
                }
                var categoriesIds = categoryLinks.Select(x => x.AssociatedObjectId).ToArray();
                if (categoriesIds.Any())
                {
                    categoriesLoadingTask = _catalogSearchService.GetCategoriesAsync(categoriesIds, CategoryResponseGroup.Info | CategoryResponseGroup.WithImages | CategoryResponseGroup.WithSeo | CategoryResponseGroup.WithOutlines);
                }
                //Populate link by associated product
                if (productsLoadingTask != null)
                {
                    var products = await productsLoadingTask;
                    foreach (var productLink in productLinks)
                    {
                        productLink.Product = products.FirstOrDefault(x => x.Id == productLink.AssociatedObjectId);
                    }
                }
                //Populate link by associated category
                if (categoriesLoadingTask != null)
                {
                    var categories = await categoriesLoadingTask;
                    foreach (var categoryLink in categoryLinks)
                    {
                        categoryLink.Category = categories.FirstOrDefault(x => x.Id == categoryLink.AssociatedObjectId);
                    }
                }
            }
            return(retVal.ToArray());
        }
        public async Task <ActionResult> AddItemJson(string productId, long quantity)
        {
            EnsureThatQuoteRequestExists();

            using (var lockObject = await AsyncLock.GetLockByKey("quote-product:" + productId).LockAsync())
            {
                var products = await _catalogSearchService.GetProductsAsync(new string[] { productId }, ItemResponseGroup.ItemLarge);

                if (products != null && products.Any())
                {
                    _quoteRequestBuilder.AddItem(products.First(), quantity);
                    await _quoteRequestBuilder.SaveAsync();
                }
            }

            return(Json(null, JsonRequestBehavior.AllowGet));
        }
        public async Task <ActionResult> AddItemToList(string productId, string listName)
        {
            //Need lock to prevent concurrent access to same cart
            using (await AsyncLock.GetLockByKey(GetAsyncLockCartKey(WorkContext, listName)).LockAsync())
            {
                var wishlistBuilder = await LoadOrCreateWishlistAsync(listName);

                var products = await _catalogSearchService.GetProductsAsync(new[] { productId }, Model.Catalog.ItemResponseGroup.ItemLarge);

                if (products != null && products.Any())
                {
                    await wishlistBuilder.AddItemAsync(products.First(), 1);

                    await wishlistBuilder.SaveAsync();
                }
                return(Json(new { ItemsCount = wishlistBuilder.Cart.ItemsQuantity }));
            }
        }
        public async Task <ActionResult> AddItem(string productId, int quantity)
        {
            EnsureQuoteRequestBelongsToCurrentCustomer(WorkContext.CurrentQuoteRequest);
            _quoteRequestBuilder.TakeQuoteRequest(WorkContext.CurrentQuoteRequest);

            using (await AsyncLock.GetLockByKey(GetAsyncLockQuoteKey(_quoteRequestBuilder.QuoteRequest.Id)).LockAsync())
            {
                var products = await _catalogSearchService.GetProductsAsync(new[] { productId }, ItemResponseGroup.ItemLarge);

                if (products != null && products.Any())
                {
                    _quoteRequestBuilder.AddItem(products.First(), quantity);
                    await _quoteRequestBuilder.SaveAsync();
                }
            }

            return(new HttpStatusCodeResult(HttpStatusCode.OK));
        }
Example #12
0
        public async Task <ActionResult> AddItemJson(string id, int quantity = 1)
        {
            EnsureThatCartExist();

            //Need lock to prevent concurrent access to same cart
            using (var lockObject = await AsyncLock.GetLockByKey(GetAsyncLockCartKey(id)).LockAsync())
            {
                var products = await _catalogService.GetProductsAsync(new string[] { id }, Model.Catalog.ItemResponseGroup.ItemLarge);

                if (products != null && products.Any())
                {
                    await _cartBuilder.AddItemAsync(products.First(), quantity);

                    await _cartBuilder.SaveAsync();
                }
            }
            return(Json(null, JsonRequestBehavior.AllowGet));
        }
        public async Task <ActionResult> AddItemToCart(string id, int quantity = 1)
        {
            EnsureThatCartExist();

            //Need lock to prevent concurrent access to same cart
            using (await AsyncLock.GetLockByKey(GetAsyncLockCartKey(WorkContext.CurrentCart)).LockAsync())
            {
                var products = await _catalogSearchService.GetProductsAsync(new[] { id }, Model.Catalog.ItemResponseGroup.ItemLarge);

                if (products != null && products.Any())
                {
                    await _cartBuilder.AddItemAsync(products.First(), quantity);

                    await _cartBuilder.SaveAsync();
                }
            }
            return(Json(new { _cartBuilder.Cart.ItemsCount }));
        }
        public async Task <Product[]> GetRecommendationsAsync(Model.Recommendations.RecommendationEvalContext context)
        {
            var cognitiveContext = context as CognitiveRecommendationEvalContext;

            if (cognitiveContext == null)
            {
                throw new InvalidCastException(nameof(context));
            }

            var result = new List <Product>();

            var recommendedProductIds = await _productRecommendationsApi.Recommendations.GetRecommendationsAsync(cognitiveContext.ToContextDto());

            if (recommendedProductIds != null)
            {
                result.AddRange(await _catalogSearchService.GetProductsAsync(recommendedProductIds.ToArray(), ItemResponseGroup.Seo | ItemResponseGroup.Outlines | ItemResponseGroup.ItemWithPrices | ItemResponseGroup.ItemWithDiscounts | ItemResponseGroup.Inventory));
            }

            return(result.ToArray());
        }
Example #15
0
        public virtual async Task <ICartBuilder> FillFromQuoteRequest(QuoteRequest quoteRequest)
        {
            var productIds = quoteRequest.Items.Select(i => i.ProductId);
            var products   = await _catalogSearchService.GetProductsAsync(productIds.ToArray(), ItemResponseGroup.ItemLarge);

            _cart.Items.Clear();
            foreach (var product in products)
            {
                var quoteItem = quoteRequest.Items.FirstOrDefault(i => i.ProductId == product.Id);
                if (quoteItem != null)
                {
                    var lineItem = product.ToLineItem(_cart.Language, (int)quoteItem.SelectedTierPrice.Quantity);
                    lineItem.ListPrice      = quoteItem.ListPrice;
                    lineItem.SalePrice      = quoteItem.SelectedTierPrice.Price;
                    lineItem.ValidationType = ValidationType.None;

                    AddLineItem(lineItem);
                }
            }

            if (quoteRequest.RequestShippingQuote)
            {
                _cart.Shipments.Clear();
                var shipment = new Shipment(_cart.Currency);

                foreach (var item in _cart.Items)
                {
                    shipment.Items.Add(item.ToShipmentItem());
                }

                if (quoteRequest.ShippingAddress != null)
                {
                    shipment.DeliveryAddress = quoteRequest.ShippingAddress;
                }

                if (quoteRequest.ShipmentMethod != null)
                {
                    var availableShippingMethods = await GetAvailableShippingMethodsAsync();

                    if (availableShippingMethods != null)
                    {
                        var availableShippingMethod = availableShippingMethods.FirstOrDefault(sm => sm.ShipmentMethodCode == quoteRequest.ShipmentMethod.ShipmentMethodCode);
                        if (availableShippingMethod != null)
                        {
                            shipment = quoteRequest.ShipmentMethod.ToShipmentModel(_cart.Currency);
                        }
                    }
                }

                _cart.Shipments.Add(shipment);
            }

            _cart.Payments.Clear();
            var payment = new Payment(_cart.Currency);

            if (quoteRequest.BillingAddress != null)
            {
                payment.BillingAddress = quoteRequest.BillingAddress;
            }

            payment.Amount = quoteRequest.Totals.GrandTotalInclTax;

            _cart.Payments.Add(payment);

            return(this);
        }
Example #16
0
        public virtual async Task FillFromQuoteRequestAsync(QuoteRequest quoteRequest)
        {
            EnsureCartExists();

            var productIds = quoteRequest.Items.Select(i => i.ProductId);
            var products   = await _catalogSearchService.GetProductsAsync(productIds.ToArray(), ItemResponseGroup.ItemLarge);

            Cart.Items.Clear();
            foreach (var product in products)
            {
                var quoteItem = quoteRequest.Items.FirstOrDefault(i => i.ProductId == product.Id);
                if (quoteItem != null)
                {
                    var lineItem = product.ToLineItem(Cart.Language, (int)quoteItem.SelectedTierPrice.Quantity);
                    lineItem.ListPrice = quoteItem.ListPrice;
                    lineItem.SalePrice = quoteItem.SelectedTierPrice.Price;
                    if (lineItem.ListPrice < lineItem.SalePrice)
                    {
                        lineItem.ListPrice = lineItem.SalePrice;
                    }
                    lineItem.IsReadOnly = true;
                    lineItem.Id         = null;
                    Cart.Items.Add(lineItem);
                }
            }

            if (quoteRequest.RequestShippingQuote)
            {
                Cart.Shipments.Clear();
                var shipment = new Shipment(Cart.Currency);

                if (quoteRequest.ShippingAddress != null)
                {
                    shipment.DeliveryAddress = quoteRequest.ShippingAddress;
                }

                if (quoteRequest.ShipmentMethod != null)
                {
                    var availableShippingMethods = await GetAvailableShippingMethodsAsync();

                    var availableShippingMethod = availableShippingMethods?.FirstOrDefault(sm => sm.ShipmentMethodCode == quoteRequest.ShipmentMethod.ShipmentMethodCode);

                    if (availableShippingMethod != null)
                    {
                        shipment = quoteRequest.ShipmentMethod.ToCartShipment(Cart.Currency);
                    }
                }
                Cart.Shipments.Add(shipment);
            }

            var payment = new Payment(Cart.Currency)
            {
                Amount = quoteRequest.Totals.GrandTotalInclTax
            };

            if (quoteRequest.BillingAddress != null)
            {
                payment.BillingAddress = quoteRequest.BillingAddress;
            }

            Cart.Payments.Clear();
            Cart.Payments.Add(payment);
        }
Example #17
0
        private async Task ValidateItemsAsync(ShoppingCart cart)
        {
            var workContext = _workContextFactory();
            var productIds  = cart.Items.Select(i => i.ProductId).ToArray();
            var cacheKey    = "CartValidator.ValidateItemsAsync-" + workContext.CurrentCurrency.Code + ":" + workContext.CurrentLanguage + ":" + string.Join(":", productIds);
            var products    = await _cacheManager.GetAsync(cacheKey, "ApiRegion", async() => await _catalogService.GetProductsAsync(productIds, ItemResponseGroup.ItemWithPrices | ItemResponseGroup.ItemWithDiscounts | ItemResponseGroup.Inventory));

            foreach (var lineItem in cart.Items.ToList())
            {
                lineItem.ValidationErrors.Clear();

                var product = products.FirstOrDefault(p => p.Id == lineItem.ProductId);
                if (product == null || !product.IsActive || !product.IsBuyable)
                {
                    lineItem.ValidationErrors.Add(new ProductUnavailableError());
                }
                else
                {
                    if (product.TrackInventory && product.Inventory != null &&
                        (lineItem.ValidationType == ValidationType.PriceAndQuantity || lineItem.ValidationType == ValidationType.Quantity))
                    {
                        var availableQuantity = product.Inventory.InStockQuantity;
                        if (product.Inventory.ReservedQuantity.HasValue)
                        {
                            availableQuantity -= product.Inventory.ReservedQuantity.Value;
                        }
                        if (availableQuantity.HasValue && lineItem.Quantity > availableQuantity.Value)
                        {
                            lineItem.ValidationErrors.Add(new ProductQuantityError(availableQuantity.Value));
                        }
                    }
                    if (lineItem.ValidationType == ValidationType.PriceAndQuantity || lineItem.ValidationType == ValidationType.Price)
                    {
                        var tierPrice = product.Price.GetTierPrice(lineItem.Quantity);
                        if (tierPrice.ActualPrice != lineItem.PlacedPrice)
                        {
                            lineItem.ValidationWarnings.Add(new ProductPriceError(lineItem.PlacedPrice, lineItem.PlacedPriceWithTax));
                            lineItem.SalePrice        = tierPrice.Price;
                            lineItem.SalePriceWithTax = tierPrice.PriceWithTax;
                        }
                    }
                }
            }
        }
Example #18
0
        private async Task ValidateItemsAsync(ShoppingCart cart)
        {
            var workContext = _workContextFactory();
            var productIds  = cart.Items.Select(i => i.ProductId).ToArray();
            var cacheKey    = "CartValidator.ValidateItemsAsync-" + workContext.CurrentCurrency.Code + ":" + workContext.CurrentLanguage + ":" + string.Join(":", productIds);
            var products    = await _cacheManager.GetAsync(cacheKey, "ApiRegion", async() => { return(await _catalogService.GetProductsAsync(productIds, ItemResponseGroup.ItemLarge)); });

            foreach (var lineItem in cart.Items.ToList())
            {
                var product = products.FirstOrDefault(x => x.Id == lineItem.ProductId);

                lineItem.ValidationErrors.Clear();
                if (product == null || (product != null && (!product.IsActive || !product.IsBuyable)))
                {
                    lineItem.ValidationErrors.Add(new ProductUnavailableError());
                }
                else if (product != null)
                {
                    if (product.TrackInventory && product.Inventory != null)
                    {
                        var availableQuantity = product.Inventory.InStockQuantity;
                        if (product.Inventory.ReservedQuantity.HasValue)
                        {
                            availableQuantity -= product.Inventory.ReservedQuantity.Value;
                        }
                        if (availableQuantity.HasValue && lineItem.Quantity > availableQuantity.Value)
                        {
                            lineItem.ValidationErrors.Add(new ProductQuantityError(availableQuantity.Value));
                        }
                    }

                    if (lineItem.PlacedPrice != product.Price.ActualPrice)
                    {
                        var newLineItem = product.ToLineItem(workContext.CurrentLanguage, lineItem.Quantity);
                        newLineItem.ValidationWarnings.Add(new ProductPriceError(lineItem.PlacedPrice));

                        cart.Items.Remove(lineItem);
                        cart.Items.Add(newLineItem);
                    }
                }
            }
        }
Example #19
0
        public async Task <ActionResult> GetProductsByIds(string[] productIds, ItemResponseGroup respGroup = ItemResponseGroup.ItemLarge)
        {
            var retVal = await _catalogSearchService.GetProductsAsync(productIds, respGroup);

            return(Json(retVal));
        }
Example #20
0
        private async Task ValidateItemsAsync(ShoppingCart cart)
        {
            var workContext = _workContextFactory();
            var productIds  = cart.Items.Select(i => i.ProductId).ToArray();
            var cacheKey    = "CartValidator.ValidateItemsAsync-" + workContext.CurrentCurrency.Code + ":" + workContext.CurrentLanguage + ":" + string.Join(":", productIds);
            var products    = await _cacheManager.GetAsync(cacheKey, "ApiRegion", async() => { return(await _catalogService.GetProductsAsync(productIds, ItemResponseGroup.ItemLarge)); });

            var productsToReevaluate = GetProductsToReevaluate(cart, products);

            // Reevaluate products promotions for getting new product prices to compare with
            if (productsToReevaluate.Any())
            {
                var promotionContext = workContext.ToPromotionEvaluationContext(productsToReevaluate);
                await _promotionEvaluator.EvaluateDiscountsAsync(promotionContext, productsToReevaluate);
            }

            foreach (var lineItem in cart.Items.ToList())
            {
                lineItem.ValidationErrors.Clear();

                var product = products.FirstOrDefault(p => p.Id == lineItem.ProductId);
                if (product == null || (product != null && (!product.IsActive || !product.IsBuyable)))
                {
                    lineItem.ValidationErrors.Add(new ProductUnavailableError());
                }
                else if (product != null)
                {
                    if (product.TrackInventory && product.Inventory != null &&
                        (lineItem.ValidationType == ValidationType.PriceAndQuantity || lineItem.ValidationType == ValidationType.Quantity))
                    {
                        var availableQuantity = product.Inventory.InStockQuantity;
                        if (product.Inventory.ReservedQuantity.HasValue)
                        {
                            availableQuantity -= product.Inventory.ReservedQuantity.Value;
                        }
                        if (availableQuantity.HasValue && lineItem.Quantity > availableQuantity.Value)
                        {
                            lineItem.ValidationErrors.Add(new ProductQuantityError(availableQuantity.Value));
                        }
                    }

                    if (lineItem.PlacedPrice != product.Price.ActualPrice &&
                        (lineItem.ValidationType == ValidationType.PriceAndQuantity || lineItem.ValidationType == ValidationType.Price))
                    {
                        var newLineItem = product.ToLineItem(workContext.CurrentLanguage, lineItem.Quantity);
                        newLineItem.ValidationWarnings.Add(new ProductPriceError(lineItem.PlacedPrice));

                        cart.Items.Remove(lineItem);
                        cart.Items.Add(newLineItem);
                    }
                }
            }

            // Reevaluate shopping cart for applying promotions for changed product prices
            if (productsToReevaluate.Any())
            {
                var promotionContext = workContext.ToPromotionEvaluationContext();
                await _promotionEvaluator.EvaluateDiscountsAsync(promotionContext, new[] { cart });
            }
        }