Commit 9f524666 authored by gdlcf88's avatar gdlcf88

Completed product management (except SKU management)

parent b8f69bae
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.EShop.Products.Products.Dtos
{
public class CreateUpdateProductAttributeDto
{
[Required]
[DisplayName("ProductAttributeDisplayName")]
public string DisplayName { get; set; }
[DisplayName("ProductAttributeDescription")]
public string Description { get; set; }
[DisplayName("ProductAttributeDisplayOrder")]
public int DisplayOrder { get; set; }
public ICollection<CreateUpdateProductAttributeOptionDto> ProductAttributeOptions { get; set; }
}
}
\ No newline at end of file
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.EShop.Products.Products.Dtos
{
public class CreateUpdateProductAttributeOptionDto
{
[Required]
[DisplayName("ProductAttributeOptionDisplayName")]
public string DisplayName { get; set; }
[DisplayName("ProductAttributeOptionDescription")]
public string Description { get; set; }
[DisplayName("ProductAttributeOptionDisplayOrder")]
public int DisplayOrder { get; set; }
}
}
\ No newline at end of file
......@@ -2,10 +2,11 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace EasyAbp.EShop.Products.Products.Dtos
{
public class CreateUpdateProductDto
public class CreateUpdateProductDto : IValidatableObject
{
[DisplayName("ProductStoreId")]
public Guid? StoreId { get; set; }
......@@ -15,21 +16,48 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public Guid ProductTypeId { get; set; }
[DisplayName("ProductCategory")]
public IEnumerable<Guid> CategoryIds { get; set; }
public ICollection<Guid> CategoryIds { get; set; }
[Required]
[DisplayName("ProductDisplayName")]
public string DisplayName { get; set; }
public CreateUpdateProductDetailDto ProductDetail { get; set; }
public ICollection<CreateUpdateProductAttributeDto> ProductAttributes { get; set; }
[DisplayName("ProductInventoryStrategy")]
public InventoryStrategy InventoryStrategy { get; set; }
[DisplayName("ProductDisplayOrder")]
public int DisplayOrder { get; set; }
[DisplayName("ProductMediaResources")]
public string MediaResources { get; set; }
[DisplayName("ProductIsPublished")]
public bool IsPublished { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ProductAttributes.Select(a => a.DisplayName.Trim()).Distinct().Count() != ProductAttributes.Count)
{
yield return new ValidationResult(
"DisplayNames of ProductAttributes should be unique!",
new[] { "ProductAttributes" }
);
}
var optionNameList = ProductAttributes.SelectMany(a => a.ProductAttributeOptions)
.Select(o => o.DisplayName.Trim()).ToList();
if (optionNameList.Distinct().Count() != optionNameList.Count)
{
yield return new ValidationResult(
"DisplayNames of ProductAttributeOptions should be unique!",
new[] { "ProductAttributeOptions" }
);
}
}
}
}
\ No newline at end of file
......@@ -12,6 +12,8 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string Description { get; set; }
public IEnumerable<ProductAttributeOptionDto> ProductAttributeOptions { get; set; }
public int DisplayOrder { get; set; }
public ICollection<ProductAttributeOptionDto> ProductAttributeOptions { get; set; }
}
}
\ No newline at end of file
......@@ -10,5 +10,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string DisplayName { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
}
}
\ No newline at end of file
......@@ -10,7 +10,7 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public Guid ProductTypeId { get; set; }
public IEnumerable<Guid> CategoryIds { get; set; }
public ICollection<Guid> CategoryIds { get; set; }
public string DisplayName { get; set; }
......@@ -18,12 +18,14 @@ namespace EasyAbp.EShop.Products.Products.Dtos
public string MediaResources { get; set; }
public int DisplayOrder { get; set; }
public bool IsPublished { get; set; }
public ProductDetailDto ProductDetail { get; set; }
public IEnumerable<ProductAttributeDto> ProductAttributes { get; set; }
public ICollection<ProductAttributeDto> ProductAttributes { get; set; }
public IEnumerable<ProductSkuDto> ProductSkus { get; set; }
public ICollection<ProductSkuDto> ProductSkus { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Authorization;
......@@ -54,13 +55,101 @@ namespace EasyAbp.EShop.Products.Products
{
await CheckCreatePolicyAsync();
var entity = MapToEntity(input);
var product = MapToEntity(input);
TryToSetTenantId(entity);
TryToSetTenantId(product);
await UpdateProductAttributesAsync(product, input);
await Repository.InsertAsync(entity, autoSave: true);
await Repository.InsertAsync(product, autoSave: true);
await UpdateProductCategoriesAsync(product.Id, product.StoreId, input.CategoryIds);
return MapToGetOutputDto(entity);
return MapToGetOutputDto(product);
}
public override async Task<ProductDto> UpdateAsync(Guid id, CreateUpdateProductDto input)
{
await CheckUpdatePolicyAsync();
var product = await GetEntityByIdAsync(id);
MapToEntity(input, product);
await UpdateProductAttributesAsync(product, input);
await Repository.UpdateAsync(product, autoSave: true);
await UpdateProductCategoriesAsync(product.Id, product.StoreId, input.CategoryIds);
return MapToGetOutputDto(product);
}
private async Task UpdateProductAttributesAsync(Product product, CreateUpdateProductDto input)
{
foreach (var attributeDto in input.ProductAttributes)
{
var attribute = product.ProductAttributes.FirstOrDefault(a => a.DisplayName == attributeDto.DisplayName);
if (attribute == null)
{
attribute = new ProductAttribute(GuidGenerator.Create(),
attributeDto.DisplayName, attributeDto.Description);
product.ProductAttributes.Add(attribute);
}
foreach (var optionDto in attributeDto.ProductAttributeOptions)
{
var option = attribute.ProductAttributeOptions.FirstOrDefault(o => o.DisplayName == optionDto.DisplayName);
if (option == null)
{
option = new ProductAttributeOption(GuidGenerator.Create(),
optionDto.DisplayName, optionDto.Description);
attribute.ProductAttributeOptions.Add(option);
}
}
var exceptOptionNames = attribute.ProductAttributeOptions.Select(o => o.DisplayName)
.Except(attributeDto.ProductAttributeOptions.Select(o => o.DisplayName));
attribute.ProductAttributeOptions.RemoveAll(o => exceptOptionNames.Contains(o.DisplayName));
}
var exceptAttributeNames = product.ProductAttributes.Select(a => a.DisplayName)
.Except(input.ProductAttributes.Select(a => a.DisplayName));
product.ProductAttributes.RemoveAll(a => exceptAttributeNames.Contains(a.DisplayName));
}
public override async Task DeleteAsync(Guid id)
{
await _productCategoryRepository.DeleteAsync(x => x.ProductId.Equals(id));
await base.DeleteAsync(id);
}
public override async Task<ProductDto> GetAsync(Guid id)
{
var dto = await base.GetAsync(id);
dto.CategoryIds = (await _productCategoryRepository.GetListByProductId(dto.Id, dto.StoreId))
.Select(x => x.CategoryId).ToList();
return dto;
}
protected virtual async Task UpdateProductCategoriesAsync(Guid productId, Guid? storeId, IEnumerable<Guid> categoryIds)
{
await _productCategoryRepository.DeleteAsync(x => x.ProductId.Equals(productId));
foreach (var categoryId in categoryIds)
{
await _productCategoryRepository.InsertAsync(new ProductCategory(GuidGenerator.Create(),
CurrentTenant.Id, storeId, categoryId, productId));
}
}
}
}
\ No newline at end of file
......@@ -26,10 +26,11 @@ namespace EasyAbp.EShop.Products
CreateMap<ProductSku, ProductSkuDto>();
CreateMap<CreateUpdateProductDto, Product>(MemberList.Source)
.ForSourceMember(dto => dto.CategoryIds, opt => opt.DoNotValidate())
.Ignore(p => p.ProductDetail)
.Ignore(p => p.ProductAttributes)
.Ignore(p => p.ProductSkus);
CreateMap<CreateUpdateProductDetailDto, ProductDetail>(MemberList.Source);
CreateMap<CreateUpdateProductAttributeDto, ProductAttribute>(MemberList.Source);
CreateMap<CreateUpdateProductAttributeOptionDto, ProductAttributeOption>(MemberList.Source);
CreateMap<Category, CategoryDto>();
CreateMap<CreateUpdateCategoryDto, Category>(MemberList.Source);
CreateMap<ProductType, ProductTypeDto>();
......
......@@ -3,14 +3,23 @@
"texts": {
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -4,14 +4,23 @@
"ManageYourProfile": "Manage your profile",
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -3,14 +3,23 @@
"texts": {
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -3,14 +3,23 @@
"texts": {
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -4,14 +4,23 @@
"ManageYourProfile": "Upravljajte svojim profilom",
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -4,14 +4,23 @@
"ManageYourProfile": "Profil y�netimi",
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -3,14 +3,23 @@
"texts": {
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -4,14 +4,23 @@
"ManageYourProfile": "管理个人资料",
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -4,14 +4,23 @@
"ManageYourProfile": "管理個人資料",
"Menu:Product": "MenuProduct",
"Product": "Product",
"ProductTenantId": "ProductTenantId",
"ProductStoreId": "ProductStoreId",
"ProductProductTypeId": "ProductProductTypeId",
"ProductStore": "ProductStore",
"ProductDisplayName": "ProductDisplayName",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
"ProductAttributeDisplayName": "ProductAttributeDisplayName",
"ProductAttributeDescription": "ProductAttributeDescription",
"ProductAttributeDisplayOrder": "ProductAttributeDisplayOrder",
"ProductAttributeOptionDisplayName": "ProductAttributeOptionDisplayName",
"ProductAttributeOptionDescription": "ProductAttributeOptionDescription",
"ProductAttributeOptionDisplayOrder": "ProductAttributeOptionDisplayOrder",
"CreateProduct": "CreateProduct",
"EditProduct": "EditProduct",
"ProductDeletionConfirmationMessage": "Are you sure to delete the product {0}?",
......
......@@ -8,8 +8,8 @@ namespace EasyAbp.EShop.Products.ProductCategories
{
public interface IProductCategoryRepository : IRepository<ProductCategory, Guid>
{
Task<IEnumerable<ProductCategory>> GetListByCategoryId(Guid categoryId, Guid? storeId, CancellationToken cancellationToken = default);
Task<List<ProductCategory>> GetListByCategoryId(Guid categoryId, Guid? storeId, CancellationToken cancellationToken = default);
Task<IEnumerable<ProductCategory>> GetListByProductId(Guid productId, Guid? storeId, CancellationToken cancellationToken = default);
Task<List<ProductCategory>> GetListByProductId(Guid productId, Guid? storeId, CancellationToken cancellationToken = default);
}
}
\ No newline at end of file
......@@ -27,7 +27,7 @@ namespace EasyAbp.EShop.Products.ProductCategories
Guid? storeId,
Guid categoryId,
Guid productId,
int displayOrder
int displayOrder = 0
) :base(id)
{
TenantId = tenantId;
......
......@@ -22,17 +22,21 @@ namespace EasyAbp.EShop.Products.Products
[CanBeNull]
public virtual string MediaResources { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
public virtual bool IsPublished { get; protected set; }
public virtual ProductDetail ProductDetail { get; protected set; }
public virtual IEnumerable<ProductAttribute> ProductAttributes { get; protected set; }
public virtual ICollection<ProductAttribute> ProductAttributes { get; protected set; }
public virtual IEnumerable<ProductSku> ProductSkus { get; protected set; }
public virtual ICollection<ProductSku> ProductSkus { get; protected set; }
protected Product()
{
ProductAttributes = new List<ProductAttribute>();
ProductSkus = new List<ProductSku>();
}
public Product(
......@@ -43,7 +47,8 @@ namespace EasyAbp.EShop.Products.Products
string displayName,
InventoryStrategy inventoryStrategy,
bool isPublished,
string mediaResources
string mediaResources,
int displayOrder
) :base(id)
{
TenantId = tenantId;
......@@ -53,6 +58,9 @@ namespace EasyAbp.EShop.Products.Products
InventoryStrategy = inventoryStrategy;
IsPublished = isPublished;
MediaResources = mediaResources;
DisplayOrder = displayOrder;
ProductAttributes = new List<ProductAttribute>();
ProductSkus = new List<ProductSku>();
}
}
}
......@@ -13,6 +13,26 @@ namespace EasyAbp.EShop.Products.Products
[CanBeNull]
public virtual string Description { get; protected set; }
public virtual IEnumerable<ProductAttributeOption> ProductAttributeOptions { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
public virtual ICollection<ProductAttributeOption> ProductAttributeOptions { get; protected set; }
protected ProductAttribute()
{
ProductAttributeOptions = new List<ProductAttributeOption>();
}
public ProductAttribute(
Guid id,
[NotNull] string displayName,
[CanBeNull] string description,
int displayOrder = 0) : base(id)
{
DisplayName = displayName;
Description = description;
DisplayOrder = displayOrder;
ProductAttributeOptions = new List<ProductAttributeOption>();
}
}
}
\ No newline at end of file
......@@ -11,5 +11,20 @@ namespace EasyAbp.EShop.Products.Products
[CanBeNull]
public virtual string Description { get; protected set; }
public virtual int DisplayOrder { get; protected set; }
protected ProductAttributeOption() {}
public ProductAttributeOption(
Guid id,
[NotNull] string displayName,
[CanBeNull] string description,
int displayOrder = 0) : base(id)
{
DisplayName = displayName;
Description = description;
DisplayOrder = displayOrder;
}
}
}
\ No newline at end of file
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities.Auditing;
namespace EasyAbp.EShop.Products.Products
{
public class ProductSku : FullAuditedEntity<Guid>
{
[NotNull]
public virtual string SerializedAttributeOptionIds { get; protected set; }
public virtual decimal OriginalPrice { get; protected set; }
......@@ -16,5 +18,24 @@ namespace EasyAbp.EShop.Products.Products
public virtual int Sold { get; protected set; }
public virtual int OrderMinQuantity { get; protected set; }
protected ProductSku() {}
public ProductSku(
Guid id,
[NotNull] string serializedAttributeOptionIds,
decimal originalPrice,
decimal price,
int inventory,
int sold,
int orderMinQuantity) : base(id)
{
SerializedAttributeOptionIds = serializedAttributeOptionIds;
OriginalPrice = originalPrice;
Price = price;
Inventory = inventory;
Sold = sold;
OrderMinQuantity = orderMinQuantity;
}
}
}
\ No newline at end of file
......@@ -78,6 +78,8 @@ namespace EasyAbp.EShop.Products.EntityFrameworkCore
b.ToTable(options.TablePrefix + "ProductSkus", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
b.Property(x => x.Price).HasColumnType("decimal(18,6)");
b.Property(x => x.OriginalPrice).HasColumnType("decimal(18,6)");
});
builder.Entity<Category>(b =>
......
......@@ -16,14 +16,14 @@ namespace EasyAbp.EShop.Products.ProductCategories
{
}
public async Task<IEnumerable<ProductCategory>> GetListByCategoryId(Guid categoryId, Guid? storeId,
public virtual async Task<List<ProductCategory>> GetListByCategoryId(Guid categoryId, Guid? storeId,
CancellationToken cancellationToken = default)
{
return await GetQueryable().Where(pc => pc.CategoryId == categoryId && pc.StoreId == storeId)
.ToListAsync(cancellationToken);
}
public async Task<IEnumerable<ProductCategory>> GetListByProductId(Guid productId, Guid? storeId,
public virtual async Task<List<ProductCategory>> GetListByProductId(Guid productId, Guid? storeId,
CancellationToken cancellationToken = default)
{
return await GetQueryable().Where(pc => pc.ProductId == productId && pc.StoreId == storeId)
......
using System;
using System.Linq;
using EasyAbp.EShop.Products.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
......@@ -10,5 +12,13 @@ namespace EasyAbp.EShop.Products.Products
public ProductRepository(IDbContextProvider<ProductsDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public override IQueryable<Product> WithDetails()
{
return base.WithDetails()
.Include(x => x.ProductDetail)
.Include(x => x.ProductAttributes).ThenInclude(x => x.ProductAttributeOptions)
.Include(x => x.ProductSkus);
}
}
}
\ No newline at end of file
......@@ -9,7 +9,6 @@
<abp-modal>
<abp-modal-header title="@L["CreateProduct"].Value"></abp-modal-header>
<abp-modal-body>
<abp-select asp-for="Product.CategoryIds" asp-items="@Model.Categories" class="data-select" data-live-search="true" multiple />
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
......
......@@ -6,6 +6,8 @@ using EasyAbp.EShop.Products.Categories;
using EasyAbp.EShop.Products.Categories.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.Application.Dtos;
......@@ -15,29 +17,39 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
public class CreateModalModel : ProductsPageModel
{
[BindProperty]
public CreateUpdateProductDto Product { get; set; }
public CreateUpdateProductViewModel Product { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public ICollection<SelectListItem> ProductTypes { get; set; }
public ICollection<SelectListItem> Categories { get; set; }
private readonly IProductTypeAppService _productTypeAppService;
private readonly ICategoryAppService _categoryAppService;
private readonly IProductAppService _service;
public CreateModalModel(
IProductTypeAppService productTypeAppService,
ICategoryAppService categoryAppService,
IProductAppService service)
{
_productTypeAppService = productTypeAppService;
_categoryAppService = categoryAppService;
_service = service;
}
public async Task OnGetAsync(Guid? storeId)
{
ProductTypes =
(await _productTypeAppService.GetListAsync(new PagedAndSortedResultRequestDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount})).Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
Categories =
(await _categoryAppService.GetListAsync(new PagedAndSortedResultRequestDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount}))?.Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString()));
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
Product = new CreateUpdateProductDto
Product = new CreateUpdateProductViewModel
{
StoreId = storeId
};
......@@ -45,7 +57,7 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
public async Task<IActionResult> OnPostAsync()
{
await _service.CreateAsync(Product);
await _service.CreateAsync(ObjectMapper.Map<CreateUpdateProductViewModel, CreateUpdateProductDto>(Product));
return NoContent();
}
}
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Categories;
using Microsoft.AspNetCore.Mvc;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
{
......@@ -13,24 +20,46 @@ namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product
public Guid Id { get; set; }
[BindProperty]
public CreateUpdateProductDto Product { get; set; }
public CreateUpdateProductViewModel Product { get; set; }
public ICollection<SelectListItem> ProductTypes { get; set; }
public ICollection<SelectListItem> Categories { get; set; }
private readonly IProductTypeAppService _productTypeAppService;
private readonly ICategoryAppService _categoryAppService;
private readonly IProductAppService _service;
public EditModalModel(IProductAppService service)
public EditModalModel(
IProductTypeAppService productTypeAppService,
ICategoryAppService categoryAppService,
IProductAppService service)
{
_productTypeAppService = productTypeAppService;
_categoryAppService = categoryAppService;
_service = service;
}
public async Task OnGetAsync()
{
var dto = await _service.GetAsync(Id);
Product = ObjectMapper.Map<ProductDto, CreateUpdateProductDto>(dto);
ProductTypes =
(await _productTypeAppService.GetListAsync(new PagedAndSortedResultRequestDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount})).Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
Categories =
(await _categoryAppService.GetListAsync(new PagedAndSortedResultRequestDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount}))?.Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
var productDto = await _service.GetAsync(Id);
Product = ObjectMapper.Map<ProductDto, CreateUpdateProductViewModel>(productDto);
}
public async Task<IActionResult> OnPostAsync()
{
await _service.UpdateAsync(Id, Product);
await _service.UpdateAsync(Id,
ObjectMapper.Map<CreateUpdateProductViewModel, CreateUpdateProductDto>(Product));
return NoContent();
}
}
......
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels
{
public class CreateUpdateProductAttributeOptionViewModel
{
[Required]
[Display(Name = "ProductAttributeOptionDisplayName")]
public string DisplayName { get; set; }
[Display(Name = "ProductAttributeOptionDescription")]
public string Description { get; set; }
[Display(Name = "ProductAttributeOptionDisplayOrder")]
public int DisplayOrder { get; set; } = 0;
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels
{
public class CreateUpdateProductAttributeViewModel
{
[Required]
[Display(Name = "ProductAttributeDisplayName")]
public string DisplayName { get; set; }
[Display(Name = "ProductAttributeDescription")]
public string Description { get; set; }
[Display(Name = "ProductAttributeDisplayOrder")]
public int DisplayOrder { get; set; } = 0;
public List<CreateUpdateProductAttributeOptionViewModel> ProductAttributeOptions { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels
{
public class CreateUpdateProductDetailViewModel
{
[TextArea(Rows = 4)]
[Display(Name = "ProductDetailDescription")]
public string Description { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels
{
public class CreateUpdateProductViewModel
{
[HiddenInput]
[Display(Name = "ProductStore")]
public Guid? StoreId { get; set; }
[Required]
[SelectItems("ProductTypes")]
[Display(Name = "ProductType")]
public Guid ProductTypeId { get; set; }
[SelectItems("Categories")]
[Display(Name = "ProductCategory")]
public List<Guid> CategoryIds { get; set; }
[Required]
[Display(Name = "ProductDisplayName")]
public string DisplayName { get; set; }
public CreateUpdateProductDetailViewModel ProductDetail { get; set; }
[Required]
[Placeholder("ProductAttributeNamesPlaceholder")]
[Display(Name = "ProductAttributeNames")]
public string ProductAttributeNames { get; set; }
[Required]
[TextArea(Rows = 4)]
[Placeholder("ProductAttributeOptionNamesPlaceholder")]
[Display(Name = "ProductAttributeOptionNames")]
public string ProductAttributeOptionNames { get; set; }
[Display(Name = "ProductInventoryStrategy")]
public InventoryStrategy InventoryStrategy { get; set; }
[Display(Name = "ProductDisplayOrder")]
public int DisplayOrder { get; set; } = 0;
[Display(Name = "ProductMediaResources")]
public string MediaResources { get; set; }
[Display(Name = "ProductIsPublished")]
public bool IsPublished { get; set; }
}
}
\ No newline at end of file
using EasyAbp.EShop.Products.Products.Dtos;
using System;
using System.Collections.Generic;
using System.Linq;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Categories.Dtos;
using EasyAbp.EShop.Products.ProductTypes.Dtos;
using AutoMapper;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using Volo.Abp.AutoMapper;
namespace EasyAbp.EShop.Products.Web
......@@ -13,8 +17,34 @@ namespace EasyAbp.EShop.Products.Web
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<ProductDto, CreateUpdateProductDto>();
CreateMap<ProductDetailDto, CreateUpdateProductDetailDto>();
CreateMap<ProductDto, CreateUpdateProductViewModel>()
// .Ignore(x => x.ProductAttributes);
.ForMember(dest => dest.ProductAttributeNames,
opt => opt.MapFrom(source =>
source.ProductAttributes.Select(x => x.DisplayName).JoinAsString(",")))
.ForMember(dest => dest.ProductAttributeOptionNames,
opt => opt.MapFrom(x =>
x.ProductAttributes
.Select(a => a.ProductAttributeOptions.Select(o => o.DisplayName).JoinAsString(","))
.JoinAsString(Environment.NewLine)));
CreateMap<CreateUpdateProductViewModel, CreateUpdateProductDto>()
.ForMember(dest => dest.ProductAttributes,
opt => opt.MapFrom(x =>
x.ProductAttributeNames.Split(",", StringSplitOptions.RemoveEmptyEntries).Select((s, i) =>
new CreateUpdateProductAttributeDto
{
DisplayName = s,
ProductAttributeOptions = new List<CreateUpdateProductAttributeOptionDto>(
x.ProductAttributeOptionNames.SplitToLines(StringSplitOptions.RemoveEmptyEntries)[i]
.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o =>
new CreateUpdateProductAttributeOptionDto {DisplayName = o}))
})));
CreateMap<ProductDetailDto, CreateUpdateProductDetailViewModel>();
CreateMap<CreateUpdateProductDetailViewModel, CreateUpdateProductDetailDto>();
CreateMap<ProductAttributeDto, CreateUpdateProductAttributeViewModel>();
CreateMap<CreateUpdateProductAttributeViewModel, CreateUpdateProductAttributeDto>();
CreateMap<ProductAttributeOptionDto, CreateUpdateProductAttributeOptionViewModel>();
CreateMap<CreateUpdateProductAttributeOptionViewModel, CreateUpdateProductAttributeOptionDto>();
CreateMap<CategoryDto, CreateUpdateCategoryDto>();
CreateMap<ProductTypeDto, CreateUpdateProductTypeDto>();
}
......
using Microsoft.EntityFrameworkCore.Migrations;
namespace EasyMall.Migrations
{
public partial class AddedDisplayOrder : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "DisplayOrder",
table: "ProductsProducts",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "DisplayOrder",
table: "ProductsProductAttributes",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "DisplayOrder",
table: "ProductsProductAttributeOptions",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DisplayOrder",
table: "ProductsProducts");
migrationBuilder.DropColumn(
name: "DisplayOrder",
table: "ProductsProductAttributes");
migrationBuilder.DropColumn(
name: "DisplayOrder",
table: "ProductsProductAttributeOptions");
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace EasyMall.Migrations
{
public partial class SetPricePrecision : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<decimal>(
name: "Price",
table: "ProductsProductSkus",
type: "decimal(18,6)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "decimal(18,2)");
migrationBuilder.AlterColumn<decimal>(
name: "OriginalPrice",
table: "ProductsProductSkus",
type: "decimal(18,6)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "decimal(18,2)");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<decimal>(
name: "Price",
table: "ProductsProductSkus",
type: "decimal(18,2)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "decimal(18,6)");
migrationBuilder.AlterColumn<decimal>(
name: "OriginalPrice",
table: "ProductsProductSkus",
type: "decimal(18,2)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "decimal(18,6)");
}
}
}
......@@ -229,6 +229,9 @@ namespace EasyMall.Migrations
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("DisplayOrder")
.HasColumnType("int");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
......@@ -299,6 +302,9 @@ namespace EasyMall.Migrations
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("DisplayOrder")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
......@@ -351,6 +357,9 @@ namespace EasyMall.Migrations
b.Property<string>("DisplayName")
.HasColumnType("nvarchar(max)");
b.Property<int>("DisplayOrder")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
......@@ -431,10 +440,10 @@ namespace EasyMall.Migrations
.HasColumnType("int");
b.Property<decimal>("OriginalPrice")
.HasColumnType("decimal(18,2)");
.HasColumnType("decimal(18,6)");
b.Property<decimal>("Price")
.HasColumnType("decimal(18,2)");
.HasColumnType("decimal(18,6)");
b.Property<Guid?>("ProductId")
.HasColumnType("uniqueidentifier");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment