* GPL v3+ * * NOLOGIN-Endpoint für die ElektroPlan-PWA / FastAPI-Backend. * Auth: Header "X-Eplan-Token: " gegen Const EPLAN_PWA_SECRET (hash_equals). * * Actions (?action=...): * - ping → {aktiv: true, version: "1.1.0"} * - auftraege_listen → Liste offener Aufträge (Commande) * - auftrag_details&id= → Einzelner Auftrag mit Positionen * - kunden_suchen&such= → Third-Party-Suche * - dokument_upload (POST) → Plan-PDF an Auftrag/Kunde anhängen */ if (!defined('NOCSRFCHECK')) define('NOCSRFCHECK', '1'); 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'); $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) { http_response_code(500); echo json_encode(['error' => 'main.inc.php not found']); exit; } dol_include_once('/eplan/lib/eplan_token.lib.php'); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store'); // --- Auth --- if (!eplanRateLimit()) { http_response_code(429); echo json_encode(['error' => 'Too Many Requests', 'detail' => 'Zu viele fehlgeschlagene Versuche. Warte 15 Minuten.']); exit; } $token = eplanTokenAusRequest(); if (!eplanTokenPruefen($token)) { eplanRateLimit(false); // Fehlversuch zählen http_response_code(401); echo json_encode(['error' => 'Unauthorized', 'detail' => 'Ungültiger oder fehlender Token']); exit; } eplanRateLimit(true); // Erfolg → Zähler zurücksetzen if (!isModEnabled('eplan')) { http_response_code(503); echo json_encode(['error' => 'Eplan-Modul deaktiviert']); exit; } $action = GETPOST('action', 'aZ09'); try { switch ($action) { case 'ping': echo json_encode([ 'aktiv' => true, 'modul' => 'eplan', 'version' => '1.2.0', 'dolibarr' => DOL_VERSION, ]); break; case 'auftraege_listen': $such = GETPOST('such', 'alphanohtml'); $limit = max(1, min(200, (int) GETPOST('limit', 'int') ?: 50)); $sql = "SELECT c.rowid as id, c.ref, c.ref_client, c.date_commande, c.total_ttc, c.fk_statut, t.nom as kunde FROM ".MAIN_DB_PREFIX."commande c LEFT JOIN ".MAIN_DB_PREFIX."societe t ON t.rowid = c.fk_soc WHERE c.entity IN (".getEntity('commande').")"; if (!empty($such)) { // LIKE-Wildcards im User-Input escapen, dann erst SQL-escape $such_esc = $db->escape(str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $such)); $sql .= " AND (c.ref LIKE '%".$such_esc."%' OR c.ref_client LIKE '%".$such_esc."%' OR t.nom LIKE '%".$such_esc."%')"; } $sql .= " ORDER BY c.date_commande DESC LIMIT ".((int) $limit); $resql = $db->query($sql); $liste = []; if ($resql) { while ($obj = $db->fetch_object($resql)) { $liste[] = [ 'id' => (int) $obj->id, 'ref' => $obj->ref, 'ref_client' => $obj->ref_client, 'kunde' => $obj->kunde, 'status' => (int) $obj->fk_statut, 'datum' => $obj->date_commande, 'total_ttc' => $obj->total_ttc !== null ? (float) $obj->total_ttc : null, ]; } $db->free($resql); } echo json_encode($liste); break; case 'auftrag_details': $id = (int) GETPOST('id', 'int'); if ($id <= 0) { http_response_code(400); echo json_encode(['error' => 'id fehlt']); break; } require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; $c = new Commande($db); if ($c->fetch($id) <= 0) { http_response_code(404); echo json_encode(['error' => 'nicht gefunden']); break; } $c->fetch_thirdparty(); echo json_encode([ 'id' => (int) $c->id, 'ref' => $c->ref, 'ref_client' => $c->ref_client, 'kunde' => $c->thirdparty ? $c->thirdparty->name : null, 'kunde_id' => $c->thirdparty ? (int) $c->thirdparty->id : null, 'datum' => $c->date_commande, 'status' => (int) $c->statut, 'total_ttc' => (float) $c->total_ttc, ]); break; case 'kunden_suchen': $such = GETPOST('such', 'alphanohtml'); if (empty($such) || strlen($such) < 2) { echo json_encode([]); break; } $such_esc = $db->escape(str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $such)); $sql = "SELECT rowid as id, nom, code_client FROM ".MAIN_DB_PREFIX."societe WHERE entity IN (".getEntity('societe').") AND (nom LIKE '%".$such_esc."%' OR code_client LIKE '%".$such_esc."%') ORDER BY nom LIMIT 30"; $resql = $db->query($sql); $liste = []; if ($resql) { while ($obj = $db->fetch_object($resql)) { $liste[] = ['id' => (int) $obj->id, 'name' => $obj->nom, 'code' => $obj->code_client]; } $db->free($resql); } echo json_encode($liste); break; case 'dokument_upload': if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'POST benötigt']); break; } $auftrag_id = (int) GETPOST('auftrag_id', 'int'); if ($auftrag_id <= 0) { http_response_code(400); echo json_encode(['error' => 'auftrag_id fehlt']); break; } if (empty($_FILES['datei'])) { http_response_code(400); echo json_encode(['error' => 'Keine Datei']); break; } require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $c = new Commande($db); if ($c->fetch($auftrag_id) <= 0) { http_response_code(404); echo json_encode(['error' => 'Auftrag nicht gefunden']); break; } $ref_sanitiert = dol_sanitizeFileName($c->ref); $ziel_dir = $conf->commande->dir_output.'/'.$ref_sanitiert; dol_mkdir($ziel_dir); $dateiname = dol_sanitizeFileName($_FILES['datei']['name']); $ziel = $ziel_dir.'/'.$dateiname; $erfolg = dol_move_uploaded_file($_FILES['datei']['tmp_name'], $ziel, 1, 0, 0); if ($erfolg > 0) { echo json_encode(['erfolg' => true, 'pfad' => $ref_sanitiert.'/'.$dateiname]); } else { http_response_code(500); echo json_encode(['error' => 'Upload fehlgeschlagen', 'code' => $erfolg]); } break; default: http_response_code(400); echo json_encode(['error' => 'Unbekannte Action', 'action' => $action]); } } catch (Throwable $e) { dol_syslog('eplan/pwa_api: '.$e->getMessage(), LOG_ERR); http_response_code(500); echo json_encode(['error' => 'Server-Fehler', 'detail' => $e->getMessage()]); } $db->close();