Browse Source

Merge pull request #143 from EasyAbp/redesign-product-detail

Make ProductDetail non-essential and reusable
pull/148/head
Super 5 years ago
committed by GitHub
parent
commit
bc938575ec
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderLineDto.cs
  2. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/INewOrderGenerator.cs
  3. 14
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs
  4. 33
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/OrderAppService.cs
  5. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/IOrderLine.cs
  6. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderLineEto.cs
  7. 8
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Orders/OrderLine.cs
  8. 140
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs
  9. 8
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.TestBase/OrderTestData.cs
  10. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/ProductDetails/Dtos/ProductDetailDto.cs
  11. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/CreateUpdateProductDto.cs
  12. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductDto.cs
  13. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application.Contracts/EasyAbp/EShop/Products/Products/Dtos/ProductViewDto.cs
  14. 15
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs
  15. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/cs.json
  16. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/en.json
  17. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/pl.json
  18. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/pt-BR.json
  19. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/sl.json
  20. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/tr.json
  21. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/vi.json
  22. 6
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/zh-Hans.json
  23. 6
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/zh-Hant.json
  24. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/IProduct.cs
  25. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Products/ProductEto.cs
  26. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsErrorCodes.cs
  27. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs
  28. 13
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductDetailHasBeenUsedException.cs
  29. 39
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductManager.cs
  30. 2
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs
  31. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductView.cs
  32. 15
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/CreateModal.cshtml.cs
  33. 49
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/EditModal.cshtml.cs
  34. 6
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/ViewModels/CreateEditProductDetailViewModel.cs
  35. 4
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/ViewModels/CreateEditProductViewModel.cs
  36. 14
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/CreateModal.cshtml.cs
  37. 62
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/EditModal.cshtml.cs
  38. 23
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/ViewModels/CreateEditSkuProductDetailViewModel.cs
  39. 6
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/ViewModels/EditProductSkuViewModel.cs
  40. 7
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsWebAutoMapperProfile.cs
  41. 74
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs
  42. 110
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs
  43. 4
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestData.cs
  44. 3
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs
  45. 5131
      samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411111127_MadeProductDetailIdNullable.Designer.cs
  46. 70
      samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411111127_MadeProductDetailIdNullable.cs
  47. 5134
      samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411204916_AddedProductDetailIdToOrderLine.Designer.cs
  48. 26
      samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411204916_AddedProductDetailIdToOrderLine.cs
  49. 11
      samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs

4
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application.Contracts/EasyAbp/EShop/Orders/Orders/Dtos/OrderLineDto.cs

@ -13,9 +13,11 @@ namespace EasyAbp.EShop.Orders.Orders.Dtos
public Guid ProductSkuId { get; set; } public Guid ProductSkuId { get; set; }
public Guid? ProductDetailId { get; set; }
public DateTime ProductModificationTime { get; set; } public DateTime ProductModificationTime { get; set; }
public DateTime ProductDetailModificationTime { get; set; } public DateTime? ProductDetailModificationTime { get; set; }
public string ProductGroupName { get; set; } public string ProductGroupName { get; set; }

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

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

14
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos; using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos; using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -40,13 +41,13 @@ namespace EasyAbp.EShop.Orders.Orders
} }
public virtual async Task<Order> GenerateAsync(Guid customerUserId, CreateOrderDto input, public virtual async Task<Order> GenerateAsync(Guid customerUserId, CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict) Dictionary<Guid, ProductDto> productDict, Dictionary<Guid, ProductDetailDto> productDetailDict)
{ {
var orderLines = new List<OrderLine>(); var orderLines = new List<OrderLine>();
foreach (var inputOrderLine in input.OrderLines) foreach (var inputOrderLine in input.OrderLines)
{ {
orderLines.Add(await GenerateOrderLineAsync(input, inputOrderLine, productDict)); orderLines.Add(await GenerateOrderLineAsync(input, inputOrderLine, productDict, productDetailDict));
} }
var storeCurrency = await GetStoreCurrencyAsync(input.StoreId); var storeCurrency = await GetStoreCurrencyAsync(input.StoreId);
@ -105,11 +106,15 @@ namespace EasyAbp.EShop.Orders.Orders
} }
protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderDto input, protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderDto input,
CreateOrderLineDto inputOrderLine, Dictionary<Guid, ProductDto> productDict) CreateOrderLineDto inputOrderLine, Dictionary<Guid, ProductDto> productDict,
Dictionary<Guid, ProductDetailDto> productDetailDict)
{ {
var product = productDict[inputOrderLine.ProductId]; var product = productDict[inputOrderLine.ProductId];
var productSku = product.GetSkuById(inputOrderLine.ProductSkuId); var productSku = product.GetSkuById(inputOrderLine.ProductSkuId);
var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId;
var productDetail = productDetailId.HasValue ? productDetailDict[productDetailId.Value] : null;
if (!inputOrderLine.Quantity.IsBetween(productSku.OrderMinQuantity, productSku.OrderMaxQuantity)) if (!inputOrderLine.Quantity.IsBetween(productSku.OrderMinQuantity, productSku.OrderMaxQuantity))
{ {
throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, inputOrderLine.Quantity); throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, inputOrderLine.Quantity);
@ -121,8 +126,9 @@ namespace EasyAbp.EShop.Orders.Orders
id: _guidGenerator.Create(), id: _guidGenerator.Create(),
productId: product.Id, productId: product.Id,
productSkuId: productSku.Id, productSkuId: productSku.Id,
productDetailId: productDetailId,
productModificationTime: product.LastModificationTime ?? product.CreationTime, productModificationTime: product.LastModificationTime ?? product.CreationTime,
productDetailModificationTime: productSku.LastModificationTime ?? productSku.CreationTime, productDetailModificationTime: productDetail?.LastModificationTime ?? productDetail?.CreationTime,
productGroupName: product.ProductGroupName, productGroupName: product.ProductGroupName,
productGroupDisplayName: product.ProductGroupDisplayName, productGroupDisplayName: product.ProductGroupDisplayName,
productUniqueName: product.UniqueName, productUniqueName: product.UniqueName,

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

@ -4,6 +4,8 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Authorization; using EasyAbp.EShop.Orders.Authorization;
using EasyAbp.EShop.Orders.Orders.Dtos; 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;
using EasyAbp.EShop.Products.Products.Dtos; using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Stores.Stores; using EasyAbp.EShop.Stores.Stores;
@ -24,17 +26,20 @@ namespace EasyAbp.EShop.Orders.Orders
private readonly INewOrderGenerator _newOrderGenerator; private readonly INewOrderGenerator _newOrderGenerator;
private readonly IProductAppService _productAppService; private readonly IProductAppService _productAppService;
private readonly IProductDetailAppService _productDetailAppService;
private readonly IOrderManager _orderManager; private readonly IOrderManager _orderManager;
private readonly IOrderRepository _repository; private readonly IOrderRepository _repository;
public OrderAppService( public OrderAppService(
INewOrderGenerator newOrderGenerator, INewOrderGenerator newOrderGenerator,
IProductAppService productAppService, IProductAppService productAppService,
IProductDetailAppService productDetailAppService,
IOrderManager orderManager, IOrderManager orderManager,
IOrderRepository repository) : base(repository) IOrderRepository repository) : base(repository)
{ {
_newOrderGenerator = newOrderGenerator; _newOrderGenerator = newOrderGenerator;
_productAppService = productAppService; _productAppService = productAppService;
_productDetailAppService = productDetailAppService;
_orderManager = orderManager; _orderManager = orderManager;
_repository = repository; _repository = repository;
} }
@ -94,7 +99,18 @@ namespace EasyAbp.EShop.Orders.Orders
new OrderOperationAuthorizationRequirement(OrderOperation.Creation) new OrderOperationAuthorizationRequirement(OrderOperation.Creation)
); );
var order = await _newOrderGenerator.GenerateAsync(CurrentUser.GetId(), input, productDict); 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);
// Todo: Can we use IProductDataScopedCache/IProductDetailDataScopedCache instead of productDict/productDetailDict?
var order = await _newOrderGenerator.GenerateAsync(CurrentUser.GetId(), input, productDict, productDetailDict);
await DiscountOrderAsync(order, productDict); await DiscountOrderAsync(order, productDict);
@ -116,7 +132,7 @@ namespace EasyAbp.EShop.Orders.Orders
{ {
var dict = new Dictionary<Guid, ProductDto>(); var dict = new Dictionary<Guid, ProductDto>();
foreach (var productId in productIds.Distinct().ToList()) foreach (var productId in productIds.Distinct())
{ {
dict.Add(productId, await _productAppService.GetAsync(productId)); dict.Add(productId, await _productAppService.GetAsync(productId));
} }
@ -124,6 +140,19 @@ namespace EasyAbp.EShop.Orders.Orders
return dict; return dict;
} }
protected virtual async Task<Dictionary<Guid, ProductDetailDto>> GetProductDetailDictionaryAsync(
IEnumerable<Guid> productDetailIds)
{
var dict = new Dictionary<Guid, ProductDetailDto>();
foreach (var productDetailId in productDetailIds.Distinct())
{
dict.Add(productDetailId, await _productDetailAppService.GetAsync(productDetailId));
}
return dict;
}
[RemoteService(false)] [RemoteService(false)]
public override Task<OrderDto> UpdateAsync(Guid id, CreateOrderDto input) public override Task<OrderDto> UpdateAsync(Guid id, CreateOrderDto input)
{ {

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

@ -9,9 +9,11 @@ namespace EasyAbp.EShop.Orders.Orders
Guid ProductSkuId { get; } Guid ProductSkuId { get; }
Guid? ProductDetailId { get; }
DateTime ProductModificationTime { get; } DateTime ProductModificationTime { get; }
DateTime ProductDetailModificationTime { get; } DateTime? ProductDetailModificationTime { get; }
string ProductGroupName { get; } string ProductGroupName { get; }

4
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Orders/OrderLineEto.cs

@ -12,9 +12,11 @@ namespace EasyAbp.EShop.Orders.Orders
public Guid ProductSkuId { get; set; } public Guid ProductSkuId { get; set; }
public Guid? ProductDetailId { get; set; }
public DateTime ProductModificationTime { get; set; } public DateTime ProductModificationTime { get; set; }
public DateTime ProductDetailModificationTime { get; set; } public DateTime? ProductDetailModificationTime { get; set; }
public string ProductGroupName { get; set; } public string ProductGroupName { get; set; }

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

@ -13,9 +13,11 @@ namespace EasyAbp.EShop.Orders.Orders
public virtual Guid ProductSkuId { get; protected set; } public virtual Guid ProductSkuId { get; protected set; }
public virtual Guid? ProductDetailId { get; protected set; }
public virtual DateTime ProductModificationTime { get; protected set; } public virtual DateTime ProductModificationTime { get; protected set; }
public virtual DateTime ProductDetailModificationTime { get; protected set; } public virtual DateTime? ProductDetailModificationTime { get; protected set; }
[NotNull] [NotNull]
public virtual string ProductGroupName { get; protected set; } public virtual string ProductGroupName { get; protected set; }
@ -68,8 +70,9 @@ namespace EasyAbp.EShop.Orders.Orders
Guid id, Guid id,
Guid productId, Guid productId,
Guid productSkuId, Guid productSkuId,
Guid? productDetailId,
DateTime productModificationTime, DateTime productModificationTime,
DateTime productDetailModificationTime, DateTime? productDetailModificationTime,
[NotNull] string productGroupName, [NotNull] string productGroupName,
[NotNull] string productGroupDisplayName, [NotNull] string productGroupDisplayName,
[CanBeNull] string productUniqueName, [CanBeNull] string productUniqueName,
@ -86,6 +89,7 @@ namespace EasyAbp.EShop.Orders.Orders
{ {
ProductId = productId; ProductId = productId;
ProductSkuId = productSkuId; ProductSkuId = productSkuId;
ProductDetailId = productDetailId;
ProductModificationTime = productModificationTime; ProductModificationTime = productModificationTime;
ProductDetailModificationTime = productDetailModificationTime; ProductDetailModificationTime = productDetailModificationTime;
ProductGroupName = productGroupName; ProductGroupName = productGroupName;

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

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos; 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;
using EasyAbp.EShop.Products.Products.Dtos; using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -33,10 +35,12 @@ namespace EasyAbp.EShop.Orders.Orders
CreationTime = DateTime.Now, CreationTime = DateTime.Now,
IsPublished = true, IsPublished = true,
Id = OrderTestData.Product1Id, Id = OrderTestData.Product1Id,
StoreId = OrderTestData.Store1Id,
ProductGroupName = "Default", ProductGroupName = "Default",
ProductGroupDisplayName = "Default", ProductGroupDisplayName = "Default",
UniqueName = "Pencil", UniqueName = "Pencil",
DisplayName = "Hello pencil", DisplayName = "Hello pencil",
ProductDetailId = OrderTestData.ProductDetail1Id,
ProductSkus = new List<ProductSkuDto> ProductSkus = new List<ProductSkuDto>
{ {
new ProductSkuDto new ProductSkuDto
@ -48,6 +52,18 @@ namespace EasyAbp.EShop.Orders.Orders
AttributeOptionIds = new List<Guid>(), AttributeOptionIds = new List<Guid>(),
Price = 1m, Price = 1m,
Currency = "CNY", Currency = "CNY",
ProductDetailId = null
},
new ProductSkuDto
{
Id = OrderTestData.ProductSku2Id,
Name = "My SKU 2",
OrderMinQuantity = 0,
OrderMaxQuantity = 100,
AttributeOptionIds = new List<Guid>(),
Price = 2m,
Currency = "CNY",
ProductDetailId = OrderTestData.ProductDetail2Id
} }
}, },
InventoryStrategy = InventoryStrategy.NoNeed, InventoryStrategy = InventoryStrategy.NoNeed,
@ -55,6 +71,28 @@ namespace EasyAbp.EShop.Orders.Orders
})); }));
services.AddTransient(_ => productAppService); services.AddTransient(_ => productAppService);
var productDetailAppService = Substitute.For<IProductDetailAppService>();
productDetailAppService.GetAsync(OrderTestData.ProductDetail1Id).Returns(Task.FromResult(
new ProductDetailDto
{
Id = OrderTestData.ProductDetail1Id,
CreationTime = OrderTestData.ProductDetailLastModificationTime,
LastModificationTime = OrderTestData.ProductDetailLastModificationTime,
StoreId = OrderTestData.Store1Id,
Description = "My Details 1"
}));
productDetailAppService.GetAsync(OrderTestData.ProductDetail2Id).Returns(Task.FromResult(
new ProductDetailDto
{
Id = OrderTestData.ProductDetail2Id,
StoreId = OrderTestData.Store1Id,
Description = "My Details 2"
}));
services.AddTransient(_ => productDetailAppService);
} }
[Fact] [Fact]
@ -72,55 +110,26 @@ namespace EasyAbp.EShop.Orders.Orders
ProductId = OrderTestData.Product1Id, ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku1Id, ProductSkuId = OrderTestData.ProductSku1Id,
Quantity = 10 Quantity = 10
},
new CreateOrderLineDto
{
ProductId = OrderTestData.Product1Id,
ProductSkuId = OrderTestData.ProductSku2Id,
Quantity = 1
} }
} }
}; };
OrderDto createResponse = null;
// Act // Act
var createResponse = await _orderAppService.CreateAsync(createOrderDto); await WithUnitOfWorkAsync(async () =>
{
createResponse = await _orderAppService.CreateAsync(createOrderDto);
});
var response = await _orderAppService.GetAsync(createResponse.Id); var response = await _orderAppService.GetAsync(createResponse.Id);
// Assert // Assert
response.ShouldNotBeNull();
response.Currency.ShouldBe("CNY");
response.CanceledTime.ShouldBeNull();
response.CancellationReason.ShouldBeNullOrEmpty();
response.CompletionTime.ShouldBeNull();
response.CustomerRemark.ShouldBe("customer remark");
response.OrderNumber.ShouldNotBeNull();
response.OrderStatus.ShouldBe(OrderStatus.Pending);
response.PaidTime.ShouldBeNull();
response.PaymentId.ShouldBeNull();
response.RefundAmount.ShouldBe(0m);
response.StaffRemark.ShouldBeNullOrEmpty();
response.StoreId.ShouldBe(OrderTestData.Store1Id);
response.TotalDiscount.ShouldBe(0m);
response.TotalPrice.ShouldBe(10m);
response.ActualTotalPrice.ShouldBe(10m);
response.CustomerUserId.ShouldBe(Guid.Parse("2e701e62-0953-4dd3-910b-dc6cc93ccb0d"));
response.ProductTotalPrice.ShouldBe(10m);
response.ReducedInventoryAfterPaymentTime.ShouldBeNull();
response.ReducedInventoryAfterPlacingTime.ShouldNotBeNull();
response.OrderLines.Count.ShouldBe(1);
var responseOrderLine = response.OrderLines.First();
responseOrderLine.ProductId.ShouldBe(OrderTestData.Product1Id);
responseOrderLine.ProductSkuId.ShouldBe(OrderTestData.ProductSku1Id);
responseOrderLine.ProductDisplayName.ShouldBe("Hello pencil");
responseOrderLine.ProductUniqueName.ShouldBe("Pencil");
responseOrderLine.ProductGroupName.ShouldBe("Default");
responseOrderLine.ProductGroupDisplayName.ShouldBe("Default");
responseOrderLine.SkuName.ShouldBe("My SKU");
responseOrderLine.UnitPrice.ShouldBe(1m);
responseOrderLine.TotalPrice.ShouldBe(10m);
responseOrderLine.TotalDiscount.ShouldBe(0m);
responseOrderLine.ActualTotalPrice.ShouldBe(10m);
responseOrderLine.Currency.ShouldBe("CNY");
responseOrderLine.Quantity.ShouldBe(10);
responseOrderLine.ProductModificationTime.ShouldBe(OrderTestData.ProductLastModificationTime);
responseOrderLine.RefundAmount.ShouldBe(0m);
responseOrderLine.RefundedQuantity.ShouldBe(0);
UsingDbContext(context => UsingDbContext(context =>
{ {
context.Orders.Count().ShouldBe(1); context.Orders.Count().ShouldBe(1);
@ -139,31 +148,36 @@ namespace EasyAbp.EShop.Orders.Orders
order.StaffRemark.ShouldBeNullOrEmpty(); order.StaffRemark.ShouldBeNullOrEmpty();
order.StoreId.ShouldBe(OrderTestData.Store1Id); order.StoreId.ShouldBe(OrderTestData.Store1Id);
order.TotalDiscount.ShouldBe(0m); order.TotalDiscount.ShouldBe(0m);
order.TotalPrice.ShouldBe(10m); order.TotalPrice.ShouldBe(12m);
order.ActualTotalPrice.ShouldBe(10m); order.ActualTotalPrice.ShouldBe(12m);
order.CustomerUserId.ShouldBe(Guid.Parse("2e701e62-0953-4dd3-910b-dc6cc93ccb0d")); order.CustomerUserId.ShouldBe(Guid.Parse("2e701e62-0953-4dd3-910b-dc6cc93ccb0d"));
order.ProductTotalPrice.ShouldBe(10m); order.ProductTotalPrice.ShouldBe(12m);
order.ReducedInventoryAfterPaymentTime.ShouldBeNull(); order.ReducedInventoryAfterPaymentTime.ShouldBeNull();
order.ReducedInventoryAfterPlacingTime.ShouldNotBeNull(); order.ReducedInventoryAfterPlacingTime.ShouldNotBeNull();
order.OrderLines.Count.ShouldBe(1); order.OrderLines.Count.ShouldBe(2);
var orderLine = order.OrderLines.First(); var orderLine1 = response.OrderLines.Single(x => x.ProductSkuId == OrderTestData.ProductSku1Id);
orderLine.ProductId.ShouldBe(OrderTestData.Product1Id); orderLine1.ProductId.ShouldBe(OrderTestData.Product1Id);
orderLine.ProductSkuId.ShouldBe(OrderTestData.ProductSku1Id); orderLine1.ProductSkuId.ShouldBe(OrderTestData.ProductSku1Id);
orderLine.ProductDisplayName.ShouldBe("Hello pencil"); orderLine1.ProductDetailId.ShouldBe(OrderTestData.ProductDetail1Id);
orderLine.ProductUniqueName.ShouldBe("Pencil"); orderLine1.ProductDisplayName.ShouldBe("Hello pencil");
orderLine.ProductGroupName.ShouldBe("Default"); orderLine1.ProductUniqueName.ShouldBe("Pencil");
orderLine.ProductGroupDisplayName.ShouldBe("Default"); orderLine1.ProductGroupName.ShouldBe("Default");
orderLine.SkuName.ShouldBe("My SKU"); orderLine1.ProductGroupDisplayName.ShouldBe("Default");
orderLine.UnitPrice.ShouldBe(1m); orderLine1.SkuName.ShouldBe("My SKU");
orderLine.TotalPrice.ShouldBe(10m); orderLine1.UnitPrice.ShouldBe(1m);
orderLine.TotalDiscount.ShouldBe(0m); orderLine1.TotalPrice.ShouldBe(10m);
orderLine.ActualTotalPrice.ShouldBe(10m); orderLine1.TotalDiscount.ShouldBe(0m);
orderLine.Currency.ShouldBe("CNY"); orderLine1.ActualTotalPrice.ShouldBe(10m);
orderLine.Quantity.ShouldBe(10); orderLine1.Currency.ShouldBe("CNY");
orderLine.ProductModificationTime.ShouldBe(OrderTestData.ProductLastModificationTime); orderLine1.Quantity.ShouldBe(10);
orderLine.RefundAmount.ShouldBe(0m); orderLine1.ProductModificationTime.ShouldBe(OrderTestData.ProductLastModificationTime);
orderLine.RefundedQuantity.ShouldBe(0); orderLine1.ProductDetailModificationTime.ShouldBe(OrderTestData.ProductDetailLastModificationTime);
orderLine1.RefundAmount.ShouldBe(0m);
orderLine1.RefundedQuantity.ShouldBe(0);
var orderLine2 = response.OrderLines.Single(x => x.ProductSkuId == OrderTestData.ProductSku2Id);
orderLine2.ProductDetailId.ShouldBe(OrderTestData.ProductDetail2Id);
}); });
} }

8
modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.TestBase/OrderTestData.cs

@ -11,6 +11,14 @@ namespace EasyAbp.EShop.Orders
public static Guid ProductSku1Id { get; } = Guid.NewGuid(); public static Guid ProductSku1Id { get; } = Guid.NewGuid();
public static Guid ProductSku2Id { get; } = Guid.NewGuid();
public static Guid ProductDetail1Id { get; } = Guid.NewGuid();
public static Guid ProductDetail2Id { get; } = Guid.NewGuid();
public static DateTime ProductLastModificationTime { get; } = DateTime.Today; public static DateTime ProductLastModificationTime { get; } = DateTime.Today;
public static DateTime ProductDetailLastModificationTime { get; } = DateTime.Today;
} }
} }

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

@ -4,7 +4,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.ProductDetails.Dtos namespace EasyAbp.EShop.Products.ProductDetails.Dtos
{ {
[Serializable] [Serializable]
public class ProductDetailDto : ExtensibleEntityDto<Guid> public class ProductDetailDto : ExtensibleFullAuditedEntityDto<Guid>
{ {
public Guid? StoreId { get; set; } public Guid? StoreId { get; set; }

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

@ -18,7 +18,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string ProductGroupName { get; set; } public string ProductGroupName { get; set; }
[DisplayName("ProductDetailId")] [DisplayName("ProductDetailId")]
public Guid ProductDetailId { get; set; } public Guid? ProductDetailId { get; set; }
[DisplayName("ProductCategory")] [DisplayName("ProductCategory")]
public ICollection<Guid> CategoryIds { get; set; } public ICollection<Guid> CategoryIds { get; set; }

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

@ -14,7 +14,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string ProductGroupDisplayName { get; set; } public string ProductGroupDisplayName { get; set; }
public Guid ProductDetailId { get; set; } public Guid? ProductDetailId { get; set; }
public string UniqueName { get; set; } public string UniqueName { get; set; }

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

@ -11,7 +11,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string ProductGroupName { get; set; } public string ProductGroupName { get; set; }
public Guid ProductDetailId { get; set; } public Guid? ProductDetailId { get; set; }
public string UniqueName { get; set; } public string UniqueName { get; set; }

15
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs

@ -19,14 +19,11 @@ namespace EasyAbp.EShop.Products.ProductDetails
protected override string GetPolicyName { get; set; } = null; protected override string GetPolicyName { get; set; } = null;
protected override string GetListPolicyName { get; set; } = ProductsPermissions.Products.Default; protected override string GetListPolicyName { get; set; } = ProductsPermissions.Products.Default;
private readonly IProductRepository _productRepository;
private readonly IProductDetailRepository _repository; private readonly IProductDetailRepository _repository;
public ProductDetailAppService( public ProductDetailAppService(
IProductRepository productRepository,
IProductDetailRepository repository) : base(repository) IProductDetailRepository repository) : base(repository)
{ {
_productRepository = productRepository;
_repository = repository; _repository = repository;
} }
@ -75,19 +72,17 @@ namespace EasyAbp.EShop.Products.ProductDetails
{ {
await CheckUpdatePolicyAsync(); await CheckUpdatePolicyAsync();
var product = await _productRepository.GetAsync(x => x.ProductDetailId == id); var detail = await GetEntityByIdAsync(id);
await AuthorizationService.CheckMultiStorePolicyAsync(product.StoreId, UpdatePolicyName, await AuthorizationService.CheckMultiStorePolicyAsync(detail.StoreId, UpdatePolicyName,
ProductsPermissions.Products.CrossStore); ProductsPermissions.Products.CrossStore);
if (input.StoreId != product.StoreId) if (input.StoreId != detail.StoreId)
{ {
await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName, await AuthorizationService.CheckMultiStorePolicyAsync(input.StoreId, UpdatePolicyName,
ProductsPermissions.Products.CrossStore); ProductsPermissions.Products.CrossStore);
} }
var detail = await GetEntityByIdAsync(id);
MapToEntity(input, detail); MapToEntity(input, detail);
await Repository.UpdateAsync(detail, autoSave: true); await Repository.UpdateAsync(detail, autoSave: true);
@ -99,9 +94,9 @@ namespace EasyAbp.EShop.Products.ProductDetails
{ {
await CheckDeletePolicyAsync(); await CheckDeletePolicyAsync();
var product = await _productRepository.GetAsync(x => x.ProductDetailId == id); var detail = await GetEntityByIdAsync(id);
await AuthorizationService.CheckMultiStorePolicyAsync(product.StoreId, DeletePolicyName, await AuthorizationService.CheckMultiStorePolicyAsync(detail.StoreId, DeletePolicyName,
ProductsPermissions.Products.CrossStore); ProductsPermissions.Products.CrossStore);
await Repository.DeleteAsync(id); await Repository.DeleteAsync(id);

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/cs.json

@ -14,7 +14,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -35,8 +34,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -86,7 +85,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/en.json

@ -15,7 +15,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -36,8 +35,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -87,7 +86,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/pl.json

@ -14,7 +14,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -35,8 +34,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -86,7 +85,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/pt-BR.json

@ -14,7 +14,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -35,8 +34,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -86,7 +85,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/sl.json

@ -15,7 +15,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -36,8 +35,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -87,7 +86,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/tr.json

@ -15,7 +15,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -36,8 +35,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -87,7 +86,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/vi.json

@ -14,7 +14,6 @@
"ProductDisplayName": "Display name", "ProductDisplayName": "Display name",
"ProductDisplayOrder": "Display order", "ProductDisplayOrder": "Display order",
"ProductSold": "Sold", "ProductSold": "Sold",
"ProductDetailStoreId": "Store ID",
"ProductDetailDescription": "Description", "ProductDetailDescription": "Description",
"ProductCategory": "Categories", "ProductCategory": "Categories",
"ProductAttribute": "Attribute", "ProductAttribute": "Attribute",
@ -35,8 +34,8 @@
"ProductSkuOrderMaxQuantity": "Maximum quantity per purchase", "ProductSkuOrderMaxQuantity": "Maximum quantity per purchase",
"ProductSkuPaymentExpireIn": "Payment expire in", "ProductSkuPaymentExpireIn": "Payment expire in",
"ProductSkuMediaResources": "Media resources", "ProductSkuMediaResources": "Media resources",
"ProductSkuProductDetailId": "Product detail ID",
"ProductSkuContentDescription": "SKU content", "ProductSkuContentDescription": "SKU content",
"ProductSkuProductDetailDescriptionPlaceholder": "Fallback to details of the product if you keep null here.",
"CreateProductSku": "New", "CreateProductSku": "New",
"EditProductSku": "Edit", "EditProductSku": "Edit",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?", "ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product SKU {0}?",
@ -86,7 +85,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "The product unique name '{uniqueName}' is duplicated.",
"EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}", "EasyAbp.EShop.Products:InventoryChangeFailed": "Inventory of product {productId} (SKU: {productSkuId}) cannot be changed by {changedInventory} from {originalInventory}",
"EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.", "EasyAbp.EShop.Products:NonexistentProductGroup": "The specified product group ({productGroupName}) is nonexistent.",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "ProductDetail {productDetailId} has been used.",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "Sku code {code} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}", "EasyAbp.EShop.Products:ProductSkuDuplicated": "Sku {serializedAttributeOptionIds} is duplicate for the product {productId}",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "You have no permission to get category list with hidden categories.",

6
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/zh-Hans.json

@ -15,8 +15,7 @@
"ProductDisplayName": "商品名称", "ProductDisplayName": "商品名称",
"ProductDisplayOrder": "展示顺序", "ProductDisplayOrder": "展示顺序",
"ProductSold": "已售", "ProductSold": "已售",
"ProductDetailStoreId": "店铺 ID", "ProductDetailDescription": "详情介绍",
"ProductDetailDescription": "内容",
"ProductCategory": "所属类目", "ProductCategory": "所属类目",
"ProductAttribute": "商品属性", "ProductAttribute": "商品属性",
"ProductAttributeNames": "商品属性", "ProductAttributeNames": "商品属性",
@ -36,8 +35,8 @@
"ProductSkuOrderMaxQuantity": "单次最大购买数量", "ProductSkuOrderMaxQuantity": "单次最大购买数量",
"ProductSkuPaymentExpireIn": "支付限时", "ProductSkuPaymentExpireIn": "支付限时",
"ProductSkuMediaResources": "多媒体资源", "ProductSkuMediaResources": "多媒体资源",
"ProductSkuProductDetailId": "商品详情 ID",
"ProductSkuContentDescription": "SKU 描述", "ProductSkuContentDescription": "SKU 描述",
"ProductSkuProductDetailDescriptionPlaceholder": "如果此处不填,将使用商品自身的详情介绍。",
"CreateProductSku": "新建", "CreateProductSku": "新建",
"EditProductSku": "编辑", "EditProductSku": "编辑",
"ProductSkuDeletionConfirmationMessage": "确认删除 SKU {0}?", "ProductSkuDeletionConfirmationMessage": "确认删除 SKU {0}?",
@ -84,7 +83,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "唯一的产品名称'{uniqueName}'重复", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "唯一的产品名称'{uniqueName}'重复",
"EasyAbp.EShop.Products:InventoryChangeFailed": "产品{productId} (SKU: {productSkuId})的库存不能由{originalInventory}中的{changedInventory}更改", "EasyAbp.EShop.Products:InventoryChangeFailed": "产品{productId} (SKU: {productSkuId})的库存不能由{originalInventory}中的{changedInventory}更改",
"EasyAbp.EShop.Products:NonexistentProductGroup": "指定的产品组({productGroupName})不存在", "EasyAbp.EShop.Products:NonexistentProductGroup": "指定的产品组({productGroupName})不存在",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "商品详情{productDetailId}已被使用",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "商品{productId}的Sku代码{code}重复", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "商品{productId}的Sku代码{code}重复",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "商品{productId}的Sku{serializedAttributeOptionIds}重复", "EasyAbp.EShop.Products:ProductSkuDuplicated": "商品{productId}的Sku{serializedAttributeOptionIds}重复",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "您没有权限获取带有隐藏类别的类别列表", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "您没有权限获取带有隐藏类别的类别列表",

6
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/Localization/Products/zh-Hant.json

@ -15,8 +15,7 @@
"ProductDisplayName": "商品名稱", "ProductDisplayName": "商品名稱",
"ProductDisplayOrder": "展示順序", "ProductDisplayOrder": "展示順序",
"ProductSold": "已售", "ProductSold": "已售",
"ProductDetailStoreId": "店铺 ID", "ProductDetailDescription": "詳情介紹",
"ProductDetailDescription": "內容",
"ProductCategory": "所屬類目", "ProductCategory": "所屬類目",
"ProductAttribute": "商品屬性", "ProductAttribute": "商品屬性",
"ProductAttributeNames": "商品屬性", "ProductAttributeNames": "商品屬性",
@ -36,8 +35,8 @@
"ProductSkuOrderMaxQuantity": "單次最大購買數量", "ProductSkuOrderMaxQuantity": "單次最大購買數量",
"ProductSkuPaymentExpireIn": "支付限時", "ProductSkuPaymentExpireIn": "支付限時",
"ProductSkuMediaResources": "多媒體資源", "ProductSkuMediaResources": "多媒體資源",
"ProductSkuProductDetailId": "商品詳情 ID",
"ProductSkuContentDescription": "SKU 描述", "ProductSkuContentDescription": "SKU 描述",
"ProductSkuProductDetailDescriptionPlaceholder": "如果此處不填,將使用商品自身的詳情介紹。",
"CreateProductSku": "新建", "CreateProductSku": "新建",
"EditProductSku": "編輯", "EditProductSku": "編輯",
"ProductSkuDeletionConfirmationMessage": "確認刪除 SKU {0}?", "ProductSkuDeletionConfirmationMessage": "確認刪除 SKU {0}?",
@ -84,7 +83,6 @@
"EasyAbp.EShop.Products:DuplicatedProductUniqueName": "唯一的產品名稱'{uniqueName}'重復", "EasyAbp.EShop.Products:DuplicatedProductUniqueName": "唯一的產品名稱'{uniqueName}'重復",
"EasyAbp.EShop.Products:InventoryChangeFailed": "產品{productId} (SKU: {productSkuId})的庫存不能由{originalInventory}中的{changedInventory}更改", "EasyAbp.EShop.Products:InventoryChangeFailed": "產品{productId} (SKU: {productSkuId})的庫存不能由{originalInventory}中的{changedInventory}更改",
"EasyAbp.EShop.Products:NonexistentProductGroup": "指定的產品組({productGroupName})不存在", "EasyAbp.EShop.Products:NonexistentProductGroup": "指定的產品組({productGroupName})不存在",
"EasyAbp.EShop.Products:ProductDetailHasBeenUsed": "商品詳情{productDetailId}已被使用",
"EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "商品{productId}的Sku代碼{code}重復", "EasyAbp.EShop.Products:ProductSkuCodeDuplicated": "商品{productId}的Sku代碼{code}重復",
"EasyAbp.EShop.Products:ProductSkuDuplicated": "商品{productId}的Sku{serializedAttributeOptionIds}重復", "EasyAbp.EShop.Products:ProductSkuDuplicated": "商品{productId}的Sku{serializedAttributeOptionIds}重復",
"EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "您沒有權限獲取帶有隱藏類別的類別列表", "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden": "您沒有權限獲取帶有隱藏類別的類別列表",

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

@ -8,7 +8,7 @@ namespace EasyAbp.EShop.Products.Products
{ {
string ProductGroupName { get; } string ProductGroupName { get; }
Guid ProductDetailId { get; } Guid? ProductDetailId { get; }
string UniqueName { get; } string UniqueName { get; }

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

@ -16,7 +16,7 @@ namespace EasyAbp.EShop.Products.Products
public string ProductGroupName { get; set; } public string ProductGroupName { get; set; }
public Guid ProductDetailId { get; set; } public Guid? ProductDetailId { get; set; }
public string UniqueName { get; set; } public string UniqueName { get; set; }

1
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsErrorCodes.cs

@ -6,7 +6,6 @@
public const string DuplicatedProductUniqueName = "EasyAbp.EShop.Products:DuplicatedProductUniqueName"; public const string DuplicatedProductUniqueName = "EasyAbp.EShop.Products:DuplicatedProductUniqueName";
public const string InventoryChangeFailed = "EasyAbp.EShop.Products:InventoryChangeFailed"; public const string InventoryChangeFailed = "EasyAbp.EShop.Products:InventoryChangeFailed";
public const string NonexistentProductGroup = "EasyAbp.EShop.Products:NonexistentProductGroup"; public const string NonexistentProductGroup = "EasyAbp.EShop.Products:NonexistentProductGroup";
public const string ProductDetailHasBeenUsed = "EasyAbp.EShop.Products:ProductDetailHasBeenUsed";
public const string ProductSkuCodeDuplicated = "EasyAbp.EShop.Products:ProductSkuCodeDuplicated"; public const string ProductSkuCodeDuplicated = "EasyAbp.EShop.Products:ProductSkuCodeDuplicated";
public const string ProductSkuDuplicated = "EasyAbp.EShop.Products:ProductSkuDuplicated"; public const string ProductSkuDuplicated = "EasyAbp.EShop.Products:ProductSkuDuplicated";
public const string NotAllowedToGetCategoryListWithShowHidden = "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden"; public const string NotAllowedToGetCategoryListWithShowHidden = "EasyAbp.EShop.Products:NotAllowedToGetCategoryListWithShowHidden";

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

@ -15,7 +15,7 @@ namespace EasyAbp.EShop.Products.Products
[NotNull] [NotNull]
public virtual string ProductGroupName { get; protected set; } public virtual string ProductGroupName { get; protected set; }
public virtual Guid ProductDetailId { get; protected set; } public virtual Guid? ProductDetailId { get; protected set; }
[CanBeNull] [CanBeNull]
public virtual string UniqueName { get; protected set; } public virtual string UniqueName { get; protected set; }
@ -51,7 +51,7 @@ namespace EasyAbp.EShop.Products.Products
Guid? tenantId, Guid? tenantId,
Guid storeId, Guid storeId,
[NotNull] string productGroupName, [NotNull] string productGroupName,
Guid productDetailId, Guid? productDetailId,
[CanBeNull] string uniqueName, [CanBeNull] string uniqueName,
[NotNull] string displayName, [NotNull] string displayName,
InventoryStrategy inventoryStrategy, InventoryStrategy inventoryStrategy,

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

@ -1,13 +0,0 @@
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Products.Products
{
public class ProductDetailHasBeenUsedException : BusinessException
{
public ProductDetailHasBeenUsedException(Guid productDetailId) : base(ProductsErrorCodes.ProductDetailHasBeenUsed)
{
WithData(nameof(productDetailId), productDetailId);
}
}
}

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

@ -4,7 +4,9 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.Options.ProductGroups; using EasyAbp.EShop.Products.Options.ProductGroups;
using EasyAbp.EShop.Products.ProductCategories; using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.ProductDetails;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -14,6 +16,7 @@ namespace EasyAbp.EShop.Products.Products
{ {
private readonly IProductRepository _productRepository; private readonly IProductRepository _productRepository;
private readonly IProductPriceProvider _productPriceProvider; private readonly IProductPriceProvider _productPriceProvider;
private readonly IProductDetailRepository _productDetailRepository;
private readonly IProductCategoryRepository _productCategoryRepository; private readonly IProductCategoryRepository _productCategoryRepository;
private readonly IProductInventoryProvider _productInventoryProvider; private readonly IProductInventoryProvider _productInventoryProvider;
private readonly IProductGroupConfigurationProvider _productGroupConfigurationProvider; private readonly IProductGroupConfigurationProvider _productGroupConfigurationProvider;
@ -21,12 +24,14 @@ namespace EasyAbp.EShop.Products.Products
public ProductManager( public ProductManager(
IProductRepository productRepository, IProductRepository productRepository,
IProductPriceProvider productPriceProvider, IProductPriceProvider productPriceProvider,
IProductDetailRepository productDetailRepository,
IProductCategoryRepository productCategoryRepository, IProductCategoryRepository productCategoryRepository,
IProductInventoryProvider productInventoryProvider, IProductInventoryProvider productInventoryProvider,
IProductGroupConfigurationProvider productGroupConfigurationProvider) IProductGroupConfigurationProvider productGroupConfigurationProvider)
{ {
_productRepository = productRepository; _productRepository = productRepository;
_productPriceProvider = productPriceProvider; _productPriceProvider = productPriceProvider;
_productDetailRepository = productDetailRepository;
_productCategoryRepository = productCategoryRepository; _productCategoryRepository = productCategoryRepository;
_productInventoryProvider = productInventoryProvider; _productInventoryProvider = productInventoryProvider;
_productGroupConfigurationProvider = productGroupConfigurationProvider; _productGroupConfigurationProvider = productGroupConfigurationProvider;
@ -43,7 +48,7 @@ namespace EasyAbp.EShop.Products.Products
await _productRepository.InsertAsync(product, autoSave: true); await _productRepository.InsertAsync(product, autoSave: true);
await CheckProductDetailAvailableAsync(product.Id, product.ProductDetailId); await CheckProductDetailAsync(product);
await UpdateProductCategoriesAsync(product.Id, categoryIds); await UpdateProductCategoriesAsync(product.Id, categoryIds);
@ -69,7 +74,7 @@ namespace EasyAbp.EShop.Products.Products
await _productRepository.UpdateAsync(product, autoSave: true); await _productRepository.UpdateAsync(product, autoSave: true);
await CheckProductDetailAvailableAsync(product.Id, product.ProductDetailId); await CheckProductDetailAsync(product);
await UpdateProductCategoriesAsync(product.Id, categoryIds); await UpdateProductCategoriesAsync(product.Id, categoryIds);
@ -101,10 +106,12 @@ namespace EasyAbp.EShop.Products.Products
await CheckProductSkuNameUniqueAsync(product, productSku); await CheckProductSkuNameUniqueAsync(product, productSku);
productSku.TrimCode(); productSku.TrimName();
product.ProductSkus.AddIfNotContains(productSku); product.ProductSkus.AddIfNotContains(productSku);
await CheckProductDetailAsync(product);
return await _productRepository.UpdateAsync(product, true); return await _productRepository.UpdateAsync(product, true);
} }
@ -140,6 +147,8 @@ namespace EasyAbp.EShop.Products.Products
{ {
await CheckProductSkuNameUniqueAsync(product, productSku); await CheckProductSkuNameUniqueAsync(product, productSku);
await CheckProductDetailAsync(product);
return await _productRepository.UpdateAsync(product, true); return await _productRepository.UpdateAsync(product, true);
} }
@ -157,17 +166,27 @@ namespace EasyAbp.EShop.Products.Products
await _productRepository.CheckUniqueNameAsync(product); await _productRepository.CheckUniqueNameAsync(product);
} }
protected virtual async Task CheckProductDetailAsync(Product product)
{
if (product.ProductDetailId.HasValue)
{
await CheckProductDetailExistAsync(product.ProductDetailId.Value, product.StoreId);
}
foreach (var sku in product.ProductSkus.Where(x => x.ProductDetailId.HasValue))
{
await CheckProductDetailExistAsync(sku.ProductDetailId!.Value, product.StoreId);
}
}
[UnitOfWork] [UnitOfWork]
protected virtual async Task CheckProductDetailAvailableAsync(Guid currentProductId, Guid desiredProductDetailId) protected virtual async Task CheckProductDetailExistAsync(Guid productDetailId, Guid storeId)
{ {
var otherOwner = await _productRepository.FindAsync(x => var productDetail = await _productDetailRepository.GetAsync(productDetailId);
x.ProductDetailId == desiredProductDetailId && x.Id != currentProductId);
// Todo: should also check ProductSku owner if (productDetail.StoreId.HasValue && productDetail.StoreId.Value != storeId)
if (otherOwner != null)
{ {
throw new ProductDetailHasBeenUsedException(desiredProductDetailId); throw new EntityNotFoundException(typeof(ProductDetail), productDetailId);
} }
} }

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

@ -70,7 +70,7 @@ namespace EasyAbp.EShop.Products.Products
this.SetDefaultsForExtraProperties(); this.SetDefaultsForExtraProperties();
} }
internal void TrimCode() internal void TrimName()
{ {
Name = Name?.Trim(); Name = Name?.Trim();
} }

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

@ -14,7 +14,7 @@ namespace EasyAbp.EShop.Products.Products
public virtual string ProductGroupName { get; protected set; } public virtual string ProductGroupName { get; protected set; }
public virtual Guid ProductDetailId { get; protected set; } public virtual Guid? ProductDetailId { get; protected set; }
public virtual string UniqueName { get; protected set; } public virtual string UniqueName { get; protected set; }
@ -51,7 +51,7 @@ namespace EasyAbp.EShop.Products.Products
Guid? tenantId, Guid? tenantId,
Guid storeId, Guid storeId,
string productGroupName, string productGroupName,
Guid productDetailId, Guid? productDetailId,
string uniqueName, string uniqueName,
string displayName, string displayName,
InventoryStrategy inventoryStrategy, InventoryStrategy inventoryStrategy,

15
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/CreateModal.cshtml.cs

@ -73,15 +73,18 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
public virtual async Task<IActionResult> OnPostAsync() public virtual async Task<IActionResult> OnPostAsync()
{ {
var detail = await _productDetailAppService.CreateAsync(
ObjectMapper
.Map<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>(Product.ProductDetail));
var createDto = ObjectMapper.Map<CreateEditProductViewModel, CreateUpdateProductDto>(Product); var createDto = ObjectMapper.Map<CreateEditProductViewModel, CreateUpdateProductDto>(Product);
createDto.ProductDetailId = detail.Id; if (Product.ProductDetail.HasContent())
{
var detail = await _productDetailAppService.CreateAsync(
ObjectMapper
.Map<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>(Product.ProductDetail));
createDto.ProductDetailId = detail.Id;
}
var product = await _service.CreateAsync(createDto); await _service.CreateAsync(createDto);
return NoContent(); return NoContent();
} }

49
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/EditModal.cshtml.cs

@ -59,8 +59,6 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList(); .Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
var productDto = await _service.GetAsync(Id); var productDto = await _service.GetAsync(Id);
var detailDto = await _productDetailAppService.GetAsync(productDto.ProductDetailId);
Product = ObjectMapper.Map<ProductDto, CreateEditProductViewModel>(productDto); Product = ObjectMapper.Map<ProductDto, CreateEditProductViewModel>(productDto);
@ -70,26 +68,51 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount
})).Items.Select(x => x.CategoryId).ToList(); })).Items.Select(x => x.CategoryId).ToList();
Product.ProductDetail = new CreateEditProductDetailViewModel if (productDto.ProductDetailId.HasValue)
{ {
StoreId = detailDto.StoreId, var detailDto = await _productDetailAppService.GetAsync(productDto.ProductDetailId.Value);
Description = detailDto.Description
}; Product.ProductDetail = new CreateEditProductDetailViewModel
{
StoreId = detailDto.StoreId,
Description = detailDto.Description
};
}
} }
public virtual async Task<IActionResult> OnPostAsync() public virtual async Task<IActionResult> OnPostAsync()
{ {
var product = await _service.GetAsync(Id); var originalProduct = await _service.GetAsync(Id);
var detail = await _productDetailAppService.GetAsync(product.ProductDetailId); var updateProductDto = ObjectMapper.Map<CreateEditProductViewModel, CreateUpdateProductDto>(Product);
await _productDetailAppService.UpdateAsync(detail.Id, if (Product.ProductDetail.HasContent())
ObjectMapper {
.Map<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>(Product.ProductDetail)); if (originalProduct.ProductDetailId.HasValue)
{
var detail = await _productDetailAppService.GetAsync(originalProduct.ProductDetailId.Value);
var updateProductDto = ObjectMapper.Map<CreateEditProductViewModel, CreateUpdateProductDto>(Product); await _productDetailAppService.UpdateAsync(detail.Id,
ObjectMapper.Map<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>(
Product.ProductDetail));
updateProductDto.ProductDetailId = detail.Id;
}
else
{
var detail = await _productDetailAppService.CreateAsync(
ObjectMapper.Map<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>(
Product.ProductDetail));
updateProductDto.ProductDetailId = detail.Id; updateProductDto.ProductDetailId = detail.Id;
}
}
else if (originalProduct.ProductDetailId.HasValue)
{
await _productDetailAppService.DeleteAsync(originalProduct.ProductDetailId.Value);
updateProductDto.ProductDetailId = null;
}
await _service.UpdateAsync(Id, updateProductDto); await _service.UpdateAsync(Id, updateProductDto);
return NoContent(); return NoContent();

6
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/ViewModels/CreateEditProductDetailViewModel.cs

@ -12,11 +12,15 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewM
public class CreateEditProductDetailViewModel public class CreateEditProductDetailViewModel
{ {
[HiddenInput] [HiddenInput]
[Display(Name = "ProductDetailStoreId")]
public Guid? StoreId { get; set; } public Guid? StoreId { get; set; }
[TextArea(Rows = 4)] [TextArea(Rows = 4)]
[Display(Name = "ProductDetailDescription")] [Display(Name = "ProductDetailDescription")]
public string Description { get; set; } public string Description { get; set; }
public bool HasContent()
{
return !Description.IsNullOrEmpty();
}
} }
} }

4
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/Product/ViewModels/CreateEditProductViewModel.cs

@ -34,8 +34,8 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewM
[Required] [Required]
[Display(Name = "ProductDisplayName")] [Display(Name = "ProductDisplayName")]
public string DisplayName { get; set; } public string DisplayName { get; set; }
public CreateEditProductDetailViewModel ProductDetail { get; set; } public CreateEditProductDetailViewModel ProductDetail { get; set; } = new();
[Required] [Required]
[Placeholder("ProductAttributeNamesPlaceholder")] [Placeholder("ProductAttributeNamesPlaceholder")]

14
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/CreateModal.cshtml.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.ProductInventories; using EasyAbp.EShop.Products.ProductInventories;
using EasyAbp.EShop.Products.ProductInventories.Dtos; using EasyAbp.EShop.Products.ProductInventories.Dtos;
using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products;
@ -27,13 +29,16 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
public Dictionary<string, ICollection<SelectListItem>> Attributes { get; set; } public Dictionary<string, ICollection<SelectListItem>> Attributes { get; set; }
private readonly IProductInventoryAppService _productInventoryAppService; private readonly IProductInventoryAppService _productInventoryAppService;
private readonly IProductDetailAppService _productDetailAppService;
private readonly IProductAppService _productAppService; private readonly IProductAppService _productAppService;
public CreateModalModel( public CreateModalModel(
IProductInventoryAppService productInventoryAppService, IProductInventoryAppService productInventoryAppService,
IProductDetailAppService productDetailAppService,
IProductAppService productAppService) IProductAppService productAppService)
{ {
_productInventoryAppService = productInventoryAppService; _productInventoryAppService = productInventoryAppService;
_productDetailAppService = productDetailAppService;
_productAppService = productAppService; _productAppService = productAppService;
} }
@ -57,6 +62,15 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
createDto.AttributeOptionIds = SelectedAttributeOptionIdDict.Values.ToList(); createDto.AttributeOptionIds = SelectedAttributeOptionIdDict.Values.ToList();
if (ProductSku.ProductDetail.HasContent())
{
var detail = await _productDetailAppService.CreateAsync(
ObjectMapper.Map<CreateEditSkuProductDetailViewModel, CreateUpdateProductDetailDto>(ProductSku
.ProductDetail));
createDto.ProductDetailId = detail.Id;
}
var skuDto = await _productAppService.CreateSkuAsync(ProductId, createDto); var skuDto = await _productAppService.CreateSkuAsync(ProductId, createDto);
await _productInventoryAppService.UpdateAsync(new UpdateProductInventoryDto await _productInventoryAppService.UpdateAsync(new UpdateProductInventoryDto

62
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/EditModal.cshtml.cs

@ -1,12 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos; using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels; using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{ {
@ -24,25 +24,71 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
public EditProductSkuViewModel ProductSku { get; set; } public EditProductSkuViewModel ProductSku { get; set; }
private readonly IProductAppService _productAppService; private readonly IProductAppService _productAppService;
private readonly IProductDetailAppService _productDetailAppService;
public EditModalModel(IProductAppService productAppService) public EditModalModel(
IProductAppService productAppService,
IProductDetailAppService productDetailAppService)
{ {
_productAppService = productAppService; _productAppService = productAppService;
_productDetailAppService = productDetailAppService;
} }
public virtual async Task OnGetAsync() public virtual async Task OnGetAsync()
{ {
var product = await _productAppService.GetAsync(ProductId); var product = await _productAppService.GetAsync(ProductId);
var sku = product.ProductSkus.Single(x => x.Id == ProductSkuId);
ProductSku = ProductSku = ObjectMapper.Map<ProductSkuDto, EditProductSkuViewModel>(sku);
ObjectMapper.Map<ProductSkuDto, EditProductSkuViewModel>(
product.ProductSkus.Single(x => x.Id == ProductSkuId)); if (sku.ProductDetailId.HasValue)
{
var detailDto = await _productDetailAppService.GetAsync(sku.ProductDetailId.Value);
ProductSku.ProductDetail = new CreateEditSkuProductDetailViewModel
{
StoreId = detailDto.StoreId,
Description = detailDto.Description
};
}
} }
public virtual async Task<IActionResult> OnPostAsync() public virtual async Task<IActionResult> OnPostAsync()
{ {
await _productAppService.UpdateSkuAsync(ProductId, ProductSkuId, var originalProduct = await _productAppService.GetAsync(ProductId);
ObjectMapper.Map<EditProductSkuViewModel, UpdateProductSkuDto>(ProductSku)); var originalSku = originalProduct.ProductSkus.Single(x => x.Id == ProductSkuId);
var updateProductSkuDto = ObjectMapper.Map<EditProductSkuViewModel, UpdateProductSkuDto>(ProductSku);
if (ProductSku.ProductDetail.HasContent())
{
if (originalSku.ProductDetailId.HasValue)
{
var detail = await _productDetailAppService.GetAsync(originalSku.ProductDetailId.Value);
await _productDetailAppService.UpdateAsync(detail.Id,
ObjectMapper.Map<CreateEditSkuProductDetailViewModel, CreateUpdateProductDetailDto>(
ProductSku.ProductDetail));
updateProductSkuDto.ProductDetailId = detail.Id;
}
else
{
var detail = await _productDetailAppService.CreateAsync(
ObjectMapper.Map<CreateEditSkuProductDetailViewModel, CreateUpdateProductDetailDto>(
ProductSku.ProductDetail));
updateProductSkuDto.ProductDetailId = detail.Id;
}
}
else if (originalSku.ProductDetailId.HasValue)
{
await _productDetailAppService.DeleteAsync(originalSku.ProductDetailId.Value);
updateProductSkuDto.ProductDetailId = null;
}
await _productAppService.UpdateSkuAsync(ProductId, ProductSkuId, updateProductSkuDto);
return NoContent(); return NoContent();
} }

23
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/ViewModels/CreateEditSkuProductDetailViewModel.cs

@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels
{
public class CreateEditSkuProductDetailViewModel
{
[HiddenInput]
public Guid? StoreId { get; set; }
[TextArea(Rows = 4)]
[Display(Name = "ProductDetailDescription")]
[Placeholder("ProductSkuProductDetailDescriptionPlaceholder")]
public string Description { get; set; }
public bool HasContent()
{
return !Description.IsNullOrEmpty();
}
}
}

6
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/EShop/Products/Products/ProductSku/ViewModels/EditProductSkuViewModel.cs

@ -42,9 +42,7 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.Vi
[Display(Name = "ProductSkuMediaResources")] [Display(Name = "ProductSkuMediaResources")]
public string MediaResources { get; set; } public string MediaResources { get; set; }
[HiddenInput] public CreateEditSkuProductDetailViewModel ProductDetail { get; set; } = new();
[Display(Name = "ProductSkuProductDetailId")]
public Guid? ProductDetailId { get; set; }
} }
} }

7
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsWebAutoMapperProfile.cs

@ -51,16 +51,23 @@ namespace EasyAbp.EShop.Products.Web
CreateMap<ProductDetailDto, CreateEditProductDetailViewModel>(); CreateMap<ProductDetailDto, CreateEditProductDetailViewModel>();
CreateMap<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>() CreateMap<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>()
.Ignore(dto => dto.ExtraProperties); .Ignore(dto => dto.ExtraProperties);
CreateMap<CreateEditSkuProductDetailViewModel, CreateUpdateProductDetailDto>()
.Ignore(dto => dto.ExtraProperties);
CreateMap<ProductAttributeDto, CreateEditProductAttributeViewModel>(); CreateMap<ProductAttributeDto, CreateEditProductAttributeViewModel>();
CreateMap<CreateEditProductAttributeViewModel, CreateUpdateProductAttributeDto>() CreateMap<CreateEditProductAttributeViewModel, CreateUpdateProductAttributeDto>()
.Ignore(dto => dto.ExtraProperties); .Ignore(dto => dto.ExtraProperties);
CreateMap<CreateProductSkuViewModel, CreateProductSkuDto>() CreateMap<CreateProductSkuViewModel, CreateProductSkuDto>()
.Ignore(dto => dto.ExtraProperties) .Ignore(dto => dto.ExtraProperties)
.Ignore(dto => dto.ProductDetailId)
.ForSourceMember(model => model.ProductDetail, opt => opt.DoNotValidate())
.ForSourceMember(model => model.Inventory, opt => opt.DoNotValidate()) .ForSourceMember(model => model.Inventory, opt => opt.DoNotValidate())
.Ignore(dto => dto.AttributeOptionIds); .Ignore(dto => dto.AttributeOptionIds);
CreateMap<EditProductSkuViewModel, UpdateProductSkuDto>() CreateMap<EditProductSkuViewModel, UpdateProductSkuDto>()
.Ignore(dto => dto.ProductDetailId)
.ForSourceMember(model => model.ProductDetail, opt => opt.DoNotValidate())
.Ignore(dto => dto.ExtraProperties); .Ignore(dto => dto.ExtraProperties);
CreateMap<ProductSkuDto, EditProductSkuViewModel>() CreateMap<ProductSkuDto, EditProductSkuViewModel>()
.Ignore(x => x.ProductDetail)
.ForSourceMember(dto => dto.AttributeOptionIds, opt => opt.DoNotValidate()) .ForSourceMember(dto => dto.AttributeOptionIds, opt => opt.DoNotValidate())
.ForSourceMember(dto => dto.Inventory, opt => opt.DoNotValidate()) .ForSourceMember(dto => dto.Inventory, opt => opt.DoNotValidate())
.ForSourceMember(dto => dto.Sold, opt => opt.DoNotValidate()); .ForSourceMember(dto => dto.Sold, opt => opt.DoNotValidate());

74
modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs

@ -4,9 +4,11 @@ using System.Linq;
using Shouldly; using Shouldly;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyAbp.EShop.Products.Options; using EasyAbp.EShop.Products.Options;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.Products.Dtos; using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Domain.Entities;
using Xunit; using Xunit;
namespace EasyAbp.EShop.Products.Products namespace EasyAbp.EShop.Products.Products
@ -20,18 +22,19 @@ namespace EasyAbp.EShop.Products.Products
{ {
_productAppService = GetRequiredService<IProductAppService>(); _productAppService = GetRequiredService<IProductAppService>();
_eShopProductsOptions = GetRequiredService<IOptions<EShopProductsOptions>>().Value; _eShopProductsOptions = GetRequiredService<IOptions<EShopProductsOptions>>().Value;
}
[Fact]
public async Task Should_Create_Product()
{
// Arrange // Arrange
_eShopProductsOptions.Groups.Configure("Default Group Name", x => _eShopProductsOptions.Groups.Configure("Default Group Name", x =>
{ {
x.DisplayName = "Default Group Name"; x.DisplayName = "Default Group Name";
x.Description = "Default Description"; x.Description = "Default Description";
}); });
}
[Fact]
public async Task Should_Create_Product()
{
// Arrange
var requestDto = new CreateUpdateProductDto var requestDto = new CreateUpdateProductDto
{ {
ProductGroupName = "Default Group Name", ProductGroupName = "Default Group Name",
@ -144,5 +147,66 @@ namespace EasyAbp.EShop.Products.Products
getResult.MinimumPrice.ShouldBe(1m); getResult.MinimumPrice.ShouldBe(1m);
getResult.MaximumPrice.ShouldBe(3m); getResult.MaximumPrice.ShouldBe(3m);
} }
[Fact]
public async Task Should_Check_ProductDetailId()
{
var wrongProductDetailId = Guid.NewGuid();
var requestDto = new CreateUpdateProductDto
{
ProductGroupName = "Default Group Name",
StoreId = ProductsTestData.Store1Id,
UniqueName = "Unique Pencil",
DisplayName = "Pencil",
ProductDetailId = wrongProductDetailId,
InventoryStrategy = InventoryStrategy.NoNeed,
DisplayOrder = 0,
IsPublished = true,
ProductAttributes = new List<CreateUpdateProductAttributeDto>
{
new CreateUpdateProductAttributeDto
{
DisplayName = "Default Attribute 1",
Description = "Default Description 1",
DisplayOrder = 1,
ProductAttributeOptions = new List<CreateUpdateProductAttributeOptionDto>
{
new CreateUpdateProductAttributeOptionDto
{
DisplayName = "Option 1"
}
}
}
}
};
// Act
(await Should.ThrowAsync<EntityNotFoundException>(async () =>
{
await _productAppService.CreateAsync(requestDto);
})).EntityType.ShouldBe(typeof(ProductDetail));
}
[Fact]
public async Task Should_Check_Sku_ProductDetailId()
{
await Should_Create_Product();
var wrongProductDetailId = Guid.NewGuid();
(await Should.ThrowAsync<EntityNotFoundException>(async () =>
{
await _productAppService.CreateSkuAsync(ProductsTestData.Product1Id, new CreateProductSkuDto
{
AttributeOptionIds = new List<Guid> {ProductsTestData.Product1Attribute1Option4Id},
ProductDetailId = wrongProductDetailId,
Currency = "CNY",
Price = 10m,
OrderMinQuantity = 1,
OrderMaxQuantity = 10
});
})).EntityType.ShouldBe(typeof(ProductDetail));
}
} }
} }

110
modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs

@ -1,4 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
@ -6,18 +9,117 @@ namespace EasyAbp.EShop.Products.Products
{ {
public class ProductDomainTests : ProductsDomainTestBase public class ProductDomainTests : ProductsDomainTestBase
{ {
private IProductRepository ProductRepository { get; }
private IProductManager ProductManager { get; }
public ProductDomainTests() public ProductDomainTests()
{ {
ProductRepository = ServiceProvider.GetRequiredService<IProductRepository>();
ProductManager = ServiceProvider.GetRequiredService<IProductManager>();
}
[Fact]
public async Task Should_Set_ProductDetailId()
{
var product2 = new Product(ProductsTestData.Product2Id, null, ProductsTestData.Store1Id, "Default",
ProductsTestData.ProductDetails2Id, "Ball", "Ball", InventoryStrategy.NoNeed, true, false, false, null,
null, 0);
await ProductManager.CreateAsync(product2);
product2 = await ProductRepository.GetAsync(product2.Id);
product2.ProductDetailId.ShouldBe(ProductsTestData.ProductDetails2Id);
}
[Fact]
public async Task Should_Set_Sku_ProductDetailId()
{
await WithUnitOfWorkAsync(async () =>
{
var product1 = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
var sku1 = product1.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
sku1.ProductDetailId.ShouldBeNull();
typeof(ProductSku).GetProperty(nameof(ProductSku.ProductDetailId))!.SetValue(sku1,
ProductsTestData.ProductDetails1Id);
await ProductManager.UpdateAsync(product1);
product1 = await ProductRepository.GetAsync(product1.Id);
sku1 = product1.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
sku1.ProductDetailId.ShouldBe(ProductsTestData.ProductDetails1Id);
});
} }
[Fact] [Fact]
public async Task Test1() public async Task Should_Reuse_ProductDetail()
{ {
// Arrange var product1 = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
product1.ProductDetailId.ShouldBe(ProductsTestData.ProductDetails1Id);
var product2 = new Product(ProductsTestData.Product2Id, null, ProductsTestData.Store1Id, "Default",
ProductsTestData.ProductDetails2Id, "Ball", "Ball", InventoryStrategy.NoNeed, true, false, false, null,
null, 0);
await ProductManager.CreateAsync(product2);
product2 = await ProductRepository.GetAsync(product2.Id);
product2.ProductDetailId.ShouldBe(ProductsTestData.ProductDetails2Id);
typeof(Product).GetProperty(nameof(Product.ProductDetailId))!.SetValue(product2,
ProductsTestData.ProductDetails1Id);
await ProductManager.UpdateAsync(product2);
product2 = await ProductRepository.GetAsync(product2.Id);
product2.ProductDetailId.ShouldBe(ProductsTestData.ProductDetails1Id);
}
[Fact]
public async Task Should_Remove_ProductDetailId()
{
await Should_Set_ProductDetailId();
var product2 = await ProductRepository.GetAsync(ProductsTestData.Product2Id);
product2.ProductDetailId.ShouldNotBeNull();
typeof(Product).GetProperty(nameof(Product.ProductDetailId))!.SetValue(product2, null);
await ProductManager.UpdateAsync(product2);
product2 = await ProductRepository.GetAsync(product2.Id);
product2.ProductDetailId.ShouldBeNull();
}
[Fact]
public async Task Should_Remove_Sku_ProductDetailId()
{
await WithUnitOfWorkAsync(async () =>
{
await Should_Set_Sku_ProductDetailId();
var product1 = await ProductRepository.GetAsync(ProductsTestData.Product1Id);
var sku1 = product1.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
sku1.ProductDetailId.ShouldNotBeNull();
typeof(ProductSku).GetProperty(nameof(Product.ProductDetailId))!.SetValue(sku1, null);
await ProductManager.UpdateAsync(product1);
// Assert product1 = await ProductRepository.GetAsync(product1.Id);
sku1 = product1.ProductSkus.Single(x => x.Id == ProductsTestData.Product1Sku1Id);
// Assert sku1.ProductDetailId.ShouldBeNull();
});
} }
} }
} }

4
modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestData.cs

@ -12,6 +12,8 @@ namespace EasyAbp.EShop.Products
public static Guid Product1Id { get; } = Guid.NewGuid(); public static Guid Product1Id { get; } = Guid.NewGuid();
public static Guid Product2Id { get; } = Guid.NewGuid();
public static Guid Product1Attribute1Id { get; } = Guid.NewGuid(); public static Guid Product1Attribute1Id { get; } = Guid.NewGuid();
public static Guid Product1Attribute1Option1Id { get; } = Guid.NewGuid(); public static Guid Product1Attribute1Option1Id { get; } = Guid.NewGuid();
@ -20,6 +22,8 @@ namespace EasyAbp.EShop.Products
public static Guid Product1Attribute1Option3Id { get; } = Guid.NewGuid(); public static Guid Product1Attribute1Option3Id { get; } = Guid.NewGuid();
public static Guid Product1Attribute1Option4Id { get; } = Guid.NewGuid();
public static Guid Product1Sku1Id { get; } = Guid.NewGuid(); public static Guid Product1Sku1Id { get; } = Guid.NewGuid();
public static Guid Product1Sku2Id { get; } = Guid.NewGuid(); public static Guid Product1Sku2Id { get; } = Guid.NewGuid();

3
modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs

@ -53,7 +53,8 @@ namespace EasyAbp.EShop.Products
{ {
new ProductAttributeOption(ProductsTestData.Product1Attribute1Option1Id, "S", null), new ProductAttributeOption(ProductsTestData.Product1Attribute1Option1Id, "S", null),
new ProductAttributeOption(ProductsTestData.Product1Attribute1Option2Id, "M", null), new ProductAttributeOption(ProductsTestData.Product1Attribute1Option2Id, "M", null),
new ProductAttributeOption(ProductsTestData.Product1Attribute1Option3Id, "L", null) new ProductAttributeOption(ProductsTestData.Product1Attribute1Option3Id, "L", null),
new ProductAttributeOption(ProductsTestData.Product1Attribute1Option4Id, "XL", null)
}); });
product.ProductAttributes.Add(attribute); product.ProductAttributes.Add(attribute);

5131
samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411111127_MadeProductDetailIdNullable.Designer.cs

File diff suppressed because it is too large

70
samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411111127_MadeProductDetailIdNullable.cs

@ -0,0 +1,70 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EShopSample.Migrations
{
public partial class MadeProductDetailIdNullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<Guid>(
name: "ProductDetailId",
table: "EasyAbpEShopProductsProductViews",
type: "uniqueidentifier",
nullable: true,
oldClrType: typeof(Guid),
oldType: "uniqueidentifier");
migrationBuilder.AlterColumn<Guid>(
name: "ProductDetailId",
table: "EasyAbpEShopProductsProducts",
type: "uniqueidentifier",
nullable: true,
oldClrType: typeof(Guid),
oldType: "uniqueidentifier");
migrationBuilder.AlterColumn<DateTime>(
name: "ProductDetailModificationTime",
table: "EasyAbpEShopOrdersOrderLines",
type: "datetime2",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime2");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<Guid>(
name: "ProductDetailId",
table: "EasyAbpEShopProductsProductViews",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uniqueidentifier",
oldNullable: true);
migrationBuilder.AlterColumn<Guid>(
name: "ProductDetailId",
table: "EasyAbpEShopProductsProducts",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uniqueidentifier",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "ProductDetailModificationTime",
table: "EasyAbpEShopOrdersOrderLines",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "datetime2",
oldNullable: true);
}
}
}

5134
samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411204916_AddedProductDetailIdToOrderLine.Designer.cs

File diff suppressed because it is too large

26
samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/20220411204916_AddedProductDetailIdToOrderLine.cs

@ -0,0 +1,26 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EShopSample.Migrations
{
public partial class AddedProductDetailIdToOrderLine : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "ProductDetailId",
table: "EasyAbpEShopOrdersOrderLines",
type: "uniqueidentifier",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ProductDetailId",
table: "EasyAbpEShopOrdersOrderLines");
}
}
}

11
samples/EShopSample/aspnet-core/src/EShopSample.EntityFrameworkCore/Migrations/EShopSampleDbContextModelSnapshot.cs

@ -19,7 +19,7 @@ namespace EShopSample.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "6.0.2") .HasAnnotation("ProductVersion", "6.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128); .HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
@ -214,7 +214,10 @@ namespace EShopSample.Migrations
b.Property<Guid?>("OrderId") b.Property<Guid?>("OrderId")
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<DateTime>("ProductDetailModificationTime") b.Property<Guid?>("ProductDetailId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime?>("ProductDetailModificationTime")
.HasColumnType("datetime2"); .HasColumnType("datetime2");
b.Property<string>("ProductDisplayName") b.Property<string>("ProductDisplayName")
@ -1398,7 +1401,7 @@ namespace EShopSample.Migrations
b.Property<TimeSpan?>("PaymentExpireIn") b.Property<TimeSpan?>("PaymentExpireIn")
.HasColumnType("time"); .HasColumnType("time");
b.Property<Guid>("ProductDetailId") b.Property<Guid?>("ProductDetailId")
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("ProductGroupName") b.Property<string>("ProductGroupName")
@ -1670,7 +1673,7 @@ namespace EShopSample.Migrations
b.Property<decimal?>("MinimumPrice") b.Property<decimal?>("MinimumPrice")
.HasColumnType("decimal(20,8)"); .HasColumnType("decimal(20,8)");
b.Property<Guid>("ProductDetailId") b.Property<Guid?>("ProductDetailId")
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("ProductGroupDisplayName") b.Property<string>("ProductGroupDisplayName")

Loading…
Cancel
Save