Replace the native <input list="texts"> + <datalist> pattern in PendingBookings and TransactionSplitDialog with FluentCombobox + Autocomplete=Both so the booking-text autocomplete matches the rest of the Fluent-driven forms. Sort the suggestions once via a sortedBookingTexts computed getter per codebehind rather than sorting inside each row's foreach. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
117 lines
No EOL
3.8 KiB
C#
117 lines
No EOL
3.8 KiB
C#
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Forms;
|
|
using Microsoft.FluentUI.AspNetCore.Components;
|
|
using Schaad.Accounting.Datasets;
|
|
using Schaad.Accounting.Interfaces;
|
|
using Schaad.Accounting.Models;
|
|
|
|
namespace Schaad.Accounting.UI.Components.Pages.Dialogs;
|
|
|
|
public partial class TransactionSplitDialog : ComponentBase
|
|
{
|
|
private EditContext? editContext;
|
|
|
|
[CascadingParameter]
|
|
public FluentDialog Dialog { get; set; } = null!;
|
|
|
|
[Parameter]
|
|
public Transaction Content { get; set; } = null!;
|
|
|
|
[Inject]
|
|
private ITransactionRepository transactionRepository { get; set; } = null!;
|
|
|
|
[Inject]
|
|
private IBankTransactionRepository bankTransactionRepository { get; set; } = null!;
|
|
|
|
[Inject]
|
|
private IAccountRepository accountRepository { get; set; } = null!;
|
|
|
|
[Inject]
|
|
private IBookingTextRepository bookingTextRepository { get; set; } = null!;
|
|
|
|
[Inject]
|
|
private IViewService viewService { get; set; } = null!;
|
|
|
|
private List<Transaction> transactionList = new();
|
|
private IReadOnlyList<AccountDataset> accounts = [];
|
|
private IReadOnlyList<string> bookingTexts = [];
|
|
private decimal openAmount = 0;
|
|
private List<string> validationErrors = new();
|
|
|
|
private IEnumerable<string> sortedBookingTexts => bookingTexts.OrderBy(t => t);
|
|
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
accounts = await viewService.GetAccountViewListAsync();
|
|
bookingTexts = (await bookingTextRepository.GetBookingTextListAsync()).Select(b => b.Text).ToArray();
|
|
var accountList = await accountRepository.GetAccountListAsync();
|
|
var bankTrx = await bankTransactionRepository.GetBankTransactionAsync(Content.BankTransactionId);
|
|
var trx = new Transaction(bankTrx, accountList);
|
|
trx.Value = Math.Abs(bankTrx.Value);
|
|
transactionList.Add(trx);
|
|
editContext = new EditContext(trx);
|
|
}
|
|
|
|
private async Task AddTransactionAsync()
|
|
{
|
|
transactionList.Add(new Transaction
|
|
{
|
|
ValueDate = Content.ValueDate,
|
|
BookingDate = Content.BookingDate,
|
|
OriginAccountId = Content.OriginAccountId,
|
|
Value = 0
|
|
});
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private void CalculateOpenAmount()
|
|
{
|
|
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()
|
|
{
|
|
if (!ValidateAllRows())
|
|
{
|
|
return;
|
|
}
|
|
foreach (var transtaction in transactionList)
|
|
{
|
|
await transactionRepository.SaveTransactionAsync(transtaction);
|
|
}
|
|
await Dialog.CloseAsync(Content);
|
|
}
|
|
|
|
private async Task CancelAsync()
|
|
{
|
|
await Dialog.CancelAsync();
|
|
}
|
|
} |