* * AJAX: Find product by barcode * Searches in: product.barcode, product_fournisseur_price (supplier EAN) */ 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.'/fourn/class/fournisseur.product.class.php'; header('Content-Type: application/json; charset=utf-8'); // Security check if (!$user->hasRight('produit', 'lire') && !$user->hasRight('product', 'read')) { echo json_encode(['success' => false, 'error' => 'Access denied']); exit; } $barcode = GETPOST('barcode', 'alphanohtml'); if (empty($barcode)) { echo json_encode(['success' => false, 'error' => 'No barcode provided']); exit; } $product = new Product($db); $found = false; // Build list of barcode variants to search // QuaggaJS sometimes adds a check digit to 12-digit codes, making them 13 digits $barcodesToSearch = array($barcode); // If 13 digits, also try without last digit (might be added check digit) if (strlen($barcode) == 13 && preg_match('/^\d{13}$/', $barcode)) { $barcodesToSearch[] = substr($barcode, 0, 12); } // If 12 digits, also try with EAN-13 check digit if (strlen($barcode) == 12 && preg_match('/^\d{12}$/', $barcode)) { $checkDigit = calculateEAN13CheckDigit($barcode); $barcodesToSearch[] = $barcode . $checkDigit; } // 1. Search by product barcode (try all variants) foreach ($barcodesToSearch as $searchCode) { $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."product WHERE barcode = '".$db->escape($searchCode)."' AND entity IN (".getEntity('product').")"; $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { $obj = $db->fetch_object($resql); $product->fetch($obj->rowid); $found = true; break; } } // 2. Search by supplier barcode (EAN in product_fournisseur_price) - try all variants if (!$found) { foreach ($barcodesToSearch as $searchCode) { $sql = "SELECT DISTINCT pfp.fk_product FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON pfp.fk_product = p.rowid"; $sql .= " WHERE (pfp.barcode = '".$db->escape($searchCode)."' OR pfp.ref_fourn = '".$db->escape($searchCode)."')"; $sql .= " AND p.entity IN (".getEntity('product').")"; $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { $obj = $db->fetch_object($resql); $product->fetch($obj->fk_product); $found = true; break; } } } // 3. Search by product ref (in case barcode equals ref) if (!$found) { $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."product WHERE ref = '".$db->escape($barcode)."' AND entity IN (".getEntity('product').")"; $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { $obj = $db->fetch_object($resql); $product->fetch($obj->rowid); $found = true; } } /** * Calculate EAN-13 check digit for a 12-digit code */ function calculateEAN13CheckDigit($code) { $sum = 0; for ($i = 0; $i < 12; $i++) { $sum += intval($code[$i]) * ($i % 2 === 0 ? 1 : 3); } return (10 - ($sum % 10)) % 10; } if (!$found) { echo json_encode(['success' => false, 'error' => 'Product not found']); exit; } // Get supplier prices $suppliers = []; $productFourn = new ProductFournisseur($db); $resql = $productFourn->list_product_fournisseur_price($product->id); if ($resql) { foreach ($resql as $supplierPrice) { // Get supplier info mit shop_url aus extrafields $supplierSql = "SELECT s.nom, s.url, se.shop_url FROM ".MAIN_DB_PREFIX."societe s LEFT JOIN ".MAIN_DB_PREFIX."societe_extrafields se ON se.fk_object = s.rowid WHERE s.rowid = ".((int) $supplierPrice->fourn_id); $supplierRes = $db->query($supplierSql); $supplierObj = $db->fetch_object($supplierRes); // ref_fourn kann unter verschiedenen Namen im Objekt sein $refFourn = ''; if (!empty($supplierPrice->ref_supplier)) { $refFourn = $supplierPrice->ref_supplier; } elseif (!empty($supplierPrice->fourn_ref)) { $refFourn = $supplierPrice->fourn_ref; } elseif (!empty($supplierPrice->ref_fourn)) { $refFourn = $supplierPrice->ref_fourn; } // shop_url aus Extrafields verwenden, sonst normale URL $shopUrl = $supplierObj->shop_url ?? $supplierObj->url ?? ''; $suppliers[] = [ 'id' => $supplierPrice->fourn_id, 'name' => $supplierObj->nom ?? 'Unknown', 'price' => (float) $supplierPrice->fourn_price, 'ref_fourn' => $refFourn, 'url' => $shopUrl ]; } // Sort by price (cheapest first) usort($suppliers, function($a, $b) { return $a['price'] <=> $b['price']; }); } // Response $response = [ 'success' => true, 'product' => [ 'id' => $product->id, 'ref' => $product->ref, 'label' => $product->label, 'barcode' => $product->barcode, 'stock' => (float) $product->stock_reel, 'stock_unit' => $product->fk_unit ? $product->getLabelOfUnit() : 'Stk', 'suppliers' => $suppliers ] ]; echo json_encode($response);