Browse Source

Use NodaMoney to format money value

pull/180/head
gdlcf88 4 years ago
parent
commit
c1a9134a6c
  1. 1
      Directory.Build.props
  2. 1
      EShop.sln.DotSettings
  3. 4
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderExtraFeeProvider.cs
  4. 5
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/IOrderLinePriceOverrider.cs
  5. 52
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Application/EasyAbp/EShop/Orders/Orders/NewOrderGenerator.cs
  6. 1
      modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj
  7. 7
      modules/EasyAbp.EShop.Orders/test/EasyAbp.EShop.Orders.Application.Tests/Orders/TestOrderLinePriceOverrider.cs
  8. 1
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj
  9. 19
      modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/Products/ProductSku.cs
  10. 30
      modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/Products/CurrencyTests.cs

1
Directory.Build.props

@ -7,6 +7,7 @@
<EasyAbpAbpTagHelperPlusModuleVersion>0.8.2</EasyAbpAbpTagHelperPlusModuleVersion>
<DaprSdkVersion>1.7.0</DaprSdkVersion>
<OrleansVersion>3.6.2</OrleansVersion>
<NodaMoneyVersion>1.0.5</NodaMoneyVersion>
</PropertyGroup>
</Project>

1
EShop.sln.DotSettings

@ -22,4 +22,5 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Authorizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dapr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=noda/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Skus/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

4
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<List<OrderExtraFeeInfoModel>> GetListAsync(Guid customerUserId, CreateOrderDto input, Dictionary<Guid, ProductDto> productDict);
Task<List<OrderExtraFeeInfoModel>> GetListAsync(Guid customerUserId, CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict, Currency effectiveCurrency);
}
}

5
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<decimal?> GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product,
ProductSkuDto productSku);
Task<Money?> GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine, ProductDto product,
ProductSkuDto productSku, Currency effectiveCurrency);
}

52
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<Order> GenerateAsync(Guid customerUserId, CreateOrderDto input,
Dictionary<Guid, ProductDto> productDict, Dictionary<Guid, ProductDetailDto> productDetailDict)
{
var effectiveCurrency = await GetEffectiveCurrencyAsync();
var orderLines = new List<OrderLine>();
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<Guid, ProductDto> productDict)
CreateOrderDto input, Dictionary<Guid, ProductDto> productDict, Currency effectiveCurrency)
{
var providers = _serviceProvider.GetServices<IOrderExtraFeeProvider>();
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<OrderLine> GenerateOrderLineAsync(CreateOrderDto input,
CreateOrderLineDto inputOrderLine, Dictionary<Guid, ProductDto> productDict,
Dictionary<Guid, ProductDetailDto> productDetailDict)
Dictionary<Guid, ProductDetailDto> 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<decimal> GetUnitPriceAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine,
ProductDto product, ProductSkuDto productSku)
protected virtual async Task<Money> 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<string> GetEffectiveCurrencyAsync()
protected virtual async Task<Currency> GetEffectiveCurrencyAsync()
{
var currencyCode = Check.NotNullOrWhiteSpace(
await _settingProvider.GetOrNullAsync(OrdersSettings.CurrencyCode),
nameof(OrdersSettings.CurrencyCode)
);
return currencyCode;
return Currency.FromCode(currencyCode);
}
}
}

1
modules/EasyAbp.EShop.Orders/src/EasyAbp.EShop.Orders.Domain/EasyAbp.EShop.Orders.Domain.csproj

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NodaMoney" Version="$(NodaMoneyVersion)" />
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(AbpVersion)" />

7
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<decimal?> GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine,
ProductDto product, ProductSkuDto productSku)
public async Task<Money?> 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;

1
modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp.EShop.Products.Domain.csproj

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NodaMoney" Version="$(NodaMoneyVersion)" />
<PackageReference Include="EasyAbp.Abp.Trees.Domain" Version="$(EasyAbpAbpTreesModuleVersion)" />
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />

19
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;

30
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<ArgumentException>(() => new Money(1.115m, Currency.FromCode("BTC")));
exception.Message.ShouldBe("BTC is an unknown currency code!");
return Task.CompletedTask;
}
}
Loading…
Cancel
Save