public void Execute(PriceCalculationContext context) { if (context.PaymentMethod != null) { context.PaymentMethodCost = context.PaymentMethod.GetPaymentMethodCost(context.Subtotal); } }
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); }
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); }
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); } }
public void Execute(PriceCalculationContext context) { // TODO: Calculate TAX var tax = 0m; context.Tax = tax; }
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; }
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; }
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; } } }
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); }
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); } }
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); }
/// <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); }
/// <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); }
/// <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; } } } }
public CartPriceCalculated(ShoppingCart cart, PriceCalculationContext context) { CartId = cart.Id; Context = context; }