diff --git a/api/index.php b/api/index.php index 24e3861..b9c4ba6 100644 --- a/api/index.php +++ b/api/index.php @@ -909,6 +909,30 @@ function useFromInventory(PDO $db): void { echo json_encode(['error' => 'Product ID required']); return; } + + // ── Server-side deduplication ───────────────────────────────────────── + // Reject if the same product already has an 'out' transaction in the last + // 12 seconds. This guards against scale double-triggers (the scale can fire + // a second stable reading ~10 s after the first auto-confirm, while the + // product is still on the plate), regardless of the client-side guard. + if (!$useAll) { + $dedup = $db->prepare( + "SELECT id FROM transactions + WHERE product_id = ? AND type IN ('out','waste') AND undone = 0 + AND created_at >= datetime('now', '-12 seconds') + LIMIT 1" + ); + $dedup->execute([$productId]); + if ($dedup->fetch()) { + echo json_encode([ + 'success' => false, + 'error' => 'Operazione già registrata di recente — attendi qualche secondo.', + 'duplicate' => true, + ]); + return; + } + } + // ───────────────────────────────────────────────────────────────────── // Handle "throw all from all locations" if ($useAll && $location === '__all__') { diff --git a/assets/js/app.js b/assets/js/app.js index 3f01562..c0d9eea 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -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'); }