Fix rilevamento modifiche nella scheda: articoli e allegati

Uscendo dalla scheda il prompt di salvataggio non compariva e il pulsante
"Salva" restava nascosto, perche' IsDirty non vedeva il lavoro fatto:

- gli articoli scelti dalla griglia (OpenSelectArt) non marcavano mai la
  scheda come modificata;
- ValueComparer confronta solo i campi del form, quindi ogni RecalcDirty()
  successivo azzerava il flag alzato con forceTrue da scanner e allegati.
  Non bastava confrontare Articoli: Clone() e' un MemberwiseClone, quindi
  _originalScheda condivide la stessa lista.

Ora il confronto usa uno snapshot dei barcode preso all'apertura piu' un
flag per gli allegati (che vivono in AttachedList fino al salvataggio), e
RecalcDirty() perde forceTrue: non puo' piu' essere azzerato. Le chiamate
su insert/remove degli articoli erano anche fatte prima della modifica
della lista, quindi valutavano lo stato precedente.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-16 13:23:34 +02:00
parent 92b60deac0
commit e3942d00cc
@@ -230,6 +230,11 @@
private int _noteFieldKey; private int _noteFieldKey;
private bool IsDirty { get; set; } private bool IsDirty { get; set; }
private Scheda _originalScheda = null!; private Scheda _originalScheda = null!;
// Clone() e' un MemberwiseClone, quindi _originalScheda condivide la stessa lista
// Articoli: per accorgerci degli articoli aggiunti/rimossi serve uno snapshot a parte.
private List<string> _originalBarcodes = [];
// Gli allegati vivono in AttachedList finche' non si salva: li tracciamo con un flag.
private bool _attachmentsDirty;
private List<AttachedDto>? AttachedList { get; set; } private List<AttachedDto>? AttachedList { get; set; }
protected override void OnInitialized() protected override void OnInitialized()
@@ -240,6 +245,7 @@
OnScannerService.OnErrorScan += OnErrorScan; OnScannerService.OnErrorScan += OnErrorScan;
_originalScheda = Scheda.Clone(); _originalScheda = Scheda.Clone();
_originalBarcodes = Scheda.Articoli.ConvertAll(x => x.Barcode);
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter; Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
LoadAttached(); LoadAttached();
@@ -456,14 +462,29 @@
StateHasChanged(); StateHasChanged();
} }
private void RecalcDirty(bool forceTrue = false) private void RecalcDirty()
{ {
IsDirty = forceTrue || !ValueComparer.AreEqual(Scheda, _originalScheda); IsDirty = _attachmentsDirty
|| !ValueComparer.AreEqual(Scheda, _originalScheda)
|| !BarcodesUnchanged();
if (IsDirty) LabelSave = !IsNew ? "Aggiorna" : "Salva"; if (IsDirty) LabelSave = !IsNew ? "Aggiorna" : "Salva";
else LabelSave = null; else LabelSave = null;
} }
private bool BarcodesUnchanged()
{
if (Scheda.Articoli.Count != _originalBarcodes.Count) return false;
for (var i = 0; i < _originalBarcodes.Count; i++)
{
if (!Scheda.Articoli[i].Barcode.Equals(_originalBarcodes[i], StringComparison.Ordinal))
return false;
}
return true;
}
private static class ValueComparer private static class ValueComparer
{ {
public static bool AreEqual(Scheda? a, Scheda? b) public static bool AreEqual(Scheda? a, Scheda? b)
@@ -526,7 +547,8 @@
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
RecalcDirty(true); _attachmentsDirty = true;
RecalcDirty();
// Processa in background e aggiorna UI man mano (o a blocchi) // Processa in background e aggiorna UI man mano (o a blocchi)
_ = Task.Run(async () => _ = Task.Run(async () =>
@@ -618,7 +640,11 @@
}) })
); );
await InvokeAsync(StateHasChanged); await InvokeAsync(() =>
{
RecalcDirty();
StateHasChanged();
});
} }
}); });
} }
@@ -631,7 +657,8 @@
AttachedList[index].ToRemove = true; AttachedList[index].ToRemove = true;
AttachedList.RemoveAt(index); AttachedList.RemoveAt(index);
RecalcDirty(true); _attachmentsDirty = true;
RecalcDirty();
StateHasChanged(); StateHasChanged();
} }
@@ -701,14 +728,13 @@
if (art != null) if (art != null)
{ {
RecalcDirty(true);
Scheda.Articoli.Insert(0, new SchedaArticolo Scheda.Articoli.Insert(0, new SchedaArticolo
{ {
Barcode = art.Barcode, Barcode = art.Barcode,
Descrizione = art.Descrizione Descrizione = art.Descrizione
}); });
RecalcDirty();
StateHasChanged(); StateHasChanged();
} }
else else
@@ -748,8 +774,8 @@
var index = Scheda.Articoli.FindIndex(x => x.Barcode.Equals(barcode)); var index = Scheda.Articoli.FindIndex(x => x.Barcode.Equals(barcode));
if (index < 0) return; if (index < 0) return;
RecalcDirty(true);
Scheda.Articoli.RemoveAt(index); Scheda.Articoli.RemoveAt(index);
RecalcDirty();
StateHasChanged(); StateHasChanged();
} }