Browse Source

Merge pull request #164 from EasyAbp/auto-cancel-refund-order

Auto cancel and refund order in case of insufficient inventory
pull/165/head
Super 4 years ago
committed by GitHub
parent
commit
223723f3fa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs
  2. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderManager.cs
  3. 72
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs
  4. 192
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs
  5. 18
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/TestRefundOrderEventHandler.cs
  6. 16
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs
  7. 22
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs
  8. 34
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs
  9. 65
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs
  10. 12
      modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs
  11. 116
      modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs
  12. 13
      modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/TestRefundPaymentEventHandler.cs

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersConsts.cs

@ -3,5 +3,6 @@
public static class OrdersConsts
{
public static string UnpaidAutoCancellationReason = "Order payment timed out and not paid";
public static string InventoryReductionFailedAutoCancellationReason = "Insufficient inventory";
}
}

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

@ -56,7 +56,6 @@ namespace EasyAbp.EShop.Orders.Orders
return order;
}
// Todo: should handler the inventory rollback.
[UnitOfWork]
public virtual async Task<Order> CancelAsync(Order order, string cancellationReason)
{

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

@ -1,6 +1,12 @@
using System.Threading.Tasks;
using System.Collections.Generic;
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;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
@ -11,18 +17,24 @@ namespace EasyAbp.EShop.Orders.Orders
{
private readonly IClock _clock;
private readonly ICurrentTenant _currentTenant;
private readonly IOrderManager _orderManager;
private readonly IOrderRepository _orderRepository;
private readonly IDistributedEventBus _distributedEventBus;
public ProductInventoryReductionEventHandler(
IClock clock,
ICurrentTenant currentTenant,
IOrderRepository orderRepository)
IOrderManager orderManager,
IOrderRepository orderRepository,
IDistributedEventBus distributedEventBus)
{
_clock = clock;
_currentTenant = currentTenant;
_orderManager = orderManager;
_orderRepository = orderRepository;
_distributedEventBus = distributedEventBus;
}
[UnitOfWork(true)]
public virtual async Task HandleEventAsync(ProductInventoryReductionAfterOrderPlacedResultEto eventData)
{
@ -37,10 +49,11 @@ namespace EasyAbp.EShop.Orders.Orders
if (!eventData.IsSuccess)
{
// Todo: Cancel order.
await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason);
return;
}
order.SetReducedInventoryAfterPlacingTime(_clock.Now);
await _orderRepository.UpdateAsync(order, true);
@ -61,15 +74,58 @@ namespace EasyAbp.EShop.Orders.Orders
if (!eventData.IsSuccess)
{
// Todo: Refund.
// Todo: Cancel order.
var refundOrderEto = CreateRefundOrderEto(order);
await _orderManager.CancelAsync(order, OrdersConsts.InventoryReductionFailedAutoCancellationReason);
await RefundOrderAsync(refundOrderEto);
return;
}
order.SetReducedInventoryAfterPaymentTime(_clock.Now);
await _orderRepository.UpdateAsync(order, true);
}
}
[UnitOfWork(true)]
protected virtual async Task RefundOrderAsync(RefundOrderEto refundOrderEto)
{
await _distributedEventBus.PublishAsync(refundOrderEto);
}
protected virtual RefundOrderEto CreateRefundOrderEto(Order order)
{
if (!order.PaymentId.HasValue)
{
throw new OrderIsInWrongStageException(order.Id);
}
var eto = new RefundOrderEto(
order.TenantId,
order.Id,
order.StoreId,
order.PaymentId.Value,
OrdersConsts.InventoryReductionFailedAutoCancellationReason,
OrdersConsts.InventoryReductionFailedAutoCancellationReason,
OrdersConsts.InventoryReductionFailedAutoCancellationReason);
eto.OrderLines.AddRange(order.OrderLines.Select(x => new OrderLineRefundInfoModel
{
OrderLineId = x.Id,
Quantity = x.Quantity - x.RefundedQuantity,
TotalAmount = x.ActualTotalPrice - x.RefundAmount
}));
eto.OrderExtraFees.AddRange(order.OrderExtraFees.Select(x => new OrderExtraFeeRefundInfoModel
{
Name = x.Name,
Key = x.Key,
TotalAmount = x.Fee - x.RefundAmount
}));
return eto;
}
}
}

192
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs

@ -0,0 +1,192 @@
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Orders.Orders;
public class InventoryReductionResultTests : OrdersDomainTestBase
{
private Order Order1 { get; set; }
protected override void AfterAddApplication(IServiceCollection services)
{
var orderRepository = Substitute.For<IOrderRepository>();
Order1 = new Order(
OrderTestData.Order1Id,
null,
OrderTestData.Store1Id,
Guid.NewGuid(),
"CNY",
1m,
0m,
1.5m,
1.5m,
null,
null);
Order1.OrderLines.Add(new OrderLine(
OrderTestData.OrderLine1Id,
OrderTestData.Product1Id,
OrderTestData.ProductSku1Id,
null,
DateTime.Now,
null,
"Default",
"Default",
null,
"Product 1",
null,
null,
null,
"CNY",
0.5m,
1m,
0m,
1m,
2
));
Order1.OrderExtraFees.Add(new OrderExtraFee(
OrderTestData.Order1Id,
"Name",
"Key",
0.3m
));
orderRepository.GetAsync(OrderTestData.Order1Id).Returns(Task.FromResult(Order1));
services.AddTransient(_ => orderRepository);
}
[Fact]
public async Task Should_Cancel_Order_If_Reduction_Failed_After_Placed()
{
typeof(Order).GetProperty(nameof(Order.CanceledTime))!.SetValue(Order1, null);
typeof(Order).GetProperty(nameof(Order.CancellationReason))!.SetValue(Order1, null);
Order1.SetReducedInventoryAfterPlacingTime(null);
Order1.SetReducedInventoryAfterPaymentTime(null);
Order1.SetOrderStatus(OrderStatus.Pending);
Order1.SetPaymentId(null);
Order1.SetPaidTime(null);
var handler = ServiceProvider.GetRequiredService<ProductInventoryReductionEventHandler>();
await handler.HandleEventAsync(new ProductInventoryReductionAfterOrderPlacedResultEto()
{
TenantId = null,
OrderId = OrderTestData.Order1Id,
IsSuccess = false
});
Order1.CanceledTime.ShouldNotBeNull();
Order1.CancellationReason.ShouldBe(OrdersConsts.InventoryReductionFailedAutoCancellationReason);
Order1.ReducedInventoryAfterPlacingTime.ShouldBeNull();
Order1.ReducedInventoryAfterPaymentTime.ShouldBeNull();
}
[Fact]
public async Task Should_Not_Cancel_Order_If_Reduction_Succeeded_After_Placed()
{
typeof(Order).GetProperty(nameof(Order.CanceledTime))!.SetValue(Order1, null);
typeof(Order).GetProperty(nameof(Order.CancellationReason))!.SetValue(Order1, null);
Order1.SetReducedInventoryAfterPlacingTime(null);
Order1.SetReducedInventoryAfterPaymentTime(null);
Order1.SetOrderStatus(OrderStatus.Pending);
Order1.SetPaymentId(null);
Order1.SetPaidTime(null);
var handler = ServiceProvider.GetRequiredService<ProductInventoryReductionEventHandler>();
await handler.HandleEventAsync(new ProductInventoryReductionAfterOrderPlacedResultEto()
{
TenantId = null,
OrderId = OrderTestData.Order1Id,
IsSuccess = true
});
Order1.CanceledTime.ShouldBeNull();
Order1.CancellationReason.ShouldBeNull();
Order1.ReducedInventoryAfterPlacingTime.ShouldNotBeNull();
Order1.ReducedInventoryAfterPaymentTime.ShouldBeNull();
}
[Fact]
public async Task Should_Cancel_Order_And_Refund_If_Reduction_Failed_After_Paid()
{
typeof(Order).GetProperty(nameof(Order.CanceledTime))!.SetValue(Order1, null);
typeof(Order).GetProperty(nameof(Order.CancellationReason))!.SetValue(Order1, null);
Order1.SetReducedInventoryAfterPlacingTime(DateTime.Now);
Order1.SetReducedInventoryAfterPaymentTime(null);
Order1.SetOrderStatus(OrderStatus.Processing);
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
var handler = ServiceProvider.GetRequiredService<ProductInventoryReductionEventHandler>();
await handler.HandleEventAsync(new ProductInventoryReductionAfterOrderPaidResultEto()
{
TenantId = null,
OrderId = OrderTestData.Order1Id,
IsSuccess = false
});
var eventData = TestRefundOrderEventHandler.LastEto;
TestRefundOrderEventHandler.LastEto = null;
eventData.ShouldNotBeNull();
eventData.DisplayReason.ShouldBe(OrdersConsts.InventoryReductionFailedAutoCancellationReason);
eventData.StaffRemark.ShouldBe(OrdersConsts.InventoryReductionFailedAutoCancellationReason);
eventData.CustomerRemark.ShouldBe(OrdersConsts.InventoryReductionFailedAutoCancellationReason);
eventData.PaymentId.ShouldBe(OrderTestData.Payment1Id);
eventData.TenantId.ShouldBeNull();
eventData.OrderId.ShouldBe(OrderTestData.Order1Id);
eventData.OrderLines.Count.ShouldBe(1);
var orderLine = eventData.OrderLines[0];
orderLine.OrderLineId.ShouldBe(OrderTestData.OrderLine1Id);
orderLine.Quantity.ShouldBe(2);
orderLine.TotalAmount.ShouldBe(1m);
eventData.OrderExtraFees.Count.ShouldBe(1);
var orderExtraFee = eventData.OrderExtraFees[0];
orderExtraFee.Name.ShouldBe("Name");
orderExtraFee.Key.ShouldBe("Key");
orderExtraFee.TotalAmount.ShouldBe(0.3m);
Order1.CanceledTime.ShouldNotBeNull();
Order1.CancellationReason.ShouldBe(OrdersConsts.InventoryReductionFailedAutoCancellationReason);
Order1.ReducedInventoryAfterPlacingTime.ShouldNotBeNull();
Order1.ReducedInventoryAfterPaymentTime.ShouldBeNull();
}
[Fact]
public async Task Should_Not_Cancel_And_Refund_Order_If_Reduction_Succeeded_After_Paid()
{
typeof(Order).GetProperty(nameof(Order.CanceledTime))!.SetValue(Order1, null);
typeof(Order).GetProperty(nameof(Order.CancellationReason))!.SetValue(Order1, null);
Order1.SetReducedInventoryAfterPlacingTime(DateTime.Now);
Order1.SetReducedInventoryAfterPaymentTime(null);
Order1.SetOrderStatus(OrderStatus.Processing);
Order1.SetPaymentId(OrderTestData.Payment1Id);
Order1.SetPaidTime(DateTime.Now);
var handler = ServiceProvider.GetRequiredService<ProductInventoryReductionEventHandler>();
await handler.HandleEventAsync(new ProductInventoryReductionAfterOrderPaidResultEto()
{
TenantId = null,
OrderId = OrderTestData.Order1Id,
IsSuccess = true
});
var eventData = TestRefundOrderEventHandler.LastEto;
TestRefundOrderEventHandler.LastEto = null;
eventData.ShouldBeNull();
Order1.CanceledTime.ShouldBeNull();
Order1.CancellationReason.ShouldBeNull();
Order1.ReducedInventoryAfterPlacingTime.ShouldNotBeNull();
Order1.ReducedInventoryAfterPaymentTime.ShouldNotBeNull();
}
}

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

@ -0,0 +1,18 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Payments.Refunds;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace EasyAbp.EShop.Orders.Orders;
public class TestRefundOrderEventHandler : IDistributedEventHandler<RefundOrderEto>, ITransientDependency
{
public static RefundOrderEto LastEto { get; set; }
public Task HandleEventAsync(RefundOrderEto eventData)
{
LastEto = eventData;
return Task.CompletedTask;
}
}

16
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/CreateEShopRefundItemInput.cs

@ -1,23 +1,9 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Payments.Refunds.Dtos
{
[Serializable]
public class CreateEShopRefundItemInput : ExtensibleObject
public class CreateEShopRefundItemInput : CreateEShopRefundItemInfoModel
{
public Guid OrderId { get; set; }
[CanBeNull]
public string CustomerRemark { get; set; }
[CanBeNull]
public string StaffRemark { get; set; }
public List<OrderLineRefundInfoModel> OrderLines { get; set; } = new();
public List<OrderExtraFeeRefundInfoModel> OrderExtraFees { get; set; } = new();
}
}

22
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/CreateEShopRefundItemInfoModel.cs

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Payments.Refunds;
[Serializable]
public class CreateEShopRefundItemInfoModel : ExtensibleObject
{
public Guid OrderId { get; set; }
[CanBeNull]
public string CustomerRemark { get; set; }
[CanBeNull]
public string StaffRemark { get; set; }
public List<OrderLineRefundInfoModel> OrderLines { get; set; } = new();
public List<OrderExtraFeeRefundInfoModel> OrderExtraFees { get; set; } = new();
}

34
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundOrderEto.cs

@ -0,0 +1,34 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Payments.Refunds;
[Serializable]
public class RefundOrderEto : CreateEShopRefundItemInfoModel, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid StoreId { get; set; }
public Guid PaymentId { get; set; }
[CanBeNull]
public string DisplayReason { get; set; }
protected RefundOrderEto()
{
}
public RefundOrderEto(Guid? tenantId, Guid orderId, Guid storeId, Guid paymentId, [CanBeNull] string displayReason,
[CanBeNull] string customerRemark, [CanBeNull] string staffRemark)
{
TenantId = tenantId;
OrderId = orderId;
StoreId = storeId;
PaymentId = paymentId;
DisplayReason = displayReason;
CustomerRemark = customerRemark;
StaffRemark = staffRemark;
}
}

65
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundOrderEventHandler.cs

@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Payments.Payments;
using EasyAbp.PaymentService.Payments;
using EasyAbp.PaymentService.Refunds;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Payments.Refunds;
public class RefundOrderEventHandler : IDistributedEventHandler<RefundOrderEto>, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IPaymentRepository _paymentRepository;
private readonly IDistributedEventBus _distributedEventBus;
public RefundOrderEventHandler(
IJsonSerializer jsonSerializer,
IPaymentRepository paymentRepository,
IDistributedEventBus distributedEventBus)
{
_jsonSerializer = jsonSerializer;
_paymentRepository = paymentRepository;
_distributedEventBus = distributedEventBus;
}
public virtual async Task HandleEventAsync(RefundOrderEto eventData)
{
var refundAmount = eventData.OrderLines.Sum(x => x.TotalAmount) +
eventData.OrderExtraFees.Sum(x => x.TotalAmount);
var payment = await _paymentRepository.GetAsync(eventData.PaymentId);
var paymentItem = payment.PaymentItems.Single(x => x.ItemKey == eventData.OrderId.ToString());
var createRefundItemInput = new CreateRefundItemInput
{
PaymentItemId = paymentItem.Id,
RefundAmount = refundAmount,
CustomerRemark = eventData.CustomerRemark,
StaffRemark = eventData.StaffRemark
};
createRefundItemInput.SetProperty(nameof(RefundItem.StoreId), eventData.StoreId);
createRefundItemInput.SetProperty(nameof(RefundItem.OrderId), eventData.OrderId);
createRefundItemInput.SetProperty(nameof(RefundItem.OrderLines),
_jsonSerializer.Serialize(eventData.OrderLines));
createRefundItemInput.SetProperty(nameof(RefundItem.OrderExtraFees),
_jsonSerializer.Serialize(eventData.OrderExtraFees));
var eto = new RefundPaymentEto(eventData.TenantId, new CreateRefundInput
{
PaymentId = eventData.PaymentId,
DisplayReason = eventData.DisplayReason,
CustomerRemark = eventData.CustomerRemark,
StaffRemark = eventData.StaffRemark,
RefundItems = new List<CreateRefundItemInput> { createRefundItemInput }
});
await _distributedEventBus.PublishAsync(eto);
}
}

12
modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs

@ -47,7 +47,8 @@ namespace EasyAbp.EShop.Payments.Refunds
paymentItemType.GetProperty(nameof(PaymentItem.ActualPaymentAmount))?.SetValue(paymentItem, 1m);
paymentItemType.GetProperty(nameof(PaymentItem.ItemType))?.SetValue(paymentItem, PaymentsConsts.PaymentItemType);
paymentItemType.GetProperty(nameof(PaymentItem.ItemKey))?.SetValue(paymentItem, PaymentsTestData.Order1.ToString());
paymentItem.ExtraProperties.Add(nameof(paymentItem.StoreId), PaymentsTestData.Store1);
paymentItemType.GetProperty(nameof(PaymentItem.StoreId))?.SetValue(paymentItem, PaymentsTestData.Store1);
// paymentItem.ExtraProperties.Add(nameof(paymentItem.StoreId), PaymentsTestData.Store1);
var payment = Activator.CreateInstance(paymentType, true) as Payment;
payment.ShouldNotBeNull();
@ -75,7 +76,9 @@ namespace EasyAbp.EShop.Payments.Refunds
?.SetValue(paymentItem, PaymentsConsts.PaymentItemType);
paymentItemType.GetProperty(nameof(PaymentItem.ItemKey))
?.SetValue(paymentItem, PaymentsTestData.Order1.ToString());
paymentItem.ExtraProperties.Add(nameof(paymentItem.StoreId), PaymentsTestData.Store1);
paymentItemType.GetProperty(nameof(PaymentItem.StoreId))
?.SetValue(paymentItem, PaymentsTestData.Store1);
// paymentItem.ExtraProperties.Add(nameof(paymentItem.StoreId), PaymentsTestData.Store1);
var payment = Activator.CreateInstance(paymentType, true) as Payment;
payment.ShouldNotBeNull();
@ -182,10 +185,9 @@ namespace EasyAbp.EShop.Payments.Refunds
// Act & Assert
await _refundAppService.CreateAsync(request);
_testRefundPaymentEventHandler.IsEventPublished.ShouldBe(true);
var eventData = _testRefundPaymentEventHandler.EventData;
var eventData = TestRefundPaymentEventHandler.LastEto;
TestRefundPaymentEventHandler.LastEto = null;
eventData.ShouldNotBeNull();
eventData.CreateRefundInput.RefundItems.Count.ShouldBe(1);

116
modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Payments.Payments;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using NSubstitute.Core;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Xunit;
namespace EasyAbp.EShop.Payments.Refunds;
public class RefundOrderEventHandlerTests : PaymentsDomainTestBase
{
private readonly IJsonSerializer _jsonSerializer;
public RefundOrderEventHandlerTests()
{
_jsonSerializer = ServiceProvider.GetRequiredService<IJsonSerializer>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
MockPaymentRepository(services);
}
private static void MockPaymentRepository(IServiceCollection services)
{
var paymentRepository = Substitute.For<IPaymentRepository>();
Payment Payment1Returns(CallInfo _)
{
var paymentType = typeof(Payment);
var paymentItemType = typeof(PaymentItem);
var paymentItem = Activator.CreateInstance(paymentItemType, true) as PaymentItem;
paymentItem.ShouldNotBeNull();
paymentItemType.GetProperty(nameof(PaymentItem.Id))?.SetValue(paymentItem, PaymentsTestData.PaymentItem1);
paymentItemType.GetProperty(nameof(PaymentItem.ActualPaymentAmount))?.SetValue(paymentItem, 1m);
paymentItemType.GetProperty(nameof(PaymentItem.ItemType))
?.SetValue(paymentItem, PaymentsConsts.PaymentItemType);
paymentItemType.GetProperty(nameof(PaymentItem.ItemKey))
?.SetValue(paymentItem, PaymentsTestData.Order1.ToString());
paymentItemType.GetProperty(nameof(PaymentItem.StoreId))
?.SetValue(paymentItem, PaymentsTestData.Store1);
var payment = Activator.CreateInstance(paymentType, true) as Payment;
payment.ShouldNotBeNull();
paymentType.GetProperty(nameof(Payment.Id))?.SetValue(payment, PaymentsTestData.Payment1);
paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "CNY");
paymentType.GetProperty(nameof(Payment.ActualPaymentAmount))?.SetValue(payment, 1m);
paymentType.GetProperty(nameof(Payment.PaymentItems))
?.SetValue(payment, new List<PaymentItem> { paymentItem });
return payment;
}
paymentRepository.GetAsync(PaymentsTestData.Payment1).Returns(Payment1Returns);
paymentRepository.FindAsync(PaymentsTestData.Payment1).Returns(Payment1Returns);
services.AddTransient(_ => paymentRepository);
}
[Fact]
public async Task Should_Refund_Order()
{
var handler = ServiceProvider.GetRequiredService<RefundOrderEventHandler>();
var eto = new RefundOrderEto(null, PaymentsTestData.Order1, PaymentsTestData.Store1,
PaymentsTestData.Payment1, "Test", null, null);
eto.OrderLines.Add(new OrderLineRefundInfoModel
{
OrderLineId = PaymentsTestData.OrderLine1,
Quantity = 2,
TotalAmount = 0.4m
});
eto.OrderExtraFees.Add(new OrderExtraFeeRefundInfoModel
{
Name = "Name",
Key = "Key",
TotalAmount = 0.6m
});
await handler.HandleEventAsync(eto);
var eventData = TestRefundPaymentEventHandler.LastEto;
TestRefundPaymentEventHandler.LastEto = null;
eventData.ShouldNotBeNull();
eventData.CreateRefundInput.RefundItems.Count.ShouldBe(1);
var refundItem = eventData.CreateRefundInput.RefundItems[0];
refundItem.GetProperty<Guid?>(nameof(RefundItem.OrderId)).ShouldBe(PaymentsTestData.Order1);
var orderLines =
_jsonSerializer.Deserialize<List<OrderLineRefundInfoModel>>(
refundItem.GetProperty<string>(nameof(RefundItem.OrderLines)));
orderLines.Count.ShouldBe(1);
orderLines[0].OrderLineId.ShouldBe(PaymentsTestData.OrderLine1);
orderLines[0].Quantity.ShouldBe(2);
orderLines[0].TotalAmount.ShouldBe(0.4m);
var orderExtraFees =
_jsonSerializer.Deserialize<List<OrderExtraFeeRefundInfoModel>>(
refundItem.GetProperty<string>(nameof(RefundItem.OrderExtraFees)));
orderExtraFees.Count.ShouldBe(1);
orderExtraFees[0].Name.ShouldBe("Name");
orderExtraFees[0].Key.ShouldBe("Key");
orderExtraFees[0].TotalAmount.ShouldBe(0.6m);
}
}

13
modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/TestRefundPaymentEventHandler.cs → modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/TestRefundPaymentEventHandler.cs

@ -5,17 +5,14 @@ using Volo.Abp.EventBus.Distributed;
namespace EasyAbp.EShop.Payments.Refunds
{
public class TestRefundPaymentEventHandler : IDistributedEventHandler<RefundPaymentEto>, ISingletonDependency
public class TestRefundPaymentEventHandler : IDistributedEventHandler<RefundPaymentEto>, ITransientDependency
{
public bool IsEventPublished { get; protected set; }
public RefundPaymentEto EventData { get; protected set; }
public static RefundPaymentEto LastEto { get; set; }
public Task HandleEventAsync(RefundPaymentEto eventData)
{
IsEventPublished = true;
EventData = eventData;
LastEto = eventData;
return Task.CompletedTask;
}
}
Loading…
Cancel
Save