added cashbook planning
This commit is contained in:
parent
4cee96408b
commit
4dae965c98
|
|
@ -1591,3 +1591,642 @@ DeathBox ist ein Eliminationsspiel nach dem Hangman-Prinzip. Spieler sammeln Str
|
|||
## Zusammenfassung Phase 4
|
||||
|
||||
**2 Phasen (J1-J2)** → **4 Dateien** → **DeathBox spielbar**
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
# IMPLEMENTIERUNGSPLAN - Phase 5: Kassenbuch (Cash Book)
|
||||
|
||||
## Übersicht
|
||||
|
||||
Kassenbuch-Modul für Vereinsfinanzverwaltung. Trackt Einnahmen/Ausgaben, integriert mit Day-Close für Strafen-Buchungen, unterstützt Mitgliedsbeiträge und bietet Berichte mit Excel/PDF-Export.
|
||||
|
||||
---
|
||||
|
||||
## Geklärte Anforderungen
|
||||
|
||||
| Aspekt | Entscheidung |
|
||||
|--------|--------------|
|
||||
| Rolle | Neue Rolle "Kassenwart" (separate von Editor/Admin) |
|
||||
| Penalty-Buchung | Eine Buchung pro Person/Tag (aggregierte Summe) |
|
||||
| Kontostand | Eröffnungssaldo in Club-Settings speichern |
|
||||
| Export | ClosedXML (Excel) + QuestPDF (PDF) |
|
||||
| Kategorien | Entity mit IsSystemCategory-Flag |
|
||||
| Mitgliedsbeiträge | Monat wählbar, Warnung bei Duplikaten |
|
||||
| PersonExpenses | Automatisch Done bei CashBookEntry-Erstellung |
|
||||
| Korrekturbuchung | Eine Kategorie für Einnahme + Ausgabe |
|
||||
|
||||
---
|
||||
|
||||
## Neue Entities
|
||||
|
||||
### BookingCategory Entity
|
||||
|
||||
```csharp
|
||||
// src/Koogle.Domain/Entities/BookingCategory.cs
|
||||
public class BookingCategory : BaseEntity
|
||||
{
|
||||
public Guid ClubId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public BookingCategoryType CategoryType { get; set; } // Income/Expense
|
||||
public bool IsSystemCategory { get; set; } // Nicht löschbar
|
||||
public string? Color { get; set; } // Hex-Farbcode
|
||||
public string? Icon { get; set; } // MudBlazor Icon-Name
|
||||
public bool IsActive { get; set; } = true; // Soft-Deaktivierung
|
||||
|
||||
public Club Club { get; set; } = null!;
|
||||
public ICollection<CashBookEntry> CashBookEntries { get; set; } = [];
|
||||
}
|
||||
```
|
||||
|
||||
### CashBookEntry Entity
|
||||
|
||||
```csharp
|
||||
// src/Koogle.Domain/Entities/CashBookEntry.cs
|
||||
public class CashBookEntry : BaseEntity
|
||||
{
|
||||
public Guid ClubId { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public CashBookEntryType EntryType { get; set; } // Income/Expense
|
||||
public decimal Amount { get; set; } // Immer positiv
|
||||
public DateTime BookingDate { get; set; } // Frei wählbar
|
||||
public string? Comment { get; set; }
|
||||
public string? ReceiptReference { get; set; } // Belegverweis
|
||||
public Guid? DayId { get; set; } // Link zu Spieltag (Strafen)
|
||||
public Guid? PersonId { get; set; } // Link zu Person
|
||||
|
||||
public Club Club { get; set; } = null!;
|
||||
public BookingCategory Category { get; set; } = null!;
|
||||
public Day? Day { get; set; }
|
||||
public Person? Person { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### Club Entity Erweiterung
|
||||
|
||||
```csharp
|
||||
// Neue Properties in Club.cs
|
||||
public decimal InitialBalance { get; set; } // Eröffnungssaldo
|
||||
public decimal MonthlyMembershipFee { get; set; } // Monatsbeitrag
|
||||
```
|
||||
|
||||
### Neue Enums
|
||||
|
||||
```csharp
|
||||
// src/Koogle.Domain/Enums/BookingCategoryType.cs
|
||||
public enum BookingCategoryType { Income = 0, Expense = 1 }
|
||||
|
||||
// src/Koogle.Domain/Enums/CashBookEntryType.cs
|
||||
public enum CashBookEntryType { Income = 0, Expense = 1 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System-Kategorien (pro Club geseeded)
|
||||
|
||||
| Name | Typ | Farbe | Beschreibung |
|
||||
|------|-----|-------|--------------|
|
||||
| Spielstrafe | Income | Green | Strafen aus Day-Close |
|
||||
| Mitgliedsbeitrag | Income | Blue | Monatliche Beiträge |
|
||||
| Korrekturbuchung | Both | Orange | Manuelle Korrekturen |
|
||||
| Saldoanpassung | Both | Gray | Kontoabgleich |
|
||||
|
||||
---
|
||||
|
||||
## Umsetzungsreihenfolge Phase 5
|
||||
|
||||
| ☐ | Phase | Bereich | Beschreibung | Dateien |
|
||||
|---|-------|---------|--------------|---------|
|
||||
| ☐ | K1 | Domain | Entities + Enums | 5 |
|
||||
| ☐ | K2 | Infrastructure | EF Configurations | 4 |
|
||||
| ☐ | K3 | Infrastructure | Migration | - |
|
||||
| ☐ | K4 | Security | Kassenwart Role + Policy | 4 |
|
||||
| ☐ | K5 | Infrastructure | Repositories | 5 |
|
||||
| ☐ | K6 | Application | DTOs | 3 |
|
||||
| ☐ | K7 | Application | Service Interfaces | 2 |
|
||||
| ☐ | K8 | Application | Service Implementations | 4 |
|
||||
| ☐ | K9 | Application | Category Seeder | 1 |
|
||||
| ☐ | K10 | Application | Day Close Integration | 1 |
|
||||
| ☐ | K11 | Web | Fluxor CategoryState | 4 |
|
||||
| ☐ | K12 | Web | Fluxor CashBookState | 4 |
|
||||
| ☐ | K13 | Web | CashBook UI | 3 |
|
||||
| ☐ | K14 | Web | Categories UI | 2 |
|
||||
| ☐ | K15 | Web | Reports UI | 2 |
|
||||
| ☐ | K16 | Application | Excel Export (ClosedXML) | 3 |
|
||||
| ☐ | K17 | Application | PDF Export (QuestPDF) | 1 |
|
||||
| ☐ | K18 | Web | Export Controller | 1 |
|
||||
| ☐ | K19 | Web | Membership Fees Feature | 2 |
|
||||
| ☐ | K20 | Web | Club Settings Extension | 3 |
|
||||
| ☐ | K21 | Web | Navigation Integration | 1 |
|
||||
| ☐ | K22 | Tests | Unit Tests | 2 |
|
||||
| ☐ | K23 | Tests | Integration Tests | 1 |
|
||||
|
||||
**Legende:** ☐ = Offen | ☑ = In Arbeit | ✓ = Fertig
|
||||
|
||||
---
|
||||
|
||||
## Detaillierte Phasen
|
||||
|
||||
### **Phase K1: Domain Layer**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Domain/Enums/BookingCategoryType.cs` (neu)
|
||||
2. `src/Koogle.Domain/Enums/CashBookEntryType.cs` (neu)
|
||||
3. `src/Koogle.Domain/Entities/BookingCategory.cs` (neu)
|
||||
4. `src/Koogle.Domain/Entities/CashBookEntry.cs` (neu)
|
||||
5. `src/Koogle.Domain/Entities/Club.cs` (erweitern)
|
||||
|
||||
---
|
||||
|
||||
### **Phase K2: EF Configurations**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Infrastructure/Data/Configurations/BookingCategoryConfiguration.cs` (neu)
|
||||
2. `src/Koogle.Infrastructure/Data/Configurations/CashBookEntryConfiguration.cs` (neu)
|
||||
3. `src/Koogle.Infrastructure/Data/Configurations/ClubConfiguration.cs` (erweitern)
|
||||
4. `src/Koogle.Infrastructure/Data/AppDbContext.cs` (erweitern)
|
||||
|
||||
**BookingCategoryConfiguration:**
|
||||
```csharp
|
||||
builder.Property(x => x.Name).HasMaxLength(100).IsRequired();
|
||||
builder.Property(x => x.Color).HasMaxLength(7); // #RRGGBB
|
||||
builder.Property(x => x.Icon).HasMaxLength(50);
|
||||
builder.HasQueryFilter(x => !x.IsDeleted);
|
||||
builder.HasIndex(x => new { x.ClubId, x.Name }).HasFilter("[IsDeleted] = 0").IsUnique();
|
||||
```
|
||||
|
||||
**CashBookEntryConfiguration:**
|
||||
```csharp
|
||||
builder.Property(x => x.Amount).HasPrecision(10, 2).IsRequired();
|
||||
builder.Property(x => x.Comment).HasMaxLength(500);
|
||||
builder.Property(x => x.ReceiptReference).HasMaxLength(100);
|
||||
builder.HasOne(x => x.Category).WithMany(c => c.CashBookEntries).HasForeignKey(x => x.CategoryId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(x => x.Day).WithMany().HasForeignKey(x => x.DayId).OnDelete(DeleteBehavior.SetNull);
|
||||
builder.HasOne(x => x.Person).WithMany().HasForeignKey(x => x.PersonId).OnDelete(DeleteBehavior.SetNull);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K3: Migration**
|
||||
|
||||
```bash
|
||||
dotnet ef migrations add AddKassenbuch --project src/Koogle.Infrastructure --startup-project src/Koogle.Web --context AppDbContext --output-dir Data/Migrations
|
||||
dotnet ef database update -p src/Koogle.Infrastructure -s src/Koogle.Web --context AppDbContext
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K4: Kassenwart Role**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Domain/Enums/UserRole.cs` (erweitern)
|
||||
2. `src/Koogle.Infrastructure/Security/IdentityRoleSeeder.cs` (erweitern)
|
||||
3. `src/Koogle.Infrastructure/Security/ClubRoleRequirement.cs` (erweitern)
|
||||
4. `src/Koogle.Infrastructure/DependencyInjection.cs` (erweitern)
|
||||
|
||||
**UserRole.cs:**
|
||||
```csharp
|
||||
public const string Treasurer = "Kassenwart";
|
||||
```
|
||||
|
||||
**ClubRoleRequirement.cs - Rank-Update:**
|
||||
```csharp
|
||||
static int Rank(string role) => role switch
|
||||
{
|
||||
"Admin" => 4,
|
||||
"Kassenwart" => 3, // NEU
|
||||
"Editor" => 2,
|
||||
"Viewer" => 1,
|
||||
_ => 0
|
||||
};
|
||||
```
|
||||
|
||||
**Policy Registration:**
|
||||
```csharp
|
||||
options.AddPolicy("ClubTreasurer", p =>
|
||||
p.Requirements.Add(new ClubRoleRequirement("Kassenwart")));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K5: Repositories**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Domain/Interfaces/IBookingCategoryRepository.cs` (neu)
|
||||
2. `src/Koogle.Domain/Interfaces/ICashBookEntryRepository.cs` (neu)
|
||||
3. `src/Koogle.Infrastructure/Repositories/BookingCategoryRepository.cs` (neu)
|
||||
4. `src/Koogle.Infrastructure/Repositories/CashBookEntryRepository.cs` (neu)
|
||||
5. `src/Koogle.Infrastructure/DependencyInjection.cs` (erweitern)
|
||||
|
||||
**IBookingCategoryRepository:**
|
||||
```csharp
|
||||
Task<List<BookingCategory>> GetByClubIdAsync(Guid clubId, bool includeInactive = false, CancellationToken ct = default);
|
||||
Task<BookingCategory?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
||||
Task<BookingCategory?> GetSystemCategoryAsync(Guid clubId, string name, CancellationToken ct = default);
|
||||
Task<BookingCategory> AddAsync(BookingCategory entity, CancellationToken ct = default);
|
||||
Task<BookingCategory> UpdateAsync(BookingCategory entity, CancellationToken ct = default);
|
||||
```
|
||||
|
||||
**ICashBookEntryRepository:**
|
||||
```csharp
|
||||
Task<List<CashBookEntry>> GetByClubIdAsync(Guid clubId, DateTime? from, DateTime? to, CancellationToken ct = default);
|
||||
Task<CashBookEntry?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
||||
Task<decimal> GetBalanceAsync(Guid clubId, DateTime? asOfDate = null, CancellationToken ct = default);
|
||||
Task<CashBookEntry> AddAsync(CashBookEntry entity, CancellationToken ct = default);
|
||||
Task<List<CashBookEntry>> GetByDayIdAsync(Guid dayId, CancellationToken ct = default);
|
||||
Task<bool> ExistsMembershipFeeForMonthAsync(Guid clubId, int year, int month, CancellationToken ct = default);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K6: DTOs**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/DTOs/BookingCategoryDto.cs` (neu)
|
||||
2. `src/Koogle.Application/DTOs/CashBookEntryDto.cs` (neu)
|
||||
3. `src/Koogle.Application/DTOs/CashBookReportDto.cs` (neu)
|
||||
|
||||
**BookingCategoryDto.cs:**
|
||||
```csharp
|
||||
public record BookingCategoryDto(Guid Id, string Name, string? Description, BookingCategoryType CategoryType, bool IsSystemCategory, string? Color, string? Icon, bool IsActive);
|
||||
public record CreateBookingCategoryDto(string Name, string? Description, BookingCategoryType CategoryType, string? Color, string? Icon);
|
||||
public record UpdateBookingCategoryDto(Guid Id, string Name, string? Description, string? Color, string? Icon, bool IsActive);
|
||||
```
|
||||
|
||||
**CashBookEntryDto.cs:**
|
||||
```csharp
|
||||
public record CashBookEntryDto(Guid Id, Guid CategoryId, string CategoryName, CashBookEntryType EntryType, decimal Amount, DateTime BookingDate, string? Comment, string? ReceiptReference, Guid? DayId, Guid? PersonId, string? PersonName, DateTime CreatedAt);
|
||||
public record CreateCashBookEntryDto(Guid CategoryId, CashBookEntryType EntryType, decimal Amount, DateTime BookingDate, string? Comment, string? ReceiptReference, Guid? PersonId);
|
||||
public record UpdateCashBookEntryDto(Guid Id, Guid CategoryId, decimal Amount, DateTime BookingDate, string? Comment, string? ReceiptReference);
|
||||
```
|
||||
|
||||
**CashBookReportDto.cs:**
|
||||
```csharp
|
||||
public record CashBookReportDto(DateTime ReportStart, DateTime ReportEnd, decimal OpeningBalance, decimal ClosingBalance, decimal TotalIncome, decimal TotalExpense, List<CategorySummaryDto> IncomeByCategory, List<CategorySummaryDto> ExpenseByCategory, List<CashBookEntryDto> Entries);
|
||||
public record CategorySummaryDto(Guid CategoryId, string CategoryName, decimal Total, int Count);
|
||||
public record CreateMembershipFeesDto(int Year, int Month, decimal? OverrideAmount);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K7: Service Interfaces**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Interfaces/IBookingCategoryService.cs` (neu)
|
||||
2. `src/Koogle.Application/Interfaces/ICashBookService.cs` (neu)
|
||||
|
||||
**IBookingCategoryService:**
|
||||
```csharp
|
||||
Task<List<BookingCategoryDto>> GetAllAsync(bool includeInactive = false, CancellationToken ct = default);
|
||||
Task<BookingCategoryDto?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
||||
Task<BookingCategoryDto> CreateAsync(CreateBookingCategoryDto dto, CancellationToken ct = default);
|
||||
Task<BookingCategoryDto> UpdateAsync(UpdateBookingCategoryDto dto, CancellationToken ct = default);
|
||||
Task EnsureSystemCategoriesAsync(Guid clubId, CancellationToken ct = default);
|
||||
```
|
||||
|
||||
**ICashBookService:**
|
||||
```csharp
|
||||
Task<List<CashBookEntryDto>> GetEntriesAsync(DateTime? from = null, DateTime? to = null, CancellationToken ct = default);
|
||||
Task<CashBookEntryDto> CreateAsync(CreateCashBookEntryDto dto, CancellationToken ct = default);
|
||||
Task<CashBookEntryDto> UpdateAsync(UpdateCashBookEntryDto dto, CancellationToken ct = default);
|
||||
Task<bool> DeleteAsync(Guid id, CancellationToken ct = default);
|
||||
Task<decimal> GetCurrentBalanceAsync(CancellationToken ct = default);
|
||||
Task<CashBookReportDto> GetMonthlyReportAsync(int year, int month, CancellationToken ct = default);
|
||||
Task<CashBookReportDto> GetYearlyReportAsync(int year, CancellationToken ct = default);
|
||||
Task CreatePenaltyEntriesForDayAsync(Guid dayId, CancellationToken ct = default);
|
||||
Task<(int Created, bool HadExisting)> CreateMembershipFeesAsync(CreateMembershipFeesDto dto, CancellationToken ct = default);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K8: Service Implementations**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Services/BookingCategoryService.cs` (neu)
|
||||
2. `src/Koogle.Application/Services/CashBookService.cs` (neu)
|
||||
3. `src/Koogle.Application/Mapping/CashBookMappingProfile.cs` (neu)
|
||||
4. `src/Koogle.Application/DependencyInjection.cs` (erweitern)
|
||||
|
||||
**CashBookService.CreatePenaltyEntriesForDayAsync Logic:**
|
||||
1. Hole alle PersonExpenses für DayId mit ExpenseType = Monetary
|
||||
2. Gruppiere nach PersonId
|
||||
3. Für jede Person: CashBookEntry mit Category = "Spielstrafe"
|
||||
4. Setze PersonExpenses auf Done
|
||||
5. Setze DayId + PersonId für Nachverfolgbarkeit
|
||||
|
||||
---
|
||||
|
||||
### **Phase K9: Category Seeder**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Services/ClubService.cs` (erweitern)
|
||||
|
||||
**Bei Club-Erstellung aufrufen:**
|
||||
```csharp
|
||||
await _bookingCategoryService.EnsureSystemCategoriesAsync(club.Id, ct);
|
||||
```
|
||||
|
||||
**System-Kategorien:**
|
||||
- "Spielstrafe" (Income, Green, Icon: Gavel)
|
||||
- "Mitgliedsbeitrag" (Income, Blue, Icon: CardMembership)
|
||||
- "Korrekturbuchung" (Income, Orange, Icon: Build)
|
||||
- "Saldoanpassung" (Income, Gray, Icon: Balance)
|
||||
|
||||
---
|
||||
|
||||
### **Phase K10: Day Close Integration**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Services/DayService.cs` (erweitern)
|
||||
|
||||
**In AdvanceStatusAsync nach Zeile 317:**
|
||||
```csharp
|
||||
if (day.Status == DayStatus.Closed)
|
||||
{
|
||||
await CreateAbsentMemberExpensesAsync(context, day.Id, ct);
|
||||
|
||||
// NEU: Kassenbuch-Einträge für Strafen
|
||||
try
|
||||
{
|
||||
await _cashBookService.CreatePenaltyEntriesForDayAsync(day.Id, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create cash book entries for day {DayId}", day.Id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K11: Fluxor CategoryState**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Store/CategoryState/CategoryState.cs`
|
||||
2. `src/Koogle.Web/Store/CategoryState/CategoryActions.cs`
|
||||
3. `src/Koogle.Web/Store/CategoryState/CategoryReducers.cs`
|
||||
4. `src/Koogle.Web/Store/CategoryState/CategoryEffects.cs`
|
||||
|
||||
**State:**
|
||||
```csharp
|
||||
[FeatureState]
|
||||
public record CategoryState
|
||||
{
|
||||
public IReadOnlyList<BookingCategoryDto> Categories { get; init; } = [];
|
||||
public bool IsLoading { get; init; }
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K12: Fluxor CashBookState**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Store/CashBookState/CashBookState.cs`
|
||||
2. `src/Koogle.Web/Store/CashBookState/CashBookActions.cs`
|
||||
3. `src/Koogle.Web/Store/CashBookState/CashBookReducers.cs`
|
||||
4. `src/Koogle.Web/Store/CashBookState/CashBookEffects.cs`
|
||||
|
||||
**State:**
|
||||
```csharp
|
||||
[FeatureState]
|
||||
public record CashBookState
|
||||
{
|
||||
public IReadOnlyList<CashBookEntryDto> Entries { get; init; } = [];
|
||||
public decimal CurrentBalance { get; init; }
|
||||
public CashBookReportDto? Report { get; init; }
|
||||
public DateTime FilterFrom { get; init; } = DateTime.Today.AddMonths(-1);
|
||||
public DateTime FilterTo { get; init; } = DateTime.Today;
|
||||
public bool IsLoading { get; init; }
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K13: CashBook UI**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Pages/CashBook/CashBook.razor`
|
||||
2. `src/Koogle.Web/Components/Pages/CashBook/CashBook.razor.cs`
|
||||
3. `src/Koogle.Web/Components/Pages/CashBook/CashBookEntryDialog.razor`
|
||||
|
||||
**Route:** `/cashbook`
|
||||
**Policy:** `ClubTreasurer`
|
||||
|
||||
**Features:**
|
||||
- Kontostand-Anzeige oben
|
||||
- MudDataGrid mit Buchungen
|
||||
- Datumsfilter (Von/Bis)
|
||||
- Add/Edit/Delete Buttons
|
||||
- Farbcodierung: Einnahmen grün, Ausgaben rot
|
||||
- Kategorie-Filter Dropdown
|
||||
|
||||
---
|
||||
|
||||
### **Phase K14: Categories UI**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Pages/CashBook/Categories.razor`
|
||||
2. `src/Koogle.Web/Components/Pages/CashBook/CategoryDialog.razor`
|
||||
|
||||
**Route:** `/cashbook/categories`
|
||||
**Policy:** `ClubTreasurer`
|
||||
|
||||
**Features:**
|
||||
- MudDataGrid mit Kategorien
|
||||
- System-Kategorien markiert (kein Löschen)
|
||||
- Inaktive Kategorien ausgegraut
|
||||
- Farbvorschau
|
||||
- Icon-Auswahl
|
||||
|
||||
---
|
||||
|
||||
### **Phase K15: Reports UI**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Pages/CashBook/Reports.razor`
|
||||
2. `src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs`
|
||||
|
||||
**Route:** `/cashbook/reports`
|
||||
**Policy:** `ClubViewer` (Leserecht)
|
||||
|
||||
**Features:**
|
||||
- Monat/Jahr-Auswahl
|
||||
- Anfangs-/Endsaldo-Anzeige
|
||||
- Einnahmen/Ausgaben-Summen
|
||||
- Kategorie-Aufschlüsselung (MudChart)
|
||||
- Expandierbare Detailansicht
|
||||
- Export-Buttons (Excel, PDF)
|
||||
|
||||
---
|
||||
|
||||
### **Phase K16: Excel Export**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Interfaces/ICashBookExportService.cs` (neu)
|
||||
2. `src/Koogle.Application/Services/CashBookExportService.cs` (neu)
|
||||
3. `src/Koogle.Web/Koogle.Web.csproj` (erweitern)
|
||||
|
||||
**NuGet:** `ClosedXML`
|
||||
|
||||
**Excel-Struktur:**
|
||||
- Sheet 1: Zusammenfassung (Salden, Summen)
|
||||
- Sheet 2: Kategorie-Aufschlüsselung
|
||||
- Sheet 3: Alle Buchungen
|
||||
|
||||
---
|
||||
|
||||
### **Phase K17: PDF Export**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Application/Services/CashBookExportService.cs` (erweitern)
|
||||
|
||||
**NuGet:** `QuestPDF`
|
||||
|
||||
**PDF-Struktur:**
|
||||
- Header mit Vereinsname, Berichtszeitraum
|
||||
- Saldo-Tabelle
|
||||
- Kategorie-Aufschlüsselung
|
||||
- Buchungsliste
|
||||
|
||||
---
|
||||
|
||||
### **Phase K18: Export Controller**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Controllers/CashBookController.cs` (neu)
|
||||
|
||||
```csharp
|
||||
[Route("api/cashbook")]
|
||||
[Authorize(Policy = "ClubTreasurer")]
|
||||
public class CashBookController : ControllerBase
|
||||
{
|
||||
[HttpGet("export/excel")]
|
||||
public async Task<IActionResult> ExportExcel(int year, int? month);
|
||||
|
||||
[HttpGet("export/pdf")]
|
||||
public async Task<IActionResult> ExportPdf(int year, int? month);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K19: Membership Fees Feature**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Pages/CashBook/CashBook.razor` (erweitern)
|
||||
2. `src/Koogle.Web/Components/Pages/CashBook/MembershipFeeDialog.razor` (neu)
|
||||
|
||||
**Features:**
|
||||
- Button "Mitgliedsbeiträge erfassen"
|
||||
- Dialog mit Monat/Jahr-Auswahl
|
||||
- Optionaler Betrags-Override
|
||||
- Warnung bei existierenden Beiträgen für Monat
|
||||
- Bestätigung trotz Warnung möglich
|
||||
- Erstellt Buchungen für alle aktiven Members
|
||||
|
||||
---
|
||||
|
||||
### **Phase K20: Club Settings Extension**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Pages/Settings.razor` (erweitern)
|
||||
2. `src/Koogle.Application/DTOs/ClubDto.cs` (erweitern)
|
||||
3. `src/Koogle.Application/Services/ClubService.cs` (erweitern)
|
||||
|
||||
**Neue Settings-Tab "Kassenbuch":**
|
||||
- Eröffnungssaldo (Decimal)
|
||||
- Monatlicher Mitgliedsbeitrag (Decimal)
|
||||
- Link zur Kategorienverwaltung
|
||||
|
||||
---
|
||||
|
||||
### **Phase K21: Navigation**
|
||||
|
||||
**Dateien:**
|
||||
1. `src/Koogle.Web/Components/Layout/NavMenu.razor` (erweitern)
|
||||
|
||||
**Menu-Struktur:**
|
||||
```
|
||||
Kassenbuch (Icon: AccountBalance)
|
||||
├─ Übersicht (/cashbook)
|
||||
├─ Kategorien (/cashbook/categories)
|
||||
└─ Berichte (/cashbook/reports)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Phase K22: Unit Tests**
|
||||
|
||||
**Dateien:**
|
||||
1. `test/Koogle.Tests/Services/BookingCategoryServiceTests.cs` (neu)
|
||||
2. `test/Koogle.Tests/Services/CashBookServiceTests.cs` (neu)
|
||||
|
||||
**Test-Cases:**
|
||||
- Create/Update/Delete Kategorie
|
||||
- System-Kategorie Löschschutz
|
||||
- Buchung erstellen/ändern/löschen
|
||||
- Kontostand-Berechnung
|
||||
- Report-Generierung
|
||||
- Day Close Integration
|
||||
- Mitgliedsbeitrags-Generierung
|
||||
- Duplikat-Warnung bei Beiträgen
|
||||
|
||||
---
|
||||
|
||||
### **Phase K23: Integration Tests**
|
||||
|
||||
**Dateien:**
|
||||
1. `test/Koogle.Tests/Integration/CashBookIntegrationTests.cs` (neu)
|
||||
|
||||
**Szenarien:**
|
||||
- Kompletter Day-Close-Workflow mit Kassenbuch-Einträgen
|
||||
- Mitgliedsbeitrags-Batch-Erstellung
|
||||
- Report-Generierung mit Datumsfiltern
|
||||
- Excel/PDF-Export
|
||||
|
||||
---
|
||||
|
||||
## Berechtigungen Phase 5
|
||||
|
||||
| Feature | Policy |
|
||||
|---------|--------|
|
||||
| Buchungen CRUD | ClubTreasurer |
|
||||
| Kategorien CRUD | ClubTreasurer |
|
||||
| Berichte lesen | ClubViewer |
|
||||
| Berichte exportieren | ClubTreasurer |
|
||||
| Mitgliedsbeiträge erstellen | ClubTreasurer |
|
||||
| Kassenbuch-Settings | ClubAdmin |
|
||||
|
||||
---
|
||||
|
||||
## Kritische Dateien
|
||||
|
||||
| Datei | Änderung |
|
||||
|-------|----------|
|
||||
| `src/Koogle.Domain/Entities/Club.cs` | +InitialBalance, +MonthlyMembershipFee |
|
||||
| `src/Koogle.Application/Services/DayService.cs:317` | Integration Day Close |
|
||||
| `src/Koogle.Infrastructure/Security/ClubRoleRequirement.cs` | Kassenwart Rang |
|
||||
| `src/Koogle.Infrastructure/DependencyInjection.cs` | Services + Policies |
|
||||
| `src/Koogle.Web/Components/Layout/NavMenu.razor` | Menu-Einträge |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung Phase 5
|
||||
|
||||
**23 Phasen (K1-K23)** → **~53 Dateien** → **Kassenbuch-Modul komplett**
|
||||
|
||||
**Geschätzter Aufwand:** ~25 Stunden
|
||||
|
||||
**Kernfeatures:**
|
||||
- Einnahmen/Ausgaben-Buchungen
|
||||
- Kategorien mit System-Kategorien
|
||||
- Automatische Strafen-Buchungen bei Day-Close
|
||||
- Mitgliedsbeitrags-Generierung
|
||||
- Monats-/Jahresberichte
|
||||
- Excel/PDF-Export
|
||||
- Neue Rolle "Kassenwart"
|
||||
|
|
|
|||
Loading…
Reference in New Issue