// 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); }
// 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); }
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()); }
public virtual async Task <bool> ValidateProductAsync(ShoppingCartItem cartItem, IList <string> warnings, int?storeId = null, int?quantity = null) { Guard.NotNull(cartItem, nameof(cartItem)); Guard.NotNull(warnings, nameof(warnings)); var product = cartItem.Product; if (product == null) { warnings.Add(T("Products.NotFound", cartItem.ProductId)); return(false); } if (product.Deleted) { warnings.Add(T("ShoppingCart.ProductDeleted")); return(false); } var currentWarnings = new List <string>(); // Grouped products are not available for order if (product.ProductType == ProductType.GroupedProduct) { currentWarnings.Add(T("ShoppingCart.ProductNotAvailableForOrder")); } // Validate product bundle, no customer entered price allowed if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && cartItem.CustomerEnteredPrice != decimal.Zero) { currentWarnings.Add(T("ShoppingCart.Bundle.NoCustomerEnteredPrice")); } // Not published or no permissions for customer or store if (!product.Published || !await _aclService.AuthorizeAsync(product, cartItem.Customer) || !await _storeMappingService.AuthorizeAsync(product, storeId ?? _storeContext.CurrentStore.Id)) { currentWarnings.Add(T("ShoppingCart.ProductUnpublished")); } // Disabled buy button if (cartItem.ShoppingCartType == ShoppingCartType.ShoppingCart && product.DisableBuyButton) { currentWarnings.Add(T("ShoppingCart.BuyingDisabled")); } // Disabled wishlist button if (cartItem.ShoppingCartType == ShoppingCartType.Wishlist && product.DisableWishlistButton) { currentWarnings.Add(T("ShoppingCart.WishlistDisabled")); } // Call for price if (cartItem.ShoppingCartType == ShoppingCartType.ShoppingCart && product.CallForPrice) { currentWarnings.Add(T("Products.CallForPrice")); } // Customer entered price if (product.CustomerEntersPrice && (cartItem.CustomerEnteredPrice < product.MinimumCustomerEnteredPrice || cartItem.CustomerEnteredPrice > product.MaximumCustomerEnteredPrice)) { var min = _currencyService.ConvertToWorkingCurrency(product.MinimumCustomerEnteredPrice); var max = _currencyService.ConvertToWorkingCurrency(product.MaximumCustomerEnteredPrice); currentWarnings.Add(T("ShoppingCart.CustomerEnteredPrice.RangeError", min, max)); } var quanitityToValidate = quantity ?? cartItem.Quantity; // Quantity validation if (quanitityToValidate <= 0) { currentWarnings.Add(T("ShoppingCart.QuantityShouldPositive")); } if (quanitityToValidate < product.OrderMinimumQuantity) { currentWarnings.Add(T("ShoppingCart.MinimumQuantity", product.OrderMinimumQuantity)); } if (quanitityToValidate > product.OrderMaximumQuantity) { currentWarnings.Add(T("ShoppingCart.MaximumQuantity", product.OrderMaximumQuantity)); } var allowedQuantities = product.ParseAllowedQuantities(); if (allowedQuantities.Length > 0 && !allowedQuantities.Contains(quanitityToValidate)) { currentWarnings.Add(T("ShoppingCart.AllowedQuantities", string.Join(", ", allowedQuantities))); } // Stock validation var validateOutOfStock = cartItem.ShoppingCartType == ShoppingCartType.ShoppingCart || !_cartSettings.AllowOutOfStockItemsToBeAddedToWishlist; if (validateOutOfStock) { switch (product.ManageInventoryMethod) { case ManageInventoryMethod.ManageStock: { if (product.BackorderMode != BackorderMode.NoBackorders || product.StockQuantity >= quanitityToValidate) { break; } var warning = product.StockQuantity > 0 ? T("ShoppingCart.QuantityExceedsStock", product.StockQuantity) : T("ShoppingCart.OutOfStock"); currentWarnings.Add(warning); } break; case ManageInventoryMethod.ManageStockByAttributes: { var combination = await _productAttributeMaterializer.FindAttributeCombinationAsync(product.Id, cartItem.AttributeSelection); if (combination == null || combination.AllowOutOfStockOrders || combination.StockQuantity >= quanitityToValidate) { break; } var warning = combination.StockQuantity > 0 ? T("ShoppingCart.QuantityExceedsStock", combination.StockQuantity) : T("ShoppingCart.OutOfStock"); currentWarnings.Add(warning); } break; case ManageInventoryMethod.DontManageStock: default: break; } } // Validate availability var availableStartDateError = false; if (product.AvailableStartDateTimeUtc.HasValue) { var availableStartDate = DateTime.SpecifyKind(product.AvailableStartDateTimeUtc.Value, DateTimeKind.Utc); if (availableStartDate.CompareTo(DateTime.UtcNow) > 0) { currentWarnings.Add(T("ShoppingCart.NotAvailable")); availableStartDateError = true; } } if (product.AvailableEndDateTimeUtc.HasValue && !availableStartDateError) { var availableEndDate = DateTime.SpecifyKind(product.AvailableEndDateTimeUtc.Value, DateTimeKind.Utc); if (availableEndDate.CompareTo(DateTime.UtcNow) < 0) { currentWarnings.Add(T("ShoppingCart.NotAvailable")); } } warnings.AddRange(currentWarnings); return(!currentWarnings.Any()); }
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()); }
// 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); }
public OrganizedShoppingCartItem(ShoppingCartItem item) { Guard.NotNull(item, nameof(item)); Item = item; }