diff --git a/Schaad.Accounting.Services/ChartService.cs b/Schaad.Accounting.Services/ChartService.cs index ecf51e9..6a72fb0 100644 --- a/Schaad.Accounting.Services/ChartService.cs +++ b/Schaad.Accounting.Services/ChartService.cs @@ -31,28 +31,32 @@ namespace Schaad.Accounting.Services .Count(); return distinctSubClasses > 1 - ? GetSubClassExpensesPerMonth(expenseTransactions) + ? await GetSubClassExpensesPerMonthAsync(expenseTransactions) : await GetAccountExpensesPerMonthAsync(); } - private List GetSubClassExpensesPerMonth(List expenseTransactions) + private async Task> GetSubClassExpensesPerMonthAsync(List expenseTransactions) { var year = settingsService.GetYear(); var maxMonth = expenseTransactions .OrderByDescending(t => t.ValueDate) .FirstOrDefault()?.ValueDate.Month ?? 12; + var subClassNameByNumber = (await viewService.GetAccountViewListAsync()) + .GroupBy(a => a.SubClass) + .ToDictionary(g => g.Key, g => g.First().SubClassName); + var list = new List(); foreach (var grp in expenseTransactions.GroupBy(t => t.TargetAccount.SubClass)) { var groupedByMonth = grp .GroupBy(t => t.ValueDate.Month) - .ToDictionary(g => g.Key, g => g.Sum(t => t.Value)); + .ToDictionary(g => g.Key, g => g.Sum(t => t.GetValue(t.TargetAccount.IsFxAccount))); EnsureEntryForEveryMonth(groupedByMonth, maxMonth); list.Add(new DataSerie( Id: grp.Key.ToString(), - Name: grp.First().TargetAccount.Name, + Name: subClassNameByNumber[grp.Key], X: groupedByMonth.OrderBy(g => g.Key).Select(g => new DateOnly(year, g.Key, 1)).ToList(), Y: groupedByMonth.OrderBy(g => g.Key).Select(g => g.Value).ToList() )); @@ -78,7 +82,7 @@ namespace Schaad.Accounting.Services var maxMonth = transactions.Max(t => t.ValueDate.Month); var groupedByMonth = transactions .GroupBy(t => t.ValueDate.Month) - .ToDictionary(g => g.Key, g => g.Sum(t => t.Value)); + .ToDictionary(g => g.Key, g => g.Sum(t => t.GetValue(account.IsFxAccount))); EnsureEntryForEveryMonth(groupedByMonth, maxMonth); list.Add(new DataSerie( diff --git a/Schaad.Accounting.Tests/ChartServiceTestShould.cs b/Schaad.Accounting.Tests/ChartServiceTestShould.cs index 1f1023f..decedb4 100644 --- a/Schaad.Accounting.Tests/ChartServiceTestShould.cs +++ b/Schaad.Accounting.Tests/ChartServiceTestShould.cs @@ -1,3 +1,4 @@ +using System.Linq; using NSubstitute; using Schaad.Accounting.Datasets; using Schaad.Accounting.Interfaces; @@ -70,7 +71,7 @@ public class ChartServiceTestShould var checking = MakeAccount("chk", 1010); viewService.GetAccountViewListAsync().Returns(new List { - AsDataset(groceries), AsDataset(utilities), AsDataset(checking) + AsDataset(groceries, subClassName: "Groceries"), AsDataset(utilities, subClassName: "Utilities"), AsDataset(checking) }); viewService.GetTransactionViewListAsync().Returns(new List { @@ -81,8 +82,8 @@ public class ChartServiceTestShould var series = await BuildService().GetExpensesPerMonthAsync(); series.Count.ShouldBe(2); - series.ShouldContain(s => s.Id == "42"); - series.ShouldContain(s => s.Id == "43"); + series.ShouldContain(s => s.Id == "42" && s.Name == "Groceries"); + series.ShouldContain(s => s.Id == "43" && s.Name == "Utilities"); } [Fact] @@ -106,18 +107,18 @@ public class ChartServiceTestShould series[0].Id.ShouldBe("groc"); } - private static Account MakeAccount(string id, int number) => new() + private static Account MakeAccount(string id, int number, string currency = "CHF") => new() { Id = id, Number = number, Name = id, - Currency = "CHF" + Currency = currency }; - private static AccountDataset AsDataset(Account a) => - new(a, balance: 0, balanceCHF: 0, startBalanceCHF: 0, className: "", subClassName: ""); + private static AccountDataset AsDataset(Account a, string subClassName = "") => + new(a, balance: 0, balanceCHF: 0, startBalanceCHF: 0, className: "", subClassName: subClassName); - private static TransactionDataset MakeTransaction(Account origin, Account target, decimal value, DateTime valueDate) => + private static TransactionDataset MakeTransaction(Account origin, Account target, decimal value, DateTime valueDate, decimal? fxRate = null) => new( new Transaction { @@ -125,8 +126,46 @@ public class ChartServiceTestShould OriginAccountId = origin.Id, TargetAccountId = target.Id, Value = value, + FxRate = fxRate, ValueDate = valueDate, BookingDate = valueDate }, origin, target); + + [Fact] + public async Task ConvertFxAccountValuesWhenBuildingAccountSeries() + { + var usSubscription = MakeAccount("us", 4000, currency: "USD"); + var checking = MakeAccount("chk", 1010); + viewService.GetAccountViewListAsync().Returns(new List { AsDataset(usSubscription), AsDataset(checking) }); + viewService.GetTransactionViewListAsync().Returns(new List + { + MakeTransaction(checking, usSubscription, value: 100m, valueDate: new DateTime(2026, 1, 1), fxRate: 0.5m) + }); + + var series = await BuildService().GetExpensesPerMonthAsync(); + + series.Single().Y.Single().ShouldBe(200m); + } + + [Fact] + public async Task ConvertFxAccountValuesWhenBuildingSubClassSeries() + { + var usSubscription = MakeAccount("us", 4200, currency: "USD"); + var utilities = MakeAccount("util", 4300); + var checking = MakeAccount("chk", 1010); + viewService.GetAccountViewListAsync().Returns(new List + { + AsDataset(usSubscription), AsDataset(utilities), AsDataset(checking) + }); + viewService.GetTransactionViewListAsync().Returns(new List + { + MakeTransaction(checking, usSubscription, value: 100m, valueDate: new DateTime(2026, 1, 1), fxRate: 0.5m), + MakeTransaction(checking, utilities, value: 30m, valueDate: new DateTime(2026, 2, 1)) + }); + + var series = await BuildService().GetExpensesPerMonthAsync(); + + series.Single(s => s.Id == "42").Y[0].ShouldBe(200m); + } }