Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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);
        }
Example #4
0
        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);
                }
            }
        }
Example #5
0
        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);
                }
            }
        }
Example #6
0
        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));
 }
Example #8
0
        /// <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);
            }
        }
Example #13
0
        /// <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);
        }