public virtual decimal GetFinalPrice( Product product, IEnumerable <ProductBundleItemData> bundleItems, Customer customer, decimal additionalCharge, bool includeDiscounts, int quantity, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { var result = decimal.Zero; var items = bundleItems; if (items == null) { items = context == null ? _productService.GetBundleItems(product.Id) : context.ProductBundleItems.GetOrLoad(product.Id).Select(x => new ProductBundleItemData(x)).ToList(); } foreach (var itemData in items.Where(x => x != null && x.Item != null)) { var itemPrice = GetFinalPrice(itemData.Item.Product, customer, itemData.AdditionalCharge, includeDiscounts, 1, itemData, context); result += decimal.Multiply(itemPrice, itemData.Item.Quantity); } return(result < decimal.Zero ? decimal.Zero : result); } return(GetFinalPrice(product, customer, additionalCharge, includeDiscounts, quantity, bundleItem, context)); }
public static ProductBundleItemOrderData ToOrderData(this ProductBundleItemData bundleItemData, decimal priceWithDiscount = decimal.Zero, string attributesXml = null, string attributesInfo = null) { if (!bundleItemData.IsValid()) { return(null); } var item = bundleItemData.Item; string bundleItemName = item.GetLocalized(x => x.Name); var bundleData = new ProductBundleItemOrderData() { BundleItemId = item.Id, ProductId = item.ProductId, Sku = item.Product.Sku, ProductName = (bundleItemName ?? item.Product.GetLocalized(x => x.Name)), ProductSeName = item.Product.GetSeName(), VisibleIndividually = item.Product.VisibleIndividually, Quantity = item.Quantity, DisplayOrder = item.DisplayOrder, PriceWithDiscount = priceWithDiscount, AttributesXml = attributesXml, AttributesInfo = attributesInfo, PerItemShoppingCart = item.BundleProduct.BundlePerItemShoppingCart }; return(bundleData); }
public virtual async Task <(decimal Amount, Discount AppliedDiscount)> GetDiscountAmountAsync( Product product, Customer customer = null, decimal additionalCharge = decimal.Zero, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null, decimal?finalPrice = null) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; var discountAmount = decimal.Zero; Discount appliedDiscount = null; if (bundleItem != null && bundleItem.Item != null) { var bi = bundleItem.Item; if (bi.Discount.HasValue && bi.BundleProduct.BundlePerItemPricing) { appliedDiscount = new Discount { UsePercentage = bi.DiscountPercentage, DiscountPercentage = bi.Discount.Value, DiscountAmount = bi.Discount.Value }; var finalPriceWithoutDiscount = finalPrice ?? await GetFinalPriceAsync(product, customer, additionalCharge, false, quantity, bundleItem, context); discountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } else { // Don't apply when customer entered price or discounts should be ignored in any case. if (!product.CustomerEntersPrice && _catalogSettings.IgnoreDiscounts) { return(discountAmount, appliedDiscount); } var allowedDiscounts = await GetAllowedDiscountsAsync(product, customer, context); if (!allowedDiscounts.Any()) { return(discountAmount, appliedDiscount); } var finalPriceWithoutDiscount = finalPrice ?? await GetFinalPriceAsync(product, customer, additionalCharge, false, quantity, bundleItem, context); appliedDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); if (appliedDiscount != null) { discountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } return(discountAmount, appliedDiscount); }
public virtual decimal GetFinalPrice( Product product, IEnumerable <ProductBundleItemData> bundleItems, Customer customer, decimal additionalCharge, bool includeDiscounts, int quantity, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { decimal result = decimal.Zero; var items = bundleItems ?? _productService.GetBundleItems(product.Id); foreach (var itemData in items.Where(x => x.IsValid())) { decimal itemPrice = GetFinalPrice(itemData.Item.Product, customer, itemData.AdditionalCharge, includeDiscounts, 1, itemData, context); result = result + decimal.Multiply(itemPrice, itemData.Item.Quantity); } return(result < decimal.Zero ? decimal.Zero : result); } return(GetFinalPrice(product, customer, additionalCharge, includeDiscounts, quantity, bundleItem, context)); }
public OrganizedShoppingCartItem(ShoppingCartItem item) { Guard.NotNull(item, nameof(item)); Item = item; BundleItemData = new ProductBundleItemData(item.BundleItem); }
public virtual decimal GetFinalPrice( Product product, Customer customer, decimal additionalCharge, bool includeDiscounts, int quantity, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null, bool isTierPrice = false) { // Initial price. decimal result = product.Price; // Special price. var specialPrice = GetSpecialPrice(product); if (specialPrice.HasValue) { result = specialPrice.Value; } if (isTierPrice) { includeDiscounts = true; } // Tier prices. if (product.HasTierPrices && includeDiscounts && !(bundleItem != null && bundleItem.Item != null)) { var tierPrice = GetMinimumTierPrice(product, customer, quantity, context); var discountAmountTest = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscountTest, bundleItem); var discountProductTest = result - discountAmountTest; //decimal? tierPrice = GetMinimumTierPrice(product, customer, quantity); if (tierPrice.HasValue && tierPrice < discountProductTest) { includeDiscounts = false; result = Math.Min(result, tierPrice.Value); } } // Discount + additional charge. if (includeDiscounts) { var discountAmount = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscount, bundleItem, context); result = result + additionalCharge - discountAmount; } else { result = result + additionalCharge; } if (result < decimal.Zero) { result = decimal.Zero; } return(result); }
public OrganizedShoppingCartItem(ShoppingCartItem item) { Guard.NotNull(item, nameof(item)); Item = item; // must not be null ChildItems = new List <OrganizedShoppingCartItem>(); BundleItemData = new ProductBundleItemData(item.BundleItem); CustomProperties = new Dictionary <string, object>(); }
public static void ToOrderData(this ProductBundleItemData bundleItemData, IList <ProductBundleItemOrderData> bundleData, decimal priceWithDiscount = decimal.Zero, string attributesXml = null, string attributesInfo = null) { var item = bundleItemData.ToOrderData(priceWithDiscount, attributesXml, attributesInfo); if (item != null && item.ProductId != 0 && item.BundleItemId != 0) { bundleData.Add(item); } }
public static bool FilterOut(this ProductBundleItemData bundleItemData, ProductVariantAttributeValue value, out ProductBundleItemAttributeFilter filter) { if (bundleItemData.IsValid() && value != null && bundleItemData.Item.FilterAttributes) { filter = bundleItemData.Item.AttributeFilters.FirstOrDefault(x => x.AttributeId == value.ProductVariantAttributeId && x.AttributeValueId == value.Id); return(filter == null); } filter = null; return(false); }
public OrganizedShoppingCartItem(ShoppingCartItem item) { if (item == null) { throw new ArgumentNullException("item"); } Item = item; // must not be null ChildItems = new List <OrganizedShoppingCartItem>(); BundleItemData = new ProductBundleItemData(item.BundleItem); }
public virtual decimal GetDiscountAmount( Product product, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { appliedDiscount = null; decimal appliedDiscountAmount = decimal.Zero; decimal finalPriceWithoutDiscount = decimal.Zero; if (bundleItem.IsValid()) { if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) { appliedDiscount = new Discount { UsePercentage = bundleItem.Item.DiscountPercentage, DiscountPercentage = bundleItem.Item.Discount.Value, DiscountAmount = bundleItem.Item.Discount.Value }; finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } else { // dont't apply when customer entered price or discounts should be ignored completely if (product.CustomerEntersPrice || _catalogSettings.IgnoreDiscounts) { return(appliedDiscountAmount); } var allowedDiscounts = GetAllowedDiscounts(product, customer, context); if (allowedDiscounts.Count == 0) { return(appliedDiscountAmount); } finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); appliedDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); if (appliedDiscount != null) { appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } return(appliedDiscountAmount); }
public virtual async Task <decimal> GetFinalPriceAsync( Product product, IEnumerable <ProductBundleItemData> bundleItems, Customer customer = null, decimal additionalCharge = decimal.Zero, bool includeDiscounts = true, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { var result = decimal.Zero; var items = bundleItems; if (items == null) { IEnumerable <ProductBundleItem> loadedBundleItems; if (context == null) { loadedBundleItems = await _db.ProductBundleItem .AsNoTracking() .Include(x => x.Product) .ApplyBundledProductsFilter(new int[] { product.Id }) .ToListAsync(); } else { loadedBundleItems = await context.ProductBundleItems.GetOrLoadAsync(product.Id); } items = loadedBundleItems.Select(x => new ProductBundleItemData(x)).ToList(); } foreach (var itemData in items.Where(x => x?.Item != null)) { var itemPrice = await GetFinalPriceAsync(itemData.Item.Product, customer, itemData.AdditionalCharge, includeDiscounts, 1, itemData, context); result += decimal.Multiply(itemPrice, itemData.Item.Quantity); } return(result < decimal.Zero ? decimal.Zero : result); } return(await GetFinalPriceAsync(product, customer, additionalCharge, includeDiscounts, quantity, bundleItem, context)); }
public virtual decimal GetFinalPrice( Product product, Customer customer, decimal additionalCharge, bool includeDiscounts, int quantity, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { //initial price decimal result = product.Price; //special price var specialPrice = GetSpecialPrice(product); if (specialPrice.HasValue) { result = specialPrice.Value; } //tier prices if (product.HasTierPrices && !bundleItem.IsValid()) { decimal?tierPrice = GetMinimumTierPrice(product, customer, quantity, context); if (tierPrice.HasValue) { result = Math.Min(result, tierPrice.Value); } } //discount + additional charge if (includeDiscounts) { Discount appliedDiscount = null; decimal discountAmount = GetDiscountAmount(product, customer, additionalCharge, quantity, out appliedDiscount, bundleItem, context); result = result + additionalCharge - discountAmount; } else { result = result + additionalCharge; } if (result < decimal.Zero) { result = decimal.Zero; } return(result); }
public virtual async Task <Money> GetFinalPriceAsync( Product product, IEnumerable <ProductBundleItemData> bundleItems, Money?additionalCharge, Customer customer = null, bool includeDiscounts = true, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { return(new(await GetFinalPriceAmountAsync( product, bundleItems, additionalCharge?.Amount ?? decimal.Zero, customer, includeDiscounts, quantity, bundleItem, context), _primaryCurrency)); }
public virtual async Task <(Money Amount, Discount AppliedDiscount)> GetDiscountAmountAsync( Product product, Money?additionalCharge, Customer customer = null, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null, Money?finalPrice = null) { var(discountAmount, appliedDiscount) = await GetDiscountAmountAsync( product, additionalCharge?.Amount ?? decimal.Zero, customer, quantity, bundleItem, context, finalPrice?.Amount); return(new(discountAmount, _primaryCurrency), appliedDiscount); }
/// <summary> /// Gets discount amount /// </summary> /// <param name="product">Product</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="quantity">Product quantity</param> /// <param name="appliedDiscount">Applied discount</param> /// <param name="bundleItem">A product bundle item</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(Product product, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount, ProductBundleItemData bundleItem = null) { appliedDiscount = null; decimal appliedDiscountAmount = decimal.Zero; if (bundleItem.IsValid()) { if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) { appliedDiscount = new Discount() { UsePercentage = bundleItem.Item.DiscountPercentage, DiscountPercentage = bundleItem.Item.Discount.Value, DiscountAmount = bundleItem.Item.Discount.Value }; } } else { //we don't apply discounts to products with price entered by a customer if (product.CustomerEntersPrice) { return(appliedDiscountAmount); } appliedDiscount = GetPreferredDiscount(product, customer, additionalCharge, quantity); } if (appliedDiscount != null) { decimal finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem); appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } return(appliedDiscountAmount); }
public ActionResult UpdateProductDetails(int productId, string itemType, int bundleItemId, ProductVariantQuery query, FormCollection form) { int quantity = 1; int galleryStartIndex = -1; string galleryHtml = null; string dynamicThumbUrl = null; bool isAssociated = itemType.IsCaseInsensitiveEqual("associateditem"); var pictureModel = new ProductDetailsPictureModel(); var m = new ProductDetailsModel(); var product = _productService.GetProductById(productId); var bItem = _productService.GetBundleItemById(bundleItemId); IList <ProductBundleItemData> bundleItems = null; ProductBundleItemData bundleItem = (bItem == null ? null : new ProductBundleItemData(bItem)); var warnings = new List <string>(); var attributes = _productAttributeService.GetProductVariantAttributesByProductId(productId); var attributeXml = query.CreateSelectedAttributesXml(productId, 0, attributes, _productAttributeParser, _localizationService, _downloadService, _catalogSettings, this.Request, warnings); var areAllAttributesForCombinationSelected = _shoppingCartService.AreAllAttributesForCombinationSelected(attributeXml, product); // quantity required for tier prices string quantityKey = form.AllKeys.FirstOrDefault(k => k.EndsWith("EnteredQuantity")); if (quantityKey.HasValue()) { int.TryParse(form[quantityKey], out quantity); } if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { bundleItems = _productService.GetBundleItems(product.Id); if (query.Variants.Count > 0) { // may add elements to query object if they are preselected by bundle item filter foreach (var itemData in bundleItems) { _helper.PrepareProductDetailsPageModel(itemData.Item.Product, query, false, itemData, null); } } } // get merged model data _helper.PrepareProductDetailModel(m, product, query, isAssociated, bundleItem, bundleItems, quantity); if (bundleItem != null) // update bundle item thumbnail { if (!bundleItem.Item.HideThumbnail) { var picture = m.GetAssignedPicture(_pictureService, null, bundleItem.Item.ProductId); dynamicThumbUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.BundledProductPictureSize, false); } } else if (isAssociated) // update associated product thumbnail { var picture = m.GetAssignedPicture(_pictureService, null, productId); dynamicThumbUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.AssociatedProductPictureSize, false); } else if (product.ProductType != ProductType.BundledProduct) // update image gallery { var pictures = _pictureService.GetPicturesByProductId(productId); if (pictures.Count <= _catalogSettings.DisplayAllImagesNumber) // all pictures rendered... only index is required { var picture = m.GetAssignedPicture(_pictureService, pictures); galleryStartIndex = (picture == null ? 0 : pictures.IndexOf(picture)); } else { var allCombinationPictureIds = _productAttributeService.GetAllProductVariantAttributeCombinationPictureIds(product.Id); _helper.PrepareProductDetailsPictureModel( pictureModel, pictures, product.GetLocalized(x => x.Name), allCombinationPictureIds, false, bundleItem, m.SelectedCombination); galleryStartIndex = pictureModel.GalleryStartIndex; galleryHtml = this.RenderPartialViewToString("Product.Picture", pictureModel); } } object partials = null; if (m.IsBundlePart) { partials = new { BundleItemPrice = this.RenderPartialViewToString("Product.Offer.Price", m), BundleItemStock = this.RenderPartialViewToString("Product.StockInfo", m) }; } else { partials = new { Attrs = this.RenderPartialViewToString("Product.Attrs", m), Price = this.RenderPartialViewToString("Product.Offer.Price", m), Stock = this.RenderPartialViewToString("Product.StockInfo", m), BundlePrice = product.ProductType == ProductType.BundledProduct ? this.RenderPartialViewToString("Product.Bundle.Price", m) : (string)null }; } object data = new { Partials = partials, DynamicThumblUrl = dynamicThumbUrl, GalleryStartIndex = galleryStartIndex, GalleryHtml = galleryHtml }; return(new JsonResult { Data = data }); }
public ActionResult UpdateProductDetails(int productId, string itemType, int bundleItemId, FormCollection form) { int quantity = 1; int galleryStartIndex = -1; string galleryHtml = null; string dynamicThumbUrl = null; bool isAssociated = itemType.IsCaseInsensitiveEqual("associateditem"); var pictureModel = new ProductDetailsPictureModel(); var m = new ProductDetailsModel(); var product = _productService.GetProductById(productId); var bItem = _productService.GetBundleItemById(bundleItemId); IList <ProductBundleItemData> bundleItems = null; ProductBundleItemData bundleItem = (bItem == null ? null : new ProductBundleItemData(bItem)); // quantity required for tier prices string quantityKey = form.AllKeys.FirstOrDefault(k => k.EndsWith("EnteredQuantity")); if (quantityKey.HasValue()) { int.TryParse(form[quantityKey], out quantity); } if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { bundleItems = _productService.GetBundleItems(product.Id); if (form.Count > 0) { foreach (var itemData in bundleItems) { var tempModel = _helper.PrepareProductDetailsPageModel(itemData.Item.Product, false, itemData, null, form); } } } // get merged model data _helper.PrepareProductDetailModel(m, product, isAssociated, bundleItem, bundleItems, form, quantity); if (bundleItem != null) // update bundle item thumbnail { if (!bundleItem.Item.HideThumbnail) { var picture = m.GetAssignedPicture(_pictureService, null, bundleItem.Item.ProductId); dynamicThumbUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.BundledProductPictureSize, false); } } else if (isAssociated) // update associated product thumbnail { var picture = m.GetAssignedPicture(_pictureService, null, productId); dynamicThumbUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.AssociatedProductPictureSize, false); } else if (product.ProductType != ProductType.BundledProduct) // update image gallery { var pictures = _pictureService.GetPicturesByProductId(productId); if (pictures.Count <= _catalogSettings.DisplayAllImagesNumber) // all pictures rendered... only index is required { var picture = m.GetAssignedPicture(_pictureService, pictures); galleryStartIndex = (picture == null ? 0 : pictures.IndexOf(picture)); } else { var allCombinationImageIds = new List <int>(); _productAttributeService .GetAllProductVariantAttributeCombinations(product.Id) .GetAllCombinationImageIds(allCombinationImageIds); _helper.PrepareProductDetailsPictureModel(pictureModel, pictures, product.GetLocalized(x => x.Name), allCombinationImageIds, false, bundleItem, m.CombinationSelected); galleryStartIndex = pictureModel.GalleryStartIndex; galleryHtml = this.RenderPartialViewToString("_PictureGallery", pictureModel); } } #region data object object data = new { Delivery = new { Id = 0, Name = m.DeliveryTimeName, Color = m.DeliveryTimeHexValue, DisplayAccordingToStock = m.DisplayDeliveryTimeAccordingToStock }, Measure = new { Weight = new { Value = m.WeightValue, Text = m.Weight }, Height = new { Value = product.Height, Text = m.Height }, Width = new { Value = product.Width, Text = m.Width }, Length = new { Value = product.Length, Text = m.Length } }, Number = new { Sku = new { Value = m.Sku, Show = m.ShowSku }, Gtin = new { Value = m.Gtin, Show = m.ShowGtin }, Mpn = new { Value = m.ManufacturerPartNumber, Show = m.ShowManufacturerPartNumber } }, Price = new { Base = new { Enabled = m.IsBasePriceEnabled, Info = m.BasePriceInfo }, Old = new { Value = decimal.Zero, Text = m.ProductPrice.OldPrice }, WithoutDiscount = new { Value = m.ProductPrice.PriceValue, Text = m.ProductPrice.Price }, WithDiscount = new { Value = m.ProductPrice.PriceWithDiscountValue, Text = m.ProductPrice.PriceWithDiscount } }, Stock = new { Quantity = new { Value = product.StockQuantity, Show = product.DisplayStockQuantity }, Availability = new { Text = m.StockAvailability, Show = product.DisplayStockAvailability, Available = m.IsAvailable } }, DynamicThumblUrl = dynamicThumbUrl, GalleryStartIndex = galleryStartIndex, GalleryHtml = galleryHtml }; #endregion return(new JsonResult { Data = data }); }
protected virtual decimal GetPreselectedPrice(Product product, PriceCalculationContext context, ProductBundleItemData bundleItem, IEnumerable <ProductBundleItemData> bundleItems) { var taxRate = decimal.Zero; var attributesTotalPriceBase = decimal.Zero; var preSelectedPriceAdjustmentBase = decimal.Zero; var isBundle = (product.ProductType == ProductType.BundledProduct); var isBundleItemPricing = (bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing); var isBundlePricing = (bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing); var bundleItemId = (bundleItem == null ? 0 : bundleItem.Item.Id); var selectedAttributes = new NameValueCollection(); var selectedAttributeValues = new List <ProductVariantAttributeValue>(); var attributes = context.Attributes.Load(product.Id); // 1. fill selectedAttributes with initially selected attributes foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Count > 0 && x.ShouldHaveValues())) { int preSelectedValueId = 0; ProductVariantAttributeValue defaultValue = null; var selectedValueIds = new List <int>(); var pvaValues = attribute.ProductVariantAttributeValues; foreach (var pvaValue in pvaValues) { ProductBundleItemAttributeFilter attributeFilter = null; if (bundleItem.FilterOut(pvaValue, out attributeFilter)) { continue; } if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) { preSelectedValueId = attributeFilter.AttributeValueId; } if (!isBundlePricing && pvaValue.IsPreSelected) { decimal attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue); decimal priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, out taxRate); preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase); } } // value pre-selected by a bundle item filter discards the default pre-selection if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) { //defaultValue.IsPreSelected = true; selectedAttributeValues.Add(defaultValue); selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, defaultValue.Id, product.Id, bundleItemId); } else { foreach (var value in pvaValues.Where(x => x.IsPreSelected)) { selectedAttributeValues.Add(value); selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, value.Id, product.Id, bundleItemId); } } } // 2. find attribute combination for selected attributes and merge it if (!isBundle && selectedAttributes.Count > 0) { var attributeXml = selectedAttributes.CreateSelectedAttributesXml(product.Id, attributes, _productAttributeParser, _services.Localization, _downloadService, _catalogSettings, _httpRequestBase, new List <string>(), true, bundleItemId); var combinations = context.AttributeCombinations.Load(product.Id); var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml)); if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue) { product.MergedDataValues = new Dictionary <string, object> { { "Price", selectedCombination.Price.Value } }; } } if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) { if (selectedAttributeValues.Count > 0) { selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x)); } else { attributesTotalPriceBase = preSelectedPriceAdjustmentBase; } } if (bundleItem != null) { bundleItem.AdditionalCharge = attributesTotalPriceBase; } var result = GetFinalPrice(product, bundleItems, _services.WorkContext.CurrentCustomer, attributesTotalPriceBase, true, 1, bundleItem, context); return(result); }
public virtual async Task <Money> GetFinalPriceAsync( Product product, Money?additionalCharge, Customer customer = null, bool includeDiscounts = true, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null, bool isTierPrice = false) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; if (isTierPrice) { includeDiscounts = true; } var currency = _workContext.WorkingCurrency; var specialPrice = GetSpecialPrice(product); var result = specialPrice.HasValue ? specialPrice.Value.Amount : product.Price; // Tier price. if (product.HasTierPrices && includeDiscounts && !(bundleItem != null && bundleItem.Item != null)) { var tierPrice = await GetMinimumTierPriceAsync(product, customer, quantity, context); if (tierPrice.HasValue) { if (_catalogSettings.ApplyPercentageDiscountOnTierPrice && !isTierPrice) { var(discountOnTierPrice, appliedDiscount) = await GetDiscountAmountAsync(product, null, customer, quantity, bundleItem, context, tierPrice.Value); if (appliedDiscount != null && appliedDiscount.UsePercentage) { result = Math.Min(result, tierPrice.Value.Amount) + (additionalCharge?.Amount ?? decimal.Zero) - discountOnTierPrice.Amount; return(currency.AsMoney(result, false, true)); } } var(discountAmountTest, _) = await GetDiscountAmountAsync(product, additionalCharge, customer, quantity, bundleItem); var discountProductTest = result - discountAmountTest.Amount; if (tierPrice < discountProductTest) { includeDiscounts = false; result = Math.Min(result, tierPrice.Value.Amount); } } } // Discount + additional charge. result += additionalCharge?.Amount ?? decimal.Zero; if (includeDiscounts) { var(discountAmount, _) = await GetDiscountAmountAsync(product, additionalCharge, customer, quantity, bundleItem, context); result -= discountAmount.Amount; } return(currency.AsMoney(result, false, true)); }
public static bool IsValid(this ProductBundleItemData bundleItemData) { return(bundleItemData != null && bundleItemData.Item != null); }
protected virtual decimal GetPreselectedPrice(Product product, ProductBundleItemData bundleItem, IList <ProductBundleItemData> bundleItems) { var taxRate = decimal.Zero; var attributesTotalPriceBase = decimal.Zero; var preSelectedPriceAdjustmentBase = decimal.Zero; var isBundle = (product.ProductType == ProductType.BundledProduct); var isBundleItemPricing = (bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing); var isBundlePricing = (bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing); var bundleItemId = (bundleItem == null ? 0 : bundleItem.Item.Id); var attributes = (isBundle ? new List <ProductVariantAttribute>() : _productAttributeService.GetProductVariantAttributesByProductId(product.Id)); var selectedAttributes = new NameValueCollection(); List <ProductVariantAttributeValue> selectedAttributeValues = null; foreach (var attribute in attributes) { int preSelectedValueId = 0; ProductVariantAttributeValue defaultValue = null; if (attribute.ShouldHaveValues()) { var pvaValues = _productAttributeService.GetProductVariantAttributeValues(attribute.Id); if (pvaValues.Count == 0) { continue; } foreach (var pvaValue in pvaValues) { ProductBundleItemAttributeFilter attributeFilter = null; if (bundleItem.FilterOut(pvaValue, out attributeFilter)) { continue; } if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) { preSelectedValueId = attributeFilter.AttributeValueId; } if (!isBundlePricing && pvaValue.IsPreSelected) { decimal attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue); decimal priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, out taxRate); preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase); } } // value pre-selected by a bundle item filter discards the default pre-selection if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) { defaultValue.IsPreSelected = true; } if (defaultValue == null) { defaultValue = pvaValues.FirstOrDefault(x => x.IsPreSelected); } if (defaultValue == null && attribute.IsRequired) { defaultValue = pvaValues.First(); } if (defaultValue != null) { selectedAttributes.AddProductAttribute(attribute.ProductAttributeId, attribute.Id, defaultValue.Id, product.Id, bundleItemId); } } } if (!isBundle && selectedAttributes.Count > 0) { string attributeXml = selectedAttributes.CreateSelectedAttributesXml(product.Id, attributes, _productAttributeParser, _commonServices.Localization, _downloadService, _catalogSettings, _httpRequestBase, new List <string>(), true, bundleItemId); selectedAttributeValues = _productAttributeParser.ParseProductVariantAttributeValues(attributeXml).ToList(); var combinations = _productAttributeService.GetAllProductVariantAttributeCombinations(product.Id); var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml)); if (selectedCombination != null && selectedCombination.IsActive) { product.MergeWithCombination(selectedCombination); } } if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) { if (selectedAttributeValues != null) { selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x)); } else { attributesTotalPriceBase = preSelectedPriceAdjustmentBase; } } if (bundleItem != null) { bundleItem.AdditionalCharge = attributesTotalPriceBase; } var result = GetFinalPrice(product, bundleItems, _commonServices.WorkContext.CurrentUser, attributesTotalPriceBase, true, 1, bundleItem); return(result); }
public async Task <IActionResult> UpdateProductDetails(int productId, string itemType, int bundleItemId, ProductVariantQuery query) { // TODO: (core) UpdateProductDetails action needs some decent refactoring. var form = HttpContext.Request.Form; int quantity = 1; int galleryStartIndex = -1; string galleryHtml = null; string dynamicThumbUrl = null; var isAssociated = itemType.EqualsNoCase("associateditem"); var product = await _db.Products.FindByIdAsync(productId); var batchContext = _productService.CreateProductBatchContext(new[] { product }, includeHidden: false); var bItem = await _db.ProductBundleItem .Include(x => x.BundleProduct) .FindByIdAsync(bundleItemId, false); IList <ProductBundleItemData> bundleItemDatas = null; ProductBundleItemData bundleItem = bItem == null ? null : new ProductBundleItemData(bItem); // Quantity required for tier prices. string quantityKey = form.Keys.FirstOrDefault(k => k.EndsWith("EnteredQuantity")); if (quantityKey.HasValue()) { _ = int.TryParse(form[quantityKey], out quantity); } if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { var bundleItemQuery = await _db.ProductBundleItem .AsNoTracking() .ApplyBundledProductsFilter(new[] { product.Id }) .Include(x => x.Product) .Include(x => x.BundleProduct) .ToListAsync(); if (bundleItemQuery.Count > 0) { bundleItemDatas = new List <ProductBundleItemData>(); bundleItemQuery.Each(x => bundleItemDatas.Add(new ProductBundleItemData(x))); } if (query.Variants.Count > 0) { batchContext.Collect(bundleItemDatas.Select(x => x.Item.Product.Id).ToArray()); // May add elements to query object if they are preselected by bundle item filter. foreach (var itemData in bundleItemDatas) { await _helper.MapProductDetailsPageModelAsync(new ProductDetailsModelContext { Product = itemData.Item.Product, BatchContext = batchContext, VariantQuery = query, ProductBundleItem = itemData }); } } } var modelContext = new ProductDetailsModelContext { Product = product, BatchContext = batchContext, VariantQuery = query, IsAssociatedProduct = isAssociated, ProductBundleItem = bundleItem, BundleItemDatas = bundleItemDatas, Customer = batchContext.Customer, Store = batchContext.Store, Currency = Services.WorkContext.WorkingCurrency }; // Get merged model data. var model = new ProductDetailsModel(); await _helper.PrepareProductDetailModelAsync(model, modelContext, quantity); if (bundleItem != null) { // Update bundle item thumbnail. if (!bundleItem.Item.HideThumbnail) { var assignedMediaIds = model.SelectedCombination?.GetAssignedMediaIds() ?? Array.Empty <int>(); var hasFile = assignedMediaIds.Any() && await _db.MediaFiles.AnyAsync(x => x.Id == assignedMediaIds[0]); if (assignedMediaIds.Any() && hasFile) { var file = await _db.ProductMediaFiles .AsNoTracking() .ApplyProductFilter(new[] { bundleItem.Item.ProductId }, 1) .Select(x => x.MediaFile) .FirstOrDefaultAsync(); dynamicThumbUrl = _mediaService.GetUrl(file, _mediaSettings.BundledProductPictureSize, null, false); } } } else if (isAssociated) { // Update associated product thumbnail. var assignedMediaIds = model.SelectedCombination?.GetAssignedMediaIds() ?? new int[0]; var hasFile = await _db.MediaFiles.AnyAsync(x => x.Id == assignedMediaIds[0]); if (assignedMediaIds.Any() && hasFile) { var file = await _db.ProductMediaFiles .AsNoTracking() .ApplyProductFilter(new[] { productId }, 1) .Select(x => x.MediaFile) .FirstOrDefaultAsync(); dynamicThumbUrl = _mediaService.GetUrl(file, _mediaSettings.AssociatedProductPictureSize, null, false); } } else if (product.ProductType != ProductType.BundledProduct) { // Update image gallery. var files = await _db.ProductMediaFiles .AsNoTracking() .ApplyProductFilter(new[] { productId }) .Select(x => _mediaService.ConvertMediaFile(x.MediaFile)) .ToListAsync(); if (product.HasPreviewPicture && files.Count > 1) { files.RemoveAt(0); } if (files.Count <= _catalogSettings.DisplayAllImagesNumber) { // All pictures rendered... only index is required. galleryStartIndex = 0; var assignedMediaIds = model.SelectedCombination?.GetAssignedMediaIds() ?? new int[0]; if (assignedMediaIds.Any()) { var file = files.FirstOrDefault(p => p.Id == assignedMediaIds[0]); galleryStartIndex = file == null ? 0 : files.IndexOf(file); } } else { var allCombinationPictureIds = await _productAttributeService.GetAttributeCombinationFileIdsAsync(product.Id); var mediaModel = _helper.PrepareProductDetailsMediaGalleryModel( files, product.GetLocalized(x => x.Name), allCombinationPictureIds, false, bundleItem, model.SelectedCombination); galleryStartIndex = mediaModel.GalleryStartIndex; galleryHtml = (await this.InvokeViewAsync("Product.Media", mediaModel)).ToString(); } model.PriceDisplayStyle = _catalogSettings.PriceDisplayStyle; model.DisplayTextForZeroPrices = _catalogSettings.DisplayTextForZeroPrices; } object partials = null; if (model.IsBundlePart) { partials = new { BundleItemPrice = await this.InvokeViewAsync("Product.Offer.Price", model), BundleItemStock = await this.InvokeViewAsync("Product.StockInfo", model), BundleItemVariants = await this.InvokeViewAsync("Product.Variants", model.ProductVariantAttributes) }; } else { var dataDictAddToCart = new ViewDataDictionary(ViewData) { Model = model }; dataDictAddToCart.TemplateInfo.HtmlFieldPrefix = $"addtocart_{model.Id}"; decimal adjustment = decimal.Zero; decimal taxRate = decimal.Zero; // TODO: (mh) (core) Implement when pricing is available. //var finalPriceWithDiscountBase = _taxService.GetProductPrice(product, product.Price, Services.WorkContext.CurrentCustomer, out taxRate); //if (!_taxSettings.Value.PricesIncludeTax && Services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax) //{ // adjustment = (m.ProductPrice.PriceValue - finalPriceWithDiscountBase) / (taxRate / 100 + 1); //} //else if (_taxSettings.Value.PricesIncludeTax && Services.WorkContext.TaxDisplayType == TaxDisplayType.ExcludingTax) //{ // adjustment = (m.ProductPrice.PriceValue - finalPriceWithDiscountBase) * (taxRate / 100 + 1); //} //else //{ // adjustment = m.ProductPrice.PriceValue - finalPriceWithDiscountBase; //} partials = new { Attrs = await this.InvokeViewAsync("Product.Attrs", model), Price = await this.InvokeViewAsync("Product.Offer.Price", model), Stock = await this.InvokeViewAsync("Product.StockInfo", model), Variants = await this.InvokeViewAsync("Product.Variants", model.ProductVariantAttributes), OfferActions = await this.InvokeViewAsync("Product.Offer.Actions", dataDictAddToCart), TierPrices = await this.InvokeViewAsync("Product.TierPrices", model.TierPrices), BundlePrice = product.ProductType == ProductType.BundledProduct ? await this.InvokeViewAsync("Product.Bundle.Price", model) : null }; } object data = new { Partials = partials, DynamicThumblUrl = dynamicThumbUrl, GalleryStartIndex = galleryStartIndex, GalleryHtml = galleryHtml }; return(new JsonResult(new { Data = data })); }
protected virtual decimal GetPreselectedPrice( Product product, Customer customer, Currency currency, PriceCalculationContext context, ProductBundleItemData bundleItem, IEnumerable <ProductBundleItemData> bundleItems) { var taxRate = decimal.Zero; var attributesTotalPriceBase = decimal.Zero; var preSelectedPriceAdjustmentBase = decimal.Zero; var isBundle = product.ProductType == ProductType.BundledProduct; var isBundleItemPricing = bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing; var isBundlePricing = bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing; var bundleItemId = bundleItem == null ? 0 : bundleItem.Item.Id; var query = new ProductVariantQuery(); var selectedAttributeValues = new List <ProductVariantAttributeValue>(); var attributes = context.Attributes.GetOrLoad(product.Id); // 1. Fill query with initially selected attributes. foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Any() && x.ShouldHaveValues())) { int preSelectedValueId = 0; ProductVariantAttributeValue defaultValue = null; var selectedValueIds = new List <int>(); var pvaValues = attribute.ProductVariantAttributeValues; foreach (var pvaValue in pvaValues) { ProductBundleItemAttributeFilter attributeFilter = null; if (bundleItem?.Item?.FilterOut(pvaValue, out attributeFilter) ?? false) { continue; } if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) { preSelectedValueId = attributeFilter.AttributeValueId; } if (!isBundlePricing && pvaValue.IsPreSelected) { var includingTax = _services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax; var attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, context, 1); var priceAdjustmentBase = _taxService.GetProductPrice(product, product.TaxCategoryId, attributeValuePriceAdjustment, includingTax, customer, currency, _taxSettings.PricesIncludeTax, out taxRate); preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase); } } // Value pre-selected by a bundle item filter discards the default pre-selection. if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) { //defaultValue.IsPreSelected = true; selectedAttributeValues.Add(defaultValue); query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString()) { ProductId = product.Id, BundleItemId = bundleItemId, AttributeId = attribute.ProductAttributeId, VariantAttributeId = attribute.Id, Alias = attribute.ProductAttribute.Alias, ValueAlias = defaultValue.Alias }); } else { foreach (var value in pvaValues.Where(x => x.IsPreSelected)) { selectedAttributeValues.Add(value); query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) { ProductId = product.Id, BundleItemId = bundleItemId, AttributeId = attribute.ProductAttributeId, VariantAttributeId = attribute.Id, Alias = attribute.ProductAttribute.Alias, ValueAlias = value.Alias }); } } } // 2. Find attribute combination for selected attributes and merge it. if (!isBundle && query.Variants.Any()) { var attributeXml = query.CreateSelectedAttributesXml(product.Id, bundleItemId, attributes, _productAttributeParser, _services.Localization, _downloadService, _catalogSettings, _httpRequestBase, new List <string>()); var combinations = context.AttributeCombinations.GetOrLoad(product.Id); var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml)); if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue) { product.MergedDataValues = new Dictionary <string, object> { { "Price", selectedCombination.Price.Value } }; if (selectedCombination.BasePriceAmount.HasValue) { product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value); } if (selectedCombination.BasePriceBaseAmount.HasValue) { product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value); } } } if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) { if (selectedAttributeValues.Count > 0) { selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x, product, customer, context, 1)); } else { attributesTotalPriceBase = preSelectedPriceAdjustmentBase; } } if (bundleItem != null) { bundleItem.AdditionalCharge = attributesTotalPriceBase; } var result = GetFinalPrice(product, bundleItems, customer, attributesTotalPriceBase, true, 1, bundleItem, context); return(result); }
public ActionResult UpdateProductDetails(int productId, string itemType, int bundleItemId, ProductVariantQuery query, FormCollection form) { int quantity = 1; int galleryStartIndex = -1; string galleryHtml = null; string dynamicThumbUrl = null; var isAssociated = itemType.IsCaseInsensitiveEqual("associateditem"); var pictureModel = new ProductDetailsPictureModel(); var m = new ProductDetailsModel(); var product = _productService.GetProductById(productId); var bItem = _productService.GetBundleItemById(bundleItemId); IList <ProductBundleItemData> bundleItems = null; ProductBundleItemData bundleItem = (bItem == null ? null : new ProductBundleItemData(bItem)); // Quantity required for tier prices. string quantityKey = form.AllKeys.FirstOrDefault(k => k.EndsWith("EnteredQuantity")); if (quantityKey.HasValue()) { int.TryParse(form[quantityKey], out quantity); } if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { bundleItems = _productService.GetBundleItems(product.Id); if (query.Variants.Count > 0) { // May add elements to query object if they are preselected by bundle item filter. foreach (var itemData in bundleItems) { _helper.PrepareProductDetailsPageModel(itemData.Item.Product, query, false, itemData, null); } } } // Get merged model data. _helper.PrepareProductDetailModel(m, product, query, isAssociated, bundleItem, bundleItems, quantity); if (bundleItem != null) { // Update bundle item thumbnail. if (!bundleItem.Item.HideThumbnail) { var picture = m.GetAssignedPicture(_pictureService, null, bundleItem.Item.ProductId); dynamicThumbUrl = _pictureService.GetUrl(picture, _mediaSettings.BundledProductPictureSize, false); } } else if (isAssociated) { // Update associated product thumbnail. var picture = m.GetAssignedPicture(_pictureService, null, productId); dynamicThumbUrl = _pictureService.GetUrl(picture, _mediaSettings.AssociatedProductPictureSize, false); } else if (product.ProductType != ProductType.BundledProduct) { // Update image gallery. var pictures = _pictureService.GetPicturesByProductId(productId); if (pictures.Count <= _catalogSettings.DisplayAllImagesNumber) { // All pictures rendered... only index is required. var picture = m.GetAssignedPicture(_pictureService, pictures); galleryStartIndex = (picture == null ? 0 : pictures.IndexOf(picture)); } else { var allCombinationPictureIds = _productAttributeService.GetAllProductVariantAttributeCombinationPictureIds(product.Id); _helper.PrepareProductDetailsPictureModel( pictureModel, pictures, product.GetLocalized(x => x.Name), allCombinationPictureIds, false, bundleItem, m.SelectedCombination); galleryStartIndex = pictureModel.GalleryStartIndex; galleryHtml = this.RenderPartialViewToString("Product.Picture", pictureModel); } } object partials = null; if (m.IsBundlePart) { partials = new { BundleItemPrice = this.RenderPartialViewToString("Product.Offer.Price", m), BundleItemStock = this.RenderPartialViewToString("Product.StockInfo", m) }; } else { var dataDictAddToCart = new ViewDataDictionary(); dataDictAddToCart.TemplateInfo.HtmlFieldPrefix = string.Format("addtocart_{0}", m.Id); decimal adjustment = decimal.Zero; decimal taxRate = decimal.Zero; var finalPriceWithDiscountBase = _taxService.GetProductPrice(product, product.Price, _services.WorkContext.CurrentCustomer, out taxRate); if (!_taxSettings.Value.PricesIncludeTax && _services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax) { adjustment = (m.ProductPrice.PriceValue - finalPriceWithDiscountBase) / (taxRate / 100 + 1); } else if (_taxSettings.Value.PricesIncludeTax && _services.WorkContext.TaxDisplayType == TaxDisplayType.ExcludingTax) { adjustment = (m.ProductPrice.PriceValue - finalPriceWithDiscountBase) * (taxRate / 100 + 1); } else { adjustment = m.ProductPrice.PriceValue - finalPriceWithDiscountBase; } partials = new { Attrs = this.RenderPartialViewToString("Product.Attrs", m), Price = this.RenderPartialViewToString("Product.Offer.Price", m), Stock = this.RenderPartialViewToString("Product.StockInfo", m), OfferActions = this.RenderPartialViewToString("Product.Offer.Actions", m, dataDictAddToCart), TierPrices = this.RenderPartialViewToString("Product.TierPrices", _helper.CreateTierPriceModel(product, adjustment)), BundlePrice = product.ProductType == ProductType.BundledProduct ? this.RenderPartialViewToString("Product.Bundle.Price", m) : (string)null }; } object data = new { Partials = partials, DynamicThumblUrl = dynamicThumbUrl, GalleryStartIndex = galleryStartIndex, GalleryHtml = galleryHtml }; return(new JsonResult { Data = data }); }
protected virtual async Task <decimal> GetFinalPriceAmountAsync( Product product, decimal additionalCharge, Customer customer = null, bool includeDiscounts = true, int quantity = 1, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null, bool isTierPrice = false) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; if (isTierPrice) { includeDiscounts = true; } var result = GetSpecialPriceAmount(product) ?? product.Price; // Tier price. if (product.HasTierPrices && includeDiscounts && !(bundleItem != null && bundleItem.Item != null)) { var tierPrice = await GetMinimumTierPriceAsync(product, customer, quantity, context); if (tierPrice.HasValue) { if (_catalogSettings.ApplyPercentageDiscountOnTierPrice && !isTierPrice) { var(discountOnTierPrice, appliedDiscount) = await GetDiscountAmountAsync(product, decimal.Zero, customer, quantity, bundleItem, context, tierPrice.Value); if (appliedDiscount != null && appliedDiscount.UsePercentage) { result = Math.Min(result, tierPrice.Value) + additionalCharge - discountOnTierPrice; return(Math.Max(result, decimal.Zero)); } } var(discountAmountTest, _) = await GetDiscountAmountAsync(product, additionalCharge, customer, quantity, bundleItem); var discountProductTest = result - discountAmountTest; if (tierPrice < discountProductTest) { includeDiscounts = false; result = Math.Min(result, tierPrice.Value); } } } result += additionalCharge; if (includeDiscounts) { var(discountAmount, _) = await GetDiscountAmountAsync(product, additionalCharge, customer, quantity, bundleItem, context); result -= discountAmount; } return(Math.Max(result, decimal.Zero)); }
protected virtual async Task <decimal> GetPreselectedPriceAmountAsync( Product product, Customer customer, PriceCalculationContext context, ProductBundleItemData bundleItem, IEnumerable <ProductBundleItemData> bundleItems) { var attributesTotalPriceBase = decimal.Zero; var preSelectedPriceAdjustmentBase = decimal.Zero; var isBundle = product.ProductType == ProductType.BundledProduct; var isBundleItemPricing = bundleItem?.Item?.BundleProduct?.BundlePerItemPricing ?? false; var isBundlePricing = bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing; var bundleItemId = bundleItem?.Item?.Id ?? 0; var query = new ProductVariantQuery(); var selectedAttributeValues = new List <ProductVariantAttributeValue>(); var attributes = await context.Attributes.GetOrLoadAsync(product.Id); // 1. Fill query with initially selected attributes. foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Any() && x.IsListTypeAttribute())) { await _db.LoadCollectionAsync(attribute, x => x.ProductVariantAttributeValues); var preSelectedValueId = 0; var selectedValueIds = new List <int>(); ProductVariantAttributeValue defaultValue = null; var pvaValues = attribute.ProductVariantAttributeValues; foreach (var pvaValue in pvaValues) { ProductBundleItemAttributeFilter attributeFilter = null; if (bundleItem?.Item?.IsFilteredOut(pvaValue, out attributeFilter) ?? false) { continue; } if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) { preSelectedValueId = attributeFilter.AttributeValueId; } if (!isBundlePricing && pvaValue.IsPreSelected) { var attributeValuePriceAdjustment = await GetVariantPriceAdjustmentAsync(pvaValue, product, customer, context, 1); // We cannot avoid money usage in calls between interfaces. var(priceAdjustmentBase, _) = await _taxService.GetProductPriceAsync(product, new(attributeValuePriceAdjustment, _primaryCurrency), customer : customer); preSelectedPriceAdjustmentBase += priceAdjustmentBase.Amount; } } // Value pre-selected by a bundle item filter discards the default pre-selection. if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) { //defaultValue.IsPreSelected = true; selectedAttributeValues.Add(defaultValue); query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString()) { ProductId = product.Id, BundleItemId = bundleItemId, AttributeId = attribute.ProductAttributeId, VariantAttributeId = attribute.Id, Alias = attribute.ProductAttribute.Alias, ValueAlias = defaultValue.Alias }); } else { foreach (var value in pvaValues.Where(x => x.IsPreSelected)) { selectedAttributeValues.Add(value); query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) { ProductId = product.Id, BundleItemId = bundleItemId, AttributeId = attribute.ProductAttributeId, VariantAttributeId = attribute.Id, Alias = attribute.ProductAttribute.Alias, ValueAlias = value.Alias }); } } } // 2. Find attribute combination for selected attributes and merge it. if (!isBundle && query.Variants.Any()) { var(selection, warnings) = await _productAttributeMaterializer.CreateAttributeSelectionAsync(query, attributes, product.Id, bundleItemId, true); var combinations = await context.AttributeCombinations.GetOrLoadAsync(product.Id); var selectedCombination = combinations.FirstOrDefault(x => x.AttributeSelection.Equals(selection)); if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue) { product.MergedDataValues = new Dictionary <string, object> { { "Price", selectedCombination.Price.Value } }; if (selectedCombination.BasePriceAmount.HasValue) { product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value); } if (selectedCombination.BasePriceBaseAmount.HasValue) { product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value); } } } if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) { if (selectedAttributeValues.Count > 0) { foreach (var value in selectedAttributeValues) { attributesTotalPriceBase += await GetVariantPriceAdjustmentAsync(value, product, customer, context, 1); } } else { attributesTotalPriceBase = preSelectedPriceAdjustmentBase; } } if (bundleItem != null) { bundleItem.AdditionalCharge = new(attributesTotalPriceBase, _primaryCurrency); } var result = await GetFinalPriceAmountAsync(product, bundleItems, attributesTotalPriceBase, customer, true, 1, bundleItem, context); return(result); }