This commit is contained in:
beo3000 2026-02-08 20:32:24 +01:00
parent a7d1441db7
commit 2b8d5daf69
6 changed files with 245 additions and 0 deletions

View File

@ -6,5 +6,6 @@ public interface ISepaExportService
{ {
Task<(SepaExportDto Export, byte[] XmlBytes)> CreateExportAsync(CreateSepaExportDto dto, CancellationToken ct = default); Task<(SepaExportDto Export, byte[] XmlBytes)> CreateExportAsync(CreateSepaExportDto dto, CancellationToken ct = default);
Task<List<SepaExportDto>> GetExportsAsync(CancellationToken ct = default); Task<List<SepaExportDto>> GetExportsAsync(CancellationToken ct = default);
Task<SepaExportDto?> GetByIdAsync(Guid exportId, CancellationToken ct = default);
Task<byte[]> RegenerateXmlAsync(Guid exportId, CancellationToken ct = default); Task<byte[]> RegenerateXmlAsync(Guid exportId, CancellationToken ct = default);
} }

View File

@ -129,6 +129,31 @@ public class SepaExportService : ISepaExportService
}).ToList(); }).ToList();
} }
public async Task<SepaExportDto?> GetByIdAsync(Guid exportId, CancellationToken ct = default)
{
await using var context = await _contextFactory.CreateDbContextAsync(ct);
var export = await context.SepaExports
.Include(e => e.Entries).ThenInclude(e => e.Category)
.Include(e => e.Entries).ThenInclude(e => e.Person)
.FirstOrDefaultAsync(e => e.Id == exportId && e.ClubId == _clubContext.ClubId && !e.IsDeleted, ct);
if (export is null)
return null;
return new SepaExportDto
{
Id = export.Id,
ClubId = export.ClubId,
Description = export.Description,
RequestedCollectionDate = export.RequestedCollectionDate,
TotalAmount = export.TotalAmount,
EntryCount = export.EntryCount,
CreatedAt = export.CreatedAt,
Entries = _mapper.Map<List<CashBookEntryDto>>(export.Entries)
};
}
public async Task<byte[]> RegenerateXmlAsync(Guid exportId, CancellationToken ct = default) public async Task<byte[]> RegenerateXmlAsync(Guid exportId, CancellationToken ct = default)
{ {
var club = await _clubRepository.GetByIdAsync(_clubContext.ClubId, ct) var club = await _clubRepository.GetByIdAsync(_clubContext.ClubId, ct)

View File

@ -82,6 +82,10 @@
OnClick="OpenSepaExportDialog"> OnClick="OpenSepaExportDialog">
SEPA Export SEPA Export
</MudButton> </MudButton>
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.History"
Href="/cashbook/sepa-exports">
SEPA Historie
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Success" StartIcon="@Icons.Material.Filled.Add" <MudButton Variant="Variant.Filled" Color="Color.Success" StartIcon="@Icons.Material.Filled.Add"
OnClick="OpenCreateDialog"> OnClick="OpenCreateDialog">
Neue Buchung Neue Buchung

View File

@ -0,0 +1,100 @@
@using GoodWood.Application.DTOs
@using GoodWood.Application.Interfaces
@inject ISepaExportService SepaExportService
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">SEPA Export Details</MudText>
</TitleContent>
<DialogContent>
@if (_loading)
{
<MudProgressCircular Indeterminate="true" />
}
else if (_exportDetails is not null)
{
<MudStack Spacing="3">
<MudPaper Class="pa-3" Elevation="0">
<MudGrid>
<MudItem xs="6">
<MudText Typo="Typo.caption" Color="Color.Secondary">Beschreibung</MudText>
<MudText Typo="Typo.body1">@_exportDetails.Description</MudText>
</MudItem>
<MudItem xs="6">
<MudText Typo="Typo.caption" Color="Color.Secondary">Einzugsdatum</MudText>
<MudText Typo="Typo.body1">@_exportDetails.RequestedCollectionDate.ToString("dd.MM.yyyy")</MudText>
</MudItem>
<MudItem xs="6">
<MudText Typo="Typo.caption" Color="Color.Secondary">Erstellt</MudText>
<MudText Typo="Typo.body1">@_exportDetails.CreatedAt.ToString("dd.MM.yyyy HH:mm")</MudText>
</MudItem>
<MudItem xs="6">
<MudText Typo="Typo.caption" Color="Color.Secondary">Gesamtbetrag</MudText>
<MudText Typo="Typo.h6" Color="Color.Success">@_exportDetails.TotalAmount.ToString("C")</MudText>
</MudItem>
</MudGrid>
</MudPaper>
<MudText Typo="Typo.subtitle1" Class="mt-2">Enthaltene Buchungen (@_exportDetails.EntryCount)</MudText>
<MudTable Items="_exportDetails.Entries" Dense="true" Hover="true">
<HeaderContent>
<MudTh>Datum</MudTh>
<MudTh>Person</MudTh>
<MudTh>Beschreibung</MudTh>
<MudTh Style="text-align:right">Betrag</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.BookingDate.ToString("dd.MM.yyyy")</MudTd>
<MudTd>@context.PersonName</MudTd>
<MudTd>@context.Comment</MudTd>
<MudTd Style="text-align:right">@context.Amount.ToString("C")</MudTd>
</RowTemplate>
</MudTable>
</MudStack>
}
else if (_error is not null)
{
<MudAlert Severity="Severity.Error">@_error</MudAlert>
}
</DialogContent>
<DialogActions>
<MudButton OnClick="Close">Schließen</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private IMudDialogInstance? MudDialog { get; set; }
[Parameter]
public SepaExportDto Export { get; set; } = null!;
private SepaExportDto? _exportDetails;
private bool _loading = true;
private string? _error;
protected override async Task OnInitializedAsync()
{
try
{
_exportDetails = await SepaExportService.GetByIdAsync(Export.Id);
if (_exportDetails is null)
{
_error = "Export nicht gefunden";
}
}
catch (Exception ex)
{
_error = ex.Message;
}
finally
{
_loading = false;
}
}
private void Close() => MudDialog?.Close();
}

View File

@ -0,0 +1,111 @@
@page "/cashbook/sepa-exports"
@attribute [Authorize(Policy = "ClubTreasurer")]
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
@using Fluxor
@using GoodWood.Application.DTOs
@using GoodWood.Application.Interfaces
@using GoodWood.Web.Store.SepaExportState
@using Microsoft.AspNetCore.Authorization
@using Microsoft.JSInterop
@inject IState<SepaExportState> SepaExportState
@inject IDispatcher Dispatcher
@inject ISnackbar Snackbar
@inject IDialogService DialogService
@inject ISepaExportService SepaExportService
@inject IJSRuntime JS
<PageTitle>SEPA Exporte</PageTitle>
<MudText Typo="Typo.h4" Class="mb-4">SEPA Exporte</MudText>
@if (SepaExportState.Value.Error is not null)
{
<MudAlert Severity="Severity.Error" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="ClearError">
@SepaExportState.Value.Error
</MudAlert>
}
<MudPaper Class="pa-4 mb-4" Elevation="1">
<MudStack Row="true" Spacing="2">
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.ArrowBack"
Href="/cashbook">
Zurück zum Kassenbuch
</MudButton>
</MudStack>
</MudPaper>
<MudTable Items="SepaExportState.Value.Exports" Dense="true" Hover="true" Loading="SepaExportState.Value.IsLoading">
<HeaderContent>
<MudTh>Erstellt</MudTh>
<MudTh>Beschreibung</MudTh>
<MudTh>Einzugsdatum</MudTh>
<MudTh Style="text-align:right">Anzahl</MudTh>
<MudTh Style="text-align:right">Betrag</MudTh>
<MudTh>Aktionen</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.CreatedAt.ToString("dd.MM.yyyy HH:mm")</MudTd>
<MudTd>@context.Description</MudTd>
<MudTd>@context.RequestedCollectionDate.ToString("dd.MM.yyyy")</MudTd>
<MudTd Style="text-align:right">@context.EntryCount</MudTd>
<MudTd Style="text-align:right">@context.TotalAmount.ToString("C")</MudTd>
<MudTd>
<MudTooltip Text="Details anzeigen">
<MudIconButton Icon="@Icons.Material.Filled.Visibility"
Size="Size.Small"
OnClick="@(() => OpenDetailsDialog(context))"/>
</MudTooltip>
<MudTooltip Text="XML erneut herunterladen">
<MudIconButton Icon="@Icons.Material.Filled.Download"
Size="Size.Small"
Color="Color.Primary"
OnClick="@(() => DownloadXml(context))"/>
</MudTooltip>
</MudTd>
</RowTemplate>
<NoRecordsContent>
<MudText>Keine SEPA Exporte vorhanden</MudText>
</NoRecordsContent>
</MudTable>
@code {
protected override void OnInitialized()
{
base.OnInitialized();
Dispatcher.Dispatch(new LoadSepaExportsAction());
}
private void ClearError()
{
Dispatcher.Dispatch(new ClearSepaExportErrorAction());
}
private async Task OpenDetailsDialog(SepaExportDto export)
{
var parameters = new DialogParameters
{
{ "Export", export }
};
await DialogService.ShowAsync<SepaExportDetailsDialog>("SEPA Export Details", parameters,
new DialogOptions { MaxWidth = MaxWidth.Large });
}
private async Task DownloadXml(SepaExportDto export)
{
try
{
var xmlBytes = await SepaExportService.RegenerateXmlAsync(export.Id);
var fileName = $"sepa-{export.RequestedCollectionDate:yyyy-MM-dd}.xml";
await JS.InvokeVoidAsync("downloadFile", fileName, "application/xml", xmlBytes);
Snackbar.Add("XML heruntergeladen", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"Fehler: {ex.Message}", Severity.Error);
}
}
}

View File

@ -316,6 +316,10 @@ else
StartIcon="@Icons.Material.Filled.Assessment" Href="/cashbook/reports"> StartIcon="@Icons.Material.Filled.Assessment" Href="/cashbook/reports">
Berichte anzeigen Berichte anzeigen
</MudButton> </MudButton>
<MudButton Variant="Variant.Outlined" Color="Color.Warning"
StartIcon="@Icons.Material.Filled.History" Href="/cashbook/sepa-exports">
SEPA Exporte
</MudButton>
</MudStack> </MudStack>
</MudCardContent> </MudCardContent>
<MudCardActions> <MudCardActions>