diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs new file mode 100644 index 00000000..c86f5463 --- /dev/null +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Products.Products.Dtos; + +namespace EasyAbp.EShop.Orders.Orders; + +public interface IOrderLinePriceOverrider +{ + Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product, + ProductSkuDto productSku); +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs index fe4c5926..b6a5eaae 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs @@ -23,6 +23,7 @@ namespace EasyAbp.EShop.Orders.Orders private readonly IServiceProvider _serviceProvider; private readonly IOrderNumberGenerator _orderNumberGenerator; private readonly IProductSkuDescriptionProvider _productSkuDescriptionProvider; + private readonly IEnumerable _orderLinePriceOverriders; public NewOrderGenerator( IClock clock, @@ -30,7 +31,8 @@ namespace EasyAbp.EShop.Orders.Orders ICurrentTenant currentTenant, IServiceProvider serviceProvider, IOrderNumberGenerator orderNumberGenerator, - IProductSkuDescriptionProvider productSkuDescriptionProvider) + IProductSkuDescriptionProvider productSkuDescriptionProvider, + IEnumerable orderLinePriceOverriders) { _clock = clock; _guidGenerator = guidGenerator; @@ -38,6 +40,7 @@ namespace EasyAbp.EShop.Orders.Orders _serviceProvider = serviceProvider; _orderNumberGenerator = orderNumberGenerator; _productSkuDescriptionProvider = productSkuDescriptionProvider; + _orderLinePriceOverriders = orderLinePriceOverriders; } public virtual async Task GenerateAsync(Guid customerUserId, CreateOrderDto input, @@ -119,8 +122,10 @@ namespace EasyAbp.EShop.Orders.Orders { throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, inputOrderLine.Quantity); } + + var unitPrice = await GetUnitPriceAsync(input, inputOrderLine, product, productSku); - var totalPrice = productSku.Price * inputOrderLine.Quantity; + var totalPrice = unitPrice * inputOrderLine.Quantity; var orderLine = new OrderLine( id: _guidGenerator.Create(), @@ -137,7 +142,7 @@ namespace EasyAbp.EShop.Orders.Orders skuDescription: await _productSkuDescriptionProvider.GenerateAsync(product, productSku), mediaResources: product.MediaResources, currency: productSku.Currency, - unitPrice: productSku.Price, + unitPrice: unitPrice, totalPrice: totalPrice, totalDiscount: 0, actualTotalPrice: totalPrice, @@ -149,6 +154,23 @@ namespace EasyAbp.EShop.Orders.Orders return orderLine; } + protected virtual async Task GetUnitPriceAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, + ProductDto product, ProductSkuDto productSku) + { + foreach (var overrider in _orderLinePriceOverriders) + { + var overridenUnitPrice = + await overrider.GetUnitPriceOrNullAsync(input, inputOrderLine, product, productSku); + + if (overridenUnitPrice is not null) + { + return overridenUnitPrice.Value; + } + } + + return productSku.Price; + } + protected virtual Task GetStoreCurrencyAsync(Guid storeId) { // Todo: Get real store currency configuration. diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs index 63670c45..885afd26 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs @@ -292,7 +292,7 @@ namespace EasyAbp.EShop.Orders.Orders }); } - [Fact] + [Fact] public async Task Paid_Order_Should_Not_Be_Auto_Canceled_When_Payment_Overtime() { // Arrange @@ -462,5 +462,41 @@ namespace EasyAbp.EShop.Orders.Orders order.CancellationReason.ShouldBe(OrdersConsts.CancellationReason); }); } + + [Fact] + public async Task Should_Override_Unit_Price() + { + var createOrderDto = new CreateOrderDto + { + CustomerRemark = "customer remark", + StoreId = OrderTestData.Store1Id, + OrderLines = new List + { + new() + { + ProductId = OrderTestData.Product1Id, + ProductSkuId = OrderTestData.ProductSku1Id, + Quantity = 10 + }, + new() + { + ProductId = OrderTestData.Product1Id, + ProductSkuId = OrderTestData.ProductSku2Id, + Quantity = 2 + } + } + }; + + await WithUnitOfWorkAsync(async () => + { + var order = await _orderAppService.CreateAsync(createOrderDto); + var orderLine = order.OrderLines.Find(x => x.ProductSkuId == OrderTestData.ProductSku2Id); + + order.ProductTotalPrice.ShouldBe(10 * 1m + 2 * TestOrderLinePriceOverrider.Sku2UnitPrice); + orderLine.ShouldNotBeNull(); + orderLine.UnitPrice.ShouldBe(TestOrderLinePriceOverrider.Sku2UnitPrice); + orderLine.TotalPrice.ShouldBe(orderLine.Quantity * orderLine.UnitPrice); + }); + } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs new file mode 100644 index 00000000..22d4c016 --- /dev/null +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Products.Products.Dtos; +using Volo.Abp.DependencyInjection; + +namespace EasyAbp.EShop.Orders.Orders; + +public class TestOrderLinePriceOverrider : IOrderLinePriceOverrider, ITransientDependency +{ + public static decimal Sku2UnitPrice { get; set; } = 100m; + + public async Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, + ProductDto product, ProductSkuDto productSku) + { + if (inputOrderLine.ProductSkuId == OrderTestData.ProductSku2Id) + { + return Sku2UnitPrice; + } + + return null; + } +} \ No newline at end of file