Browse Source

Merge pull request #260 from EasyAbp/order-discount-uts

Fix a bug of the order discount and create UTs
pull/261/head
Super 3 years ago
committed by GitHub
parent
commit
067f32e2de
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountDistributor.cs
  2. 25
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountDistributor.cs
  3. 7
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountResolver.cs
  4. 18
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs
  5. 12
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountDistributorTests.cs
  6. 47
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountProviderTests.cs

4
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountDistributor.cs

@ -5,6 +5,6 @@ namespace EasyAbp.EShop.Orders.Orders;
public interface IOrderDiscountDistributor public interface IOrderDiscountDistributor
{ {
Task<OrderDiscountDistributionModel> DistributeAsync(IOrder order, Dictionary<IOrderLine, decimal> currentPrices, Task<OrderDiscountDistributionModel> DistributeAsync(IOrder order,
OrderDiscountInfoModel discount); Dictionary<IOrderLine, decimal> currentTotalPrices, OrderDiscountInfoModel discount);
} }

25
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountDistributor.cs

@ -11,16 +11,17 @@ namespace EasyAbp.EShop.Orders.Orders;
public class OrderDiscountDistributor : IOrderDiscountDistributor, ITransientDependency public class OrderDiscountDistributor : IOrderDiscountDistributor, ITransientDependency
{ {
public virtual Task<OrderDiscountDistributionModel> DistributeAsync(IOrder order, public virtual Task<OrderDiscountDistributionModel> DistributeAsync(IOrder order,
Dictionary<IOrderLine, decimal> currentPrices, OrderDiscountInfoModel discount) Dictionary<IOrderLine, decimal> currentTotalPrices, OrderDiscountInfoModel discount)
{ {
var affectedOrderLines = discount.AffectedOrderLineIds var affectedOrderLines = discount.AffectedOrderLineIds
.Select(orderLineId => order.OrderLines.Single(x => x.Id == orderLineId)) .Select(orderLineId => order.OrderLines.Single(x => x.Id == orderLineId))
.ToList(); .ToList();
var affectedOrderLinesCurrentPrice = var affectedOrderLinesCurrentTotalPrice =
new Money(affectedOrderLines.Sum(x => currentPrices[x]), order.Currency); new Money(affectedOrderLines.Sum(x => currentTotalPrices[x]), order.Currency);
var totalDiscountAmount = discount.CalculateDiscountAmount(affectedOrderLinesCurrentPrice.Amount, order.Currency); var totalDiscountAmount =
discount.CalculateDiscountAmount(affectedOrderLinesCurrentTotalPrice.Amount, order.Currency);
var distributions = new Dictionary<Guid, decimal>(); var distributions = new Dictionary<Guid, decimal>();
var remainingDiscountAmount = totalDiscountAmount; var remainingDiscountAmount = totalDiscountAmount;
@ -28,31 +29,31 @@ public class OrderDiscountDistributor : IOrderDiscountDistributor, ITransientDep
foreach (var orderLine in affectedOrderLines) foreach (var orderLine in affectedOrderLines)
{ {
var calculatedDiscountAmount = new Money( var calculatedDiscountAmount = new Money(
currentPrices[orderLine] / affectedOrderLinesCurrentPrice.Amount * currentTotalPrices[orderLine] / affectedOrderLinesCurrentTotalPrice.Amount *
totalDiscountAmount, order.Currency, MidpointRounding.ToZero); totalDiscountAmount, order.Currency, MidpointRounding.ToZero);
var discountAmount = calculatedDiscountAmount.Amount > currentPrices[orderLine] var discountAmount = calculatedDiscountAmount.Amount > currentTotalPrices[orderLine]
? currentPrices[orderLine] ? currentTotalPrices[orderLine]
: calculatedDiscountAmount.Amount; : calculatedDiscountAmount.Amount;
distributions[orderLine.Id] = discountAmount; distributions[orderLine.Id] = discountAmount;
currentPrices[orderLine] -= discountAmount; currentTotalPrices[orderLine] -= discountAmount;
remainingDiscountAmount -= discountAmount; remainingDiscountAmount -= discountAmount;
} }
foreach (var orderLine in affectedOrderLines.OrderByDescending(x => currentPrices[x])) foreach (var orderLine in affectedOrderLines.OrderByDescending(x => currentTotalPrices[x]))
{ {
if (remainingDiscountAmount == decimal.Zero) if (remainingDiscountAmount == decimal.Zero)
{ {
break; break;
} }
var discountAmount = remainingDiscountAmount > currentPrices[orderLine] var discountAmount = remainingDiscountAmount > currentTotalPrices[orderLine]
? currentPrices[orderLine] ? currentTotalPrices[orderLine]
: remainingDiscountAmount; : remainingDiscountAmount;
distributions[orderLine.Id] += discountAmount; distributions[orderLine.Id] += discountAmount;
currentPrices[orderLine] -= discountAmount; currentTotalPrices[orderLine] -= discountAmount;
remainingDiscountAmount -= discountAmount; remainingDiscountAmount -= discountAmount;
} }

7
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountResolver.cs

@ -55,8 +55,8 @@ public class OrderDiscountResolver : IOrderDiscountResolver, ITransientDependenc
var affectedOrderLineIdsInEffectGroup = new Dictionary<string, List<Guid>>(); var affectedOrderLineIdsInEffectGroup = new Dictionary<string, List<Guid>>();
var usedDiscountNameKeyPairs = new HashSet<(string, string)>(); var usedDiscountNameKeyPairs = new HashSet<(string, string)>();
var currentPrices = var currentTotalPrices =
new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => x, x => x.UnitPrice)); new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => x, x => x.TotalPrice));
var distributionModels = new List<OrderDiscountDistributionModel>(); var distributionModels = new List<OrderDiscountDistributionModel>();
@ -88,7 +88,8 @@ public class OrderDiscountResolver : IOrderDiscountResolver, ITransientDependenc
electionModel.TryEnqueue(newCandidateDiscounts); electionModel.TryEnqueue(newCandidateDiscounts);
} }
var distributionResult = await OrderDiscountDistributor.DistributeAsync(order, currentPrices, discount); var distributionResult =
await OrderDiscountDistributor.DistributeAsync(order, currentTotalPrices, discount);
distributionModels.Add(new OrderDiscountDistributionModel(discount, distributionResult.Distributions)); distributionModels.Add(new OrderDiscountDistributionModel(discount, distributionResult.Distributions));
} }

18
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs

@ -14,16 +14,24 @@ public class DemoOrderDiscountProvider : IOrderDiscountProvider
public Task DiscountAsync(OrderDiscountContext context) public Task DiscountAsync(OrderDiscountContext context)
{ {
var firstOrderLine = context.Order.OrderLines.First(); var orderLines = context.Order.OrderLines.ToList();
var firstOrderLine = orderLines[0];
var secondOrderLine = orderLines[1];
var models = new List<OrderDiscountInfoModel> var models = new List<OrderDiscountInfoModel>
{ {
new(new List<Guid> { firstOrderLine.Id }, null, "DemoDiscount1", "1", "Demo Discount 1", new(new List<Guid> { firstOrderLine.Id }, null, "DemoDiscount1", "1", "Demo Discount 1",
new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)), new DynamicDiscountAmountModel("USD", 5.00m, 0m, null)),
new(new List<Guid> { firstOrderLine.Id }, "A", "DemoDiscount2", "2", "Demo Discount 2", new(new List<Guid> { secondOrderLine.Id }, "A", "DemoDiscount2", "2", "Demo Discount 2",
new DynamicDiscountAmountModel("USD", 0.1m, 0m, null)), new DynamicDiscountAmountModel("USD", 0.10m, 0m, null)),
new(new List<Guid> { firstOrderLine.Id }, "A", "DemoDiscount3", "3", "Demo Discount 3", new(new List<Guid> { secondOrderLine.Id }, "A", "DemoDiscount3", "3", "Demo Discount 3",
new DynamicDiscountAmountModel("USD", 0.05m, 0m, null)), new DynamicDiscountAmountModel("USD", 0.05m, 0m, null)),
new(new List<Guid> { firstOrderLine.Id, secondOrderLine.Id }, "B", "DemoDiscount4", "4", "Demo Discount 4",
new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)),
new(new List<Guid> { firstOrderLine.Id, secondOrderLine.Id }, null, "DemoDiscount5", "5", "Demo Discount 5",
new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)),
new(new List<Guid> { firstOrderLine.Id }, "B", "DemoDiscount6", "6", "Demo Discount 6",
new DynamicDiscountAmountModel("USD", 0.00m, 0.55m, null)),
}; };
foreach (var model in models) foreach (var model in models)

12
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountDistributorTests.cs

@ -57,11 +57,11 @@ public class OrderDiscountDistributorTests : OrdersDomainTestBase
var discount = new OrderDiscountInfoModel(order.OrderLines.Select(x => x.Id).ToList(), null, "Test", null, var discount = new OrderDiscountInfoModel(order.OrderLines.Select(x => x.Id).ToList(), null, "Test", null,
null, new DynamicDiscountAmountModel("USD", 0m, discountRate, null)); null, new DynamicDiscountAmountModel("USD", 0m, discountRate, null));
var currentPrices = var currentTotalPrices =
new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => (IOrderLine)x, new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => (IOrderLine)x,
x => x.UnitPrice)); x => x.TotalPrice));
var distributionResult = await OrderDiscountDistributor.DistributeAsync(order, currentPrices, discount); var distributionResult = await OrderDiscountDistributor.DistributeAsync(order, currentTotalPrices, discount);
order.AddDiscounts(distributionResult); order.AddDiscounts(distributionResult);
@ -128,11 +128,11 @@ public class OrderDiscountDistributorTests : OrdersDomainTestBase
var discount = new OrderDiscountInfoModel(order.OrderLines.Select(x => x.Id).ToList(), null, "Test", null, var discount = new OrderDiscountInfoModel(order.OrderLines.Select(x => x.Id).ToList(), null, "Test", null,
null, new DynamicDiscountAmountModel("USD", discountedAmount, 0m, null)); null, new DynamicDiscountAmountModel("USD", discountedAmount, 0m, null));
var currentPrices = var currentTotalPrices =
new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => (IOrderLine)x, new Dictionary<IOrderLine, decimal>(order.OrderLines.ToDictionary(x => (IOrderLine)x,
x => x.UnitPrice)); x => x.TotalPrice));
var distributionResult = await OrderDiscountDistributor.DistributeAsync(order, currentPrices, discount); var distributionResult = await OrderDiscountDistributor.DistributeAsync(order, currentTotalPrices, discount);
order.AddDiscounts(distributionResult); order.AddDiscounts(distributionResult);

47
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountProviderTests.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NodaMoney;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
@ -25,7 +26,8 @@ public class OrderDiscountProviderTests : OrdersDomainTestBase
var createOrderInfoModel = new CreateOrderInfoModel(OrderTestData.Store1Id, null, var createOrderInfoModel = new CreateOrderInfoModel(OrderTestData.Store1Id, null,
new List<CreateOrderLineInfoModel> new List<CreateOrderLineInfoModel>
{ {
new(OrderTestData.Product1Id, OrderTestData.ProductSku1Id, 2) new(OrderTestData.Product1Id, OrderTestData.ProductSku1Id, 2),
new(OrderTestData.Product1Id, OrderTestData.ProductSku2Id, 1),
} }
); );
@ -42,7 +44,16 @@ public class OrderDiscountProviderTests : OrdersDomainTestBase
{ {
Id = OrderTestData.ProductSku1Id, Id = OrderTestData.ProductSku1Id,
AttributeOptionIds = new List<Guid>(), AttributeOptionIds = new List<Guid>(),
Price = 1m, Price = 100m,
Currency = "USD",
OrderMinQuantity = 1,
OrderMaxQuantity = 100,
},
new()
{
Id = OrderTestData.ProductSku2Id,
AttributeOptionIds = new List<Guid>(),
Price = 2m,
Currency = "USD", Currency = "USD",
OrderMinQuantity = 1, OrderMinQuantity = 1,
OrderMaxQuantity = 100, OrderMaxQuantity = 100,
@ -52,14 +63,32 @@ public class OrderDiscountProviderTests : OrdersDomainTestBase
} }
}, new Dictionary<Guid, DateTime>()); }, new Dictionary<Guid, DateTime>());
order.ActualTotalPrice.ShouldBe(1.89m); var orderLines = order.OrderLines;
order.TotalDiscount.ShouldBe(0.11m);
order.OrderDiscounts.Count.ShouldBe(2); const decimal orderLine1PriceWithoutDiscount = 2 * 100m;
const decimal orderLine2PriceWithoutDiscount = 1 * 2m;
var orderLine1ExpectedPrice = new Money((orderLine1PriceWithoutDiscount - 5m - 0.01m) * (1 - 0.55m), "USD",
MidpointRounding.AwayFromZero).Amount;
var orderLine2ExpectedPrice = orderLine2PriceWithoutDiscount - 0.1m;
order.ActualTotalPrice.ShouldBe(orderLine1ExpectedPrice + orderLine2ExpectedPrice);
order.TotalDiscount.ShouldBe(order.TotalPrice - order.ActualTotalPrice);
order.OrderDiscounts.Count.ShouldBe(5);
order.OrderDiscounts.ShouldContain(x =>
x.OrderId == order.Id && x.OrderLineId == orderLines[0].Id && x.Name == "DemoDiscount1" &&
x.Key == "1" && x.DisplayName == "Demo Discount 1" && x.DiscountedAmount == 5.00m);
order.OrderDiscounts.ShouldContain(x =>
x.OrderId == order.Id && x.OrderLineId == orderLines[1].Id && x.Name == "DemoDiscount2" &&
x.Key == "2" && x.DisplayName == "Demo Discount 2" && x.DiscountedAmount == 0.10m);
order.OrderDiscounts.ShouldContain(x =>
x.OrderId == order.Id && x.OrderLineId == orderLines[0].Id && x.Name == "DemoDiscount5" &&
x.Key == "5" && x.DisplayName == "Demo Discount 5" && x.DiscountedAmount == 0.01m);
order.OrderDiscounts.ShouldContain(x => order.OrderDiscounts.ShouldContain(x =>
x.OrderId == order.Id && x.OrderLineId == order.OrderLines.First().Id && x.Name == "DemoDiscount1" && x.OrderId == order.Id && x.OrderLineId == orderLines[1].Id && x.Name == "DemoDiscount5" &&
x.Key == "1" && x.DisplayName == "Demo Discount 1" && x.DiscountedAmount == 0.01m); x.Key == "5" && x.DisplayName == "Demo Discount 5" && x.DiscountedAmount == 0m);
order.OrderDiscounts.ShouldContain(x => order.OrderDiscounts.ShouldContain(x =>
x.OrderId == order.Id && x.OrderLineId == order.OrderLines.First().Id && x.Name == "DemoDiscount2" && x.OrderId == order.Id && x.OrderLineId == orderLines[0].Id && x.Name == "DemoDiscount6" &&
x.Key == "2" && x.DisplayName == "Demo Discount 2" && x.DiscountedAmount == 0.1m); x.Key == "6" && x.DisplayName == "Demo Discount 6" && x.DiscountedAmount == 107.24m);
} }
} }
Loading…
Cancel
Save