fix: prevent scale double-deduction (duplicate inventory_use)

Root cause: after scale auto-confirm fires submitUse(), the old code called
_cancelScaleAutoConfirm(false) which reset _scaleLastConfirmedGrams to null.
This allowed the scale (still showing the same reading) to start a new 10-second
stability+confirm cycle and trigger a second identical deduction.

JS fix:
- submitUse() now calls _cancelScaleTimersOnly() instead of
  _cancelScaleAutoConfirm(false), preserving _scaleLastConfirmedGrams so the
  same weight is rejected until the product is removed from the plate.
- _scaleStabilityVal reset to null so a genuinely new weight starts fresh.
- Duplicate API response (result.duplicate) silently ignored in the UI.

PHP fix (server-side safety net):
- useFromInventory() rejects a second 'out' transaction for the same product
  within 12 seconds with { success: false, duplicate: true }.
  This catches any client-side edge cases regardless of scale timing.
This commit is contained in:
dadaloop82
2026-04-27 17:01:11 +00:00
parent d269f919b9
commit 28a8c938bd
2 changed files with 32 additions and 1 deletions
+8 -1
View File
@@ -6176,7 +6176,12 @@ async function submitUse(e) {
e.preventDefault();
if (_useSubmitting) return; // prevent double-submit from scale auto-confirm
_useSubmitting = true;
_cancelScaleAutoConfirm(false); // stop any running auto-confirm
// Stop timers but KEEP _scaleLastConfirmedGrams: this prevents the scale from
// re-triggering another auto-submit while the product is still on the plate.
// (Calling _cancelScaleAutoConfirm(false) would reset the sentinel to null,
// allowing the same weight to start a new 10-second cycle immediately.)
_cancelScaleTimersOnly();
_scaleStabilityVal = null; // reset sentinel so a new DIFFERENT weight restarts correctly
showLoading(true);
try {
let qty = parseFloat(document.getElementById('use-quantity').value) || 1;
@@ -6212,6 +6217,8 @@ async function submitUse(e) {
: () => showPage('dashboard');
// Check low stock → Bring! prompt
showLowStockBringPrompt(result, moveCallback);
} else if (result.duplicate) {
// Silently ignore: this was a scale double-trigger, not a real error
} else {
showToast(result.error || 'Errore', 'error');
}