// 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 async Task <IActionResult> Index() { var storeId = Services.StoreContext.CurrentStore.Id; var customer = Services.WorkContext.CurrentCustomer; var cart = await _shoppingCartService.GetCartItemsAsync(storeId : storeId); if (!cart.Any()) { return(RedirectToRoute("ShoppingCart")); } if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) { return(new UnauthorizedResult()); } customer.ResetCheckoutData(storeId); // Validate checkout attributes. var checkoutAttributes = customer.GenericAttributes.CheckoutAttributes; var warnings = new List <string>(); var isValid = await _shoppingCartValidator.ValidateCartItemsAsync(cart, warnings, true, checkoutAttributes); if (!isValid) { NotifyWarning(string.Join(Environment.NewLine, warnings.Take(3))); return(RedirectToRoute("ShoppingCart")); } var validatingCartEvent = new ValidatingCartEvent(cart, warnings, customer); await Services.EventPublisher.PublishAsync(validatingCartEvent); if (validatingCartEvent.Result != null) { return(validatingCartEvent.Result); } if (warnings.Any()) { NotifyWarning(string.Join(Environment.NewLine, warnings.Take(3))); return(RedirectToRoute("ShoppingCart")); } // Validate each shopping cart item. foreach (var cartItem in cart) { var ctx = new AddToCartContext { StoreId = storeId, Product = cartItem.Item.Product, BundleItem = cartItem.Item.BundleItem, ChildItems = cartItem.ChildItems.Select(x => x.Item).ToList() }; isValid = await _shoppingCartValidator.ValidateAddToCartItemAsync(ctx, cartItem.Item, cart); if (!isValid) { warnings.AddRange(ctx.Warnings); NotifyWarning(string.Join(Environment.NewLine, warnings.Take(3))); return(RedirectToRoute("ShoppingCart")); } } await _db.SaveChangesAsync(); return(RedirectToAction(nameof(BillingAddress))); }