/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodesToValidate">Coupon codes to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(Discount discount, Customer customer, string[] couponCodesToValidate) { if (discount == null) { throw new ArgumentNullException(nameof(discount)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (string.IsNullOrEmpty(discount.CouponCode)) { return(result); } if (couponCodesToValidate == null) { return(result); } if (!couponCodesToValidate.Any(x => x.Equals(discount.CouponCode, StringComparison.InvariantCultureIgnoreCase))) { return(result); } } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var shoppingCartService = EngineContext.Current.Resolve <IShoppingCartService>(); var cart = shoppingCartService.GetShoppingCart(customer, ShoppingCartType.ShoppingCart, storeId: _storeContext.CurrentStore.Id); var cartProductIds = cart.Select(ci => ci.ProductId).ToArray(); if (_productService.HasAnyGiftCardProduct(cartProductIds)) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards") }; return(result); } } //check date range var now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { var startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet") }; return(result); } } if (discount.EndDateUtc.HasValue) { var endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.Expired") }; return(result); } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerCustomer: { if (_customerService.IsRegistered(customer)) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore") }; return(result); } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements var key = _cacheKeyService.PrepareKeyForDefaultCache(NopDiscountDefaults.DiscountRequirementModelCacheKey, discount); var requirements = _staticCacheManager.Get(key, () => GetAllDiscountRequirements(discount.Id, true)); //get top-level group var topLevelGroup = requirements.FirstOrDefault(); if (topLevelGroup == null || (topLevelGroup.IsGroup && !GetDiscountRequirementsByParent(topLevelGroup).Any()) || !topLevelGroup.InteractionType.HasValue) { //there are no requirements, so discount is valid result.IsValid = true; return(result); } //requirements exist, let's check them var errors = new List <string>(); result.IsValid = GetValidationResult(requirements, topLevelGroup.InteractionType.Value, customer, errors); //set errors if result is not valid if (!result.IsValid) { result.Errors = errors; } return(result); }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodesToValidate">Coupon codes to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(DiscountForCaching discount, Customer customer, string[] couponCodesToValidate) { if (discount == null) { throw new ArgumentNullException(nameof(discount)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (string.IsNullOrEmpty(discount.CouponCode)) { return(result); } if (couponCodesToValidate == null) { return(result); } if (!couponCodesToValidate.Any(x => x.Equals(discount.CouponCode, StringComparison.InvariantCultureIgnoreCase))) { return(result); } } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.Product.IsGiftCard); if (hasGiftCards) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards") }; return(result); } } //check date range var now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { var startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet") }; return(result); } } if (discount.EndDateUtc.HasValue) { var endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.Expired") }; return(result); } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.Errors = new List <string> { _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore") }; return(result); } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements var key = string.Format(DiscountEventConsumer.DISCOUNT_REQUIREMENT_MODEL_KEY, discount.Id); var requirementsForCaching = _cacheManager.Get(key, () => { var requirements = GetAllDiscountRequirements(discount.Id, true); return(GetReqirementsForCaching(requirements)); }); //get top-level group var topLevelGroup = requirementsForCaching.FirstOrDefault(); if (topLevelGroup == null || (topLevelGroup.IsGroup && !topLevelGroup.ChildRequirements.Any()) || !topLevelGroup.InteractionType.HasValue) { //there are no requirements, so discount is valid result.IsValid = true; return(result); } //requirements exist, let's check them var errors = new List <string>(); result.IsValid = GetValidationResult(requirementsForCaching, topLevelGroup.InteractionType.Value, customer, errors); //set errors if result is not valid if (!result.IsValid) { result.Errors = errors; } return(result); }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodeToValidate">Coupon code to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(Discount discount, Customer customer, string couponCodeToValidate) { if (discount == null) { throw new ArgumentNullException("discount"); } if (customer == null) { throw new ArgumentNullException("customer"); } //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (String.IsNullOrEmpty(discount.CouponCode)) { return(result); } if (!discount.CouponCode.Equals(couponCodeToValidate, StringComparison.InvariantCultureIgnoreCase)) { return(result); } } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.Product.IsGiftCard); if (hasGiftCards) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards"); return(result); } } //check date range DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet"); return(result); } } if (discount.EndDateUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.Expired"); return(result); } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return(result); } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements //UNDONE we should inject static cache manager into constructor. we we already have "per request" cache manager injected. better way to do it? //we cache meta info of discount requirements. this way we should not load them for each HTTP request var staticCacheManager = EngineContext.Current.ContainerManager.Resolve <ICacheManager>("nop_cache_static"); string key = string.Format(DiscountRequirementEventConsumer.DISCOUNT_REQUIREMENT_MODEL_KEY, discount.Id); //var requirements = discount.DiscountRequirements; var requirements = staticCacheManager.Get(key, () => { var cachedRequirements = new List <DiscountRequirementForCaching>(); foreach (var dr in discount.DiscountRequirements) { cachedRequirements.Add(new DiscountRequirementForCaching { Id = dr.Id, SystemName = dr.DiscountRequirementRuleSystemName }); } return(cachedRequirements); }); foreach (var req in requirements) { //load a plugin var requirementRulePlugin = LoadDiscountRequirementRuleBySystemName(req.SystemName); if (requirementRulePlugin == null) { continue; } if (!_pluginFinder.AuthenticateStore(requirementRulePlugin.PluginDescriptor, _storeContext.CurrentStore.Id)) { continue; } var ruleRequest = new DiscountRequirementValidationRequest { DiscountRequirementId = req.Id, Customer = customer, Store = _storeContext.CurrentStore }; var ruleResult = requirementRulePlugin.CheckRequirement(ruleRequest); if (!ruleResult.IsValid) { result.UserError = ruleResult.UserError; return(result); } } result.IsValid = true; return(result); }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodeToValidate">Coupon code to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(Discount discount, Customer customer, string couponCodeToValidate) { if (discount == null) throw new ArgumentNullException("discount"); if (customer == null) throw new ArgumentNullException("customer"); //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (String.IsNullOrEmpty(discount.CouponCode)) return result; if (!discount.CouponCode.Equals(couponCodeToValidate, StringComparison.InvariantCultureIgnoreCase)) return result; } //check date range DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet"); return result; } } if (discount.EndDateUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.Expired"); return result; } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) return result; } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return result; } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements var requirements = discount.DiscountRequirements; foreach (var req in requirements) { var requirementRule = LoadDiscountRequirementRuleBySystemName(req.DiscountRequirementRuleSystemName); if (requirementRule == null) continue; if (!_pluginFinder.AuthenticateStore(requirementRule.PluginDescriptor, _storeContext.CurrentStore.Id)) continue; var ruleRequest = new DiscountRequirementValidationRequest { DiscountRequirement = req, Customer = customer, Store = _storeContext.CurrentStore }; var ruleResult = requirementRule.CheckRequirement(ruleRequest); if (!ruleResult.IsValid) { result.UserError = ruleResult.UserError; return result; } } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.Product.IsGiftCard); if (hasGiftCards) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards"); return result; } } result.IsValid = true; return result; }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodeToValidate">Coupon code to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(Discount discount, Customer customer, string couponCodeToValidate) { if (discount == null) throw new ArgumentNullException("discount"); if (customer == null) throw new ArgumentNullException("customer"); //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (String.IsNullOrEmpty(discount.CouponCode)) return result; if (!discount.CouponCode.Equals(couponCodeToValidate, StringComparison.InvariantCultureIgnoreCase)) return result; } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.Product.IsGiftCard); if (hasGiftCards) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards"); return result; } } //check date range DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet"); return result; } } if (discount.EndDateUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.Expired"); return result; } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) return result; } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return result; } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements //UNDONE we should inject static cache manager into constructor. we we already have "per request" cache manager injected. better way to do it? //we cache meta info of rdiscount requirements. this way we should not load them for each HTTP request var staticCacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>("nop_cache_static"); string key = string.Format(DiscountRequirementEventConsumer.DISCOUNT_REQUIREMENT_MODEL_KEY, discount.Id); //var requirements = discount.DiscountRequirements; var requirements = staticCacheManager.Get(key, () => { var cachedRequirements = new List<DiscountRequirementForCaching>(); foreach (var dr in discount.DiscountRequirements) cachedRequirements.Add(new DiscountRequirementForCaching { Id = dr.Id, SystemName = dr.DiscountRequirementRuleSystemName }); return cachedRequirements; }); foreach (var req in requirements) { //load a plugin var requirementRulePlugin = LoadDiscountRequirementRuleBySystemName(req.SystemName); if (requirementRulePlugin == null) continue; if (!_pluginFinder.AuthenticateStore(requirementRulePlugin.PluginDescriptor, _storeContext.CurrentStore.Id)) continue; var ruleRequest = new DiscountRequirementValidationRequest { DiscountRequirementId = req.Id, Customer = customer, Store = _storeContext.CurrentStore }; var ruleResult = requirementRulePlugin.CheckRequirement(ruleRequest); if (!ruleResult.IsValid) { result.UserError = ruleResult.UserError; return result; } } result.IsValid = true; return result; }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodeToValidate">Coupon code to validate</param> /// <returns>Discount validation result</returns> public virtual DiscountValidationResult ValidateDiscount(Discount discount, Customer customer, string couponCodeToValidate) { if (discount == null) { throw new ArgumentNullException("discount"); } if (customer == null) { throw new ArgumentNullException("customer"); } //invalid by default var result = new DiscountValidationResult(); //check coupon code if (discount.RequiresCouponCode) { if (String.IsNullOrEmpty(discount.CouponCode)) { return(result); } if (!discount.CouponCode.Equals(couponCodeToValidate, StringComparison.InvariantCultureIgnoreCase)) { return(result); } } //check date range DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.NotStartedYet"); return(result); } } if (discount.EndDateUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.Expired"); return(result); } } //discount limitation switch (discount.DiscountLimitation) { case DiscountLimitationType.NTimesOnly: { var usedTimes = GetAllDiscountUsageHistory(discount.Id, null, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = GetAllDiscountUsageHistory(discount.Id, customer.Id, null, 0, 1).TotalCount; if (usedTimes >= discount.LimitationTimes) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return(result); } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements var requirements = discount.DiscountRequirements; foreach (var req in requirements) { var requirementRule = LoadDiscountRequirementRuleBySystemName(req.DiscountRequirementRuleSystemName); if (requirementRule == null) { continue; } if (!_pluginFinder.AuthenticateStore(requirementRule.PluginDescriptor, _storeContext.CurrentStore.Id)) { continue; } var ruleRequest = new DiscountRequirementValidationRequest { DiscountRequirement = req, Customer = customer, Store = _storeContext.CurrentStore }; var ruleResult = requirementRule.CheckRequirement(ruleRequest); if (!ruleResult.IsValid) { result.UserError = ruleResult.UserError; return(result); } } //Do not allow discounts applied to order subtotal or total when a customer has gift cards in the cart. //Otherwise, this customer can purchase gift cards with discount and get more than paid ("free money"). if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal || discount.DiscountType == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.Product.IsGiftCard); if (hasGiftCards) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftCards"); return(result); } } result.IsValid = true; return(result); }