Browse Source

Merge branch 'dev' into plugins-promotions

pull/240/head
gdlcf88 3 years ago
parent
commit
64f6493261
  1. 2
      Directory.Build.props
  2. 18
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderDto.cs
  3. 8
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderLineDto.cs
  4. 16
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderDiscountDto.cs
  5. 2
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderDto.cs
  6. 12
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderExtraFeeDto.cs
  7. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicOrderCreationAuthorizationHandler.cs
  8. 15
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs
  9. 12
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs
  10. 43
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs
  11. 3
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationResource.cs
  12. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/OrdersApplicationAutoMapperProfile.cs
  13. 21
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json
  14. 17
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json
  15. 17
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json
  16. 27
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/CreateOrderInfoModel.cs
  17. 25
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/CreateOrderLineInfoModel.cs
  18. 12
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/ICreateOrderInfo.cs
  19. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/ICreateOrderLineInfo.cs
  20. 45
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrder.cs
  21. 23
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrderExtraFee.cs
  22. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrderLine.cs
  23. 6
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderEto.cs
  24. 20
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderExtraFeeEto.cs
  25. 3
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersErrorCodes.cs
  26. 16
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/DuplicateOrderDiscountException.cs
  27. 13
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs
  28. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountProvider.cs
  29. 7
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs
  30. 11
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs
  31. 104
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs
  32. 87
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/Order.cs
  33. 48
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscount.cs
  34. 34
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountInfoModel.cs
  35. 7
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderExtraFee.cs
  36. 9
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderExtraFeeInfoModel.cs
  37. 8
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderLine.cs
  38. 0
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderLineInvalidQuantityException.cs
  39. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/ProductInventoryReductionEventHandler.cs
  40. 0
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/UnexpectedCurrencyException.cs
  41. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/OrdersDomainAutoMapperProfile.cs
  42. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.EntityFrameworkCore/EasyAbp/EShop/Orders/EntityFrameworkCore/OrdersDbContext.cs
  43. 9
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.EntityFrameworkCore/EasyAbp/EShop/Orders/EntityFrameworkCore/OrdersDbContextModelCreatingExtensions.cs
  44. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/EasyAbp.EShop.Orders.Application.Tests.csproj
  45. 26
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs
  46. 9
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs
  47. 19
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs
  48. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs
  49. 65
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountTests.cs
  50. 1
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs
  51. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.EntityFrameworkCore.Tests/EasyAbp.EShop.Orders.EntityFrameworkCore.Tests.csproj
  52. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Orders.HttpApi.Client.ConsoleTestApp.csproj
  53. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.MongoDB.Tests/EasyAbp.EShop.Orders.MongoDB.Tests.csproj
  54. 2
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.TestBase/EasyAbp.EShop.Orders.TestBase.csproj
  55. 13
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Payments/Dtos/PaymentDto.cs
  56. 11
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/RefundDto.cs
  57. 5
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Payments/EShopPaymentEto.cs
  58. 21
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/EShopRefundEto.cs
  59. 6
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/OrderExtraFeeRefundInfoModel.cs
  60. 6
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundItemOrderExtraFeeEto.cs
  61. 28
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Payments/Payment.cs
  62. 24
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/Refund.cs
  63. 14
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundItemOrderExtraFee.cs
  64. 2
      modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundSynchronizer.cs
  65. 2
      modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs
  66. 3
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductAttributeDto.cs
  67. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductAttributeOptionDto.cs
  68. 22
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs
  69. 3
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs
  70. 5
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductViewDto.cs
  71. 24
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs
  72. 8
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs
  73. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp.EShop.Products.Domain.Shared.csproj
  74. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/EShopProductsDomainSharedModule.cs
  75. 9
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasAttributeOptionIds.cs
  76. 9
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasProductGroupDisplayName.cs
  77. 31
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProduct.cs
  78. 15
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductAttribute.cs
  79. 12
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductAttributeOption.cs
  80. 47
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductBase.cs
  81. 24
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductSku.cs
  82. 3
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductSkuDescriptionProvider.cs
  83. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductAttributeEto.cs
  84. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductEto.cs
  85. 24
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductExtensions.cs
  86. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductSkuDescriptionProvider.cs
  87. 5
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductSkuEto.cs
  88. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Options/ProductGroups/IProductGroup.cs
  89. 39
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/AttributeOptionIdsSerializer.cs
  90. 15
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IAttributeOptionIdsSerializer.cs
  91. 10
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs
  92. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAttribute.cs
  93. 10
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAttributeOption.cs
  94. 26
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs
  95. 23
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs
  96. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs
  97. 17
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/AttributeOptionIds/AttributeOptionIdsValueComparer.cs
  98. 15
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/AttributeOptionIds/AttributeOptionIdsValueConverter.cs
  99. 20
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/EShopProductsEntityTypeBuilderExtensions.cs
  100. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ProductsDbContextModelCreatingExtensions.cs

2
Directory.Build.props

@ -3,7 +3,7 @@
<AbpVersion>7.1.0</AbpVersion>
<EasyAbpAbpTreesModuleVersion>2.10.0</EasyAbpAbpTreesModuleVersion>
<EasyAbpPaymentServiceModuleVersion>2.4.0</EasyAbpPaymentServiceModuleVersion>
<EasyAbpPaymentServiceModuleVersion>2.5.0</EasyAbpPaymentServiceModuleVersion>
<EasyAbpAbpTagHelperPlusModuleVersion>1.4.0</EasyAbpAbpTagHelperPlusModuleVersion>
<EasyAbpBookingServiceModuleVersion>0.5.0</EasyAbpBookingServiceModuleVersion>
<DaprSdkVersion>1.9.0</DaprSdkVersion>

18
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderDto.cs

@ -3,13 +3,16 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using EasyAbp.EShop.Orders.Localization;
using EasyAbp.EShop.Stores.Stores;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
[Serializable]
public class CreateOrderDto : ExtensibleObject, IMultiStore
public class CreateOrderDto : ExtensibleObject, ICreateOrderInfo
{
[DisplayName("OrderStoreId")]
public Guid StoreId { get; set; }
@ -17,6 +20,8 @@ namespace EasyAbp.EShop.Orders.Orders.Dtos
[DisplayName("OrderCustomerRemark")]
public string CustomerRemark { get; set; }
IEnumerable<ICreateOrderLineInfo> ICreateOrderInfo.OrderLines => OrderLines;
[DisplayName("OrderLine")]
public List<CreateOrderLineDto> OrderLines { get; set; }
@ -26,19 +31,20 @@ namespace EasyAbp.EShop.Orders.Orders.Dtos
{
yield return result;
}
var localizer = validationContext.GetRequiredService<IStringLocalizer<OrdersResource>>();
if (OrderLines.Count == 0)
{
yield return new ValidationResult(
"OrderLines should not be empty.",
localizer[OrdersErrorCodes.OrderLinesShouldNotBeEmpty],
new[] { "OrderLines" }
);
}
if (OrderLines.Any(orderLine => orderLine.Quantity <= 0))
if (OrderLines.Any(orderLine => orderLine.Quantity < 1))
{
yield return new ValidationResult(
"Quantity should be greater than 0.",
localizer[OrdersErrorCodes.QuantityShouldBeGreaterThanZero],
new[] { "OrderLines" }
);
}

8
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/CreateOrderLineDto.cs

@ -6,17 +6,17 @@ using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
[Serializable]
public class CreateOrderLineDto : ExtensibleObject
public class CreateOrderLineDto : ExtensibleObject, ICreateOrderLineInfo
{
public const int MinimumQuantity = 1;
public const int MaximumQuantity = int.MaxValue;
[DisplayName("OrderLineProductId")]
public Guid ProductId { get; set; }
[DisplayName("OrderLineProductSkuId")]
public Guid ProductSkuId { get; set; }
[DisplayName("OrderLineQuantity")]
[Range(MinimumQuantity, MaximumQuantity)]
public int Quantity { get; set; }

16
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderDiscountDto.cs

@ -0,0 +1,16 @@
using System;
namespace EasyAbp.EShop.Orders.Orders.Dtos;
public class OrderDiscountDto
{
public Guid OrderLineId { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public string DisplayName { get; set; }
public decimal DiscountedAmount { get; set; }
}

2
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderDto.cs

@ -47,8 +47,10 @@ namespace EasyAbp.EShop.Orders.Orders.Dtos
public DateTime? PaymentExpiration { get; set; }
IEnumerable<IOrderLine> IOrder.OrderLines => OrderLines;
public List<OrderLineDto> OrderLines { get; set; }
IEnumerable<IOrderExtraFee> IOrder.OrderExtraFees => OrderExtraFees;
public List<OrderExtraFeeDto> OrderExtraFees { get; set; }
}
}

12
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderExtraFeeDto.cs

@ -2,12 +2,18 @@
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class OrderExtraFeeDto
public class OrderExtraFeeDto : IOrderExtraFee
{
public Guid OrderId { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public string DisplayName { get; set; }
public decimal Fee { get; set; }
public decimal RefundAmount { get; set; }
}
}

4
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/BasicOrderCreationAuthorizationHandler.cs

@ -44,7 +44,7 @@ namespace EasyAbp.EShop.Orders.Orders
context.Succeed(requirement);
}
protected virtual Task<bool> IsProductsPublishedAsync(CreateOrderDto input,
protected virtual Task<bool> IsProductsPublishedAsync(ICreateOrderInfo input,
Dictionary<Guid, ProductDto> productDictionary)
{
return Task.FromResult(
@ -53,7 +53,7 @@ namespace EasyAbp.EShop.Orders.Orders
);
}
protected virtual Task<bool> IsInventoriesSufficientAsync(CreateOrderDto input,
protected virtual Task<bool> IsInventoriesSufficientAsync(ICreateOrderInfo input,
Dictionary<Guid, ProductDto> productDictionary)
{
return Task.FromResult(

15
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public interface INewOrderGenerator
{
Task<Order> GenerateAsync(Guid customerUserId, CreateOrderDto input, Dictionary<Guid, ProductDto> productDict,
Dictionary<Guid, ProductDetailDto> productDetailDict);
}
}

12
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs

@ -1,12 +0,0 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using NodaMoney;
namespace EasyAbp.EShop.Orders.Orders;
public interface IOrderLinePriceOverrider
{
Task<Money?> GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product,
ProductSkuDto productSku, Currency effectiveCurrency);
}

43
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs

@ -5,7 +5,6 @@ using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Authorization;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Stores.Stores;
@ -102,21 +101,12 @@ namespace EasyAbp.EShop.Orders.Orders
new OrderOperationAuthorizationRequirement(OrderOperation.Creation)
);
var productDetailIds = input.OrderLines
.Select(dto =>
productDict[dto.ProductId].GetSkuById(dto.ProductSkuId).ProductDetailId ??
productDict[dto.ProductId].ProductDetailId)
.Where(x => x.HasValue)
.Select(x => x.Value)
.ToList();
var productDetailDict = await GetProductDetailDictionaryAsync(productDetailIds);
var productDetailModificationTimeDict =
await GetProductDetailModificationTimeDictionaryAsync(input, productDict);
// Todo: Can we use IProductDataScopedCache/IProductDetailDataScopedCache instead of productDict/productDetailDict?
var order = await _newOrderGenerator.GenerateAsync(CurrentUser.GetId(), input, productDict,
productDetailDict);
await DiscountOrderAsync(order, productDict);
var order = await _newOrderGenerator.GenerateAsync(CurrentUser.GetId(), input,
productDict.ToDictionary(x => x.Key, x => (IProduct)x.Value), productDetailModificationTimeDict);
await Repository.InsertAsync(order, autoSave: true);
@ -131,14 +121,6 @@ namespace EasyAbp.EShop.Orders.Orders
}
}
protected virtual async Task DiscountOrderAsync(Order order, Dictionary<Guid, ProductDto> productDict)
{
foreach (var provider in LazyServiceProvider.LazyGetService<IEnumerable<IOrderDiscountProvider>>())
{
await provider.DiscountAsync(order, productDict);
}
}
protected virtual async Task<Dictionary<Guid, ProductDto>> GetProductDictionaryAsync(
IEnumerable<Guid> productIds)
{
@ -152,14 +134,23 @@ namespace EasyAbp.EShop.Orders.Orders
return dict;
}
protected virtual async Task<Dictionary<Guid, ProductDetailDto>> GetProductDetailDictionaryAsync(
IEnumerable<Guid> productDetailIds)
protected virtual async Task<Dictionary<Guid, DateTime>> GetProductDetailModificationTimeDictionaryAsync(
ICreateOrderInfo input, Dictionary<Guid, ProductDto> productDict)
{
var dict = new Dictionary<Guid, ProductDetailDto>();
var productDetailIds = input.OrderLines
.Select(dto =>
productDict[dto.ProductId].GetSkuById(dto.ProductSkuId).ProductDetailId ??
productDict[dto.ProductId].ProductDetailId)
.Where(x => x.HasValue)
.Select(x => x.Value)
.ToList();
var dict = new Dictionary<Guid, DateTime>();
foreach (var productDetailId in productDetailIds.Distinct())
{
dict.Add(productDetailId, await _productDetailAppService.GetAsync(productDetailId));
var product = await _productDetailAppService.GetAsync(productDetailId);
dict.Add(productDetailId, product.LastModificationTime ?? product.CreationTime);
}
return dict;

3
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderCreationResource.cs

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderCreationResource
{
public CreateOrderDto Input { get; set; }
public ICreateOrderInfo Input { get; set; }
public Dictionary<Guid, ProductDto> ProductDictionary { get; set; }
}

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/OrdersApplicationAutoMapperProfile.cs

@ -14,6 +14,7 @@ namespace EasyAbp.EShop.Orders
* into multiple profile classes for a better organization. */
CreateMap<Order, OrderDto>();
CreateMap<OrderLine, OrderLineDto>();
CreateMap<OrderDiscount, OrderDiscountDto>(MemberList.Destination);
CreateMap<OrderExtraFee, OrderExtraFeeDto>(MemberList.Destination);
}
}

21
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json

@ -45,16 +45,33 @@
"OrderLineTotalDiscount": "Total discount",
"OrderLineActualTotalPrice": "Actual total price",
"OrderLineQuantity": "Quantity",
"OrderDiscount": "Order discount",
"OrderDiscountOrderId": "Order ID",
"OrderDiscountOrderLineId": "Order line ID",
"OrderDiscountName": "Name",
"OrderDiscountKey": "Key",
"OrderDiscountDisplayName": "Display name",
"OrderDiscountDiscountedAmount": "Discounted amount",
"OrderExtraFee": "Order extra fee",
"OrderExtraFeeOrderId": "Order ID",
"OrderExtraFeeName": "Extra fee name",
"OrderExtraFeeKey": "Extra fee key",
"OrderExtraFeeDisplayName": "Display name",
"OrderExtraFeeFee": "Fee",
"OrderExtraFeeRefundAmount": "Refund amount",
"EasyAbp.EShop.Orders:UnexpectedCurrency": "Only the specified currency {expectedCurrency} is allowed.",
"EasyAbp.EShop.Orders:OrderLineInvalidQuantity": "Invalid quantity {quantity} for product {productId} (SKU: {productSkuId}).",
"EasyAbp.EShop.Orders:DiscountAmountOverflow": "The discount amount overflow.",
"EasyAbp.EShop.Orders:DuplicateOrderExtraFee": "The extra fee {extraFeeName} (key: {extraFeeKey}) is existed.",
"EasyAbp.EShop.Orders:DuplicateOrderDiscount": "The discount {discountName} (key: {discountKey}) of order line {orderLineId} already exists.",
"EasyAbp.EShop.Orders:DuplicateOrderExtraFee": "The extra fee {extraFeeName} (key: {extraFeeKey}) already exists.",
"EasyAbp.EShop.Orders:InvalidOrderExtraFee": "The extra fee {extraFee} is invalid.",
"EasyAbp.EShop.Orders:InvalidPayment": "The payment {paymentId} has invalid configurations for the order {orderId}.",
"EasyAbp.EShop.Orders:InvalidRefundAmount": "The refund amount ({amount}) is invalid.",
"EasyAbp.EShop.Orders:InvalidRefundQuantity": "The refund quantity ({quantity}) is invalid.",
"EasyAbp.EShop.Orders:OrderIsInWrongStage": "The order {orderId} is in the wrong stage.",
"EasyAbp.EShop.Orders:ExistFlashSalesProduct": "Exist unexpected flash-sales product",
"EasyAbp.EShop.Orders:ExistFlashSalesProduct": "Exist unexpected flash-sales product.",
"EasyAbp.EShop.Orders:OrderLinesShouldNotBeEmpty": "OrderLines should not be empty.",
"EasyAbp.EShop.Orders:QuantityShouldBeGreaterThanZero": "Quantity should be greater than 0.",
"DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "Currency code",
"Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 code (see https://en.wikipedia.org/wiki/ISO_4217)"
}

17
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json

@ -45,9 +45,24 @@
"OrderLineTotalDiscount": "总折扣",
"OrderLineActualTotalPrice": "折后总价",
"OrderLineQuantity": "数量",
"OrderDiscount": "订单折扣项",
"OrderDiscountOrderId": "订单 ID",
"OrderDiscountOrderLineId": "订单项 ID",
"OrderDiscountName": "折扣项名称",
"OrderDiscountKey": "折扣项 Key",
"OrderDiscountDisplayName": "显示名称",
"OrderDiscountDiscountedAmount": "折扣金额",
"OrderExtraFee": "订单额外费用",
"OrderExtraFeeOrderId": "订单 ID",
"OrderExtraFeeName": "额外费用名称",
"OrderExtraFeeKey": "额外费用 Key",
"OrderExtraFeeDisplayName": "显示名称",
"OrderExtraFeeFee": "金额",
"OrderExtraFeeRefundAmount": "已退款金额",
"EasyAbp.EShop.Orders:UnexpectedCurrency": "只允许指定的{expectedCurrency}货币",
"EasyAbp.EShop.Orders:OrderLineInvalidQuantity": "产品{productId}(SKU: {productSkuId})的{quantity}数量无效",
"EasyAbp.EShop.Orders:DiscountAmountOverflow": "折扣金额溢出",
"EasyAbp.EShop.Orders:DuplicateOrderDiscount": "订单项{orderLineId}的折扣项{discountName}(key: {discountKey})已经存在",
"EasyAbp.EShop.Orders:DuplicateOrderExtraFee": "额外费用{extraFeeName}(key: {extraFeeKey})已经存在",
"EasyAbp.EShop.Orders:InvalidOrderExtraFee": "额外费用{extraFee}无效",
"EasyAbp.EShop.Orders:InvalidPayment": "付款{paymentId}有无效的订单配置{orderId}",
@ -55,6 +70,8 @@
"EasyAbp.EShop.Orders:InvalidRefundQuantity": "退款数量({quantity})无效",
"EasyAbp.EShop.Orders:OrderIsInWrongStage": "订单{orderId}处于错误的阶段",
"EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清单中不允许存在闪购产品",
"EasyAbp.EShop.Orders:OrderLinesShouldNotBeEmpty": "订单项不可为空",
"EasyAbp.EShop.Orders:QuantityShouldBeGreaterThanZero": "购买数量应大于0",
"DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "货币代码",
"Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 货币代码 (详见 https://en.wikipedia.org/wiki/ISO_4217)"
}

17
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json

@ -45,9 +45,24 @@
"OrderLineTotalDiscount": "總折扣",
"OrderLineActualTotalPrice": "折後總價",
"OrderLineQuantity": "數量",
"OrderDiscount": "訂單折扣項",
"OrderDiscountOrderId": "訂單 ID",
"OrderDiscountOrderLineId": "訂單項 ID",
"OrderDiscountName": "折扣項名稱",
"OrderDiscountKey": "折扣項 Key",
"OrderDiscountDisplayName": "顯示名稱",
"OrderDiscountDiscountedAmount": "折扣金額",
"OrderExtraFee": "訂單額外費用",
"OrderExtraFeeOrderId": "訂單 ID",
"OrderExtraFeeName": "額外費用名稱",
"OrderExtraFeeKey": "額外費用 Key",
"OrderExtraFeeDisplayName": "顯示名稱",
"OrderExtraFeeFee": "金額",
"OrderExtraFeeRefundAmount": "已退款金額",
"EasyAbp.EShop.Orders:UnexpectedCurrency": "只允許指定的{expectedCurrency}貨幣",
"EasyAbp.EShop.Orders:OrderLineInvalidQuantity": "產品{productId}(SKU: {productSkuId})的{quantity}數量無效",
"EasyAbp.EShop.Orders:DiscountAmountOverflow": "折扣金額溢出",
"EasyAbp.EShop.Orders:DuplicateOrderDiscount": "訂單項{orderLineId}的折扣項{discountName}(key: {discountKey})已經存在",
"EasyAbp.EShop.Orders:DuplicateOrderExtraFee": "額外費用{extraFeeName}(key: {extraFeeKey})已經存在",
"EasyAbp.EShop.Orders:InvalidOrderExtraFee": "額外費用{extraFee}無效",
"EasyAbp.EShop.Orders:InvalidPayment": "付款{paymentId}有無效的訂單配置{orderId}",
@ -55,6 +70,8 @@
"EasyAbp.EShop.Orders:InvalidRefundQuantity": "退款數量({quantity})無效",
"EasyAbp.EShop.Orders:OrderIsInWrongStage": "訂單{orderId}處於錯誤的階段",
"EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清單中不允許存在閃購產品",
"EasyAbp.EShop.Orders:OrderLinesShouldNotBeEmpty": "訂單項不可為空",
"EasyAbp.EShop.Orders:QuantityShouldBeGreaterThanZero": "購買數量應大於0",
"DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "貨幣代碼",
"Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 貨幣代碼 (詳見 https://en.wikipedia.org/wiki/ISO_4217)"
}

27
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/CreateOrderInfoModel.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Orders.Orders;
[Serializable]
public class CreateOrderInfoModel : ExtensibleObject, ICreateOrderInfo
{
public Guid StoreId { get; set; }
public string CustomerRemark { get; set; }
IEnumerable<ICreateOrderLineInfo> ICreateOrderInfo.OrderLines => OrderLines;
public List<CreateOrderLineInfoModel> OrderLines { get; set; }
public CreateOrderInfoModel()
{
}
public CreateOrderInfoModel(Guid storeId, string customerRemark, List<CreateOrderLineInfoModel> orderLines)
{
StoreId = storeId;
CustomerRemark = customerRemark;
OrderLines = orderLines;
}
}

25
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/CreateOrderLineInfoModel.cs

@ -0,0 +1,25 @@
using System;
using Volo.Abp.ObjectExtending;
namespace EasyAbp.EShop.Orders.Orders;
[Serializable]
public class CreateOrderLineInfoModel : ExtensibleObject, ICreateOrderLineInfo
{
public Guid ProductId { get; set; }
public Guid ProductSkuId { get; set; }
public int Quantity { get; set; }
public CreateOrderLineInfoModel()
{
}
public CreateOrderLineInfoModel(Guid productId, Guid productSkuId, int quantity)
{
ProductId = productId;
ProductSkuId = productSkuId;
Quantity = quantity;
}
}

12
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/ICreateOrderInfo.cs

@ -0,0 +1,12 @@
using System.Collections.Generic;
using EasyAbp.EShop.Stores.Stores;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Orders.Orders;
public interface ICreateOrderInfo : IMultiStore, IHasExtraProperties
{
string CustomerRemark { get; }
IEnumerable<ICreateOrderLineInfo> OrderLines { get; }
}

13
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/ICreateOrderLineInfo.cs

@ -0,0 +1,13 @@
using System;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Orders.Orders;
public interface ICreateOrderLineInfo : IHasExtraProperties
{
Guid ProductId { get; }
Guid ProductSkuId { get; }
int Quantity { get; }
}

45
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrder.cs

@ -1,50 +1,63 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Stores.Stores;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrder : IMultiStore, IHasExtraProperties
{
Guid Id { get; }
[NotNull]
string OrderNumber { get; }
Guid CustomerUserId { get; }
OrderStatus OrderStatus { get; }
[NotNull]
string Currency { get; }
decimal ProductTotalPrice { get; }
decimal TotalDiscount { get; }
decimal TotalPrice { get; }
/// <summary>
/// ActualTotalPrice = TotalPrice - TotalDiscount
/// </summary>
decimal ActualTotalPrice { get; }
decimal RefundAmount { get; }
[CanBeNull]
string CustomerRemark { get; }
[CanBeNull]
string StaffRemark { get; }
Guid? PaymentId { get; }
DateTime? PaidTime { get; }
DateTime? CompletionTime { get; }
DateTime? CanceledTime { get; }
[CanBeNull]
string CancellationReason { get; }
DateTime? ReducedInventoryAfterPlacingTime { get; }
DateTime? ReducedInventoryAfterPaymentTime { get; }
DateTime? PaymentExpiration { get; }
IEnumerable<IOrderLine> OrderLines { get; }
IEnumerable<IOrderExtraFee> OrderExtraFees { get; }
}
}

23
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrderExtraFee.cs

@ -0,0 +1,23 @@
using System;
using JetBrains.Annotations;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderExtraFee
{
Guid OrderId { get; }
[NotNull]
string Name { get; }
[CanBeNull]
string Key { get; }
[CanBeNull]
string DisplayName { get; }
decimal Fee { get; }
decimal RefundAmount { get; }
}
}

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrderLine.cs

@ -1,11 +1,14 @@
using System;
using EasyAbp.EShop.Products.Products;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderLine : IHasExtraProperties
public interface IOrderLine : IHasExtraProperties, IHasProductGroupDisplayName
{
Guid Id { get; }
Guid ProductId { get; }
Guid ProductSkuId { get; }
@ -16,12 +19,13 @@ namespace EasyAbp.EShop.Orders.Orders
DateTime? ProductDetailModificationTime { get; }
[NotNull]
string ProductGroupName { get; }
string ProductGroupDisplayName { get; }
[CanBeNull]
string ProductUniqueName { get; }
[NotNull]
string ProductDisplayName { get; }
/// <summary>
@ -30,12 +34,16 @@ namespace EasyAbp.EShop.Orders.Orders
/// </summary>
InventoryStrategy? ProductInventoryStrategy { get; }
[CanBeNull]
string SkuName { get; }
[CanBeNull]
string SkuDescription { get; }
[CanBeNull]
string MediaResources { get; }
[NotNull]
string Currency { get; }
decimal UnitPrice { get; }

6
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderEto.cs

@ -54,8 +54,12 @@ namespace EasyAbp.EShop.Orders.Orders
public DateTime? PaymentExpiration { get; set; }
IEnumerable<IOrderLine> IOrder.OrderLines => OrderLines;
public List<OrderLineEto> OrderLines { get; set; }
IEnumerable<IOrderExtraFee> IOrder.OrderExtraFees => OrderExtraFees;
public List<OrderExtraFeeEto> OrderExtraFees { get; set; }
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }

20
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderExtraFeeEto.cs

@ -0,0 +1,20 @@
using System;
namespace EasyAbp.EShop.Orders.Orders
{
[Serializable]
public class OrderExtraFeeEto : IOrderExtraFee
{
public Guid OrderId { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public string DisplayName { get; set; }
public decimal Fee { get; set; }
public decimal RefundAmount { get; set; }
}
}

3
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/OrdersErrorCodes.cs

@ -6,11 +6,14 @@
public const string OrderLineInvalidQuantity = "EasyAbp.EShop.Orders:OrderLineInvalidQuantity";
public const string DiscountAmountOverflow = "EasyAbp.EShop.Orders:DiscountAmountOverflow";
public const string DuplicateOrderExtraFee = "EasyAbp.EShop.Orders:DuplicateOrderExtraFee";
public const string DuplicateOrderDiscount = "EasyAbp.EShop.Orders:DuplicateOrderDiscount";
public const string InvalidOrderExtraFee = "EasyAbp.EShop.Orders:InvalidOrderExtraFee";
public const string InvalidPayment = "EasyAbp.EShop.Orders:InvalidPayment";
public const string InvalidRefundAmount = "EasyAbp.EShop.Orders:InvalidRefundAmount";
public const string InvalidRefundQuantity = "EasyAbp.EShop.Orders:InvalidRefundQuantity";
public const string OrderIsInWrongStage = "EasyAbp.EShop.Orders:OrderIsInWrongStage";
public const string ExistFlashSalesProduct = "EasyAbp.EShop.Orders:ExistFlashSalesProduct";
public const string OrderLinesShouldNotBeEmpty = "EasyAbp.EShop.Orders:OrderLinesShouldNotBeEmpty";
public const string QuantityShouldBeGreaterThanZero = "EasyAbp.EShop.Orders:QuantityShouldBeGreaterThanZero";
}
}

16
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/DuplicateOrderDiscountException.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Orders.Orders
{
public class DuplicateOrderDiscountException : BusinessException
{
public DuplicateOrderDiscountException(Guid orderLineId, string discountName, string discountKey) : base(
OrdersErrorCodes.DuplicateOrderDiscount)
{
WithData(nameof(orderLineId), orderLineId);
WithData(nameof(discountName), discountName);
WithData(nameof(discountKey), discountKey);
}
}
}

13
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Orders.Orders
{
public interface INewOrderGenerator
{
Task<Order> GenerateAsync(Guid customerUserId, ICreateOrderInfo input, Dictionary<Guid, IProduct> productDict,
Dictionary<Guid, DateTime> productDetailModificationTimeDict);
}
}

4
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderDiscountProvider.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderDiscountProvider.cs

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderDiscountProvider
{
Task<Order> DiscountAsync(Order order, Dictionary<Guid, ProductDto> productDict);
Task<List<OrderDiscountInfoModel>> GetAllAsync(Order order, Dictionary<Guid, IProduct> productDict);
}
}

7
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs

@ -1,15 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Products;
using NodaMoney;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderExtraFeeProvider
{
Task<List<OrderExtraFeeInfoModel>> GetListAsync(Guid customerUserId, CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict, Currency effectiveCurrency);
Task<List<OrderExtraFeeInfoModel>> GetListAsync(Guid customerUserId, ICreateOrderInfo input,
Dictionary<Guid, IProduct> productDict, Currency effectiveCurrency);
}
}

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

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using NodaMoney;
namespace EasyAbp.EShop.Orders.Orders;
public interface IOrderLinePriceOverrider
{
Task<Money?> GetUnitPriceOrNullAsync(ICreateOrderInfo input, ICreateOrderLineInfo inputOrderLine,
IProduct product, IProductSku productSku, Currency effectiveCurrency);
}

104
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs

@ -2,57 +2,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Orders.Settings;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.Extensions.DependencyInjection;
using NodaMoney;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Domain.Services;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Settings;
using Volo.Abp.Timing;
namespace EasyAbp.EShop.Orders.Orders
{
public class NewOrderGenerator : INewOrderGenerator, ITransientDependency
public class NewOrderGenerator : DomainService, INewOrderGenerator, ITransientDependency
{
private readonly IClock _clock;
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
private readonly ISettingProvider _settingProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IOrderNumberGenerator _orderNumberGenerator;
private readonly IProductSkuDescriptionProvider _productSkuDescriptionProvider;
private readonly IEnumerable<IOrderLinePriceOverrider> _orderLinePriceOverriders;
public NewOrderGenerator(
IClock clock,
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
ISettingProvider settingProvider,
IServiceProvider serviceProvider,
IOrderNumberGenerator orderNumberGenerator,
IProductSkuDescriptionProvider productSkuDescriptionProvider,
IEnumerable<IOrderLinePriceOverrider> orderLinePriceOverriders)
{
_clock = clock;
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
_settingProvider = settingProvider;
_serviceProvider = serviceProvider;
_orderNumberGenerator = orderNumberGenerator;
_productSkuDescriptionProvider = productSkuDescriptionProvider;
_orderLinePriceOverriders = orderLinePriceOverriders;
}
public virtual async Task<Order> GenerateAsync(Guid customerUserId, CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict, Dictionary<Guid, ProductDetailDto> productDetailDict)
public virtual async Task<Order> GenerateAsync(Guid customerUserId, ICreateOrderInfo input,
Dictionary<Guid, IProduct> productDict, Dictionary<Guid, DateTime> productDetailModificationTimeDict)
{
await ValidateInputAsync(input);
var effectiveCurrency = await GetEffectiveCurrencyAsync();
var orderLines = new List<OrderLine>();
@ -60,7 +45,7 @@ namespace EasyAbp.EShop.Orders.Orders
foreach (var inputOrderLine in input.OrderLines)
{
orderLines.Add(await GenerateOrderLineAsync(
input, inputOrderLine, productDict, productDetailDict, effectiveCurrency));
input, inputOrderLine, productDict, productDetailModificationTimeDict, effectiveCurrency));
}
var productTotalPrice = orderLines.Select(x => x.TotalPrice).Sum();
@ -72,8 +57,8 @@ namespace EasyAbp.EShop.Orders.Orders
var totalDiscount = orderLines.Select(x => x.TotalDiscount).Sum();
var order = new Order(
id: _guidGenerator.Create(),
tenantId: _currentTenant.Id,
id: GuidGenerator.Create(),
tenantId: CurrentTenant.Id,
storeId: input.StoreId,
customerUserId: customerUserId,
currency: effectiveCurrency.Code,
@ -82,7 +67,7 @@ namespace EasyAbp.EShop.Orders.Orders
totalPrice: totalPrice,
actualTotalPrice: totalPrice - totalDiscount,
customerRemark: input.CustomerRemark,
paymentExpiration: paymentExpireIn.HasValue ? _clock.Now.Add(paymentExpireIn.Value) : null
paymentExpiration: paymentExpireIn.HasValue ? Clock.Now.Add(paymentExpireIn.Value) : null
);
input.MapExtraPropertiesTo(order, MappingPropertyDefinitionChecks.Destination);
@ -97,16 +82,47 @@ namespace EasyAbp.EShop.Orders.Orders
// see https://github.com/EasyAbp/EShop/issues/214
if (order.OrderLines.All(x => x.ProductInventoryStrategy != InventoryStrategy.ReduceAfterPlacing))
{
order.SetReducedInventoryAfterPlacingTime(_clock.Now);
order.SetReducedInventoryAfterPlacingTime(Clock.Now);
}
await DiscountOrderAsync(order, productDict);
return order;
}
protected virtual Task ValidateInputAsync(ICreateOrderInfo info)
{
if (!info.OrderLines.Any())
{
throw new BusinessException(OrdersErrorCodes.OrderLinesShouldNotBeEmpty);
}
if (info.OrderLines.Any(orderLine => orderLine.Quantity < 1))
{
throw new BusinessException(OrdersErrorCodes.QuantityShouldBeGreaterThanZero);
}
return Task.CompletedTask;
}
protected virtual async Task DiscountOrderAsync(Order order, Dictionary<Guid, IProduct> productDict)
{
foreach (var provider in LazyServiceProvider.LazyGetService<IEnumerable<IOrderDiscountProvider>>())
{
var discounts = await provider.GetAllAsync(order, productDict);
foreach (var discount in discounts)
{
order.AddDiscount(discount.OrderLineId, discount.Name, discount.Key, discount.DisplayName,
discount.DiscountedAmount);
}
}
}
protected virtual async Task AddOrderExtraFeesAsync(Order order, Guid customerUserId,
CreateOrderDto input, Dictionary<Guid, ProductDto> productDict, Currency effectiveCurrency)
ICreateOrderInfo input, Dictionary<Guid, IProduct> productDict, Currency effectiveCurrency)
{
var providers = _serviceProvider.GetServices<IOrderExtraFeeProvider>();
var providers = LazyServiceProvider.LazyGetService<IEnumerable<IOrderExtraFeeProvider>>();
foreach (var provider in providers)
{
@ -115,25 +131,27 @@ namespace EasyAbp.EShop.Orders.Orders
foreach (var infoModel in infoModels)
{
var fee = new Money(infoModel.Fee, effectiveCurrency);
order.AddOrderExtraFee(fee.Amount, infoModel.Name, infoModel.Key);
order.AddOrderExtraFee(fee.Amount, infoModel.Name, infoModel.Key, infoModel.DisplayName);
}
}
}
protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderDto input,
CreateOrderLineDto inputOrderLine, Dictionary<Guid, ProductDto> productDict,
Dictionary<Guid, ProductDetailDto> productDetailDict, Currency effectiveCurrency)
protected virtual async Task<OrderLine> GenerateOrderLineAsync(ICreateOrderInfo input,
ICreateOrderLineInfo inputOrderLine, Dictionary<Guid, IProduct> productDict,
Dictionary<Guid, DateTime> productDetailModificationTimeDict, Currency effectiveCurrency)
{
var product = productDict[inputOrderLine.ProductId];
var productSku = product.GetSkuById(inputOrderLine.ProductSkuId);
if (productSku.Currency != effectiveCurrency.Code)
{
throw new UnexpectedCurrencyException(effectiveCurrency.Code);
}
var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId;
var productDetail = productDetailId.HasValue ? productDetailDict[productDetailId.Value] : null;
var productDetailModificationTime = productDetailId.HasValue
? productDetailModificationTimeDict[productDetailId.Value]
: (DateTime?)null;
if (!inputOrderLine.Quantity.IsBetween(productSku.OrderMinQuantity, productSku.OrderMaxQuantity))
{
@ -145,14 +163,18 @@ namespace EasyAbp.EShop.Orders.Orders
var totalPrice = unitPrice * inputOrderLine.Quantity;
var orderLine = new OrderLine(
id: _guidGenerator.Create(),
id: GuidGenerator.Create(),
productId: product.Id,
productSkuId: productSku.Id,
productDetailId: productDetailId,
productModificationTime: product.LastModificationTime ?? product.CreationTime,
productDetailModificationTime: productDetail?.LastModificationTime ?? productDetail?.CreationTime,
productModificationTime: product is IAuditedObject auditedProduct
? auditedProduct.LastModificationTime ?? auditedProduct.CreationTime
: Clock.Now,
productDetailModificationTime: productDetailModificationTime,
productGroupName: product.ProductGroupName,
productGroupDisplayName: product.ProductGroupDisplayName,
productGroupDisplayName: product is IHasProductGroupDisplayName hasProductGroupDisplayName
? hasProductGroupDisplayName.ProductGroupDisplayName
: product.ProductGroupName,
productUniqueName: product.UniqueName,
productDisplayName: product.DisplayName,
productInventoryStrategy: product.InventoryStrategy,
@ -172,8 +194,8 @@ namespace EasyAbp.EShop.Orders.Orders
return orderLine;
}
protected virtual async Task<Money> GetUnitPriceAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine,
ProductDto product, ProductSkuDto productSku, Currency effectiveCurrency)
protected virtual async Task<Money> GetUnitPriceAsync(ICreateOrderInfo input,
ICreateOrderLineInfo inputOrderLine, IProduct product, IProductSku productSku, Currency effectiveCurrency)
{
foreach (var overrider in _orderLinePriceOverriders)
{

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

@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Stores.Stores;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
@ -12,54 +10,54 @@ namespace EasyAbp.EShop.Orders.Orders
public class Order : FullAuditedAggregateRoot<Guid>, IOrder, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid StoreId { get; protected set; }
[NotNull]
public virtual string OrderNumber { get; protected set; }
public virtual Guid CustomerUserId { get; protected set; }
public virtual OrderStatus OrderStatus { get; protected set; }
[NotNull]
public virtual string Currency { get; protected set; }
public virtual decimal ProductTotalPrice { get; protected set; }
public virtual decimal TotalDiscount { get; protected set; }
public virtual decimal TotalPrice { get; protected set; }
public virtual decimal ActualTotalPrice { get; protected set; }
public virtual decimal RefundAmount { get; protected set; }
[CanBeNull]
public virtual string CustomerRemark { get; protected set; }
[CanBeNull]
public virtual string StaffRemark { get; protected set; }
public virtual Guid? PaymentId { get; protected set; }
public virtual DateTime? PaidTime { get; protected set; }
public virtual DateTime? CompletionTime { get; protected set; }
public virtual DateTime? CanceledTime { get; protected set; }
[CanBeNull]
public virtual string CancellationReason { get; protected set; }
public virtual DateTime? ReducedInventoryAfterPlacingTime { get; protected set; }
public virtual DateTime? ReducedInventoryAfterPaymentTime { get; protected set; }
public virtual DateTime? PaymentExpiration { get; protected set; }
IEnumerable<IOrderLine> IOrder.OrderLines => OrderLines;
public virtual List<OrderLine> OrderLines { get; protected set; }
IEnumerable<IOrderExtraFee> IOrder.OrderExtraFees => OrderExtraFees;
public virtual List<OrderDiscount> OrderDiscounts { get; protected set; }
public virtual List<OrderExtraFee> OrderExtraFees { get; protected set; }
protected Order()
@ -92,9 +90,10 @@ namespace EasyAbp.EShop.Orders.Orders
PaymentExpiration = paymentExpiration;
RefundAmount = 0;
OrderStatus = OrderStatus.Pending;
OrderLines = new List<OrderLine>();
OrderDiscounts = new List<OrderDiscount>();
OrderExtraFees = new List<OrderExtraFee>();
}
@ -187,35 +186,47 @@ namespace EasyAbp.EShop.Orders.Orders
return !(!PaymentId.HasValue || PaidTime.HasValue);
}
public void AddDiscount(Guid orderLineId, decimal expectedDiscountAmount)
public void AddDiscount(Guid orderLineId, [NotNull] string discountName, [CanBeNull] string discountKey,
[CanBeNull] string discountDisplayName, decimal discountedAmount)
{
var orderLine = OrderLines.Single(x => x.Id == orderLineId);
orderLine.AddDiscount(expectedDiscountAmount);
TotalDiscount += expectedDiscountAmount;
ActualTotalPrice -= expectedDiscountAmount;
orderLine.AddDiscount(discountedAmount);
TotalDiscount += discountedAmount;
ActualTotalPrice -= discountedAmount;
if (ActualTotalPrice < decimal.Zero)
{
throw new DiscountAmountOverflowException();
}
if (OrderDiscounts.Any(x => x.OrderLineId == orderLineId && x.Name == discountName && x.Key == discountKey))
{
throw new DuplicateOrderDiscountException(orderLineId, discountName, discountKey);
}
var orderDiscount = new OrderDiscount(
Id, orderLineId, discountName, discountKey, discountDisplayName, discountedAmount);
OrderDiscounts.Add(orderDiscount);
}
public void AddOrderExtraFee(decimal extraFee, [NotNull] string extraFeeName, [CanBeNull] string extraFeeKey)
public void AddOrderExtraFee(decimal extraFee, [NotNull] string extraFeeName, [CanBeNull] string extraFeeKey,
[CanBeNull] string extraFeeDisplayName)
{
if (extraFee <= decimal.Zero)
{
throw new InvalidOrderExtraFeeException(extraFee);
}
var orderExtraFee = new OrderExtraFee(Id, extraFeeName, extraFeeKey, extraFee);
if (OrderExtraFees.Any(x => x.EntityEquals(orderExtraFee)))
if (OrderExtraFees.Any(x => x.Name == extraFeeName && x.Key == extraFeeKey))
{
throw new DuplicateOrderExtraFeeException(extraFeeName, extraFeeKey);
}
var orderExtraFee = new OrderExtraFee(Id, extraFeeName, extraFeeKey, extraFeeDisplayName, extraFee);
OrderExtraFees.Add(orderExtraFee);
TotalPrice += extraFee;
@ -233,4 +244,4 @@ namespace EasyAbp.EShop.Orders.Orders
PaidTime.HasValue && !ReducedInventoryAfterPaymentTime.HasValue;
}
}
}
}

48
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscount.cs

@ -0,0 +1,48 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace EasyAbp.EShop.Orders.Orders;
public class OrderDiscount : Entity
{
public virtual Guid OrderId { get; protected set; }
public virtual Guid OrderLineId { get; protected set; }
[NotNull]
public virtual string Name { get; protected set; }
[CanBeNull]
public virtual string Key { get; protected set; }
[CanBeNull]
public virtual string DisplayName { get; protected set; }
public virtual decimal DiscountedAmount { get; protected set; }
protected OrderDiscount()
{
}
public OrderDiscount(
Guid orderId,
Guid orderLineId,
[NotNull] string name,
[CanBeNull] string key,
[CanBeNull] string displayName,
decimal discountedAmount)
{
OrderId = orderId;
OrderLineId = orderLineId;
Name = name;
Key = key;
DisplayName = displayName;
DiscountedAmount = discountedAmount;
}
public override object[] GetKeys()
{
return new object[] { OrderId, OrderLineId, Name, Key };
}
}

34
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderDiscountInfoModel.cs

@ -0,0 +1,34 @@
using System;
using JetBrains.Annotations;
namespace EasyAbp.EShop.Orders.Orders;
public class OrderDiscountInfoModel
{
public Guid OrderLineId { get; set; }
[NotNull]
public string Name { get; set; }
[CanBeNull]
public string Key { get; set; }
[CanBeNull]
public string DisplayName { get; set; }
public decimal DiscountedAmount { get; set; }
public OrderDiscountInfoModel(
Guid orderLineId,
[NotNull] string name,
[CanBeNull] string key,
[CanBeNull] string displayName,
decimal discountedAmount)
{
OrderLineId = orderLineId;
Name = name;
Key = key ?? string.Empty;
DisplayName = displayName;
DiscountedAmount = discountedAmount;
}
}

7
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderExtraFee.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderExtraFee : Entity
public class OrderExtraFee : Entity, IOrderExtraFee
{
public virtual Guid OrderId { get; protected set; }
@ -13,6 +13,9 @@ namespace EasyAbp.EShop.Orders.Orders
[CanBeNull]
public virtual string Key { get; protected set; }
[CanBeNull]
public virtual string DisplayName { get; protected set; }
public virtual decimal Fee { get; protected set; }
@ -26,11 +29,13 @@ namespace EasyAbp.EShop.Orders.Orders
Guid orderId,
[NotNull] string name,
[CanBeNull] string key,
[CanBeNull] string displayName,
decimal fee)
{
OrderId = orderId;
Name = name;
Key = key;
DisplayName = displayName;
Fee = fee;
}

9
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderExtraFeeInfoModel.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderExtraFeeInfoModel.cs

@ -6,18 +6,23 @@ namespace EasyAbp.EShop.Orders.Orders
{
[NotNull]
public string Name { get; set; }
[CanBeNull]
public string Key { get; set; }
[CanBeNull]
public string DisplayName { get; set; }
public decimal Fee { get; set; }
public OrderExtraFeeInfoModel(
[NotNull] string name,
[CanBeNull] string key,
[CanBeNull] string displayName,
decimal fee)
{
Name = name;
DisplayName = displayName;
Key = key ?? string.Empty;
Fee = fee;
}

8
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderLine.cs

@ -19,30 +19,22 @@ namespace EasyAbp.EShop.Orders.Orders
public virtual DateTime? ProductDetailModificationTime { get; protected set; }
[NotNull]
public virtual string ProductGroupName { get; protected set; }
[NotNull]
public virtual string ProductGroupDisplayName { get; protected set; }
[CanBeNull]
public virtual string ProductUniqueName { get; protected set; }
[NotNull]
public virtual string ProductDisplayName { get; protected set; }
public virtual InventoryStrategy? ProductInventoryStrategy { get; protected set; }
[CanBeNull]
public virtual string SkuName { get; protected set; }
[CanBeNull]
public virtual string SkuDescription { get; protected set; }
[CanBeNull]
public virtual string MediaResources { get; protected set; }
[NotNull]
public virtual string Currency { get; protected set; }
public virtual decimal UnitPrice { get; protected set; }

0
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderLineInvalidQuantityException.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderLineInvalidQuantityException.cs

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

@ -121,6 +121,7 @@ namespace EasyAbp.EShop.Orders.Orders
{
Name = x.Name,
Key = x.Key,
DisplayName = x.DisplayName,
TotalAmount = x.Fee - x.RefundAmount
}));

0
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/UnexpectedCurrencyException.cs → modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/UnexpectedCurrencyException.cs

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

@ -12,6 +12,7 @@ namespace EasyAbp.EShop.Orders
* into multiple profile classes for a better organization. */
CreateMap<Order, OrderEto>();
CreateMap<OrderLine, OrderLineEto>();
CreateMap<OrderExtraFee, OrderExtraFeeEto>();
}
}
}

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.EntityFrameworkCore/EasyAbp/EShop/Orders/EntityFrameworkCore/OrdersDbContext.cs

@ -13,6 +13,7 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
*/
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
public DbSet<OrderDiscount> OrderDiscounts { get; set; }
public DbSet<OrderExtraFee> OrderExtraFees { get; set; }
public OrdersDbContext(DbContextOptions<OrdersDbContext> options)

9
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.EntityFrameworkCore/EasyAbp/EShop/Orders/EntityFrameworkCore/OrdersDbContextModelCreatingExtensions.cs

@ -66,6 +66,15 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
b.Property(x => x.RefundAmount).HasColumnType("decimal(20,8)");
});
builder.Entity<OrderDiscount>(b =>
{
b.ToTable(options.TablePrefix + "OrderDiscounts", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
b.Property(x => x.DiscountedAmount).HasColumnType("decimal(20,8)");
b.HasKey(x => new {x.OrderId, x.OrderLineId, x.Name, x.Key});
});
builder.Entity<OrderExtraFee>(b =>
{
b.ToTable(options.TablePrefix + "OrderExtraFees", options.Schema);

2
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/EasyAbp.EShop.Orders.Application.Tests.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
</PropertyGroup>
<ItemGroup>

26
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs

@ -119,10 +119,7 @@ namespace EasyAbp.EShop.Orders.Orders
{
var orderRepository = ServiceProvider.GetRequiredService<IOrderRepository>();
var orderCount = 0;
await WithUnitOfWorkAsync(async () =>
{
orderCount = await orderRepository.CountAsync();
});
await WithUnitOfWorkAsync(async () => { orderCount = await orderRepository.CountAsync(); });
// Arrange
var checkCreateOrderInput = new CheckCreateOrderInput
@ -131,7 +128,7 @@ namespace EasyAbp.EShop.Orders.Orders
StoreId = OrderTestData.Store1Id,
OrderLines = new List<CreateOrderLineDto>
{
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id,
@ -160,10 +157,7 @@ namespace EasyAbp.EShop.Orders.Orders
{
var orderRepository = ServiceProvider.GetRequiredService<IOrderRepository>();
var orderCount = 0;
await WithUnitOfWorkAsync(async () =>
{
orderCount = await orderRepository.CountAsync();
});
await WithUnitOfWorkAsync(async () => { orderCount = await orderRepository.CountAsync(); });
// Arrange
var checkCreateOrderInput = new CheckCreateOrderInput
@ -172,7 +166,7 @@ namespace EasyAbp.EShop.Orders.Orders
StoreId = OrderTestData.Store1Id,
OrderLines = new List<CreateOrderLineDto>
{
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id,
@ -206,13 +200,13 @@ namespace EasyAbp.EShop.Orders.Orders
StoreId = OrderTestData.Store1Id,
OrderLines = new List<CreateOrderLineDto>
{
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id,
Quantity = 10
},
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku2Id,
@ -684,13 +678,13 @@ namespace EasyAbp.EShop.Orders.Orders
StoreId = OrderTestData.Store1Id,
OrderLines = new List<CreateOrderLineDto>
{
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id,
Quantity = 10
},
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku2Id,
@ -725,13 +719,13 @@ namespace EasyAbp.EShop.Orders.Orders
StoreId = OrderTestData.Store1Id,
OrderLines = new List<CreateOrderLineDto>
{
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id,
Quantity = 10
},
new CreateOrderLineDto
new()
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku2Id,

9
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs

@ -1,6 +1,5 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Products;
using NodaMoney;
using Volo.Abp.DependencyInjection;
@ -9,9 +8,9 @@ namespace EasyAbp.EShop.Orders.Orders;
public class TestOrderLinePriceOverrider : IOrderLinePriceOverrider, ITransientDependency
{
public static decimal Sku3UnitPrice { get; set; } = 100m;
public async Task<Money?> GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine,
ProductDto product, ProductSkuDto productSku, Currency effectiveCurrency)
public async Task<Money?> GetUnitPriceOrNullAsync(ICreateOrderInfo input, ICreateOrderLineInfo inputOrderLine,
IProduct product, IProductSku productSku, Currency effectiveCurrency)
{
if (inputOrderLine.ProductSkuId == OrderTestData.ProductSku3Id)
{

19
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/DemoOrderDiscountProvider.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
namespace EasyAbp.EShop.Orders.Orders;
public class DemoOrderDiscountProvider : IOrderDiscountProvider
{
public Task<List<OrderDiscountInfoModel>> GetAllAsync(Order order, Dictionary<Guid, IProduct> productDict)
{
return Task.FromResult(new List<OrderDiscountInfoModel>
{
new(order.OrderLines.First().Id, "DemoDiscount1", "1", "Demo Discount 1", 0.01m),
new(order.OrderLines.First().Id, "DemoDiscount2", "2", "Demo Discount 2", 0.1m),
});
}
}

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

@ -53,6 +53,7 @@ public class InventoryReductionResultTests : OrdersDomainTestBase
OrderTestData.Order1Id,
"Name",
"Key",
"DisplayName",
0.3m
));
@ -153,6 +154,7 @@ public class InventoryReductionResultTests : OrdersDomainTestBase
var orderExtraFee = eventData.OrderExtraFees[0];
orderExtraFee.Name.ShouldBe("Name");
orderExtraFee.Key.ShouldBe("Key");
orderExtraFee.DisplayName.ShouldBe("DisplayName");
orderExtraFee.TotalAmount.ShouldBe(0.3m);
Order1.CanceledTime.ShouldNotBeNull();

65
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDiscountTests.cs

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Orders.Orders;
public class OrderDiscountTests : OrdersDomainTestBase
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.AddTransient<IOrderDiscountProvider, DemoOrderDiscountProvider>();
base.AfterAddApplication(services);
}
[Fact]
public async Task Should_Create_Order_With_Discount()
{
var orderGenerator = GetRequiredService<INewOrderGenerator>();
var createOrderInfoModel = new CreateOrderInfoModel(OrderTestData.Store1Id, null,
new List<CreateOrderLineInfoModel>
{
new(OrderTestData.Product1Id, OrderTestData.ProductSku1Id, 2)
}
);
var order = await orderGenerator.GenerateAsync(Guid.NewGuid(), createOrderInfoModel,
new Dictionary<Guid, IProduct>
{
{
OrderTestData.Product1Id, new ProductEto
{
Id = OrderTestData.Product1Id,
ProductSkus = new List<ProductSkuEto>
{
new()
{
Id = OrderTestData.ProductSku1Id,
AttributeOptionIds = new List<Guid>(),
Price = 1m,
Currency = "USD",
OrderMinQuantity = 1,
OrderMaxQuantity = 100,
}
}
}
}
}, new Dictionary<Guid, DateTime>());
order.ActualTotalPrice.ShouldBe(1.89m);
order.TotalDiscount.ShouldBe(0.11m);
order.OrderDiscounts.Count.ShouldBe(2);
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);
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);
}
}

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

@ -62,6 +62,7 @@ namespace EasyAbp.EShop.Orders.Orders
OrderTestData.Order1Id,
"Name",
"Key",
"DisplayName",
0.3m
));

2
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.EntityFrameworkCore.Tests/EasyAbp.EShop.Orders.EntityFrameworkCore.Tests.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
</PropertyGroup>
<ItemGroup>

2
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Orders.HttpApi.Client.ConsoleTestApp.csproj

@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
</PropertyGroup>
<ItemGroup>

2
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.MongoDB.Tests/EasyAbp.EShop.Orders.MongoDB.Tests.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
</PropertyGroup>
<ItemGroup>

2
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.TestBase/EasyAbp.EShop.Orders.TestBase.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
</PropertyGroup>
<ItemGroup>

13
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Payments/Dtos/PaymentDto.cs

@ -11,9 +11,9 @@ namespace EasyAbp.EShop.Payments.Payments.Dtos
#region Base properties
public Guid UserId { get; set; }
public string PaymentMethod { get; set; }
public string PayeeAccount { get; set; }
public string ExternalTradingCode { get; set; }
@ -27,15 +27,16 @@ namespace EasyAbp.EShop.Payments.Payments.Dtos
public decimal ActualPaymentAmount { get; set; }
public decimal RefundAmount { get; set; }
public decimal PendingRefundAmount { get; set; }
public DateTime? CompletionTime { get; set; }
public DateTime? CanceledTime { get; set; }
#endregion
IEnumerable<IPaymentItem> IPayment.PaymentItems => PaymentItems;
public List<PaymentItemDto> PaymentItems { get; set; }
}
}

11
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Application.Contracts/EasyAbp/EShop/Payments/Refunds/Dtos/RefundDto.cs

@ -19,19 +19,20 @@ namespace EasyAbp.EShop.Payments.Refunds.Dtos
public string Currency { get; set; }
public decimal RefundAmount { get; set; }
public string DisplayReason { get; set; }
public string CustomerRemark { get; set; }
public string StaffRemark { get; set; }
public DateTime? CompletedTime { get; set; }
public DateTime? CanceledTime { get; set; }
#endregion
IEnumerable<IRefundItem> IRefund.RefundItems => RefundItems;
public List<RefundItemDto> RefundItems { get; set; }
}
}

5
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Payments/EShopPaymentEto.cs

@ -14,7 +14,7 @@ namespace EasyAbp.EShop.Payments.Payments
public Guid Id { get; set; }
public Guid? TenantId { get; set; }
public Guid UserId { get; set; }
public string PaymentMethod { get; set; }
@ -41,6 +41,7 @@ namespace EasyAbp.EShop.Payments.Payments
#endregion
public List<EShopPaymentItemEto> PaymentItems { get; set; } = new List<EShopPaymentItemEto>();
IEnumerable<IPaymentItem> IPayment.PaymentItems => PaymentItems;
public List<EShopPaymentItemEto> PaymentItems { get; set; } = new();
}
}

21
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/EShopRefundEto.cs

@ -16,27 +16,28 @@ namespace EasyAbp.EShop.Payments.Refunds
public Guid? TenantId { get; set; }
public Guid PaymentId { get; set; }
public string RefundPaymentMethod { get; set; }
public string ExternalTradingCode { get; set; }
public string Currency { get; set; }
public decimal RefundAmount { get; set; }
public string DisplayReason { get; set; }
public string CustomerRemark { get; set; }
public string StaffRemark { get; set; }
public DateTime? CompletedTime { get; set; }
public DateTime? CanceledTime { get; set; }
#endregion
public List<EShopRefundItemEto> RefundItems { get; set; } = new List<EShopRefundItemEto>();
IEnumerable<IRefundItem> IRefund.RefundItems => RefundItems;
public List<EShopRefundItemEto> RefundItems { get; set; } = new();
}
}

6
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/OrderExtraFeeRefundInfoModel.cs

@ -6,9 +6,11 @@ namespace EasyAbp.EShop.Payments.Refunds
public class OrderExtraFeeRefundInfoModel
{
public string Name { get; set; }
public string Key { get; set; }
public string DisplayName { get; set; }
public decimal TotalAmount { get; set; }
}
}

6
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain.Shared/EasyAbp/EShop/Payments/Refunds/RefundItemOrderExtraFeeEto.cs

@ -6,9 +6,11 @@ namespace EasyAbp.EShop.Payments.Refunds
public class RefundItemOrderExtraFeeEto
{
public string Name { get; set; }
public string Key { get; set; }
public string DisplayName { get; set; }
public decimal RefundAmount { get; set; }
}
}

28
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Payments/Payment.cs

@ -10,44 +10,44 @@ namespace EasyAbp.EShop.Payments.Payments
public class Payment : FullAuditedAggregateRoot<Guid>, IPayment, IMultiTenant
{
#region Base properties
public virtual Guid? TenantId { get; protected set; }
public virtual Guid UserId { get; protected set; }
[NotNull]
public virtual string PaymentMethod { get; protected set; }
[CanBeNull]
public virtual string PayeeAccount { get; protected set; }
[CanBeNull]
public virtual string ExternalTradingCode { get; protected set; }
[NotNull]
public virtual string Currency { get; protected set; }
public virtual decimal OriginalPaymentAmount { get; protected set; }
public virtual decimal PaymentDiscount { get; protected set; }
public virtual decimal ActualPaymentAmount { get; protected set; }
public virtual decimal RefundAmount { get; protected set; }
public virtual decimal PendingRefundAmount { get; protected set; }
public virtual DateTime? CompletionTime { get; protected set; }
public virtual DateTime? CanceledTime { get; protected set; }
IEnumerable<IPaymentItem> IPayment.PaymentItems => PaymentItems;
public virtual List<PaymentItem> PaymentItems { get; protected set; }
#endregion
protected Payment()
{
}
public void SetPaymentItems(List<PaymentItem> paymentItems)

24
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/Refund.cs

@ -12,41 +12,41 @@ namespace EasyAbp.EShop.Payments.Refunds
#region Base properties
public virtual Guid? TenantId { get; protected set; }
public virtual Guid PaymentId { get; protected set; }
[NotNull]
public virtual string RefundPaymentMethod { get; protected set; }
[NotNull]
public virtual string ExternalTradingCode { get; protected set; }
[NotNull]
public virtual string Currency { get; protected set; }
public virtual decimal RefundAmount { get; protected set; }
public virtual string DisplayReason { get; protected set; }
[CanBeNull]
public virtual string CustomerRemark { get; protected set; }
[CanBeNull]
public virtual string StaffRemark { get; protected set; }
public virtual DateTime? CompletedTime { get; protected set; }
public virtual DateTime? CanceledTime { get; protected set; }
#endregion
IEnumerable<IRefundItem> IRefund.RefundItems => RefundItems;
public virtual List<RefundItem> RefundItems { get; protected set; }
protected Refund()
{
}
public void SetRefundItems(List<RefundItem> refundItems)
{
RefundItems = refundItems;

14
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundItemOrderExtraFee.cs

@ -10,21 +10,29 @@ namespace EasyAbp.EShop.Payments.Refunds
{
[NotNull]
public virtual string Name { get; protected set; }
[CanBeNull]
public virtual string Key { get; protected set; }
[CanBeNull]
public virtual string DisplayName { get; protected set; }
public virtual decimal RefundAmount { get; protected set; }
protected RefundItemOrderExtraFee()
{
}
public RefundItemOrderExtraFee(Guid id, [NotNull] string name, [CanBeNull] string key,
public RefundItemOrderExtraFee(
Guid id,
[NotNull] string name,
[CanBeNull] string key,
[CanBeNull] string displayName,
decimal refundAmount) : base(id)
{
Name = name;
Key = key;
DisplayName = displayName;
RefundAmount = refundAmount;
}
}

2
modules/EasyAbp.EShop.Payments/src/EasyAbp.EShop.Payments.Domain/EasyAbp/EShop/Payments/Refunds/RefundSynchronizer.cs

@ -195,7 +195,7 @@ namespace EasyAbp.EShop.Payments.Refunds
{
refundItemOrderExtraFeeEntity = new RefundItemOrderExtraFee(_guidGenerator.Create(),
orderExtraFeeInfoModel.Name, orderExtraFeeInfoModel.Key,
orderExtraFeeInfoModel.TotalAmount);
orderExtraFeeInfoModel.DisplayName, orderExtraFeeInfoModel.TotalAmount);
refundItem.OrderExtraFees.Add(refundItemOrderExtraFeeEntity);
}

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

@ -82,6 +82,7 @@ public class RefundOrderEventHandlerTests : PaymentsDomainTestBase
{
Name = "Name",
Key = "Key",
DisplayName = "DisplayName",
TotalAmount = 0.6m
});
@ -111,6 +112,7 @@ public class RefundOrderEventHandlerTests : PaymentsDomainTestBase
orderExtraFees.Count.ShouldBe(1);
orderExtraFees[0].Name.ShouldBe("Name");
orderExtraFees[0].Key.ShouldBe("Key");
orderExtraFees[0].DisplayName.ShouldBe("DisplayName");
orderExtraFees[0].TotalAmount.ShouldBe(0.6m);
}
}

3
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductAttributeDto.cs

@ -6,7 +6,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductAttributeDto : ExtensibleFullAuditedEntityDto<Guid>
public class ProductAttributeDto : ExtensibleFullAuditedEntityDto<Guid>, IProductAttribute
{
[Required]
public string DisplayName { get; set; }
@ -15,6 +15,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public int DisplayOrder { get; set; }
IEnumerable<IProductAttributeOption> IProductAttribute.ProductAttributeOptions => ProductAttributeOptions;
public List<ProductAttributeOptionDto> ProductAttributeOptions { get; set; }
}
}

2
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductAttributeOptionDto.cs

@ -5,7 +5,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductAttributeOptionDto : ExtensibleFullAuditedEntityDto<Guid>
public class ProductAttributeOptionDto : ExtensibleFullAuditedEntityDto<Guid>, IProductAttributeOption
{
[Required]
public string DisplayName { get; set; }

22
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs

@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductDto : ExtensibleFullAuditedEntityDto<Guid>
public class ProductDto : ExtensibleFullAuditedEntityDto<Guid>, IProduct, IHasProductGroupDisplayName
{
public Guid StoreId { get; set; }
@ -44,25 +43,10 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public decimal? MaximumPrice { get; set; }
IEnumerable<IProductAttribute> IProduct.ProductAttributes => ProductAttributes;
public List<ProductAttributeDto> ProductAttributes { get; set; }
IEnumerable<IProductSku> IProduct.ProductSkus => ProductSkus;
public List<ProductSkuDto> ProductSkus { get; set; }
public ProductSkuDto GetSkuById(Guid skuId)
{
return ProductSkus.Single(x => x.Id == skuId);
}
public ProductSkuDto FindSkuById(Guid skuId)
{
return ProductSkus.FirstOrDefault(x => x.Id == skuId);
}
public TimeSpan? GetSkuPaymentExpireIn(Guid skuId)
{
var sku = GetSkuById(skuId);
return sku.PaymentExpireIn ?? PaymentExpireIn;
}
}
}

3
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductSkuDto.cs

@ -6,10 +6,9 @@ using Volo.Abp.Data;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductSkuDto : ExtensibleFullAuditedEntityDto<Guid>
public class ProductSkuDto : ExtensibleFullAuditedEntityDto<Guid>, IProductSku
{
public List<Guid> AttributeOptionIds { get; set; }
public string Name { get; set; }
public string Currency { get; set; }

5
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductViewDto.cs

@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Products.Dtos
{
[Serializable]
public class ProductViewDto : ExtensibleCreationAuditedEntityDto<Guid>
public class ProductViewDto : ExtensibleCreationAuditedEntityDto<Guid>, IProductBase, IHasProductGroupDisplayName
{
public Guid StoreId { get; set; }
@ -33,6 +32,8 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public bool IsHidden { get; set; }
public TimeSpan? PaymentExpireIn { get; set; }
public decimal? MinimumPrice { get; set; }
public decimal? MaximumPrice { get; set; }

24
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs

@ -32,7 +32,6 @@ namespace EasyAbp.EShop.Products.Products
private readonly EShopProductsOptions _options;
private readonly IProductInventoryProviderResolver _productInventoryProviderResolver;
private readonly IProductViewCacheKeyProvider _productViewCacheKeyProvider;
private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer;
private readonly IProductRepository _repository;
public ProductAppService(
@ -41,7 +40,6 @@ namespace EasyAbp.EShop.Products.Products
IDistributedCache<ProductViewCacheItem> cache,
IProductInventoryProviderResolver productInventoryProviderResolver,
IProductViewCacheKeyProvider productViewCacheKeyProvider,
IAttributeOptionIdsSerializer attributeOptionIdsSerializer,
IProductRepository repository) : base(repository)
{
_productManager = productManager;
@ -49,7 +47,6 @@ namespace EasyAbp.EShop.Products.Products
_options = options.Value;
_productInventoryProviderResolver = productInventoryProviderResolver;
_productViewCacheKeyProvider = productViewCacheKeyProvider;
_attributeOptionIdsSerializer = attributeOptionIdsSerializer;
_repository = repository;
}
@ -127,21 +124,11 @@ namespace EasyAbp.EShop.Products.Products
return dto;
}
protected virtual async Task UpdateProductAttributesAsync(Product product, CreateUpdateProductDto input)
protected virtual Task UpdateProductAttributesAsync(Product product, CreateUpdateProductDto input)
{
var isProductSkusEmpty = product.ProductSkus.IsNullOrEmpty();
var usedAttributeOptionIds = new HashSet<Guid>();
foreach (var serializedAttributeOptionIds in product.ProductSkus.Select(sku =>
sku.SerializedAttributeOptionIds))
{
foreach (var attributeOptionId in await _attributeOptionIdsSerializer.DeserializeAsync(
serializedAttributeOptionIds))
{
usedAttributeOptionIds.Add(attributeOptionId);
}
}
var usedAttributeOptionIds = new HashSet<Guid>(product.ProductSkus.SelectMany(x => x.AttributeOptionIds));
foreach (var attributeDto in input.ProductAttributes)
{
@ -198,6 +185,7 @@ namespace EasyAbp.EShop.Products.Products
}
product.ProductAttributes.RemoveAll(a => removedAttributeNames.Contains(a.DisplayName));
return Task.CompletedTask;
}
public override async Task<ProductDto> GetAsync(Guid id)
@ -528,11 +516,11 @@ namespace EasyAbp.EShop.Products.Products
return Task.CompletedTask;
}
protected virtual async Task<ProductSku> MapToProductSkuAsync(CreateProductSkuDto createInput)
protected virtual Task<ProductSku> MapToProductSkuAsync(CreateProductSkuDto createInput)
{
var entity = new ProductSku(
GuidGenerator.Create(),
await _attributeOptionIdsSerializer.SerializeAsync(createInput.AttributeOptionIds),
createInput.AttributeOptionIds,
createInput.Name,
createInput.Currency,
createInput.OriginalPrice,
@ -546,7 +534,7 @@ namespace EasyAbp.EShop.Products.Products
createInput.MapExtraPropertiesTo(entity);
return entity;
return Task.FromResult(entity);
}
protected virtual Task MapToProductSkuAsync(UpdateProductSkuDto updateInput, ProductSku entity)

8
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs

@ -19,7 +19,7 @@ namespace EasyAbp.EShop.Products
{
public class ProductsApplicationAutoMapperProfile : Profile, ISingletonDependency
{
public ProductsApplicationAutoMapperProfile(IAttributeOptionIdsSerializer attributeOptionIdsSerializer)
public ProductsApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
@ -33,14 +33,10 @@ namespace EasyAbp.EShop.Products
CreateMap<ProductAttribute, ProductAttributeDto>();
CreateMap<ProductAttributeOption, ProductAttributeOptionDto>();
CreateMap<ProductSku, ProductSkuDto>()
.ForSourceMember(entity => entity.SerializedAttributeOptionIds, opt => opt.DoNotValidate())
.Ignore(dto => dto.AttributeOptionIds)
.Ignore(dto => dto.Price)
.Ignore(dto => dto.DiscountedPrice)
.Ignore(dto => dto.Inventory)
.Ignore(dto => dto.Sold)
.AfterMap(async (src, dest) => dest.AttributeOptionIds =
(await attributeOptionIdsSerializer.DeserializeAsync(src.SerializedAttributeOptionIds)).ToList());
.Ignore(dto => dto.Sold);
CreateMap<CreateUpdateProductDetailDto, ProductDetail>(MemberList.Source)
.ForSourceMember(dto => dto.StoreId, opt => opt.DoNotValidate());
CreateMap<CreateUpdateProductAttributeDto, ProductAttribute>(MemberList.Source);

1
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp.EShop.Products.Domain.Shared.csproj

@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="EasyAbp.Abp.Trees.Domain.Shared" Version="$(EasyAbpAbpTreesModuleVersion)" />
<PackageReference Include="Volo.Abp.Validation" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Json.Abstractions" Version="$(AbpVersion)" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Domain.Shared\EasyAbp.EShop.Stores.Domain.Shared.csproj" />
</ItemGroup>

2
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/EShopProductsDomainSharedModule.cs

@ -3,6 +3,7 @@ using Volo.Abp.Modularity;
using Volo.Abp.Localization;
using EasyAbp.EShop.Products.Localization;
using EasyAbp.EShop.Stores;
using Volo.Abp.Json;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Validation;
using Volo.Abp.Validation.Localization;
@ -12,6 +13,7 @@ namespace EasyAbp.EShop.Products
{
[DependsOn(
typeof(AbpValidationModule),
typeof(AbpJsonAbstractionsModule),
typeof(AbpTreesDomainSharedModule),
typeof(EShopStoresDomainSharedModule)
)]

9
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasAttributeOptionIds.cs

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace EasyAbp.EShop.Products.Products;
public interface IHasAttributeOptionIds
{
List<Guid> AttributeOptionIds { get; }
}

9
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IHasProductGroupDisplayName.cs

@ -0,0 +1,9 @@
using JetBrains.Annotations;
namespace EasyAbp.EShop.Products.Products;
public interface IHasProductGroupDisplayName
{
[NotNull]
string ProductGroupDisplayName { get; }
}

31
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProduct.cs

@ -1,34 +1,11 @@
using System;
using EasyAbp.EShop.Stores.Stores;
using JetBrains.Annotations;
using Volo.Abp.Data;
using System.Collections.Generic;
namespace EasyAbp.EShop.Products.Products
{
public interface IProduct : IHasExtraProperties, IMultiStore
public interface IProduct : IProductBase
{
string ProductGroupName { get; }
IEnumerable<IProductAttribute> ProductAttributes { get; }
Guid? ProductDetailId { get; }
string UniqueName { get; }
string DisplayName { get; }
string Overview { get; }
InventoryStrategy InventoryStrategy { get; }
[CanBeNull] string InventoryProviderName { get; }
string MediaResources { get; }
int DisplayOrder { get; }
bool IsPublished { get; }
bool IsStatic { get; }
bool IsHidden { get; }
IEnumerable<IProductSku> ProductSkus { get; }
}
}

15
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductAttribute.cs

@ -1,13 +1,22 @@
using Volo.Abp.Data;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductAttribute : IHasExtraProperties
{
Guid Id { get; }
[NotNull]
string DisplayName { get; }
[CanBeNull]
string Description { get; }
int DisplayOrder { get; }
IEnumerable<IProductAttributeOption> ProductAttributeOptions { get; }
}
}

12
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductAttributeOption.cs

@ -1,13 +1,19 @@
using Volo.Abp.Data;
using System;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductAttributeOption : IHasExtraProperties
{
Guid Id { get; }
[NotNull]
string DisplayName { get; }
[CanBeNull]
string Description { get; }
int DisplayOrder { get; }
}
}

47
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductBase.cs

@ -0,0 +1,47 @@
using System;
using EasyAbp.EShop.Stores.Stores;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductBase : IHasExtraProperties, IMultiStore
{
Guid Id { get; }
[NotNull]
string ProductGroupName { get; }
Guid? ProductDetailId { get; }
[CanBeNull]
string UniqueName { get; }
[NotNull]
string DisplayName { get; }
/// <summary>
/// Tell your customer what the product is. It is usually shown in the product list.
/// </summary>
[CanBeNull]
string Overview { get; }
InventoryStrategy InventoryStrategy { get; }
[CanBeNull]
string InventoryProviderName { get; }
[CanBeNull]
string MediaResources { get; }
int DisplayOrder { get; }
bool IsPublished { get; }
bool IsStatic { get; }
bool IsHidden { get; }
TimeSpan? PaymentExpireIn { get; }
}
}

24
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductSku.cs

@ -1,26 +1,32 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.Data;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductSku : IHasExtraProperties
public interface IProductSku : IHasAttributeOptionIds, IHasExtraProperties
{
string SerializedAttributeOptionIds { get; }
Guid Id { get; }
[CanBeNull]
string Name { get; }
[NotNull]
string Currency { get; }
decimal? OriginalPrice { get; }
decimal Price { get; }
int OrderMinQuantity { get; }
int OrderMaxQuantity { get; }
TimeSpan? PaymentExpireIn { get; }
[CanBeNull]
string MediaResources { get; }
Guid? ProductDetailId { get; }
}
}

3
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/IProductSkuDescriptionProvider.cs → modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProductSkuDescriptionProvider.cs

@ -1,10 +1,9 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Products.Products
{
public interface IProductSkuDescriptionProvider
{
Task<string> GenerateAsync(ProductDto productDto, ProductSkuDto productSkuDto);
Task<string> GenerateAsync(IProduct product, IProductSku productSku);
}
}

7
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductAttributeEto.cs

@ -10,11 +10,12 @@ namespace EasyAbp.EShop.Products.Products
public Guid Id { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
IEnumerable<IProductAttributeOption> IProductAttribute.ProductAttributeOptions => ProductAttributeOptions;
public List<ProductAttributeOptionEto> ProductAttributeOptions { get; set; }
}
}

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductEto.cs

@ -37,8 +37,12 @@ namespace EasyAbp.EShop.Products.Products
public bool IsHidden { get; set; }
public TimeSpan? PaymentExpireIn { get; set; }
IEnumerable<IProductAttribute> IProduct.ProductAttributes => ProductAttributes;
public List<ProductAttributeEto> ProductAttributes { get; set; }
IEnumerable<IProductSku> IProduct.ProductSkus => ProductSkus;
public List<ProductSkuEto> ProductSkus { get; set; }
}
}

24
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductExtensions.cs

@ -0,0 +1,24 @@
using System;
using System.Linq;
namespace EasyAbp.EShop.Products.Products;
public static class ProductExtensions
{
public static IProductSku GetSkuById(this IProduct product, Guid skuId)
{
return product.ProductSkus.Single(x => x.Id == skuId);
}
public static IProductSku FindSkuById(this IProduct product, Guid skuId)
{
return product.ProductSkus.FirstOrDefault(x => x.Id == skuId);
}
public static TimeSpan? GetSkuPaymentExpireIn(this IProduct product, Guid skuId)
{
var sku = product.GetSkuById(skuId);
return sku.PaymentExpireIn ?? product.PaymentExpireIn;
}
}

7
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/ProductSkuDescriptionProvider.cs → modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductSkuDescriptionProvider.cs

@ -1,7 +1,6 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
@ -16,13 +15,13 @@ namespace EasyAbp.EShop.Products.Products
_jsonSerializer = jsonSerializer;
}
public virtual Task<string> GenerateAsync(ProductDto productDto, ProductSkuDto productSkuDto)
public virtual Task<string> GenerateAsync(IProduct product, IProductSku productSku)
{
var names = new Collection<string[]>();
foreach (var attributeOptionId in productSkuDto.AttributeOptionIds)
foreach (var attributeOptionId in productSku.AttributeOptionIds)
{
names.Add(productDto.ProductAttributes.SelectMany(
names.Add(product.ProductAttributes.SelectMany(
attribute => attribute.ProductAttributeOptions.Where(option => option.Id == attributeOptionId),
(attribute, option) => new [] {attribute.DisplayName, option.DisplayName}).Single());
}

5
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductSkuEto.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Data;
using Volo.Abp.ObjectExtending;
@ -8,7 +9,7 @@ namespace EasyAbp.EShop.Products.Products
{
public Guid Id { get; set; }
public string SerializedAttributeOptionIds { get; set; }
public List<Guid> AttributeOptionIds { get; set; }
public string Name { get; set; }
@ -22,6 +23,8 @@ namespace EasyAbp.EShop.Products.Products
public int OrderMaxQuantity { get; set; }
public TimeSpan? PaymentExpireIn { get; }
public string MediaResources { get; set; }
public Guid? ProductDetailId { get; set; }

7
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Options/ProductGroups/IProductGroup.cs

@ -1,7 +0,0 @@
namespace EasyAbp.EShop.Products.Options.ProductGroups
{
public interface IProductGroup
{
}
}

39
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/AttributeOptionIdsSerializer.cs

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace EasyAbp.EShop.Products.Products
{
public class AttributeOptionIdsSerializer : IAttributeOptionIdsSerializer, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
public AttributeOptionIdsSerializer(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
}
public async Task<string> FormatAsync(string serializedAttributeOptionIds)
{
return await SerializeAsync(await DeserializeAsync(serializedAttributeOptionIds));
}
public Task<string> SerializeAsync(IEnumerable<Guid> attributeOptionIds)
{
if (attributeOptionIds == null)
{
return Task.FromResult(string.Empty);
}
return Task.FromResult(_jsonSerializer.Serialize(attributeOptionIds.OrderBy(x => x)));
}
public Task<IEnumerable<Guid>> DeserializeAsync(string serializedAttributeOptionIds)
{
return Task.FromResult(_jsonSerializer.Deserialize<IEnumerable<Guid>>(serializedAttributeOptionIds));
}
}
}

15
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/IAttributeOptionIdsSerializer.cs

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface IAttributeOptionIdsSerializer
{
Task<string> FormatAsync(string serializedAttributeOptionIds);
Task<string> SerializeAsync(IEnumerable<Guid> attributeOptionIds);
Task<IEnumerable<Guid>> DeserializeAsync(string serializedAttributeOptionIds);
}
}

10
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs

@ -14,21 +14,14 @@ namespace EasyAbp.EShop.Products.Products
public virtual Guid StoreId { get; protected set; }
[NotNull]
public virtual string ProductGroupName { get; protected set; }
public virtual Guid? ProductDetailId { get; protected set; }
[CanBeNull]
public virtual string UniqueName { get; protected set; }
[NotNull]
public virtual string DisplayName { get; protected set; }
/// <summary>
/// Tell your customer what the product is. It is usually shown in the product list.
/// </summary>
[CanBeNull]
public virtual string Overview { get; protected set; }
public virtual InventoryStrategy InventoryStrategy { get; protected set; }
@ -39,7 +32,6 @@ namespace EasyAbp.EShop.Products.Products
/// </summary>
public virtual string InventoryProviderName { get; protected set; }
[CanBeNull]
public virtual string MediaResources { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
@ -52,8 +44,10 @@ namespace EasyAbp.EShop.Products.Products
public virtual TimeSpan? PaymentExpireIn { get; protected set; }
IEnumerable<IProductAttribute> IProduct.ProductAttributes => ProductAttributes;
public virtual List<ProductAttribute> ProductAttributes { get; protected set; }
IEnumerable<IProductSku> IProduct.ProductSkus => ProductSkus;
public virtual List<ProductSku> ProductSkus { get; protected set; }
protected Product()

13
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAttribute.cs

@ -9,16 +9,15 @@ namespace EasyAbp.EShop.Products.Products
{
public class ProductAttribute : FullAuditedEntity<Guid>, IProductAttribute
{
[NotNull]
public virtual string DisplayName { get; protected set; }
[CanBeNull]
public virtual string Description { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
IEnumerable<IProductAttributeOption> IProductAttribute.ProductAttributeOptions => ProductAttributeOptions;
public virtual List<ProductAttributeOption> ProductAttributeOptions { get; protected set; }
protected ProductAttribute()
@ -26,7 +25,7 @@ namespace EasyAbp.EShop.Products.Products
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}
public ProductAttribute(
Guid id,
[NotNull] string displayName,
@ -38,7 +37,7 @@ namespace EasyAbp.EShop.Products.Products
DisplayOrder = displayOrder;
ProductAttributeOptions = new List<ProductAttributeOption>();
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}

10
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductAttributeOption.cs

@ -8,12 +8,10 @@ namespace EasyAbp.EShop.Products.Products
{
public class ProductAttributeOption : FullAuditedEntity<Guid>, IProductAttributeOption
{
[NotNull]
public virtual string DisplayName { get; protected set; }
[CanBeNull]
public virtual string Description { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
@ -23,7 +21,7 @@ namespace EasyAbp.EShop.Products.Products
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}
public ProductAttributeOption(
Guid id,
[NotNull] string displayName,
@ -33,7 +31,7 @@ namespace EasyAbp.EShop.Products.Products
DisplayName = displayName;
Description = description;
DisplayOrder = displayOrder;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}

26
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs

@ -19,7 +19,6 @@ namespace EasyAbp.EShop.Products.Products
private readonly IProductDetailRepository _productDetailRepository;
private readonly IProductCategoryRepository _productCategoryRepository;
private readonly IProductInventoryProviderResolver _productInventoryProviderResolver;
private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer;
private readonly IProductGroupConfigurationProvider _productGroupConfigurationProvider;
public ProductManager(
@ -28,7 +27,6 @@ namespace EasyAbp.EShop.Products.Products
IProductDetailRepository productDetailRepository,
IProductCategoryRepository productCategoryRepository,
IProductInventoryProviderResolver productInventoryProviderResolver,
IAttributeOptionIdsSerializer attributeOptionIdsSerializer,
IProductGroupConfigurationProvider productGroupConfigurationProvider)
{
_productRepository = productRepository;
@ -36,7 +34,6 @@ namespace EasyAbp.EShop.Products.Products
_productDetailRepository = productDetailRepository;
_productCategoryRepository = productCategoryRepository;
_productInventoryProviderResolver = productInventoryProviderResolver;
_attributeOptionIdsSerializer = attributeOptionIdsSerializer;
_productGroupConfigurationProvider = productGroupConfigurationProvider;
}
@ -120,8 +117,6 @@ namespace EasyAbp.EShop.Products.Products
[UnitOfWork]
public virtual async Task<Product> CreateSkuAsync(Product product, ProductSku productSku)
{
// productSku.SetSerializedAttributeOptionIds(await _attributeOptionIdsSerializer.FormatAsync(productSku.SerializedAttributeOptionIds));
await CheckSkuAttributeOptionsAsync(product, productSku);
await CheckProductSkuNameUniqueAsync(product, productSku);
@ -151,25 +146,22 @@ namespace EasyAbp.EShop.Products.Products
return Task.CompletedTask;
}
protected virtual async Task CheckSkuAttributeOptionsAsync(Product product, ProductSku productSku)
protected virtual Task CheckSkuAttributeOptionsAsync(Product product, ProductSku productSku)
{
var attributeOptionIds =
(await _attributeOptionIdsSerializer.DeserializeAsync(productSku.SerializedAttributeOptionIds))
.ToList();
if (!product.ProductAttributes.TrueForAll(attribute =>
attribute.ProductAttributeOptions.Select(option => option.Id).Intersect(attributeOptionIds)
.Count() == 1))
if (!product.ProductAttributes.TrueForAll(attribute => attribute.ProductAttributeOptions
.Select(option => option.Id).Intersect(productSku.AttributeOptionIds).Count() == 1))
{
throw new ProductSkuIncorrectAttributeOptionsException(product.Id,
productSku.SerializedAttributeOptionIds);
productSku.AttributeOptionIds.JoinAsString(","));
}
if (product.ProductSkus.Where(sku => sku.Id != productSku.Id).FirstOrDefault(sku =>
sku.SerializedAttributeOptionIds.Equals(productSku.SerializedAttributeOptionIds)) != null)
if (product.ProductSkus.Where(sku => sku.Id != productSku.Id).Any(sku =>
sku.AttributeOptionIds.Order().SequenceEqual(productSku.AttributeOptionIds.Order())))
{
throw new ProductSkuDuplicatedException(product.Id, productSku.SerializedAttributeOptionIds);
throw new ProductSkuDuplicatedException(product.Id, productSku.AttributeOptionIds.JoinAsString(","));
}
return Task.CompletedTask;
}
[UnitOfWork]

23
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using NodaMoney;
using Volo.Abp;
@ -10,13 +11,20 @@ namespace EasyAbp.EShop.Products.Products
{
public class ProductSku : FullAuditedEntity<Guid>, IProductSku
{
[NotNull]
public virtual string SerializedAttributeOptionIds { get; protected set; }
private List<Guid> _attributeOptionIds;
public virtual List<Guid> AttributeOptionIds
{
get => _attributeOptionIds;
protected set
{
_attributeOptionIds = value;
_attributeOptionIds.Sort();
}
}
[CanBeNull]
public virtual string Name { get; protected set; }
[NotNull]
public virtual string Currency { get; protected set; }
public virtual decimal? OriginalPrice { get; protected set; }
@ -29,7 +37,6 @@ namespace EasyAbp.EShop.Products.Products
public virtual TimeSpan? PaymentExpireIn { get; protected set; }
[CanBeNull]
public virtual string MediaResources { get; protected set; }
public virtual Guid? ProductDetailId { get; protected set; }
@ -44,7 +51,7 @@ namespace EasyAbp.EShop.Products.Products
public ProductSku(
Guid id,
[NotNull] string serializedAttributeOptionIds,
List<Guid> attributeOptionIds,
[CanBeNull] string name,
[NotNull] string currency,
decimal? originalPrice,
@ -58,8 +65,8 @@ namespace EasyAbp.EShop.Products.Products
Check.NotNullOrWhiteSpace(currency, nameof(currency));
var nodaCurrency = NodaMoney.Currency.FromCode(currency);
SerializedAttributeOptionIds =
Check.NotNullOrWhiteSpace(serializedAttributeOptionIds, nameof(serializedAttributeOptionIds));
Check.NotNullOrEmpty(attributeOptionIds, nameof(attributeOptionIds));
AttributeOptionIds = attributeOptionIds;
Name = name?.Trim();
Currency = nodaCurrency.Code;
OriginalPrice = originalPrice.HasValue ? new Money(originalPrice.Value, nodaCurrency).Amount : null;

7
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs

@ -4,7 +4,8 @@ using Volo.Abp.MultiTenancy;
namespace EasyAbp.EShop.Products.Products
{
public class ProductView : CreationAuditedAggregateRoot<Guid>, IProduct, IMultiTenant
public class ProductView : CreationAuditedAggregateRoot<Guid>,
IProductBase, IHasProductGroupDisplayName, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
@ -36,6 +37,8 @@ namespace EasyAbp.EShop.Products.Products
public virtual bool IsHidden { get; protected set; }
public virtual TimeSpan? PaymentExpireIn { get; protected set; }
#endregion
public virtual string ProductGroupDisplayName { get; protected set; }
@ -64,6 +67,7 @@ namespace EasyAbp.EShop.Products.Products
bool isPublished,
bool isStatic,
bool isHidden,
TimeSpan? paymentExpireIn,
string mediaResources,
int displayOrder,
string productGroupDisplayName,
@ -84,6 +88,7 @@ namespace EasyAbp.EShop.Products.Products
IsPublished = isPublished;
IsStatic = isStatic;
IsHidden = isHidden;
PaymentExpireIn = paymentExpireIn;
MediaResources = mediaResources;
DisplayOrder = displayOrder;

17
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/AttributeOptionIds/AttributeOptionIdsValueComparer.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace EasyAbp.EShop.Products.EntityFrameworkCore.AttributeOptionIds;
public class AttributeOptionIdsValueComparer : ValueComparer<List<Guid>>
{
public AttributeOptionIdsValueComparer()
: base(
(d1, d2) => d1.SequenceEqual(d2),
d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())),
d => new List<Guid>(d))
{
}
}

15
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/AttributeOptionIds/AttributeOptionIdsValueConverter.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EasyAbp.EShop.Products.EntityFrameworkCore.AttributeOptionIds;
public class AttributeOptionIdsValueConverter : ValueConverter<List<Guid>, string>
{
public AttributeOptionIdsValueConverter() : base(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<List<Guid>>(v, (JsonSerializerOptions)null))
{
}
}

20
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/EShopProductsEntityTypeBuilderExtensions.cs

@ -0,0 +1,20 @@
using System;
using EasyAbp.EShop.Products.EntityFrameworkCore.AttributeOptionIds;
using EasyAbp.EShop.Products.Products;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EasyAbp.EShop.Products.EntityFrameworkCore;
public static class EShopProductsEntityTypeBuilderExtensions
{
public static void TryConfigureAttributeOptionIds(this EntityTypeBuilder b)
{
if (b.Metadata.ClrType.IsAssignableTo<IHasAttributeOptionIds>())
{
b.Property(nameof(IHasAttributeOptionIds.AttributeOptionIds))
.HasConversion<AttributeOptionIdsValueConverter>()
.Metadata.SetValueComparer(new AttributeOptionIdsValueComparer());
}
}
}

1
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.EntityFrameworkCore/EasyAbp/EShop/Products/EntityFrameworkCore/ProductsDbContextModelCreatingExtensions.cs

@ -81,6 +81,7 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
{
b.ToTable(options.TablePrefix + "ProductSkus", options.Schema);
b.ConfigureByConvention();
b.TryConfigureAttributeOptionIds();
/* Configure more properties here */
b.Property(x => x.Price).HasColumnType("decimal(20,8)");
b.Property(x => x.OriginalPrice).HasColumnType("decimal(20,8)");

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save