diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountDistributor.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountDistributor.cs index 1d196908..f9421701 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountDistributor.cs +++ b/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 { - Task DistributeAsync(IOrder order, Dictionary currentPrices, - OrderDiscountInfoModel discount); + Task DistributeAsync(IOrder order, + Dictionary currentTotalPrices, OrderDiscountInfoModel discount); } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountDistributor.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountDistributor.cs index fdfea633..fb8401df 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountDistributor.cs +++ b/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 virtual Task DistributeAsync(IOrder order, - Dictionary currentPrices, OrderDiscountInfoModel discount) + Dictionary currentTotalPrices, OrderDiscountInfoModel discount) { var affectedOrderLines = discount.AffectedOrderLineIds .Select(orderLineId => order.OrderLines.Single(x => x.Id == orderLineId)) .ToList(); - var affectedOrderLinesCurrentPrice = - new Money(affectedOrderLines.Sum(x => currentPrices[x]), order.Currency); + var affectedOrderLinesCurrentTotalPrice = + 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(); var remainingDiscountAmount = totalDiscountAmount; @@ -28,31 +29,31 @@ public class OrderDiscountDistributor : IOrderDiscountDistributor, ITransientDep foreach (var orderLine in affectedOrderLines) { var calculatedDiscountAmount = new Money( - currentPrices[orderLine] / affectedOrderLinesCurrentPrice.Amount * + currentTotalPrices[orderLine] / affectedOrderLinesCurrentTotalPrice.Amount * totalDiscountAmount, order.Currency, MidpointRounding.ToZero); - var discountAmount = calculatedDiscountAmount.Amount > currentPrices[orderLine] - ? currentPrices[orderLine] + var discountAmount = calculatedDiscountAmount.Amount > currentTotalPrices[orderLine] + ? currentTotalPrices[orderLine] : calculatedDiscountAmount.Amount; distributions[orderLine.Id] = discountAmount; - currentPrices[orderLine] -= discountAmount; + currentTotalPrices[orderLine] -= 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) { break; } - var discountAmount = remainingDiscountAmount > currentPrices[orderLine] - ? currentPrices[orderLine] + var discountAmount = remainingDiscountAmount > currentTotalPrices[orderLine] + ? currentTotalPrices[orderLine] : remainingDiscountAmount; distributions[orderLine.Id] += discountAmount; - currentPrices[orderLine] -= discountAmount; + currentTotalPrices[orderLine] -= discountAmount; remainingDiscountAmount -= discountAmount; } diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountResolver.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountResolver.cs index 34f49eb7..86a32a6a 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountResolver.cs +++ b/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>(); var usedDiscountNameKeyPairs = new HashSet<(string, string)>(); - var currentPrices = - new Dictionary(order.OrderLines.ToDictionary(x => x, x => x.UnitPrice)); + var currentTotalPrices = + new Dictionary(order.OrderLines.ToDictionary(x => x, x => x.TotalPrice)); var distributionModels = new List(); @@ -88,7 +88,8 @@ public class OrderDiscountResolver : IOrderDiscountResolver, ITransientDependenc 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)); } diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs index a53ed2d4..a61e9f06 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs +++ b/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) { - var firstOrderLine = context.Order.OrderLines.First(); + var orderLines = context.Order.OrderLines.ToList(); + var firstOrderLine = orderLines[0]; + var secondOrderLine = orderLines[1]; var models = new List { new(new List { firstOrderLine.Id }, null, "DemoDiscount1", "1", "Demo Discount 1", - new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)), - new(new List { firstOrderLine.Id }, "A", "DemoDiscount2", "2", "Demo Discount 2", - new DynamicDiscountAmountModel("USD", 0.1m, 0m, null)), - new(new List { firstOrderLine.Id }, "A", "DemoDiscount3", "3", "Demo Discount 3", + new DynamicDiscountAmountModel("USD", 5.00m, 0m, null)), + new(new List { secondOrderLine.Id }, "A", "DemoDiscount2", "2", "Demo Discount 2", + new DynamicDiscountAmountModel("USD", 0.10m, 0m, null)), + new(new List { secondOrderLine.Id }, "A", "DemoDiscount3", "3", "Demo Discount 3", new DynamicDiscountAmountModel("USD", 0.05m, 0m, null)), + new(new List { firstOrderLine.Id, secondOrderLine.Id }, "B", "DemoDiscount4", "4", "Demo Discount 4", + new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)), + new(new List { firstOrderLine.Id, secondOrderLine.Id }, null, "DemoDiscount5", "5", "Demo Discount 5", + new DynamicDiscountAmountModel("USD", 0.01m, 0m, null)), + new(new List { firstOrderLine.Id }, "B", "DemoDiscount6", "6", "Demo Discount 6", + new DynamicDiscountAmountModel("USD", 0.00m, 0.55m, null)), }; foreach (var model in models) diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountDistributorTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountDistributorTests.cs index 0a88fc75..804f9874 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountDistributorTests.cs +++ b/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, null, new DynamicDiscountAmountModel("USD", 0m, discountRate, null)); - var currentPrices = + var currentTotalPrices = new Dictionary(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); @@ -128,11 +128,11 @@ public class OrderDiscountDistributorTests : OrdersDomainTestBase var discount = new OrderDiscountInfoModel(order.OrderLines.Select(x => x.Id).ToList(), null, "Test", null, null, new DynamicDiscountAmountModel("USD", discountedAmount, 0m, null)); - var currentPrices = + var currentTotalPrices = new Dictionary(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); diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountProviderTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountProviderTests.cs index 9a830109..9edbebc3 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountProviderTests.cs +++ b/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 EasyAbp.EShop.Products.Products; using Microsoft.Extensions.DependencyInjection; +using NodaMoney; using Shouldly; using Xunit; @@ -25,7 +26,8 @@ public class OrderDiscountProviderTests : OrdersDomainTestBase var createOrderInfoModel = new CreateOrderInfoModel(OrderTestData.Store1Id, null, new List { - 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, AttributeOptionIds = new List(), - Price = 1m, + Price = 100m, + Currency = "USD", + OrderMinQuantity = 1, + OrderMaxQuantity = 100, + }, + new() + { + Id = OrderTestData.ProductSku2Id, + AttributeOptionIds = new List(), + Price = 2m, Currency = "USD", OrderMinQuantity = 1, OrderMaxQuantity = 100, @@ -52,14 +63,32 @@ public class OrderDiscountProviderTests : OrdersDomainTestBase } }, new Dictionary()); - order.ActualTotalPrice.ShouldBe(1.89m); - order.TotalDiscount.ShouldBe(0.11m); - order.OrderDiscounts.Count.ShouldBe(2); + var orderLines = order.OrderLines; + + 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 => - x.OrderId == order.Id && x.OrderLineId == order.OrderLines.First().Id && x.Name == "DemoDiscount1" && - x.Key == "1" && x.DisplayName == "Demo Discount 1" && x.DiscountedAmount == 0.01m); + x.OrderId == order.Id && x.OrderLineId == orderLines[1].Id && x.Name == "DemoDiscount5" && + x.Key == "5" && x.DisplayName == "Demo Discount 5" && x.DiscountedAmount == 0m); order.OrderDiscounts.ShouldContain(x => - x.OrderId == order.Id && x.OrderLineId == order.OrderLines.First().Id && x.Name == "DemoDiscount2" && - x.Key == "2" && x.DisplayName == "Demo Discount 2" && x.DiscountedAmount == 0.1m); + x.OrderId == order.Id && x.OrderLineId == orderLines[0].Id && x.Name == "DemoDiscount6" && + x.Key == "6" && x.DisplayName == "Demo Discount 6" && x.DiscountedAmount == 107.24m); } } \ No newline at end of file