feat(accounts): preview and confirm cross-year balance corrections (U12)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<ModalDialog @ref="modal" Title="Neues Konto" OnCancel="OnCancel" Dirty="@(!string.IsNullOrWhiteSpace(name))">
|
||||
@inject ToastService Toasts
|
||||
<ModalDialog @ref="modal" Title="Neues Konto" OnCancel="OnCancel" Dirty="@(!string.IsNullOrWhiteSpace(name) || initialAmount != "0,00")" Busy="saving">
|
||||
<h5 class="mb-3">Neues Konto</h5>
|
||||
<form @onsubmit="Save" @onsubmit:preventDefault="true">
|
||||
<div class="form-container">
|
||||
@@ -6,8 +7,11 @@
|
||||
<input id="account-name" data-initial-focus type="text" class="form-control" @bind="name" @bind:event="oninput"
|
||||
placeholder="z.B. Girokonto" />
|
||||
</div>
|
||||
<label for="account-initial">Anfangsbestand (€)</label>
|
||||
<AmountInput Id="account-initial" @bind-Value="initialAmount" AllowNegative="true" Disabled="saving" />
|
||||
<p>Geld, das vor der ersten erfassten Buchung vorhanden ist. 0 ist möglich, ein Minus bei überzogenem Konto ebenfalls. Der Anfangsbestand zählt nicht als neue Einnahme.</p>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="submit" class="btn btn-success" disabled="@string.IsNullOrWhiteSpace(name)"><i class="bi bi-check-lg"></i> Erstellen</button>
|
||||
<button type="submit" class="btn btn-success" disabled="@(saving || string.IsNullOrWhiteSpace(name) || !ValidInitial)"><i class="bi bi-check-lg"></i> Erstellen</button>
|
||||
<button type="button" class="btn btn-outline-secondary" @onclick="Cancel"><i class="bi bi-x-lg"></i> Abbrechen</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -7,7 +7,7 @@ public partial class AddAccountDialog
|
||||
#region Parameters
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> OnSave { get; set; }
|
||||
public EventCallback<Duempelkas.App.Services.Models.AccountSetup> OnSave { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnCancel { get; set; }
|
||||
@@ -17,6 +17,9 @@ public partial class AddAccountDialog
|
||||
#region Fields
|
||||
|
||||
private string name = string.Empty;
|
||||
private string initialAmount = "0,00";
|
||||
private bool ValidInitial => Duempelkas.App.Services.BookingInput.TryBalance(initialAmount, out _);
|
||||
private bool saving;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -24,9 +27,12 @@ public partial class AddAccountDialog
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
if (!saving && !string.IsNullOrWhiteSpace(name) && ValidInitial)
|
||||
{
|
||||
await OnSave.InvokeAsync(name.Trim());
|
||||
saving = true;
|
||||
try { Duempelkas.App.Services.BookingInput.TryBalance(initialAmount, out var initial); await OnSave.InvokeAsync(new(name.Trim(), initial)); }
|
||||
catch (Exception ex) { Toasts.Show("Das Konto konnte nicht angelegt werden. Die Eingaben bleiben erhalten.", Duempelkas.App.Services.ToastKind.Error, ex.Message); }
|
||||
finally { saving = false; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<ModalDialog @ref="modal" Title="Bestand korrigieren" OnCancel="OnCancel" Dirty="@(amount != CurrentAmount)">
|
||||
<h5 class="mb-3">Übertrag bearbeiten</h5>
|
||||
<div class="form-container">
|
||||
<label class="form-label" for="carryover">Übertrag (€)</label>
|
||||
<input id="carryover" data-initial-focus type="number" class="form-control no-spinner" step="0.01" @bind="amount" />
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-success" @onclick="Save"><i class="bi bi-check-lg"></i> Speichern</button>
|
||||
<button type="button" class="btn btn-outline-secondary" @onclick="Cancel"><i class="bi bi-x-lg"></i> Abbrechen</button>
|
||||
</div>
|
||||
@inject ToastService Toasts
|
||||
<ModalDialog @ref="modal" Title="Bestandskorrektur" OnCancel="OnCancel" Dirty="@(amount != CurrentAmount)" Busy="saving">
|
||||
<h5>Bestandskorrektur – @AccountName</h5>
|
||||
<p>@(Year.HasValue ? $"Übertrag zum 01.01.{Year}" : "Gemeinsamer Anfangsbestand – alle Jahre")</p>
|
||||
<p>Anfangsbestand + Einnahmen − Ausgaben = Kontostand</p>
|
||||
<p>Dies ist eine spätere Korrektur, keine zusätzliche Einnahme oder Ausgabe.</p>
|
||||
<label for="carryover">@(Year.HasValue ? "Neuer Übertrag (€)" : "Neuer Anfangsbestand (€)")</label>
|
||||
<AmountInput Id="carryover" Value="amountText" ValueChanged="ChangeAmount" AllowNegative="true" Disabled="saving" />
|
||||
<div class="correction-preview">
|
||||
<p>Gewählter Bestand: @Money(CurrentAmount) → @Money(amount)</p>
|
||||
<p>Gemeinsamer Anfangsbestand: @Money(InitialBalance) → @Money(InitialBalance + Delta)</p>
|
||||
<p>Kontostand der Ansicht: @Money(CurrentEndBalance) → @Money(CurrentEndBalance + Delta)</p>
|
||||
<p><strong>Alle Kontostände dieses Kontos ändern sich um @Money(Delta). Das betrifft frühere und spätere Jahre.</strong></p>
|
||||
</div>
|
||||
<label><input type="checkbox" @bind="acknowledged" disabled="@saving" /> Ich habe die Auswirkungen auf alle Jahre geprüft.</label>
|
||||
<div class="dialog-actions">
|
||||
<button type="button" class="btn btn-success" disabled="@(saving || !ValidAmount || !acknowledged || Delta == 0m)" @onclick="Save">Korrektur speichern</button>
|
||||
<button type="button" class="btn btn-outline-secondary" disabled="@saving" data-safe-focus @onclick="Cancel">Abbrechen</button>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
|
||||
@code { private ModalDialog? modal; }
|
||||
|
||||
@@ -1,39 +1,36 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using Duempelkas.App.Services;
|
||||
namespace Duempelkas.App.Components.Dialogs;
|
||||
|
||||
public partial class EditCarryoverDialog
|
||||
{
|
||||
#region Parameters
|
||||
|
||||
[Parameter]
|
||||
public decimal CurrentAmount { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<decimal> OnSave { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnCancel { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private decimal amount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
protected override void OnParametersSet() => amount = CurrentAmount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
|
||||
private async Task Save() => await OnSave.InvokeAsync(amount);
|
||||
|
||||
private async Task Cancel() { if (modal != null) await modal.RequestCloseAsync(); else await OnCancel.InvokeAsync(); }
|
||||
|
||||
#endregion
|
||||
[Parameter] public decimal CurrentAmount { get; set; }
|
||||
[Parameter] public decimal InitialBalance { get; set; }
|
||||
[Parameter] public decimal CurrentEndBalance { get; set; }
|
||||
[Parameter] public int? Year { get; set; }
|
||||
[Parameter] public string AccountName { get; set; } = "Konto";
|
||||
[Parameter] public EventCallback<decimal> OnSave { get; set; }
|
||||
[Parameter] public EventCallback OnCancel { get; set; }
|
||||
private string amountText = string.Empty;
|
||||
private bool acknowledged;
|
||||
private bool initialized;
|
||||
private bool saving;
|
||||
private bool ValidAmount => BookingInput.TryBalance(amountText, out _);
|
||||
private decimal amount => BookingInput.TryBalance(amountText, out var value) ? value : CurrentAmount;
|
||||
private decimal Delta => amount - CurrentAmount;
|
||||
private static string Money(decimal value) => value.ToString("N2", System.Globalization.CultureInfo.GetCultureInfo("de-DE")) + " €";
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!initialized) { amountText = CurrentAmount.ToString("0.00", System.Globalization.CultureInfo.GetCultureInfo("de-DE")); initialized = true; }
|
||||
}
|
||||
private void ChangeAmount(string value) { amountText = value; acknowledged = false; }
|
||||
private async Task Save()
|
||||
{
|
||||
if (saving || !ValidAmount || !acknowledged || Delta == 0m) return;
|
||||
saving = true;
|
||||
try { await OnSave.InvokeAsync(amount); }
|
||||
catch (Exception ex) { Toasts.Show("Die Korrektur konnte nicht gespeichert werden. Bitte den aktuellen Bestand prüfen.", ToastKind.Error, ex.Message); }
|
||||
finally { saving = false; }
|
||||
}
|
||||
private Task Cancel() => modal?.RequestCloseAsync() ?? Task.CompletedTask;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user