ChartService summed raw transaction Value instead of converting via GetValue(account.IsFxAccount), so FX expense accounts charted in their foreign currency labeled as CHF. Subclass series also took their Name from an arbitrary account in the group instead of the actual subclass name.
109 lines
4.3 KiB
C#
109 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Schaad.Accounting.Datasets;
|
|
using Schaad.Accounting.Datasets.Charts;
|
|
using Schaad.Accounting.Interfaces;
|
|
|
|
namespace Schaad.Accounting.Services
|
|
{
|
|
public class ChartService : IChartService
|
|
{
|
|
private readonly ISettingsService settingsService;
|
|
private readonly IViewService viewService;
|
|
|
|
public ChartService(ISettingsService settingsService, IViewService viewService)
|
|
{
|
|
this.settingsService = settingsService;
|
|
this.viewService = viewService;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<DataSerie>> GetExpensesPerMonthAsync()
|
|
{
|
|
var expenseTransactions = (await viewService.GetTransactionViewListAsync())
|
|
.Where(t => t.TargetAccount.Class == ClassIds.Expenses)
|
|
.ToList();
|
|
|
|
var distinctSubClasses = expenseTransactions
|
|
.Select(t => t.TargetAccount.SubClass)
|
|
.Distinct()
|
|
.Count();
|
|
|
|
return distinctSubClasses > 1
|
|
? await GetSubClassExpensesPerMonthAsync(expenseTransactions)
|
|
: await GetAccountExpensesPerMonthAsync();
|
|
}
|
|
|
|
private async Task<List<DataSerie>> GetSubClassExpensesPerMonthAsync(List<TransactionDataset> 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<DataSerie>();
|
|
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.GetValue(t.TargetAccount.IsFxAccount)));
|
|
EnsureEntryForEveryMonth(groupedByMonth, maxMonth);
|
|
|
|
list.Add(new DataSerie(
|
|
Id: grp.Key.ToString(),
|
|
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()
|
|
));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private async Task<List<DataSerie>> GetAccountExpensesPerMonthAsync()
|
|
{
|
|
var year = settingsService.GetYear();
|
|
var allTransactions = await viewService.GetTransactionViewListAsync();
|
|
var expenseAccounts = (await viewService.GetAccountViewListAsync()).Where(a => a.Class == ClassIds.Expenses);
|
|
|
|
var list = new List<DataSerie>();
|
|
foreach (var account in expenseAccounts)
|
|
{
|
|
var transactions = allTransactions.Where(t => t.TargetAccountId == account.Id).ToList();
|
|
if (transactions.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
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.GetValue(account.IsFxAccount)));
|
|
EnsureEntryForEveryMonth(groupedByMonth, maxMonth);
|
|
|
|
list.Add(new DataSerie(
|
|
Id: account.Id,
|
|
Name: account.Name,
|
|
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()
|
|
));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static void EnsureEntryForEveryMonth(Dictionary<int, decimal> values, int maxMonth = 12)
|
|
{
|
|
for (int i = 1; i <= maxMonth; i++)
|
|
{
|
|
if (values.ContainsKey(i) == false)
|
|
{
|
|
values.Add(i, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|