198 lines
8.5 KiB
PHP
Executable file
198 lines
8.5 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/**
|
|
* \file handybarcodescanner/handybarcodescannerindex.php
|
|
* \ingroup handybarcodescanner
|
|
* \brief Mobile Barcode Scanner - Hauptseite (Dolibarr integriert)
|
|
*/
|
|
|
|
// Load Dolibarr environment
|
|
$res = 0;
|
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
|
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
|
}
|
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
|
$tmp2 = realpath(__FILE__);
|
|
$i = strlen($tmp) - 1;
|
|
$j = strlen($tmp2) - 1;
|
|
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
|
|
$i--;
|
|
$j--;
|
|
}
|
|
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
|
$res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
|
}
|
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
|
|
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/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 && file_exists("../../../main.inc.php")) {
|
|
$res = @include "../../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
|
|
|
|
// Load translation files
|
|
$langs->loadLangs(array("handybarcodescanner@handybarcodescanner", "products", "orders", "stocks"));
|
|
|
|
// Get parameters
|
|
$action = GETPOST('action', 'aZ09');
|
|
$mode = GETPOST('mode', 'alpha') ?: 'order';
|
|
|
|
// Security check
|
|
if (!$user->hasRight('handybarcodescanner', 'use')) {
|
|
// Fallback fuer aeltere Rechte-Pruefung
|
|
if (!$user->hasRight('fournisseur', 'commande', 'creer') && !$user->hasRight('supplier_order', 'creer')) {
|
|
accessforbidden('You need permission to use the barcode scanner');
|
|
}
|
|
}
|
|
|
|
// Check mode-specific permissions
|
|
$enableOrder = getDolGlobalInt('HANDYBARCODESCANNER_ENABLE_ORDER', 1);
|
|
$enableShop = getDolGlobalInt('HANDYBARCODESCANNER_ENABLE_SHOP', 1);
|
|
$enableInventory = getDolGlobalInt('HANDYBARCODESCANNER_ENABLE_INVENTORY', 1);
|
|
|
|
/*
|
|
* View
|
|
*/
|
|
|
|
$form = new Form($db);
|
|
|
|
// CSS einbinden (JS wird manuell am Ende geladen)
|
|
$arrayofcss = array('/handybarcodescanner/css/scanner.css');
|
|
|
|
llxHeader('', $langs->trans("HandyBarcodeScanner"), '', '', 0, 0, array(), $arrayofcss, '', 'mod-handybarcodescanner page-scanner classforhorizontalscrolloftabs');
|
|
|
|
print '<div class="scanner-wrapper fichecenter">';
|
|
|
|
// Mode Tabs - eigene Buttons (kein Seitenreload, Kamera bleibt aktiv)
|
|
print '<div class="scanner-mode-tabs">';
|
|
if ($enableOrder) {
|
|
$activeClass = ($mode == 'order') ? ' active' : '';
|
|
print '<button type="button" class="scanner-tab'.$activeClass.'" data-mode="order" onclick="scannerSwitchMode(\'order\', this)">'.img_picto('', 'order', 'class="pictofixedwidth"').$langs->trans("Order").'</button>';
|
|
}
|
|
if ($enableShop) {
|
|
$activeClass = ($mode == 'shop') ? ' active' : '';
|
|
print '<button type="button" class="scanner-tab'.$activeClass.'" data-mode="shop" onclick="scannerSwitchMode(\'shop\', this)">'.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("Shop").'</button>';
|
|
}
|
|
if ($enableInventory) {
|
|
$activeClass = ($mode == 'inventory') ? ' active' : '';
|
|
print '<button type="button" class="scanner-tab'.$activeClass.'" data-mode="inventory" onclick="scannerSwitchMode(\'inventory\', this)">'.img_picto('', 'stock', 'class="pictofixedwidth"').$langs->trans("Inventory").'</button>';
|
|
}
|
|
print '</div>';
|
|
// Inline Tab-Switch Logik (unabhaengig von scanner.js Ladereihenfolge)
|
|
print '<script>
|
|
function scannerSwitchMode(mode, btn) {
|
|
// Aktiven Tab umschalten
|
|
document.querySelectorAll(".scanner-tab").forEach(function(t) { t.classList.remove("active"); });
|
|
btn.classList.add("active");
|
|
// CONFIG aktualisieren (wenn scanner.js geladen)
|
|
if (typeof SCANNER_CONFIG !== "undefined") SCANNER_CONFIG.mode = mode;
|
|
// URL aktualisieren ohne Reload
|
|
history.replaceState(null, "", window.location.pathname + "?mode=" + mode);
|
|
// Ergebnis im neuen Modus anzeigen (wenn scanner.js geladen)
|
|
if (typeof window._scannerHideResult === "function") window._scannerHideResult();
|
|
if (typeof window._scannerCurrentProduct !== "undefined" && window._scannerCurrentProduct && typeof window._scannerShowResult === "function") {
|
|
window._scannerShowResult(window._scannerCurrentProduct);
|
|
}
|
|
}
|
|
</script>';
|
|
|
|
// Scanner Container
|
|
print '<div class="div-table-responsive-no-min">';
|
|
print '<div class="scanner-box">';
|
|
|
|
// Video Container
|
|
print '<div id="scanner-video-container" class="scanner-video-box">';
|
|
print '<video id="scanner-video" playsinline></video>';
|
|
print '<div class="scan-region-highlight"></div>';
|
|
print '</div>';
|
|
|
|
// Scanner Controls
|
|
print '<div class="scanner-controls center">';
|
|
print '<button type="button" id="start-scan-btn" class="button" style="background: #0077b3 !important; color: #fff !important; opacity: 1 !important;">'.$langs->trans("StartScan").'</button>';
|
|
print '<button type="button" id="stop-scan-btn" class="button butActionDelete hidden">'.$langs->trans("StopScan").'</button>';
|
|
print '</div>';
|
|
|
|
print '</div>'; // scanner-box
|
|
print '</div>'; // div-table-responsive
|
|
|
|
// Last Scan Info
|
|
print '<div class="scanner-last-scan marginbottomonly margintoponly">';
|
|
print '<span class="opacitymedium">'.$langs->trans("LastScan").':</span> ';
|
|
print '<span id="last-scan-code" class="badge badge-status4 badge-status">-</span>';
|
|
print '</div>';
|
|
|
|
// Result Area
|
|
print '<div id="result-area" class="scanner-result hidden">';
|
|
print '<!-- Dynamic content loaded via JavaScript -->';
|
|
print '</div>';
|
|
|
|
// PWA-Link fuer mobilen Zugriff (aktuelle Domain verwenden, nicht DOL_MAIN_URL_ROOT)
|
|
$pwaUrl = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].dol_buildpath('/handybarcodescanner/pwa.php', 1);
|
|
print '<div class="scanner-pwa-link margintoponly" style="text-align: center; padding: 10px;">';
|
|
print '<a href="'.$pwaUrl.'" target="_blank" rel="noopener" style="font-size: 13px; opacity: 0.7;">';
|
|
print img_picto('', 'fa-mobile-alt', 'class="pictofixedwidth"');
|
|
print 'PWA: '.$pwaUrl;
|
|
print '</a>';
|
|
print '</div>';
|
|
|
|
print '</div>'; // scanner-wrapper
|
|
|
|
// Hidden config for JavaScript
|
|
?>
|
|
<script>
|
|
var SCANNER_CONFIG = {
|
|
ajaxUrl: '<?php echo dol_buildpath('/handybarcodescanner/ajax/', 1); ?>',
|
|
token: '<?php echo newToken(); ?>',
|
|
mode: '<?php echo $mode; ?>',
|
|
enableVibration: <?php echo getDolGlobalInt('HANDYBARCODESCANNER_ENABLE_VIBRATION', 1); ?>,
|
|
enableSound: <?php echo getDolGlobalInt('HANDYBARCODESCANNER_ENABLE_SOUND', 0); ?>,
|
|
lang: {
|
|
productNotFound: '<?php echo dol_escape_js($langs->trans("ProductNotFound")); ?>',
|
|
added: '<?php echo dol_escape_js($langs->trans("Added")); ?>',
|
|
saved: '<?php echo dol_escape_js($langs->trans("Saved")); ?>',
|
|
error: '<?php echo dol_escape_js($langs->trans("Error")); ?>',
|
|
selectSupplier: '<?php echo dol_escape_js($langs->trans("SelectSupplier")); ?>',
|
|
noSupplier: '<?php echo dol_escape_js($langs->trans("NoSupplierForProduct")); ?>',
|
|
quantity: '<?php echo dol_escape_js($langs->trans("Qty")); ?>',
|
|
add: '<?php echo dol_escape_js($langs->trans("Add")); ?>',
|
|
price: '<?php echo dol_escape_js($langs->trans("Price")); ?>',
|
|
stock: '<?php echo dol_escape_js($langs->trans("Stock")); ?>',
|
|
currentStock: '<?php echo dol_escape_js($langs->trans("CurrentStock")); ?>',
|
|
newStock: '<?php echo dol_escape_js($langs->trans("NewStock")); ?>',
|
|
save: '<?php echo dol_escape_js($langs->trans("Save")); ?>',
|
|
confirmStockChange: '<?php echo dol_escape_js($langs->trans("ConfirmStockChange")); ?>',
|
|
cancel: '<?php echo dol_escape_js($langs->trans("Cancel")); ?>',
|
|
confirm: '<?php echo dol_escape_js($langs->trans("Confirm")); ?>',
|
|
openShop: '<?php echo dol_escape_js($langs->trans("OpenShop")); ?>',
|
|
cheapest: '<?php echo dol_escape_js($langs->trans("Cheapest")); ?>',
|
|
cameraError: '<?php echo dol_escape_js($langs->trans("CameraAccessError")); ?>',
|
|
ref: '<?php echo dol_escape_js($langs->trans("Ref")); ?>',
|
|
product: '<?php echo dol_escape_js($langs->trans("Product")); ?>',
|
|
supplierOrderRef: '<?php echo dol_escape_js($langs->trans("SupplierOrderRef")); ?>'
|
|
}
|
|
};
|
|
</script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
|
|
<script src="<?php echo dol_buildpath('/handybarcodescanner/js/scanner.js', 1); ?>"></script>
|
|
<?php
|
|
|
|
// End of page
|
|
llxFooter();
|
|
$db->close();
|