Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <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);
        }
Exemple #3
0
        /// <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;
        }
Exemple #5
0
        /// <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);
        }