K18 fertig.

Erstellte Datei: src/Koogle.Web/Controllers/CashBookController.cs

  Endpoints:
  - GET /api/cashbook/export/excel?year=2026&month=1 → Excel-Download
  - GET /api/cashbook/export/pdf?year=2026&month=1 → PDF-Download

  Policy: ClubTreasurer

  Reports.razor.cs aktualisiert → Export-Buttons öffnen jetzt die Controller-Endpoints.
This commit is contained in:
beo3000 2026-01-03 22:08:27 +01:00
parent ea237183f9
commit 8a9096c95e
3 changed files with 89 additions and 7 deletions

View File

@ -1716,7 +1716,7 @@ public enum CashBookEntryType { Income = 0, Expense = 1 }
| ✓ | K15 | Web | Reports UI | 2 |
| ✓ | K16 | Application | Excel Export (ClosedXML) | 3 |
| ✓ | K17 | Application | PDF Export (QuestPDF) | 1 |
| | K18 | Web | Export Controller | 1 |
| | K18 | Web | Export Controller | 1 |
| ☐ | K19 | Web | Membership Fees Feature | 2 |
| ☐ | K20 | Web | Club Settings Extension | 3 |
| ☐ | K21 | Web | Navigation Integration | 1 |

View File

@ -2,6 +2,7 @@ using Koogle.Application.DTOs;
using Koogle.Domain.Enums;
using Koogle.Web.Store.CashBookState;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor;
namespace Koogle.Web.Components.Pages.CashBook;
@ -11,6 +12,8 @@ namespace Koogle.Web.Components.Pages.CashBook;
/// </summary>
public partial class Reports
{
[Inject] private IJSRuntime JS { get; set; } = default!;
private int _selectedMonth = DateTime.Today.Month;
private int _selectedYear = DateTime.Today.Year;
@ -80,15 +83,15 @@ public partial class Reports
return type == CashBookEntryType.Income ? Color.Success : Color.Error;
}
private void ExportExcel()
private async Task ExportExcel()
{
// Will be implemented in K16
Snackbar.Add("Excel-Export wird in einer späteren Version verfügbar sein", Severity.Info);
var url = $"/api/cashbook/export/excel?year={_selectedYear}&month={_selectedMonth}";
await JS.InvokeVoidAsync("open", url, "_blank");
}
private void ExportPdf()
private async Task ExportPdf()
{
// Will be implemented in K17
Snackbar.Add("PDF-Export wird in einer späteren Version verfügbar sein", Severity.Info);
var url = $"/api/cashbook/export/pdf?year={_selectedYear}&month={_selectedMonth}";
await JS.InvokeVoidAsync("open", url, "_blank");
}
}

View File

@ -0,0 +1,79 @@
using Koogle.Application.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Koogle.Web.Controllers;
/// <summary>
/// Controller for cash book export operations.
/// </summary>
[Route("api/cashbook")]
[Authorize(Policy = "ClubTreasurer")]
public class CashBookController : Controller
{
private readonly ICashBookService _cashBookService;
private readonly ICashBookExportService _exportService;
private readonly ICurrentClubContext _clubContext;
public CashBookController(
ICashBookService cashBookService,
ICashBookExportService exportService,
ICurrentClubContext clubContext)
{
_cashBookService = cashBookService;
_exportService = exportService;
_clubContext = clubContext;
}
/// <summary>
/// Exports cash book report to Excel format.
/// </summary>
[HttpGet("export/excel")]
public async Task<IActionResult> ExportExcel([FromQuery] int year, [FromQuery] int? month)
{
var (from, to) = GetDateRange(year, month);
var report = await _cashBookService.GetReportAsync(from, to);
var bytes = await _exportService.ExportToExcelAsync(report, _clubContext.ClubName);
var fileName = GetFileName("xlsx", year, month);
return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
/// <summary>
/// Exports cash book report to PDF format.
/// </summary>
[HttpGet("export/pdf")]
public async Task<IActionResult> ExportPdf([FromQuery] int year, [FromQuery] int? month)
{
var (from, to) = GetDateRange(year, month);
var report = await _cashBookService.GetReportAsync(from, to);
var bytes = await _exportService.ExportToPdfAsync(report, _clubContext.ClubName);
var fileName = GetFileName("pdf", year, month);
return File(bytes, "application/pdf", fileName);
}
private static (DateTime from, DateTime to) GetDateRange(int year, int? month)
{
if (month.HasValue)
{
var from = new DateTime(year, month.Value, 1);
var to = from.AddMonths(1).AddDays(-1);
return (from, to);
}
else
{
var from = new DateTime(year, 1, 1);
var to = new DateTime(year, 12, 31);
return (from, to);
}
}
private static string GetFileName(string extension, int year, int? month)
{
var period = month.HasValue ? $"{year}-{month:D2}" : $"{year}";
return $"Kassenbericht_{period}.{extension}";
}
}