/// <summary>
        /// Adds selected product attributes of a shopping cart item to be taken into account in the price calculation.
        /// For example required for price adjustments of attributes selected by the customer.
        /// Also adds selected attributes of bundle items if <see cref="Product.BundlePerItemPricing"/> is activated.
        /// </summary>
        /// <param name="context">The target product calculation context.</param>
        /// <param name="item">Shopping cart item.</param>
        public static void AddSelectedAttributes(this PriceCalculationContext context, OrganizedShoppingCartItem cartItem)
        {
            Guard.NotNull(context, nameof(context));

            if (cartItem != null)
            {
                var item = cartItem.Item;

                context.AddSelectedAttributes(item);

                if (item.Product.ProductType == ProductType.BundledProduct && item.Product.BundlePerItemPricing)
                {
                    cartItem.ChildItems.Each(x => context.AddSelectedAttributes(x.Item));
                }
            }
        }
 private static void AddSelectedAttributes(this PriceCalculationContext context, ShoppingCartItem item)
 {
     if (item != null)
     {
         context.AddSelectedAttributes(item.AttributeSelection, item.ProductId, item.BundleItemId);
     }
 }
        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. 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));
        }