PWA Mobile App für Schaltschrank-Dokumentation vor Ort: - Token-basierte Authentifizierung (15 Tage gültig) - Kundensuche mit Offline-Cache - Anlagen-Auswahl und Offline-Laden - Felder/Hutschienen/Automaten erfassen - Automatische Synchronisierung wenn wieder online - Installierbar auf dem Smartphone Home Screen - Touch-optimiertes Dark Mode Design - Quick-Select für Automaten-Werte (B16, C32, etc.) Schaltplan-Editor Verbesserungen: - Block Hover-Tooltip mit show_in_hover Feldern - Produktinfo mit Icon im Tooltip - Position und Breite in TE Neue Dateien: - pwa.php, pwa_auth.php - PWA Einstieg & Auth - ajax/pwa_api.php - PWA AJAX API - js/pwa.js, css/pwa.css - PWA App & Styles - sw.js, manifest.json - Service Worker & Manifest - img/pwa-icon-192.png, img/pwa-icon-512.png Version: 5.2.0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
333 lines
8.8 KiB
PHP
333 lines
8.8 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* PWA API - AJAX Endpoints für die mobile App
|
|
* Token-basierte Authentifizierung
|
|
*/
|
|
|
|
if (!defined('NOLOGIN')) {
|
|
define('NOLOGIN', '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) {
|
|
die(json_encode(array('success' => false, 'error' => 'Dolibarr not loaded')));
|
|
}
|
|
|
|
header('Content-Type: application/json; charset=UTF-8');
|
|
|
|
$response = array('success' => false);
|
|
|
|
// Verify token
|
|
$token = GETPOST('token', 'none');
|
|
if (empty($token)) {
|
|
echo json_encode(array('success' => false, 'error' => 'Nicht authentifiziert'));
|
|
exit;
|
|
}
|
|
|
|
$tokenData = json_decode(base64_decode($token), true);
|
|
if (!$tokenData || empty($tokenData['user_id']) || empty($tokenData['expires']) || $tokenData['expires'] < time()) {
|
|
echo json_encode(array('success' => false, 'error' => 'Token ungültig oder abgelaufen'));
|
|
exit;
|
|
}
|
|
|
|
// Verify hash
|
|
$expectedHash = md5($tokenData['user_id'] . $tokenData['login'] . getDolGlobalString('MAIN_SECURITY_SALT', 'defaultsalt'));
|
|
if ($tokenData['hash'] !== $expectedHash) {
|
|
echo json_encode(array('success' => false, 'error' => 'Token manipuliert'));
|
|
exit;
|
|
}
|
|
|
|
// Load user
|
|
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
|
$user = new User($db);
|
|
$user->fetch($tokenData['user_id']);
|
|
|
|
if ($user->id <= 0 || $user->statut != 1) {
|
|
echo json_encode(array('success' => false, 'error' => 'Benutzer nicht mehr aktiv'));
|
|
exit;
|
|
}
|
|
|
|
// Check permission
|
|
if (!$user->hasRight('kundenkarte', 'read')) {
|
|
echo json_encode(array('success' => false, 'error' => 'Keine Berechtigung'));
|
|
exit;
|
|
}
|
|
|
|
// Load required classes
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/anlage.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipmentpanel.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipmentcarrier.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipment.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipmenttype.class.php';
|
|
|
|
$action = GETPOST('action', 'aZ09');
|
|
|
|
switch ($action) {
|
|
// ============================================
|
|
// CUSTOMER SEARCH
|
|
// ============================================
|
|
case 'search_customers':
|
|
$query = GETPOST('query', 'alphanohtml');
|
|
if (strlen($query) < 2) {
|
|
$response['error'] = 'Mindestens 2 Zeichen';
|
|
break;
|
|
}
|
|
|
|
$sql = "SELECT s.rowid, s.nom as name, s.town";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
|
|
$sql .= " WHERE s.entity IN (".getEntity('societe').")";
|
|
$sql .= " AND s.status = 1";
|
|
$sql .= " AND (s.nom LIKE '%".$db->escape($query)."%'";
|
|
$sql .= " OR s.name_alias LIKE '%".$db->escape($query)."%')";
|
|
$sql .= " ORDER BY s.nom ASC";
|
|
$sql .= " LIMIT 30";
|
|
|
|
$resql = $db->query($sql);
|
|
$customers = array();
|
|
|
|
if ($resql) {
|
|
while ($obj = $db->fetch_object($resql)) {
|
|
$customers[] = array(
|
|
'id' => $obj->rowid,
|
|
'name' => $obj->name,
|
|
'town' => $obj->town
|
|
);
|
|
}
|
|
}
|
|
|
|
$response['success'] = true;
|
|
$response['customers'] = $customers;
|
|
break;
|
|
|
|
// ============================================
|
|
// GET ANLAGEN FOR CUSTOMER
|
|
// ============================================
|
|
case 'get_anlagen':
|
|
$customerId = GETPOSTINT('customer_id');
|
|
if ($customerId <= 0) {
|
|
$response['error'] = 'Keine Kunden-ID';
|
|
break;
|
|
}
|
|
|
|
$anlage = new Anlage($db);
|
|
$anlagen = $anlage->fetchAll('ASC', 'label', 0, 0, array('fk_soc' => $customerId));
|
|
|
|
$result = array();
|
|
if (is_array($anlagen)) {
|
|
foreach ($anlagen as $a) {
|
|
$result[] = array(
|
|
'id' => $a->id,
|
|
'label' => $a->label,
|
|
'has_editor' => !empty($a->schematic_editor_enabled)
|
|
);
|
|
}
|
|
}
|
|
|
|
$response['success'] = true;
|
|
$response['anlagen'] = $result;
|
|
break;
|
|
|
|
// ============================================
|
|
// GET ANLAGE DATA (Panels, Carriers, Equipment)
|
|
// ============================================
|
|
case 'get_anlage_data':
|
|
$anlageId = GETPOSTINT('anlage_id');
|
|
if ($anlageId <= 0) {
|
|
$response['error'] = 'Keine Anlagen-ID';
|
|
break;
|
|
}
|
|
|
|
// Load panels
|
|
$panel = new EquipmentPanel($db);
|
|
$panels = $panel->fetchByAnlage($anlageId);
|
|
$panelsData = array();
|
|
foreach ($panels as $p) {
|
|
$panelsData[] = array(
|
|
'id' => $p->id,
|
|
'label' => $p->label,
|
|
'position' => $p->position
|
|
);
|
|
}
|
|
|
|
// Load carriers
|
|
$carrier = new EquipmentCarrier($db);
|
|
$carriersData = array();
|
|
foreach ($panels as $p) {
|
|
$p->fetchCarriers();
|
|
foreach ($p->carriers as $c) {
|
|
$carriersData[] = array(
|
|
'id' => $c->id,
|
|
'fk_panel' => $c->fk_panel,
|
|
'label' => $c->label,
|
|
'total_te' => $c->total_te,
|
|
'position' => $c->position
|
|
);
|
|
}
|
|
}
|
|
|
|
// Load equipment
|
|
$equipment = new Equipment($db);
|
|
$equipmentData = array();
|
|
foreach ($carriersData as $c) {
|
|
$items = $equipment->fetchByCarrier($c['id']);
|
|
foreach ($items as $eq) {
|
|
$equipmentData[] = array(
|
|
'id' => $eq->id,
|
|
'fk_carrier' => $eq->fk_carrier,
|
|
'fk_equipment_type' => $eq->fk_equipment_type,
|
|
'label' => $eq->label,
|
|
'position_te' => $eq->position_te,
|
|
'width_te' => $eq->width_te,
|
|
'field_values' => $eq->getFieldValues()
|
|
);
|
|
}
|
|
}
|
|
|
|
// Load equipment types
|
|
$eqType = new EquipmentType($db);
|
|
$types = $eqType->fetchAllBySystem(1, 1); // System 1 = Elektro, nur aktive
|
|
$typesData = array();
|
|
foreach ($types as $t) {
|
|
$typesData[] = array(
|
|
'id' => $t->id,
|
|
'ref' => $t->ref,
|
|
'label' => $t->label,
|
|
'label_short' => $t->label_short,
|
|
'width_te' => $t->width_te,
|
|
'color' => $t->color
|
|
);
|
|
}
|
|
|
|
$response['success'] = true;
|
|
$response['panels'] = $panelsData;
|
|
$response['carriers'] = $carriersData;
|
|
$response['equipment'] = $equipmentData;
|
|
$response['types'] = $typesData;
|
|
break;
|
|
|
|
// ============================================
|
|
// CREATE PANEL
|
|
// ============================================
|
|
case 'create_panel':
|
|
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
$response['error'] = 'Keine Schreibberechtigung';
|
|
break;
|
|
}
|
|
|
|
$anlageId = GETPOSTINT('anlage_id');
|
|
$label = GETPOST('label', 'alphanohtml');
|
|
|
|
if ($anlageId <= 0) {
|
|
$response['error'] = 'Keine Anlagen-ID';
|
|
break;
|
|
}
|
|
|
|
$panel = new EquipmentPanel($db);
|
|
$panel->fk_anlage = $anlageId;
|
|
$panel->label = $label ?: 'Feld';
|
|
|
|
$result = $panel->create($user);
|
|
if ($result > 0) {
|
|
$response['success'] = true;
|
|
$response['panel_id'] = $result;
|
|
} else {
|
|
$response['error'] = $panel->error ?: 'Fehler beim Anlegen';
|
|
}
|
|
break;
|
|
|
|
// ============================================
|
|
// CREATE CARRIER
|
|
// ============================================
|
|
case 'create_carrier':
|
|
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
$response['error'] = 'Keine Schreibberechtigung';
|
|
break;
|
|
}
|
|
|
|
$panelId = GETPOSTINT('panel_id');
|
|
$totalTe = GETPOSTINT('total_te') ?: 12;
|
|
$label = GETPOST('label', 'alphanohtml');
|
|
|
|
if ($panelId <= 0) {
|
|
$response['error'] = 'Keine Panel-ID';
|
|
break;
|
|
}
|
|
|
|
$carrier = new EquipmentCarrier($db);
|
|
$carrier->fk_panel = $panelId;
|
|
$carrier->label = $label ?: 'Hutschiene';
|
|
$carrier->total_te = $totalTe;
|
|
|
|
$result = $carrier->create($user);
|
|
if ($result > 0) {
|
|
$response['success'] = true;
|
|
$response['carrier_id'] = $result;
|
|
} else {
|
|
$response['error'] = $carrier->error ?: 'Fehler beim Anlegen';
|
|
}
|
|
break;
|
|
|
|
// ============================================
|
|
// CREATE EQUIPMENT
|
|
// ============================================
|
|
case 'create_equipment':
|
|
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
$response['error'] = 'Keine Schreibberechtigung';
|
|
break;
|
|
}
|
|
|
|
$carrierId = GETPOSTINT('carrier_id');
|
|
$typeId = GETPOSTINT('type_id');
|
|
$label = GETPOST('label', 'alphanohtml');
|
|
$positionTe = GETPOSTINT('position_te') ?: 1;
|
|
$fieldValues = GETPOST('field_values', 'nohtml');
|
|
|
|
if ($carrierId <= 0 || $typeId <= 0) {
|
|
$response['error'] = 'Carrier-ID und Typ-ID erforderlich';
|
|
break;
|
|
}
|
|
|
|
// Load type for width
|
|
$eqType = new EquipmentType($db);
|
|
$eqType->fetch($typeId);
|
|
|
|
$equipment = new Equipment($db);
|
|
$equipment->fk_carrier = $carrierId;
|
|
$equipment->fk_equipment_type = $typeId;
|
|
$equipment->label = $label;
|
|
$equipment->position_te = $positionTe;
|
|
$equipment->width_te = $eqType->width_te ?: 1;
|
|
$equipment->field_values = $fieldValues;
|
|
|
|
$result = $equipment->create($user);
|
|
if ($result > 0) {
|
|
$response['success'] = true;
|
|
$response['equipment_id'] = $result;
|
|
} else {
|
|
$response['error'] = $equipment->error ?: 'Fehler beim Anlegen';
|
|
}
|
|
break;
|
|
|
|
default:
|
|
$response['error'] = 'Unbekannte Aktion: ' . $action;
|
|
}
|
|
|
|
echo json_encode($response);
|
|
$db->close();
|