private void MapProductSummaryItem(Product product, MapProductSummaryItemContext ctx) { var contextProduct = product; var finalPrice = decimal.Zero; var model = ctx.Model; var settings = ctx.Settings; var item = new ProductSummaryModel.SummaryItem(ctx.Model) { Id = product.Id, Name = product.GetLocalized(x => x.Name).EmptyNull(), SeName = product.GetSeName() }; if (model.ShowDescription) { item.ShortDescription = product.GetLocalized(x => x.ShortDescription); } if (settings.MapFullDescription) { item.FullDescription = product.GetLocalized(x => x.FullDescription); } // Price if (settings.MapPrices) { finalPrice = MapSummaryItemPrice(product, ref contextProduct, item, ctx); } // (Color) Attributes if (settings.MapColorAttributes || settings.MapAttributes) { #region Map (color) attributes var attributes = ctx.BatchContext.Attributes.GetOrLoad(contextProduct.Id); var cachedAttributeNames = new Dictionary <int, string>(); // Color squares if (attributes.Any() && settings.MapColorAttributes) { var colorAttributes = attributes .Where(x => x.IsListTypeAttribute()) .SelectMany(x => x.ProductVariantAttributeValues) .Where(x => x.Color.HasValue() && !x.Color.IsCaseInsensitiveEqual("transparent")) .Distinct() .Take(20) // limit results .Select(x => { var attr = x.ProductVariantAttribute.ProductAttribute; var attrName = cachedAttributeNames.Get(attr.Id) ?? (cachedAttributeNames[attr.Id] = attr.GetLocalized(l => l.Name)); return(new ProductSummaryModel.ColorAttributeValue { Id = x.Id, Color = x.Color, Alias = x.Alias, FriendlyName = x.GetLocalized(l => l.Name), AttributeId = x.ProductVariantAttributeId, AttributeName = attrName, ProductAttributeId = attr.Id, ProductUrl = _productUrlHelper.GetProductUrl(product.Id, item.SeName, 0, x) }); }) .ToList(); item.ColorAttributes = colorAttributes; // TODO: (mc) Resolve attribute value images also } // Variant Attributes if (attributes.Any() && settings.MapAttributes) { if (item.ColorAttributes != null && item.ColorAttributes.Any()) { var processedIds = item.ColorAttributes.Select(x => x.AttributeId).Distinct().ToArray(); attributes = attributes.Where(x => !processedIds.Contains(x.Id)).ToList(); } foreach (var attr in attributes) { var pa = attr.ProductAttribute; item.Attributes.Add(new ProductSummaryModel.Attribute { Id = attr.Id, Alias = pa.Alias, Name = cachedAttributeNames.Get(pa.Id) ?? (cachedAttributeNames[pa.Id] = pa.GetLocalized(l => l.Name)) }); } } #endregion } // Picture if (settings.MapPictures) { #region Map product picture // If a size has been set in the view, we use it in priority int pictureSize = model.ThumbSize.HasValue ? model.ThumbSize.Value : _mediaSettings.ProductThumbPictureSize; // Prepare picture model var defaultProductPictureCacheKey = string.Format( ModelCacheEventConsumer.PRODUCT_DEFAULTPICTURE_MODEL_KEY, product.Id, pictureSize, true, _services.WorkContext.WorkingLanguage.Id, ctx.Store.Id); item.Picture = _services.Cache.Get(defaultProductPictureCacheKey, () => { if (!ctx.BatchContext.Pictures.FullyLoaded) { ctx.BatchContext.Pictures.LoadAll(); } var picture = ctx.BatchContext.Pictures.GetOrLoad(product.Id).FirstOrDefault(); var pictureModel = new PictureModel { Size = pictureSize, ImageUrl = _pictureService.GetPictureUrl(picture, pictureSize, !_catalogSettings.HideProductDefaultPictures), FullSizeImageUrl = _pictureService.GetPictureUrl(picture, 0, !_catalogSettings.HideProductDefaultPictures), Title = string.Format(ctx.Resources["Media.Product.ImageLinkTitleFormat"], item.Name), AlternateText = string.Format(ctx.Resources["Media.Product.ImageAlternateTextFormat"], item.Name), PictureId = picture == null ? 0 : picture.Id }; return(pictureModel); }, TimeSpan.FromHours(6)); #endregion } // Manufacturers if (settings.MapManufacturers) { item.Manufacturer = PrepareManufacturersOverviewModel( ctx.BatchContext.ProductManufacturers.GetOrLoad(product.Id), ctx.CachedManufacturerModels, _catalogSettings.ShowManufacturerLogoInLists && settings.ViewMode == ProductSummaryViewMode.List).FirstOrDefault(); } // Spec Attributes if (settings.MapSpecificationAttributes) { item.SpecificationAttributes.AddRange(MapProductSpecificationModels(ctx.BatchContext.SpecificationAttributes.GetOrLoad(product.Id))); } item.MinPriceProductId = contextProduct.Id; item.Sku = contextProduct.Sku; // Measure Dimensions if (model.ShowDimensions && (contextProduct.Width != 0 || contextProduct.Height != 0 || contextProduct.Length != 0)) { item.Dimensions = ctx.Resources["Products.DimensionsValue"].Text.FormatCurrent( contextProduct.Width.ToString("N2"), contextProduct.Height.ToString("N2"), contextProduct.Length.ToString("N2") ); item.DimensionMeasureUnit = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).SystemKeyword; } // Delivery Times item.HideDeliveryTime = (product.ProductType == ProductType.GroupedProduct); if (model.ShowDeliveryTimes && !item.HideDeliveryTime) { item.StockAvailablity = contextProduct.FormatStockMessage(_localizationService); item.DisplayDeliveryTimeAccordingToStock = contextProduct.DisplayDeliveryTimeAccordingToStock(_catalogSettings); var deliveryTime = _deliveryTimeService.GetDeliveryTime(contextProduct); if (deliveryTime != null) { item.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name); item.DeliveryTimeHexValue = deliveryTime.ColorHexValue; } } item.LegalInfo = ctx.LegalInfo; item.RatingSum = product.ApprovedRatingSum; item.TotalReviews = product.ApprovedTotalReviews; item.IsShippingEnabled = contextProduct.IsShipEnabled; if (finalPrice != decimal.Zero && model.ShowBasePrice) { item.BasePriceInfo = contextProduct.GetBasePriceInfo(finalPrice, _localizationService, _priceFormatter, ctx.Currency); } if (settings.MapPrices) { var addShippingPrice = _currencyService.ConvertCurrency(contextProduct.AdditionalShippingCharge, ctx.Store.PrimaryStoreCurrency, ctx.Currency); if (addShippingPrice > 0) { item.TransportSurcharge = ctx.Resources["Common.AdditionalShippingSurcharge"].Text.FormatCurrent(_priceFormatter.FormatPrice(addShippingPrice, true, false)); } } if (model.ShowWeight && contextProduct.Weight > 0) { item.Weight = "{0} {1}".FormatCurrent(contextProduct.Weight.ToString("N2"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name); } // New Badge if (product.IsNew(_catalogSettings)) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Common.New"), Style = BadgeStyle.Success }); } model.Items.Add(item); }
private decimal MapSummaryItemPrice(Product product, ref Product contextProduct, ProductSummaryModel.SummaryItem item, MapProductSummaryItemContext ctx) { // Returns the final price var finalPrice = decimal.Zero; var model = ctx.Model; var priceModel = new ProductSummaryModel.PriceModel(); if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && !ctx.BatchContext.ProductBundleItems.FullyLoaded) { ctx.BatchContext.ProductBundleItems.LoadAll(); } if (product.ProductType == ProductType.GroupedProduct) { #region Grouped product if (ctx.GroupedProducts == null) { // One-time batched retrieval of all associated products var searchQuery = new CatalogSearchQuery() .PublishedOnly(true) .HasStoreId(ctx.Store.Id) .HasParentGroupedProduct(ctx.BatchContext.ProductIds.ToArray()); // Get all associated products for this batch grouped by ParentGroupedProductId var allAssociatedProducts = _catalogSearchService.Search(searchQuery).Hits .OrderBy(x => x.ParentGroupedProductId) .ThenBy(x => x.DisplayOrder); ctx.GroupedProducts = allAssociatedProducts.ToMultimap(x => x.ParentGroupedProductId, x => x); if (ctx.GroupedProducts.Any()) { ctx.BatchContext.AppliedDiscounts.Collect(allAssociatedProducts.Select(x => x.Id)); } } var associatedProducts = ctx.GroupedProducts[product.Id]; priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; if (associatedProducts.Count > 0) { contextProduct = associatedProducts.OrderBy(x => x.DisplayOrder).First(); _services.DisplayControl.Announce(contextProduct); if (ctx.AllowPrices && _catalogSettings.PriceDisplayType != PriceDisplayType.Hide) { decimal?displayPrice = null; bool displayFromMessage = false; if (_catalogSettings.PriceDisplayType == PriceDisplayType.PreSelectedPrice) { displayPrice = _priceCalculationService.GetPreselectedPrice(contextProduct, ctx.Customer, ctx.BatchContext); } else if (_catalogSettings.PriceDisplayType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) { displayPrice = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, false, 1, null, ctx.BatchContext); } else { displayFromMessage = true; displayPrice = _priceCalculationService.GetLowestPrice(product, ctx.Customer, ctx.BatchContext, associatedProducts, out contextProduct); } if (contextProduct != null && !contextProduct.CustomerEntersPrice) { if (contextProduct.CallForPrice) { priceModel.RegularPriceValue = null; priceModel.PriceValue = 0; priceModel.RegularPrice = null; priceModel.Price = ctx.Resources["Products.CallForPrice"]; } else if (displayPrice.HasValue) { // Calculate prices decimal taxRate = decimal.Zero; decimal oldPriceBase = _taxService.GetProductPrice(contextProduct, contextProduct.OldPrice, out taxRate); decimal finalPriceBase = _taxService.GetProductPrice(contextProduct, displayPrice.Value, out taxRate); finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, ctx.Currency); priceModel.RegularPriceValue = null; priceModel.PriceValue = finalPrice; priceModel.RegularPrice = null; if (displayFromMessage) { priceModel.Price = String.Format(ctx.Resources["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice)); } else { priceModel.Price = _priceFormatter.FormatPrice(finalPrice); } if (oldPriceBase > 0) { priceModel.RegularPriceValue = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, ctx.Currency); } priceModel.HasDiscount = (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero); } else { // Actually it's not possible (we presume that displayPrice always has a value). We never should get here Debug.WriteLine(string.Format("Cannot calculate displayPrice for product #{0}", product.Id)); } } } } #endregion } else { #region Simple product //add to cart button priceModel.DisableBuyButton = product.DisableBuyButton || !ctx.AllowShoppingCart || !ctx.AllowPrices; //add to wishlist button priceModel.DisableWishlistButton = product.DisableWishlistButton || !ctx.AllowWishlist || !ctx.AllowPrices; //pre-order priceModel.AvailableForPreOrder = product.AvailableForPreOrder; //prices if (ctx.AllowPrices && _catalogSettings.PriceDisplayType != PriceDisplayType.Hide && !product.CustomerEntersPrice) { if (product.CallForPrice) { // call for price priceModel.RegularPriceValue = null; priceModel.PriceValue = 0; priceModel.RegularPrice = null; priceModel.Price = ctx.Resources["Products.CallForPrice"]; } else { //calculate prices bool displayFromMessage = false; decimal displayPrice = decimal.Zero; if (_catalogSettings.PriceDisplayType == PriceDisplayType.PreSelectedPrice) { displayPrice = _priceCalculationService.GetPreselectedPrice(product, ctx.Customer, ctx.BatchContext); } else if (_catalogSettings.PriceDisplayType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) { displayPrice = _priceCalculationService.GetFinalPrice(product, null, ctx.Customer, decimal.Zero, false, 1, null, ctx.BatchContext); } else { displayPrice = _priceCalculationService.GetLowestPrice(product, ctx.Customer, ctx.BatchContext, out displayFromMessage); } decimal taxRate = decimal.Zero; decimal oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceBase = _taxService.GetProductPrice(product, displayPrice, out taxRate); decimal oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, ctx.Currency); finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, ctx.Currency); priceModel.HasDiscount = (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero); if (displayFromMessage) { priceModel.RegularPriceValue = null; priceModel.RegularPrice = null; priceModel.Price = String.Format(ctx.Resources["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice)); } else { priceModel.PriceValue = finalPrice; if (priceModel.HasDiscount) { priceModel.RegularPriceValue = oldPrice; priceModel.RegularPrice = _priceFormatter.FormatPrice(oldPrice); priceModel.Price = _priceFormatter.FormatPrice(finalPrice); } else { priceModel.RegularPriceValue = null; priceModel.RegularPrice = null; priceModel.Price = _priceFormatter.FormatPrice(finalPrice); } } } } #endregion } var regularPriceValue = priceModel.RegularPriceValue.GetValueOrDefault(); if (priceModel.HasDiscount && regularPriceValue > 0 && regularPriceValue > priceModel.PriceValue) { priceModel.SavingPercent = (float)((priceModel.RegularPriceValue - priceModel.PriceValue) / priceModel.RegularPriceValue) * 100; priceModel.SavingAmount = _priceFormatter.FormatPrice(regularPriceValue - priceModel.PriceValue, true, false); if (model.ShowDiscountBadge) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Products.SavingBadgeLabel", priceModel.SavingPercent.ToString("N0")), Style = BadgeStyle.Danger }); } } priceModel.CallForPrice = product.CallForPrice; item.Price = priceModel; return(finalPrice); }
/// <param name="contextProduct">The product or the first associated product of a group.</param> /// <returns>The final price</returns> private decimal MapSummaryItemPrice(Product product, ref Product contextProduct, ProductSummaryModel.SummaryItem item, MapProductSummaryItemContext ctx) { var displayFromMessage = false; var taxRate = decimal.Zero; var oldPriceBase = decimal.Zero; var oldPrice = decimal.Zero; var finalPriceBase = decimal.Zero; var finalPrice = decimal.Zero; var displayPrice = decimal.Zero; ICollection <Product> associatedProducts = null; var priceModel = new ProductSummaryModel.PriceModel(); item.Price = priceModel; if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && !ctx.BatchContext.ProductBundleItems.FullyLoaded) { ctx.BatchContext.ProductBundleItems.LoadAll(); } if (product.ProductType == ProductType.GroupedProduct) { priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; if (ctx.GroupedProducts == null) { // One-time batched retrieval of all associated products. var searchQuery = new CatalogSearchQuery() .PublishedOnly(true) .HasStoreId(ctx.Store.Id) .HasParentGroupedProduct(ctx.BatchContext.ProductIds.ToArray()); // Get all associated products for this batch grouped by ParentGroupedProductId. var allAssociatedProducts = _catalogSearchService.Search(searchQuery).Hits .OrderBy(x => x.ParentGroupedProductId) .ThenBy(x => x.DisplayOrder); ctx.GroupedProducts = allAssociatedProducts.ToMultimap(x => x.ParentGroupedProductId, x => x); ctx.AssociatedProductBatchContext = _dataExporter.Value.CreateProductExportContext(allAssociatedProducts, ctx.Customer, null, null, false); } associatedProducts = ctx.GroupedProducts[product.Id]; if (associatedProducts.Any()) { contextProduct = associatedProducts.OrderBy(x => x.DisplayOrder).First(); _services.DisplayControl.Announce(contextProduct); } } else { priceModel.DisableBuyButton = product.DisableBuyButton || !ctx.AllowShoppingCart || !ctx.AllowPrices; priceModel.DisableWishlistButton = product.DisableWishlistButton || !ctx.AllowWishlist || !ctx.AllowPrices; priceModel.AvailableForPreOrder = product.AvailableForPreOrder; } // Return if there's no pricing at all. if (contextProduct == null || contextProduct.CustomerEntersPrice || !ctx.AllowPrices || _catalogSettings.PriceDisplayType == PriceDisplayType.Hide) { return(finalPrice); } // Return if group has no associated products. if (product.ProductType == ProductType.GroupedProduct && !associatedProducts.Any()) { return(finalPrice); } // Call for price. priceModel.CallForPrice = contextProduct.CallForPrice; if (contextProduct.CallForPrice) { priceModel.Price = ctx.Resources["Products.CallForPrice"]; return(finalPrice); } // Calculate prices. var batchContext = product.ProductType == ProductType.GroupedProduct ? ctx.AssociatedProductBatchContext : ctx.BatchContext; if (_catalogSettings.PriceDisplayType == PriceDisplayType.PreSelectedPrice) { displayPrice = _priceCalculationService.GetPreselectedPrice(contextProduct, ctx.Customer, ctx.Currency, batchContext); } else if (_catalogSettings.PriceDisplayType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) { displayPrice = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, false, 1, null, batchContext); } else { // Display lowest price. if (product.ProductType == ProductType.GroupedProduct) { displayFromMessage = true; displayPrice = _priceCalculationService.GetLowestPrice(product, ctx.Customer, batchContext, associatedProducts, out contextProduct) ?? decimal.Zero; } else { displayPrice = _priceCalculationService.GetLowestPrice(product, ctx.Customer, batchContext, out displayFromMessage); } } oldPriceBase = _taxService.GetProductPrice(contextProduct, contextProduct.OldPrice, out taxRate); finalPriceBase = _taxService.GetProductPrice(contextProduct, displayPrice, out taxRate); oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, ctx.Currency); finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, ctx.Currency); priceModel.PriceValue = finalPrice; priceModel.Price = displayFromMessage ? string.Format(ctx.Resources["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice)) : _priceFormatter.FormatPrice(finalPrice); priceModel.HasDiscount = oldPriceBase > decimal.Zero && oldPriceBase > finalPriceBase; if (priceModel.HasDiscount) { priceModel.RegularPriceValue = oldPrice; priceModel.RegularPrice = _priceFormatter.FormatPrice(oldPrice); } // Calculate saving. var finalPriceWithDiscount = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, true, 1, null, batchContext); finalPriceWithDiscount = _taxService.GetProductPrice(contextProduct, finalPriceWithDiscount, out taxRate); finalPriceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscount, ctx.Currency); var finalPriceWithoutDiscount = finalPrice; if (_catalogSettings.PriceDisplayType != PriceDisplayType.PriceWithoutDiscountsAndAttributes) { finalPriceWithoutDiscount = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, false, 1, null, batchContext); finalPriceWithoutDiscount = _taxService.GetProductPrice(contextProduct, finalPriceWithoutDiscount, out taxRate); finalPriceWithoutDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithoutDiscount, ctx.Currency); } // Discounted price has priority over the old price (avoids differing percentage discount in product lists and detail page). var regularPrice = finalPriceWithDiscount < finalPriceWithoutDiscount ? finalPriceWithoutDiscount : oldPrice; if (regularPrice > 0 && regularPrice > finalPriceWithDiscount) { priceModel.HasDiscount = true; priceModel.SavingPercent = (float)((regularPrice - finalPriceWithDiscount) / regularPrice) * 100; priceModel.SavingAmount = _priceFormatter.FormatPrice(regularPrice - finalPriceWithDiscount, true, false); if (!priceModel.RegularPriceValue.HasValue) { priceModel.RegularPriceValue = regularPrice; priceModel.RegularPrice = _priceFormatter.FormatPrice(regularPrice); } if (ctx.Model.ShowDiscountBadge) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Products.SavingBadgeLabel", priceModel.SavingPercent.ToString("N0")), Style = BadgeStyle.Danger }); } } return(finalPrice); }
public virtual ProductSummaryModel MapProductSummaryModel(IPagedList <Product> products, ProductSummaryMappingSettings settings) { Guard.NotNull(products, nameof(products)); if (settings == null) { settings = new ProductSummaryMappingSettings(); } using (_services.Chronometer.Step("MapProductSummaryModel")) { // PERF!! var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var currency = _services.WorkContext.WorkingCurrency; var allowPrices = _services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices); var sllowShoppingCart = _services.Permissions.Authorize(StandardPermissionProvider.EnableShoppingCart); var allowWishlist = _services.Permissions.Authorize(StandardPermissionProvider.EnableWishlist); var taxDisplayType = _services.WorkContext.GetTaxDisplayTypeFor(customer, store.Id); var cachedManufacturerModels = new Dictionary <int, ManufacturerOverviewModel>(); string taxInfo = T(taxDisplayType == TaxDisplayType.IncludingTax ? "Tax.InclVAT" : "Tax.ExclVAT"); var legalInfo = ""; var res = new Dictionary <string, LocalizedString>(StringComparer.OrdinalIgnoreCase) { { "Products.CallForPrice", T("Products.CallForPrice") }, { "Products.PriceRangeFrom", T("Products.PriceRangeFrom") }, { "Media.Product.ImageLinkTitleFormat", T("Media.Product.ImageLinkTitleFormat") }, { "Media.Product.ImageAlternateTextFormat", T("Media.Product.ImageAlternateTextFormat") }, { "Products.DimensionsValue", T("Products.DimensionsValue") }, { "Common.AdditionalShippingSurcharge", T("Common.AdditionalShippingSurcharge") } }; if (settings.MapLegalInfo) { if (_topicService.Value.GetTopicBySystemName("ShippingInfo", store.Id) == null) { legalInfo = T("Tax.LegalInfoShort2").Text.FormatInvariant(taxInfo); } else { var shippingInfoLink = _urlHelper.RouteUrl("Topic", new { SystemName = "shippinginfo" }); legalInfo = T("Tax.LegalInfoShort").Text.FormatInvariant(taxInfo, shippingInfoLink); } } using (var scope = new DbContextScope(ctx: _services.DbContext, autoCommit: false, validateOnSave: false)) { // Run in uncommitting scope, because pictures could be updated (IsNew property) var batchContext = _dataExporter.Value.CreateProductExportContext(products); if (settings.MapPrices) { batchContext.AppliedDiscounts.LoadAll(); batchContext.TierPrices.LoadAll(); } if (settings.MapAttributes || settings.MapColorAttributes) { batchContext.Attributes.LoadAll(); } if (settings.MapManufacturers) { batchContext.ProductManufacturers.LoadAll(); } if (settings.MapSpecificationAttributes) { batchContext.SpecificationAttributes.LoadAll(); } var model = new ProductSummaryModel(products) { ViewMode = settings.ViewMode, GridColumnSpan = _catalogSettings.GridStyleListColumnSpan, ShowSku = _catalogSettings.ShowProductSku, ShowWeight = _catalogSettings.ShowWeight, ShowDimensions = settings.MapDimensions, ShowLegalInfo = settings.MapLegalInfo, ShowDescription = settings.MapShortDescription, ShowFullDescription = settings.MapFullDescription, ShowRatings = settings.MapReviews, ShowDeliveryTimes = settings.MapDeliveryTimes, ShowPrice = settings.MapPrices, ShowBasePrice = settings.MapPrices && _catalogSettings.ShowBasePriceInProductLists && settings.ViewMode != ProductSummaryViewMode.Mini, ShowShippingSurcharge = settings.MapPrices && settings.ViewMode != ProductSummaryViewMode.Mini, ShowButtons = settings.ViewMode != ProductSummaryViewMode.Mini, ShowBrand = settings.MapManufacturers, ForceRedirectionAfterAddingToCart = settings.ForceRedirectionAfterAddingToCart, CompareEnabled = _catalogSettings.CompareProductsEnabled, WishlistEnabled = _permissionService.Value.Authorize(StandardPermissionProvider.EnableWishlist), BuyEnabled = !_catalogSettings.HideBuyButtonInLists, ThumbSize = settings.ThumbnailSize, ShowDiscountBadge = _catalogSettings.ShowDiscountSign, ShowNewBadge = _catalogSettings.LabelAsNewForMaxDays.HasValue }; var mapItemContext = new MapProductSummaryItemContext { BatchContext = batchContext, CachedManufacturerModels = cachedManufacturerModels, Currency = currency, LegalInfo = legalInfo, Model = model, Resources = res, Settings = settings, Customer = customer, Store = store, AllowPrices = allowPrices, AllowShoppingCart = sllowShoppingCart, AllowWishlist = allowWishlist, TaxDisplayType = taxDisplayType }; foreach (var product in products) { MapProductSummaryItem(product, mapItemContext); } _services.DisplayControl.AnnounceRange(products); scope.Commit(); batchContext.Clear(); // don't show stuff without data at all model.ShowDescription = model.ShowDescription && model.Items.Any(x => x.ShortDescription.HasValue()); model.ShowBrand = model.ShowBrand && model.Items.Any(x => x.Manufacturer != null); return(model); } } }
public virtual ProductSummaryModel MapProductSummaryModel(IPagedList <Product> products, ProductSummaryMappingSettings settings) { Guard.NotNull(products, nameof(products)); if (settings == null) { settings = new ProductSummaryMappingSettings(); } using (_services.Chronometer.Step("MapProductSummaryModel")) { var model = new ProductSummaryModel(products) { ViewMode = settings.ViewMode, GridColumnSpan = _catalogSettings.GridStyleListColumnSpan, ShowSku = _catalogSettings.ShowProductSku, ShowWeight = _catalogSettings.ShowWeight, ShowDimensions = settings.MapDimensions, ShowLegalInfo = settings.MapLegalInfo, ShowDescription = settings.MapShortDescription, ShowFullDescription = settings.MapFullDescription, ShowRatings = settings.MapReviews, ShowDeliveryTimes = settings.MapDeliveryTimes, ShowPrice = settings.MapPrices, ShowBasePrice = settings.MapPrices && _catalogSettings.ShowBasePriceInProductLists && settings.ViewMode != ProductSummaryViewMode.Mini, ShowShippingSurcharge = settings.MapPrices && settings.ViewMode != ProductSummaryViewMode.Mini, ShowButtons = settings.ViewMode != ProductSummaryViewMode.Mini, ShowBrand = settings.MapManufacturers, ForceRedirectionAfterAddingToCart = settings.ForceRedirectionAfterAddingToCart, CompareEnabled = _catalogSettings.CompareProductsEnabled, WishlistEnabled = _services.Permissions.Authorize(Permissions.Cart.AccessWishlist), BuyEnabled = !_catalogSettings.HideBuyButtonInLists, ThumbSize = settings.ThumbnailSize, ShowDiscountBadge = _catalogSettings.ShowDiscountSign, ShowNewBadge = _catalogSettings.LabelAsNewForMaxDays.HasValue }; if (products.Count == 0) { // No products, stop here. return(model); } // PERF!! var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var currency = _services.WorkContext.WorkingCurrency; var language = _services.WorkContext.WorkingLanguage; var allowPrices = _services.Permissions.Authorize(Permissions.Catalog.DisplayPrice); var allowShoppingCart = _services.Permissions.Authorize(Permissions.Cart.AccessShoppingCart); var allowWishlist = _services.Permissions.Authorize(Permissions.Cart.AccessWishlist); var taxDisplayType = _services.WorkContext.GetTaxDisplayTypeFor(customer, store.Id); var cachedManufacturerModels = new Dictionary <int, ManufacturerOverviewModel>(); var prefetchTranslations = settings.PrefetchTranslations == true || (settings.PrefetchTranslations == null && _performanceSettings.AlwaysPrefetchTranslations); var prefetchSlugs = settings.PrefetchUrlSlugs == true || (settings.PrefetchUrlSlugs == null && _performanceSettings.AlwaysPrefetchUrlSlugs); var allProductIds = prefetchSlugs || prefetchTranslations?products.Select(x => x.Id).ToArray() : new int[0]; //var productIds = products.Select(x => x.Id).ToArray(); string taxInfo = T(taxDisplayType == TaxDisplayType.IncludingTax ? "Tax.InclVAT" : "Tax.ExclVAT"); var legalInfo = ""; var res = new Dictionary <string, LocalizedString>(StringComparer.OrdinalIgnoreCase) { { "Products.CallForPrice", T("Products.CallForPrice") }, { "Products.PriceRangeFrom", T("Products.PriceRangeFrom") }, { "Media.Product.ImageLinkTitleFormat", T("Media.Product.ImageLinkTitleFormat") }, { "Media.Product.ImageAlternateTextFormat", T("Media.Product.ImageAlternateTextFormat") }, { "Products.DimensionsValue", T("Products.DimensionsValue") }, { "Common.AdditionalShippingSurcharge", T("Common.AdditionalShippingSurcharge") } }; if (settings.MapLegalInfo) { var shippingInfoUrl = _urlHelper.Topic("shippinginfo").ToString(); legalInfo = shippingInfoUrl.HasValue() ? T("Tax.LegalInfoShort").Text.FormatInvariant(taxInfo, shippingInfoUrl) : T("Tax.LegalInfoShort2").Text.FormatInvariant(taxInfo); } if (prefetchSlugs) { _urlRecordService.PrefetchUrlRecords(nameof(Product), new[] { language.Id, 0 }, allProductIds); } if (prefetchTranslations) { // Prefetch all delivery time translations _localizedEntityService.PrefetchLocalizedProperties(nameof(DeliveryTime), language.Id, null); } using (var scope = new DbContextScope(ctx: _services.DbContext, autoCommit: false, validateOnSave: false)) { // Run in uncommitting scope, because pictures could be updated (IsNew property) var batchContext = _dataExporter.Value.CreateProductExportContext(products, customer, null, 1, false); if (settings.MapPrices) { batchContext.AppliedDiscounts.LoadAll(); batchContext.TierPrices.LoadAll(); } if (settings.MapAttributes || settings.MapColorAttributes) { batchContext.Attributes.LoadAll(); if (prefetchTranslations) { // Prefetch all product attribute translations PrefetchTranslations( nameof(ProductAttribute), language.Id, batchContext.Attributes.SelectMany(x => x.Value).Select(x => x.ProductAttribute)); // Prefetch all variant attribute value translations PrefetchTranslations( nameof(ProductVariantAttributeValue), language.Id, batchContext.Attributes.SelectMany(x => x.Value).SelectMany(x => x.ProductVariantAttributeValues)); } } if (settings.MapManufacturers) { batchContext.ProductManufacturers.LoadAll(); } if (settings.MapSpecificationAttributes) { batchContext.SpecificationAttributes.LoadAll(); if (prefetchTranslations) { // Prefetch all spec attribute option translations PrefetchTranslations( nameof(SpecificationAttributeOption), language.Id, batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption)); // Prefetch all spec attribute translations PrefetchTranslations( nameof(SpecificationAttribute), language.Id, batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption.SpecificationAttribute)); } } // If a size has been set in the view, we use it in priority int thumbSize = model.ThumbSize ?? _mediaSettings.ProductThumbPictureSize; var mapItemContext = new MapProductSummaryItemContext { BatchContext = batchContext, CachedManufacturerModels = cachedManufacturerModels, Currency = currency, LegalInfo = legalInfo, Model = model, Resources = res, Settings = settings, Customer = customer, Store = store, AllowPrices = allowPrices, AllowShoppingCart = allowShoppingCart, AllowWishlist = allowWishlist, TaxDisplayType = taxDisplayType }; if (settings.MapPictures) { var fileIds = products .Select(x => x.MainPictureId ?? 0) .Where(x => x != 0) .Distinct() .ToArray(); mapItemContext.MediaFiles = _mediaService.GetFilesByIds(fileIds).ToDictionarySafe(x => x.Id); } foreach (var product in products) { MapProductSummaryItem(product, mapItemContext); } _services.DisplayControl.AnnounceRange(products); scope.Commit(); batchContext.Clear(); // don't show stuff without data at all model.ShowDescription = model.ShowDescription && model.Items.Any(x => x.ShortDescription?.Value?.HasValue() == true); model.ShowBrand = model.ShowBrand && model.Items.Any(x => x.Manufacturer != null); return(model); } } }
private void MapProductSummaryItem(Product product, MapProductSummaryItemContext ctx) { var contextProduct = product; var finalPrice = decimal.Zero; var model = ctx.Model; var settings = ctx.Settings; var item = new ProductSummaryModel.SummaryItem(ctx.Model) { Id = product.Id, Name = product.GetLocalized(x => x.Name), SeName = product.GetSeName() }; if (model.ShowDescription) { item.ShortDescription = product.GetLocalized(x => x.ShortDescription); } if (settings.MapFullDescription) { item.FullDescription = product.GetLocalized(x => x.FullDescription, detectEmptyHtml: true); } // Price if (settings.MapPrices) { finalPrice = MapSummaryItemPrice(product, ref contextProduct, item, ctx); } // (Color) Attributes if (settings.MapColorAttributes || settings.MapAttributes) { var attributes = ctx.BatchContext.Attributes.GetOrLoad(contextProduct.Id); var cachedAttributeNames = new Dictionary <int, LocalizedValue <string> >(); // Color squares if (attributes.Any() && settings.MapColorAttributes) { var colorAttributes = attributes .Where(x => x.IsListTypeAttribute()) .SelectMany(x => x.ProductVariantAttributeValues) .Where(x => x.Color.HasValue() && !x.Color.IsCaseInsensitiveEqual("transparent")) .Distinct() .Take(20) // limit results .Select(x => { var attr = x.ProductVariantAttribute.ProductAttribute; var attrName = cachedAttributeNames.Get(attr.Id) ?? (cachedAttributeNames[attr.Id] = attr.GetLocalized(l => l.Name)); return(new ProductSummaryModel.ColorAttributeValue { Id = x.Id, Color = x.Color, Alias = x.Alias, FriendlyName = x.GetLocalized(l => l.Name), AttributeId = x.ProductVariantAttributeId, AttributeName = attrName, ProductAttributeId = attr.Id, ProductUrl = _productUrlHelper.GetProductUrl(product.Id, item.SeName, 0, x) }); }) .ToList(); item.ColorAttributes = colorAttributes; // TODO: (mc) Resolve attribute value images also } // Variant Attributes if (attributes.Any() && settings.MapAttributes) { if (item.ColorAttributes != null && item.ColorAttributes.Any()) { var processedIds = item.ColorAttributes.Select(x => x.AttributeId).Distinct().ToArray(); attributes = attributes.Where(x => !processedIds.Contains(x.Id)).ToList(); } foreach (var attr in attributes) { var pa = attr.ProductAttribute; item.Attributes.Add(new ProductSummaryModel.Attribute { Id = attr.Id, Alias = pa.Alias, Name = cachedAttributeNames.Get(pa.Id) ?? (cachedAttributeNames[pa.Id] = pa.GetLocalized(l => l.Name)) }); } } } // Picture if (settings.MapPictures) { var thumbSize = model.ThumbSize ?? _mediaSettings.ProductThumbPictureSize; ctx.MediaFiles.TryGetValue(product.MainPictureId ?? 0, out var file); item.Picture = new PictureModel { PictureId = file?.Id ?? 0, Size = thumbSize, ImageUrl = _mediaService.GetUrl(file, thumbSize, null, !_catalogSettings.HideProductDefaultPictures), FullSizeImageUrl = _mediaService.GetUrl(file, 0, null, !_catalogSettings.HideProductDefaultPictures), FullSizeImageWidth = file?.Dimensions.Width, FullSizeImageHeight = file?.Dimensions.Height, Title = string.Format(ctx.Resources["Media.Product.ImageLinkTitleFormat"], item.Name), AlternateText = string.Format(ctx.Resources["Media.Product.ImageAlternateTextFormat"], item.Name), File = file }; } // Manufacturers if (settings.MapManufacturers) { item.Manufacturer = PrepareManufacturersOverviewModel( ctx.BatchContext.ProductManufacturers.GetOrLoad(product.Id), ctx.CachedManufacturerModels, _catalogSettings.ShowManufacturerLogoInLists && settings.ViewMode == ProductSummaryViewMode.List).FirstOrDefault(); } // Spec Attributes if (settings.MapSpecificationAttributes) { item.SpecificationAttributes.AddRange(MapProductSpecificationModels(ctx.BatchContext.SpecificationAttributes.GetOrLoad(product.Id))); } item.MinPriceProductId = contextProduct.Id; item.Sku = contextProduct.Sku; // Measure Dimensions if (model.ShowDimensions && (contextProduct.Width != 0 || contextProduct.Height != 0 || contextProduct.Length != 0)) { item.Dimensions = ctx.Resources["Products.DimensionsValue"].Text.FormatCurrent( contextProduct.Width.ToString("N2"), contextProduct.Height.ToString("N2"), contextProduct.Length.ToString("N2") ); item.DimensionMeasureUnit = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).SystemKeyword; } // Delivery Times item.HideDeliveryTime = (product.ProductType == ProductType.GroupedProduct); if (model.ShowDeliveryTimes && !item.HideDeliveryTime) { // We cannot include ManageInventoryMethod.ManageStockByAttributes because it's only functional with MergeWithCombination. //item.StockAvailablity = contextProduct.FormatStockMessage(_localizationService); //item.DisplayDeliveryTimeAccordingToStock = contextProduct.DisplayDeliveryTimeAccordingToStock(_catalogSettings); //var deliveryTime = _deliveryTimeService.GetDeliveryTime(contextProduct); //if (deliveryTime != null) //{ // item.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name); // item.DeliveryTimeHexValue = deliveryTime.ColorHexValue; //} var deliveryTimeId = product.DeliveryTimeId ?? 0; if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.StockQuantity <= 0 && _catalogSettings.DeliveryTimeIdForEmptyStock.HasValue) { deliveryTimeId = _catalogSettings.DeliveryTimeIdForEmptyStock.Value; } var deliveryTime = _deliveryTimeService.GetDeliveryTimeById(deliveryTimeId); if (deliveryTime != null) { item.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name); item.DeliveryTimeHexValue = deliveryTime.ColorHexValue; } item.DisplayDeliveryTimeAccordingToStock = product.ManageInventoryMethod == ManageInventoryMethod.ManageStock ? product.StockQuantity > 0 || (product.StockQuantity <= 0 && _catalogSettings.DeliveryTimeIdForEmptyStock.HasValue) : true; if (product.DisplayStockAvailability && product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { if (product.StockQuantity > 0) { item.StockAvailablity = product.DisplayStockQuantity ? T("Products.Availability.InStockWithQuantity", product.StockQuantity) : T("Products.Availability.InStock"); } else { item.StockAvailablity = product.BackorderMode == BackorderMode.NoBackorders || product.BackorderMode == BackorderMode.AllowQtyBelow0 ? T("Products.Availability.OutOfStock") : T("Products.Availability.Backordering"); } } } item.LegalInfo = ctx.LegalInfo; item.RatingSum = product.ApprovedRatingSum; item.TotalReviews = product.ApprovedTotalReviews; item.IsShippingEnabled = contextProduct.IsShipEnabled; if (finalPrice != decimal.Zero && model.ShowBasePrice) { item.BasePriceInfo = contextProduct.GetBasePriceInfo(finalPrice, _localizationService, _priceFormatter, ctx.Currency); } if (settings.MapPrices) { var addShippingPrice = _currencyService.ConvertCurrency(contextProduct.AdditionalShippingCharge, ctx.Store.PrimaryStoreCurrency, ctx.Currency); if (addShippingPrice > 0) { item.TransportSurcharge = ctx.Resources["Common.AdditionalShippingSurcharge"].Text.FormatCurrent(_priceFormatter.FormatPrice(addShippingPrice, true, false)); } item.PriceDisplayStyle = _catalogSettings.PriceDisplayStyle; item.DisplayTextForZeroPrices = _catalogSettings.DisplayTextForZeroPrices; } if (model.ShowWeight && contextProduct.Weight > 0) { var measureWeightName = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId)?.GetLocalized(x => x.Name) ?? string.Empty; item.Weight = "{0} {1}".FormatCurrent(contextProduct.Weight.ToString("N2"), measureWeightName); } // New Badge if (product.IsNew(_catalogSettings)) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Common.New"), Style = BadgeStyle.Success }); } model.Items.Add(item); }
private Money ToWorkingCurrency(Money amount, MapProductSummaryItemContext ctx) { return(_currencyService.ConvertToCurrency(amount, ctx.CalculationOptions.TargetCurrency)); }
/// <param name="contextProduct">The product or the first associated product of a group.</param> /// <returns>The final price</returns> private Task <(Money FinalPrice, Product ContextProduct)> MapSummaryItemPrice(Product product, ProductSummaryModel.SummaryItem item, MapProductSummaryItemContext ctx) { var result = (ctx.StoreCurrency.AsMoney(100m), product); return(Task.FromResult(result)); //var displayFromMessage = false; //var taxRate = decimal.Zero; //var oldPriceBase = default(Money); //var oldPrice = default(Money); //var finalPriceBase = default(Money); //var finalPrice = decimal.Zero; //var displayPrice = default(Money); //ICollection<Product> associatedProducts = null; //var priceModel = new ProductSummaryModel.PriceModel(); //item.Price = priceModel; //if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && !ctx.BatchContext.ProductBundleItems.FullyLoaded) //{ // await ctx.BatchContext.ProductBundleItems.LoadAllAsync(); //} //if (product.ProductType == ProductType.GroupedProduct) //{ // priceModel.DisableBuyButton = true; // priceModel.DisableWishlistButton = true; // priceModel.AvailableForPreOrder = false; // if (ctx.GroupedProducts == null) // { // // One-time batched retrieval of all associated products. // var searchQuery = new CatalogSearchQuery() // .PublishedOnly(true) // .HasStoreId(ctx.Store.Id) // .HasParentGroupedProduct(ctx.BatchContext.ProductIds.ToArray()); // // Get all associated products for this batch grouped by ParentGroupedProductId. // var searchResult = await _catalogSearchService.SearchAsync(searchQuery); // var allAssociatedProducts = (await searchResult.GetHitsAsync()) // .OrderBy(x => x.ParentGroupedProductId) // .ThenBy(x => x.DisplayOrder); // ctx.GroupedProducts = allAssociatedProducts.ToMultimap(x => x.ParentGroupedProductId, x => x); // ctx.AssociatedProductBatchContext = _dataExporter.Value.CreateProductExportContext(allAssociatedProducts, ctx.Customer, null, null, false); // } // associatedProducts = ctx.GroupedProducts[product.Id]; // if (associatedProducts.Any()) // { // contextProduct = associatedProducts.OrderBy(x => x.DisplayOrder).First(); // _services.DisplayControl.Announce(contextProduct); // } //} //else //{ // priceModel.DisableBuyButton = product.DisableBuyButton || !ctx.AllowShoppingCart || !ctx.AllowPrices; // priceModel.DisableWishlistButton = product.DisableWishlistButton || !ctx.AllowWishlist || !ctx.AllowPrices; // priceModel.AvailableForPreOrder = product.AvailableForPreOrder; //} //// Return if there's no pricing at all. //if (contextProduct == null || contextProduct.CustomerEntersPrice || !ctx.AllowPrices || _catalogSettings.PriceDisplayType == PriceDisplayType.Hide) //{ // return ctx.Currency.AsMoney(finalPrice); //} //// Return if group has no associated products. //if (product.ProductType == ProductType.GroupedProduct && !associatedProducts.Any()) //{ // return ctx.Currency.AsMoney(finalPrice); //} //// Call for price. //priceModel.CallForPrice = contextProduct.CallForPrice; //if (contextProduct.CallForPrice) //{ // priceModel.Price = ctx.Resources["Products.CallForPrice"]; // return ctx.Currency.AsMoney(finalPrice); //} //// Calculate prices. //var batchContext = product.ProductType == ProductType.GroupedProduct ? ctx.AssociatedProductBatchContext : ctx.BatchContext; //if (_catalogSettings.PriceDisplayType == PriceDisplayType.PreSelectedPrice) //{ // displayPrice = await _priceCalculationService.GetPreselectedPriceAsync(contextProduct, ctx.Customer, ctx.Currency, batchContext); //} //else if (_catalogSettings.PriceDisplayType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) //{ // displayPrice = await _priceCalculationService.GetFinalPriceAsync(contextProduct, null, null, ctx.Customer, false, 1, null, batchContext); //} //else //{ // // Display lowest price. // if (product.ProductType == ProductType.GroupedProduct) // { // displayFromMessage = true; // (displayPrice, contextProduct) = await _priceCalculationService.GetLowestPriceAsync(product, ctx.Customer, batchContext, associatedProducts); // } // else // { // (displayPrice, displayFromMessage) = await _priceCalculationService.GetLowestPriceAsync(product, ctx.Customer, batchContext); // } //} //(oldPriceBase, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, contextProduct.OldPrice); //(finalPriceBase, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, displayPrice); //oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, ctx.Currency); //finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, ctx.Currency); //priceModel.PriceValue = finalPrice; //priceModel.Price = displayFromMessage // ? string.Format(ctx.Resources["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice)) // : _priceFormatter.FormatPrice(finalPrice); //priceModel.HasDiscount = oldPriceBase > decimal.Zero && oldPriceBase > finalPriceBase; //if (priceModel.HasDiscount) //{ // priceModel.RegularPriceValue = oldPrice; // priceModel.RegularPrice = _priceFormatter.FormatPrice(oldPrice); //} //// Calculate saving. //var finalPriceWithDiscount = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, true, 1, null, batchContext); //finalPriceWithDiscount = _taxService.GetProductPrice(contextProduct, finalPriceWithDiscount, out taxRate); //finalPriceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscount, ctx.Currency); //var finalPriceWithoutDiscount = finalPrice; //if (_catalogSettings.PriceDisplayType != PriceDisplayType.PriceWithoutDiscountsAndAttributes) //{ // finalPriceWithoutDiscount = _priceCalculationService.GetFinalPrice(contextProduct, null, ctx.Customer, decimal.Zero, false, 1, null, batchContext); // finalPriceWithoutDiscount = _taxService.GetProductPrice(contextProduct, finalPriceWithoutDiscount, out taxRate); // finalPriceWithoutDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithoutDiscount, ctx.Currency); //} //// Discounted price has priority over the old price (avoids differing percentage discount in product lists and detail page). //var regularPrice = finalPriceWithDiscount < finalPriceWithoutDiscount // ? finalPriceWithoutDiscount // : oldPrice; //if (regularPrice > 0 && regularPrice > finalPriceWithDiscount) //{ // priceModel.HasDiscount = true; // priceModel.SavingPercent = (float)((regularPrice - finalPriceWithDiscount) / regularPrice) * 100; // priceModel.SavingAmount = _priceFormatter.FormatPrice(regularPrice - finalPriceWithDiscount, true, false); // if (!priceModel.RegularPriceValue.HasValue) // { // priceModel.RegularPriceValue = regularPrice; // priceModel.RegularPrice = _priceFormatter.FormatPrice(regularPrice); // } // if (ctx.Model.ShowDiscountBadge) // { // item.Badges.Add(new ProductSummaryModel.Badge // { // Label = T("Products.SavingBadgeLabel", priceModel.SavingPercent.ToString("N0")), // Style = BadgeStyle.Danger // }); // } //} //return finalPrice; }
private Money ToWorkingCurrency(decimal amount, MapProductSummaryItemContext ctx, bool showCurrency = true) { return(_currencyService.ConvertToCurrency(new Money(amount, ctx.PrimaryCurrency, !showCurrency), ctx.CalculationOptions.TargetCurrency)); }
/// <param name="contextProduct">The product or the first associated product of a group.</param> /// <returns>The final price</returns> private async Task <(Money FinalPrice, Product ContextProduct)> MapSummaryItemPrice(Product product, ProductSummaryModel.SummaryItem item, MapProductSummaryItemContext ctx) { var options = ctx.CalculationOptions; var batchContext = ctx.BatchContext; var contextProduct = product; ICollection <Product> associatedProducts = null; var priceModel = new ProductSummaryModel.PriceModel(); item.Price = priceModel; if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && !batchContext.ProductBundleItems.FullyLoaded) { await batchContext.ProductBundleItems.LoadAllAsync(); } if (product.ProductType == ProductType.GroupedProduct) { priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; if (ctx.GroupedProducts == null) { // One-time batched retrieval of all associated products. var searchQuery = new CatalogSearchQuery() .PublishedOnly(true) .HasStoreId(options.Store.Id) .HasParentGroupedProduct(batchContext.ProductIds.ToArray()); // Get all associated products for this batch grouped by ParentGroupedProductId. var searchResult = await _catalogSearchService.SearchAsync(searchQuery); var allAssociatedProducts = (await searchResult.GetHitsAsync()) .OrderBy(x => x.ParentGroupedProductId) .ThenBy(x => x.DisplayOrder); ctx.GroupedProducts = allAssociatedProducts.ToMultimap(x => x.ParentGroupedProductId, x => x); ctx.AssociatedProductBatchContext = _productService.CreateProductBatchContext(allAssociatedProducts, options.Store, options.Customer, false); options.ChildProductsBatchContext = ctx.AssociatedProductBatchContext; } associatedProducts = ctx.GroupedProducts[product.Id]; if (associatedProducts.Any()) { contextProduct = associatedProducts.OrderBy(x => x.DisplayOrder).First(); _services.DisplayControl.Announce(contextProduct); } } else { priceModel.DisableBuyButton = product.DisableBuyButton || !ctx.AllowShoppingCart || !ctx.AllowPrices; priceModel.DisableWishlistButton = product.DisableWishlistButton || !ctx.AllowWishlist || !ctx.AllowPrices; priceModel.AvailableForPreOrder = product.AvailableForPreOrder; } // Return if there's no pricing at all. if (contextProduct == null || contextProduct.CustomerEntersPrice || !ctx.AllowPrices || _catalogSettings.PriceDisplayType == PriceDisplayType.Hide) { return(new Money(options.TargetCurrency), contextProduct); } // Return if group has no associated products. if (product.ProductType == ProductType.GroupedProduct && !associatedProducts.Any()) { return(new Money(options.TargetCurrency), contextProduct); } // Call for price. priceModel.CallForPrice = contextProduct.CallForPrice; if (contextProduct.CallForPrice) { var money = new Money(options.TargetCurrency).WithPostFormat(ctx.Resources["Products.CallForPrice"]); priceModel.Price = money; return(money, contextProduct); } var calculationContext = new PriceCalculationContext(product, options) { AssociatedProducts = associatedProducts }; // -----> Perform calculation <------- var calculatedPrice = await _priceCalculationService.CalculatePriceAsync(calculationContext); var savings = calculatedPrice.PriceSaving; priceModel.Price = calculatedPrice.FinalPrice; priceModel.HasDiscount = savings.HasSaving; if (savings.HasSaving) { priceModel.RegularPrice = savings.SavingPrice; priceModel.SavingAmount = savings.SavingAmount; priceModel.SavingPercent = savings.SavingPercent; if (ctx.Model.ShowDiscountBadge) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Products.SavingBadgeLabel", priceModel.SavingPercent.ToString("N0")), Style = BadgeStyle.Danger }); } } return(calculatedPrice.FinalPrice, contextProduct); }
private async Task MapProductSummaryItem(Product product, MapProductSummaryItemContext ctx) { var contextProduct = product; var finalPrice = default(Money); var model = ctx.Model; var settings = ctx.MappingSettings; var options = ctx.CalculationOptions; var slug = await product.GetActiveSlugAsync(); var item = new ProductSummaryModel.SummaryItem(ctx.Model) { Id = product.Id, Name = product.GetLocalized(x => x.Name), SeName = slug, DetailUrl = _urlHelper.RouteUrl("Product", new { SeName = slug }) }; if (model.ShowDescription) { item.ShortDescription = product.GetLocalized(x => x.ShortDescription); } if (settings.MapFullDescription) { item.FullDescription = product.GetLocalized(x => x.FullDescription, detectEmptyHtml: true); } // Price if (settings.MapPrices) { (finalPrice, contextProduct) = await MapSummaryItemPrice(product, item, ctx); } // (Color) Attributes if (settings.MapColorAttributes || settings.MapAttributes) { var attributes = await ctx.BatchContext.Attributes.GetOrLoadAsync(contextProduct.Id); var cachedAttributeNames = new Dictionary <int, LocalizedValue <string> >(); // Color squares if (attributes.Any() && settings.MapColorAttributes) { var colorAttributes = attributes .Where(x => x.IsListTypeAttribute()) .SelectMany(x => x.ProductVariantAttributeValues) .Where(x => x.Color.HasValue() && !x.Color.EqualsNoCase("transparent")) .Distinct() .Take(20) // limit results .Select(x => { var attr = x.ProductVariantAttribute.ProductAttribute; var attrName = cachedAttributeNames.Get(attr.Id) ?? (cachedAttributeNames[attr.Id] = attr.GetLocalized(l => l.Name)); return(new ProductSummaryModel.ColorAttributeValue { Id = x.Id, Color = x.Color, Alias = x.Alias, FriendlyName = x.GetLocalized(l => l.Name), AttributeId = x.ProductVariantAttributeId, AttributeName = attrName, ProductAttributeId = attr.Id, ProductUrl = _productUrlHelper.GetProductUrl(product.Id, item.SeName, 0, x) }); }) .ToList(); item.ColorAttributes = colorAttributes; // TODO: (mc) Resolve attribute value images also } // Variant Attributes if (attributes.Any() && settings.MapAttributes) { if (item.ColorAttributes != null && item.ColorAttributes.Any()) { var processedIds = item.ColorAttributes.Select(x => x.AttributeId).Distinct().ToArray(); attributes = attributes.Where(x => !processedIds.Contains(x.Id)).ToList(); } foreach (var attr in attributes) { var pa = attr.ProductAttribute; item.Attributes.Add(new ProductSummaryModel.Attribute { Id = attr.Id, Alias = pa.Alias, Name = cachedAttributeNames.Get(pa.Id) ?? (cachedAttributeNames[pa.Id] = pa.GetLocalized(l => l.Name)) }); } } } // Picture if (settings.MapPictures) { var thumbSize = model.ThumbSize ?? _mediaSettings.ProductThumbPictureSize; ctx.MediaFiles.TryGetValue(product.MainPictureId ?? 0, out var file); item.Image = new ImageModel { File = file, ThumbSize = thumbSize, Title = file?.File?.GetLocalized(x => x.Title)?.Value.NullEmpty() ?? string.Format(ctx.Resources["Media.Product.ImageLinkTitleFormat"], item.Name), Alt = file?.File?.GetLocalized(x => x.Alt)?.Value.NullEmpty() ?? string.Format(ctx.Resources["Media.Product.ImageAlternateTextFormat"], item.Name), NoFallback = _catalogSettings.HideProductDefaultPictures }; _services.DisplayControl.Announce(file?.File); } // Brands if (settings.MapManufacturers) { item.Brand = (await PrepareBrandOverviewModelAsync( await ctx.BatchContext.ProductManufacturers.GetOrLoadAsync(product.Id), ctx.CachedBrandModels, _catalogSettings.ShowManufacturerLogoInLists && settings.ViewMode == ProductSummaryViewMode.List)).FirstOrDefault(); } // Spec Attributes if (settings.MapSpecificationAttributes) { item.SpecificationAttributes.AddRange(MapProductSpecificationModels(await ctx.BatchContext.SpecificationAttributes.GetOrLoadAsync(product.Id))); } item.MinPriceProductId = contextProduct.Id; item.Sku = contextProduct.Sku; // Measure Dimensions if (model.ShowDimensions && (contextProduct.Width != 0 || contextProduct.Height != 0 || contextProduct.Length != 0)) { item.Dimensions = ctx.Resources["Products.DimensionsValue"].Value.FormatCurrent( contextProduct.Width.ToString("N2"), contextProduct.Height.ToString("N2"), contextProduct.Length.ToString("N2") ); item.DimensionMeasureUnit = (await _db.MeasureDimensions.FindByIdAsync(_measureSettings.BaseDimensionId, false))?.SystemKeyword; } // Delivery Times. item.HideDeliveryTime = product.ProductType == ProductType.GroupedProduct; if (!item.HideDeliveryTime && model.DeliveryTimesPresentation != DeliveryTimesPresentation.None) { // We cannot include ManageInventoryMethod.ManageStockByAttributes because it's only functional with MergeWithCombination. // INFO: (core) Don't uncomment this part //item.StockAvailablity = contextProduct.FormatStockMessage(_localizationService); //item.DisplayDeliveryTimeAccordingToStock = contextProduct.DisplayDeliveryTimeAccordingToStock(_catalogSettings); //var deliveryTime = _deliveryTimeService.GetDeliveryTime(contextProduct); //if (deliveryTime != null) //{ // item.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name); // item.DeliveryTimeHexValue = deliveryTime.ColorHexValue; //} var deliveryTimeId = product.DeliveryTimeId ?? 0; if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.StockQuantity <= 0 && _catalogSettings.DeliveryTimeIdForEmptyStock.HasValue) { deliveryTimeId = _catalogSettings.DeliveryTimeIdForEmptyStock.Value; } var deliveryTime = await _db.DeliveryTimes.FindByIdAsync(deliveryTimeId, false); if (deliveryTime != null) { item.DeliveryTimeName = deliveryTime.GetLocalized(x => x.Name); item.DeliveryTimeHexValue = deliveryTime.ColorHexValue; // Due to lack of space, the grid view does not show a date for the delivery time. if (settings.ViewMode >= ProductSummaryViewMode.List && (model.DeliveryTimesPresentation == DeliveryTimesPresentation.DateOnly || model.DeliveryTimesPresentation == DeliveryTimesPresentation.LabelAndDate)) { item.DeliveryTimeDate = _deliveryTimeService.GetFormattedDeliveryDate(deliveryTime); } } item.DisplayDeliveryTimeAccordingToStock = product.ManageInventoryMethod == ManageInventoryMethod.ManageStock ? product.StockQuantity > 0 || (product.StockQuantity <= 0 && _catalogSettings.DeliveryTimeIdForEmptyStock.HasValue) : true; if (product.DisplayStockAvailability && product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { if (product.StockQuantity > 0) { item.StockAvailablity = product.DisplayStockQuantity ? T("Products.Availability.InStockWithQuantity", product.StockQuantity) : T("Products.Availability.InStock"); } else { item.StockAvailablity = product.BackorderMode == BackorderMode.NoBackorders || product.BackorderMode == BackorderMode.AllowQtyBelow0 ? T("Products.Availability.OutOfStock") : T("Products.Availability.Backordering"); } } } item.LegalInfo = ctx.LegalInfo; item.RatingSum = product.ApprovedRatingSum; item.TotalReviews = product.ApprovedTotalReviews; item.IsShippingEnabled = contextProduct.IsShippingEnabled; if (finalPrice != decimal.Zero && model.ShowBasePrice) { item.BasePriceInfo = _priceCalculationService.GetBasePriceInfo(contextProduct, finalPrice, options.TargetCurrency); } if (settings.MapPrices) { var addShippingPrice = ToWorkingCurrency(contextProduct.AdditionalShippingCharge, ctx); if (addShippingPrice > 0) { item.TransportSurcharge = addShippingPrice.WithPostFormat(ctx.Resources["Common.AdditionalShippingSurcharge"]); } item.PriceDisplayStyle = _catalogSettings.PriceDisplayStyle; item.DisplayTextForZeroPrices = _catalogSettings.DisplayTextForZeroPrices; } if (model.ShowWeight && contextProduct.Weight > 0) { var measureWeightName = (await _db.MeasureWeights.FindByIdAsync(_measureSettings.BaseWeightId, false))?.GetLocalized(x => x.Name) ?? string.Empty; item.Weight = $"{contextProduct.Weight.ToString("N2")} {measureWeightName}"; } // New Badge if (product.IsNew(_catalogSettings)) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Common.New"), Style = BadgeStyle.Success }); } model.Items.Add(item); }
public virtual async Task <ProductSummaryModel> MapProductSummaryModelAsync(IPagedList <Product> products, CatalogSearchResult sourceResult, ProductSummaryMappingSettings settings) { Guard.NotNull(products, nameof(products)); if (settings == null) { settings = new ProductSummaryMappingSettings(); } using (_services.Chronometer.Step("MapProductSummaryModel")) { var model = new ProductSummaryModel(products, sourceResult) { ViewMode = settings.ViewMode, GridColumnSpan = _catalogSettings.GridStyleListColumnSpan, ShowSku = _catalogSettings.ShowProductSku, ShowWeight = _catalogSettings.ShowWeight, ShowDimensions = settings.MapDimensions, ShowLegalInfo = settings.MapLegalInfo, ShowDescription = settings.MapShortDescription, ShowFullDescription = settings.MapFullDescription, ShowRatings = settings.MapReviews, ShowPrice = settings.MapPrices, ShowBasePrice = settings.MapPrices && _catalogSettings.ShowBasePriceInProductLists && settings.ViewMode != ProductSummaryViewMode.Mini, ShowShippingSurcharge = settings.MapPrices && settings.ViewMode != ProductSummaryViewMode.Mini, ShowButtons = settings.ViewMode != ProductSummaryViewMode.Mini, ShowBrand = settings.MapManufacturers, ForceRedirectionAfterAddingToCart = settings.ForceRedirectionAfterAddingToCart, CompareEnabled = _catalogSettings.CompareProductsEnabled, WishlistEnabled = _services.Permissions.Authorize(Permissions.Cart.AccessWishlist), BuyEnabled = !_catalogSettings.HideBuyButtonInLists, ThumbSize = settings.ThumbnailSize, ShowDiscountBadge = _catalogSettings.ShowDiscountSign, ShowNewBadge = _catalogSettings.LabelAsNewForMaxDays.HasValue, DeliveryTimesPresentation = settings.DeliveryTimesPresentation, }; if (products.Count == 0) { // No products, stop here. return(model); } using var scope = new DbContextScope(_db, retainConnection: true, deferCommit: true); // PERF!! var calculationOptions = _priceCalculationService.CreateDefaultOptions(true); var language = calculationOptions.Language; var customer = calculationOptions.Customer; var allowPrices = await _services.Permissions.AuthorizeAsync(Permissions.Catalog.DisplayPrice); var allowShoppingCart = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessShoppingCart); var allowWishlist = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessWishlist); var cachedBrandModels = new Dictionary <int, BrandOverviewModel>(); var prefetchTranslations = settings.PrefetchTranslations == true || (settings.PrefetchTranslations == null && _performanceSettings.AlwaysPrefetchTranslations); var prefetchSlugs = settings.PrefetchUrlSlugs == true || (settings.PrefetchUrlSlugs == null && _performanceSettings.AlwaysPrefetchUrlSlugs); var allProductIds = prefetchSlugs || prefetchTranslations?products.Select(x => x.Id).ToArray() : Array.Empty <int>(); //var productIds = products.Select(x => x.Id).ToArray(); string taxInfo = T(calculationOptions.TaxInclusive ? "Tax.InclVAT" : "Tax.ExclVAT"); var legalInfo = string.Empty; var res = new Dictionary <string, LocalizedString>(StringComparer.OrdinalIgnoreCase) { { "Products.CallForPrice", T("Products.CallForPrice") }, { "Products.PriceRangeFrom", T("Products.PriceRangeFrom") }, { "Media.Product.ImageLinkTitleFormat", T("Media.Product.ImageLinkTitleFormat") }, { "Media.Product.ImageAlternateTextFormat", T("Media.Product.ImageAlternateTextFormat") }, { "Products.DimensionsValue", T("Products.DimensionsValue") }, { "Common.AdditionalShippingSurcharge", T("Common.AdditionalShippingSurcharge") } }; if (settings.MapLegalInfo) { var shippingInfoUrl = (await _urlHelper.TopicAsync("shippinginfo")); legalInfo = shippingInfoUrl.HasValue() ? T("Tax.LegalInfoShort", taxInfo, shippingInfoUrl) : T("Tax.LegalInfoShort2", taxInfo); } if (prefetchSlugs) { await _urlService.PrefetchUrlRecordsAsync(nameof(Product), new[] { language.Id, 0 }, allProductIds); } if (prefetchTranslations) { // Prefetch all delivery time translations await _localizedEntityService.PrefetchLocalizedPropertiesAsync(nameof(DeliveryTime), language.Id, null); } // Run in uncommitting scope, because pictures could be updated (IsNew property) var batchContext = _productService.CreateProductBatchContext(products, calculationOptions.Store, customer, false, true); if (settings.MapPrices) { await batchContext.AppliedDiscounts.LoadAllAsync(); await batchContext.TierPrices.LoadAllAsync(); } if (settings.MapAttributes || settings.MapColorAttributes) { await batchContext.Attributes.LoadAllAsync(); if (prefetchTranslations) { // Prefetch all product attribute translations await PrefetchTranslations( nameof(ProductAttribute), language.Id, batchContext.Attributes.SelectMany(x => x.Value).Select(x => x.ProductAttribute)); // Prefetch all variant attribute value translations await PrefetchTranslations( nameof(ProductVariantAttributeValue), language.Id, batchContext.Attributes.SelectMany(x => x.Value).SelectMany(x => x.ProductVariantAttributeValues)); } } if (settings.MapManufacturers) { await batchContext.ProductManufacturers.LoadAllAsync(); } if (settings.MapSpecificationAttributes) { await batchContext.SpecificationAttributes.LoadAllAsync(); if (prefetchTranslations) { // Prefetch all spec attribute option translations await PrefetchTranslations( nameof(SpecificationAttributeOption), language.Id, batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption)); // Prefetch all spec attribute translations await PrefetchTranslations( nameof(SpecificationAttribute), language.Id, batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption.SpecificationAttribute)); } } // If a size has been set in the view, we use it in priority int thumbSize = model.ThumbSize ?? _mediaSettings.ProductThumbPictureSize; calculationOptions.BatchContext = batchContext; // Don't perform discount limitation and coupon code check in list rendering as it can have heavy impact on performance. calculationOptions.CheckDiscountValidity = false; var mapItemContext = new MapProductSummaryItemContext { BatchContext = batchContext, CalculationOptions = calculationOptions, CachedBrandModels = cachedBrandModels, PrimaryCurrency = _currencyService.PrimaryCurrency, LegalInfo = legalInfo, Model = model, Resources = res, MappingSettings = settings, AllowPrices = allowPrices, AllowShoppingCart = allowShoppingCart, AllowWishlist = allowWishlist, ShippingChargeTaxFormat = _currencyService.GetTaxFormat(priceIncludesTax: calculationOptions.TaxInclusive, target: PricingTarget.ShippingCharge, language: language), }; if (settings.MapPictures) { var fileIds = products .Select(x => x.MainPictureId ?? 0) .Where(x => x != 0) .Distinct() .ToArray(); mapItemContext.MediaFiles = (await _mediaService.GetFilesByIdsAsync(fileIds)).ToDictionarySafe(x => x.Id); } foreach (var product in products) { await MapProductSummaryItem(product, mapItemContext); } _services.DisplayControl.AnnounceRange(products); await scope.CommitAsync(); batchContext.Clear(); // don't show stuff without data at all model.ShowDescription = model.ShowDescription && model.Items.Any(x => x.ShortDescription?.Value?.HasValue() == true); model.ShowBrand = model.ShowBrand && model.Items.Any(x => x.Brand != null); return(model); } }
/// <param name="contextProduct">The product or the first associated product of a group.</param> /// <returns>The final price</returns> private async Task <(Money FinalPrice, Product ContextProduct)> MapSummaryItemPrice(Product product, ProductSummaryModel.SummaryItem item, MapProductSummaryItemContext ctx) { //return (new Money(0, ctx.WorkingCurrency), product); var options = ctx.CalculationOptions; var batchContext = ctx.BatchContext; var contextProduct = product; var displayFromMessage = false; var taxRate = decimal.Zero; var oldPriceBase = default(Money); var oldPrice = default(Money); var finalPriceBase = default(Money); var finalPrice = new Money(ctx.PrimaryCurrency); var displayPrice = (Money?)null; ICollection <Product> associatedProducts = null; var priceModel = new ProductSummaryModel.PriceModel(); item.Price = priceModel; if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing && !batchContext.ProductBundleItems.FullyLoaded) { await batchContext.ProductBundleItems.LoadAllAsync(); } if (product.ProductType == ProductType.GroupedProduct) { priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; if (ctx.GroupedProducts == null) { // One-time batched retrieval of all associated products. var searchQuery = new CatalogSearchQuery() .PublishedOnly(true) .HasStoreId(options.Store.Id) .HasParentGroupedProduct(batchContext.ProductIds.ToArray()); // Get all associated products for this batch grouped by ParentGroupedProductId. var searchResult = await _catalogSearchService.SearchAsync(searchQuery); var allAssociatedProducts = (await searchResult.GetHitsAsync()) .OrderBy(x => x.ParentGroupedProductId) .ThenBy(x => x.DisplayOrder); ctx.GroupedProducts = allAssociatedProducts.ToMultimap(x => x.ParentGroupedProductId, x => x); ctx.AssociatedProductBatchContext = _productService.CreateProductBatchContext(allAssociatedProducts, options.Store, options.Customer, false, null); options.ChildProductsBatchContext = ctx.AssociatedProductBatchContext; } associatedProducts = ctx.GroupedProducts[product.Id]; if (associatedProducts.Any()) { contextProduct = associatedProducts.OrderBy(x => x.DisplayOrder).First(); _services.DisplayControl.Announce(contextProduct); } } else { priceModel.DisableBuyButton = product.DisableBuyButton || !ctx.AllowShoppingCart || !ctx.AllowPrices; priceModel.DisableWishlistButton = product.DisableWishlistButton || !ctx.AllowWishlist || !ctx.AllowPrices; priceModel.AvailableForPreOrder = product.AvailableForPreOrder; } // Return if there's no pricing at all. if (contextProduct == null || contextProduct.CustomerEntersPrice || !ctx.AllowPrices || _catalogSettings.PriceDisplayType == PriceDisplayType.Hide) { return(new Money(options.TargetCurrency), contextProduct); } // Return if group has no associated products. if (product.ProductType == ProductType.GroupedProduct && !associatedProducts.Any()) { return(new Money(options.TargetCurrency), contextProduct); } // Call for price. priceModel.CallForPrice = contextProduct.CallForPrice; if (contextProduct.CallForPrice) { return(new Money(options.TargetCurrency).WithPostFormat(ctx.Resources["Products.CallForPrice"]), contextProduct); } #region Test NEW var calculationContext = new PriceCalculationContext(product, options) { AssociatedProducts = associatedProducts }; // -----> Perform calculation <------- var calculatedPrice = await _priceCalculationService.CalculatePriceAsync(calculationContext); priceModel.Price = calculatedPrice.FinalPrice; priceModel.HasDiscount = calculatedPrice.HasDiscount; if (calculatedPrice.HasDiscount) { priceModel.RegularPrice = calculatedPrice.RegularPrice; priceModel.SavingAmount = calculatedPrice.SavingAmount; priceModel.SavingPercent = calculatedPrice.SavingPercent; } return(calculatedPrice.FinalPrice, contextProduct); #endregion // Calculate prices. batchContext = product.ProductType == ProductType.GroupedProduct ? ctx.AssociatedProductBatchContext : ctx.BatchContext; if (_catalogSettings.PriceDisplayType == PriceDisplayType.PreSelectedPrice) { displayPrice = await _priceCalculationService.GetPreselectedPriceAsync(contextProduct, options.Customer, batchContext); } else if (_catalogSettings.PriceDisplayType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) { displayPrice = await _priceCalculationService.GetFinalPriceAsync(contextProduct, null, null, options.Customer, false, 1, null, batchContext); } else { // Display lowest price. if (product.ProductType == ProductType.GroupedProduct) { displayFromMessage = true; (displayPrice, contextProduct) = await _priceCalculationService.GetLowestPriceAsync(product, options.Customer, batchContext, associatedProducts); } else { (displayPrice, displayFromMessage) = await _priceCalculationService.GetLowestPriceAsync(product, options.Customer, batchContext); } } (oldPriceBase, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, new Money(contextProduct.OldPrice, ctx.PrimaryCurrency)); (finalPriceBase, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, displayPrice ?? new Money(ctx.PrimaryCurrency)); oldPrice = ToWorkingCurrency(oldPriceBase, ctx); finalPrice = ToWorkingCurrency(finalPriceBase, ctx).WithPostFormat(options.TaxFormat); string finalPricePostFormat = finalPrice.PostFormat; if (displayFromMessage) { finalPricePostFormat = finalPricePostFormat == null ? ctx.Resources["Products.PriceRangeFrom"] : string.Format(ctx.Resources["Products.PriceRangeFrom"], finalPricePostFormat); } priceModel.Price = finalPrice.WithPostFormat(finalPricePostFormat); priceModel.HasDiscount = oldPriceBase > decimal.Zero && oldPriceBase > finalPriceBase; if (priceModel.HasDiscount) { priceModel.RegularPrice = oldPrice.WithPostFormat(options.TaxFormat); } // Calculate saving. var finalPriceWithDiscount = await _priceCalculationService.GetFinalPriceAsync(contextProduct, null, null, options.Customer, true, 1, null, batchContext); (finalPriceWithDiscount, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, finalPriceWithDiscount); finalPriceWithDiscount = ToWorkingCurrency(finalPriceWithDiscount, ctx); var finalPriceWithoutDiscount = finalPrice; if (_catalogSettings.PriceDisplayType != PriceDisplayType.PriceWithoutDiscountsAndAttributes) { finalPriceWithoutDiscount = await _priceCalculationService.GetFinalPriceAsync(contextProduct, null, null, options.Customer, false, 1, null, batchContext); (finalPriceWithoutDiscount, taxRate) = await _taxService.GetProductPriceAsync(contextProduct, finalPriceWithoutDiscount); finalPriceWithoutDiscount = ToWorkingCurrency(finalPriceWithoutDiscount, ctx); } // Discounted price has priority over the old price (avoids differing percentage discount in product lists and detail page). var regularPrice = finalPriceWithDiscount < finalPriceWithoutDiscount ? finalPriceWithoutDiscount : oldPrice; if (regularPrice > 0 && regularPrice > finalPriceWithDiscount) { priceModel.HasDiscount = true; priceModel.SavingPercent = (float)((regularPrice - finalPriceWithDiscount) / regularPrice) * 100; priceModel.SavingAmount = (regularPrice - finalPriceWithDiscount).WithPostFormat(null); if (priceModel.RegularPrice == null) { priceModel.RegularPrice = regularPrice.WithPostFormat(options.TaxFormat); } if (ctx.Model.ShowDiscountBadge) { item.Badges.Add(new ProductSummaryModel.Badge { Label = T("Products.SavingBadgeLabel", priceModel.SavingPercent.ToString("N0")), Style = BadgeStyle.Danger }); } } return(finalPrice, contextProduct); }