dolibarr.handybarcodescanner/ajax/updatestock.php
data ad180db510 v4.6: Menü unter Produkte, bessere Barcode-Erkennung, Tab-Wechsel ohne Reload
- Menü aus Header entfernt, neuer Eintrag unter Produkte > Scanner
- Barcode-Erkennung: patchSize medium, grösserer Scan-Bereich, höhere Frequenz
- Timeout-Hinweis nach 8s wenn kein Barcode erkannt wird
- Tab-Wechsel (Order/Shop/Inventur) ohne Seitenreload, Kamera bleibt aktiv
- PWA: gleiche Tab-Logik, Buttons statt Links
- Changelog und README aktualisiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:54:13 +01:00

124 lines
3.4 KiB
PHP
Executable file

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* 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
]);