public virtual async Task <bool> ValidateAddToCartItemAsync(AddToCartContext ctx, ShoppingCartItem cartItem, IEnumerable <OrganizedShoppingCartItem> cartItems)
        {
            Guard.NotNull(ctx, nameof(ctx));
            Guard.NotNull(cartItem, nameof(cartItem));
            Guard.NotNull(cartItems, nameof(cartItems));

            var warnings = new List <string>();

            await ValidateProductAsync(cartItem, warnings, ctx.StoreId);
            await ValidateProductAttributesAsync(cartItem, cartItems, warnings);

            ValidateGiftCardInfo(cartItem, warnings);
            await ValidateRequiredProductsAsync(ctx.Product, cartItems, warnings);

            // Bundle and bundle items (child items) warnings
            if (ctx.BundleItem != null || !ctx.ChildItems.IsNullOrEmpty())
            {
                var bundleItem = ctx.BundleItem ?? ctx.ChildItems.Select(x => x.BundleItem).FirstOrDefault();
                if (bundleItem != null)
                {
                    ValidateBundleItem(bundleItem, warnings);
                }
            }

            ctx.Warnings.AddRange(warnings);
            return(!warnings.Any());
        }
Esempio n. 2
0
        public virtual async Task <IList <string> > CopyAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = await AddToCartAsync(ctx);

            if (warnings.Count > 0 || ctx.ChildItems == null)
            {
                return(warnings);
            }

            foreach (var childItem in ctx.ChildItems)
            {
                ctx.BundleItem           = childItem.BundleItem;
                ctx.Product              = childItem.Product;
                ctx.Quantity             = childItem.Quantity;
                ctx.RawAttributes        = childItem.AttributeSelection.AsJson();
                ctx.CustomerEnteredPrice = childItem.CustomerEnteredPrice;
                ctx.AutomaticallyAddRequiredProductsIfEnabled = false;

                warnings.AddRange(await AddToCartAsync(ctx));
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            return(warnings);
        }
Esempio n. 3
0
        // was GetShoppingCartItemWarnings
        // TODO: (ms) (core) Have bundle item, and child item already added to context
        public virtual async Task <IList <string> > ValidateCartItemAsync(AddToCartContext ctx, IEnumerable <OrganizedShoppingCartItem> shoppingCart)
        {
            Guard.NotNull(ctx, nameof(ctx));
            Guard.NotNull(shoppingCart, nameof(shoppingCart));

            await ValidateProductAsync(ctx, shoppingCart);
            await ValidateProductAttributesAsync(ctx, shoppingCart);

            ValidateGiftCardInfo(ctx);
            ctx.Warnings.AddRange(await ValidateRequiredProductsAsync(ctx, shoppingCart));

            // Bundle and bundle item warnings
            if (ctx.BundleItem != null)
            {
                ctx.Warnings.AddRange(this.ValidateBundleItem(ctx.BundleItem));
            }

            if (ctx.ChildItems != null)
            {
                var childItems = ctx.ChildItems.Select(x => x.BundleItem).ToList();
                ctx.Warnings.AddRange(ValidateBundleItems(childItems));
            }

            return(ctx.Warnings);
        }
Esempio n. 4
0
        public virtual async Task <bool> CopyAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            if (!await AddToCartAsync(ctx) || ctx.ChildItems == null)
            {
                return(false);
            }

            foreach (var childItem in ctx.ChildItems)
            {
                ctx.BundleItem           = childItem.BundleItem;
                ctx.Product              = childItem.Product;
                ctx.Quantity             = childItem.Quantity;
                ctx.RawAttributes        = childItem.AttributeSelection.AsJson();
                ctx.CustomerEnteredPrice = new(childItem.CustomerEnteredPrice, ctx.CustomerEnteredPrice.Currency);
                ctx.AutomaticallyAddRequiredProductsIfEnabled = false;
                ctx.AutomaticallyAddBundleProductsIfEnabled   = false;

                await AddToCartAsync(ctx);
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            return(!ctx.Warnings.Any());
        }
Esempio n. 5
0
        public virtual async Task <IList <string> > UpdateCartItemAsync(Customer customer, int cartItemId, int newQuantity, bool resetCheckoutData)
        {
            Guard.NotNull(customer, nameof(customer));

            var warnings = new List <string>();

            var cartItem = customer.ShoppingCartItems.FirstOrDefault(x => x.Id == cartItemId && x.ParentItemId == null);

            if (cartItem == null)
            {
                return(warnings);
            }

            if (resetCheckoutData)
            {
                customer.ResetCheckoutData(cartItem.StoreId);
            }

            if (newQuantity > 0)
            {
                var ctx = new AddToCartContext
                {
                    Customer             = customer,
                    CartType             = cartItem.ShoppingCartType,
                    Product              = cartItem.Product,
                    StoreId              = cartItem.StoreId,
                    RawAttributes        = cartItem.AttributeSelection.AsJson(),
                    CustomerEnteredPrice = new Money(cartItem.CustomerEnteredPrice, _primaryCurrency),
                    Quantity             = newQuantity,
                    AutomaticallyAddRequiredProductsIfEnabled = false,
                };

                var cartItems = await GetCartItemsAsync(customer, cartItem.ShoppingCartType, cartItem.StoreId);

                if (await _cartValidator.ValidateAddToCartItemAsync(ctx, cartItem, cartItems))
                {
                    cartItem.Quantity     = newQuantity;
                    cartItem.UpdatedOnUtc = DateTime.UtcNow;
                    _db.TryUpdate(customer);
                    await _db.SaveChangesAsync();
                }
                else
                {
                    warnings.AddRange(ctx.Warnings);
                }
            }
            else
            {
                await DeleteCartItemsAsync(new[] { cartItem }, resetCheckoutData, true);
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            return(warnings);
        }
        public virtual async Task <bool> MigrateCartAsync(Customer fromCustomer, Customer toCustomer)
        {
            Guard.NotNull(fromCustomer, nameof(fromCustomer));
            Guard.NotNull(toCustomer, nameof(toCustomer));

            if (fromCustomer.Id == toCustomer.Id)
            {
                return(false);
            }

            var cartItems = await OrganizeCartItemsAsync(fromCustomer.ShoppingCartItems);

            if (cartItems.IsNullOrEmpty())
            {
                return(false);
            }

            var storeId = 0;

            foreach (var cartItem in cartItems)
            {
                if (storeId == 0)
                {
                    storeId = cartItem.Item.StoreId;
                }

                var ctx = new AddToCartContext
                {
                    Product              = cartItem.Item.Product,
                    RawAttributes        = cartItem.Item.AttributeSelection.AsJson(),
                    CustomerEnteredPrice = new Money(cartItem.Item.CustomerEnteredPrice, _workContext.WorkingCurrency),
                    Quantity             = cartItem.Item.Quantity,
                    ChildItems           = cartItem.ChildItems.Select(x => x.Item).ToList(),
                    Customer             = toCustomer,
                    CartType             = cartItem.Item.ShoppingCartType,
                    StoreId              = cartItem.Item.StoreId,
                    AutomaticallyAddRequiredProductsIfEnabled = false
                };

                if (!await CopyAsync(ctx))
                {
                    return(false);
                }
            }

            if (fromCustomer != null && toCustomer != null)
            {
                _eventPublisher.Publish(new MigrateShoppingCartEvent(fromCustomer, toCustomer, storeId));
            }

            var itemsToDelete = cartItems.Select(x => x.Item);

            return(await DeleteCartItemsAsync(itemsToDelete) - itemsToDelete.Count() == 0);
        }
Esempio n. 7
0
        public virtual async Task <IList <string> > ValidateAccessPermissionsAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = new List <string>();

            // Validate access permissions
            if (ctx.CartType == ShoppingCartType.ShoppingCart && !await _permissionService.AuthorizeAsync(Permissions.Cart.AccessShoppingCart, ctx.Customer))
            {
                warnings.Add(T("ShoppingCart.IsDisabled"));
            }
            else if (ctx.CartType == ShoppingCartType.Wishlist && !await _permissionService.AuthorizeAsync(Permissions.Cart.AccessWishlist, ctx.Customer))
            {
                warnings.Add(T("Wishlist.IsDisabled"));
            }

            ctx.Warnings.AddRange(warnings);
            return(warnings);
        }
Esempio n. 8
0
        // was GetShoppingCartItemGiftCardWarnings
        public virtual IList <string> ValidateGiftCardInfo(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = new List <string>();

            if (!ctx.Product.IsGiftCard)
            {
                return(warnings);
            }

            var recipientName  = ctx.AttributeSelection.GiftCardInfo?.RecipientName;
            var recipientEmail = ctx.AttributeSelection.GiftCardInfo?.RecipientEmail;
            var senderName     = ctx.AttributeSelection.GiftCardInfo?.SenderName;
            var senderEmail    = ctx.AttributeSelection.GiftCardInfo?.SenderEmail;

            if (recipientName.IsEmpty())
            {
                warnings.Add(T("ShoppingCart.RecipientNameError"));
            }

            if (senderName.IsEmpty())
            {
                warnings.Add(T("ShoppingCart.SenderNameError"));
            }

            if (ctx.Product.GiftCardType == GiftCardType.Virtual)
            {
                if (recipientEmail.IsEmpty() || !recipientEmail.IsEmail())
                {
                    warnings.Add(T("ShoppingCart.RecipientEmailError"));
                }

                if (senderEmail.IsEmpty() || !senderEmail.IsEmail())
                {
                    warnings.Add(T("ShoppingCart.SenderEmailError"));
                }
            }

            ctx.Warnings.AddRange(warnings);
            return(warnings);
        }
Esempio n. 9
0
        protected virtual async Task AddItemToCartAsync(AddToCartContext ctx)
        {
            if (ctx.Item != null)
            {
                ctx.Customer.ShoppingCartItems.Add(ctx.Item);

                if (!ctx.ChildItems.IsNullOrEmpty())
                {
                    foreach (var childItem in ctx.ChildItems)
                    {
                        childItem.ParentItemId = ctx.Item.Id;
                    }

                    ctx.Customer.ShoppingCartItems.AddRange(ctx.ChildItems);
                }

                _db.TryUpdate(ctx.Customer);
                await _db.SaveChangesAsync();
            }
        }
Esempio n. 10
0
        public virtual async Task AddItemToCartAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));
            Guard.NotNull(ctx.Item, nameof(ctx.Item));

            var customer = ctx.Customer ?? _workContext.CurrentCustomer;

            customer.ShoppingCartItems.Add(ctx.Item);
            await _db.SaveChangesAsync();

            if (ctx.ChildItems.Any())
            {
                foreach (var childItem in ctx.ChildItems)
                {
                    childItem.ParentItemId = ctx.Item.Id;
                }

                customer.ShoppingCartItems.AddRange(ctx.ChildItems);
                await _db.SaveChangesAsync();
            }
        }
Esempio n. 11
0
        public virtual async Task <bool> CopyAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var childItems = ctx.ChildItems;

            ctx.ChildItems = new();

            foreach (var childItem in childItems)
            {
                var childCtx = new AddToCartContext
                {
                    Customer             = ctx.Customer,
                    CartType             = ctx.CartType,
                    StoreId              = ctx.StoreId,
                    BundleItem           = childItem.BundleItem,
                    Product              = childItem.Product,
                    Quantity             = childItem.Quantity,
                    RawAttributes        = childItem.RawAttributes,
                    CustomerEnteredPrice = new(childItem.CustomerEnteredPrice, ctx.CustomerEnteredPrice.Currency),
                    ChildItems           = ctx.ChildItems
                };

                // TODO: (ms) (core) what about customer entered prices?

                if (!await AddToCartAsync(childCtx))
                {
                    ctx.Warnings.AddRange(childCtx.Warnings);
                }
            }

            if (ctx.Warnings.Any() || !await AddToCartAsync(ctx))
            {
                return(false);
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            return(!ctx.Warnings.Any());
        }
Esempio n. 12
0
        public virtual async Task <IList <string> > ValidateRequiredProductsAsync(AddToCartContext ctx, IEnumerable <OrganizedShoppingCartItem> cartItems)
        {
            Guard.NotNull(cartItems, nameof(cartItems));
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = new List <string>();

            if (!ctx.Product.RequireOtherProducts)
            {
                return(warnings);
            }

            var requiredProductIds = ctx.Product.ParseRequiredProductIds();

            if (!requiredProductIds.Any())
            {
                return(warnings);
            }

            var cartProductIds = cartItems.Select(x => x.Item.ProductId);

            var missingRequiredProductIds = requiredProductIds.Except(cartProductIds);

            if (!missingRequiredProductIds.Any())
            {
                return(warnings);
            }

            var missingRequiredProducts = await _db.Products.GetManyAsync(missingRequiredProductIds);

            foreach (var product in missingRequiredProducts)
            {
                warnings.Add(T("ShoppingCart.RequiredProductWarning", product.GetLocalized(x => x.Name)));
            }

            ctx.Warnings.AddRange(warnings);
            return(warnings);
        }
Esempio n. 13
0
        // TODO: (ms) (core) TESTING! Make sure it works in any case - Works for ReOrder().
        public virtual async Task <bool> AddToCartAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            // This is called when customer adds a product to cart
            ctx.Customer ??= _workContext.CurrentCustomer;
            ctx.StoreId ??= _storeContext.CurrentStore.Id;

            ctx.Customer.ResetCheckoutData(ctx.StoreId.Value);

            // Checks whether attributes have been selected
            if (ctx.VariantQuery != null)
            {
                // Create attribute selection from product attributes
                var attributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(ctx.Item.AttributeSelection);

                ctx.RawAttributes = ctx.Item.RawAttributes;

                // Check context for bundle item errors
                if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.RawAttributes.HasValue())
                {
                    ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes"));

                    if (ctx.BundleItem != null)
                    {
                        return(false);
                    }
                }
            }

            if (!await _cartValidator.ValidateAccessPermissionsAsync(ctx.Customer, ctx.CartType, ctx.Warnings))
            {
                return(false);
            }

            var cartItems = await GetCartItemsAsync(ctx.Customer, ctx.CartType, ctx.StoreId.Value);

            // Adds required products automatically if it is enabled
            if (ctx.AutomaticallyAddRequiredProductsIfEnabled)
            {
                var requiredProductIds = ctx.Product.ParseRequiredProductIds();
                if (requiredProductIds.Any())
                {
                    var cartProductIds            = cartItems.Select(x => x.Item.ProductId);
                    var missingRequiredProductIds = requiredProductIds.Except(cartProductIds);
                    var missingRequiredProducts   = await _db.Products.GetManyAsync(missingRequiredProductIds);

                    foreach (var product in missingRequiredProducts)
                    {
                        var item = new ShoppingCartItem
                        {
                            CustomerEnteredPrice = ctx.CustomerEnteredPrice.Amount,
                            RawAttributes        = ctx.AttributeSelection.AsJson(),
                            ShoppingCartType     = ctx.CartType,
                            StoreId      = ctx.StoreId.Value,
                            Quantity     = ctx.Quantity,
                            Customer     = ctx.Customer,
                            Product      = product,
                            ParentItemId = product.ParentGroupedProductId,
                            BundleItemId = ctx.BundleItem?.Id
                        };

                        await AddItemToCartAsync(new AddToCartContext
                        {
                            Item       = item,
                            ChildItems = ctx.ChildItems,
                            Customer   = ctx.Customer
                        });
                    }
                }
            }

            // Checks whether required products are still missing
            await _cartValidator.ValidateRequiredProductsAsync(ctx.Product, cartItems, ctx.Warnings);

            ShoppingCartItem existingCartItem = null;

            if (ctx.BundleItem == null)
            {
                existingCartItem = cartItems.FindItemInCart(ctx.CartType, ctx.Product, ctx.AttributeSelection, ctx.CustomerEnteredPrice)?.Item;
            }

            // Add item to cart (if no warnings accured)
            if (existingCartItem != null)
            {
                // Product is already in cart, find existing item
                var newQuantity = ctx.Quantity + existingCartItem.Quantity;

                if (!await _cartValidator.ValidateAddToCartItemAsync(ctx, existingCartItem, cartItems))
                {
                    return(false);
                }

                // Update cart item
                existingCartItem.Quantity      = newQuantity;
                existingCartItem.UpdatedOnUtc  = DateTime.UtcNow;
                existingCartItem.RawAttributes = ctx.AttributeSelection.AsJson();
                _db.TryUpdate(ctx.Customer);
                await _db.SaveChangesAsync();
            }
            else
            {
                if (!_cartValidator.ValidateItemsMaximumCartQuantity(ctx.CartType, cartItems.Count, ctx.Warnings))
                {
                    return(false);
                }

                // Product is not in cart yet, create new item
                var cartItem = new ShoppingCartItem
                {
                    CustomerEnteredPrice = ctx.CustomerEnteredPrice.Amount,
                    RawAttributes        = ctx.RawAttributes,
                    ShoppingCartType     = ctx.CartType,
                    StoreId      = ctx.StoreId.Value,
                    Quantity     = ctx.Quantity,
                    Customer     = ctx.Customer,
                    Product      = ctx.Product,
                    ProductId    = ctx.Product.Id,
                    ParentItemId = null,
                    BundleItemId = ctx.BundleItem?.Id
                };

                if (!await _cartValidator.ValidateAddToCartItemAsync(ctx, cartItem, cartItems))
                {
                    return(false);
                }

                // Check whether the product is part of a bundle, the bundle item or just any item.
                // If product is no child of bundle or no bundle at all
                if (ctx.BundleItem == null)
                {
                    // Set cart item as item for simple & bundle products, only if its not set by the caller
                    ctx.Item ??= cartItem;
                }
                else
                {
                    ctx.ChildItems.Add(cartItem);
                }
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            // If ctx.Product is a bundle product and the setting to automatically add bundle products is true, try to add all corresponding BundleItems.

            if (ctx.AutomaticallyAddBundleProductsIfEnabled &&
                ctx.Product.ProductType == ProductType.BundledProduct &&
                ctx.BundleItem == null &&
                ctx.Warnings.Count == 0)
            {
                var bundleItems = await _db.ProductBundleItem
                                  .Include(x => x.Product)
                                  .Include(x => x.BundleProduct)
                                  .ApplyBundledProductsFilter(new[] { ctx.Product.Id }, true)
                                  .ToListAsync();

                foreach (var bundleItem in bundleItems)
                {
                    var bundleItemContext = new AddToCartContext
                    {
                        Warnings             = new(),
                        Item                 = ctx.Item,
                        StoreId              = ctx.StoreId,
                        Customer             = ctx.Customer,
                        CartType             = ctx.CartType,
                        BundleItem           = bundleItem,
                        ChildItems           = ctx.ChildItems,
                        Product              = bundleItem.Product,
                        Quantity             = bundleItem.Quantity,
                        VariantQuery         = ctx.VariantQuery,
                        RawAttributes        = ctx.RawAttributes,
                        CustomerEnteredPrice = ctx.CustomerEnteredPrice,
                        AutomaticallyAddRequiredProductsIfEnabled = ctx.AutomaticallyAddRequiredProductsIfEnabled,
                    };

                    // If bundleItem could not be added to the shopping cart, remove child items
                    if (!await AddToCartAsync(bundleItemContext))
                    {
                        ctx.ChildItems.Clear();
                        break;
                    }
                }
            }

            // If context is no bundleItem, add item (parent) and its children (grouped product)
            if ((ctx.Product.ProductType == ProductType.SimpleProduct || ctx.AutomaticallyAddBundleProductsIfEnabled) &&
                ctx.BundleItem == null && ctx.Warnings.Count == 0)
            {
                await AddItemToCartAsync(ctx);
            }

            return(true);
        }
Esempio n. 14
0
        // was GetShoppingCartItemAttributeWarnings
        public virtual async Task <IList <string> > ValidateProductAttributesAsync(AddToCartContext ctx, IEnumerable <OrganizedShoppingCartItem> shoppingCart)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = new List <string>();

            // Customer cannot select anything cause bundles have no attributes
            if (ctx.Product.ProductType == ProductType.BundledProduct ||
                ctx.BundleItem != null && !ctx.BundleItem.BundleProduct.BundlePerItemPricing)
            {
                return(warnings);
            }

            // Get selected product variant attributes
            var selectedAttributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(ctx.AttributeSelection);

            foreach (var attribute in selectedAttributes)
            {
                if (attribute.Product == null || attribute.Product.Id != ctx.Product.Id)
                {
                    warnings.Add(T("ShoppingCart.AttributeError"));
                    ctx.Warnings.AddRange(warnings);
                    return(warnings);
                }
            }

            // Get existing product variant attributes
            foreach (var existingAttribute in ctx.Product.ProductVariantAttributes)
            {
                if (!existingAttribute.IsRequired)
                {
                    continue;
                }

                var found = false;
                // Selected product attributes
                foreach (var selectedAttribute in selectedAttributes)
                {
                    if (selectedAttribute.Id == existingAttribute.Id)
                    {
                        var values = ctx.AttributeSelection.GetAttributeValues(selectedAttribute.Id).Select(x => x.ToString()).ToList();
                        found = values.Find(x => x.HasValue()).HasValue();

                        if (found)
                        {
                            break;
                        }
                    }
                }

                // If attribute is filtered out by bundle item, it cannot be selected by the customer
                found = !found && (ctx.BundleItem?.FilterAttributes ?? false)
                    ? !ctx.BundleItem.AttributeFilters.Any(x => x.AttributeId == existingAttribute.ProductAttributeId)
                    : true;

                if (!found)
                {
                    warnings.Add(T(
                                     "ShoppingCart.SelectAttribute",
                                     existingAttribute.TextPrompt.IsEmpty()
                            ? existingAttribute.ProductAttribute.GetLocalized(x => x.Name)
                            : existingAttribute.GetLocalized(x => x.TextPrompt)
                                     ));
                }
            }

            if (warnings.Count > 0)
            {
                ctx.Warnings.AddRange(warnings);
                return(warnings);
            }

            // Checks whether there is an active selected attribute combination
            if (ctx.AttributeSelection.AttributesMap.Any())
            {
                var combination = await _productAttributeMaterializer.FindAttributeCombinationAsync(ctx.Product.Id, ctx.AttributeSelection);

                if (combination != null && !combination.IsActive)
                {
                    warnings.Add(T("ShoppingCart.NotAvailable"));
                }
            }

            var attributeValues = await _productAttributeMaterializer.MaterializeProductVariantAttributeValuesAsync(ctx.AttributeSelection);

            var linkedProductIds = attributeValues
                                   .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage)
                                   .Select(x => x.LinkedProductId)
                                   .Distinct();

            // Get products linked to attributes
            var linkedProducts = await _db.Products.GetManyAsync(linkedProductIds);

            // Filter products which could not be loaded
            var notFoundProductIds = linkedProductIds.Except(linkedProducts.Select(x => x.Id));

            foreach (var productId in notFoundProductIds)
            {
                warnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", productId));
            }

            // Validate each linkedProduct, create shopping cart item from linkedProduct and run validation
            foreach (var attributeValue in attributeValues)
            {
                var linkedProduct = linkedProducts.FirstOrDefault(x => x.Id == attributeValue.LinkedProductId);
                if (linkedProduct == null)
                {
                    warnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", attributeValue.LinkedProductId));
                    continue;
                }

                var newCtx = new AddToCartContext
                {
                    Product  = linkedProduct,
                    Customer = ctx.Customer,
                    CartType = ctx.CartType,
                    StoreId  = ctx.StoreId,
                    Quantity = ctx.Quantity * attributeValue.Quantity
                };

                var linkageWarnings = await ValidateCartItemAsync(newCtx, shoppingCart);

                foreach (var linkageWarning in linkageWarnings)
                {
                    warnings.Add(
                        T("ShoppingCart.ProductLinkageAttributeWarning",
                          attributeValue.ProductVariantAttribute.ProductAttribute.GetLocalized(x => x.Name),
                          attributeValue.GetLocalized(x => x.Name),
                          linkageWarning)
                        );
                }
            }

            ctx.Warnings.AddRange(warnings);
            return(warnings);
        }
Esempio n. 15
0
        // TODO: (ms) (core) AddToCartContext needs to have bundleItem and childItems already included correctly, they get just added...
        public virtual async Task <IList <string> > AddToCartAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            // This is called when customer adds a product to cart
            var warnings = new List <string>();

            ctx.Customer ??= _workContext.CurrentCustomer;
            ctx.StoreId ??= _storeContext.CurrentStore.Id;


            // TODO: (ms) (core) customerService.ResetCheckoutData() is missing
            //_customerService.ResetCheckoutData(customer, storeId);

            // Checks whether attributes have been selected
            // TODO: (ms) (core) VariantQuery is missing. Tries to get attributes of product.
            //if (ctx.VariantQuery != null)
            //{
            //    // Create attribute selection from  product attributes
            //    var attributes = _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(ctx.AttributeSelection);
            //    //ctx.RawAttributes = ctx.VariantQuery.CreateSelectedAttributesXml(ctx.Product.Id, ctx.BundleItemId, attributes, _productAttributeParser,
            //    //_localizationService, _downloadService, _catalogSettings, null, ctx.Warnings);

            //    // Check context for bundle item errors
            //    if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.RawAttributes.HasValue())
            //    {
            //        ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes"));

            //        if (ctx.BundleItem != null)
            //            return ctx.Warnings;
            //    }
            //}

            warnings.AddRange(await _cartValidator.ValidateAccessPermissionsAsync(ctx));
            if (warnings.Count > 0)
            {
                return(warnings);
            }


            // TODO: (ms) (core) Call ValidateRequiredProductsAsync and check product for required products, that are not included in cart
            // add them if neccessary and wanted
            //ctx.AutomaticallyAddRequiredProductsIfEnabled

            var shoppingCart = await GetCartItemsAsync(ctx.Customer, ctx.CartType, ctx.StoreId.Value);

            warnings.AddRange(await _cartValidator.ValidateRequiredProductsAsync(ctx, shoppingCart));
            //if(ctx.AutomaticallyAddRequiredProductsIfEnabled)
            // Add missing products.....

            OrganizedShoppingCartItem existingCartItem = null;

            if (ctx.BundleItem == null)
            {
                existingCartItem = shoppingCart.FindItemInCart(ctx.CartType, ctx.Product, ctx.AttributeSelection, ctx.CustomerEnteredPrice);
            }

            // Add item to cart (if no warnings accured)
            if (existingCartItem != null)
            {
                // Product is already in cart, find existing item
                ctx.Quantity += existingCartItem.Item.Quantity;

                warnings.AddRange(await _cartValidator.ValidateCartItemAsync(ctx, shoppingCart));
                if (warnings.Count > 0)
                {
                    return(warnings);
                }

                // Update cart item
                existingCartItem.Item.Quantity      = ctx.Quantity;
                existingCartItem.Item.UpdatedOnUtc  = DateTime.UtcNow;
                existingCartItem.Item.RawAttributes = ctx.RawAttributes;
                _db.TryUpdate(ctx.Customer);
                await _db.SaveChangesAsync();
            }
            else
            {
                warnings.AddRange(await _cartValidator.ValidateCartItemAsync(ctx, shoppingCart));
                if (warnings.Count > 0)
                {
                    return(warnings);
                }

                var warning = _cartValidator.ValidateCartItemsMaximum(ctx.CartType, shoppingCart.Count);
                if (warning.Count > 0)
                {
                    ctx.Warnings.AddRange(warning);
                    warnings.AddRange(warning);
                    return(warnings);
                }

                // Product is not in cart yet, create new item
                var cartItem = new ShoppingCartItem
                {
                    CustomerEnteredPrice = ctx.CustomerEnteredPrice,
                    RawAttributes        = ctx.RawAttributes,
                    ShoppingCartType     = ctx.CartType,
                    StoreId      = ctx.StoreId.Value,
                    Quantity     = ctx.Quantity,
                    Customer     = ctx.Customer,
                    Product      = ctx.Product,
                    ParentItemId = null,
                    BundleItemId = ctx.BundleItem?.Id
                };

                // If product is no bundle, add it as cartItem
                if (ctx.BundleItem == null)
                {
                    Debug.Assert(ctx.Item == null, "Add to cart item already specified");
                    ctx.Item = cartItem;
                }
                else
                {
                    ctx.ChildItems.Add(cartItem);
                }
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            // If ctx.Product is a bundle product, try adding all corresponding bundleItems
            if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.BundleItem == null && warnings.Count == 0)
            {
                var bundleItems = new List <ProductBundleItem>();

                // Get all bundle items and add each to the cart
                if (_db.IsCollectionLoaded(ctx.Product, x => x.ProductBundleItems))
                {
                    bundleItems = ctx.Product.ProductBundleItems.ToList();
                }
                else
                {
                    bundleItems = await _db.ProductBundleItem
                                  .ApplyBundledProductsFilter(new[] { ctx.Product.Id })
                                  .ToListAsync();
                }

                foreach (var bundleItem in bundleItems)
                {
                    // Try add each bundleItem to the cart
                    warnings.AddRange(
                        await AddToCartAsync(
                            new AddToCartContext
                    {
                        Item       = ctx.Item,
                        Customer   = ctx.Customer,
                        BundleItem = bundleItem,
                        Warnings   = ctx.Warnings,
                        CartType   = ctx.CartType,
                        StoreId    = ctx.StoreId.Value,
                        ChildItems = ctx.ChildItems,
                        Product    = bundleItem.Product,
                        Quantity   = bundleItem.Quantity,
                        //VariantQuery = ctx.VariantQuery,
                        AutomaticallyAddRequiredProductsIfEnabled = ctx.AutomaticallyAddRequiredProductsIfEnabled
                    })
                        );

                    // If bundleItem could not be added to the shopping cart, remove child items as they
                    if (warnings.Count > 0)
                    {
                        ctx.ChildItems.Clear();
                        break;
                    }
                }
            }

            // If context is no bundleItem, add item (parent) and its children (grouped product)
            if (ctx.BundleItem == null && warnings.Count == 0)
            {
                await AddItemToCartAsync(ctx);
            }

            return(warnings);
        }
        public virtual async Task <bool> ValidateProductAttributesAsync(ShoppingCartItem cartItem, IEnumerable <OrganizedShoppingCartItem> cartItems, IList <string> warnings)
        {
            Guard.NotNull(cartItem, nameof(cartItem));

            // Check if the product is a bundle. Since bundles have no attributes, the customer has nothing to select
            if (cartItem.Product.ProductType == ProductType.BundledProduct ||
                cartItem.BundleItem?.BundleProduct != null && !cartItem.BundleItem.BundleProduct.BundlePerItemPricing)
            {
                if (cartItem.RawAttributes.HasValue())
                {
                    warnings.Add(T("ShoppingCart.Bundle.NoAttributes"));
                }

                return(true);
            }

            // Get selected product variant attributes and check for product errors
            var selectedAttributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(cartItem.AttributeSelection);

            foreach (var attribute in selectedAttributes)
            {
                if (attribute.Product == null || attribute.Product.Id != cartItem.Product.Id)
                {
                    warnings.Add(T("ShoppingCart.AttributeError"));
                    return(false);
                }
            }

            await _db.LoadCollectionAsync(cartItem.Product, x => x.ProductVariantAttributes, false, q => q.Include(x => x.ProductAttribute));

            var currentWarnings = new List <string>();

            // Get existing product variant attributes
            foreach (var existingAttribute in cartItem.Product.ProductVariantAttributes)
            {
                if (!existingAttribute.IsRequired)
                {
                    continue;
                }

                var found = false;
                // Selected product attributes
                foreach (var selectedAttribute in selectedAttributes)
                {
                    if (selectedAttribute.Id == existingAttribute.Id)
                    {
                        var values = cartItem.AttributeSelection.GetAttributeValues(selectedAttribute.Id)
                                     .Select(x => x.ToString())
                                     .ToList();

                        found = values.Find(x => x.HasValue()).HasValue();

                        if (found)
                        {
                            break;
                        }
                    }
                }

                // If attribute is filtered out by bundle item, it cannot be selected by the customer
                if (!found &&
                    (cartItem.BundleItem?.FilterAttributes ?? false) &&
                    !cartItem.BundleItem.AttributeFilters.Any(x => x.AttributeId == existingAttribute.ProductAttributeId))
                {
                    found = true;
                }

                if (!found)
                {
                    currentWarnings.Add(T(
                                            "ShoppingCart.SelectAttribute",
                                            existingAttribute.TextPrompt.IsEmpty()
                            ? existingAttribute.ProductAttribute.GetLocalized(x => x.Name)
                            : existingAttribute.GetLocalized(x => x.TextPrompt)
                                            ));
                }
            }

            if (currentWarnings.Any())
            {
                warnings.AddRange(currentWarnings);
                return(false);
            }

            // Checks whether there is an active selected attribute combination
            if (cartItem.AttributeSelection.AttributesMap.Any())
            {
                var combination = await _productAttributeMaterializer.FindAttributeCombinationAsync(cartItem.Product.Id, cartItem.AttributeSelection);

                if (combination != null && !combination.IsActive)
                {
                    currentWarnings.Add(T("ShoppingCart.NotAvailable"));
                }
            }

            var attributeValues = await _productAttributeMaterializer.MaterializeProductVariantAttributeValuesAsync(cartItem.AttributeSelection);

            var linkedProductIds = attributeValues
                                   .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage)
                                   .Select(x => x.LinkedProductId)
                                   .Distinct();

            // Get products linked to attributes
            var linkedProducts = await _db.Products.GetManyAsync(linkedProductIds);

            // Filter products which could not be loaded
            var notFoundProductIds = linkedProductIds.Except(linkedProducts.Select(x => x.Id));

            foreach (var productId in notFoundProductIds)
            {
                currentWarnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", productId));
            }

            // Validate each linkedProduct, create shopping cart item from linkedProduct and run validation
            foreach (var linkedProductId in linkedProductIds)
            {
                var linkedProduct        = linkedProducts.FirstOrDefault(x => x.Id == linkedProductId);
                var linkedAttributeValue = attributeValues.FirstOrDefault(x => x.LinkedProductId == linkedProductId);

                if (linkedProduct == null || linkedAttributeValue == null)
                {
                    currentWarnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", linkedProductId));
                    continue;
                }

                var item = new ShoppingCartItem
                {
                    ProductId        = linkedProduct.Id,
                    Product          = linkedProduct,
                    ShoppingCartType = cartItem.ShoppingCartType,
                    Customer         = cartItem.Customer,
                    StoreId          = cartItem.StoreId,
                    Quantity         = cartItem.Quantity * linkedAttributeValue.Quantity
                };

                var ctx = new AddToCartContext
                {
                    Product  = linkedProduct,
                    Customer = cartItem.Customer,
                    CartType = cartItem.ShoppingCartType,
                    StoreId  = cartItem.StoreId,
                    Quantity = cartItem.Quantity * linkedAttributeValue.Quantity
                };

                // Get product linkage warnings
                await ValidateAddToCartItemAsync(ctx, item, cartItems);

                foreach (var linkageWarning in ctx.Warnings)
                {
                    currentWarnings.Add(
                        T("ShoppingCart.ProductLinkageAttributeWarning",
                          linkedAttributeValue.ProductVariantAttribute.ProductAttribute.GetLocalized(x => x.Name),
                          linkedAttributeValue.GetLocalized(x => x.Name),
                          linkageWarning)
                        );
                }
            }

            warnings.AddRange(currentWarnings);
            return(!currentWarnings.Any());
        }
Esempio n. 17
0
        public virtual async Task <IList <string> > ValidateProductAsync(AddToCartContext ctx, IEnumerable <OrganizedShoppingCartItem> shoppingCart)
        {
            Guard.NotNull(ctx, nameof(ctx));

            var warnings = new List <string>();
            var product  = ctx.Product;
            var cartType = ctx.CartType;

            if (product.Deleted)
            {
                warnings.Add(T("ShoppingCart.ProductDeleted"));
                ctx.Warnings.AddRange(warnings);
                return(ctx.Warnings);
            }

            // Grouped products are not available for order
            if (product.ProductType == ProductType.GroupedProduct)
            {
                warnings.Add(T("ShoppingCart.ProductNotAvailableForOrder"));
            }

            // Validate product bundle, no customer entered price allowed
            if (product.ProductType == ProductType.BundledProduct &&
                product.BundlePerItemPricing &&
                ctx.CustomerEnteredPrice != decimal.Zero)
            {
                warnings.Add(T("ShoppingCart.Bundle.NoCustomerEnteredPrice"));
            }

            // Not published or no permissions for customer or store
            if (!product.Published ||
                !await _aclService.AuthorizeAsync(product, ctx.Customer) ||
                !await _storeMappingService.AuthorizeAsync(product.Name, product.Id, ctx.StoreId ?? _storeContext.CurrentStore.Id))
            {
                warnings.Add(T("ShoppingCart.ProductUnpublished"));
            }

            // Disabled buy button
            if (cartType == ShoppingCartType.ShoppingCart && product.DisableBuyButton)
            {
                warnings.Add(T("ShoppingCart.BuyingDisabled"));
            }

            // Disabled wishlist button
            if (cartType == ShoppingCartType.Wishlist && product.DisableWishlistButton)
            {
                warnings.Add(T("ShoppingCart.WishlistDisabled"));
            }

            // Call for price
            if (cartType == ShoppingCartType.ShoppingCart && product.CallForPrice)
            {
                warnings.Add(T("Products.CallForPrice"));
            }

            // Customer entered price
            if (product.CustomerEntersPrice &&
                (ctx.CustomerEnteredPrice < product.MinimumCustomerEnteredPrice || ctx.CustomerEnteredPrice > product.MaximumCustomerEnteredPrice))
            {
                var minimum = _currencyService.ConvertFromPrimaryStoreCurrency(product.MinimumCustomerEnteredPrice, _workContext.WorkingCurrency);
                var maximum = _currencyService.ConvertFromPrimaryStoreCurrency(product.MaximumCustomerEnteredPrice, _workContext.WorkingCurrency);

                var moneyMin = _currencyService.CreateMoney(minimum, true, displayTax: false);
                var moneyMax = _currencyService.CreateMoney(maximum, true, displayTax: false);

                warnings.Add(T("ShoppingCart.CustomerEnteredPrice.RangeError", moneyMin.ToString(), moneyMax.ToString()));
            }

            // Quantity validation
            if (ctx.Quantity <= 0)
            {
                warnings.Add(T("ShoppingCart.QuantityShouldPositive"));
            }

            if (ctx.Quantity < product.OrderMinimumQuantity)
            {
                warnings.Add(T("ShoppingCart.MinimumQuantity", product.OrderMinimumQuantity));
            }

            if (ctx.Quantity > product.OrderMaximumQuantity)
            {
                warnings.Add(T("ShoppingCart.MaximumQuantity", product.OrderMaximumQuantity));
            }

            var allowedQuantities = product.ParseAllowedQuantities();

            if (allowedQuantities.Length > 0 && !allowedQuantities.Contains(ctx.Quantity))
            {
                warnings.Add(T("ShoppingCart.AllowedQuantities", string.Join(", ", allowedQuantities)));
            }

            // Stock validation
            var validateOutOfStock = ctx.CartType == ShoppingCartType.ShoppingCart || !_cartSettings.AllowOutOfStockItemsToBeAddedToWishlist;

            if (validateOutOfStock)
            {
                switch (product.ManageInventoryMethod)
                {
                case ManageInventoryMethod.ManageStock:
                {
                    if (product.BackorderMode != BackorderMode.NoBackorders || product.StockQuantity >= ctx.Quantity)
                    {
                        break;
                    }

                    var warning = product.StockQuantity > 0
                                ? T("ShoppingCart.QuantityExceedsStock", product.StockQuantity)
                                : T("ShoppingCart.OutOfStock");

                    warnings.Add(warning);
                }
                break;

                case ManageInventoryMethod.ManageStockByAttributes:
                {
                    var combination = await _productAttributeMaterializer.FindAttributeCombinationAsync(product.Id, ctx.AttributeSelection);

                    if (combination == null || combination.AllowOutOfStockOrders || combination.StockQuantity >= ctx.Quantity)
                    {
                        break;
                    }

                    var warning = combination.StockQuantity > 0
                                ? T("ShoppingCart.QuantityExceedsStock", combination.StockQuantity)
                                : T("ShoppingCart.OutOfStock");

                    warnings.Add(warning);
                }
                break;

                case ManageInventoryMethod.DontManageStock:
                default:
                    break;
                }
            }

            // Validate availability
            var availableStartDateError = false;

            if (ctx.Product.AvailableStartDateTimeUtc.HasValue)
            {
                var availableStartDate = DateTime.SpecifyKind(ctx.Product.AvailableStartDateTimeUtc.Value, DateTimeKind.Utc);
                if (availableStartDate.CompareTo(DateTime.UtcNow) > 0)
                {
                    warnings.Add(T("ShoppingCart.NotAvailable"));
                    availableStartDateError = true;
                }
            }

            if (ctx.Product.AvailableEndDateTimeUtc.HasValue && !availableStartDateError)
            {
                var availableEndDate = DateTime.SpecifyKind(ctx.Product.AvailableEndDateTimeUtc.Value, DateTimeKind.Utc);
                if (availableEndDate.CompareTo(DateTime.UtcNow) < 0)
                {
                    warnings.Add(T("ShoppingCart.NotAvailable"));
                }
            }

            ctx.Warnings.AddRange(warnings);
            return(warnings);
        }
        // TODO: (ms) (core) AddToCartContext needs to have bundleItem and childItems already included correctly, they get just added...
        public virtual async Task <IList <string> > AddToCartAsync(AddToCartContext ctx)
        {
            Guard.NotNull(ctx, nameof(ctx));

            // This is called when customer adds a product to cart
            var warnings = new List <string>();

            ctx.Customer ??= _workContext.CurrentCustomer;
            ctx.StoreId ??= _storeContext.CurrentStore.Id;

            ctx.Customer.ResetCheckoutData(ctx.StoreId.Value);

            // Checks whether attributes have been selected
            if (ctx.VariantQuery != null)
            {
                // Create attribute selection from  product attributes
                var attributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(ctx.Item.AttributeSelection);

                ctx.RawAttributes = ctx.Item.AttributeSelection.AsJson();

                // Check context for bundle item errors
                if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.RawAttributes.HasValue())
                {
                    ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes"));

                    if (ctx.BundleItem != null)
                    {
                        return(ctx.Warnings);
                    }
                }
            }

            warnings.AddRange(await _cartValidator.ValidateAccessPermissionsAsync(ctx));
            if (warnings.Count > 0)
            {
                return(warnings);
            }

            var shoppingCart = await GetCartItemsAsync(ctx.Customer, ctx.CartType, ctx.StoreId.Value);

            // Adds required products automatically if it is enabled
            if (ctx.AutomaticallyAddRequiredProductsIfEnabled)
            {
                var requiredProductIds = ctx.Product.ParseRequiredProductIds();
                if (requiredProductIds.Any())
                {
                    var cartProductIds            = shoppingCart.Select(x => x.Item.ProductId);
                    var missingRequiredProductIds = requiredProductIds.Except(cartProductIds);
                    var missingRequiredProducts   = await _db.Products.GetManyAsync(missingRequiredProductIds);

                    foreach (var product in missingRequiredProducts)
                    {
                        var item = new ShoppingCartItem
                        {
                            CustomerEnteredPrice = ctx.CustomerEnteredPrice,
                            RawAttributes        = ctx.AttributeSelection.AsJson(),
                            ShoppingCartType     = ctx.CartType,
                            StoreId      = ctx.StoreId.Value,
                            Quantity     = ctx.Quantity,
                            Customer     = ctx.Customer,
                            Product      = product,
                            ParentItemId = product.ParentGroupedProductId,
                            BundleItemId = ctx.BundleItem?.Id
                        };

                        await AddItemToCartAsync(new AddToCartContext
                        {
                            Item       = item,
                            ChildItems = ctx.ChildItems,
                            Customer   = ctx.Customer
                        });
                    }
                }
            }

            // Checks whether required products are still missing
            warnings.AddRange(await _cartValidator.ValidateRequiredProductsAsync(ctx, shoppingCart));

            OrganizedShoppingCartItem existingCartItem = null;

            if (ctx.BundleItem == null)
            {
                existingCartItem = shoppingCart.FindItemInCart(ctx.CartType, ctx.Product, ctx.AttributeSelection, ctx.CustomerEnteredPrice);
            }

            // Add item to cart (if no warnings accured)
            if (existingCartItem != null)
            {
                // Product is already in cart, find existing item
                ctx.Quantity += existingCartItem.Item.Quantity;

                warnings.AddRange(await _cartValidator.ValidateCartItemAsync(ctx, shoppingCart));
                if (warnings.Count > 0)
                {
                    return(warnings);
                }

                // Update cart item
                existingCartItem.Item.Quantity      = ctx.Quantity;
                existingCartItem.Item.UpdatedOnUtc  = DateTime.UtcNow;
                existingCartItem.Item.RawAttributes = ctx.AttributeSelection.AsJson();
                _db.TryUpdate(ctx.Customer);
                await _db.SaveChangesAsync();
            }
            else
            {
                warnings.AddRange(await _cartValidator.ValidateCartItemAsync(ctx, shoppingCart));
                if (warnings.Count > 0)
                {
                    return(warnings);
                }

                var warning = _cartValidator.ValidateCartItemsMaximum(ctx.CartType, shoppingCart.Count);
                if (warning.Count > 0)
                {
                    ctx.Warnings.AddRange(warning);
                    warnings.AddRange(warning);
                    return(warnings);
                }

                // Product is not in cart yet, create new item
                var cartItem = new ShoppingCartItem
                {
                    CustomerEnteredPrice = ctx.CustomerEnteredPrice,
                    RawAttributes        = ctx.AttributeSelection.AsJson(),
                    ShoppingCartType     = ctx.CartType,
                    StoreId      = ctx.StoreId.Value,
                    Quantity     = ctx.Quantity,
                    Customer     = ctx.Customer,
                    Product      = ctx.Product,
                    ParentItemId = null,
                    BundleItemId = ctx.BundleItem?.Id
                };

                // If product is no bundle, add it as cartItem
                if (ctx.BundleItem == null)
                {
                    Debug.Assert(ctx.Item == null, "Add to cart item already specified");
                    ctx.Item = cartItem;
                }
                else
                {
                    ctx.ChildItems.Add(cartItem);
                }
            }

            _requestCache.RemoveByPattern(CartItemsPatternKey);

            // If ctx.Product is a bundle product, try adding all corresponding bundleItems
            if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.BundleItem == null && warnings.Count == 0)
            {
                // Get all bundle items and add each to the cart
                var bundleItems = _db.IsCollectionLoaded(ctx.Product, x => x.ProductBundleItems)
                    ? ctx.Product.ProductBundleItems
                    : await _db.ProductBundleItem
                                  .ApplyBundledProductsFilter(new[] { ctx.Product.Id })
                                  .ToListAsync();

                foreach (var bundleItem in bundleItems)
                {
                    // Try add each bundleItem to the cart
                    warnings.AddRange(
                        await AddToCartAsync(
                            new AddToCartContext
                    {
                        Item         = ctx.Item,
                        Customer     = ctx.Customer,
                        BundleItem   = bundleItem,
                        Warnings     = ctx.Warnings,
                        CartType     = ctx.CartType,
                        StoreId      = ctx.StoreId.Value,
                        ChildItems   = ctx.ChildItems,
                        Product      = bundleItem.Product,
                        Quantity     = bundleItem.Quantity,
                        VariantQuery = ctx.VariantQuery,
                        AutomaticallyAddRequiredProductsIfEnabled = ctx.AutomaticallyAddRequiredProductsIfEnabled
                    })
                        );

                    // If bundleItem could not be added to the shopping cart, remove child items
                    if (warnings.Count > 0)
                    {
                        ctx.ChildItems.Clear();
                        break;
                    }
                }
            }

            // If context is no bundleItem, add item (parent) and its children (grouped product)
            if (ctx.BundleItem == null && warnings.Count == 0)
            {
                await AddItemToCartAsync(ctx);
            }

            return(warnings);
        }