PR U-3: validate every row in TransactionSplitDialog

The single EditContext only wrapped the first split row; subsequent
rows added by AddTransactionAsync silently bypassed
DataAnnotationsValidator. Replace editContext.Validate() with an
explicit ValidateAllRows() loop that checks Text, TargetAccountId, and
Value != 0 for every row, and render the collected messages under the
existing FluentValidationSummary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Claudio Schaad 2026-07-04 08:36:21 +02:00
parent 70f9ce0d58
commit 93bfcae2a0
2 changed files with 44 additions and 7 deletions

View file

@ -73,6 +73,15 @@
</table> </table>
<div> <div>
<FluentValidationSummary/> <FluentValidationSummary/>
@if (validationErrors.Count > 0)
{
<ul class="validation-message">
@foreach (var err in validationErrors)
{
<li>@err</li>
}
</ul>
}
</div> </div>
</EditForm> </EditForm>
</FluentDialogBody> </FluentDialogBody>

View file

@ -36,7 +36,8 @@ public partial class TransactionSplitDialog : ComponentBase
private IReadOnlyList<AccountDataset> accounts = []; private IReadOnlyList<AccountDataset> accounts = [];
private IReadOnlyList<string> bookingTexts = []; private IReadOnlyList<string> bookingTexts = [];
private decimal openAmount = 0; private decimal openAmount = 0;
private List<string> validationErrors = new();
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -68,16 +69,43 @@ public partial class TransactionSplitDialog : ComponentBase
openAmount = Content.Value + transactionList.Sum(t => t.Value); openAmount = Content.Value + transactionList.Sum(t => t.Value);
} }
// Every row in transactionList needs Text, TargetAccountId, and a non-zero Value.
// The single EditContext above only covers the first row (that's what it was
// constructed with) — so DataAnnotations validation misses subsequent split rows.
// Run explicit per-row checks here and surface everything in validationErrors.
private bool ValidateAllRows()
{
validationErrors.Clear();
for (var i = 0; i < transactionList.Count; i++)
{
var t = transactionList[i];
if (string.IsNullOrWhiteSpace(t.Text))
{
validationErrors.Add($"Zeile {i + 1}: Buchungstext fehlt");
}
if (string.IsNullOrEmpty(t.TargetAccountId))
{
validationErrors.Add($"Zeile {i + 1}: Konto fehlt");
}
if (t.Value == 0)
{
validationErrors.Add($"Zeile {i + 1}: Betrag darf nicht 0 sein");
}
}
return validationErrors.Count == 0;
}
private async Task SaveAsync() private async Task SaveAsync()
{ {
if (editContext.Validate()) if (!ValidateAllRows())
{ {
foreach (var transtaction in transactionList) return;
{
await transactionRepository.SaveTransactionAsync(transtaction);
}
await Dialog.CloseAsync(Content);
} }
foreach (var transtaction in transactionList)
{
await transactionRepository.SaveTransactionAsync(transtaction);
}
await Dialog.CloseAsync(Content);
} }
private async Task CancelAsync() private async Task CancelAsync()