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); }
// 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 ShoppingCartLineItem(OrganizedShoppingCartItem item) { Guard.NotNull(item, nameof(item)); Item = item; }