refactor(app): implement year filtering for accounts and enhance dashboard PDF generation

This commit is contained in:
2026-04-03 14:24:46 +02:00
parent 4636acf7b0
commit 68c7a1ca6a
10 changed files with 208 additions and 34 deletions
@@ -146,7 +146,12 @@ public class PdfStatementService : IPdfStatementService
return document.GeneratePdf();
}
public async Task<byte[]> GenerateDashboardStatementAsync()
public Task<byte[]> GenerateDashboardStatementAsync()
{
return GenerateDashboardStatementAsync(null);
}
public async Task<byte[]> GenerateDashboardStatementAsync(int? year)
{
await using var db = await _dbFactory.CreateDbContextAsync();
@@ -169,25 +174,65 @@ public class PdfStatementService : IPdfStatementService
transferByEntryId[transfer.TargetEntryId] = transfer;
}
var entries = await db.Entries
var entriesQuery = db.Entries
.Include(e => e.Account)
.Where(e => !e.IsDeleted)
.Where(e => !e.IsDeleted);
if (year.HasValue)
{
entriesQuery = entriesQuery.Where(e => e.Date.Year == year.Value);
}
var entries = await entriesQuery
.OrderBy(e => e.Date)
.ThenBy(e => e.CreatedUtc)
.ToListAsync();
var movementByAccountId = entries
.GroupBy(e => e.AccountId)
.ToDictionary(
g => g.Key,
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
Dictionary<int, decimal> balanceByAccountId;
Dictionary<int, decimal>? carryoverByAccountId = null;
var balanceByAccountId = accounts
.ToDictionary(
a => a.Id,
a => a.CarryoverBalance + movementByAccountId.GetValueOrDefault(a.Id));
if (year.HasValue)
{
var movementBeforeYearByAccountId = await db.Entries
.Where(e => !e.IsDeleted && e.Date.Year < year.Value)
.GroupBy(e => e.AccountId)
.ToDictionaryAsync(
g => g.Key,
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
var totalClubBalance = accounts.Sum(a => a.CarryoverBalance + movementByAccountId.GetValueOrDefault(a.Id));
var movementInYearByAccountId = entries
.GroupBy(e => e.AccountId)
.ToDictionary(
g => g.Key,
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
carryoverByAccountId = accounts
.ToDictionary(
a => a.Id,
a => a.CarryoverBalance + movementBeforeYearByAccountId.GetValueOrDefault(a.Id));
balanceByAccountId = accounts
.ToDictionary(
a => a.Id,
a => a.CarryoverBalance
+ movementBeforeYearByAccountId.GetValueOrDefault(a.Id)
+ movementInYearByAccountId.GetValueOrDefault(a.Id));
}
else
{
var movementByAccountId = entries
.GroupBy(e => e.AccountId)
.ToDictionary(
g => g.Key,
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
balanceByAccountId = accounts
.ToDictionary(
a => a.Id,
a => a.CarryoverBalance + movementByAccountId.GetValueOrDefault(a.Id));
}
var totalClubBalance = balanceByAccountId.Values.Sum();
var processedTransferIds = new HashSet<int>();
var rows = new List<DashboardStatementRow>();
@@ -248,7 +293,10 @@ public class PdfStatementService : IPdfStatementService
page.Header().Column(col =>
{
col.Item().Text(clubName).Bold().FontSize(18);
col.Item().Text("Übersicht aller Konten").FontSize(13).FontColor(Colors.Grey.Darken1);
var headerTitle = year.HasValue
? $"Übersicht aller Konten ({year.Value})"
: "Übersicht aller Konten";
col.Item().Text(headerTitle).FontSize(13).FontColor(Colors.Grey.Darken1);
col.Item().PaddingTop(2).Text($"Gesamtvermögen: {FormatCurrency(totalClubBalance)}")
.FontSize(10)
.SemiBold()
@@ -285,6 +333,18 @@ public class PdfStatementService : IPdfStatementService
}
});
if (year.HasValue && carryoverByAccountId is not null)
{
BodyCell(table, string.Empty);
BodyCell(table, string.Empty);
BodyCell(table, $"Übertrag von {year.Value - 1}", alignRight: true);
foreach (var account in accounts)
{
BodyAmountCell(table, carryoverByAccountId.GetValueOrDefault(account.Id));
}
}
foreach (var row in rows)
{
BodyCell(table, row.DisplayId);