refactor(app): implement year filter for account entries and update related services

This commit is contained in:
2026-04-03 14:11:42 +02:00
parent 08185f88cd
commit 4636acf7b0
11 changed files with 196 additions and 42 deletions

View File

@@ -39,8 +39,8 @@
<i class="bi bi-file-earmark-pdf"></i> PDF
</button>
|
<button class="btn btn-nav @(showCurrentYearOnly ? "btn-primary" : "btn-outline-secondary")" @onclick="ToggleYearFilter">
<i class="bi bi-funnel"></i> @(showCurrentYearOnly ? $"Nur {DateTime.Now.Year}" : "Alle Buchungen")
<button class="btn btn-nav @(selectedYear.HasValue ? "btn-primary" : "btn-outline-secondary")" @onclick="OpenYearFilterDialog">
<i class="bi bi-funnel"></i> Filter<br>@GetFilterLabel()
</button>
</div>
@@ -56,21 +56,21 @@
</div>
<div class="text-end">
<small class="text-muted">Einnahmen</small>
<div class="amount-positive">@FormatAmount(showCurrentYearOnly ? balance.CurrentYearIncome : balance.TotalIncome)</div>
<div class="amount-positive">@FormatAmount(selectedYear.HasValue ? balance.CurrentYearIncome : balance.TotalIncome)</div>
</div>
<div class="text-end">
<small class="text-muted">Ausgaben</small>
<div class="amount-negative">@FormatAmount(showCurrentYearOnly ? balance.CurrentYearExpense : balance.TotalExpense)</div>
<div class="amount-negative">@FormatAmount(selectedYear.HasValue ? balance.CurrentYearExpense : balance.TotalExpense)</div>
</div>
</div>
</div>
@if (showCurrentYearOnly)
@if (selectedYear.HasValue)
{
<div class="summary-section">
<div class="summary-flex">
<div>
<small class="text-muted">Übertrag von @(DateTime.Now.Year - 1)</small>
<small class="text-muted">Übertrag von @(selectedYear.Value - 1)</small>
<div class="d-flex align-items-center gap-1">
<span class="fw-bold">@FormatAmount(balance.CarryoverBalance)</span>
<button class="btn-edit-pen" @onclick="() => showEditCarryover = true" title="Übertrag bearbeiten">
@@ -79,7 +79,7 @@
</div>
</div>
<div class="text-end">
<small class="text-muted">Umsätze @DateTime.Now.Year</small>
<small class="text-muted">Umsätze @selectedYear.Value</small>
<div class="fw-bold @(balance.CurrentYearIncome - balance.CurrentYearExpense >= 0 ? "amount-positive" : "amount-negative")">
@FormatAmount(balance.CurrentYearIncome - balance.CurrentYearExpense)
</div>
@@ -179,3 +179,29 @@
OnCancel="() => editingTransferEntry = null" />
}
@if (showYearFilterDialog)
{
<div class="dialog-backdrop" @onclick="CloseYearFilterDialog">
<div class="dialog-content" @onclick:stopPropagation="true">
<h5>Filter auswählen</h5>
<p class="text-muted mb-3">Wähle ein Jahr oder alle Buchungen.</p>
<div class="filter-options-grid">
<button class="btn @(selectedYear.HasValue ? "btn-outline-secondary" : "btn-primary")" @onclick="() => SelectFilterYear(null)">
Alle Buchungen
</button>
@foreach (var year in availableYears)
{
<button class="btn @(selectedYear == year ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SelectFilterYear(year)">
@year
</button>
}
</div>
<div class="d-flex justify-content-end mt-3">
<button class="btn btn-outline-secondary" @onclick="CloseYearFilterDialog">
<i class="bi bi-x-lg"></i> Schließen
</button>
</div>
</div>
</div>
}

View File

@@ -21,7 +21,9 @@ public partial class AccountDetail
private bool showAddTransfer;
private bool showEditName;
private bool showEditCarryover;
private bool showCurrentYearOnly = true;
private bool showYearFilterDialog;
private int? selectedYear = DateTime.Now.Year;
private List<int> availableYears = [];
private int? confirmDeleteEntryId;
private string? confirmDeleteEntryTitle;
@@ -56,14 +58,26 @@ public partial class AccountDetail
private async Task LoadAll()
{
account = await AccountService.GetAccountAsync(AccountId);
balance = await BalanceQueryService.GetAccountBalanceAsync(AccountId);
entries = await EntryService.GetEntriesAsync(AccountId, showCurrentYearOnly);
balance = await BalanceQueryService.GetAccountBalanceAsync(AccountId, selectedYear);
entries = await EntryService.GetEntriesAsync(AccountId, selectedYear);
availableYears = await EntryService.GetEntryYearsAsync(AccountId);
}
private async Task ToggleYearFilter()
private void OpenYearFilterDialog()
{
showCurrentYearOnly = !showCurrentYearOnly;
entries = await EntryService.GetEntriesAsync(AccountId, showCurrentYearOnly);
showYearFilterDialog = true;
}
private void CloseYearFilterDialog()
{
showYearFilterDialog = false;
}
private async Task SelectFilterYear(int? year)
{
selectedYear = year;
showYearFilterDialog = false;
await LoadAll();
}
#endregion
@@ -79,7 +93,7 @@ public partial class AccountDetail
private async Task HandleSaveCarryover(decimal newAmount)
{
await AccountService.UpdateCarryoverAsync(AccountId, newAmount);
await AccountService.UpdateCarryoverAsync(AccountId, newAmount, selectedYear);
showEditCarryover = false;
await LoadAll();
}
@@ -164,8 +178,8 @@ public partial class AccountDetail
private async Task HandleExport()
{
var pdf = await PdfStatementService.GenerateStatementAsync(AccountId, showCurrentYearOnly);
var suffix = showCurrentYearOnly ? $"_{DateTime.Now.Year}" : "_Gesamt";
var pdf = await PdfStatementService.GenerateStatementAsync(AccountId, selectedYear);
var suffix = selectedYear.HasValue ? $"_{selectedYear.Value}" : "_Gesamt";
savedPdfPath = await FileSaveService.SaveFileAsync(pdf, $"{account?.Name}{suffix}.pdf");
}
@@ -190,5 +204,7 @@ public partial class AccountDetail
private static string FormatAmount(decimal amount) => $"{amount:N2} €";
private string GetFilterLabel() => selectedYear?.ToString() ?? "Alle";
#endregion
}

View File

@@ -9,5 +9,6 @@ public interface IAccountService
Task<AccountSummaryDto> CreateAccountAsync(string name);
Task RenameAccountAsync(int accountId, string newName);
Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance);
Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance, int? year);
Task DeleteAccountAsync(int accountId);
}

View File

@@ -4,5 +4,5 @@ namespace Duempelkas.App.Services;
public interface IBalanceQueryService
{
Task<AccountBalanceDto> GetAccountBalanceAsync(int accountId);
Task<AccountBalanceDto> GetAccountBalanceAsync(int accountId, int? year = null);
}

View File

@@ -5,7 +5,9 @@ namespace Duempelkas.App.Services;
public interface IEntryService
{
Task<List<EntryDto>> GetEntriesAsync(int accountId, int? year);
Task<List<EntryDto>> GetEntriesAsync(int accountId, bool currentYearOnly);
Task<List<int>> GetEntryYearsAsync(int accountId);
Task<EntryDto> CreateEntryAsync(int accountId, EntryType type, DateTime date, string title, decimal amount);
Task CreateTransferAsync(int sourceAccountId, int targetAccountId, DateTime date, string title, decimal amount);
Task DeleteEntryAsync(int entryId);

View File

@@ -2,6 +2,7 @@ namespace Duempelkas.App.Services;
public interface IPdfStatementService
{
Task<byte[]> GenerateStatementAsync(int accountId, int? year);
Task<byte[]> GenerateStatementAsync(int accountId, bool currentYearOnly);
Task<byte[]> GenerateDashboardStatementAsync();
}