public virtual PriceCalculationOptions CreateDefaultOptions(
            bool forListing,
            Customer customer                = null,
            Currency targetCurrency          = null,
            ProductBatchContext batchContext = null)
        {
            customer ??= batchContext?.Customer ?? _workContext.CurrentCustomer;

            var store        = batchContext?.Store ?? _storeContext.CurrentStore;
            var language     = _workContext.WorkingLanguage;
            var priceDisplay = _catalogSettings.PriceDisplayType;
            var taxInclusive = _workContext.GetTaxDisplayTypeFor(customer, store.Id) == TaxDisplayType.IncludingTax;
            var determinePreselectedPrice = forListing && priceDisplay == PriceDisplayType.PreSelectedPrice;

            targetCurrency ??= _workContext.WorkingCurrency;
            batchContext ??= _productService.CreateProductBatchContext(null, store, customer, false);

            var options = new PriceCalculationOptions(batchContext, customer, store, language, targetCurrency)
            {
                IsGrossPrice = _taxSettings.PricesIncludeTax,
                TaxInclusive = taxInclusive,
                IgnorePercentageDiscountOnTierPrices = !_catalogSettings.ApplyPercentageDiscountOnTierPrice,
                IgnorePercentageTierPricesOnAttributePriceAdjustments = !_catalogSettings.ApplyTierPricePercentageToAttributePriceAdjustments,
                IgnoreDiscounts            = forListing && priceDisplay == PriceDisplayType.PriceWithoutDiscountsAndAttributes,
                DetermineLowestPrice       = forListing && priceDisplay == PriceDisplayType.LowestPrice,
                DeterminePreselectedPrice  = determinePreselectedPrice,
                ApplyPreselectedAttributes = determinePreselectedPrice,
                TaxFormat        = _currencyService.GetTaxFormat(null, taxInclusive, PricingTarget.Product, language),
                PriceRangeFormat = T("Products.PriceRangeFrom").Value,
                RoundingCurrency = targetCurrency == _primaryCurrency ? _workContext.WorkingCurrency : targetCurrency
            };

            return(options);
        }
Esempio n. 2
0
        /// <summary>
        /// Creates a new context instance for given <paramref name="cartItem"/> and <paramref name="options"/>.
        /// </summary>
        /// <param name="cartItem">Shopping cart item.</param>
        /// <param name="options">The calculation options.</param>
        public PriceCalculationContext(OrganizedShoppingCartItem cartItem, PriceCalculationOptions options)
            : this(cartItem?.Item?.Product, cartItem?.Item?.Quantity ?? 1, options)
        {
            Guard.NotNull(cartItem, nameof(cartItem));

            CartItem = cartItem;
        }
Esempio n. 3
0
        /// <summary>
        /// Creates a new context instance for given <paramref name="product"/>, <paramref name="quantity"/> and <paramref name="options"/>.
        /// </summary>
        /// <param name="product">The product to calculate price for.</param>
        /// <param name="quantity">The product quantity.</param>
        /// <param name="options">The calculation options.</param>
        public PriceCalculationContext(Product product, int quantity, PriceCalculationOptions options)
        {
            Guard.NotNull(options, nameof(options));

            Product  = product;
            Quantity = quantity;

            // Always work with a shallow copy of options
            Options = options.Clone();
        }
Esempio n. 4
0
        /// <summary>
        /// Calculates the unit price for a given shopping cart item.
        /// </summary>
        /// <param name="priceCalculationService">Price calculation service.</param>
        /// <param name="cartItem">Shopping cart item.</param>
        /// <param name="ignoreDiscounts">A value indicating whether to ignore discounts.</param>
        /// <param name="targetCurrency">The target currency to use for money conversion. Obtained from <see cref="IWorkContext.WorkingCurrency"/> if <c>null</c>.</param>
        /// <returns>Calculated unit price.</returns>
        //public static async Task<CalculatedPrice> CalculateUnitPriceAsync(
        //    this IPriceCalculationService2 priceCalculationService,
        //    OrganizedShoppingCartItem cartItem,
        //    bool ignoreDiscounts = false,
        //    Currency targetCurrency = null)
        //{
        //    Guard.NotNull(priceCalculationService, nameof(priceCalculationService));
        //    Guard.NotNull(cartItem, nameof(cartItem));

        //    var options = priceCalculationService.CreateDefaultOptions(false, cartItem.Item.Customer, targetCurrency);
        //    options.IgnoreDiscounts = ignoreDiscounts;

        //    var context = new PriceCalculationContext(cartItem, options);

        //    return await priceCalculationService.CalculatePriceAsync(context);
        //}

        /// <summary>
        /// Calculates both the unit price and the subtotal for a given shopping cart item.
        /// The subtotal is calculated by multiplying the unit price by <see cref="ShoppingCartItem.Quantity"/>.
        /// </summary>
        /// <param name="priceCalculationService">Price calculation service.</param>
        /// <param name="cartItem">Shopping cart item.</param>
        /// <param name="ignoreDiscounts">A value indicating whether to ignore discounts.</param>
        /// <param name="targetCurrency">The target currency to use for money conversion. Obtained from <see cref="IWorkContext.WorkingCurrency"/> if <c>null</c>.</param>
        /// <returns>Calculated subtotal.</returns>
        //public static async Task<(CalculatedPrice UnitPrice, CalculatedPrice Subtotal)> CalculateSubtotalAsync(
        //    this IPriceCalculationService2 priceCalculationService,
        //    OrganizedShoppingCartItem cartItem,
        //    bool ignoreDiscounts = false,
        //    Currency targetCurrency = null)
        //{
        //    Guard.NotNull(priceCalculationService, nameof(priceCalculationService));
        //    Guard.NotNull(cartItem, nameof(cartItem));

        //    var options = priceCalculationService.CreateDefaultOptions(false, cartItem.Item.Customer, targetCurrency);
        //    options.IgnoreDiscounts = ignoreDiscounts;

        //    var context = new PriceCalculationContext(cartItem, options);

        //    return await priceCalculationService.CalculateSubtotalAsync(context);
        //}

        /// <summary>
        /// Calculates the price adjustments of product attributes, usually <see cref="ProductVariantAttributeValue.PriceAdjustment"/>.
        /// Typically used to display price adjustments of selected attributes on the cart page.
        /// The calculated adjustment is always a unit price.
        /// </summary>
        /// <param name="priceCalculationService">Price calculation service.</param>
        /// <param name="product">The product.</param>
        /// <param name="selection">Attribute selection.</param>
        /// <param name="quantity">
        /// The product quantity. May have impact on the price, e.g. if tier prices are applied to price adjustments.
        /// Note that the calculated price is always the unit price.
        /// </param>
        /// <param name="options">Price calculation options. The default options are used if <c>null</c>.</param>
        /// <returns>Price adjustments of selected attributes. Key: <see cref="ProductVariantAttributeValue.Id"/>, value: attribute price adjustment.</returns>
        public static async Task <IDictionary <int, CalculatedPriceAdjustment> > CalculateAttributePriceAdjustmentsAsync(
            this IPriceCalculationService2 priceCalculationService,
            Product product,
            ProductVariantAttributeSelection selection,
            int quantity = 1,
            PriceCalculationOptions options = null)
        {
            Guard.NotNull(priceCalculationService, nameof(priceCalculationService));
            Guard.NotNull(selection, nameof(selection));

            options ??= priceCalculationService.CreateDefaultOptions(false);
            options.DeterminePriceAdjustments = true;

            var pricingContext = new PriceCalculationContext(product, quantity, options);

            pricingContext.AddSelectedAttributes(selection, product.Id);

            var price = await priceCalculationService.CalculatePriceAsync(pricingContext);

            return(price.AttributePriceAdjustments.ToDictionarySafe(x => x.AttributeValue.Id));
        }
        /// <summary>
        /// Gets the base price info for a product.
        /// </summary>
        /// <param name="priceCalculationService">Price calculation service.</param>
        /// <param name="product">The product to get the base price info for.</param>
        /// <param name="options">Price calculation options. The default options are used if <c>null</c>.</param>
        /// <returns>Base price info.</returns>
        public static async Task <string> GetBasePriceInfoAsync(this IPriceCalculationService priceCalculationService, Product product, PriceCalculationOptions options = null)
        {
            Guard.NotNull(priceCalculationService, nameof(priceCalculationService));
            Guard.NotNull(product, nameof(product));

            if (!product.BasePriceHasValue || product.BasePriceAmount == 0)
            {
                return(string.Empty);
            }

            options ??= priceCalculationService.CreateDefaultOptions(false);

            var context = new PriceCalculationContext(product, options);
            var price   = await priceCalculationService.CalculatePriceAsync(context);

            return(priceCalculationService.GetBasePriceInfo(product, price.FinalPrice, options.TargetCurrency));
        }
        public virtual async Task <PriceCalculationContext> CreateCalculationContextAsync(OrganizedShoppingCartItem cartItem, PriceCalculationOptions options)
        {
            Guard.NotNull(cartItem, nameof(cartItem));
            Guard.NotNull(options, nameof(options));

            var product = cartItem.Item.Product;
            var context = new PriceCalculationContext(product, cartItem.Item.Quantity, options)
            {
                CartItem = cartItem
            };

            // Include attributes selected for this cart item in price calculation.
            context.AddSelectedAttributes(cartItem);

            // Include bundle item data if the cart item is a bundle item.
            if (cartItem.Item.BundleItem != null)
            {
                context.BundleItem = cartItem.Item.BundleItem;
            }

            // Perf: we already have the bundle items of a bundled product. No need to load them again during calculation.
            if (cartItem.ChildItems?.Any() ?? false)
            {
                context.BundleItems = cartItem.ChildItems
                                      .Where(x => x.Item.BundleItem != null)
                                      .Select(x => x.Item.BundleItem)
                                      .ToList();
            }

            if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)
            {
                Guard.NotNull(cartItem.ChildItems, nameof(cartItem.ChildItems));

                foreach (var bundleItem in cartItem.ChildItems)
                {
                    await _productAttributeMaterializer.MergeWithCombinationAsync(bundleItem.Item.Product, bundleItem.Item.AttributeSelection);
                }
            }
            else
            {
                await _productAttributeMaterializer.MergeWithCombinationAsync(product, cartItem.Item.AttributeSelection);
            }

            return(context);
        }
Esempio n. 7
0
 /// <summary>
 /// Creates a new context instance for given <paramref name="product"/> and <paramref name="options"/>.
 /// </summary>
 /// <param name="product">The product to calculate price for.</param>
 /// <param name="options">The calculation options.</param>
 public PriceCalculationContext(Product product, PriceCalculationOptions options)
     : this(product, 1, options)
 {
 }