Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Grand.Business.Core.Interfaces.Catalog.Discounts;
using Grand.Business.Core.Queries.Catalog;
using Grand.Business.Core.Utilities.Catalog;
using Grand.Data;
using Grand.Domain.Customers;
Expand All @@ -8,23 +7,22 @@
using Grand.Domain.Orders;
using Grand.Domain.Stores;
using Grand.Infrastructure.Extensions;
using Grand.Mediator;

namespace Grand.Business.Catalog.Services.Discounts;

public class DiscountValidationService : IDiscountValidationService
{
private readonly IRepository<DiscountCoupon> _discountCouponRepository;
private readonly IRepository<DiscountUsageHistory> _discountUsageHistoryRepository;
private readonly IDiscountProviderLoader _discountProviderLoader;
private readonly IMediator _mediator;

public DiscountValidationService(
IDiscountProviderLoader discountProviderLoader,
IRepository<DiscountCoupon> discountCouponRepository,
IMediator mediator)
IRepository<DiscountUsageHistory> discountUsageHistoryRepository)
{
_discountCouponRepository = discountCouponRepository;
_mediator = mediator;
_discountUsageHistoryRepository = discountUsageHistoryRepository;
_discountProviderLoader = discountProviderLoader;
}

Expand Down Expand Up @@ -163,17 +161,15 @@ public virtual async Task<DiscountValidationResult> ValidateDiscount(Discount di
{
case DiscountLimitationType.NTimes:
{
var usedTimes = await _mediator.Send(new GetDiscountUsageHistoryQuery
{ DiscountId = discount.Id, PageSize = 1 });
if (usedTimes.TotalCount >= discount.LimitationTimes)
var usedTimes = await GetUsedTimes(discount.Id, null);
if (usedTimes >= discount.LimitationTimes)
return result;
}
break;
case DiscountLimitationType.NTimesPerUser:
{
var usedTimes = await _mediator.Send(new GetDiscountUsageHistoryQuery
{ DiscountId = discount.Id, CustomerId = customer.Id, PageSize = 1 });
if (usedTimes.TotalCount >= discount.LimitationTimes)
var usedTimes = await GetUsedTimes(discount.Id, customer.Id);
if (usedTimes >= discount.LimitationTimes)
{
result.UserErrorResource = "ShoppingCart.Discount.CannotBeUsedAnymore";
return result;
Expand Down Expand Up @@ -219,6 +215,18 @@ public virtual async Task<DiscountValidationResult> ValidateDiscount(Discount di
return result;
}

/// <summary>
/// Counts the usage history of a discount, of one customer when customerId is set
/// </summary>
private Task<int> GetUsedTimes(string discountId, string customerId)
{
var query = _discountUsageHistoryRepository.Table.Where(x => x.DiscountId == discountId);
if (!string.IsNullOrEmpty(customerId))
query = query.Where(x => x.CustomerId == customerId);

return _discountUsageHistoryRepository.CountAsync(query);
}

/// <summary>
/// Exist coupon code in discount
/// </summary>
Expand All @@ -237,7 +245,6 @@ public async Task<bool> ExistsCodeInDiscount(string couponCode, string discountI
if (used.HasValue)
query = query.Where(x => x.Used == used.Value);

var result = await _discountCouponRepository.ToListAsync(query);
return result.Count != 0;
return await _discountCouponRepository.AnyAsync(query);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public void InitializeTests()
_discountAmountProviders = new List<IDiscountAmountProvider> { new DiscountAmountProviderTests() };
_discountProviderLoader = new DiscountProviderLoader(_discountProviders, _discountAmountProviders);
_discountValidationService =
new DiscountValidationService(_discountProviderLoader, _discountCouponRepository, _mediatorMock.Object);
new DiscountValidationService(_discountProviderLoader, _discountCouponRepository,
_discountUsageHistoryRepository);
_dicountService = new DiscountService(_cacheBase, _repository, _discountCouponRepository,
_discountUsageHistoryRepository, _mediatorMock.Object, new AccessControlConfig());
handler = new GetDiscountUsageHistoryQueryHandler(_discountUsageHistoryRepository);
Expand Down Expand Up @@ -850,4 +851,100 @@ public void LoadDiscountAmountProvidersTest()
//Assert
Assert.HasCount(1, discountProviders);
}

[TestMethod]
public async Task ValidateDiscountTest_NTimes_InValid_WhenLimitReached()
{
//Arrange
var discount = await InsertLimitedDiscount(DiscountLimitationType.NTimes, 2);
await InsertUsage(discount.Id, "customer1");
await InsertUsage(discount.Id, "customer2");
//Act
var result = await _discountValidationService.ValidateDiscount(discount, new Customer(), new Store(),
new Currency { CurrencyCode = "USD" });
//Assert
Assert.IsFalse(result.IsValid);
}

[TestMethod]
public async Task ValidateDiscountTest_NTimes_Valid_BelowLimit()
{
//Arrange
var discount = await InsertLimitedDiscount(DiscountLimitationType.NTimes, 2);
await InsertUsage(discount.Id, "customer1");
await InsertUsage("otherDiscount", "customer1");
//Act
var result = await _discountValidationService.ValidateDiscount(discount, new Customer(), new Store(),
new Currency { CurrencyCode = "USD" });
//Assert
Assert.IsTrue(result.IsValid);
}

[TestMethod]
public async Task ValidateDiscountTest_NTimesPerUser_CountsOnlyTheCustomer()
{
//Arrange
var discount = await InsertLimitedDiscount(DiscountLimitationType.NTimesPerUser, 1);
var customer = new Customer();
await InsertUsage(discount.Id, "otherCustomer");
//Act
var valid = await _discountValidationService.ValidateDiscount(discount, customer, new Store(),
new Currency { CurrencyCode = "USD" });
await InsertUsage(discount.Id, customer.Id);
var used = await _discountValidationService.ValidateDiscount(discount, customer, new Store(),
new Currency { CurrencyCode = "USD" });
//Assert
Assert.IsTrue(valid.IsValid);
Assert.IsFalse(used.IsValid);
Assert.AreEqual("ShoppingCart.Discount.CannotBeUsedAnymore", used.UserErrorResource);
}

[TestMethod]
public async Task ValidateDiscountTest_NTimes_SeesUsageRecordedInTheScope()
{
//Arrange
var discount = await InsertLimitedDiscount(DiscountLimitationType.NTimes, 1);
//Act - order placement validates, records the usage, and the next order validates again
var first = await _discountValidationService.ValidateDiscount(discount, new Customer(), new Store(),
new Currency { CurrencyCode = "USD" });
await InsertUsage(discount.Id, "customer1");
var second = await _discountValidationService.ValidateDiscount(discount, new Customer(), new Store(),
new Currency { CurrencyCode = "USD" });
//Assert
Assert.IsTrue(first.IsValid);
Assert.IsFalse(second.IsValid);
}

[TestMethod]
public async Task ExistsCodeInDiscountTest()
{
//Arrange
await _discountCouponRepository.InsertAsync(new DiscountCoupon
{ CouponCode = "USED1", DiscountId = "discount1", Used = true });
//Act & Assert
Assert.IsTrue(await _discountValidationService.ExistsCodeInDiscount("USED1", "discount1", null));
Assert.IsTrue(await _discountValidationService.ExistsCodeInDiscount("USED1", "discount1", true));
Assert.IsFalse(await _discountValidationService.ExistsCodeInDiscount("USED1", "discount1", false));
Assert.IsFalse(await _discountValidationService.ExistsCodeInDiscount("USED1", "discount2", null));
Assert.IsFalse(await _discountValidationService.ExistsCodeInDiscount("", "discount1", null));
}

private async Task<Discount> InsertLimitedDiscount(DiscountLimitationType limitation, int times)
{
var discount = new Discount {
Name = "limited",
IsEnabled = true,
CurrencyCode = "USD",
DiscountLimitationId = limitation,
LimitationTimes = times
};
await _dicountService.InsertDiscount(discount);
return discount;
}

private Task InsertUsage(string discountId, string customerId)
{
return _discountUsageHistoryRepository.InsertAsync(new DiscountUsageHistory
{ DiscountId = discountId, CustomerId = customerId, OrderId = "order" });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public void StandardDiscountRules_DoNotDependBackOnPricing()
AddMocks(services, typeof(IContextAccessor), typeof(IProductService), typeof(IMediator),
typeof(ICurrencyService), typeof(IDiscountService), typeof(ICategoryService), typeof(IBrandService),
typeof(ICollectionService), typeof(IVendorService), typeof(IRepository<DiscountCoupon>),
typeof(IOrderService), typeof(ITranslationService), typeof(IRequestTranslationService));
typeof(IRepository<DiscountUsageHistory>), typeof(IOrderService), typeof(ITranslationService),
typeof(IRequestTranslationService));
services.AddSingleton(new ShoppingCartSettings());
services.AddSingleton(new CatalogSettings());

Expand All @@ -134,7 +135,8 @@ public void ShippingProviders_DoNotDependBackOnShipping()
typeof(IShippingMethodService), typeof(IShippingByWeightService), typeof(ICheckoutAttributeParser),
typeof(IContextAccessor), typeof(IProductService), typeof(IMediator), typeof(ICurrencyService),
typeof(IDiscountService), typeof(ICategoryService), typeof(IBrandService), typeof(ICollectionService),
typeof(IVendorService), typeof(IRepository<DiscountCoupon>), typeof(IOrderService),
typeof(IVendorService), typeof(IRepository<DiscountCoupon>), typeof(IRepository<DiscountUsageHistory>),
typeof(IOrderService),
typeof(ITranslationService), typeof(IRequestTranslationService));
services.AddSingleton(new ShippingProviderSettings());
services.AddSingleton(new ShippingSettings());
Expand Down
Loading