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
+24
View File
@@ -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__') {