/// <summary> /// Formats the price /// </summary> /// <param name="price">Price</param> /// <param name="showCurrency">A value indicating whether to show a currency</param> /// <param name="targetCurrency">Target currency</param> /// <param name="language">Language</param> /// <param name="priceIncludesTax">A value indicating whether price includes tax</param> /// <param name="showTax">A value indicating whether to show tax suffix</param> /// <returns>Price</returns> public string FormatPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax) { //round before rendering //should we use RoundingHelper.RoundPrice here? price = RoundingHelper.RoundPrice(price, targetCurrency); string currencyString = GetCurrencyString(price, showCurrency, targetCurrency); if (showTax) { //show tax suffix string formatStr; if (priceIncludesTax) { formatStr = _localizationService.GetResource("Products.InclTaxSuffix", language.Id, false); if (String.IsNullOrEmpty(formatStr)) { formatStr = "{0} incl tax"; } } else { formatStr = _localizationService.GetResource("Products.ExclTaxSuffix", language.Id, false); if (String.IsNullOrEmpty(formatStr)) { formatStr = "{0} excl tax"; } } return(string.Format(formatStr, currencyString)); } return(currencyString); }
/// <summary> /// Gets the shopping cart unit price (one item) /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="quantity">Quantity</param> /// <param name="attributesXml">Product atrributes (XML format)</param> /// <param name="customerEnteredPrice">Customer entered price (if specified)</param> /// <param name="rentalStartDate">Rental start date (null for not rental products)</param> /// <param name="rentalEndDate">Rental end date (null for not rental products)</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for price computation</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Shopping cart unit price (one item)</returns> public virtual async Task <(decimal unitprice, decimal discountAmount, List <AppliedDiscount> appliedDiscounts)> GetUnitPrice(Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException("product"); } if (customer == null) { throw new ArgumentNullException("customer"); } var discountAmount = decimal.Zero; var appliedDiscounts = new List <AppliedDiscount>(); decimal?finalPrice = null; if (shoppingCartType == ShoppingCartType.Auctions && product.ProductType == ProductType.Auction) { finalPrice = customerEnteredPrice; } if (!finalPrice.HasValue) { var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { if (combination.OverriddenPrice.HasValue) { finalPrice = combination.OverriddenPrice.Value; } if (combination.TierPrices.Any()) { var storeId = _storeContext.CurrentStore.Id; var actualTierPrices = combination.TierPrices.Where(x => string.IsNullOrEmpty(x.StoreId) || x.StoreId == storeId) .Where(x => string.IsNullOrEmpty(x.CustomerRoleId) || customer.CustomerRoles.Where(role => role.Active).Select(role => role.Id).Contains(x.CustomerRoleId)).ToList(); var tierPrice = actualTierPrices.LastOrDefault(price => quantity >= price.Quantity); if (tierPrice != null) { finalPrice = tierPrice.Price; } } } } if (!finalPrice.HasValue) { //summarize price of all attributes decimal attributesTotalPrice = decimal.Zero; var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, attributesXml); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += await GetProductAttributeValuePriceAdjustment(attributeValue); } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = customer.ShoppingCartItems .Where(x => x.ProductId == product.Id) .Where(x => x.ShoppingCartType == shoppingCartType) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } var getfinalPrice = await GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, qty, product.ProductType == ProductType.Reservation?rentalStartDate : null, product.ProductType == ProductType.Reservation?rentalEndDate : null); finalPrice = getfinalPrice.finalPrice; discountAmount = getfinalPrice.discountAmount; appliedDiscounts = getfinalPrice.appliedDiscounts; } } if (!finalPrice.HasValue) { finalPrice = 0; } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { var primaryCurrency = await _currencyService.GetPrimaryExchangeRateCurrency(); finalPrice = RoundingHelper.RoundPrice(finalPrice.Value, primaryCurrency); } return(finalPrice.Value, discountAmount, appliedDiscounts); }
/// <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); }
/// <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); }
/// <summary> /// Gets the shopping cart unit price (one item) /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="quantity">Quantity</param> /// <param name="attributesXml">Product atrributes (XML format)</param> /// <param name="customerEnteredPrice">Customer entered price (if specified)</param> /// <param name="rentalStartDate">Rental start date (null for not rental products)</param> /// <param name="rentalEndDate">Rental end date (null for not rental products)</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for price computation</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Shopping cart unit price (one item)</returns> public virtual decimal GetUnitPrice(Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts, out decimal discountAmount, out List <Discount> appliedDiscounts) { if (product == null) { throw new ArgumentNullException("product"); } if (customer == null) { throw new ArgumentNullException("customer"); } discountAmount = decimal.Zero; appliedDiscounts = new List <Discount>(); decimal finalPrice; var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null && combination.OverriddenPrice.HasValue) { finalPrice = combination.OverriddenPrice.Value; } else { //summarize price of all attributes decimal attributesTotalPrice = decimal.Zero; var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, attributesXml); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += GetProductAttributeValuePriceAdjustment(attributeValue); } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = customer.ShoppingCartItems .Where(x => x.ProductId == product.Id) .Where(x => x.ShoppingCartType == shoppingCartType) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } finalPrice = GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, qty, product.IsRental ? rentalStartDate : null, product.IsRental ? rentalEndDate : null, out discountAmount, out appliedDiscounts); } } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { finalPrice = RoundingHelper.RoundPrice(finalPrice); } return(finalPrice); }