diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index fb82152..59f5e7b 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -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 | diff --git a/src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs b/src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs index 0eef36d..4809676 100644 --- a/src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs +++ b/src/Koogle.Web/Components/Pages/CashBook/Reports.razor.cs @@ -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; /// 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"); } } diff --git a/src/Koogle.Web/Controllers/CashBookController.cs b/src/Koogle.Web/Controllers/CashBookController.cs new file mode 100644 index 0000000..be7738c --- /dev/null +++ b/src/Koogle.Web/Controllers/CashBookController.cs @@ -0,0 +1,79 @@ +using Koogle.Application.Interfaces; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Koogle.Web.Controllers; + +/// +/// Controller for cash book export operations. +/// +[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; + } + + /// + /// Exports cash book report to Excel format. + /// + [HttpGet("export/excel")] + public async Task 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); + } + + /// + /// Exports cash book report to PDF format. + /// + [HttpGet("export/pdf")] + public async Task 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}"; + } +}