diff --git a/Directory.Build.props b/Directory.Build.props
index 989380733..f91d24918 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 7b7d41762..e8263a9e0 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 0b19dd1de..9cd5ba9da 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.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 c86f54634..a1440d637 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 2ab9a54c4..495930c8d 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.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 @@ public NewOrderGenerator(
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 @@ public virtual async Task GenerateAsync(Guid customerUserId, CreateOrderD
tenantId: _currentTenant.Id,
storeId: input.StoreId,
customerUserId: customerUserId,
- currency: effectiveCurrency,
+ currency: effectiveCurrency.Code,
productTotalPrice: productTotalPrice,
totalDiscount: totalDiscount,
totalPrice: totalPrice,
@@ -90,7 +87,7 @@ public virtual async Task GenerateAsync(Guid customerUserId, CreateOrderD
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 @@ public virtual async Task GenerateAsync(Guid customerUserId, CreateOrderD
}
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 @@ protected virtual async Task GenerateOrderLineAsync(CreateOrderDto in
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 @@ protected virtual async Task GenerateOrderLineAsync(CreateOrderDto in
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 @@ protected virtual async Task GenerateOrderLineAsync(CreateOrderDto in
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 @@ protected virtual async Task GetUnitPriceAsync(CreateOrderDto input, Cr
}
}
- 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 3bfe30854..4689b724a 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 5d580aece..10d61c50e 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 627d37210..bac32da5c 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 9f83221db..efe06138e 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 @@ public ProductSku(
[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 @@ public void Update(
[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 000000000..f6b4422f1
--- /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