public virtual Task <List <OrganizedShoppingCartItem> > GetCartItemsAsync( Customer customer = null, ShoppingCartType cartType = ShoppingCartType.ShoppingCart, int storeId = 0) { customer ??= _workContext.CurrentCustomer; var cacheKey = CartItemsKey.FormatInvariant(customer.Id, (int)cartType, storeId); var result = _requestCache.Get(cacheKey, async() => { var cartItems = new List <ShoppingCartItem>(); if (_db.IsCollectionLoaded(customer, x => x.ShoppingCartItems)) { var filteredCartItems = customer.ShoppingCartItems .Where(x => x.CustomerId == customer.Id && x.ShoppingCartTypeId == (int)cartType); if (storeId > 0) { filteredCartItems = cartItems.Where(x => x.StoreId == storeId); } cartItems = filteredCartItems.ToList(); } else { cartItems = await _db.ShoppingCartItems .Include(x => x.Product) .ThenInclude(x => x.ProductVariantAttributes) .ApplyStandardFilter(cartType, storeId, customer) .ToListAsync(); customer.ShoppingCartItems = cartItems; } // Prefetch all product variant attributes var allAttributes = new ProductVariantAttributeSelection(string.Empty); var allAttributeMaps = cartItems.SelectMany(x => x.AttributeSelection.AttributesMap); foreach (var attribute in allAttributeMaps) { if (allAttributes.AttributesMap.Contains(attribute)) { continue; } allAttributes.AddAttribute(attribute.Key, attribute.Value); } await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(allAttributes); return(await OrganizeCartItemsAsync(cartItems)); }); return(result); }
public virtual async Task <TaskExecutionInfo> GetLastExecutionInfoByTaskAsync(TaskDescriptor task, bool?runningOnly = null) { Guard.NotNull(task, nameof(task)); var query = _db.IsCollectionLoaded(task, x => x.ExecutionHistory) ? task.ExecutionHistory.AsQueryable() : GetExecutionInfoQuery().Include(x => x.Task).ApplyTaskFilter(task.Id); if (runningOnly.HasValue) { query = query.Where(x => x.IsRunning == runningOnly.Value); } query = query.ApplyCurrentMachineNameFilter(); return(await ExecuteWithRetry(() => query.FirstOrDefaultAsync())); }
// 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); }
protected virtual async Task <ICollection <Discount> > GetApplicableDiscounts(Product product, CalculatorContext context) { var result = new HashSet <Discount>(); var batchContext = context.Options.BatchContext; var customer = context.Options.Customer; // Check discounts assigned to the product. // We use the property "HasDiscountsApplied" for performance optimization to avoid unnecessary database calls. if (product.HasDiscountsApplied) { var collectionLoaded = _db.IsCollectionLoaded(product, x => x.AppliedDiscounts); var appliedDiscounts = collectionLoaded ? product.AppliedDiscounts : await batchContext.AppliedDiscounts.GetOrLoadAsync(product.Id); if (appliedDiscounts != null) { await TryAddDiscounts(appliedDiscounts, result, DiscountType.AssignedToSkus, context.Options); } } // Check discounts assigned to categories. var categoryDiscounts = await _discountService.GetAllDiscountsAsync(DiscountType.AssignedToCategories); if (categoryDiscounts.Any()) { var productCategories = await batchContext.ProductCategories.GetOrLoadAsync(product.Id); foreach (var productCategory in productCategories) { var category = productCategory.Category; if (category?.HasDiscountsApplied ?? false) { // INFO: AppliedDiscounts are eager loaded already. //await _db.LoadCollectionAsync(category, x => x.AppliedDiscounts); await TryAddDiscounts(category.AppliedDiscounts, result, DiscountType.AssignedToCategories, context.Options); } } } // Check discounts assigned to manufacturers. var manufacturerDiscounts = await _discountService.GetAllDiscountsAsync(DiscountType.AssignedToManufacturers); if (manufacturerDiscounts.Any()) { var productManufacturers = await batchContext.ProductManufacturers.GetOrLoadAsync(product.Id); foreach (var productManufacturer in productManufacturers) { var manu = productManufacturer.Manufacturer; if (manu?.HasDiscountsApplied ?? false) { // INFO: AppliedDiscounts are eager loaded already. //await _db.LoadCollectionAsync(manu, x => x.AppliedDiscounts); await TryAddDiscounts(manu.AppliedDiscounts, result, DiscountType.AssignedToManufacturers, context.Options); } } } 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); }