diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/ChangeLog.md b/ChangeLog.md index b281a43..9b6f5cc 100755 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -25,9 +25,24 @@ ### UI/UX - **Kompakterer Scan-Button**: Mehr Platz für Tool-Buttons -- **Swipe-Hinweis**: Visueller Hinweis "← Bestellungen" im Order-Mode - **Service Worker v5.3**: Aktualisiertes Caching mit JsBarcode-Library +## 5.1 + +### Bestellungen verwalten +- **Bestellung löschen**: Lösch-Button (Papierkorb-Icon) an jeder Entwurfs-Bestellung +- **Bestätigungsdialog**: Sicherheitsabfrage vor dem Löschen einer Bestellung +- **Freitext-Zeilen bearbeiten**: Beschreibung und Menge von Freitext-Positionen änderbar +- **Lieferant merken**: Zuletzt verwendeter Freitext-Lieferant wird für nächsten Eintrag vorausgewählt + +### Dark Theme Verbesserungen +- **Bestellzeilen**: Korrektes Styling im Dark Theme (war weiß/unleserlich) +- **Dialoge**: Alle Dialoge mit konsistenten Dark Theme Farben +- **Aktive Bestellung**: Bessere Hervorhebung der aktiven Bestellung + +### Entfernt +- Swipe-Hinweis-Button (überflüssig, Swipe ist intuitiv) + ## 4.7 - PWA-Link auf der Scanner-Seite angezeigt (korrekter externer Hostname statt interner IP) diff --git a/ajax/addfreetextline.php b/ajax/addfreetextline.php old mode 100644 new mode 100755 diff --git a/ajax/deleteorder.php b/ajax/deleteorder.php new file mode 100644 index 0000000..f5d9b05 --- /dev/null +++ b/ajax/deleteorder.php @@ -0,0 +1,74 @@ + + * + * AJAX: Delete entire order + */ + +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.'/fourn/class/fournisseur.commande.class.php'; + +header('Content-Type: application/json; charset=utf-8'); + +// Security check +if (!$user->hasRight('fournisseur', 'commande', 'supprimer') && !$user->hasRight('supplier_order', 'supprimer')) { + echo json_encode(['success' => false, 'error' => 'Keine Berechtigung zum Löschen']); + exit; +} + +$orderId = GETPOSTINT('order_id'); + +if (empty($orderId)) { + echo json_encode(['success' => false, 'error' => 'Missing order_id']); + exit; +} + +$order = new CommandeFournisseur($db); +if ($order->fetch($orderId) <= 0) { + echo json_encode(['success' => false, 'error' => 'Bestellung nicht gefunden']); + exit; +} + +// Only allow deletion of draft orders +if ($order->statut != 0) { + echo json_encode(['success' => false, 'error' => 'Nur Entwürfe können gelöscht werden']); + exit; +} + +$result = $order->delete($user); + +if ($result < 0) { + echo json_encode(['success' => false, 'error' => 'Löschen fehlgeschlagen: ' . $order->error]); + exit; +} + +echo json_encode([ + 'success' => true, + 'message' => 'Bestellung gelöscht' +]); diff --git a/ajax/getorderlines.php b/ajax/getorderlines.php old mode 100644 new mode 100755 index 6e2be51..cc14753 --- a/ajax/getorderlines.php +++ b/ajax/getorderlines.php @@ -82,11 +82,13 @@ foreach ($order->lines as $line) { 'product_id' => (int) $line->fk_product, 'product_ref' => $productRef, 'product_label' => $productLabel, + 'description' => $line->desc ?: '', 'qty' => (float) $line->qty, 'price' => (float) $line->subprice, 'total_ht' => (float) $line->total_ht, 'stock' => $stock, - 'ref_fourn' => $line->ref_fourn ?: '' + 'ref_fourn' => $line->ref_fourn ?: '', + 'is_freetext' => empty($line->fk_product) ? true : false ]; } diff --git a/ajax/getorders.php b/ajax/getorders.php old mode 100644 new mode 100755 diff --git a/ajax/searchproduct.php b/ajax/searchproduct.php old mode 100644 new mode 100755 diff --git a/ajax/updateorderline.php b/ajax/updateorderline.php old mode 100644 new mode 100755 index 139fc3c..5db9c98 --- a/ajax/updateorderline.php +++ b/ajax/updateorderline.php @@ -105,15 +105,19 @@ if ($action === 'delete') { } elseif ($action === 'update') { $newQty = GETPOSTFLOAT('qty'); + $newDesc = GETPOST('description', 'restricthtml'); if ($newQty <= 0) { echo json_encode(['success' => false, 'error' => 'Quantity must be greater than 0']); exit; } + // Use new description if provided, otherwise keep existing + $description = !empty($newDesc) ? $newDesc : $targetLine->desc; + $result = $order->updateline( $lineId, - $targetLine->desc, + $description, $targetLine->subprice, $newQty, $targetLine->remise_percent, diff --git a/css/scanner.css b/css/scanner.css index 8e94692..6db0583 100755 --- a/css/scanner.css +++ b/css/scanner.css @@ -933,8 +933,8 @@ } .order-card.active { - border-color: var(--butactionbg, #0077b3); - background: rgba(0, 119, 179, 0.05); + border-color: var(--butactionbg, #ad8c4f); + background: rgba(173, 140, 79, 0.15); } .order-card.direkt { @@ -955,7 +955,7 @@ .order-card-ref { font-size: 12px; - color: #666; + color: var(--colortextmuted, #b4b4b4); margin-bottom: 4px; } @@ -981,7 +981,41 @@ .order-card-info { margin-top: 8px; font-size: 11px; - color: #888; + color: var(--colortextmuted, #b4b4b4); +} + +/* Order delete button */ +.order-delete-btn { + position: absolute; + top: 8px; + right: 8px; + width: 28px; + height: 28px; + border: none; + background: transparent; + color: var(--colortextmuted, #888); + cursor: pointer; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + opacity: 0.6; +} + +.order-delete-btn:hover { + background: var(--danger, #993013); + color: #fff; + opacity: 1; +} + +.order-delete-btn svg { + width: 16px; + height: 16px; +} + +.order-card { + position: relative; } /* Order detail panel */ @@ -1037,16 +1071,18 @@ align-items: center; justify-content: space-between; padding: 12px; - background: var(--colorbacklinepair1, #f8f8f8); - border: 1px solid var(--colorborder, #ddd); + background: var(--colorbackline, #38393d); + border: 1px solid var(--colorborder, #2b2c2e); border-radius: 6px; margin-bottom: 8px; cursor: pointer; transition: all 0.2s ease; + color: var(--colortext, #dcdcdc); } .order-line:hover { - background: var(--colorbacklinepair2, #f0f0f0); + background: var(--colorbacktitle, #3b3c3e); + border-color: var(--butactionbg, #ad8c4f); } .order-line-info { @@ -1064,7 +1100,7 @@ .order-line-ref { font-size: 11px; - color: #888; + color: var(--colortextmuted, #b4b4b4); margin-top: 2px; } @@ -1092,22 +1128,24 @@ } .line-edit-content { - background: #fff; + background: var(--colorbackcard, #1d1e20); + color: var(--colortext, #dcdcdc); border-radius: 12px; padding: 20px; width: 100%; max-width: 350px; - box-shadow: 0 10px 40px rgba(0,0,0,0.3); + box-shadow: 0 10px 40px rgba(0,0,0,0.5); } .line-edit-title { font-size: 16px; font-weight: 600; margin-bottom: 15px; + color: var(--colortext, #dcdcdc); } .line-edit-info { - background: var(--colorbacklinepair1, #f8f8f8); + background: var(--colorbackline, #38393d); padding: 12px; border-radius: 6px; margin-bottom: 15px; @@ -1115,10 +1153,32 @@ .line-edit-stock { font-size: 13px; - color: #666; + color: var(--colortextmuted, #b4b4b4); margin-bottom: 8px; } +.line-edit-desc { + margin-bottom: 15px; +} + +.line-edit-desc label { + display: block; + font-size: 14px; + font-weight: 500; + margin-bottom: 6px; + color: var(--colortext, #dcdcdc); +} + +.line-edit-desc input { + width: 100%; + padding: 10px; + font-size: 14px; + border: 1px solid var(--colorborder, #2b2c2e); + border-radius: 6px; + background: var(--colorbackinput, #464646); + color: var(--colortext, #dcdcdc); +} + .line-edit-qty { display: flex; align-items: center; @@ -1129,6 +1189,7 @@ .line-edit-qty label { font-size: 14px; font-weight: 500; + color: var(--colortext, #dcdcdc); } .line-edit-qty input { @@ -1137,8 +1198,10 @@ text-align: center; font-size: 16px; font-weight: 600; - border: 1px solid var(--colorborder, #ddd); + border: 1px solid var(--colorborder, #2b2c2e); border-radius: 6px; + background: var(--colorbackinput, #464646); + color: var(--colortext, #dcdcdc); } .line-edit-buttons { diff --git a/js/scanner.js b/js/scanner.js index 39c3935..e51bcd5 100755 --- a/js/scanner.js +++ b/js/scanner.js @@ -46,6 +46,7 @@ let allSuppliers = []; let quaggaInitialized = false; let openDialogCount = 0; // Zaehlt offene Dialoge + let lastFreetextSupplierId = null; // Zuletzt verwendeter Freitext-Lieferant // Order overview state let lastOrderId = null; @@ -865,7 +866,7 @@
@@ -922,6 +923,9 @@ return; } + // Lieferant merken für nächstes Mal + lastFreetextSupplierId = supplierId; + showLoading(); fetch(CONFIG.ajaxUrl + 'addfreetextline.php', { @@ -1037,9 +1041,13 @@ const statusClass = order.status === 0 ? 'draft' : 'validated'; const isActive = order.id === lastOrderId; const direktClass = order.is_direkt ? 'direkt' : ''; + const canDelete = order.status === 0; // Nur Entwürfe können gelöscht werden return `
+ ${canDelete ? `` : ''}
${escapeHtml(order.supplier_name)}
${escapeHtml(order.ref)}
${escapeHtml(order.status_label)}
@@ -1048,13 +1056,88 @@ `; }).join(''); - // Bind click events + // Bind click events for order cards scroller.querySelectorAll('.order-card').forEach(card => { - card.addEventListener('click', function() { + card.addEventListener('click', function(e) { + // Ignoriere Klicks auf den Lösch-Button + if (e.target.closest('.order-delete-btn')) return; const orderId = parseInt(this.dataset.orderId); openOrderDetailInModal(orderId); }); }); + + // Bind delete button events + scroller.querySelectorAll('.order-delete-btn').forEach(btn => { + btn.addEventListener('click', function(e) { + e.stopPropagation(); + const orderId = parseInt(this.dataset.orderId); + const order = cachedOrders.find(o => o.id === orderId); + showDeleteOrderDialog(orderId, order ? order.ref : ''); + }); + }); + } + + function showDeleteOrderDialog(orderId, orderRef) { + const dialog = document.createElement('div'); + dialog.className = 'line-edit-dialog'; + dialog.id = 'delete-order-dialog'; + dialog.innerHTML = ` +
+
Bestellung löschen?
+
+ ${escapeHtml(orderRef)}
+ Diese Aktion kann nicht rückgängig gemacht werden. +
+
+ + +
+
+ `; + document.body.appendChild(dialog); + + function closeDialog() { + dialog.remove(); + } + + document.getElementById('delete-order-cancel').addEventListener('click', closeDialog); + + document.getElementById('delete-order-confirm').addEventListener('click', function() { + closeDialog(); + deleteOrder(orderId); + }); + + dialog.addEventListener('click', function(e) { + if (e.target === dialog) closeDialog(); + }); + } + + function deleteOrder(orderId) { + showLoading(); + + fetch(CONFIG.ajaxUrl + 'deleteorder.php', { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: `token=${CONFIG.token}&order_id=${orderId}` + }) + .then(res => res.json()) + .then(data => { + hideLoading(); + if (data.success) { + showToast('Bestellung gelöscht', 'success'); + loadOrdersIntoModal(); + } else { + showToast(data.error || CONFIG.lang.error, 'error'); + } + }) + .catch(err => { + hideLoading(); + console.error('Delete order error:', err); + showToast(CONFIG.lang.error, 'error'); + }); } function openOrderDetailInModal(orderId) { @@ -1124,15 +1207,24 @@ function showLineEditDialogInModal(line, orderId) { pauseScanner(); + const isFreetext = line.is_freetext || !line.product_id; + const dialog = document.createElement('div'); dialog.className = 'line-edit-dialog'; dialog.id = 'line-edit-dialog'; dialog.innerHTML = `
-
${escapeHtml(line.product_label)}
-
-
Lagerbestand: ${line.stock}
-
+
${isFreetext ? 'Freitext-Position' : escapeHtml(line.product_label)}
+ ${isFreetext ? ` +
+ + +
+ ` : ` +
+
Lagerbestand: ${line.stock}
+
+ `}
@@ -1155,7 +1247,9 @@ document.getElementById('line-save').addEventListener('click', () => { const newQty = parseFloat(document.getElementById('line-qty-input').value) || 1; - updateOrderLineInModal(orderId, line.id, 'update', newQty); + const descInput = document.getElementById('line-desc-input'); + const newDesc = descInput ? descInput.value.trim() : null; + updateOrderLineInModal(orderId, line.id, 'update', newQty, newDesc); closeDialog(); }); @@ -1173,12 +1267,15 @@ }); } - function updateOrderLineInModal(orderId, lineId, action, qty = 0) { + function updateOrderLineInModal(orderId, lineId, action, qty = 0, description = null) { showLoading(); let body = `token=${CONFIG.token}&order_id=${orderId}&line_id=${lineId}&action=${action}`; if (action === 'update') { body += `&qty=${qty}`; + if (description !== null) { + body += `&description=${encodeURIComponent(description)}`; + } } fetch(CONFIG.ajaxUrl + 'updateorderline.php', { @@ -1410,15 +1507,24 @@ function showLineEditDialog(line, orderId) { pauseScanner(); + const isFreetext = line.is_freetext || !line.product_id; + const dialog = document.createElement('div'); dialog.className = 'line-edit-dialog'; dialog.id = 'line-edit-dialog'; dialog.innerHTML = `
-
${escapeHtml(line.product_label)}
-
-
Lagerbestand: ${line.stock}
-
+
${isFreetext ? 'Freitext-Position' : escapeHtml(line.product_label)}
+ ${isFreetext ? ` +
+ + +
+ ` : ` +
+
Lagerbestand: ${line.stock}
+
+ `}
@@ -1441,7 +1547,9 @@ document.getElementById('line-save').addEventListener('click', () => { const newQty = parseFloat(document.getElementById('line-qty-input').value) || 1; - updateOrderLine(orderId, line.id, 'update', newQty); + const descInput = document.getElementById('line-desc-input'); + const newDesc = descInput ? descInput.value.trim() : null; + updateOrderLine(orderId, line.id, 'update', newQty, newDesc); closeDialog(); }); @@ -1459,12 +1567,15 @@ }); } - function updateOrderLine(orderId, lineId, action, qty = 0) { + function updateOrderLine(orderId, lineId, action, qty = 0, description = null) { showLoading(); let body = `token=${CONFIG.token}&order_id=${orderId}&line_id=${lineId}&action=${action}`; if (action === 'update') { body += `&qty=${qty}`; + if (description !== null) { + body += `&description=${encodeURIComponent(description)}`; + } } fetch(CONFIG.ajaxUrl + 'updateorderline.php', { diff --git a/pwa.php b/pwa.php index 88195b7..51a79fb 100755 --- a/pwa.php +++ b/pwa.php @@ -784,10 +784,6 @@ $colormain = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#0077b3'); -
- - diff --git a/pwa_login.php b/pwa_login.php new file mode 100755 index 0000000..65e9046 --- /dev/null +++ b/pwa_login.php @@ -0,0 +1,152 @@ + + * + * AJAX: PWA Login - Authentifiziert Benutzer und gibt Token zurueck + */ + +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOREQUIREAJAX')) { + define('NOREQUIREAJAX', '1'); +} +// Wichtig: Kein Login erforderlich fuer diese Seite +if (!defined('NOLOGIN')) { + define('NOLOGIN', '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.'/user/class/user.class.php'; + +header('Content-Type: application/json; charset=utf-8'); + +// Nur POST erlaubt +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + echo json_encode(['success' => false, 'error' => 'Method not allowed']); + exit; +} + +$login = GETPOST('login', 'alphanohtml'); +$password = GETPOST('password', 'none'); // 'none' = kein Filter fuer Passwort + +if (empty($login) || empty($password)) { + echo json_encode(['success' => false, 'error' => 'Login und Passwort erforderlich']); + exit; +} + +// Benutzer authentifizieren +$userobj = new User($db); +$result = $userobj->fetch('', $login); + +if ($result <= 0) { + // Benutzer nicht gefunden - generische Fehlermeldung aus Sicherheitsgruenden + sleep(1); // Brute-Force-Schutz + echo json_encode(['success' => false, 'error' => 'Login fehlgeschlagen']); + exit; +} + +// Passwort pruefen +$passOk = false; + +// Methode 1: password_verify (moderne Dolibarr-Versionen) +if (function_exists('password_verify') && !empty($userobj->pass_indatabase_crypted)) { + $passOk = password_verify($password, $userobj->pass_indatabase_crypted); +} + +// Methode 2: dol_hash (aeltere Versionen) +if (!$passOk && !empty($userobj->pass_indatabase_crypted)) { + $passOk = (dol_hash($password) === $userobj->pass_indatabase_crypted); +} + +// Methode 3: MD5 (sehr alte Installationen) +if (!$passOk && !empty($userobj->pass_indatabase_crypted)) { + $passOk = (md5($password) === $userobj->pass_indatabase_crypted); +} + +if (!$passOk) { + sleep(1); // Brute-Force-Schutz + echo json_encode(['success' => false, 'error' => 'Login fehlgeschlagen']); + exit; +} + +// Benutzer ist aktiv? +if ($userobj->statut != 1) { + echo json_encode(['success' => false, 'error' => 'Benutzer deaktiviert']); + exit; +} + +// Rechte pruefen +$userobj->getrights(); +$hasAccess = false; + +if ($userobj->hasRight('handybarcodescanner', 'use')) { + $hasAccess = true; +} elseif ($userobj->hasRight('fournisseur', 'commande', 'creer') || $userobj->hasRight('supplier_order', 'creer')) { + $hasAccess = true; +} + +if (!$hasAccess) { + echo json_encode(['success' => false, 'error' => 'Keine Berechtigung fuer Scanner']); + exit; +} + +// Session starten und Benutzer einloggen +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} + +// Dolibarr Session-Variablen setzen +$_SESSION['dol_login'] = $userobj->login; +$_SESSION['dol_authmode'] = 'dolibarr'; +$_SESSION['dol_tz'] = GETPOST('tz', 'alpha'); +$_SESSION['dol_entity'] = $conf->entity; + +// Token generieren fuer 15 Tage +$tokenData = [ + 'user_id' => $userobj->id, + 'login' => $userobj->login, + 'entity' => $conf->entity, + 'created' => time(), + 'expires' => time() + (15 * 24 * 60 * 60), // 15 Tage + 'hash' => bin2hex(random_bytes(16)) +]; + +// Token als Base64-encoded JSON (nicht sicher fuer echte Auth, aber reicht fuer PWA-Cache) +$pwaToken = base64_encode(json_encode($tokenData)); + +// Dolibarr CSRF-Token +$csrfToken = newToken(); + +echo json_encode([ + 'success' => true, + 'pwa_token' => $pwaToken, + 'csrf_token' => $csrfToken, + 'user' => [ + 'id' => $userobj->id, + 'login' => $userobj->login, + 'firstname' => $userobj->firstname, + 'lastname' => $userobj->lastname + ], + 'expires' => $tokenData['expires'], + 'expires_human' => date('Y-m-d H:i:s', $tokenData['expires']) +]); diff --git a/pwa_verify.php b/pwa_verify.php new file mode 100755 index 0000000..4cc6f83 --- /dev/null +++ b/pwa_verify.php @@ -0,0 +1,136 @@ + + * + * AJAX: PWA Token Verify - Prueft gespeicherten Token und startet Session + */ + +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOREQUIREAJAX')) { + define('NOREQUIREAJAX', '1'); +} +if (!defined('NOLOGIN')) { + define('NOLOGIN', '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.'/user/class/user.class.php'; + +header('Content-Type: application/json; charset=utf-8'); + +// Token aus Header oder POST +$pwaToken = ''; +if (!empty($_SERVER['HTTP_X_PWA_TOKEN'])) { + $pwaToken = $_SERVER['HTTP_X_PWA_TOKEN']; +} else { + $pwaToken = GETPOST('pwa_token', 'alphanohtml'); +} + +if (empty($pwaToken)) { + echo json_encode(['success' => false, 'error' => 'Token fehlt', 'need_login' => true]); + exit; +} + +// Token dekodieren +$tokenJson = base64_decode($pwaToken); +if ($tokenJson === false) { + echo json_encode(['success' => false, 'error' => 'Ungueltiger Token', 'need_login' => true]); + exit; +} + +$tokenData = json_decode($tokenJson, true); +if (!$tokenData || !isset($tokenData['user_id']) || !isset($tokenData['expires'])) { + echo json_encode(['success' => false, 'error' => 'Token-Format ungueltig', 'need_login' => true]); + exit; +} + +// Ablauf pruefen +if ($tokenData['expires'] < time()) { + echo json_encode(['success' => false, 'error' => 'Token abgelaufen', 'need_login' => true]); + exit; +} + +// Benutzer laden +$userobj = new User($db); +$result = $userobj->fetch($tokenData['user_id']); + +if ($result <= 0) { + echo json_encode(['success' => false, 'error' => 'Benutzer nicht gefunden', 'need_login' => true]); + exit; +} + +// Benutzer noch aktiv? +if ($userobj->statut != 1) { + echo json_encode(['success' => false, 'error' => 'Benutzer deaktiviert', 'need_login' => true]); + exit; +} + +// Login stimmt ueberein? +if ($userobj->login !== $tokenData['login']) { + echo json_encode(['success' => false, 'error' => 'Token ungueltig', 'need_login' => true]); + exit; +} + +// Rechte pruefen +$userobj->getrights(); +$hasAccess = false; + +if ($userobj->hasRight('handybarcodescanner', 'use')) { + $hasAccess = true; +} elseif ($userobj->hasRight('fournisseur', 'commande', 'creer') || $userobj->hasRight('supplier_order', 'creer')) { + $hasAccess = true; +} + +if (!$hasAccess) { + echo json_encode(['success' => false, 'error' => 'Keine Berechtigung', 'need_login' => true]); + exit; +} + +// Session starten/aktualisieren +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} + +$_SESSION['dol_login'] = $userobj->login; +$_SESSION['dol_authmode'] = 'dolibarr'; +$_SESSION['dol_entity'] = $tokenData['entity'] ?? $conf->entity; + +// Neuen CSRF-Token generieren +$csrfToken = newToken(); + +// Verbleibende Zeit berechnen +$remainingDays = ceil(($tokenData['expires'] - time()) / (24 * 60 * 60)); + +echo json_encode([ + 'success' => true, + 'csrf_token' => $csrfToken, + 'user' => [ + 'id' => $userobj->id, + 'login' => $userobj->login, + 'firstname' => $userobj->firstname, + 'lastname' => $userobj->lastname + ], + 'expires' => $tokenData['expires'], + 'remaining_days' => $remainingDays +]); diff --git a/sw.js b/sw.js index caa2054..c509abe 100755 --- a/sw.js +++ b/sw.js @@ -1,5 +1,5 @@ // Service Worker for HandyBarcodeScanner PWA -const CACHE_NAME = 'scanner-v5.3'; +const CACHE_NAME = 'scanner-v5.4'; const ASSETS = [ 'pwa.php', 'css/scanner.css',