/// <summary> /// Gets allowed discounts applied to manufacturers /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <returns>Discounts</returns> protected virtual IList<Discount> GetAllowedDiscountsAppliedToManufacturers(Product product, Customer customer) { var allowedDiscounts = new List<Discount>(); if (_catalogSettings.IgnoreDiscounts) return allowedDiscounts; foreach (var discount in _discountService.GetAllDiscounts(DiscountType.AssignedToManufacturers)) { //load identifier of categories with this discount applied to var cacheKey = string.Format(PriceCacheEventConsumer.DISCOUNT_MANUFACTURER_IDS_MODEL_KEY, discount.Id, string.Join(",", customer.GetCustomerRoleIds()), _storeContext.CurrentStore.Id); var appliedToManufacturerIds = _cacheManager.Get(cacheKey, () => discount.AppliedToManufacturers.Select(x => x.Id).ToList()); //compare with manufacturers of this product if (appliedToManufacturerIds.Any()) { //load identifier of categories with this discount applied to var cacheKey2 = string.Format(PriceCacheEventConsumer.DISCOUNT_PRODUCT_MANUFACTURER_IDS_MODEL_KEY, product.Id, string.Join(",", customer.GetCustomerRoleIds()), _storeContext.CurrentStore.Id); var manufacturerIds = _cacheManager.Get(cacheKey2, () => _manufacturerService .GetProductManufacturersByProductId(product.Id) .Select(x => x.ManufacturerId) .ToList()); foreach (var id in manufacturerIds) { if (appliedToManufacturerIds.Contains(id)) { if (_discountService.ValidateDiscount(discount, customer).IsValid && discount.DiscountType == DiscountType.AssignedToManufacturers && !allowedDiscounts.ContainsDiscount(discount)) allowedDiscounts.Add(discount); } } } } return allowedDiscounts; }
/// <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 allowed discounts applied to categories /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <returns>Discounts</returns> protected virtual IList<Discount> GetAllowedDiscountsAppliedToCategories(Product product, Customer customer) { var allowedDiscounts = new List<Discount>(); if (_catalogSettings.IgnoreDiscounts) return allowedDiscounts; foreach (var discount in _discountService.GetAllDiscounts(DiscountType.AssignedToCategories)) { //load identifier of categories with this discount applied to var cacheKey = string.Format(PriceCacheEventConsumer.DISCOUNT_CATEGORY_IDS_MODEL_KEY, discount.Id, string.Join(",", customer.GetCustomerRoleIds()), _storeContext.CurrentStore.Id); var appliedToCategoryIds = _cacheManager.Get(cacheKey, () => { var categoryIds = new List<int>(); foreach (var category in discount.AppliedToCategories) { if (!categoryIds.Contains(category.Id)) categoryIds.Add(category.Id); if (discount.AppliedToSubCategories) { //include subcategories foreach (var childCategoryId in _categoryService .GetAllCategoriesByParentCategoryId(category.Id, false, true) .Select(x => x.Id)) { if (!categoryIds.Contains(childCategoryId)) categoryIds.Add(childCategoryId); } } } return categoryIds; }); //compare with categories of this product if (appliedToCategoryIds.Any()) { //load identifier of categories with this discount applied to var cacheKey2 = string.Format(PriceCacheEventConsumer.DISCOUNT_PRODUCT_CATEGORY_IDS_MODEL_KEY, product.Id, string.Join(",", customer.GetCustomerRoleIds()), _storeContext.CurrentStore.Id); var categoryIds = _cacheManager.Get(cacheKey2, () => _categoryService .GetProductCategoriesByProductId(product.Id) .Select(x => x.CategoryId) .ToList()); foreach (var id in categoryIds) { if (appliedToCategoryIds.Contains(id)) { if (_discountService.ValidateDiscount(discount, customer).IsValid && discount.DiscountType == DiscountType.AssignedToCategories && !allowedDiscounts.ContainsDiscount(discount)) allowedDiscounts.Add(discount); } } } } return allowedDiscounts; }