From a728b3807d95d3eb992e9037dc5e8f4b4a2db540 Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Mon, 13 Jun 2022 19:34:16 +0800 Subject: [PATCH 01/11] Improve order-status display on the list page --- .../EShop/Orders/Localization/Orders/en.json | 4 ++++ .../Orders/Localization/Orders/zh-Hans.json | 4 ++++ .../Orders/Localization/Orders/zh-Hant.json | 4 ++++ .../Pages/EShop/Orders/Orders/Order/index.css | 14 ++++++++++++++ .../Pages/EShop/Orders/Orders/Order/index.js | 19 ++++++++++++++++++- 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json index 622b7f6d..55b6b7c6 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json @@ -9,6 +9,10 @@ "OrderOrderNumber": "Order number", "OrderCustomerUserId": "Customer user ID", "OrderOrderStatus": "Status", + "OrderOrderStatusPending": "Pending", + "OrderOrderStatusProcessing": "Processing", + "OrderOrderStatusCompleted": "Completed", + "OrderOrderStatusCanceled": "Canceled", "OrderCurrency": "Currency", "OrderProductTotalPrice": "Product total price", "OrderTotalDiscount": "Total discount", diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json index e6c3385d..515b4ce9 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json @@ -9,6 +9,10 @@ "OrderOrderNumber": "订单号", "OrderCustomerUserId": "客户 ID", "OrderOrderStatus": "状态", + "OrderOrderStatusPending": "待支付", + "OrderOrderStatusProcessing": "进行中", + "OrderOrderStatusCompleted": "已完成", + "OrderOrderStatusCanceled": "已取消", "OrderCurrency": "币种", "OrderProductTotalPrice": "商品总价", "OrderTotalDiscount": "总折扣", diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json index a5efa664..63e4fc76 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json @@ -9,6 +9,10 @@ "OrderOrderNumber": "訂單號", "OrderCustomerUserId": "客戶 ID", "OrderOrderStatus": "狀態", + "OrderOrderStatusPending": "待支付", + "OrderOrderStatusProcessing": "進行中", + "OrderOrderStatusCompleted": "已完成", + "OrderOrderStatusCanceled": "已取消", "OrderCurrency": "幣種", "OrderProductTotalPrice": "商品總價", "OrderTotalDiscount": "總折扣", diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.css b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.css index e69de29b..3a4f487a 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.css +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.css @@ -0,0 +1,14 @@ +.status-pending-text { + color: orange; +} + +.status-processing-text { + color: green; +} + +.status-completed-text { + color: blue; +} + +.status-canceled-text { +} \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.js b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.js index 3cef19de..ff583b8a 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.js +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Web/Pages/EShop/Orders/Orders/Order/index.js @@ -31,7 +31,24 @@ $(function () { }, { data: "orderNumber" }, { data: "customerUserId" }, - { data: "orderStatus" }, + { + data: "orderStatus", + render: function (data, type, row) { + if (data === 1) { + return '' + l('OrderOrderStatusPending') + '' + } + if (data === 2) { + return '' + l('OrderOrderStatusProcessing') + '' + } + if (data === 4) { + return '' + l('OrderOrderStatusCompleted') + '' + } + if (data === 8) { + return '' + l('OrderOrderStatusCanceled') + '' + } + return '' + } + }, { data: "currency" }, { data: "actualTotalPrice" }, ] From 3dbf03a57a56e8d2305b5e16409f54fb27e08fdd Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Mon, 13 Jun 2022 19:43:51 +0800 Subject: [PATCH 02/11] Introduce a quick-start home page in the sample app --- .../Localization/EShopSample/en.json | 31 +- .../Localization/EShopSample/zh-Hans.json | 43 ++- .../Localization/EShopSample/zh-Hant.json | 43 ++- .../Data/SampleDataConsts.cs | 7 + .../Data/SampleDataSeedContributor.cs | 181 +++++++++++ .../EShopSample.Web/EShopSample.Web.csproj | 1 + .../src/EShopSample.Web/Pages/Index.cshtml | 115 ++++++- .../src/EShopSample.Web/Pages/Index.cshtml.cs | 137 +++++++- .../src/EShopSample.Web/Pages/Index.css | 53 +++- .../src/EShopSample.Web/Pages/Index.js | 128 +++++++- .../src/EShopSample.Web/Pages/SkuSelector.js | 299 ++++++++++++++++++ 11 files changed, 1013 insertions(+), 25 deletions(-) create mode 100644 samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs create mode 100644 samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs create mode 100644 samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json index 38faa8df..8a479bc4 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json @@ -4,6 +4,35 @@ "Menu:EasyAbpEShop": "EShop", "Menu:Home": "Home", "Welcome": "Welcome", - "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io." + "LongWelcomeMessage": "Welcome to the application. This is a demo project of the EShop module. For more information, visit easyabp.io.", + "Product": "Product", + "Cake": "Cake", + "OrderCake": "Order a cake", + "CreateOrder": "Create order", + "PayForOrder": "Pay for order", + "TopUp-1-CNY": "Top-up 1.00 CNY", + "OrderHistory": "Order history", + "Flavor": "Flavor", + "Size": "Size", + "Chocolate": "Chocolate", + "Vanilla": "Vanilla", + "YourChoice": "Your choice", + "SkuId": "SKU ID", + "OrderCreated": "Order created", + "OrderCanceled": "Order canceled", + "TopUpSucceeded": "Top-up succeeded", + "PaymentSucceeded": "Payment succeeded", + "InsufficientBalance": "Insufficient account balance", + "OrderNumber": "Order number", + "Created": "Created", + "OrderStatus": "Status", + "OrderStatusPending": "Pending", + "OrderStatusProcessing": "Processing", + "OrderStatusCompleted": "Completed", + "OrderStatusCanceled": "Canceled", + "TotalPrice": "Price", + "AccountBalance": "Account balance", + "TimeToAutoCancel": "Order auto-cancel in", + "CancelOrder": "Cancel order" } } \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json index 038ece66..da46a8de 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json @@ -1,9 +1,38 @@ { - "culture": "zh-Hans", - "texts": { + "culture": "zh-Hans", + "texts": { "Menu:EasyAbpEShop": "EShop 商城", - "Menu:Home": "首页", - "Welcome": "欢迎", - "LongWelcomeMessage": "欢迎来到该应用程序. 这是一个基于ABP框架的启动项目. 有关更多信息, 请访问 abp.io." - } - } \ No newline at end of file + "Menu:Home": "首页", + "Welcome": "欢迎", + "LongWelcomeMessage": "欢迎来到该应用程序. 这是一个 EShop 模块的示例项目. 有关更多信息, 请访问 easyabp.io.", + "Product": "商品", + "Cake": "蛋糕", + "OrderCake": "下单买蛋糕", + "CreateOrder": "创建订单", + "PayForOrder": "支付订单", + "TopUp-1-CNY": "充值 1.00 元", + "OrderHistory": "历史订单", + "Flavor": "口味", + "Size": "分量", + "Chocolate": "巧克力味", + "Vanilla": "香草味", + "YourChoice": "你的选择", + "SkuId": "SKU ID", + "OrderCreated": "订单创建成功", + "OrderCanceled": "订单已取消", + "TopUpSucceeded": "充值成功", + "PaymentSucceeded": "支付成功", + "InsufficientBalance": "账户余额不足", + "OrderNumber": "订单编号", + "Created": "创建时间", + "OrderStatus": "状态", + "OrderStatusPending": "待支付", + "OrderStatusProcessing": "进行中", + "OrderStatusCompleted": "已完成", + "OrderStatusCanceled": "已取消", + "TotalPrice": "价格", + "AccountBalance": "账户余额", + "TimeToAutoCancel": "剩余可支付时间", + "CancelOrder": "取消订单" + } +} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json index 75e9122e..9b79a497 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json @@ -1,9 +1,38 @@ { - "culture": "zh-Hant", - "texts": { + "culture": "zh-Hant", + "texts": { "Menu:EasyAbpEShop": "EShop 商城", - "Menu:Home": "首頁", - "Welcome": "歡迎", - "LongWelcomeMessage": "歡迎來到此應用程式. 這是一個基於ABP框架的起始專案. 有關更多訊息, 請瀏覽 abp.io." - } - } \ No newline at end of file + "Menu:Home": "首頁", + "Welcome": "歡迎", + "LongWelcomeMessage": "歡迎來到此應用程式. 這是一個 EShop 模組的示例專案. 有關更多訊息, 請瀏覽 easyabp.io.", + "Product": "商品", + "Cake": "蛋糕", + "OrderCake": "下單買蛋糕", + "CreateOrder": "創建訂單", + "PayForOrder": "支付訂單", + "TopUp-1-CNY": "給賬戶充值", + "OrderHistory": "歷史訂單", + "Flavor": "口味", + "Size": "分量", + "Chocolate": "巧克力味", + "Vanilla": "香草味", + "YourChoice": "你的選擇", + "SkuId": "SKU ID", + "OrderCreated": "訂單創建成功", + "OrderCanceled": "訂單已取消", + "TopUpSucceeded": "充值成功", + "PaymentSucceeded": "支付成功", + "InsufficientBalance": "賬戶餘額不足", + "OrderNumber": "訂單編號", + "Created": "創建時間", + "OrderStatus": "狀態", + "OrderStatusPending": "待支付", + "OrderStatusProcessing": "進行中", + "OrderStatusCompleted": "已完成", + "OrderStatusCanceled": "已取消", + "TotalPrice": "價格", + "AccountBalance": "賬戶餘額", + "TimeToAutoCancel": "剩餘可支付時間", + "CancelOrder": "取消訂單" + } +} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs new file mode 100644 index 00000000..1bb1bb65 --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs @@ -0,0 +1,7 @@ +namespace EShopSample.Data; + +public static class SampleDataConsts +{ + public const string FoodsCategoryUniqueName = "Foods"; + public const string CakeProductUniqueName = "Cake"; +} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs new file mode 100644 index 00000000..90bf1c27 --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs @@ -0,0 +1,181 @@ +using System; +using System.Threading.Tasks; +using EasyAbp.EShop.Products; +using EasyAbp.EShop.Products.Categories; +using EasyAbp.EShop.Products.ProductCategories; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Stores.Settings; +using EasyAbp.EShop.Stores.Stores; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Settings; +using Volo.Abp.Uow; + +namespace EShopSample.Data; + +public class SampleDataSeedContributor : IDataSeedContributor, ITransientDependency +{ + private readonly IGuidGenerator _guidGenerator; + private readonly ICurrentTenant _currentTenant; + private readonly IStoreRepository _storeRepository; + private readonly IProductManager _productManager; + private readonly IProductRepository _productRepository; + private readonly ICategoryManager _categoryManager; + private readonly ICategoryRepository _categoryRepository; + private readonly IProductCategoryRepository _productCategoryRepository; + private readonly ISettingProvider _settingProvider; + private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer; + + public SampleDataSeedContributor( + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant, + IStoreRepository storeRepository, + IProductManager productManager, + IProductRepository productRepository, + ICategoryManager categoryManager, + ICategoryRepository categoryRepository, + IProductCategoryRepository productCategoryRepository, + ISettingProvider settingProvider, + IAttributeOptionIdsSerializer attributeOptionIdsSerializer) + { + _guidGenerator = guidGenerator; + _currentTenant = currentTenant; + _storeRepository = storeRepository; + _productManager = productManager; + _productRepository = productRepository; + _categoryManager = categoryManager; + _categoryRepository = categoryRepository; + _productCategoryRepository = productCategoryRepository; + _settingProvider = settingProvider; + _attributeOptionIdsSerializer = attributeOptionIdsSerializer; + } + + [UnitOfWork(true)] + public virtual async Task SeedAsync(DataSeedContext context) + { + using var changeTenant = _currentTenant.Change(context.TenantId); + + await SeedStoresAsync(); + await SeedCategoriesAsync(); + await SeedProductsAsync(); + await SeedProductCategoryMappingsAsync(); + } + + protected virtual async Task SeedProductCategoryMappingsAsync() + { + var product = await _productRepository.GetAsync( + x => x.UniqueName == SampleDataConsts.CakeProductUniqueName); + + var category = await _categoryRepository.GetAsync( + x => x.UniqueName == SampleDataConsts.FoodsCategoryUniqueName); + + if (await _productCategoryRepository.AnyAsync(x => x.ProductId == product.Id && x.CategoryId == category.Id)) + { + return; + } + + await _productCategoryRepository.InsertAsync( + new ProductCategory(_guidGenerator.Create(), _currentTenant.Id, category.Id, product.Id)); + } + + public virtual async Task SeedStoresAsync() + { + var storeName = await _settingProvider.GetOrNullAsync(StoresSettings.DefaultStoreName); + + if (await _storeRepository.AnyAsync(x => x.Name == storeName)) + { + return; + } + + await _storeRepository.InsertAsync(new Store(_guidGenerator.Create(), _currentTenant.Id, storeName), true); + } + + public virtual async Task SeedCategoriesAsync() + { + if (await _categoryRepository.AnyAsync(x => x.UniqueName == SampleDataConsts.FoodsCategoryUniqueName)) + { + return; + } + + var category = await _categoryManager.CreateAsync(null, SampleDataConsts.FoodsCategoryUniqueName, "Foods", + "Some delicious foods.", null, false); + + await _categoryRepository.InsertAsync(category, true); + } + + public virtual async Task SeedProductsAsync() + { + if (await _productRepository.AnyAsync(x => x.UniqueName == SampleDataConsts.CakeProductUniqueName)) + { + return; + } + + var defaultStore = await _storeRepository.FindDefaultStoreAsync(); + + var product = new Product( + _guidGenerator.Create(), + _currentTenant.Id, + defaultStore.Id, + ProductsConsts.DefaultProductGroupName, + null, + SampleDataConsts.CakeProductUniqueName, + "Cake", + InventoryStrategy.NoNeed, + null, + true, + false, + false, + TimeSpan.FromMinutes(15), + null, + 0); + + var attribute1 = new ProductAttribute(_guidGenerator.Create(), "Size", null, 2); + var attribute2 = new ProductAttribute(_guidGenerator.Create(), "Flavor", null, 1); + + attribute1.ProductAttributeOptions.AddRange(new[] + { + new ProductAttributeOption(_guidGenerator.Create(), "S", null, 1), + new ProductAttributeOption(_guidGenerator.Create(), "M", null, 2), + new ProductAttributeOption(_guidGenerator.Create(), "L", null, 3), + }); + + attribute2.ProductAttributeOptions.AddRange(new[] + { + new ProductAttributeOption(_guidGenerator.Create(), "Chocolate", null, 1), + new ProductAttributeOption(_guidGenerator.Create(), "Vanilla", null, 2), + }); + + product.ProductAttributes.Add(attribute1); + product.ProductAttributes.Add(attribute2); + + await _productManager.CreateAsync(product); + + var productSku1 = new ProductSku(_guidGenerator.Create(), + await _attributeOptionIdsSerializer.SerializeAsync(new[] + { attribute1.ProductAttributeOptions[0].Id, attribute2.ProductAttributeOptions[0].Id }), + null, "CNY", null, 1m, 1, 10, null, null, null); + + var productSku2 = new ProductSku(_guidGenerator.Create(), + await _attributeOptionIdsSerializer.SerializeAsync(new[] + { attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[0].Id }), + null, "CNY", null, 2m, 1, 10, null, null, null); + + var productSku3 = new ProductSku(_guidGenerator.Create(), + await _attributeOptionIdsSerializer.SerializeAsync(new[] + { attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[1].Id }), + null, "CNY", null, 3m, 1, 10, null, null, null); + + var productSku4 = new ProductSku(_guidGenerator.Create(), + await _attributeOptionIdsSerializer.SerializeAsync(new[] + { attribute1.ProductAttributeOptions[2].Id, attribute2.ProductAttributeOptions[1].Id }), + null, "CNY", null, 4m, 1, 10, null, null, null); + + await _productManager.CreateSkuAsync(product, productSku1); + await _productManager.CreateSkuAsync(product, productSku2); + await _productManager.CreateSkuAsync(product, productSku3); + await _productManager.CreateSkuAsync(product, productSku4); + } +} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj index c602502d..ecd5361c 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj @@ -20,6 +20,7 @@ + diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml index 6580377f..40b4148a 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml @@ -1,16 +1,33 @@ @page @inherits EShopSample.Web.Pages.EShopSamplePage +@using EasyAbp.EShop.Orders.Orders @model EShopSample.Web.Pages.IndexModel + @section styles { - + } + @section scripts { - + + } + + +

@L["Welcome"]

@@ -19,9 +36,99 @@
- abp.io @if (!CurrentUser.IsAuthenticated) { - @L["Login"] + + @L["Login"] + + } + else + { +
+
+ + +

@L[Model.CakeProduct.DisplayName]

+
+
+
+
+
+
+
+ @L["CreateOrder"] +
+ + @if (Model.Order is not null) + { + + + @L["OrderNumber"] + @Model.Order.OrderNumber + + + @L["Product"] + @L[Model.CakeProduct.DisplayName] + + @foreach (var optionId in Model.CakeProduct.ProductSkus.First(x => x.Id == Model.Order.OrderLines[0].ProductSkuId).AttributeOptionIds) + { + var option = Model.CakeProduct.ProductAttributes.SelectMany(x => x.ProductAttributeOptions) + .Single(x => x.Id == optionId); + var attr = Model.CakeProduct.ProductAttributes.Single(x => x.ProductAttributeOptions.Contains(option)); + + @L[attr.DisplayName] + @L[option.DisplayName] + + } + + @L["TotalPrice"] + @Model.Order.ActualTotalPrice.ToString("F2") @Model.Order.Currency + + + @L["AccountBalance"] + @Model.Wallet.Balance.ToString("F2") @Model.Order.Currency + + +
+
+

+ @L["TimeToAutoCancel"] ? +

+
+
+ @L["CancelOrder"] + @L["TopUp-1-CNY"] + @L["PayForOrder"] + } +
+ + + + + # + @L["OrderNumber"] + @L["Created"] + @L["OrderStatus"] + + + + @{ + var index = Model.OrderList.TotalCount; + foreach (var order in Model.OrderList.Items) + { + + @(index--) + @order.OrderNumber + @order.CreationTime + @L[$"OrderStatus{order.OrderStatus}"] + + } + } + + + +
+
+
} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs index 8dcb2be4..c6d2443a 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs @@ -1,10 +1,141 @@ -namespace EShopSample.Web.Pages +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EasyAbp.EShop.Orders.Orders; +using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Products.Products; +using EasyAbp.EShop.Products.Products.Dtos; +using EasyAbp.EShop.Stores.Stores; +using EasyAbp.EShop.Stores.Stores.Dtos; +using EasyAbp.PaymentService.Prepayment.Accounts; +using EasyAbp.PaymentService.Prepayment.Accounts.Dtos; +using EShopSample.Data; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Users; + +namespace EShopSample.Web.Pages; + +public class IndexModel : EShopSamplePageModel { - public class IndexModel : EShopSamplePageModel + private readonly IOrderAppService _orderAppService; + private readonly IStoreAppService _storeAppService; + private readonly IProductAppService _productAppService; + private readonly IAccountAppService _accountAppService; + + public OrderDto Order { get; set; } + public PagedResultDto OrderList { get; set; } + public StoreDto Store { get; set; } + public ProductDto CakeProduct { get; set; } + public AccountDto Wallet { get; set; } + + public IndexModel( + IOrderAppService orderAppService, + IStoreAppService storeAppService, + IProductAppService productAppService, + IAccountAppService accountAppService) { - public void OnGet() + _orderAppService = orderAppService; + _storeAppService = storeAppService; + _productAppService = productAppService; + _accountAppService = accountAppService; + } + + public async Task OnGetAsync() + { + if (CurrentUser.Id is null) + { + return; + } + + OrderList = await _orderAppService.GetListAsync(new GetOrderListDto { + MaxResultCount = 5, + CustomerUserId = CurrentUser.GetId(), + Sorting = "CreationTime DESC" + }); + + Order = OrderList.Items.FirstOrDefault(x => x.OrderStatus is OrderStatus.Pending); + + Store = await _storeAppService.GetDefaultAsync(); + + CakeProduct = await _productAppService.GetByUniqueNameAsync(Store.Id, SampleDataConsts.CakeProductUniqueName); + + Wallet = (await _accountAppService.GetListAsync(new GetAccountListInput { UserId = CurrentUser.Id })).Items[0]; + } + + public string GetJsonSkuInfo() + { + var sb = new StringBuilder("["); + + foreach (var sku in CakeProduct.ProductSkus) + { + sb.Append('{'); + sb.Append("\"skus\":"); + sb.Append('{'); + + foreach (var optionId in sku.AttributeOptionIds) + { + var option = CakeProduct.ProductAttributes.SelectMany(x => x.ProductAttributeOptions) + .Single(x => x.Id == optionId); + + var attribute = CakeProduct.ProductAttributes.Single(x => x.ProductAttributeOptions.Contains(option)); + + sb.Append($"\"{L[attribute.DisplayName].Value}\":\"{L[option.DisplayName].Value}\""); + + if (optionId != sku.AttributeOptionIds.Last()) + { + sb.Append(','); + } + } + sb.Append('}'); + + sb.Append($",\"skuId\":\"{sku.Id}\""); + sb.Append($",\"skuPrice\":{sku.Price}"); + sb.Append($",\"skuCurrency\":\"{sku.Currency}\""); + + sb.Append('}'); + + if (sku != CakeProduct.ProductSkus.Last()) + { + sb.Append(','); + } + } + + sb.Append(']'); + + return sb.ToString(); + } + + public int GetSecondsToAutoCancel() + { + if (Order is null) + { + return 0; + } + + return Order.PaymentExpiration.HasValue + ? Convert.ToInt32((Order.PaymentExpiration.Value - Clock.Now).TotalSeconds) + : 0; + } + + public string GetOrderStatusColumnClass(OrderDto order) + { + if (order is null) + { + return string.Empty; } + + return order.OrderStatus switch + { + OrderStatus.Pending => "status-pending-text", + OrderStatus.Processing => "status-processing-text", + OrderStatus.Completed => "status-completed-text", + OrderStatus.Canceled => "status-canceled-text", + _ => throw new ArgumentOutOfRangeException() + }; } } \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css index 3f73b78b..c5bd7627 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css @@ -1,3 +1,52 @@ -body { - +#skuSelector dt { + width: 100px; + text-align: right; + font-weight: normal; + padding-top: 6px; } + +#skuSelector dl { + clear: both; + overflow: hidden; +} + +#skuSelector dl.hl { + background: #ddd; +} + +#skuSelector dt, #skuSelector dd { + float: left; + line-height: 18px; + margin-left: 10px; + margin-bottom: 0; +} + +#skuSelector button { + font-size: 14px; + font-weight: bold; + padding: 4px 4px; +} + +#skuSelector .disabled { + color: #999; + border: 1px dashed #666; +} + +#skuSelector .active { + color: red; +} + +.status-pending-text { + color: orange; +} + +.status-processing-text { + color: green; +} + +.status-completed-text { + color: blue; +} + +.status-canceled-text { +} \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js index 46f089b9..f568a742 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js @@ -1,3 +1,129 @@ $(function () { - abp.log.debug('Index.js initialized!'); + const l = abp.localization.getResource('EasyAbpEShopEShopSample'); + const orderService = easyAbp.eShop.orders.orders.order; + const eShopPaymentService = easyAbp.eShop.payments.payments.payment; + const paymentService = easyAbp.paymentService.payments.payment; + const accountService = easyAbp.paymentService.prepayment.accounts.account; + + $('#CreateOrderButton').click(function (e) { + e.preventDefault(); + if (!cakeProductSkuId) return + let btn = $(this) + btn.buttonBusy(true); + orderService.create({ + storeId: storeId, + orderLines: [{ + productId: cakeProductId, + productSkuId: cakeProductSkuId, + quantity: 1 + }] + }).then(function () { + abp.message.success(l('OrderCreated')) + .then(function () { + window.location.reload(); + }) + }).catch(function () { + btn.buttonBusy(false); + }); + }); + + let redirectToOrderHistory = function () { + document.location.href = document.location.origin + '?showOrderHistory=true' + } + + $('#CancelOrderButton').click(function (e) { + e.preventDefault(); + orderService.cancel(orderId, {}) + .then(function () { + abp.message.success(l('OrderCanceled')) + .then(function () { + redirectToOrderHistory() + }) + }); + }); + + $('#TopUpButton').click(function (e) { + e.preventDefault(); + accountService.changeBalance(accountId, {changedBalance: 1.00}) + .then(function () { + abp.message.success(l('TopUpSucceeded')) + .then(function () { + window.location.reload(); + }) + }); + }); + + $('#PayForOrderButton').click(function (e) { + e.preventDefault(); + if (currentBalance < totalPrice) { + abp.message.error(l('InsufficientBalance')) + .then(function () { + window.location.reload(); + }) + return + } + eShopPaymentService.create({ + paymentMethod: "Prepayment", + orderIds: [orderId] + }).then(function () { + getPaymentIdAndPay() + }); + }); + + let getPaymentIdAndPay = function () { + orderService.get(orderId).then(function (order) { + if (!order) return + if (!order.paymentId) { + setTimeout(getPaymentIdAndPay, 500) + return + } + paymentService.pay(order.paymentId, { + extraProperties: { + "AccountId": accountId + } + }).then(function () { + abp.message.success(l('PaymentSucceeded')) + .then(function () { + redirectToOrderHistory() + }) + }) + }) + } + + let updateTimeToAutoCancel = function () { + $('#timeToAutoCancel').text(new Date(secondsToAutoCancel * 1000).toISOString().substring(11, 19)) + } + + let cancelCurrentPayment = function () { + if (!orderId) return + orderService.get(orderId).then(function (order) { + if (order.paymentId) { + paymentService.cancel(order.paymentId) + } + }) + } + + function tryGoToOrderHistoryTab() { + if (location.search.indexOf('showOrderHistory=true') < 0) return + $('#OrderHistoryTab-tab').tab("show") + history.pushState(null, null, location.origin) + // document.location.href = document.location.href.split('#')[0] + } + + let init = function () { + tryGoToOrderHistoryTab() + updateTimeToAutoCancel() + cancelCurrentPayment() + } + + init() + + let interval = setInterval(function () { + if (secondsToAutoCancel <= 0) { + clearInterval(interval) + return + } + secondsToAutoCancel-- + updateTimeToAutoCancel() + }, 1000); }); \ No newline at end of file diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js new file mode 100644 index 00000000..2770d481 --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js @@ -0,0 +1,299 @@ +// Copied from https://codepen.io/keelii/pen/RoOzgb +const l = abp.localization.getResource('EasyAbpEShopEShopSample'); + +var data = JSON.parse(skuInfo) + +var res = {} + +var spliter = '\u2299' +var r = {} +var keys = [] +var selectedCache = [] + +function combineAttr(data, keys) { + var allKeys = [] + var result = {} + + for (var i = 0; i < data.length; i++) { + var item = data[i].skus + var values = [] + + for (var j = 0; j < keys.length; j++) { + var key = keys[j] + if (!result[key]) result[key] = [] + if (result[key].indexOf(item[key]) < 0) result[key].push(item[key]) + values.push(item[key]) + } + allKeys.push({ + path: values.join(spliter), + sku: data[i].skuId, + price: data[i].skuPrice, + currency: data[i].skuCurrency + }) + } + return { + result: result, + items: allKeys + } +} + + +function render(data) { + var output = '' + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var items = data[key] + + output += '
' + output += '
' + key + ':
' + output += '
' + for (var j = 0; j < items.length; j++) { + var item = items[j] + var cName = j == 0 ? 'active' : '' + if (j == 0) { + selectedCache.push(item) + } + output += ' ' + } + output += '
' + output += '
' + } + output += '' + + $('#skuSelector').html(output) +} + +function getAllKeys(arr) { + var result = [] + for (var i = 0; i < arr.length; i++) { + result.push(arr[i].path) + } + return result +} + +function powerset(arr) { + var ps = [[]]; + for (var i = 0; i < arr.length; i++) { + for (var j = 0, len = ps.length; j < len; j++) { + ps.push(ps[j].concat(arr[i])); + } + } + return ps; +} + +function buildResult(items) { + var allKeys = getAllKeys(items) + + for (var i = 0; i < allKeys.length; i++) { + var curr = allKeys[i] + var sku = items[i].sku + var price = items[i].price + var values = curr.split(spliter) + + var allSets = powerset(values) + + for (var j = 0; j < allSets.length; j++) { + var set = allSets[j] + var key = set.join(spliter) + + if (res[key]) { + res[key].sku = items[i].sku + res[key].price = items[i].price + res[key].currency = items[i].currency + } else { + res[key] = { + sku: items[i].sku, + price: items[i].price, + currency: items[i].currency + } + } + } + } +} + +function trimSpliter(str, spliter) { + var reLeft = new RegExp('^' + spliter + '+', 'g'); + var reRight = new RegExp(spliter + '+$', 'g'); + var reSpliter = new RegExp(spliter + '+', 'g'); + return str.replace(reLeft, '') + .replace(reRight, '') + .replace(reSpliter, spliter) +} + +function getSelectedItem() { + var result = [] + $('dl[data-type]').each(function () { + var $selected = $(this).find('.active') + if ($selected.length) { + result.push($selected.val()) + } else { + result.push('') + } + }) + + return result +} + +function updateStatus(selected) { + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var data = r.result[key] + var hasActive = !!selected[i] + var copy = selected.slice() + + for (var j = 0; j < data.length; j++) { + var item = data[j] + if (selected[i] == item) continue + copy[i] = item + + var curr = trimSpliter(copy.join(spliter), spliter) + var $item = $('dl').filter('[data-type="' + key + '"]').find('[value="' + item + '"]') + + var titleStr = '[' + copy.join('-') + ']' + + if (res[curr]) { + $item.removeClass('disabled') + setTitle($item.get(0)) + } else { + $item.addClass('disabled').attr('title', titleStr + ' 无此属性搭配') + } + } + } +} + +function handleNormalClick($this) { + $this.siblings().removeClass('active') + $this.addClass('active') +} + +function handleDisableClick($this) { + var $currAttr = $this.parents('dl').eq(0) + var idx = $currAttr.data('idx') + var type = $currAttr.data('type') + var value = $this.val() + + $this.removeClass('disabled') + selectedCache[idx] = value + + console.log(selectedCache) + $('dl').not($currAttr).find('button').removeClass('active') + updateStatus(getSelectedItem()) + + for (var i = 0; i < keys.length; i++) { + var item = keys[i] + var $curr = $('dl[data-type="' + item + '"]') + if (item == type) continue + + var $lastSelected = $curr.find('button[value="' + selectedCache[i] + '"]') + + if (!$lastSelected.hasClass('disabled')) { + $lastSelected.addClass('active') + updateStatus(getSelectedItem()) + } + } + +} + +function highLightAttr() { + for (var i = 0; i < keys.length; i++) { + var key = keys[i] + var $curr = $('dl[data-type="' + key + '"]') + if ($curr.find('.active').length < 1) { + $curr.addClass('hl') + } else { + $curr.removeClass('hl') + } + } +} + +function bindEvent() { + $('#skuSelector').undelegate().delegate('button', 'click', function (e) { + var $this = $(this) + + var isActive = $this.hasClass('.active') + var isDisable = $this.hasClass('disabled') + + if (!isActive) { + handleNormalClick($this) + + if (isDisable) { + handleDisableClick($this) + } else { + selectedCache[$this.parents('dl').eq(0).data('idx')] = $this.val() + } + updateStatus(getSelectedItem()) + highLightAttr() + showResult() + } + }) + + $('button').each(function () { + var value = $(this).val() + + if (!res[value] && !$(this).hasClass('active')) { + $(this).addClass('disabled') + } + }) +} + +function showResult() { + var result = getSelectedItem() + var s = [] + + for (var i = 0; i < result.length; i++) { + var item = result[i]; + if (!!item) { + s.push(item) + } + } + + if (s.length == keys.length) { + var curr = res[s.join(spliter)] + if (curr && curr.sku) { + cakeProductSkuId = curr.sku + $('#selectedSku').html('' + l('YourChoice') + '
' + s.join('\u3000-\u3000')) + $('#selectedSkuId').html('' + l('SkuId') + '
' + curr.sku) + $('#selectedSkuPrice').html('' + l('TotalPrice') + '
' + curr.price.toFixed(2) + ' ' + curr.currency) + } + } +} + +function updateData() { + data = JSON.parse(skuInfo) + init(data) +} + +function setTitle(el) { + var title = $(el).data('title'); + if (title) $(el).attr('title', title); +} + +function setAllTitle() { + $('#skuSelector').find('button').each(setTitle) +} + +function initSkuSelector(data) { + res = {} + r = {} + keys = [] + selectedCache = [] + + for (var attr_key in data[0].skus) { + if (!data[0].skus.hasOwnProperty(attr_key)) continue; + keys.push(attr_key) + } + setAllTitle(); + + r = combineAttr(data, keys) + + render(r.result) + + buildResult(r.items) + + updateStatus(getSelectedItem()) + showResult() + + bindEvent() +} + +initSkuSelector(data) \ No newline at end of file From bcc2ae2df0e15e96587f5e6d2e5b1e853885133d Mon Sep 17 00:00:00 2001 From: Super Date: Mon, 13 Jun 2022 22:44:06 +0800 Subject: [PATCH 03/11] Update EShopSample.Web.csproj --- .../aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj index ecd5361c..c602502d 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj @@ -20,7 +20,6 @@ - From 2fc165f38bb7cfe6a1728aa425c216e32fa631b6 Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Tue, 14 Jun 2022 20:38:42 +0800 Subject: [PATCH 04/11] Fix https://github.com/abpframework/abp/issues/12978 --- .../MyMenuViewModelProvider.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 samples/EShopSample/aspnet-core/src/EShopSample.Web/MyMenuViewModelProvider.cs diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/MyMenuViewModelProvider.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Web/MyMenuViewModelProvider.cs new file mode 100644 index 00000000..4e36b9f8 --- /dev/null +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/MyMenuViewModelProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Layout; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Themes.LeptonXLite.Components.Menu; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ObjectMapping; +using Volo.Abp.UI.Navigation; + +namespace EShopSample.Web; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(MenuViewModelProvider))] +public class MyMenuViewModelProvider : MenuViewModelProvider +{ + public MyMenuViewModelProvider(IMenuManager menuManager, IPageLayout pageLayout, + IObjectMapper objectMapper) : base(menuManager, pageLayout, + objectMapper) + { + } + + protected override bool SetActiveMenuItems(IList items, string activeMenuItemName) + { + foreach (var item in items) + { + if (SetActiveMenuItems(item.Items, activeMenuItemName) || item.MenuItem.Name == activeMenuItemName) + { + item.IsActive = true; + return true; + } + } + + return false; + } +} \ No newline at end of file From d3ab895922f728b83ffd8334a1a71ff743a63d8e Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Wed, 15 Jun 2022 00:08:26 +0800 Subject: [PATCH 05/11] Change all default/example/test currency codes from `CNY` to `USD` Resolve #176 --- .../EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs | 2 +- .../Orders/OrderAppServiceTests.cs | 10 +++++----- .../Orders/InventoryReductionResultTests.cs | 4 ++-- .../Orders/OrderDomainTests.cs | 10 +++++----- .../Payments/PaymentAppServiceTests.cs | 4 ++-- .../Refunds/RefundAppServiceTests.cs | 12 ++++++------ .../Refunds/RefundOrderEventHandlerTests.cs | 2 +- .../Products/ProductAppServiceTests.cs | 6 +++--- .../Products/ProductDomainTests.cs | 2 +- .../ProductsTestDataBuilder.cs | 6 +++--- .../Localization/EShopSample/en.json | 2 +- .../Localization/EShopSample/zh-Hans.json | 2 +- .../Localization/EShopSample/zh-Hant.json | 2 +- .../Data/SampleDataSeedContributor.cs | 8 ++++---- .../EShopSample.Domain/EShopSampleDomainModule.cs | 2 +- .../src/EShopSample.Web/Pages/Index.cshtml | 2 +- 16 files changed, 38 insertions(+), 38 deletions(-) diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs index b6a5eaae..102695f6 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs @@ -174,7 +174,7 @@ namespace EasyAbp.EShop.Orders.Orders protected virtual Task GetStoreCurrencyAsync(Guid storeId) { // Todo: Get real store currency configuration. - return Task.FromResult("CNY"); + return Task.FromResult("USD"); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs index b7f2eebd..2d507b7b 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs @@ -53,7 +53,7 @@ namespace EasyAbp.EShop.Orders.Orders OrderMaxQuantity = 100, AttributeOptionIds = new List(), Price = 1m, - Currency = "CNY", + Currency = "USD", ProductDetailId = null }, new ProductSkuDto @@ -64,7 +64,7 @@ namespace EasyAbp.EShop.Orders.Orders OrderMaxQuantity = 100, AttributeOptionIds = new List(), Price = 2m, - Currency = "CNY", + Currency = "USD", ProductDetailId = OrderTestData.ProductDetail2Id }, new ProductSkuDto @@ -75,7 +75,7 @@ namespace EasyAbp.EShop.Orders.Orders OrderMaxQuantity = 100, AttributeOptionIds = new List(), Price = 3m, - Currency = "CNY", + Currency = "USD", ProductDetailId = OrderTestData.ProductDetail2Id } }, @@ -151,7 +151,7 @@ namespace EasyAbp.EShop.Orders.Orders context.Orders.Count().ShouldBe(1); var order = context.Orders.Include(x => x.OrderLines).First(); order.ShouldNotBeNull(); - order.Currency.ShouldBe("CNY"); + order.Currency.ShouldBe("USD"); order.CanceledTime.ShouldBeNull(); order.CancellationReason.ShouldBeNullOrEmpty(); order.CompletionTime.ShouldBeNull(); @@ -185,7 +185,7 @@ namespace EasyAbp.EShop.Orders.Orders orderLine1.TotalPrice.ShouldBe(10m); orderLine1.TotalDiscount.ShouldBe(0m); orderLine1.ActualTotalPrice.ShouldBe(10m); - orderLine1.Currency.ShouldBe("CNY"); + orderLine1.Currency.ShouldBe("USD"); orderLine1.Quantity.ShouldBe(10); orderLine1.ProductModificationTime.ShouldBe(OrderTestData.ProductLastModificationTime); orderLine1.ProductDetailModificationTime.ShouldBe(OrderTestData.ProductDetailLastModificationTime); diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs index 756c9eca..c20476dc 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/InventoryReductionResultTests.cs @@ -20,7 +20,7 @@ public class InventoryReductionResultTests : OrdersDomainTestBase null, OrderTestData.Store1Id, Guid.NewGuid(), - "CNY", + "USD", 1m, 0m, 1.5m, @@ -41,7 +41,7 @@ public class InventoryReductionResultTests : OrdersDomainTestBase null, null, null, - "CNY", + "USD", 0.5m, 1m, 0m, diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs index 6641e1ad..4a4a61f1 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Domain.Tests/Orders/OrderDomainTests.cs @@ -28,7 +28,7 @@ namespace EasyAbp.EShop.Orders.Orders null, OrderTestData.Store1Id, Guid.NewGuid(), - "CNY", + "USD", 1m, 0m, 1.5m, @@ -49,7 +49,7 @@ namespace EasyAbp.EShop.Orders.Orders null, null, null, - "CNY", + "USD", 0.5m, 1m, 0m, @@ -82,7 +82,7 @@ namespace EasyAbp.EShop.Orders.Orders Id = Guid.NewGuid(), TenantId = null, PaymentId = OrderTestData.Payment1Id, - Currency = "CNY", + Currency = "USD", RefundAmount = 0.3m, RefundItems = new List { @@ -140,7 +140,7 @@ namespace EasyAbp.EShop.Orders.Orders Id = Guid.NewGuid(), TenantId = null, PaymentId = OrderTestData.Payment1Id, - Currency = "CNY", + Currency = "USD", RefundAmount = -1m, RefundItems = new List { @@ -181,7 +181,7 @@ namespace EasyAbp.EShop.Orders.Orders Id = Guid.NewGuid(), TenantId = null, PaymentId = OrderTestData.Payment1Id, - Currency = "CNY", + Currency = "USD", RefundAmount = 0.3m, RefundItems = new List { diff --git a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Payments/PaymentAppServiceTests.cs b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Payments/PaymentAppServiceTests.cs index a792fdc5..c4967028 100644 --- a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Payments/PaymentAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Payments/PaymentAppServiceTests.cs @@ -27,7 +27,7 @@ namespace EasyAbp.EShop.Payments.Payments orderService.GetAsync(PaymentsTestData.Order1).Returns(Task.FromResult(new OrderDto { Id = PaymentsTestData.Order1, - Currency = "CNY", + Currency = "USD", ActualTotalPrice = 0, StoreId = PaymentsTestData.Store1, OrderLines = new List @@ -35,7 +35,7 @@ namespace EasyAbp.EShop.Payments.Payments new OrderLineDto { Id = PaymentsTestData.OrderLine1, - Currency = "CNY", + Currency = "USD", ActualTotalPrice = 0, Quantity = 1 } diff --git a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs index 7ab2f1a6..85000368 100644 --- a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Application.Tests/Refunds/RefundAppServiceTests.cs @@ -53,7 +53,7 @@ namespace EasyAbp.EShop.Payments.Refunds var payment = Activator.CreateInstance(paymentType, true) as Payment; payment.ShouldNotBeNull(); paymentType.GetProperty(nameof(Payment.Id))?.SetValue(payment, PaymentsTestData.Payment1); - paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "CNY"); + paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "USD"); paymentType.GetProperty(nameof(Payment.ActualPaymentAmount))?.SetValue(payment, 1m); paymentType.GetProperty(nameof(Payment.PaymentItems))?.SetValue(payment, new List {paymentItem}); @@ -83,7 +83,7 @@ namespace EasyAbp.EShop.Payments.Refunds var payment = Activator.CreateInstance(paymentType, true) as Payment; payment.ShouldNotBeNull(); paymentType.GetProperty(nameof(Payment.Id))?.SetValue(payment, PaymentsTestData.Payment1); - paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "CNY"); + paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "USD"); paymentType.GetProperty(nameof(Payment.ActualPaymentAmount))?.SetValue(payment, 1m); // pending refund amount paymentType.GetProperty(nameof(Payment.PendingRefundAmount))?.SetValue(payment, 1m); @@ -108,7 +108,7 @@ namespace EasyAbp.EShop.Payments.Refunds orderService.GetAsync(PaymentsTestData.Order1).Returns(Task.FromResult(new OrderDto { Id = PaymentsTestData.Order1, - Currency = "CNY", + Currency = "USD", ActualTotalPrice = 0, StoreId = PaymentsTestData.Store1, OrderLines = new List @@ -116,7 +116,7 @@ namespace EasyAbp.EShop.Payments.Refunds new() { Id = PaymentsTestData.OrderLine1, - Currency = "CNY", + Currency = "USD", ActualTotalPrice = 1m, Quantity = 1 } @@ -476,7 +476,7 @@ namespace EasyAbp.EShop.Payments.Refunds PaymentId = PaymentsTestData.Payment1, RefundPaymentMethod = null, ExternalTradingCode = "testcode", - Currency = "CNY", + Currency = "USD", RefundAmount = 1.5m, DisplayReason = "DisplayReason", CustomerRemark = "CustomerRemark", @@ -490,7 +490,7 @@ namespace EasyAbp.EShop.Payments.Refunds refundDto.PaymentId.ShouldBe(PaymentsTestData.Payment1); refundDto.ExternalTradingCode.ShouldBe("testcode"); - refundDto.Currency.ShouldBe("CNY"); + refundDto.Currency.ShouldBe("USD"); refundDto.RefundAmount.ShouldBe(1.5m); refundDto.DisplayReason.ShouldBe("DisplayReason"); refundDto.CustomerRemark.ShouldBe("CustomerRemark"); diff --git a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs index e1f7b468..6694ac75 100644 --- a/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs +++ b/modules/EasyAbp.EShop.Payments/test/EasyAbp.EShop.Payments.Domain.Tests/Refunds/RefundOrderEventHandlerTests.cs @@ -49,7 +49,7 @@ public class RefundOrderEventHandlerTests : PaymentsDomainTestBase var payment = Activator.CreateInstance(paymentType, true) as Payment; payment.ShouldNotBeNull(); paymentType.GetProperty(nameof(Payment.Id))?.SetValue(payment, PaymentsTestData.Payment1); - paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "CNY"); + paymentType.GetProperty(nameof(Payment.Currency))?.SetValue(payment, "USD"); paymentType.GetProperty(nameof(Payment.ActualPaymentAmount))?.SetValue(payment, 1m); paymentType.GetProperty(nameof(Payment.PaymentItems)) ?.SetValue(payment, new List { paymentItem }); diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs index 57cc997f..8d7a09cc 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/Products/ProductAppServiceTests.cs @@ -105,7 +105,7 @@ namespace EasyAbp.EShop.Products.Products var response = await _productAppService.CreateSkuAsync(productId, new CreateProductSkuDto { AttributeOptionIds = new List {productAttributeOptionId}, - Currency = "CNY", + Currency = "USD", Price = 1m, OrderMinQuantity = 1, OrderMaxQuantity = 10 @@ -117,7 +117,7 @@ namespace EasyAbp.EShop.Products.Products response.ProductSkus.Count.ShouldBe(1); var responseSku = response.ProductSkus.First(); - responseSku.Currency.ShouldBe("CNY"); + responseSku.Currency.ShouldBe("USD"); responseSku.Price.ShouldBe(1m); responseSku.AttributeOptionIds.Count.ShouldBe(1); responseSku.AttributeOptionIds.First().ShouldBe(productAttributeOptionId); @@ -202,7 +202,7 @@ namespace EasyAbp.EShop.Products.Products AttributeOptionIds = new List { ProductsTestData.Product1Attribute1Option4Id, ProductsTestData.Product1Attribute2Option1Id }, ProductDetailId = wrongProductDetailId, - Currency = "CNY", + Currency = "USD", Price = 10m, OrderMinQuantity = 1, OrderMaxQuantity = 10 diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs index 5ab449a8..1fe241aa 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs @@ -197,7 +197,7 @@ namespace EasyAbp.EShop.Products.Products private async Task CreateTestSkuAsync(IEnumerable attributeOptionIds) { return new ProductSku(Guid.NewGuid(), await AttributeOptionIdsSerializer.SerializeAsync(attributeOptionIds), - "test-sku", "CNY", null, 0m, 1, 10, null, null, null); + "test-sku", "USD", null, 0m, 1, 10, null, null, null); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs index 3962853d..ae88e96a 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs @@ -72,17 +72,17 @@ namespace EasyAbp.EShop.Products var productSku1 = new ProductSku(ProductsTestData.Product1Sku1Id, await _attributeOptionIdsSerializer.SerializeAsync(new[] { ProductsTestData.Product1Attribute1Option1Id, ProductsTestData.Product1Attribute2Option1Id }), - null, "CNY", null, 1m, 1, 10, null, null, null); + null, "USD", null, 1m, 1, 10, null, null, null); var productSku2 = new ProductSku(ProductsTestData.Product1Sku2Id, await _attributeOptionIdsSerializer.SerializeAsync(new[] { ProductsTestData.Product1Attribute1Option2Id, ProductsTestData.Product1Attribute2Option1Id }), - null, "CNY", null, 2m, 1, 10, null, null, null); + null, "USD", null, 2m, 1, 10, null, null, null); var productSku3 = new ProductSku(ProductsTestData.Product1Sku3Id, await _attributeOptionIdsSerializer.SerializeAsync(new[] { ProductsTestData.Product1Attribute1Option3Id, ProductsTestData.Product1Attribute2Option2Id }), - null, "CNY", null, 3m, 1, 10, null, null, null); + null, "USD", null, 3m, 1, 10, null, null, null); await _productManager.CreateSkuAsync(product, productSku1); await _productManager.CreateSkuAsync(product, productSku2); diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json index 8a479bc4..d05236b2 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json @@ -10,7 +10,7 @@ "OrderCake": "Order a cake", "CreateOrder": "Create order", "PayForOrder": "Pay for order", - "TopUp-1-CNY": "Top-up 1.00 CNY", + "TopUp-1-USD": "Top-up 1.00 USD", "OrderHistory": "Order history", "Flavor": "Flavor", "Size": "Size", diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json index da46a8de..7a10dedd 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json @@ -10,7 +10,7 @@ "OrderCake": "下单买蛋糕", "CreateOrder": "创建订单", "PayForOrder": "支付订单", - "TopUp-1-CNY": "充值 1.00 元", + "TopUp-1-USD": "充值 1.00 美元", "OrderHistory": "历史订单", "Flavor": "口味", "Size": "分量", diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json index 9b79a497..c2194d2f 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json @@ -10,7 +10,7 @@ "OrderCake": "下單買蛋糕", "CreateOrder": "創建訂單", "PayForOrder": "支付訂單", - "TopUp-1-CNY": "給賬戶充值", + "TopUp-1-USD": "充值 1.00 美元", "OrderHistory": "歷史訂單", "Flavor": "口味", "Size": "分量", diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs index 90bf1c27..03b7207a 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs @@ -156,22 +156,22 @@ public class SampleDataSeedContributor : IDataSeedContributor, ITransientDepende var productSku1 = new ProductSku(_guidGenerator.Create(), await _attributeOptionIdsSerializer.SerializeAsync(new[] { attribute1.ProductAttributeOptions[0].Id, attribute2.ProductAttributeOptions[0].Id }), - null, "CNY", null, 1m, 1, 10, null, null, null); + null, "USD", null, 1m, 1, 10, null, null, null); var productSku2 = new ProductSku(_guidGenerator.Create(), await _attributeOptionIdsSerializer.SerializeAsync(new[] { attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[0].Id }), - null, "CNY", null, 2m, 1, 10, null, null, null); + null, "USD", null, 2m, 1, 10, null, null, null); var productSku3 = new ProductSku(_guidGenerator.Create(), await _attributeOptionIdsSerializer.SerializeAsync(new[] { attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[1].Id }), - null, "CNY", null, 3m, 1, 10, null, null, null); + null, "USD", null, 3m, 1, 10, null, null, null); var productSku4 = new ProductSku(_guidGenerator.Create(), await _attributeOptionIdsSerializer.SerializeAsync(new[] { attribute1.ProductAttributeOptions[2].Id, attribute2.ProductAttributeOptions[1].Id }), - null, "CNY", null, 4m, 1, 10, null, null, null); + null, "USD", null, 4m, 1, 10, null, null, null); await _productManager.CreateSkuAsync(product, productSku1); await _productManager.CreateSkuAsync(product, productSku2); diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs index 4d401431..77db8a10 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Domain/EShopSampleDomainModule.cs @@ -78,7 +78,7 @@ namespace EShopSample { options.AccountGroups.Configure(accountGroup => { - accountGroup.Currency = "CNY"; + accountGroup.Currency = "USD"; }); }); } diff --git a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml index 40b4148a..e120a0b4 100644 --- a/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml +++ b/samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml @@ -97,7 +97,7 @@ let secondsToAutoCancel = @Model.GetSecondsToAutoCancel() @L["CancelOrder"] - @L["TopUp-1-CNY"] + @L["TopUp-1-USD"] @L["PayForOrder"] } From 29cf960a5ebe9fea205a54580907a331e1578516 Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Wed, 15 Jun 2022 00:41:51 +0800 Subject: [PATCH 06/11] Introduce CurrencyCode setting in the Orders module Resolve #22 --- .../EShop/Orders/Orders/NewOrderGenerator.cs | 33 ++++++---- .../EShop/Orders/Localization/Orders/en.json | 4 +- .../Orders/Localization/Orders/zh-Hans.json | 4 +- .../Orders/Localization/Orders/zh-Hant.json | 4 +- .../OrdersSettingDefinitionProvider.cs | 21 ++++++- .../EShop/Orders/Settings/OrdersSettings.cs | 2 + .../Orders/OrderAppServiceTests.cs | 61 ++++++++++++++++--- 7 files changed, 107 insertions(+), 22 deletions(-) diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs index 102695f6..2ab9a54c 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs @@ -3,14 +3,17 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Orders.Settings; using EasyAbp.EShop.Products.ProductDetails.Dtos; using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products.Dtos; using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectExtending; +using Volo.Abp.Settings; using Volo.Abp.Timing; namespace EasyAbp.EShop.Orders.Orders @@ -20,6 +23,7 @@ namespace EasyAbp.EShop.Orders.Orders private readonly IClock _clock; private readonly IGuidGenerator _guidGenerator; private readonly ICurrentTenant _currentTenant; + private readonly ISettingProvider _settingProvider; private readonly IServiceProvider _serviceProvider; private readonly IOrderNumberGenerator _orderNumberGenerator; private readonly IProductSkuDescriptionProvider _productSkuDescriptionProvider; @@ -29,6 +33,7 @@ namespace EasyAbp.EShop.Orders.Orders IClock clock, IGuidGenerator guidGenerator, ICurrentTenant currentTenant, + ISettingProvider settingProvider, IServiceProvider serviceProvider, IOrderNumberGenerator orderNumberGenerator, IProductSkuDescriptionProvider productSkuDescriptionProvider, @@ -37,6 +42,7 @@ namespace EasyAbp.EShop.Orders.Orders _clock = clock; _guidGenerator = guidGenerator; _currentTenant = currentTenant; + _settingProvider = settingProvider; _serviceProvider = serviceProvider; _orderNumberGenerator = orderNumberGenerator; _productSkuDescriptionProvider = productSkuDescriptionProvider; @@ -53,16 +59,17 @@ namespace EasyAbp.EShop.Orders.Orders orderLines.Add(await GenerateOrderLineAsync(input, inputOrderLine, productDict, productDetailDict)); } - var storeCurrency = await GetStoreCurrencyAsync(input.StoreId); + var effectiveCurrency = await GetEffectiveCurrencyAsync(); - if (orderLines.Any(x => x.Currency != storeCurrency)) + if (orderLines.Any(x => x.Currency != effectiveCurrency)) { - throw new UnexpectedCurrencyException(storeCurrency); + throw new UnexpectedCurrencyException(effectiveCurrency); } var productTotalPrice = orderLines.Select(x => x.TotalPrice).Sum(); - - var paymentExpireIn = orderLines.Select(x => productDict[x.ProductId].GetSkuPaymentExpireIn(x.ProductSkuId)).Min(); + + var paymentExpireIn = orderLines.Select(x => productDict[x.ProductId].GetSkuPaymentExpireIn(x.ProductSkuId)) + .Min(); var totalPrice = productTotalPrice; var totalDiscount = orderLines.Select(x => x.TotalDiscount).Sum(); @@ -72,7 +79,7 @@ namespace EasyAbp.EShop.Orders.Orders tenantId: _currentTenant.Id, storeId: input.StoreId, customerUserId: customerUserId, - currency: storeCurrency, + currency: effectiveCurrency, productTotalPrice: productTotalPrice, totalDiscount: totalDiscount, totalPrice: totalPrice, @@ -124,7 +131,7 @@ namespace EasyAbp.EShop.Orders.Orders } var unitPrice = await GetUnitPriceAsync(input, inputOrderLine, product, productSku); - + var totalPrice = unitPrice * inputOrderLine.Quantity; var orderLine = new OrderLine( @@ -148,7 +155,7 @@ namespace EasyAbp.EShop.Orders.Orders actualTotalPrice: totalPrice, quantity: inputOrderLine.Quantity ); - + inputOrderLine.MapExtraPropertiesTo(orderLine, MappingPropertyDefinitionChecks.Destination); return orderLine; @@ -171,10 +178,14 @@ namespace EasyAbp.EShop.Orders.Orders return productSku.Price; } - protected virtual Task GetStoreCurrencyAsync(Guid storeId) + protected virtual async Task GetEffectiveCurrencyAsync() { - // Todo: Get real store currency configuration. - return Task.FromResult("USD"); + var currencyCode = Check.NotNullOrWhiteSpace( + await _settingProvider.GetOrNullAsync(OrdersSettings.CurrencyCode), + nameof(OrdersSettings.CurrencyCode) + ); + + return currencyCode; } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json index 55b6b7c6..67aa9901 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/en.json @@ -53,6 +53,8 @@ "EasyAbp.EShop.Orders:InvalidRefundAmount": "The refund amount ({amount}) is invalid.", "EasyAbp.EShop.Orders:InvalidRefundQuantity": "The refund quantity ({quantity}) is invalid.", "EasyAbp.EShop.Orders:OrderIsInWrongStage": "The order {orderId} is in the wrong stage.", - "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "Exist unexpected flash-sales product" + "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "Exist unexpected flash-sales product", + "DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "Currency code", + "Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 code (see https://en.wikipedia.org/wiki/ISO_4217)" } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json index 515b4ce9..00245f2f 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hans.json @@ -53,6 +53,8 @@ "EasyAbp.EShop.Orders:InvalidRefundAmount": "退款金额({amount})无效", "EasyAbp.EShop.Orders:InvalidRefundQuantity": "退款数量({quantity})无效", "EasyAbp.EShop.Orders:OrderIsInWrongStage": "订单{orderId}处于错误的阶段", - "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清单中不允许存在闪购产品" + "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清单中不允许存在闪购产品", + "DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "货币代码", + "Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 货币代码 (详见 https://en.wikipedia.org/wiki/ISO_4217)" } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json index 63e4fc76..6529af18 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain.Shared/EasyAbp/EShop/Orders/Localization/Orders/zh-Hant.json @@ -53,6 +53,8 @@ "EasyAbp.EShop.Orders:InvalidRefundAmount": "退款金額({amount})無效", "EasyAbp.EShop.Orders:InvalidRefundQuantity": "退款數量({quantity})無效", "EasyAbp.EShop.Orders:OrderIsInWrongStage": "訂單{orderId}處於錯誤的階段", - "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清單中不允許存在閃購產品" + "EasyAbp.EShop.Orders:ExistFlashSalesProduct": "清單中不允許存在閃購產品", + "DisplayName:EasyAbp.EShop.Orders.CurrencyCode": "貨幣代碼", + "Description:EasyAbp.EShop.Orders.CurrencyCode": "ISO 4217 貨幣代碼 (詳見 https://en.wikipedia.org/wiki/ISO_4217)" } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettingDefinitionProvider.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettingDefinitionProvider.cs index 92d73790..3ae499ef 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettingDefinitionProvider.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettingDefinitionProvider.cs @@ -1,14 +1,33 @@ -using Volo.Abp.Settings; +using EasyAbp.EShop.Orders.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Settings; namespace EasyAbp.EShop.Orders.Settings { public class OrdersSettingDefinitionProvider : SettingDefinitionProvider { + public static string DefaultCurrency { get; set; } = "USD"; + public override void Define(ISettingDefinitionContext context) { /* Define module settings here. * Use names from OrdersSettings class. */ + + context.Add( + new SettingDefinition( + OrdersSettings.CurrencyCode, + DefaultCurrency, + L($"DisplayName:{OrdersSettings.CurrencyCode}"), + L($"Description:{OrdersSettings.CurrencyCode}"), + isVisibleToClients: true + ) + ); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettings.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettings.cs index a37c170e..f6646bb1 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettings.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp/EShop/Orders/Settings/OrdersSettings.cs @@ -7,5 +7,7 @@ /* Add constants for setting names. Example: * public const string MySettingName = GroupName + ".MySettingName"; */ + + public const string CurrencyCode = GroupName + ".CurrencyCode"; } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs index 2d507b7b..bc99825f 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/OrderAppServiceTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EasyAbp.EShop.Orders.Orders.Dtos; +using EasyAbp.EShop.Orders.Settings; using EasyAbp.EShop.Products.ProductDetails; using EasyAbp.EShop.Products.ProductDetails.Dtos; using EasyAbp.EShop.Products.Products; @@ -12,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Shouldly; using Volo.Abp; +using Volo.Abp.Settings; using Volo.Abp.Timing; using Xunit; @@ -532,17 +534,62 @@ namespace EasyAbp.EShop.Orders.Orders } }; - Product1.InventoryStrategy = InventoryStrategy.FlashSales; + try + { + Product1.InventoryStrategy = InventoryStrategy.FlashSales; - await WithUnitOfWorkAsync(async () => + await WithUnitOfWorkAsync(async () => + { + var exception = + await Should.ThrowAsync(() => _orderAppService.CreateAsync(createOrderDto)); + + exception.Code.ShouldBe(OrdersErrorCodes.ExistFlashSalesProduct); + }); + + Product1.InventoryStrategy = InventoryStrategy.NoNeed; + } + catch { - var exception = - await Should.ThrowAsync(() => _orderAppService.CreateAsync(createOrderDto)); + Product1.InventoryStrategy = InventoryStrategy.NoNeed; + throw; + } + } - exception.Code.ShouldBe(OrdersErrorCodes.ExistFlashSalesProduct); - }); + [Fact] + public async Task Should_Throw_If_Product_Sku_Uses_Unexpected_Currency() + { + var createOrderDto = new CreateOrderDto + { + StoreId = OrderTestData.Store1Id, + OrderLines = new List + { + new() + { + ProductId = OrderTestData.Product1Id, + ProductSkuId = OrderTestData.ProductSku1Id, + Quantity = 1 + } + } + }; + + try + { + OrdersSettingDefinitionProvider.DefaultCurrency = "CNY"; // The effective value is "USD" + await WithUnitOfWorkAsync(async () => + { + var exception = + await Should.ThrowAsync(() => _orderAppService.CreateAsync(createOrderDto)); - Product1.InventoryStrategy = InventoryStrategy.NoNeed; + exception.Code.ShouldBe(OrdersErrorCodes.UnexpectedCurrency); + }); + + OrdersSettingDefinitionProvider.DefaultCurrency = "USD"; + } + catch + { + OrdersSettingDefinitionProvider.DefaultCurrency = "USD"; + throw; + } } } } \ No newline at end of file From 71bdd1c78f21f5380ffa6728072798f09af9adaa Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Wed, 15 Jun 2022 00:48:26 +0800 Subject: [PATCH 07/11] Fix Should_Get_Orderly_ProductAttributes_And_ProductAttributeOptions #169 --- .../ProductsTestDataBuilder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs index 3962853d..a963b3ce 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestDataBuilder.cs @@ -47,21 +47,21 @@ namespace EasyAbp.EShop.Products var product = new Product(ProductsTestData.Product1Id, null, ProductsTestData.Store1Id, "Default", productDetail1.Id, "Cake", "Cake", InventoryStrategy.NoNeed, null, true, false, false, null, null, 0); - var attribute1 = new ProductAttribute(ProductsTestData.Product1Attribute1Id, "Size", null, 2); - var attribute2 = new ProductAttribute(ProductsTestData.Product1Attribute2Id, "Color", null, 1); + var attribute1 = new ProductAttribute(ProductsTestData.Product1Attribute1Id, "Size", null, 1); + var attribute2 = new ProductAttribute(ProductsTestData.Product1Attribute2Id, "Color", null, 2); attribute1.ProductAttributeOptions.AddRange(new[] { - new ProductAttributeOption(ProductsTestData.Product1Attribute1Option4Id, "XL", null, 1), - new ProductAttributeOption(ProductsTestData.Product1Attribute1Option2Id, "M", null, 3), - new ProductAttributeOption(ProductsTestData.Product1Attribute1Option1Id, "S", null, 4), - new ProductAttributeOption(ProductsTestData.Product1Attribute1Option3Id, "L", null, 2), + new ProductAttributeOption(ProductsTestData.Product1Attribute1Option4Id, "XL", null, 4), + new ProductAttributeOption(ProductsTestData.Product1Attribute1Option2Id, "M", null, 2), + new ProductAttributeOption(ProductsTestData.Product1Attribute1Option1Id, "S", null, 1), + new ProductAttributeOption(ProductsTestData.Product1Attribute1Option3Id, "L", null, 3), }); attribute2.ProductAttributeOptions.AddRange(new[] { - new ProductAttributeOption(ProductsTestData.Product1Attribute2Option2Id, "Green", null, 1), - new ProductAttributeOption(ProductsTestData.Product1Attribute2Option1Id, "Red", null, 2), + new ProductAttributeOption(ProductsTestData.Product1Attribute2Option2Id, "Green", null, 2), + new ProductAttributeOption(ProductsTestData.Product1Attribute2Option1Id, "Red", null, 1), }); product.ProductAttributes.Add(attribute2); From 37f25eb83c4a9b0eefcd2a25ba093afb8b15553e Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Wed, 15 Jun 2022 02:07:11 +0800 Subject: [PATCH 08/11] Product/ProductSku use ctor and Update method instead of ObjectMapper --- .../ProductDetails/ProductDetailAppService.cs | 2 +- .../Products/Products/ProductAppService.cs | 96 ++++++++++++++++--- .../ProductsApplicationAutoMapperProfile.cs | 13 --- .../EShop/Products/Products/Product.cs | 47 +++++++-- .../EShop/Products/Products/ProductSku.cs | 46 ++++++--- .../Products/ProductDomainTests.cs | 2 +- .../MultiStoreAbstractKeyCrudAppService.cs | 4 +- 7 files changed, 156 insertions(+), 54 deletions(-) diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs index f4f8604a..47266633 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductDetails/ProductDetailAppService.cs @@ -83,7 +83,7 @@ namespace EasyAbp.EShop.Products.ProductDetails ProductsPermissions.Products.CrossStore); } - MapToEntity(input, detail); + await MapToEntityAsync(input, detail); await Repository.UpdateAsync(detail, autoSave: true); diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs index 9cc63dd6..e5060506 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/Products/ProductAppService.cs @@ -70,16 +70,9 @@ namespace EasyAbp.EShop.Products.Products .ThenBy(x => x.Id); } - protected override Product MapToEntity(CreateUpdateProductDto createInput) - { - var product = base.MapToEntity(createInput); - - return product; - } - public override async Task CreateAsync(CreateUpdateProductDto input) { - var product = MapToEntity(input); + var product = await MapToEntityAsync(input); await CheckMultiStorePolicyAsync(product.StoreId, CreatePolicyName); @@ -117,7 +110,7 @@ namespace EasyAbp.EShop.Products.Products CheckProductIsNotStatic(product); - MapToEntity(input, product); + await MapToEntityAsync(input, product); await UpdateProductAttributesAsync(product, input); @@ -356,7 +349,7 @@ namespace EasyAbp.EShop.Products.Products UnitOfWorkManager.Current.OnCompleted(async () => { await ClearProductViewCacheAsync(product.StoreId); }); } - private static void CheckProductIsNotStatic(Product product) + protected virtual void CheckProductIsNotStatic(Product product) { if (product.IsStatic) { @@ -372,7 +365,7 @@ namespace EasyAbp.EShop.Products.Products CheckProductIsNotStatic(product); - var sku = ObjectMapper.Map(input); + var sku = await MapToProductSkuAsync(input); EntityHelper.TrySetId(sku, GuidGenerator.Create); @@ -388,7 +381,8 @@ namespace EasyAbp.EShop.Products.Products return dto; } - public async Task UpdateSkuAsync(Guid productId, Guid productSkuId, UpdateProductSkuDto input) + public virtual async Task UpdateSkuAsync(Guid productId, Guid productSkuId, + UpdateProductSkuDto input) { var product = await GetEntityByIdAsync(productId); @@ -398,7 +392,7 @@ namespace EasyAbp.EShop.Products.Products var sku = product.ProductSkus.Single(x => x.Id == productSkuId); - ObjectMapper.Map(input, sku); + await MapToProductSkuAsync(input, sku); await _productManager.UpdateSkuAsync(product, sku); @@ -412,7 +406,7 @@ namespace EasyAbp.EShop.Products.Products return dto; } - public async Task DeleteSkuAsync(Guid productId, Guid productSkuId) + public virtual async Task DeleteSkuAsync(Guid productId, Guid productSkuId) { var product = await GetEntityByIdAsync(productId); @@ -485,6 +479,80 @@ namespace EasyAbp.EShop.Products.Products return SortAttributesAndOptions(productDto); } + protected override Task MapToEntityAsync(CreateUpdateProductDto createInput) + { + return Task.FromResult(new Product( + GuidGenerator.Create(), + CurrentTenant.Id, + createInput.StoreId, + createInput.ProductGroupName, + createInput.ProductDetailId, + createInput.UniqueName, + createInput.DisplayName, + createInput.InventoryStrategy, + createInput.InventoryProviderName, + createInput.IsPublished, + false, + createInput.IsHidden, + createInput.PaymentExpireIn, + createInput.MediaResources, + createInput.DisplayOrder)); + } + + protected override Task MapToEntityAsync(CreateUpdateProductDto updateInput, Product entity) + { + entity.Update( + updateInput.StoreId, + updateInput.ProductGroupName, + updateInput.ProductDetailId, + updateInput.UniqueName, + updateInput.DisplayName, + updateInput.InventoryStrategy, + updateInput.InventoryProviderName, + updateInput.IsPublished, + false, + updateInput.IsHidden, + updateInput.PaymentExpireIn, + updateInput.MediaResources, + updateInput.DisplayOrder); + + return Task.CompletedTask; + } + + protected virtual async Task MapToProductSkuAsync(CreateProductSkuDto createInput) + { + return new ProductSku( + GuidGenerator.Create(), + await _attributeOptionIdsSerializer.SerializeAsync(createInput.AttributeOptionIds), + createInput.Name, + createInput.Currency, + createInput.OriginalPrice, + createInput.Price, + createInput.OrderMinQuantity, + createInput.OrderMaxQuantity, + createInput.PaymentExpireIn, + createInput.MediaResources, + createInput.ProductDetailId + ); + } + + protected virtual Task MapToProductSkuAsync(UpdateProductSkuDto updateInput, ProductSku entity) + { + entity.Update( + updateInput.Name, + updateInput.Currency, + updateInput.OriginalPrice, + updateInput.Price, + updateInput.OrderMinQuantity, + updateInput.OrderMaxQuantity, + updateInput.PaymentExpireIn, + updateInput.MediaResources, + updateInput.ProductDetailId + ); + + return Task.CompletedTask; + } + protected virtual ProductDto SortAttributesAndOptions(ProductDto productDto) { productDto.ProductAttributes = productDto.ProductAttributes.OrderBy(x => x.DisplayOrder).ToList(); diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs index bfefd578..f575692b 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Application/EasyAbp/EShop/Products/ProductsApplicationAutoMapperProfile.cs @@ -41,23 +41,10 @@ namespace EasyAbp.EShop.Products .Ignore(dto => dto.Sold) .AfterMap(async (src, dest) => dest.AttributeOptionIds = (await attributeOptionIdsSerializer.DeserializeAsync(src.SerializedAttributeOptionIds)).ToList()); - CreateMap(MemberList.Source) - .ForSourceMember(dto => dto.StoreId, opt => opt.DoNotValidate()) - .ForSourceMember(dto => dto.CategoryIds, opt => opt.DoNotValidate()) - .Ignore(p => p.ProductAttributes) - .Ignore(p => p.ProductSkus) - .AfterMap((src, dest) => dest.InitializeNullCollections()); CreateMap(MemberList.Source) .ForSourceMember(dto => dto.StoreId, opt => opt.DoNotValidate()); CreateMap(MemberList.Source); CreateMap(MemberList.Source); - CreateMap(MemberList.Source) - .ForSourceMember(dto => dto.AttributeOptionIds, opt => opt.DoNotValidate()) - .Ignore(entity => entity.SerializedAttributeOptionIds) - .AfterMap(async (src, dest) => - dest.SetSerializedAttributeOptionIds( - await attributeOptionIdsSerializer.SerializeAsync(src.AttributeOptionIds))); - CreateMap(MemberList.Source); CreateMap(); CreateMap(); CreateMap(); diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs index 1de5fec9..457c1a4a 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/Product.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using System; using System.Collections.Generic; using EasyAbp.EShop.Products.Options.ProductGroups; +using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; @@ -13,13 +14,16 @@ namespace EasyAbp.EShop.Products.Products public virtual Guid StoreId { get; protected set; } - [NotNull] public virtual string ProductGroupName { get; protected set; } + [NotNull] + public virtual string ProductGroupName { get; protected set; } public virtual Guid? ProductDetailId { get; protected set; } - [CanBeNull] public virtual string UniqueName { get; protected set; } + [CanBeNull] + public virtual string UniqueName { get; protected set; } - [NotNull] public virtual string DisplayName { get; protected set; } + [NotNull] + public virtual string DisplayName { get; protected set; } public virtual InventoryStrategy InventoryStrategy { get; protected set; } @@ -29,7 +33,8 @@ namespace EasyAbp.EShop.Products.Products /// public virtual string InventoryProviderName { get; protected set; } - [CanBeNull] public virtual string MediaResources { get; protected set; } + [CanBeNull] + public virtual string MediaResources { get; protected set; } public virtual int DisplayOrder { get; protected set; } @@ -69,10 +74,10 @@ namespace EasyAbp.EShop.Products.Products { TenantId = tenantId; StoreId = storeId; - ProductGroupName = productGroupName; + ProductGroupName = Check.NotNullOrWhiteSpace(productGroupName, nameof(productGroupName)); ProductDetailId = productDetailId; UniqueName = uniqueName?.Trim(); - DisplayName = displayName; + DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); InventoryStrategy = inventoryStrategy; InventoryProviderName = inventoryProviderName; IsPublished = isPublished; @@ -86,10 +91,34 @@ namespace EasyAbp.EShop.Products.Products ProductSkus = new List(); } - public void InitializeNullCollections() + public void Update( + Guid storeId, + [NotNull] string productGroupName, + Guid? productDetailId, + [CanBeNull] string uniqueName, + [NotNull] string displayName, + InventoryStrategy inventoryStrategy, + [CanBeNull] string inventoryProviderName, + bool isPublished, + bool isStatic, + bool isHidden, + TimeSpan? paymentExpireIn, + [CanBeNull] string mediaResources, + int displayOrder) { - ProductAttributes ??= new List(); - ProductSkus ??= new List(); + StoreId = storeId; + ProductGroupName = Check.NotNullOrWhiteSpace(productGroupName, nameof(productGroupName)); + ProductDetailId = productDetailId; + UniqueName = uniqueName?.Trim(); + DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); + InventoryStrategy = inventoryStrategy; + InventoryProviderName = inventoryProviderName; + IsPublished = isPublished; + IsStatic = isStatic; + IsHidden = isHidden; + PaymentExpireIn = paymentExpireIn; + MediaResources = mediaResources; + DisplayOrder = displayOrder; } public void TrimUniqueName() diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs index c8ab79a9..9f83221d 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; using System.Text.Json.Serialization; using JetBrains.Annotations; +using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; @@ -11,28 +11,28 @@ namespace EasyAbp.EShop.Products.Products { [NotNull] public virtual string SerializedAttributeOptionIds { get; protected set; } - + [CanBeNull] public virtual string Name { get; protected set; } - + [NotNull] public virtual string Currency { get; protected set; } - + public virtual decimal? OriginalPrice { get; protected set; } - + public virtual decimal Price { get; protected set; } public virtual int OrderMinQuantity { get; protected set; } - + public virtual int OrderMaxQuantity { get; protected set; } - + public virtual TimeSpan? PaymentExpireIn { get; protected set; } [CanBeNull] public virtual string MediaResources { get; protected set; } public virtual Guid? ProductDetailId { get; protected set; } - + [JsonInclude] public virtual ExtraPropertyDictionary ExtraProperties { get; protected set; } @@ -41,7 +41,7 @@ namespace EasyAbp.EShop.Products.Products ExtraProperties = new ExtraPropertyDictionary(); this.SetDefaultsForExtraProperties(); } - + public ProductSku( Guid id, [NotNull] string serializedAttributeOptionIds, @@ -55,9 +55,10 @@ namespace EasyAbp.EShop.Products.Products [CanBeNull] string mediaResources, Guid? productDetailId) : base(id) { - SerializedAttributeOptionIds = serializedAttributeOptionIds; + SerializedAttributeOptionIds = + Check.NotNullOrWhiteSpace(serializedAttributeOptionIds, nameof(serializedAttributeOptionIds)); Name = name?.Trim(); - Currency = currency; + Currency = Check.NotNullOrWhiteSpace(currency, nameof(currency)); OriginalPrice = originalPrice; Price = price; OrderMinQuantity = orderMinQuantity; @@ -65,7 +66,7 @@ namespace EasyAbp.EShop.Products.Products PaymentExpireIn = paymentExpireIn; MediaResources = mediaResources; ProductDetailId = productDetailId; - + ExtraProperties = new ExtraPropertyDictionary(); this.SetDefaultsForExtraProperties(); } @@ -75,9 +76,26 @@ namespace EasyAbp.EShop.Products.Products Name = Name?.Trim(); } - public void SetSerializedAttributeOptionIds(string serializedAttributeOptionIds) + public void Update( + [CanBeNull] string name, + [NotNull] string currency, + decimal? originalPrice, + decimal price, + int orderMinQuantity, + int orderMaxQuantity, + TimeSpan? paymentExpireIn, + [CanBeNull] string mediaResources, + Guid? productDetailId) { - SerializedAttributeOptionIds = serializedAttributeOptionIds; + Name = name?.Trim(); + Currency = Check.NotNullOrWhiteSpace(currency, nameof(currency)); + OriginalPrice = originalPrice; + Price = price; + OrderMinQuantity = orderMinQuantity; + OrderMaxQuantity = orderMaxQuantity; + PaymentExpireIn = paymentExpireIn; + MediaResources = mediaResources; + ProductDetailId = productDetailId; } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs index 1fe241aa..0d9aaae6 100644 --- a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/ProductDomainTests.cs @@ -186,7 +186,7 @@ namespace EasyAbp.EShop.Products.Products await ProductManager.CreateAsync(product2); - var fakeSku = new ProductSku(Guid.NewGuid(), "", null, "", null, 0m, 1, 1, null, null, null); + var fakeSku = new ProductSku(Guid.NewGuid(), "[]", null, "USD", null, 0m, 1, 1, null, null, null); var inventoryDataModel = await ProductManager.GetInventoryDataAsync(product2, fakeSku); diff --git a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs index a34c202c..0995f97e 100644 --- a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs +++ b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs @@ -82,7 +82,7 @@ namespace EasyAbp.EShop.Stores.Stores { await CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName); - var entity = MapToEntity(input); + var entity = await MapToEntityAsync(input); TryToSetTenantId(entity); @@ -96,7 +96,7 @@ namespace EasyAbp.EShop.Stores.Stores var entity = await GetEntityByIdAsync(id); await CheckMultiStorePolicyAsync(entity.StoreId, UpdatePolicyName); - MapToEntity(input, entity); + await MapToEntityAsync(input, entity); await Repository.UpdateAsync(entity, autoSave: true); return await MapToGetOutputDtoAsync(entity); From c1a9134a6ca76dbe389d8d2013dae8296f6e5fea Mon Sep 17 00:00:00 2001 From: gdlcf88 Date: Wed, 15 Jun 2022 02:16:07 +0800 Subject: [PATCH 09/11] Use NodaMoney to format money value --- Directory.Build.props | 1 + EShop.sln.DotSettings | 1 + .../Orders/Orders/IOrderExtraFeeProvider.cs | 4 +- .../Orders/Orders/IOrderLinePriceOverrider.cs | 5 +- .../EShop/Orders/Orders/NewOrderGenerator.cs | 52 ++++++++++--------- .../EasyAbp.EShop.Orders.Domain.csproj | 1 + .../Orders/TestOrderLinePriceOverrider.cs | 7 +-- .../EasyAbp.EShop.Products.Domain.csproj | 1 + .../EShop/Products/Products/ProductSku.cs | 19 ++++--- .../Products/CurrencyTests.cs | 30 +++++++++++ 10 files changed, 85 insertions(+), 36 deletions(-) create mode 100644 modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/CurrencyTests.cs diff --git a/Directory.Build.props b/Directory.Build.props index 98938073..f91d2491 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,6 +7,7 @@ 0.8.2 1.7.0 3.6.2 + 1.0.5 \ No newline at end of file diff --git a/EShop.sln.DotSettings b/EShop.sln.DotSettings index 7b7d4176..e8263a9e 100644 --- a/EShop.sln.DotSettings +++ b/EShop.sln.DotSettings @@ -22,4 +22,5 @@ SQL True True + True True \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs index 0b19dd1d..9cd5ba9d 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs @@ -3,11 +3,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using EasyAbp.EShop.Orders.Orders.Dtos; using EasyAbp.EShop.Products.Products.Dtos; +using NodaMoney; namespace EasyAbp.EShop.Orders.Orders { public interface IOrderExtraFeeProvider { - Task> GetListAsync(Guid customerUserId, CreateOrderDto input, Dictionary productDict); + Task> GetListAsync(Guid customerUserId, CreateOrderDto input, + Dictionary productDict, Currency effectiveCurrency); } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs index c86f5463..a1440d63 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; using EasyAbp.EShop.Orders.Orders.Dtos; using EasyAbp.EShop.Products.Products.Dtos; +using NodaMoney; namespace EasyAbp.EShop.Orders.Orders; public interface IOrderLinePriceOverrider { - Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product, - ProductSkuDto productSku); + Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product, + ProductSkuDto productSku, Currency effectiveCurrency); } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs index 2ab9a54c..495930c8 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs @@ -8,6 +8,7 @@ using EasyAbp.EShop.Products.ProductDetails.Dtos; using EasyAbp.EShop.Products.Products; using EasyAbp.EShop.Products.Products.Dtos; using Microsoft.Extensions.DependencyInjection; +using NodaMoney; using Volo.Abp; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; @@ -52,18 +53,14 @@ namespace EasyAbp.EShop.Orders.Orders public virtual async Task GenerateAsync(Guid customerUserId, CreateOrderDto input, Dictionary productDict, Dictionary productDetailDict) { + var effectiveCurrency = await GetEffectiveCurrencyAsync(); + var orderLines = new List(); foreach (var inputOrderLine in input.OrderLines) { - orderLines.Add(await GenerateOrderLineAsync(input, inputOrderLine, productDict, productDetailDict)); - } - - var effectiveCurrency = await GetEffectiveCurrencyAsync(); - - if (orderLines.Any(x => x.Currency != effectiveCurrency)) - { - throw new UnexpectedCurrencyException(effectiveCurrency); + orderLines.Add(await GenerateOrderLineAsync( + input, inputOrderLine, productDict, productDetailDict, effectiveCurrency)); } var productTotalPrice = orderLines.Select(x => x.TotalPrice).Sum(); @@ -79,7 +76,7 @@ namespace EasyAbp.EShop.Orders.Orders tenantId: _currentTenant.Id, storeId: input.StoreId, customerUserId: customerUserId, - currency: effectiveCurrency, + currency: effectiveCurrency.Code, productTotalPrice: productTotalPrice, totalDiscount: totalDiscount, totalPrice: totalPrice, @@ -90,7 +87,7 @@ namespace EasyAbp.EShop.Orders.Orders input.MapExtraPropertiesTo(order, MappingPropertyDefinitionChecks.Destination); - await AddOrderExtraFeesAsync(order, customerUserId, input, productDict); + await AddOrderExtraFeesAsync(order, customerUserId, input, productDict, effectiveCurrency); order.SetOrderLines(orderLines); @@ -100,27 +97,33 @@ namespace EasyAbp.EShop.Orders.Orders } protected virtual async Task AddOrderExtraFeesAsync(Order order, Guid customerUserId, - CreateOrderDto input, Dictionary productDict) + CreateOrderDto input, Dictionary productDict, Currency effectiveCurrency) { var providers = _serviceProvider.GetServices(); foreach (var provider in providers) { - var infoModels = await provider.GetListAsync(customerUserId, input, productDict); + var infoModels = await provider.GetListAsync(customerUserId, input, productDict, effectiveCurrency); foreach (var infoModel in infoModels) { - order.AddOrderExtraFee(infoModel.Fee, infoModel.Name, infoModel.Key); + var fee = new Money(infoModel.Fee, effectiveCurrency); + order.AddOrderExtraFee(fee.Amount, infoModel.Name, infoModel.Key); } } } protected virtual async Task GenerateOrderLineAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, Dictionary productDict, - Dictionary productDetailDict) + Dictionary productDetailDict, Currency effectiveCurrency) { var product = productDict[inputOrderLine.ProductId]; var productSku = product.GetSkuById(inputOrderLine.ProductSkuId); + + if (productSku.Currency != effectiveCurrency.Code) + { + throw new UnexpectedCurrencyException(effectiveCurrency.Code); + } var productDetailId = productSku.ProductDetailId ?? product.ProductDetailId; var productDetail = productDetailId.HasValue ? productDetailDict[productDetailId.Value] : null; @@ -130,7 +133,7 @@ namespace EasyAbp.EShop.Orders.Orders throw new OrderLineInvalidQuantityException(product.Id, productSku.Id, inputOrderLine.Quantity); } - var unitPrice = await GetUnitPriceAsync(input, inputOrderLine, product, productSku); + var unitPrice = await GetUnitPriceAsync(input, inputOrderLine, product, productSku, effectiveCurrency); var totalPrice = unitPrice * inputOrderLine.Quantity; @@ -149,10 +152,10 @@ namespace EasyAbp.EShop.Orders.Orders skuDescription: await _productSkuDescriptionProvider.GenerateAsync(product, productSku), mediaResources: product.MediaResources, currency: productSku.Currency, - unitPrice: unitPrice, - totalPrice: totalPrice, + unitPrice: unitPrice.Amount, + totalPrice: totalPrice.Amount, totalDiscount: 0, - actualTotalPrice: totalPrice, + actualTotalPrice: totalPrice.Amount, quantity: inputOrderLine.Quantity ); @@ -161,13 +164,14 @@ namespace EasyAbp.EShop.Orders.Orders return orderLine; } - protected virtual async Task GetUnitPriceAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, - ProductDto product, ProductSkuDto productSku) + protected virtual async Task GetUnitPriceAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, + ProductDto product, ProductSkuDto productSku, Currency effectiveCurrency) { foreach (var overrider in _orderLinePriceOverriders) { var overridenUnitPrice = - await overrider.GetUnitPriceOrNullAsync(input, inputOrderLine, product, productSku); + await overrider.GetUnitPriceOrNullAsync(input, inputOrderLine, product, productSku, + effectiveCurrency); if (overridenUnitPrice is not null) { @@ -175,17 +179,17 @@ namespace EasyAbp.EShop.Orders.Orders } } - return productSku.Price; + return new Money(productSku.Price, effectiveCurrency); } - protected virtual async Task GetEffectiveCurrencyAsync() + protected virtual async Task GetEffectiveCurrencyAsync() { var currencyCode = Check.NotNullOrWhiteSpace( await _settingProvider.GetOrNullAsync(OrdersSettings.CurrencyCode), nameof(OrdersSettings.CurrencyCode) ); - return currencyCode; + return Currency.FromCode(currencyCode); } } } \ No newline at end of file diff --git a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj index 3bfe3085..4689b724 100644 --- a/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj +++ b/modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs index 5d580aec..10d61c50 100644 --- a/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs +++ b/modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using EasyAbp.EShop.Orders.Orders.Dtos; using EasyAbp.EShop.Products.Products.Dtos; +using NodaMoney; using Volo.Abp.DependencyInjection; namespace EasyAbp.EShop.Orders.Orders; @@ -9,12 +10,12 @@ public class TestOrderLinePriceOverrider : IOrderLinePriceOverrider, ITransientD { public static decimal Sku3UnitPrice { get; set; } = 100m; - public async Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, - ProductDto product, ProductSkuDto productSku) + public async Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, + ProductDto product, ProductSkuDto productSku, Currency effectiveCurrency) { if (inputOrderLine.ProductSkuId == OrderTestData.ProductSku3Id) { - return Sku3UnitPrice; + return new Money(Sku3UnitPrice, effectiveCurrency); } return null; diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj index 627d3721..bac32da5 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs index 9f83221d..efe06138 100644 --- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs +++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using JetBrains.Annotations; +using NodaMoney; using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; @@ -55,12 +56,15 @@ namespace EasyAbp.EShop.Products.Products [CanBeNull] string mediaResources, Guid? productDetailId) : base(id) { + Check.NotNullOrWhiteSpace(currency, nameof(currency)); + var nodaCurrency = NodaMoney.Currency.FromCode(currency); + SerializedAttributeOptionIds = Check.NotNullOrWhiteSpace(serializedAttributeOptionIds, nameof(serializedAttributeOptionIds)); Name = name?.Trim(); - Currency = Check.NotNullOrWhiteSpace(currency, nameof(currency)); - OriginalPrice = originalPrice; - Price = price; + Currency = nodaCurrency.Code; + OriginalPrice = originalPrice.HasValue ? new Money(originalPrice.Value, nodaCurrency).Amount : null; + Price = new Money(price, nodaCurrency).Amount; OrderMinQuantity = orderMinQuantity; OrderMaxQuantity = orderMaxQuantity; PaymentExpireIn = paymentExpireIn; @@ -87,10 +91,13 @@ namespace EasyAbp.EShop.Products.Products [CanBeNull] string mediaResources, Guid? productDetailId) { + Check.NotNullOrWhiteSpace(currency, nameof(currency)); + var nodaCurrency = NodaMoney.Currency.FromCode(currency); + Name = name?.Trim(); - Currency = Check.NotNullOrWhiteSpace(currency, nameof(currency)); - OriginalPrice = originalPrice; - Price = price; + Currency = nodaCurrency.Code; + OriginalPrice = originalPrice.HasValue ? new Money(originalPrice.Value, nodaCurrency).Amount : null; + Price = new Money(price, nodaCurrency).Amount; OrderMinQuantity = orderMinQuantity; OrderMaxQuantity = orderMaxQuantity; PaymentExpireIn = paymentExpireIn; diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/CurrencyTests.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/CurrencyTests.cs new file mode 100644 index 00000000..f6b4422f --- /dev/null +++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/CurrencyTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using NodaMoney; +using Shouldly; +using Xunit; + +namespace EasyAbp.EShop.Products.Products; + +public class CurrencyTests : ProductsDomainTestBase +{ + [Fact] + public Task Should_Rounding_If_Amount_Is_Out_Of_Decimals() + { + var money = new Money(1.115m, Currency.FromCode("CNY")); + + money.Currency.ShouldBe(Currency.FromCode("CNY")); + money.Amount.ShouldBe(1.12m); + + return Task.CompletedTask; + } + [Fact] + public Task Should_Throw_If_Currency_Is_Undefined() + { + var exception = Should.Throw(() => new Money(1.115m, Currency.FromCode("BTC"))); + + exception.Message.ShouldBe("BTC is an unknown currency code!"); + + return Task.CompletedTask; + } +} \ No newline at end of file From 194b0ddc00e318e4aea3f77769b88717b829fe96 Mon Sep 17 00:00:00 2001 From: Super Date: Wed, 15 Jun 2022 14:09:03 +0800 Subject: [PATCH 10/11] Update README.md --- docs/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/README.md b/docs/README.md index 80de91a0..6e71f004 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,10 @@ An abp application module group that provides basic e-shop service. We have launched an online demo for this module: [https://eshop.samples.easyabp.io](https://eshop.samples.easyabp.io) +![4c53f67d7e1bdaef0449baf4e5b6840](https://user-images.githubusercontent.com/30018771/173754387-2b15a480-9de2-4f87-9366-f98e3c0a909c.png) + +> You can also clone this repo and run the [demo project](https://github.com/EasyAbp/EShop/tree/dev/samples/EShopSample/aspnet-core/src/EShopSample.Web) locally. + ## Installation 1. Follow [the document](https://github.com/EasyAbp/PaymentService#installation) to install the dependent PaymentService module. From f26c5ce3eca20827c563fbd71c6d2bc1c308c205 Mon Sep 17 00:00:00 2001 From: Super Date: Wed, 15 Jun 2022 14:19:58 +0800 Subject: [PATCH 11/11] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 6e71f004..826d5836 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ An abp application module group that provides basic e-shop service. We have launched an online demo for this module: [https://eshop.samples.easyabp.io](https://eshop.samples.easyabp.io) -![4c53f67d7e1bdaef0449baf4e5b6840](https://user-images.githubusercontent.com/30018771/173754387-2b15a480-9de2-4f87-9366-f98e3c0a909c.png) +![image](https://user-images.githubusercontent.com/30018771/173756329-fe16a753-dddf-4b97-a5e5-a2f4c9d9983f.png) > You can also clone this repo and run the [demo project](https://github.com/EasyAbp/EShop/tree/dev/samples/EShopSample/aspnet-core/src/EShopSample.Web) locally.