* * AJAX: Update product stock (Inventory mode) */ if (!defined('NOTOKENRENEWAL')) { define('NOTOKENRENEWAL', '1'); } if (!defined('NOREQUIREMENU')) { define('NOREQUIREMENU', '1'); } if (!defined('NOREQUIREHTML')) { define('NOREQUIREHTML', '1'); } if (!defined('NOREQUIREAJAX')) { define('NOREQUIREAJAX', '1'); } // Load Dolibarr environment $res = 0; if (!$res && file_exists("../../main.inc.php")) { $res = @include "../../main.inc.php"; } if (!$res && file_exists("../../../main.inc.php")) { $res = @include "../../../main.inc.php"; } if (!$res && file_exists("../../../../main.inc.php")) { $res = @include "../../../../main.inc.php"; } if (!$res) { die(json_encode(['success' => false, 'error' => 'Failed to load Dolibarr'])); } require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; header('Content-Type: application/json; charset=utf-8'); // Security check if (!$user->hasRight('stock', 'mouvement', 'creer')) { echo json_encode(['success' => false, 'error' => 'Access denied - no stock movement rights']); exit; } // Get parameters $productId = GETPOSTINT('product_id'); $newStock = GETPOSTFLOAT('stock'); if (empty($productId)) { echo json_encode(['success' => false, 'error' => 'Missing product_id']); exit; } if ($newStock === null || $newStock === '') { echo json_encode(['success' => false, 'error' => 'Missing stock value']); exit; } // Load product $product = new Product($db); if ($product->fetch($productId) <= 0) { echo json_encode(['success' => false, 'error' => 'Product not found']); exit; } $currentStock = (float) $product->stock_reel; $newStock = (float) $newStock; $diff = $newStock - $currentStock; if ($diff == 0) { echo json_encode(['success' => true, 'message' => 'No change needed']); exit; } // Get default warehouse from config or use first available $warehouseId = getDolGlobalInt('HANDYBARCODESCANNER_DEFAULT_WAREHOUSE', 0); if ($warehouseId <= 0) { // Get first warehouse $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."entrepot WHERE statut = 1 AND entity IN (".getEntity('stock').") ORDER BY rowid ASC LIMIT 1"; $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { $obj = $db->fetch_object($resql); $warehouseId = $obj->rowid; } } if ($warehouseId <= 0) { echo json_encode(['success' => false, 'error' => 'No warehouse configured']); exit; } // Create stock movement $movementStock = new MouvementStock($db); $movementStock->origin_type = 'handybarcodescanner'; $movementStock->origin_id = 0; $inventoryCode = 'INV-SCAN-'.date('Ymd-His'); $label = 'Inventur per HandyBarcodeScanner'; if ($diff > 0) { // Increase stock $result = $movementStock->reception($user, $productId, $warehouseId, $diff, 0, $label, '', '', '', '', 0, $inventoryCode); } else { // Decrease stock $result = $movementStock->livraison($user, $productId, $warehouseId, abs($diff), 0, $label, '', '', '', '', 0, $inventoryCode); } if ($result < 0) { echo json_encode(['success' => false, 'error' => 'Stock movement failed: ' . $movementStock->error]); exit; } // Reload product to get updated stock $product->fetch($productId); echo json_encode([ 'success' => true, 'old_stock' => $currentStock, 'new_stock' => (float) $product->stock_reel, 'difference' => $diff, 'warehouse_id' => $warehouseId ]);