Browse Source

Merge pull request #142 from EasyAbp/roll-back-inventory

Inventory auto rollback
pull/143/head
Super 5 years ago
committed by GitHub
parent
commit
b0a650fa58
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/EShopProductsDomainModule.cs
  2. 30
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs
  3. 47
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/InventoryRollbackOrderCanceledEventHandler.cs
  4. 150
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/InventoryRollbackTests.cs

1
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/EShopProductsDomainModule.cs

@ -13,6 +13,7 @@ namespace EasyAbp.EShop.Products
{
[DependsOn(
typeof(EShopProductsDomainSharedModule),
typeof(AbpAutoMapperModule),
typeof(AbpCachingModule),
typeof(AbpTreesDomainModule)
)]

30
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/DefaultProductInventoryProvider.cs

@ -5,6 +5,8 @@ using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductInventories;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EasyAbp.EShop.Products.Products
@ -12,15 +14,21 @@ namespace EasyAbp.EShop.Products.Products
public class DefaultProductInventoryProvider : IProductInventoryProvider, ITransientDependency
{
// Todo: should use IProductInventoryStore.
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IDistributedEventBus _distributedEventBus;
private readonly IProductInventoryRepository _productInventoryRepository;
public DefaultProductInventoryProvider(
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IDistributedEventBus distributedEventBus,
IProductInventoryRepository productInventoryRepository)
{
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
_unitOfWorkManager = unitOfWorkManager;
_distributedEventBus = distributedEventBus;
_productInventoryRepository = productInventoryRepository;
@ -49,7 +57,7 @@ namespace EasyAbp.EShop.Products.Products
[UnitOfWork(true)]
public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, int quantity, bool decreaseSold)
{
var productInventory = await _productInventoryRepository.GetAsync(x => x.ProductSkuId == productSku.Id);
var productInventory = await GetOrCreateProductInventoryAsync(product.Id, productSku.Id);
return await TryIncreaseInventoryAsync(product, productInventory, quantity, decreaseSold);
}
@ -57,11 +65,29 @@ namespace EasyAbp.EShop.Products.Products
[UnitOfWork(true)]
public virtual async Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, int quantity, bool increaseSold)
{
var productInventory = await _productInventoryRepository.GetAsync(x => x.ProductSkuId == productSku.Id);
var productInventory = await GetOrCreateProductInventoryAsync(product.Id, productSku.Id);
return await TryReduceInventoryAsync(product, productInventory, quantity, increaseSold);
}
[UnitOfWork]
protected virtual async Task<ProductInventory> GetOrCreateProductInventoryAsync(Guid productId, Guid productSkuId)
{
var productInventory =
await _productInventoryRepository.FindAsync(x =>
x.ProductId == productId && x.ProductSkuId == productSkuId);
if (productInventory is null)
{
productInventory = new ProductInventory(_guidGenerator.Create(), _currentTenant.Id, productId,
productSkuId, 0, 0);
await _productInventoryRepository.InsertAsync(productInventory, true);
}
return productInventory;
}
[UnitOfWork(true)]
public virtual async Task<bool> TryIncreaseInventoryAsync(Product product, ProductInventory productInventory, int quantity, bool decreaseSold)
{

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

@ -0,0 +1,47 @@
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
namespace EasyAbp.EShop.Products.Products
{
// see: https://github.com/EasyAbp/EShop/issues/139
public class InventoryRollbackOrderCanceledEventHandler : IDistributedEventHandler<OrderCanceledEto>, ITransientDependency
{
private readonly IProductManager _productManager;
private readonly IProductRepository _productRepository;
public InventoryRollbackOrderCanceledEventHandler(
IProductManager productManager,
IProductRepository productRepository)
{
_productManager = productManager;
_productRepository = productRepository;
}
[UnitOfWork(true)]
public virtual async Task HandleEventAsync(OrderCanceledEto eventData)
{
if (eventData.Order.PaidTime.HasValue)
{
return;
}
foreach (var orderLine in eventData.Order.OrderLines)
{
var product = await _productRepository.GetAsync(orderLine.ProductId);
if (product.InventoryStrategy is InventoryStrategy.NoNeed or InventoryStrategy.ReduceAfterPayment)
{
continue;
}
var productSku = product.ProductSkus.Single(x => x.Id == orderLine.ProductSkuId);
await _productManager.TryIncreaseInventoryAsync(product, productSku, orderLine.Quantity, true);
}
}
}
}

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

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Products.Products
{
public class InventoryRollbackTests : ProductsDomainTestBase
{
private InventoryRollbackOrderCanceledEventHandler EventHandler { get; }
private IProductRepository ProductRepository { get; }
private IProductManager ProductManager { get; }
public InventoryRollbackTests()
{
EventHandler = ServiceProvider.GetRequiredService<InventoryRollbackOrderCanceledEventHandler>();
ProductRepository = ServiceProvider.GetRequiredService<IProductRepository>();
ProductManager = ServiceProvider.GetRequiredService<IProductManager>();
}
[Fact]
public async Task Should_Roll_Back_ReduceAfterPlacing_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPlacing, false, true);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPlacing_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPlacing, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPayment_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPayment, false, false);
}
[Fact]
public async Task Should_Not_Roll_Back_ReduceAfterPayment_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.ReduceAfterPayment, true, false);
}
[Fact]
public async Task Should_Not_Roll_Back_NoNeed_Inventory_If_Order_Is_Not_Paid()
{
await TestAsync(InventoryStrategy.NoNeed, false, false);
}
[Fact]
public async Task Should_Not_Roll_Back_NoNeed_Inventory_If_Order_Is_Paid()
{
await TestAsync(InventoryStrategy.NoNeed, true, false);
}
protected async Task TestAsync(InventoryStrategy inventoryStrategy, bool orderPaid, bool expectRollback)
{
var product = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
var productSku = product.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
typeof(Product).GetProperty("InventoryStrategy")!.SetValue(product, inventoryStrategy);
await ProductRepository.UpdateAsync(product, true);
(await ProductManager.TryIncreaseInventoryAsync(product, productSku, 3, false)).ShouldBeTrue();
(await ProductManager.TryReduceInventoryAsync(product, productSku, 3, true)).ShouldBeTrue();
var inventoryDataModel = await ProductManager.GetInventoryDataAsync(product, productSku);
inventoryDataModel.Inventory.ShouldBe(0);
await EventHandler.HandleEventAsync(new OrderCanceledEto(
new OrderEto
{
#region properties
Id = Guid.NewGuid(),
TenantId = null,
StoreId = ProductsTestData.Store1Id,
OrderNumber = null,
CustomerUserId = Guid.NewGuid(),
OrderStatus = OrderStatus.Processing,
Currency = null,
ProductTotalPrice = 0,
TotalDiscount = 0,
TotalPrice = 0,
ActualTotalPrice = 0,
RefundAmount = 0,
CustomerRemark = null,
StaffRemark = null,
PaymentId = orderPaid ? Guid.NewGuid() : null,
PaidTime = orderPaid ? DateTime.Now : null,
CompletionTime = null,
CanceledTime = null,
CancellationReason = null,
ReducedInventoryAfterPlacingTime = null,
ReducedInventoryAfterPaymentTime = null,
PaymentExpiration = null,
OrderLines = new List<OrderLineEto>
{
new()
{
Id = Guid.NewGuid(),
ProductId = product.Id,
ProductSkuId = productSku.Id,
ProductModificationTime = default,
ProductDetailModificationTime = default,
ProductGroupName = null,
ProductGroupDisplayName = null,
ProductUniqueName = null,
ProductDisplayName = null,
SkuName = null,
SkuDescription = null,
MediaResources = null,
Currency = null,
UnitPrice = 0,
TotalPrice = 0,
TotalDiscount = 0,
ActualTotalPrice = 0,
Quantity = 3,
RefundedQuantity = 0,
RefundAmount = 0
}
},
CreationTime = default,
CreatorId = null,
LastModificationTime = null,
LastModifierId = null,
IsDeleted = false,
DeletionTime = null,
DeleterId = null
#endregion
}
));
product = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
productSku = product.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
inventoryDataModel = await ProductManager.GetInventoryDataAsync(product, productSku);
inventoryDataModel.Inventory.ShouldBe(expectRollback ? 3 : 0);
}
}
}
Loading…
Cancel
Save