public void Execute(PriceCalculationContext context)
 {
     if (context.PaymentMethod != null)
     {
         context.PaymentMethodCost = context.PaymentMethod.GetPaymentMethodCost(context.Subtotal);
     }
 }
Exemplo n.º 2
0
        public IEnumerable <PromotionMatch> MatchApplicablePromotions(PriceCalculationContext context, IEnumerable <Promotion> candidatePromotions)
        {
            var matches = new List <PromotionMatch>();

            // Lower priority promotions check first
            candidatePromotions = candidatePromotions.OrderBy(x => x.Priority).ThenBy(x => x.Id);

            foreach (var promotion in candidatePromotions)
            {
                var match = TryMatchPromotion(promotion, context);
                if (match != null)
                {
                    matches.Add(match);
                }
            }

            if (matches.Count > 0)
            {
                // Higher priority check first
                matches.Reverse();
                CheckOverlappingUsage(matches);
            }

            return(matches);
        }
Exemplo n.º 3
0
        public bool ApplyCoupon(ShoppingCart cart, string coupon)
        {
            if (String.IsNullOrWhiteSpace(coupon))
            {
                return(false);
            }

            var oldCoupon = cart.CouponCode;

            cart.CouponCode = coupon;

            var context = PriceCalculationContext.CreateFrom(cart);

            new PriceCalculator().Calculate(context);

            if (context.AppliedPromotions.Any(p => p.RequireCouponCode && p.CouponCode == coupon))
            {
                return(true);
            }

            cart.CouponCode = oldCoupon;

            _repository.Database.SaveChanges();

            return(false);
        }
Exemplo n.º 4
0
        public void Execute(PriceCalculationContext context)
        {
            var matcher    = new PromotionMatcher(_ruleEngine);
            var promotions = _promotionService.Query().WhereAvailableNow().ToList();

            var matches = matcher.MatchApplicablePromotions(context, promotions);

            foreach (var match in matches)
            {
                var policy = _policyFactory.FindByName(match.Promotion.PromotionPolicyName);
                if (policy == null)
                {
                    throw new InvalidOperationException("Cannot load promotion policy with name '" + match.Promotion.PromotionPolicyName + "'. Ensure corresponding add-in has been installed.");
                }

                object config = null;
                if (policy.ConfigType != null)
                {
                    config = match.Promotion.LoadPolicyConfig(policy.ConfigType);
                }

                policy.Execute(new PromotionContext(match.Promotion, config, match.ConditionMatchedItems, context));
                context.AppliedPromotions.Add(match.Promotion);
            }
        }
Exemplo n.º 5
0
        public void Execute(PriceCalculationContext context)
        {
            // TODO: Calculate TAX
            var tax = 0m;

            context.Tax = tax;
        }
 public void Execute(PriceCalculationContext context)
 {
     if (context.PaymentMethod != null)
     {
         context.PaymentMethodCost = context.PaymentMethod.GetPaymentMethodCost(context.Subtotal);
     }
 }
        public CheckPromotionConditionResult CheckConditions(Promotion promotion, PriceCalculationContext context)
        {
            var result = new CheckPromotionConditionResult();

            if (!promotion.Conditions.Any())
            {
                result.Success = true;
                foreach (var item in context.Items)
                {
                    result.MatchedItems.Add(item);
                }

                return(result);
            }

            var operators = _ruleEngine.ComparisonOperators.Select(o => o.Name).ToList();

            foreach (var item in context.Items)
            {
                var contextModel = new PromotionConditionContextModel
                {
                    Item     = item,
                    Customer = context.Customer
                };

                if (_ruleEngine.Evaluate(promotion.Conditions, contextModel))
                {
                    result.Success = true;
                    result.MatchedItems.Add(item);
                }
            }

            return(result);
        }
        public ShippingRateCalculationContext(ShippingMethod shippingMethod, object shippingRateProviderConfig, PriceCalculationContext pricingContext)
        {
            Require.NotNull(shippingMethod, "shippingMethod");
            Require.NotNull(pricingContext, "pricingContext");

            ShippingMethod             = shippingMethod;
            ShippingRateProviderConfig = shippingRateProviderConfig;
            PricingContext             = pricingContext;
        }
Exemplo n.º 9
0
        public PromotionContext(
            Promotion promotion, object policyConfig, IEnumerable <PriceCalculationItem> conditionMatchedItems, PriceCalculationContext pricingContext)
        {
            Require.NotNull(promotion, "promotion");
            Require.NotNull(pricingContext, "pricingContext");

            Promotion             = promotion;
            PolicyConfig          = policyConfig;
            ConditionMatchedItems = (conditionMatchedItems ?? Enumerable.Empty <PriceCalculationItem>()).ToList();
            PricingContext        = pricingContext;
        }
Exemplo n.º 10
0
        public PriceCalculationContext CalculatePrice(ShoppingCart cart, ShoppingContext shoppingContext)
        {
            Require.NotNull(cart, "cart");

            var context = PriceCalculationContext.CreateFrom(cart);

            if (shoppingContext != null)
            {
                context.Culture = shoppingContext.Culture;
            }

            new PriceCalculator().Calculate(context);

            Event.Raise(new CartPriceCalculated(cart, context), _instance);

            return(context);
        }
        public void Execute(PriceCalculationContext context)
        {
            if (context.ShippingMethod != null)
            {
                var provider = _factory.FindByName(context.ShippingMethod.ShippingRateProviderName);
                if (provider != null)
                {
                    object shippingRateProviderConfig = null;
                    if (provider.ConfigType != null)
                    {
                        shippingRateProviderConfig = context.ShippingMethod.LoadShippingRateProviderConfig(provider.ConfigType);
                    }

                    var shippingCost = provider.GetShippingRate(
                        new ShippingRateCalculationContext(context.ShippingMethod, shippingRateProviderConfig, context));

                    context.ShippingCost = shippingCost;
                }
            }
        }
Exemplo n.º 12
0
        public void Execute(PriceCalculationContext context)
        {
            if (context.ShippingMethod != null)
            {
                var provider = _factory.FindByName(context.ShippingMethod.ShippingRateProviderName);
                if (provider != null)
                {
                    object shippingRateProviderConfig = null;
                    if (provider.ConfigType != null)
                    {
                        shippingRateProviderConfig = context.ShippingMethod.LoadShippingRateProviderConfig(provider.ConfigType);
                    }

                    var shippingCost = provider.GetShippingRate(
                        new ShippingRateCalculationContext(context.ShippingMethod, shippingRateProviderConfig, context));

                    context.ShippingCost = shippingCost;
                }
            }
        }
Exemplo n.º 13
0
        private PromotionMatch TryMatchPromotion(Promotion promotion, PriceCalculationContext context)
        {
            if (promotion.RequireCouponCode && context.CouponCode != promotion.CouponCode)
            {
                return(null);
            }

            var isMatch = false;
            var conditionMatchedItems = new List <PriceCalculationItem>();

            if (!promotion.Conditions.Any())
            {
                isMatch = true;
            }
            else
            {
                var conditionChecker = new PromotionConditionChecker(_ruleEngine);
                var result           = conditionChecker.CheckConditions(promotion, context);
                if (result.Success)
                {
                    isMatch = true;
                    foreach (var item in result.MatchedItems)
                    {
                        if (!conditionMatchedItems.Contains(item))
                        {
                            conditionMatchedItems.Add(item);
                        }
                    }
                }
            }

            if (isMatch)
            {
                return(new PromotionMatch(promotion, conditionMatchedItems));
            }

            return(null);
        }
Exemplo n.º 14
0
        public void Execute(PriceCalculationContext context)
        {
            var matcher = new PromotionMatcher(_ruleEngine);
            var promotions = _promotionService.Query().WhereAvailableNow().ToList();

            var matches = matcher.MatchApplicablePromotions(context, promotions);

            foreach (var match in matches)
            {
                var policy = _policyFactory.FindByName(match.Promotion.PromotionPolicyName);
                if (policy == null)
                    throw new InvalidOperationException("Cannot load promotion policy with name '" + match.Promotion.PromotionPolicyName + "'. Ensure corresponding add-in has been installed.");

                object config = null;
                if (policy.ConfigType != null)
                {
                    config = match.Promotion.LoadPolicyConfig(policy.ConfigType);
                }

                policy.Execute(new PromotionContext(match.Promotion, config, match.ConditionMatchedItems, context));
                context.AppliedPromotions.Add(match.Promotion);
            }
        }
Exemplo n.º 15
0
 public decimal GetFinalPrice(ShoppingContext context)
 {
     return(PriceCalculationContext.GetFinalPrice(ProductId, Id, Price, context));
 }
        /// <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);
        }
Exemplo n.º 17
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);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Creates an expando object with all product properties.
        /// This method is used when exporting products and when exporting variant combinations as products.
        /// </summary>
        private async Task <dynamic> ToDynamic(Product product, bool isParent, DataExporterContext ctx, DynamicProductContext productContext)
        {
            var combination = productContext.Combination;

            product.MergeWithCombination(combination);

            var productManufacturers = await ctx.ProductBatchContext.ProductManufacturers.GetOrLoadAsync(product.Id);

            var productCategories = await ctx.ProductBatchContext.ProductCategories.GetOrLoadAsync(product.Id);

            var productAttributes = await ctx.ProductBatchContext.Attributes.GetOrLoadAsync(product.Id);

            var productTags = await ctx.ProductBatchContext.ProductTags.GetOrLoadAsync(product.Id);

            var specificationAttributes = await ctx.ProductBatchContext.SpecificationAttributes.GetOrLoadAsync(product.Id);

            var selectedAttributes     = combination?.AttributeSelection;
            var variantAttributeValues = combination?.AttributeSelection?.MaterializeProductVariantAttributeValues(productAttributes);

            // Price calculation.
            var calculationContext = new PriceCalculationContext(product, ctx.PriceCalculationOptions);

            calculationContext.AddSelectedAttributes(combination?.AttributeSelection, product.Id);
            var price = await _priceCalculationService.CalculatePriceAsync(calculationContext);

            dynamic dynObject = ToDynamic(product, ctx, productContext.SeName, price.FinalPrice);

            dynObject._IsParent     = isParent;
            dynObject._CategoryName = null;
            dynObject._CategoryPath = null;
            dynObject._AttributeCombinationValues = null;
            dynObject._AttributeCombinationId     = 0;

            dynObject.Price = price.FinalPrice.Amount;

            if (combination != null)
            {
                dynObject._AttributeCombinationId = combination.Id;
                dynObject._UniqueId = product.Id + "-" + combination.Id;

                if (ctx.Supports(ExportFeatures.UsesAttributeCombination))
                {
                    dynObject._AttributeCombinationValues = variantAttributeValues;
                }

                if (ctx.Projection.AttributeCombinationValueMerging == ExportAttributeValueMerging.AppendAllValuesToName)
                {
                    var valueNames = variantAttributeValues
                                     .Select(x => ctx.GetTranslation(x, nameof(x.Name), x.Name))
                                     .ToList();

                    dynObject.Name = ((string)dynObject.Name).Grow(string.Join(", ", valueNames), " ");
                }
            }
            else
            {
                dynObject._UniqueId = product.Id.ToString();
            }

            if (selectedAttributes?.AttributesMap?.Any() ?? false)
            {
                var query = new ProductVariantQuery();
                await _productUrlHelper.AddAttributesToQueryAsync(query, selectedAttributes, product.Id, 0, productAttributes);

                dynObject._DetailUrl = productContext.AbsoluteProductUrl + _productUrlHelper.ToQueryString(query);
            }
            else
            {
                dynObject._DetailUrl = productContext.AbsoluteProductUrl;
            }

            // Category path.
            {
                var categoryPath = string.Empty;
                var pc           = productCategories.OrderBy(x => x.DisplayOrder).FirstOrDefault();
                if (pc != null)
                {
                    var node = await _categoryService.GetCategoryTreeAsync(pc.CategoryId, true, ctx.Store.Id);

                    if (node != null)
                    {
                        categoryPath = _categoryService.GetCategoryPath(node, ctx.Projection.LanguageId, null, " > ");
                    }
                }

                dynObject._CategoryPath = categoryPath;
            }

            dynObject.CountryOfOrigin = ctx.Countries.TryGetValue(product.CountryOfOriginId ?? 0, out var countryOfOrigin)
                ? ToDynamic(countryOfOrigin, ctx)
                : null;

            dynObject.ProductManufacturers = productManufacturers
                                             .OrderBy(x => x.DisplayOrder)
                                             .Select(x =>
            {
                dynamic dyn              = new DynamicEntity(x);
                dyn.Manufacturer         = ToDynamic(x.Manufacturer, ctx);
                dyn.Manufacturer.Picture = x.Manufacturer != null && x.Manufacturer.MediaFileId.HasValue
                        ? ToDynamic(x.Manufacturer.MediaFile, _mediaSettings.ManufacturerThumbPictureSize, _mediaSettings.ManufacturerThumbPictureSize, ctx)
                        : null;

                return(dyn);
            })
                                             .ToList();

            dynObject.ProductCategories = productCategories
                                          .OrderBy(x => x.DisplayOrder)
                                          .Select(x =>
            {
                dynamic dyn          = new DynamicEntity(x);
                dyn.Category         = ToDynamic(x.Category, ctx);
                dyn.Category.Picture = x.Category != null && x.Category.MediaFileId.HasValue
                        ? ToDynamic(x.Category.MediaFile, _mediaSettings.CategoryThumbPictureSize, _mediaSettings.CategoryThumbPictureSize, ctx)
                        : null;

                if (dynObject._CategoryName == null)
                {
                    dynObject._CategoryName = (string)dyn.Category.Name;
                }

                return(dyn);
            })
                                          .ToList();

            dynObject.ProductAttributes = productAttributes
                                          .OrderBy(x => x.DisplayOrder)
                                          .Select(x => ToDynamic(x, ctx))
                                          .ToList();

            // Do not export combinations if a combination is exported as a product.
            if (productContext.Combinations != null && productContext.Combination == null)
            {
                var pictureSize       = ctx.Projection.PictureSize > 0 ? ctx.Projection.PictureSize : _mediaSettings.ProductDetailsPictureSize;
                var productMediaFiles = (await ctx.ProductBatchContext.ProductMediaFiles.GetOrLoadAsync(product.Id))
                                        .ToDictionarySafe(x => x.MediaFileId, x => x);

                dynObject.ProductAttributeCombinations = productContext.Combinations
                                                         .Select(x =>
                {
                    dynamic dyn       = DataExporter.ToDynamic(x, ctx);
                    var assignedFiles = new List <dynamic>();

                    foreach (var fileId in x.GetAssignedMediaIds().Take(ctx.Projection.NumberOfMediaFiles ?? int.MaxValue))
                    {
                        if (productMediaFiles.TryGetValue(fileId, out var assignedFile) && assignedFile.MediaFile != null)
                        {
                            assignedFiles.Add(ToDynamic(assignedFile.MediaFile, _mediaSettings.ProductThumbPictureSize, pictureSize, ctx));
                        }
                    }

                    dyn.Pictures = assignedFiles;
                    return(dyn);
                })
                                                         .ToList();
            }
            else
            {
                dynObject.ProductAttributeCombinations = Enumerable.Empty <ProductVariantAttributeCombination>();
            }

            if (product.HasTierPrices)
            {
                var tierPrices = await ctx.ProductBatchContext.TierPrices.GetOrLoadAsync(product.Id);

                dynObject.TierPrices = tierPrices
                                       .RemoveDuplicatedQuantities()
                                       .Select(x =>
                {
                    dynamic dyn = new DynamicEntity(x);
                    return(dyn);
                })
                                       .ToList();
            }

            if (product.HasDiscountsApplied)
            {
                var appliedDiscounts = await ctx.ProductBatchContext.AppliedDiscounts.GetOrLoadAsync(product.Id);

                dynObject.AppliedDiscounts = appliedDiscounts
                                             .Select(x => CreateDynamic(x))
                                             .ToList();
            }

            if (product.IsDownload)
            {
                var downloads = await ctx.ProductBatchContext.Downloads.GetOrLoadAsync(product.Id);

                dynObject.Downloads = downloads
                                      .Select(x => CreateDynamic(x))
                                      .ToList();
            }

            dynObject.ProductTags = productTags
                                    .Select(x =>
            {
                var localizedName = ctx.GetTranslation(x, nameof(x.Name), x.Name);
                dynamic dyn       = new DynamicEntity(x);
                dyn.Name          = localizedName;
                dyn.SeName        = SeoHelper.BuildSlug(localizedName, _seoSettings);
                dyn._Localized    = GetLocalized(ctx, x, y => y.Name);

                return(dyn);
            })
                                    .ToList();

            dynObject.ProductSpecificationAttributes = specificationAttributes
                                                       .Select(x => ToDynamic(x, ctx))
                                                       .ToList();

            if (product.ProductType == ProductType.BundledProduct)
            {
                var bundleItems = await ctx.ProductBatchContext.ProductBundleItems.GetOrLoadAsync(product.Id);

                dynObject.ProductBundleItems = bundleItems
                                               .Select(x =>
                {
                    dynamic dyn          = new DynamicEntity(x);
                    dyn.Name             = ctx.GetTranslation(x, nameof(x.Name), x.Name);
                    dyn.ShortDescription = ctx.GetTranslation(x, nameof(x.ShortDescription), x.ShortDescription);
                    dyn._Localized       = GetLocalized(ctx, x, y => y.Name, y => y.ShortDescription);

                    return(dyn);
                })
                                               .ToList();
            }

            var mediaFiles = await ApplyMediaFiles(dynObject, product, ctx, productContext);

            await ApplyExportFeatures(dynObject, product, price, mediaFiles, ctx, productContext);

            return(dynObject);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Applies extra data to an expando object.
        /// Export feature flags set by the export provider controls whether and what to be exported here.
        /// </summary>
        private async Task ApplyExportFeatures(
            dynamic dynObject,
            Product product,
            CalculatedPrice price,
            IEnumerable <ProductMediaFile> mediaFiles,
            DataExporterContext ctx,
            DynamicProductContext productContext)
        {
            if (ctx.Supports(ExportFeatures.CanProjectDescription))
            {
                await ApplyProductDescription(dynObject, product, ctx);
            }

            if (ctx.Supports(ExportFeatures.OffersBrandFallback))
            {
                string brand        = null;
                var    productManus = await ctx.ProductBatchContext.ProductManufacturers.GetOrLoadAsync(product.Id);

                if (productManus?.Any() ?? false)
                {
                    var manufacturer = productManus.First().Manufacturer;
                    brand = ctx.GetTranslation(manufacturer, nameof(manufacturer.Name), manufacturer.Name);
                }
                if (brand.IsEmpty())
                {
                    brand = ctx.Projection.Brand;
                }

                dynObject._Brand = brand;
            }

            if (ctx.Supports(ExportFeatures.CanIncludeMainPicture))
            {
                var imageQuery = ctx.Projection.PictureSize > 0 ? new ProcessImageQuery {
                    MaxWidth = ctx.Projection.PictureSize
                } : null;

                if (mediaFiles?.Any() ?? false)
                {
                    var file = _mediaService.ConvertMediaFile(mediaFiles.Select(x => x.MediaFile).First());

                    dynObject._MainPictureUrl         = _mediaService.GetUrl(file, imageQuery, ctx.Store.GetHost());
                    dynObject._MainPictureRelativeUrl = _mediaService.GetUrl(file, imageQuery);
                }
                else if (!_catalogSettings.HideProductDefaultPictures)
                {
                    // Get fallback image URL.
                    dynObject._MainPictureUrl         = _mediaService.GetUrl(null, imageQuery, ctx.Store.GetHost());
                    dynObject._MainPictureRelativeUrl = _mediaService.GetUrl(null, imageQuery);
                }
                else
                {
                    dynObject._MainPictureUrl         = null;
                    dynObject._MainPictureRelativeUrl = null;
                }
            }

            if (ctx.Supports(ExportFeatures.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty())
            {
                dynObject.ManufacturerPartNumber = product.Sku;
            }

            if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback))
            {
                dynamic deliveryTime = dynObject.DeliveryTime;
                dynObject._ShippingTime = deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name;
            }

            if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback))
            {
                dynObject._FreeShippingThreshold = ctx.Projection.FreeShippingThreshold;

                dynObject._ShippingCosts = product.IsFreeShipping || (ctx.Projection.FreeShippingThreshold.HasValue && (decimal)dynObject.Price >= ctx.Projection.FreeShippingThreshold.Value)
                    ? decimal.Zero
                    : ctx.Projection.ShippingCosts;
            }

            if (ctx.Supports(ExportFeatures.UsesOldPrice))
            {
                if (product.OldPrice != decimal.Zero && product.OldPrice != (decimal)dynObject.Price && !(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing))
                {
                    if (ctx.Projection.ConvertNetToGrossPrices)
                    {
                        var tax = await _taxCalculator.CalculateProductTaxAsync(product, product.OldPrice, true, ctx.ContextCustomer, ctx.ContextCurrency);

                        dynObject._OldPrice = tax.Price;
                    }
                    else
                    {
                        dynObject._OldPrice = product.OldPrice;
                    }
                }
                else
                {
                    dynObject._OldPrice = null;
                }
            }

            if (ctx.Supports(ExportFeatures.UsesSpecialPrice))
            {
                dynObject._SpecialPrice       = null;   // Special price which is valid now.
                dynObject._FutureSpecialPrice = null;   // Special price which is valid now and in future.
                dynObject._RegularPrice       = null;   // Price as if a special price would not exist.

                if (!(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing))
                {
                    if (price.OfferPrice.HasValue && product.SpecialPriceEndDateTimeUtc.HasValue)
                    {
                        var endDate = DateTime.SpecifyKind(product.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc);
                        if (endDate > DateTime.UtcNow)
                        {
                            dynObject._FutureSpecialPrice = price.OfferPrice.Value.Amount;
                        }
                    }

                    dynObject._SpecialPrice = price.OfferPrice?.Amount ?? null;

                    if (price.OfferPrice.HasValue || dynObject._FutureSpecialPrice != null)
                    {
                        var clonedOptions = ctx.PriceCalculationOptions.Clone();
                        clonedOptions.IgnoreOfferPrice = true;

                        var calculationContext = new PriceCalculationContext(product, clonedOptions);
                        calculationContext.AddSelectedAttributes(productContext?.Combination?.AttributeSelection, product.Id);
                        var priceWithoutOfferPrice = await _priceCalculationService.CalculatePriceAsync(calculationContext);

                        dynObject._RegularPrice = priceWithoutOfferPrice.FinalPrice.Amount;
                    }
                }
            }
        }
Exemplo n.º 20
0
 public CartPriceCalculated(ShoppingCart cart, PriceCalculationContext context)
 {
     CartId  = cart.Id;
     Context = context;
 }
Exemplo n.º 21
0
 public void Execute(PriceCalculationContext context)
 {
     // TODO: Calculate TAX
     var tax = 0m;
     context.Tax = tax;
 }