Browse Source

Forbid canceling orders during the inventory reduction state

Fix #221
pull/222/head
gdlcf88 4 years ago
parent
commit
9fd1b679e7
  1. 2
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderManager.cs
  2. 6
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/Order.cs
  3. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderManager.cs
  4. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/PaymentCanceledEventHandler.cs
  5. 11
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs
  6. 38
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs
  7. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/InventoryRollbackOrderCanceledEventHandler.cs
  8. 38
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/InventoryRollbackTests.cs

2
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderManager.cs

@ -8,6 +8,6 @@ namespace EasyAbp.EShop.Orders.Orders
{
Task<Order> CompleteAsync(Order order);
Task<Order> CancelAsync(Order order, string cancellationReason);
Task<Order> CancelAsync(Order order, string cancellationReason, bool forceCancel = false);
}
}

6
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/Order.cs

@ -226,5 +226,11 @@ namespace EasyAbp.EShop.Orders.Orders
{
StaffRemark = staffRemark;
}
public bool IsInInventoryDeductionStage()
{
return !ReducedInventoryAfterPlacingTime.HasValue ||
PaidTime.HasValue && !ReducedInventoryAfterPaymentTime.HasValue;
}
}
}

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

@ -57,14 +57,14 @@ namespace EasyAbp.EShop.Orders.Orders
}
[UnitOfWork]
public virtual async Task<Order> CancelAsync(Order order, string cancellationReason)
public virtual async Task<Order> CancelAsync(Order order, string cancellationReason, bool forceCancel = false)
{
if (order.CanceledTime.HasValue)
{
throw new OrderIsInWrongStageException(order.Id);
}
if (order.IsInPayment())
if (!forceCancel && (order.IsInPayment() || order.IsInInventoryDeductionStage()))
{
throw new OrderIsInWrongStageException(order.Id);
}

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/PaymentCanceledEventHandler.cs

@ -40,6 +40,7 @@ namespace EasyAbp.EShop.Orders.Orders
order.SetPaymentId(null);
// OrderAutoCancelOnUpdatedHandler may auto cancel the unpaid order.
await _orderRepository.UpdateAsync(order, true);
}
}

11
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs

@ -1,10 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Payments.Refunds;
using EasyAbp.EShop.Products.Products;
using EasyAbp.PaymentService.Refunds;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
@ -49,7 +46,8 @@ namespace EasyAbp.EShop.Orders.Orders
if (!eventData.IsSuccess)
{
await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason);
await _orderManager.CancelAsync(
order, OrdersConsts.InventoryReductionFailedAutoCancellationReason, true);
return;
}
@ -76,7 +74,8 @@ namespace EasyAbp.EShop.Orders.Orders
{
var refundOrderEto = CreateRefundOrderEto(order);
await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason);
await _orderManager.CancelAsync(
order, OrdersConsts.InventoryReductionFailedAutoCancellationReason, true);
await RefundOrderAsync(refundOrderEto);

38
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs

@ -64,8 +64,6 @@ namespace EasyAbp.EShop.Orders.Orders
"Key",
0.3m
));
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
orderRepository.GetAsync(OrderTestData.Order1Id).Returns(Task.FromResult(Order1));
@ -118,6 +116,9 @@ namespace EasyAbp.EShop.Orders.Orders
}
});
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
Order1.RefundAmount.ShouldBe(0.3m);
var orderLine1 = Order1.OrderLines.Single(x => x.Id == OrderTestData.OrderLine1Id);
@ -133,6 +134,9 @@ namespace EasyAbp.EShop.Orders.Orders
{
var handler = ServiceProvider.GetRequiredService<RefundCompletedEventHandler>();
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
await Should.ThrowAsync<InvalidRefundAmountException>(async () =>
{
await handler.HandleEventAsync(new EShopRefundCompletedEto
@ -174,6 +178,9 @@ namespace EasyAbp.EShop.Orders.Orders
{
var handler = ServiceProvider.GetRequiredService<RefundCompletedEventHandler>();
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
await Should.ThrowAsync<InvalidRefundQuantityException>(async () =>
{
await handler.HandleEventAsync(new EShopRefundCompletedEto
@ -209,5 +216,32 @@ namespace EasyAbp.EShop.Orders.Orders
});
});
}
[Fact]
public async Task Should_Forbid_Canceling_Order_During_Payment_State()
{
var orderManager = ServiceProvider.GetRequiredService<IOrderManager>();
var order = await _orderRepository.GetAsync(OrderTestData.Order1Id);
order.SetPaymentId(Guid.NewGuid());
order.SetPaidTime(null);
await Should.ThrowAsync<OrderIsInWrongStageException>(() => orderManager.CancelAsync(order, "my-reason"));
}
[Fact]
public async Task Should_Forbid_Canceling_Order_During_Inventory_Reduction_State()
{
var orderManager = ServiceProvider.GetRequiredService<IOrderManager>();
var order = await _orderRepository.GetAsync(OrderTestData.Order1Id);
order.SetReducedInventoryAfterPlacingTime(null);
await Should.ThrowAsync<OrderIsInWrongStageException>(() => orderManager.CancelAsync(order, "my-reason"));
order.SetReducedInventoryAfterPlacingTime(DateTime.Now);
order.SetPaymentId(Guid.NewGuid());
order.SetPaidTime(DateTime.Now);
order.SetReducedInventoryAfterPlacingTime(null);
await Should.ThrowAsync<OrderIsInWrongStageException>(() => orderManager.CancelAsync(order, "my-reason"));
}
}
}

2
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/InventoryRollbackOrderCanceledEventHandler.cs

@ -28,7 +28,7 @@ namespace EasyAbp.EShop.Products.Products
[UnitOfWork(true)]
public virtual async Task HandleEventAsync(OrderCanceledEto eventData)
{
if (eventData.Order.PaidTime.HasValue)
if (eventData.Order.PaidTime.HasValue || !eventData.Order.ReducedInventoryAfterPlacingTime.HasValue)
{
return;
}

38
modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/InventoryRollbackTests.cs

@ -25,40 +25,62 @@ namespace EasyAbp.EShop.Products.Products
[Fact]
public async Task Should_Roll_Back_ReduceAfterPlacing_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPlacing, false, true);
await TestAsync(InventoryStrategy.ReduceAfterPlacing, false, true, true);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPlacing_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPlacing, true, false);
await TestAsync(InventoryStrategy.ReduceAfterPlacing, true, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPayment_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPayment, false, false);
await TestAsync(InventoryStrategy.ReduceAfterPayment, false, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPayment_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPayment, true, false);
await TestAsync(InventoryStrategy.ReduceAfterPayment, true, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_NoNeed_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.NoNeed, false, false);
await TestAsync(InventoryStrategy.NoNeed, false, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_NoNeed_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.NoNeed, true, false);
await TestAsync(InventoryStrategy.NoNeed, true, true, false);
}
protected async Task TestAsync(InventoryStrategy inventoryStrategy, bool orderPaid, bool expectRollback)
[Fact]
public async Task Should_Roll_Back_FlashSales_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.FlashSales, false, true, true);
}
[Fact]
public async Task Should_Not_Roll_Back_FlashSales_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.FlashSales, true, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_Inventory_If_ReducedInventoryAfterPlacingTime_Is_Null()
{
await TestAsync(InventoryStrategy.NoNeed, true, false, false);
await TestAsync(InventoryStrategy.ReduceAfterPlacing, true, false, false);
await TestAsync(InventoryStrategy.ReduceAfterPayment, true, false, false);
await TestAsync(InventoryStrategy.FlashSales, true, false, false);
}
protected async Task TestAsync(InventoryStrategy inventoryStrategy, bool orderPaid,
bool hasReducedInventoryAfterPlacingTime, bool expectRollback)
{
var product = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
var productSku = product.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
@ -98,7 +120,7 @@ namespace EasyAbp.EShop.Products.Products
CompletionTime = null,
CanceledTime = null,
CancellationReason = null,
ReducedInventoryAfterPlacingTime = null,
ReducedInventoryAfterPlacingTime = hasReducedInventoryAfterPlacingTime ? DateTime.Now : null,
ReducedInventoryAfterPaymentTime = null,
PaymentExpiration = null,
OrderLines = new List<OrderLineEto>

Loading…
Cancel
Save