diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md
index 59f5e7b..ef7a2c9 100644
--- a/docs/IMPLEMENTATION_PLAN.md
+++ b/docs/IMPLEMENTATION_PLAN.md
@@ -1717,7 +1717,7 @@ public enum CashBookEntryType { Income = 0, Expense = 1 }
| ✓ | K16 | Application | Excel Export (ClosedXML) | 3 |
| ✓ | K17 | Application | PDF Export (QuestPDF) | 1 |
| ✓ | K18 | Web | Export Controller | 1 |
-| ☐ | K19 | Web | Membership Fees Feature | 2 |
+| ✓ | K19 | Web | Membership Fees Feature | 2 |
| ☐ | K20 | Web | Club Settings Extension | 3 |
| ☐ | K21 | Web | Navigation Integration | 1 |
| ☐ | K22 | Tests | Unit Tests | 2 |
diff --git a/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor b/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor
index 27716ef..a3f0191 100644
--- a/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor
+++ b/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor
@@ -69,6 +69,10 @@
Filtern
+
+ Mitgliedsbeiträge
+
Neue Buchung
diff --git a/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor.cs b/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor.cs
index bc8d7cb..089e3ac 100644
--- a/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor.cs
+++ b/src/Koogle.Web/Components/Pages/CashBook/CashBook.razor.cs
@@ -114,4 +114,27 @@ public partial class CashBook
Snackbar.Add("Buchung wird gelöscht...", Severity.Info);
}
}
+
+ private async Task OpenMembershipFeeDialog()
+ {
+ // Check if there are existing membership fees for current month
+ var hasExisting = CashBookState.Value.Entries
+ .Any(e => e.CategoryName == "Mitgliedsbeitrag"
+ && e.BookingDate.Month == DateTime.Today.Month
+ && e.BookingDate.Year == DateTime.Today.Year);
+
+ var parameters = new DialogParameters
+ {
+ { "HasExistingFees", hasExisting }
+ };
+
+ var dialog = await DialogService.ShowAsync("Mitgliedsbeiträge erfassen", parameters);
+ var result = await dialog.Result;
+
+ if (result != null && !result.Canceled && result.Data is CreateMembershipFeesDto dto)
+ {
+ Dispatcher.Dispatch(new CreateMembershipFeesAction(dto));
+ Snackbar.Add("Mitgliedsbeiträge werden erstellt...", Severity.Info);
+ }
+ }
}
diff --git a/src/Koogle.Web/Components/Pages/CashBook/MembershipFeeDialog.razor b/src/Koogle.Web/Components/Pages/CashBook/MembershipFeeDialog.razor
new file mode 100644
index 0000000..73698b6
--- /dev/null
+++ b/src/Koogle.Web/Components/Pages/CashBook/MembershipFeeDialog.razor
@@ -0,0 +1,110 @@
+@using Koogle.Application.DTOs
+
+
+
+ Mitgliedsbeiträge erfassen
+
+
+
+ Erstellt automatisch Einnahme-Buchungen für alle aktiven Mitglieder.
+
+
+
+
+ @for (int m = 1; m <= 12; m++)
+ {
+ @GetMonthName(m)
+ }
+
+
+ @for (int y = DateTime.Today.Year; y >= DateTime.Today.Year - 2; y--)
+ {
+ @y
+ }
+
+
+
+
+
+ @if (_useOverride)
+ {
+
+ }
+ else
+ {
+
+ Es wird der in den Club-Einstellungen hinterlegte Standardbeitrag verwendet.
+
+ }
+
+ @if (_showWarning)
+ {
+
+ Für @GetMonthName(_selectedMonth) @_selectedYear wurden bereits Mitgliedsbeiträge erfasst.
+ Trotzdem fortfahren?
+
+ }
+
+
+ Abbrechen
+
+ Beiträge erstellen
+
+
+
+
+@code {
+ [CascadingParameter]
+ private IMudDialogInstance? MudDialog { get; set; }
+
+ [Parameter]
+ public bool HasExistingFees { get; set; }
+
+ private int _selectedMonth = DateTime.Today.Month;
+ private int _selectedYear = DateTime.Today.Year;
+ private bool _useOverride;
+ private decimal _overrideAmount = 10.00m;
+ private bool _showWarning;
+
+ protected override void OnParametersSet()
+ {
+ _showWarning = HasExistingFees;
+ }
+
+ private static string GetMonthName(int month) => month switch
+ {
+ 1 => "Januar",
+ 2 => "Februar",
+ 3 => "März",
+ 4 => "April",
+ 5 => "Mai",
+ 6 => "Juni",
+ 7 => "Juli",
+ 8 => "August",
+ 9 => "September",
+ 10 => "Oktober",
+ 11 => "November",
+ 12 => "Dezember",
+ _ => month.ToString()
+ };
+
+ private void Cancel() => MudDialog?.Cancel();
+
+ private void Submit()
+ {
+ var dto = new CreateMembershipFeesDto
+ {
+ Year = _selectedYear,
+ Month = _selectedMonth,
+ OverrideAmount = _useOverride ? _overrideAmount : null
+ };
+ MudDialog?.Close(DialogResult.Ok(dto));
+ }
+}