/// <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.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>(); 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, DiscountId = dr.DiscountId, 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, DiscountId = req.DiscountId, }; 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="couponCodesToValidate">Coupon codes to validate</param> /// <returns>Discount validation result</returns> public virtual async Task <DiscountValidationResult> ValidateDiscount(Discount discount, Customer customer, string[] couponCodesToValidate) { if (discount == null) { throw new ArgumentNullException("discount"); } if (customer == null) { throw new ArgumentNullException("customer"); } var result = new DiscountValidationResult(); //check is enabled if (!discount.IsEnabled) { return(result); } //do not allow use discount in the current store if (discount.LimitedToStores && !discount.Stores.Any(x => _storeContext.CurrentStore.Id == x)) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedInStore"); return(result); } //check coupon code if (discount.RequiresCouponCode) { if (couponCodesToValidate == null || !couponCodesToValidate.Any()) { return(result); } var exists = false; foreach (var item in couponCodesToValidate) { if (discount.Reused) { if (await ExistsCodeInDiscount(item, discount.Id, null)) { result.CouponCode = item; exists = true; } } else { if (await ExistsCodeInDiscount(item, discount.Id, false)) { result.CouponCode = item; exists = true; } } } if (!exists) { 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(_shoppingCartSettings.CartsSharedBetweenStores, _storeContext.CurrentStore.Id) .ToList(); var hasGiftCards = cart.Any(x => x.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 = await GetAllDiscountUsageHistory(discount.Id, null, null, false, 0, 1); if (usedTimes.TotalCount >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerCustomer: { if (customer.IsRegistered()) { var usedTimes = await GetAllDiscountUsageHistory(discount.Id, customer.Id, null, false, 0, 1); if (usedTimes.TotalCount >= discount.LimitationTimes) { result.UserError = _localizationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return(result); } } } break; case DiscountLimitationType.Unlimited: default: break; } //discount requirements string keyReq = string.Format(DiscountRequirementEventConsumer.DISCOUNT_REQUIREMENT_MODEL_KEY, discount.Id); var requirements = await _cacheBase.GetAsync(keyReq, async() => { return(await Task.FromResult(discount.DiscountRequirements.ToList())); }); foreach (var req in requirements) { //load a plugin var discountRequirementPlugin = LoadDiscountPluginBySystemName(req.DiscountRequirementRuleSystemName); if (discountRequirementPlugin == null) { continue; } if (!_pluginFinder.AuthenticateStore(discountRequirementPlugin.PluginDescriptor, _storeContext.CurrentStore.Id)) { continue; } var ruleRequest = new DiscountRequirementValidationRequest { DiscountRequirementId = req.Id, DiscountId = req.DiscountId, Customer = customer, Store = _storeContext.CurrentStore }; var singleRequirementRule = discountRequirementPlugin.GetRequirementRules().Single(x => x.SystemName == req.DiscountRequirementRuleSystemName); var ruleResult = await singleRequirementRule.CheckRequirement(ruleRequest); if (!ruleResult.IsValid) { result.UserError = ruleResult.UserError; return(result); } } result.IsValid = true; return(result); // }); //return validationResult; }