/// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="overriddenProductPrice">Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscounts">Applied discounts</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product,
                                             Customer customer,
                                             decimal?overriddenProductPrice,
                                             decimal additionalCharge,
                                             bool includeDiscounts,
                                             int quantity,
                                             DateTime?rentalStartDate,
                                             DateTime?rentalEndDate,
                                             out decimal discountAmount,
                                             out List <DiscountForCaching> appliedDiscounts)
        {
            if (product == null)
            {
                throw new ArgumentNullException("product");
            }

            discountAmount   = decimal.Zero;
            appliedDiscounts = new List <DiscountForCaching>();


            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;

            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
            {
                cacheTime = 0;
            }
            var result = new ProductPriceForCaching();

            //initial price
            decimal price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;

            //tier prices
            var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);

            if (tierPrice != null)
            {
                price = tierPrice.Price;
            }

            //additional charge
            price = price + additionalCharge;

            //rental products
            if (product.IsRental)
            {
                if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                {
                    price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);
                }
            }

            if (includeDiscounts)
            {
                //discount
                List <DiscountForCaching> tmpAppliedDiscounts;
                decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
                price = price - tmpDiscountAmount;

                if (tmpAppliedDiscounts != null)
                {
                    result.AppliedDiscounts      = tmpAppliedDiscounts;
                    result.AppliedDiscountAmount = tmpDiscountAmount;
                }
            }

            if (price < decimal.Zero)
            {
                price = decimal.Zero;
            }

            result.Price = price;
            var cachedPrice = result;

            if (includeDiscounts)
            {
                if (cachedPrice.AppliedDiscounts.Any())
                {
                    appliedDiscounts.AddRange(cachedPrice.AppliedDiscounts);
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return(cachedPrice.Price);
        }
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscount">Applied discount</param>
        /// <returns>Final price</returns>
        public virtual async Task <(decimal finalPrice, decimal discountAmount, List <AppliedDiscount> appliedDiscounts, TierPrice preferredTierPrice)> GetFinalPrice(Product product,
                                                                                                                                                                      Customer customer,
                                                                                                                                                                      decimal additionalCharge,
                                                                                                                                                                      bool includeDiscounts,
                                                                                                                                                                      int quantity,
                                                                                                                                                                      DateTime?rentalStartDate,
                                                                                                                                                                      DateTime?rentalEndDate)
        {
            if (product == null)
            {
                throw new ArgumentNullException("product");
            }

            var discountAmount   = decimal.Zero;
            var appliedDiscounts = new List <AppliedDiscount>();

            async Task <ProductPriceForCaching> PrepareModel()
            {
                var result = new ProductPriceForCaching();

                //initial price
                decimal price = product.Price;

                //tier prices
                var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);

                if (tierPrice != null)
                {
                    price = tierPrice.Price;
                    result.PreferredTierPrice = tierPrice;
                }

                //customer product price
                if (_catalogSettings.CustomerProductPrice)
                {
                    var customerPrice = await _customerProductService.GetPriceByCustomerProduct(customer.Id, product.Id);

                    if (customerPrice.HasValue && customerPrice.Value < price)
                    {
                        price = customerPrice.Value;
                    }
                }

                //additional charge
                price = price + additionalCharge;

                //reservations
                if (product.ProductType == ProductType.Reservation)
                {
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                    {
                        decimal d = 0;
                        if (product.IncBothDate)
                        {
                            decimal.TryParse(((rentalEndDate - rentalStartDate).Value.TotalDays + 1).ToString(), out d);
                        }
                        else
                        {
                            decimal.TryParse((rentalEndDate - rentalStartDate).Value.TotalDays.ToString(), out d);
                        }
                        price = price * d;
                    }
                }

                if (includeDiscounts)
                {
                    //discount
                    var discountamount = await GetDiscountAmount(product, customer, price);

                    decimal tmpDiscountAmount = discountamount.discountAmount;
                    List <AppliedDiscount> tmpAppliedDiscounts = discountamount.appliedDiscounts;
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscounts != null)
                    {
                        result.AppliedDiscounts      = tmpAppliedDiscounts.ToList();
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                {
                    price = decimal.Zero;
                }

                //rounding
                if (_shoppingCartSettings.RoundPricesDuringCalculation)
                {
                    var primaryCurrency = await _currencyService.GetPrimaryExchangeRateCurrency();

                    result.Price = RoundingHelper.RoundPrice(price, primaryCurrency);
                }
                else
                {
                    result.Price = price;
                }

                return(result);
            }

            var modelprice = await PrepareModel();

            if (includeDiscounts)
            {
                appliedDiscounts = modelprice.AppliedDiscounts.ToList();
                if (appliedDiscounts.Any())
                {
                    discountAmount = modelprice.AppliedDiscountAmount;
                }
            }

            return(modelprice.Price, discountAmount, appliedDiscounts, modelprice.PreferredTierPrice);
        }
Example #3
0
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="overriddenProductPrice">Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscounts">Applied discounts</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product, 
            Customer customer,
            decimal? overriddenProductPrice,
            decimal additionalCharge, 
            bool includeDiscounts,
            int quantity,
            DateTime? rentalStartDate,
            DateTime? rentalEndDate,
            out decimal discountAmount,
            out List<Discount> appliedDiscounts)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            discountAmount = decimal.Zero;
            appliedDiscounts = new List<Discount>();

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                product.Id,
                overriddenProductPrice.HasValue ? overriddenProductPrice.Value.ToString(CultureInfo.InvariantCulture) : null,
                additionalCharge.ToString(CultureInfo.InvariantCulture),
                includeDiscounts, 
                quantity,
                string.Join(",", customer.GetCustomerRoleIds()),
                _storeContext.CurrentStore.Id);
             var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;
            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
                cacheTime = 0;
            var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            {
                var result = new ProductPriceForCaching();

                //initial price
                decimal price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;

                //special price
                var specialPrice = product.GetSpecialPrice();
                if (specialPrice.HasValue)
                    price = specialPrice.Value;

                //tier prices
                if (product.HasTierPrices)
                {
                    decimal? tierPrice = GetMinimumTierPrice(product, customer, quantity);
                    if (tierPrice.HasValue)
                        price = Math.Min(price, tierPrice.Value);
                }

                //additional charge
                price = price + additionalCharge;

                //rental products
                if (product.IsRental)
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                        price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);

                if (includeDiscounts)
                {
                    //discount
                    List<Discount> tmpAppliedDiscounts;
                    decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscounts != null)
                    {
                        result.AppliedDiscountIds = tmpAppliedDiscounts.Select(x=>x.Id).ToList();
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                    price = decimal.Zero;

                result.Price = price;
                return result;
            });

            if (includeDiscounts)
            {
                //Discount instance cannnot be cached between requests (when "catalogSettings.CacheProductPrices" is "true)
                //This is limitation of Entity Framework
                //That's why we load it here after working with cache
                foreach (var appliedDiscountId in cachedPrice.AppliedDiscountIds)
                {
                    var appliedDiscount = _discountService.GetDiscountById(appliedDiscountId);
                    if (appliedDiscount != null)
                        appliedDiscounts.Add(appliedDiscount);
                }
                if (appliedDiscounts.Any())
                {
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return cachedPrice.Price;
        }
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscount">Applied discount</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product,
                                             Customer customer,
                                             decimal additionalCharge,
                                             bool includeDiscounts,
                                             int quantity,
                                             DateTime?rentalStartDate,
                                             DateTime?rentalEndDate,
                                             out decimal discountAmount,
                                             out Discount appliedDiscount)
        {
            if (product == null)
            {
                throw new ArgumentNullException("product");
            }

            discountAmount  = decimal.Zero;
            appliedDiscount = null;

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                                         product.Id,
                                         additionalCharge.ToString(CultureInfo.InvariantCulture),
                                         includeDiscounts,
                                         quantity,
                                         string.Join(",", customer.CustomerRoles.Where(cr => cr.Active).Select(cr => cr.Id).ToList()),
                                         _storeContext.CurrentStore.Id);
            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;

            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
            {
                cacheTime = 0;
            }
            var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            {
                var result = new ProductPriceForCaching();

                //initial price
                decimal price = product.Price;

                //special price
                var specialPrice = product.GetSpecialPrice();
                if (specialPrice.HasValue)
                {
                    price = specialPrice.Value;
                }

                //tier prices
                if (product.HasTierPrices)
                {
                    decimal?tierPrice = GetMinimumTierPrice(product, customer, quantity);
                    if (tierPrice.HasValue)
                    {
                        price = Math.Min(price, tierPrice.Value);
                    }
                }

                //additional charge
                price = price + additionalCharge;

                //rental products
                if (product.IsRental)
                {
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                    {
                        price = price * GetRentalPeriods(product, rentalStartDate.Value, rentalEndDate.Value);
                    }
                }

                if (includeDiscounts)
                {
                    //discount
                    Discount tmpAppliedDiscount;
                    decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscount);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscount != null)
                    {
                        result.AppliedDiscountId     = tmpAppliedDiscount.Id;
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                {
                    price = decimal.Zero;
                }

                result.Price = price;
                return(result);
            });

            if (includeDiscounts)
            {
                //Discount instance cannnot be cached between requests (when "catalogSettings.CacheProductPrices" is "true)
                //This is limitation of Entity Framework
                //That's why we load it here after working with cache
                appliedDiscount = _discountService.GetDiscountById(cachedPrice.AppliedDiscountId);
                if (appliedDiscount != null)
                {
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return(cachedPrice.Price);
        }
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="overriddenProductPrice">Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscounts">Applied discounts</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product,
                                             Customer customer,
                                             decimal?overriddenProductPrice,
                                             decimal additionalCharge,
                                             bool includeDiscounts,
                                             int quantity,
                                             DateTime?rentalStartDate,
                                             DateTime?rentalEndDate,
                                             out decimal discountAmount,
                                             out List <DiscountForCaching> appliedDiscounts)
        {
            if (product == null)
            {
                throw new ArgumentNullException(nameof(product));
            }

            discountAmount   = decimal.Zero;
            appliedDiscounts = new List <DiscountForCaching>();

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                                         product.Id,
                                         overriddenProductPrice.HasValue ? overriddenProductPrice.Value.ToString(CultureInfo.InvariantCulture) : null,
                                         additionalCharge.ToString(CultureInfo.InvariantCulture),
                                         includeDiscounts,
                                         quantity,
                                         string.Join(",", customer.GetCustomerRoleIds()),
                                         _storeContext.CurrentStore.Id);
            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;

            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
            {
                cacheTime = 0;
            }
            var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            {
                var result = new ProductPriceForCaching();

                //initial price
                var price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;

                //tier prices
                var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);
                if (tierPrice != null)
                {
                    price = tierPrice.Price;
                }

                //additional charge
                price = price + additionalCharge;

                //rental products
                if (product.IsRental)
                {
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                    {
                        price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);
                    }
                }

                if (includeDiscounts)
                {
                    //discount
                    var tmpDiscountAmount = GetDiscountAmount(product, customer, price, out List <DiscountForCaching> tmpAppliedDiscounts);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscounts != null)
                    {
                        result.AppliedDiscounts      = tmpAppliedDiscounts;
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                {
                    price = decimal.Zero;
                }

                result.Price = price;
                return(result);
            });

            if (includeDiscounts)
            {
                if (cachedPrice.AppliedDiscounts.Any())
                {
                    appliedDiscounts.AddRange(cachedPrice.AppliedDiscounts);
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return(cachedPrice.Price);
        }
Example #6
0
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscount">Applied discount</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product,
                                             Customer customer,
                                             decimal additionalCharge,
                                             bool includeDiscounts,
                                             int quantity,
                                             DateTime?rentalStartDate,
                                             DateTime?rentalEndDate,
                                             out decimal discountAmount,
                                             out List <AppliedDiscount> appliedDiscounts)
        {
            if (product == null)
            {
                throw new ArgumentNullException("product");
            }

            discountAmount   = decimal.Zero;
            appliedDiscounts = new List <AppliedDiscount>();

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                                         product.Id,
                                         additionalCharge.ToString(CultureInfo.InvariantCulture),
                                         includeDiscounts,
                                         quantity,
                                         string.Join(",", customer.GetCustomerRoleIds()),
                                         _storeContext.CurrentStore.Id);
            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;

            //we do not cache price for reservation products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.ProductType == ProductType.Reservation)
            {
                cacheTime = 0;
            }

            ProductPriceForCaching PrepareModel()
            {
                var result = new ProductPriceForCaching();

                //initial price
                decimal price = product.Price;

                //customer product price
                var customerPrice = _customerService.GetPriceByCustomerProduct(customer.Id, product.Id);

                if (customerPrice.HasValue && customerPrice.Value < price)
                {
                    price = customerPrice.Value;
                }

                //tier prices
                var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);

                if (tierPrice != null)
                {
                    price = tierPrice.Price;
                }

                //additional charge
                price = price + additionalCharge;

                //reservations
                if (product.ProductType == ProductType.Reservation)
                {
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                    {
                        decimal d = 0;
                        if (product.IncBothDate)
                        {
                            decimal.TryParse(((rentalEndDate - rentalStartDate).Value.TotalDays + 1).ToString(), out d);
                        }
                        else
                        {
                            decimal.TryParse((rentalEndDate - rentalStartDate).Value.TotalDays.ToString(), out d);
                        }
                        price = price * d;
                    }
                }

                if (includeDiscounts)
                {
                    //discount
                    List <AppliedDiscount> tmpAppliedDiscounts;
                    decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscounts != null)
                    {
                        result.AppliedDiscountIds    = tmpAppliedDiscounts.Select(x => x.DiscountId).ToList();
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                {
                    price = decimal.Zero;
                }

                //rounding
                if (_shoppingCartSettings.RoundPricesDuringCalculation)
                {
                    var primaryCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryExchangeRateCurrencyId);
                    result.Price = RoundingHelper.RoundPrice(price, primaryCurrency);
                }
                else
                {
                    result.Price = price;
                }

                return(result);
            }

            var cachedPrice = cacheTime > 0 ? _cacheManager.Get(cacheKey, cacheTime, () => { return(PrepareModel()); }) : PrepareModel();

            if (includeDiscounts)
            {
                foreach (var appliedDiscountId in cachedPrice.AppliedDiscountIds)
                {
                    var appliedDiscount = _discountService.GetDiscountById(appliedDiscountId);
                    if (appliedDiscount != null)
                    {
                        appliedDiscounts.Add(new AppliedDiscount()
                        {
                            DiscountId = appliedDiscount.Id, IsCumulative = appliedDiscount.IsCumulative
                        });
                    }
                }
                if (appliedDiscounts.Any())
                {
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return(cachedPrice.Price);
        }
Example #7
0
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscount">Applied discount</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product,
                                             Customer customer,
                                             decimal additionalCharge,
                                             bool includeDiscounts,
                                             int quantity,
                                             DateTime?rentalStartDate,
                                             DateTime?rentalEndDate,
                                             out decimal discountAmount,
                                             out List <Discount> appliedDiscounts)
        {
            if (product == null)
            {
                throw new ArgumentNullException("product");
            }

            discountAmount   = decimal.Zero;
            appliedDiscounts = new List <Discount>();

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                                         product.Id,
                                         additionalCharge.ToString(CultureInfo.InvariantCulture),
                                         includeDiscounts,
                                         quantity,
                                         string.Join(",", customer.GetCustomerRoleIds()),
                                         _storeContext.CurrentStore.Id);
            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;

            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
            {
                cacheTime = 0;
            }



            //var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            //{
            var result = new ProductPriceForCaching();

            //initial price
            decimal price = product.Price;

            //tier prices
            var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);

            if (tierPrice != null)
            {
                price = tierPrice.Price;
            }

            //additional charge
            price = price + additionalCharge;

            //rental products
            if (product.IsRental)
            {
                if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                {
                    price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);
                }
            }

            if (false)    //includeDiscounts)
            {
                //discount
                List <Discount> tmpAppliedDiscounts;
                decimal         tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
                price = price - tmpDiscountAmount;

                if (tmpAppliedDiscounts != null)
                {
                    result.AppliedDiscountIds    = tmpAppliedDiscounts.Select(x => x.Id).ToList();
                    result.AppliedDiscountAmount = tmpDiscountAmount;
                }
            }

            if (price < decimal.Zero)
            {
                price = decimal.Zero;
            }

            result.Price = price;
            //return result;
            //});



            //rtl
            var cachedPrice = result;



            if (false)//includeDiscounts)
            {
                //Discount instance cannnot be cached between requests (when "catalogSettings.CacheProductPrices" is "true)
                //This is limitation of Entity Framework
                //That's why we load it here after working with cache
                foreach (var appliedDiscountId in cachedPrice.AppliedDiscountIds)
                {
                    var appliedDiscount = _discountService.GetDiscountById(appliedDiscountId);
                    if (appliedDiscount != null)
                    {
                        appliedDiscounts.Add(appliedDiscount);
                    }
                }
                if (appliedDiscounts.Any())
                {
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return(cachedPrice.Price);
        }
        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscount">Applied discount</param>
        /// <returns>Final price</returns>
        public virtual decimal GetFinalPrice(Product product, 
            Customer customer,
            decimal additionalCharge, 
            bool includeDiscounts,
            int quantity,
            out decimal discountAmount,
            out Discount appliedDiscount)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            discountAmount = decimal.Zero;
            appliedDiscount = null;

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                product.Id, 
                additionalCharge.ToString(CultureInfo.InvariantCulture),
                includeDiscounts, 
                quantity, 
                string.Join(",", customer.CustomerRoles.Where(cr => cr.Active).Select(cr => cr.Id).ToList()),
                _storeContext.CurrentStore.Id);
            var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;
            var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            {
                var result = new ProductPriceForCaching();

                //initial price
                decimal price = product.Price;

                //special price
                var specialPrice = GetSpecialPrice(product);
                if (specialPrice.HasValue)
                    price = specialPrice.Value;

                //tier prices
                if (product.HasTierPrices)
                {
                    decimal? tierPrice = GetMinimumTierPrice(product, customer, quantity);
                    if (tierPrice.HasValue)
                        price = Math.Min(price, tierPrice.Value);
                }

                //additional charge
                price = price + additionalCharge;

                if (includeDiscounts)
                {
                    //discount
                    Discount tmpAppliedDiscount = null;
                    decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscount);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscount != null)
                    {
                        result.AppliedDiscountId = tmpAppliedDiscount.Id;
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                    price = decimal.Zero;

                result.Price = price;
                return result;
            });

            if (includeDiscounts)
            {
                //Discount instance cannnot be cached between requests (when "catalogSettings.CacheProductPrices" is "true)
                //This is limitation of Entity Framework
                //That's why we load it here after working with cache
                appliedDiscount = _discountService.GetDiscountById(cachedPrice.AppliedDiscountId);
                if (appliedDiscount != null)
                {
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return cachedPrice.Price;
        }