/// <summary> /// Check discount requirement /// </summary> /// <param name="request">Object that contains all information required to check the requirement (Current customer, discount, etc)</param> /// <returns>true - requirement is met; otherwise, false</returns> public async Task <DiscountRuleValidationResult> CheckRequirement(DiscountRuleValidationRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } //invalid by default var result = new DiscountRuleValidationResult(); if (request.Customer == null) { return(result); } var restrictedToCustomerGroupId = _settingService.GetSettingByKey <RequirementCustomerGroup>(string.Format("DiscountRules.Standard.MustBeAssignedToCustomerGroup-{0}-{1}", request.DiscountId, request.DiscountRequirementId)); if (restrictedToCustomerGroupId == null || String.IsNullOrEmpty(restrictedToCustomerGroupId?.CustomerGroupId)) { return(result); } foreach (var customerGroup in request.Customer.Groups.ToList()) { if (restrictedToCustomerGroupId.CustomerGroupId == customerGroup) { //valid result.IsValid = true; return(result); } } return(await Task.FromResult(result)); }
/// <summary> /// Check discount requirement /// </summary> /// <param name="request">Object that contains all information required to check the requirement (Current customer, discount, etc)</param> /// <returns>true - requirement is met; otherwise, false</returns> public async Task <DiscountRuleValidationResult> CheckRequirement(DiscountRuleValidationRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var result = new DiscountRuleValidationResult(); var spentAmountRequirement = _settingService.GetSettingByKey <RequirementSpentAmount>(string.Format("DiscountRequirement.ShoppingCart-{0}", request.DiscountRequirementId)); if (spentAmountRequirement == null || spentAmountRequirement.SpentAmount == 0) { result.IsValid = true; return(result); } var cart = _workContext.CurrentCustomer.ShoppingCartItems .Where(sci => sci.ShoppingCartTypeId == ShoppingCartType.ShoppingCart) .LimitPerStore(_shoppingCartSettings.SharedCartBetweenStores, request.Store.Id) .ToList(); if (cart.Count == 0) { result.IsValid = false; return(result); } double spentAmount = 0; var priceCalculationService = _serviceProvider.GetRequiredService <IPricingService>(); foreach (var ca in cart) { bool calculateWithDiscount = false; var product = await _productService.GetProductById(ca.ProductId); if (product != null) { spentAmount += (await priceCalculationService.GetSubTotal(ca, product, calculateWithDiscount)).subTotal; } } result.IsValid = spentAmount > spentAmountRequirement.SpentAmount; return(result); }
/// <summary> /// Check discount requirement /// </summary> /// <param name="request">Object that contains all information required to check the requirement (Current customer, discount, etc)</param> /// <returns>true - requirement is met; otherwise, false</returns> public async Task <DiscountRuleValidationResult> CheckRequirement(DiscountRuleValidationRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } //invalid by default var result = new DiscountRuleValidationResult(); var spentAmountRequirement = _settingService.GetSettingByKey <RequirementHadSpentAmount>(string.Format("DiscountRules.Standard.HadSpentAmount-{0}-{1}", request.DiscountId, request.DiscountRequirementId)); if (spentAmountRequirement == null || spentAmountRequirement.SpentAmount == 0) { //valid result.IsValid = true; return(result); } if (request.Customer == null) { return(result); } var orders = await _orderService.SearchOrders(storeId : request.Store.Id, customerId : request.Customer.Id, os : (int)OrderStatusSystem.Complete); double spentAmount = orders.Sum(o => o.OrderTotal); if (spentAmount > spentAmountRequirement.SpentAmount) { result.IsValid = true; } else { result.UserError = _translationService.GetResource("Plugins.DiscountRules.Standard.HadSpentAmount.NotEnough"); } return(result); }
/// <summary> /// Check discount requirement /// </summary> /// <param name="request">Object that contains all information required to check the requirement (Current customer, discount, etc)</param> /// <returns>true - requirement is met; otherwise, false</returns> public async Task <DiscountRuleValidationResult> CheckRequirement(DiscountRuleValidationRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } //invalid by default var result = new DiscountRuleValidationResult(); var restrictedProductIds = _settingService.GetSettingByKey <RequirementProducts>(string.Format("DiscountRules.Standard.RestrictedProductIds-{0}-{1}", request.DiscountId, request.DiscountRequirementId)); if (restrictedProductIds == null || !restrictedProductIds.Products.Any()) { //valid result.IsValid = true; return(result); } if (request.Customer == null) { return(result); } //group products in the cart by product ID //it could be the same product with distinct product attributes //that's why we get the total quantity of this product var cartQuery = from sci in request.Customer.ShoppingCartItems.LimitPerStore(_shoppingCartSettings.SharedCartBetweenStores, request.Store.Id) where sci.ShoppingCartTypeId == ShoppingCartType.ShoppingCart group sci by sci.ProductId into g select new { ProductId = g.Key, TotalQuantity = g.Sum(x => x.Quantity) }; var cart = cartQuery.ToList(); bool allFound = true; foreach (var restrictedProduct in restrictedProductIds.Products.Select(x => x.Trim())) { if (String.IsNullOrWhiteSpace(restrictedProduct)) { continue; } bool found1 = false; foreach (var sci in cart) { if (restrictedProduct.Contains(":")) { if (restrictedProduct.Contains("-")) { //the third way (the quantity rage specified) //{Product ID}:{Min quantity}-{Max quantity}. For example, 77:1-3, 123:2-5, 156:3-8 string restrictedProductId = restrictedProduct.Split(new[] { ':' })[0]; int quantityMin; if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[0], out quantityMin)) { //parsing error; exit; return(result); } int quantityMax; if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[1], out quantityMax)) { //parsing error; exit; return(result); } if (sci.ProductId == restrictedProductId && quantityMin <= sci.TotalQuantity && sci.TotalQuantity <= quantityMax) { found1 = true; break; } } else { //the second way (the quantity specified) //{Product ID}:{Quantity}. For example, 77:1, 123:2, 156:3 string restrictedProductId = restrictedProduct.Split(new[] { ':' })[0]; int quantity; if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1], out quantity)) { //parsing error; exit; return(result); } if (sci.ProductId == restrictedProductId && sci.TotalQuantity == quantity) { found1 = true; break; } } } else { //the first way (the quantity is not specified) if (sci.ProductId == restrictedProduct) { found1 = true; break; } } } if (!found1) { allFound = false; break; } } if (allFound) { //valid result.IsValid = true; return(result); } return(await Task.FromResult(result)); }
/// <summary> /// Validate discount /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="currency">Currency</param> /// <param name="couponCodesToValidate">Coupon codes</param> /// <returns>Discount validation result</returns> public virtual async Task <DiscountValidationResult> ValidateDiscount(Discount discount, Customer customer, Currency currency, string[] couponCodesToValidate) { if (discount == null) { throw new ArgumentNullException(nameof(discount)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } var result = new DiscountValidationResult(); //is enabled and use the same currency if (!discount.IsEnabled || discount.CurrencyCode != currency.CurrencyCode) { return(result); } //do not allow use discount in the current store if (discount.LimitedToStores && !discount.Stores.Any(x => _workContext.CurrentStore.Id == x)) { result.UserError = _translationService.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); } } if (discount.DiscountTypeId == DiscountType.AssignedToOrderSubTotal || discount.DiscountTypeId == DiscountType.AssignedToOrderTotal) { var cart = customer.ShoppingCartItems .Where(sci => sci.ShoppingCartTypeId == ShoppingCartType.ShoppingCart) .ToList(); var hasGiftVouchers = cart.Any(x => x.IsGiftVoucher); if (hasGiftVouchers) { result.UserError = _translationService.GetResource("ShoppingCart.Discount.CannotBeUsedWithGiftVouchers"); return(result); } } //time range check DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) { result.UserError = _translationService.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 = _translationService.GetResource("ShoppingCart.Discount.Expired"); return(result); } } //discount limitation - n times and n times per user switch (discount.DiscountLimitationId) { case DiscountLimitationType.NTimes: { var usedTimes = await GetAllDiscountUsageHistory(discount.Id, null, null, false, 0, 1); if (usedTimes.TotalCount >= discount.LimitationTimes) { return(result); } } break; case DiscountLimitationType.NTimesPerUser: { var usedTimes = await GetAllDiscountUsageHistory(discount.Id, customer.Id, null, false, 0, 1); if (usedTimes.TotalCount >= discount.LimitationTimes) { result.UserError = _translationService.GetResource("ShoppingCart.Discount.CannotBeUsedAnymore"); return(result); } } break; case DiscountLimitationType.Nolimits: default: break; } //discount requirements var requirements = discount.DiscountRules.ToList(); foreach (var req in requirements) { //load a plugin var discountRequirementPlugin = LoadDiscountProviderBySystemName(req.DiscountRequirementRuleSystemName); if (discountRequirementPlugin == null) { continue; } if (!discountRequirementPlugin.IsAuthenticateStore(_workContext.CurrentStore)) { continue; } var ruleRequest = new DiscountRuleValidationRequest { DiscountRequirementId = req.Id, DiscountId = req.DiscountId, Customer = customer, Store = _workContext.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); }