Exemplo n.º 1
0
        protected virtual async Task <IList <OrganizedShoppingCartItem> > OrganizeCartItemsAsync(ICollection <ShoppingCartItem> cart)
        {
            var result = new List <OrganizedShoppingCartItem>();

            if (cart.IsNullOrEmpty())
            {
                return(result);
            }

            var parents = cart.Where(x => x.ParentItemId is null);

            // TODO: (ms) (core) to reduce db roundtrips -> load and filter children by parents (id and so on) into lists and try to get from db as batch request

            foreach (var parent in parents)
            {
                var parentItem = new OrganizedShoppingCartItem(parent);

                var children = cart.Where(x => x.ParentItemId != null &&
                                          x.ParentItemId == parent.Id &&
                                          x.Id != parent.Id &&
                                          x.ShoppingCartTypeId == parent.ShoppingCartTypeId &&
                                          x.Product.CanBeBundleItem());

                // TODO: (ms) (core) Reduce database roundtrips in OrganizeCartItemsAsync
                foreach (var child in children)
                {
                    var childItem = new OrganizedShoppingCartItem(child);

                    if (child.RawAttributes.HasValue() &&
                        (parent.Product?.BundlePerItemPricing ?? false) &&
                        child.BundleItem != null)
                    {
                        var selection = new ProductVariantAttributeSelection(child.RawAttributes);

                        await _productAttributeMaterializer.MergeWithCombinationAsync(child.Product, selection);

                        var attributeValues = await _productAttributeMaterializer
                                              .MaterializeProductVariantAttributeValuesAsync(selection);

                        if (!attributeValues.IsNullOrEmpty())
                        {
                            childItem.BundleItemData.AdditionalCharge += attributeValues.Sum(x => x.PriceAdjustment);
                        }
                    }

                    parentItem.ChildItems.Add(childItem);
                }

                result.Add(parentItem);
            }

            return(result);
        }
        // TODO: (ms) (core) AddToCartContext needs to have bundleItem and childItems already included correctly, they get just added...TEST THIS EXTENSIVELY
        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.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(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);

            OrganizedShoppingCartItem existingCartItem = null;

            if (ctx.BundleItem == null)
            {
                existingCartItem = cartItems.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;

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

                // 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
            {
                if (!await _cartValidator.ValidateAddToCartItemAsync(ctx, cartItems))
                {
                    return(false);
                }

                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.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 && ctx.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)
                {
                    var tempCtx = 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.AttributeSelection.AsJson(),
                        CustomerEnteredPrice = ctx.CustomerEnteredPrice,
                        AutomaticallyAddRequiredProductsIfEnabled = ctx.AutomaticallyAddRequiredProductsIfEnabled,
                    };

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

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

            return(true);
        }
Exemplo n.º 3
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);
        }
Exemplo n.º 4
0
        public ShoppingCartLineItem(OrganizedShoppingCartItem item)
        {
            Guard.NotNull(item, nameof(item));

            Item = item;
        }