Commit ba5e397c authored by gdlcf88's avatar gdlcf88

Product sku management, close #5

parent f56695c2
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.EShop.Products.Products.Dtos
{
public class UpdateProductSkuDto
{
[DisplayName("ProductSkuCurrency")]
public string Currency { get; set; }
[DisplayName("ProductSkuOriginalPrice")]
public decimal OriginalPrice { get; set; }
[DisplayName("ProductSkuPrice")]
public decimal Price { get; set; }
[DisplayName("ProductSkuInventory")]
public int Inventory { get; set; }
[DisplayName("ProductSkuOrderMinQuantity")]
public int OrderMinQuantity { get; set; }
[DisplayName("ProductSkuProductDetailId")]
public Guid? ProductDetailId { get; set; }
}
public class CreateProductSkuDto : UpdateProductSkuDto
{
[Required]
[DisplayName("ProductSkuSerializedAttributeOptionIds")]
public string SerializedAttributeOptionIds { get; set; }
}
}
\ No newline at end of file
......@@ -7,6 +7,8 @@ namespace EasyAbp.EShop.Products.Products.Dtos
{
public string SerializedAttributeOptionIds { get; set; }
public string Currency { get; set; }
public decimal OriginalPrice { get; set; }
public decimal Price { get; set; }
......
......@@ -15,5 +15,11 @@ namespace EasyAbp.EShop.Products.Products
CreateUpdateProductDto>
{
Task DeleteAsync(Guid id, Guid storeId);
Task<ProductDto> CreateSkuAsync(Guid productId, Guid storeId, CreateProductSkuDto input);
Task<ProductDto> UpdateSkuAsync(Guid productId, Guid productSkuId, Guid storeId, UpdateProductSkuDto input);
Task<ProductDto> DeleteSkuAsync(Guid productId, Guid productSkuId, Guid storeId);
}
}
\ No newline at end of file
......@@ -22,15 +22,18 @@ namespace EasyAbp.EShop.Products.Products
protected override string GetPolicyName { get; set; } = ProductsPermissions.Products.Default;
protected override string GetListPolicyName { get; set; } = ProductsPermissions.Products.Default;
private readonly ISerializedAttributeOptionIdsFormatter _serializedAttributeOptionIdsFormatter;
private readonly IProductStoreRepository _productStoreRepository;
private readonly IProductCategoryRepository _productCategoryRepository;
private readonly IProductRepository _repository;
public ProductAppService(
ISerializedAttributeOptionIdsFormatter serializedAttributeOptionIdsFormatter,
IProductStoreRepository productStoreRepository,
IProductCategoryRepository productCategoryRepository,
IProductRepository repository) : base(repository)
{
_serializedAttributeOptionIdsFormatter = serializedAttributeOptionIdsFormatter;
_productStoreRepository = productStoreRepository;
_productCategoryRepository = productCategoryRepository;
_repository = repository;
......@@ -178,6 +181,75 @@ namespace EasyAbp.EShop.Products.Products
await base.DeleteAsync(id);
}
public async Task<ProductDto> CreateSkuAsync(Guid productId, Guid storeId, CreateProductSkuDto input)
{
await CheckUpdatePolicyAsync();
await CheckStoreIsProductOwnerAsync(productId, storeId);
var product = await GetEntityByIdAsync(productId);
input.SerializedAttributeOptionIds =
await _serializedAttributeOptionIdsFormatter.ParseAsync(input.SerializedAttributeOptionIds);
await CheckSkuAttributeOptionsAsync(product, input.SerializedAttributeOptionIds);
var sku = ObjectMapper.Map<CreateProductSkuDto, ProductSku>(input);
EntityHelper.TrySetId(sku, GuidGenerator.Create);
product.ProductSkus.Add(sku);
await _repository.UpdateAsync(product, true);
return ObjectMapper.Map<Product, ProductDto>(product);
}
protected virtual Task CheckSkuAttributeOptionsAsync(Product product, string inputSerializedAttributeOptionIds)
{
if (product.ProductSkus.FirstOrDefault(sku =>
sku.SerializedAttributeOptionIds.Equals(inputSerializedAttributeOptionIds)) != null)
{
throw new ProductSkuDuplicatedException(product.Id, inputSerializedAttributeOptionIds);
}
return Task.CompletedTask;
}
public async Task<ProductDto> UpdateSkuAsync(Guid productId, Guid productSkuId, Guid storeId, UpdateProductSkuDto input)
{
await CheckUpdatePolicyAsync();
await CheckStoreIsProductOwnerAsync(productId, storeId);
var product = await GetEntityByIdAsync(productId);
var sku = product.ProductSkus.Single(x => x.Id == productSkuId);
ObjectMapper.Map(input, sku);
await _repository.UpdateAsync(product, true);
return ObjectMapper.Map<Product, ProductDto>(product);
}
public async Task<ProductDto> DeleteSkuAsync(Guid productId, Guid productSkuId, Guid storeId)
{
await CheckUpdatePolicyAsync();
await CheckStoreIsProductOwnerAsync(productId, storeId);
var product = await GetEntityByIdAsync(productId);
var sku = product.ProductSkus.Single(x => x.Id == productSkuId);
product.ProductSkus.Remove(sku);
await _repository.UpdateAsync(product, true);
return ObjectMapper.Map<Product, ProductDto>(product);
}
protected virtual async Task UpdateProductCategoriesAsync(Guid productId, IEnumerable<Guid> categoryIds)
{
await _productCategoryRepository.DeleteAsync(x => x.ProductId.Equals(productId));
......
using System;
using Volo.Abp;
namespace EasyAbp.EShop.Products.Products
{
public class ProductSkuDuplicatedException : BusinessException
{
public ProductSkuDuplicatedException(Guid productId, string serializedAttributeOptionIds) : base(
message: $"Sku {serializedAttributeOptionIds} is duplicate for the product {productId}")
{
}
}
}
\ No newline at end of file
......@@ -35,6 +35,8 @@ namespace EasyAbp.EShop.Products
.ForSourceMember(dto => dto.StoreId, opt => opt.DoNotValidate());
CreateMap<CreateUpdateProductAttributeDto, ProductAttribute>(MemberList.Source);
CreateMap<CreateUpdateProductAttributeOptionDto, ProductAttributeOption>(MemberList.Source);
CreateMap<CreateProductSkuDto, ProductSku>(MemberList.Source);
CreateMap<UpdateProductSkuDto, ProductSku>(MemberList.Source);
CreateMap<Category, CategoryDto>();
CreateMap<CreateUpdateCategoryDto, Category>(MemberList.Source);
CreateMap<ProductType, ProductTypeDto>();
......
......@@ -12,10 +12,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -13,10 +13,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -12,10 +12,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -12,10 +12,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -13,10 +13,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -13,10 +13,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -12,10 +12,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -13,10 +13,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
......@@ -13,10 +13,24 @@
"ProductDetailProductSkuId": "ProductDetailProductSkuId",
"ProductDetailDescription": "ProductDetailDescription",
"ProductDetailDisplayOrder": "ProductDetailDisplayOrder",
"ProductAttribute": "ProductAttribute",
"ProductAttributeNames": "ProductAttributeNames",
"ProductAttributeNamesPlaceholder": "ProductAttributeNamesPlaceholder",
"ProductAttributeOption": "ProductAttributeOption",
"ProductAttributeOptionNames": "ProductAttributeOptionNames",
"ProductAttributeOptionNamesPlaceholder": "ProductAttributeOptionNamesPlaceholder",
"ProductSku": "ProductSku",
"ProductSkuSerializedAttributeOptionIds": "ProductSkuSerializedAttributeOptionIds",
"ProductSkuCurrency": "ProductSkuCurrency",
"ProductSkuOriginalPrice": "ProductSkuOriginalPrice",
"ProductSkuPrice": "ProductSkuPrice",
"ProductSkuInventory": "ProductSkuInventory",
"ProductSkuOrderMinQuantity": "ProductSkuOrderMinQuantity",
"ProductSkuProductDetailId": "ProductSkuProductDetailId",
"ProductSkuContentDescription": "ProductSkuContentDescription",
"CreateProductSku": "CreateProductSku",
"EditProductSku": "EditProductSku",
"ProductSkuDeletionConfirmationMessage": "Are you sure to delete the product sku {0}?",
"ProductInventoryStrategy": "ProductInventoryStrategy",
"ProductIsPublished": "ProductIsPublished",
"ProductMediaResources": "ProductMediaResources",
......
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Products.Products
{
public interface ISerializedAttributeOptionIdsFormatter
{
Task<string> ParseAsync(string serializedAttributeOptionIds);
Task<string> ParseAsync(IEnumerable<Guid> attributeOptionIds);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.Products
{
public class SerializedAttributeOptionIdsFormatter : ISerializedAttributeOptionIdsFormatter, ITransientDependency
{
public async Task<string> ParseAsync(string serializedAttributeOptionIds)
{
return await ParseAsync(JsonConvert.DeserializeObject<IEnumerable<Guid>>(serializedAttributeOptionIds));
}
public Task<string> ParseAsync(IEnumerable<Guid> attributeOptionIds)
{
return Task.FromResult(JsonConvert.SerializeObject(attributeOptionIds.OrderBy(x => x)));
}
}
}
\ No newline at end of file
......@@ -37,7 +37,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Pages\EShop\Products\Products" />
<Folder Include="wwwroot\" />
</ItemGroup>
......@@ -53,4 +52,16 @@
<_ContentIncludedByDefault Remove="Pages\Categories\Category\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Compile Update="Pages\EShop\Products\Products\ProductSku\CreateModal.cshtml.cs">
<DependentUpon>CreateModal.cshtml</DependentUpon>
</Compile>
<Compile Update="Pages\EShop\Products\Products\ProductSku\EditModal.cshtml.cs">
<DependentUpon>EditModal.cshtml</DependentUpon>
</Compile>
<Compile Update="Pages\EShop\Products\Products\ProductSku\Index.cshtml.cs">
<DependentUpon>Index.cshtml</DependentUpon>
</Compile>
</ItemGroup>
</Project>
......@@ -22,6 +22,12 @@ $(function () {
rowAction: {
items:
[
{
text: l('ProductSku'),
action: function (data) {
document.location.href = document.location.origin + '/EShop/Products/Products/ProductSku?ProductId=' + data.record.id + '&StoreId=' + storeId;
}
},
{
text: l('Edit'),
action: function (data) {
......
@page
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.CreateModalModel
@{
Layout = null;
}
<abp-dynamic-form abp-model="ProductSku" data-ajaxForm="true" asp-page="CreateModal">
<abp-modal>
<abp-modal-header title="@L["CreateProductSku"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content/>
@foreach (var attr in Model.Attributes)
{
<abp-select asp-for="@Model.SelectedAttributeOptionIdDict[attr.Key]" asp-items="@attr.Value" label="@L["ProductAttribute"].Value - @attr.Key" />
}
<abp-input asp-for="StoreId" />
<abp-input asp-for="ProductId" />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Categories;
using EasyAbp.EShop.Products.ProductDetails;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.ProductTypes;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{
public class CreateModalModel : ProductsPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid StoreId { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid ProductId { get; set; }
[BindProperty]
public CreateEditProductSkuViewModel ProductSku { get; set; }
[BindProperty]
public Dictionary<string, Guid> SelectedAttributeOptionIdDict { get; set; }
public Dictionary<string, ICollection<SelectListItem>> Attributes { get; set; }
private readonly IProductAppService _productAppService;
public CreateModalModel(IProductAppService productAppService)
{
_productAppService = productAppService;
}
public virtual async Task OnGetAsync()
{
var product = await _productAppService.GetAsync(ProductId);
Attributes = new Dictionary<string, ICollection<SelectListItem>>();
foreach (var attribute in product.ProductAttributes.ToList())
{
Attributes.Add(attribute.DisplayName,
attribute.ProductAttributeOptions
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList());
}
}
public virtual async Task<IActionResult> OnPostAsync()
{
var createDto = ObjectMapper.Map<CreateEditProductSkuViewModel, CreateProductSkuDto>(ProductSku);
createDto.SerializedAttributeOptionIds = JsonConvert.SerializeObject(SelectedAttributeOptionIdDict.Values);
await _productAppService.CreateSkuAsync(ProductId, StoreId, createDto);
return NoContent();
}
}
}
\ No newline at end of file
@page
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.EditModalModel
@{
Layout = null;
}
<abp-dynamic-form abp-model="ProductSku" data-ajaxForm="true" asp-page="EditModal">
<abp-modal>
<abp-modal-header title="@L["EditProductSku"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id" />
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{
public class EditModalModel : ProductsPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid Id { get; set; }
[BindProperty(SupportsGet = true)]
public Guid StoreId { get; set; }
[BindProperty(SupportsGet = true)]
public Guid ProductId { get; set; }
[BindProperty(SupportsGet = true)]
public Guid ProductSkuId { get; set; }
[BindProperty]
public CreateEditProductSkuViewModel ProductSku { get; set; }
private readonly IProductAppService _productAppService;
public EditModalModel(IProductAppService productAppService)
{
_productAppService = productAppService;
}
public virtual async Task OnGetAsync()
{
var product = await _productAppService.GetAsync(ProductId);
ProductSku =
ObjectMapper.Map<ProductSkuDto, CreateEditProductSkuViewModel>(
product.ProductSkus.Single(x => x.Id == ProductSkuId));
}
public virtual async Task<IActionResult> OnPostAsync()
{
await _productAppService.UpdateSkuAsync(ProductId, ProductSkuId, StoreId,
ObjectMapper.Map<CreateEditProductSkuViewModel, UpdateProductSkuDto>(ProductSku));
return NoContent();
}
}
}
\ No newline at end of file
@page
@using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage
@model IndexModel
@inject IPageLayout PageLayout
@{
PageLayout.Content.Title = L["ProductSku"].Value;
PageLayout.Content.BreadCrumb.Add(L["Menu:Product"].Value);
PageLayout.Content.MenuItemName = "Product";
}
@section scripts
{
<abp-script src="/Pages/EShop/Products/Products/ProductSku/index.js" />
}
@section styles
{
<abp-style src="/Pages/EShop/Products/Products/ProductSku/index.css"/>
}
<script>
let storeId = '@Model.StoreId';
let productId = '@Model.ProductId';
</script>
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<abp-card-title>@L["ProductSku"] - @Model.ProductDisplayName</abp-card-title>
</abp-column>
<abp-column size-md="_6" class="text-right">
<abp-button id="NewProductSkuButton"
text="@L["CreateProductSku"].Value"
icon="plus"
button-type="Primary" />
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="ProductSkuTable" class="nowrap">
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["ProductSkuContentDescription"]</th>
<th>@L["ProductSkuPrice"]</th>
<th>@L["ProductSkuInventory"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
\ No newline at end of file
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Stores.Stores;
using Microsoft.AspNetCore.Mvc;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku
{
public class IndexModel : ProductsPageModel
{
private readonly IProductAppService _productAppService;
[BindProperty(SupportsGet = true)]
public Guid StoreId { get; set; }
[BindProperty(SupportsGet = true)]
public Guid ProductId { get; set; }
public string ProductDisplayName { get; set; }
public IndexModel(IProductAppService productAppService)
{
_productAppService = productAppService;
}
public virtual async Task OnGetAsync()
{
ProductDisplayName = (await _productAppService.GetAsync(ProductId)).DisplayName;
}
}
}
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels
{
public class CreateEditProductSkuViewModel
{
[Required]
[Display(Name = "ProductSkuCurrency")]
public string Currency { get; set; }
[Required]
[Display(Name = "ProductSkuPrice")]
public decimal Price { get; set; }
[Display(Name = "ProductSkuOriginalPrice")]
public decimal OriginalPrice { get; set; }
[Required]
[Display(Name = "ProductSkuInventory")]
public int Inventory { get; set; }
[Required]
[Display(Name = "ProductSkuOrderMinQuantity")]
public int OrderMinQuantity { get; set; }
[HiddenInput]
[Display(Name = "ProductSkuProductDetailId")]
public Guid? ProductDetailId { get; set; }
}
}
\ No newline at end of file
$(function () {
var l = abp.localization.getResource('Products');
var service = easyAbp.eShop.products.products.product;
var createModal = new abp.ModalManager(abp.appPath + 'EShop/Products/Products/ProductSku/CreateModal');
var editModal = new abp.ModalManager(abp.appPath + 'EShop/Products/Products/ProductSku/EditModal');
var dataTable = $('#ProductSkuTable').DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
autoWidth: false,
scrollCollapse: true,
order: [[1, "asc"]],
ajax: function (requestData, callback, settings) {
if (callback) {
service.get(productId).then(function (result) {
callback({
recordsTotal: result.productSkus.length,
recordsFiltered: result.productSkus.length,
data: fillProductSkusContentDescription(result).productSkus
});
});
}
},
columnDefs: [
{
rowAction: {
items:
[
{
text: l('Edit'),
action: function (data) {
editModal.open({ productId: productId, productSkuId: data.record.id, storeId: storeId });
}
},
{
text: l('Delete'),
confirmMessage: function (data) {
return l('ProductDeletionConfirmationMessage', data.record.id);
},
action: function (data) {
service.deleteSku(productId, data.record.id, storeId)
.then(function () {
abp.notify.info(l('SuccessfullyDeleted'));
dataTable.ajax.reload();
});
}
}
]
}
},
{ data: "contentDescription" },
{ data: "price" },
{ data: "inventory" },
]
}));
createModal.onResult(function () {
dataTable.ajax.reload();
});
editModal.onResult(function () {
dataTable.ajax.reload();
});
$('#NewProductSkuButton').click(function (e) {
e.preventDefault();
createModal.open({ storeId: storeId, productId: productId });
});
function fillProductSkusContentDescription(product) {
let attributeOptionNames = [];
for (let i in product.productAttributes) {
let attribute = product.productAttributes[i];
for (let j in attribute.productAttributeOptions) {
let option = attribute.productAttributeOptions[j];
attributeOptionNames[option.id] = option.displayName;
}
}
for (let i in product.productSkus) {
let sku = product.productSkus[i];
let options = [];
let attributeOptionIds = JSON.parse(sku.serializedAttributeOptionIds);
for (let j in attributeOptionIds) {
let optionId = attributeOptionIds[j];
options.push(attributeOptionNames[optionId]);
}
sku.contentDescription = options.join(',');
}
return product;
}
});
\ No newline at end of file
......@@ -7,6 +7,7 @@ using EasyAbp.EShop.Products.ProductTypes.Dtos;
using AutoMapper;
using EasyAbp.EShop.Products.ProductDetails.Dtos;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels;
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels;
using Volo.Abp.AutoMapper;
namespace EasyAbp.EShop.Products.Web
......@@ -50,6 +51,11 @@ namespace EasyAbp.EShop.Products.Web
CreateMap<CreateEditProductDetailViewModel, CreateUpdateProductDetailDto>();
CreateMap<ProductAttributeDto, CreateEditProductAttributeViewModel>();
CreateMap<CreateEditProductAttributeViewModel, CreateUpdateProductAttributeDto>();
CreateMap<CreateEditProductSkuViewModel, CreateProductSkuDto>()
.Ignore(dto => dto.SerializedAttributeOptionIds);
CreateMap<CreateEditProductSkuViewModel, UpdateProductSkuDto>();
CreateMap<ProductSkuDto, CreateEditProductSkuViewModel>()
.ForSourceMember(dto => dto.SerializedAttributeOptionIds, opt => opt.DoNotValidate());
CreateMap<ProductAttributeOptionDto, CreateEditProductAttributeOptionViewModel>();
CreateMap<CreateEditProductAttributeOptionViewModel, CreateUpdateProductAttributeOptionDto>();
CreateMap<CategoryDto, CreateUpdateCategoryDto>();
......
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