Skip to content

Commit

Permalink
Merge pull request #180 from EasyAbp/noda-money
Browse files Browse the repository at this point in the history
Use NodaMoney to format money value
  • Loading branch information
gdlcf88 authored Jun 15, 2022
2 parents 94c2159 + c1a9134 commit bdca9b0
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 36 deletions.
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -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 change: 1 addition & 0 deletions EShop.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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<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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -52,18 +53,14 @@ public NewOrderGenerator(
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();
Expand All @@ -79,7 +76,7 @@ public virtual async Task<Order> GenerateAsync(Guid customerUserId, CreateOrderD
tenantId: _currentTenant.Id,
storeId: input.StoreId,
customerUserId: customerUserId,
currency: effectiveCurrency,
currency: effectiveCurrency.Code,
productTotalPrice: productTotalPrice,
totalDiscount: totalDiscount,
totalPrice: totalPrice,
Expand All @@ -90,7 +87,7 @@ public virtual async Task<Order> 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);

Expand All @@ -100,27 +97,33 @@ public virtual async Task<Order> GenerateAsync(Guid customerUserId, CreateOrderD
}

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;
Expand All @@ -130,7 +133,7 @@ protected virtual async Task<OrderLine> 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;

Expand All @@ -149,10 +152,10 @@ protected virtual async Task<OrderLine> 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
);

Expand All @@ -161,31 +164,32 @@ protected virtual async Task<OrderLine> GenerateOrderLineAsync(CreateOrderDto in
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)
{
return overridenUnitPrice.Value;
}
}

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}

0 comments on commit bdca9b0

Please sign in to comment.