Vorab: drei Punkte der Roadmap-Liste waren Fehlannahmen. Eine Analyse mit
anschließender Gegenprüfung (jeder Befund musste einen Widerlegungsversuch
überstehen) hat sie ausgeräumt, bevor Code geändert wurde:
- "ab Seite 2 alles nach rechts verschoben" existiert nicht. Nachgemessen am
Prod-PDF ND2026-0015 mit pdftotext -bbox: Seite 1 und Seite 2 beginnen beide
bei 16,0 mm. Die echten Umbruchfehler waren andere.
- measure_status validieren war seit Phase 1 erledigt.
- Werkzeug-IDs / Teilnetz-Gruppierung / TCPDF-Fußzeile: verworfen, die
vorgeschlagenen Änderungen hätten das PDF verschlechtert.
PDF (alle Punkte am mehrseitigen Dokument nachgeprüft):
- Tabellenkopf der Geräteliste wird auf Folgeseiten wiederholt. Vorher standen
ab Seite 2 unbeschriftete Spalten — bei leeren MAC/Hostname-Feldern vier
namenlose Spalten.
- Messungs-Titelzeile und Ergebnis werden zusammengehalten. Vorher blieb die
Überschrift samt Ampel am Seitenende allein zurück, darunter ein leerer,
unten offener Rahmen; in einem Testlauf über 61 Umbruchlagen 5-mal (~8 %).
- Spalte "Gerätetyp" hatte 15 mm, ließ aber 10 Zeichen zu — "Chromecast/TV"
lief bis 199,0 mm bei 195 mm Tabellenkante über den Rahmen in den Druckrand.
- Deutsche Bezeichnungen mit Einheiten statt roher JSON-Schlüssel: aus
"VerlustProzent: 0 | MinMs: 4.4 | UptimeSek: 8123456" wird "Paketverlust: 0 %
| Kürzeste Antwortzeit: 4.4 ms | Betriebszeit: 94 Tage 1 Std". Als Whitelist
(netdiagKundenfelder(), gemeinsam für Karte und PDF) — interne Felder wie
arpAvailable, mdnsOk, probed, answered fallen damit automatisch heraus.
- "ARP-Tabelle nicht lesbar (/proc/net/arp) — braucht Root" wird beim Drucken
zu einem kundentauglichen Satz. Altdaten stehen so in der DB, deshalb
Ersetzung beim Drucken statt nur in der App.
Gerätemerkmale (der eigentliche Roadmap-Punkt): Der Techniker sah in der App
"Drucker HP, Port 9100", im Kundenprotokoll stand nur die IP. Die Felder
fehlten dabei nicht in der Übertragung, sondern durchgängig — ein Fix allein
in der API wäre folgenlos geblieben, weil Dolibarrs setSaveQuery() nur
deklarierte $fields schreibt. Ergänzt über die ganze Kette:
sql/llx_netdiag_device.sql + neue Migration llx_netdiag_device_v2.sql
(ADD COLUMN IF NOT EXISTS, wiederholbar, läuft bei jedem Modul-Update),
NetDiagDevice::$fields + Properties, api/protocols.php POST und GET,
Kartenansicht und PDF.
Neu: netbios_name, mdns_name, mdns_services, custom_name, open_ports,
found_via, last_seen. Im PDF steht jetzt statt "192.168.178.20" die Zeile
"Brother HL-L2350DW · Brother · Drucker · 80,443,9100" — der Name kommt aus
mDNS, obwohl der Hostname leer ist.
Sprachschlüssel Vendor -> NetDiagVendor: Die Gegenprüfung hielt den Punkt für
falsch (Translate::load() ist first-wins, im CLI-Test kam "Hersteller"), im
Browser stand in der Kartenansicht aber "Lieferant" — im HTTP-Kontext lädt
Dolibarr vorher andere Sprachdateien als im CLI. Statt der Ursache nachzugehen
jetzt ein eigener, kollisionsfreier Schlüssel; im Browser gegengeprüft.
Nebenbei: doppeltes "OK OK" beim Status 0 im PDF.
Gegen das Test-Dolibarr geprüft: Sync über die echte API (Login, POST, GET),
Felder in der DB kontrolliert, Kartenansicht im Browser, mehrseitiges PDF
gerendert und angesehen, Migration zweimal ausgeführt (idempotent).
Die App kennt seit Phase 1 eine vierte Bewertung: der Test konnte gar nicht
durchgefuehrt werden (Switch antwortet nicht auf SNMP, kein Netz, Gegenstelle
fehlt). Ohne diesen Zustand musste sich ein fehlgeschlagener Test als ok, warn
oder fail ausgeben — in der Praxis meist als Gruen.
- protocols.php nimmt nur noch Bewertungen 0-3 an. Ein unbekannter Wert landete
bisher ungeprueft als Array-Index in Karte und PDF und lief dort ins Leere;
jetzt wird daraus eine Warnung, nie ein OK.
- Protokollkarte und PDF stellen Status 3 grau dar ("Nicht messbar") — weder
gruen (waere gelogen) noch rot (waere eine Aussage ueber das Kundennetz, die
die Messung nicht hergibt).
- Das PDF zeigt die Ampel zusaetzlich als Zeichen (OK / ! / X / ?), damit sie im
Schwarz-Weiss-Ausdruck beim Kunden lesbar bleibt.
- Sprachschluessel NetDiagMeasureUnmeasurable in de_DE und en_US.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DER eigentliche Fehler: Die App schickt dateDiag/dateMeasure als
JavaScript-Millisekunden (Date.now(), 13-stellig). Dolibarrs idate()
erwartet Unix-Sekunden -> MySQL: "Incorrect datetime value: Bad value
1779211311036 for date" -> createCommon scheitert -> HTTP 500.
Fix: netdiag_api_timestamp() rechnet ms-Zeitstempel (> 1e11) auf Sekunden
um. protocols.php nutzt sie fuer date_diag und date_measure.
Serverseitig bewusst — so synchronisieren auch bereits installierte
App-Versionen ohne APK-Update korrekt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Der Endpoint gab bei Speicherfehlern nur $obj->error (Singular) aus — das
ist leer, weil CommonObject::createCommon den Grund nach $obj->errors[]
(Array) schreibt. Ergebnis: "Protokoll speichern fehlgeschlagen: " ohne
Grund. Jetzt errorsToString() — liefert error + errors[] zusammen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nachtrag zum tms-Fix: explicit_defaults_for_timestamp=1 auf der Prod-DB —
ein INSERT mit tms=NULL in die NOT-NULL-Spalte schlaegt fehl. createCommon
fuegt tms aber als NULL ein, wenn die Property leer ist.
Loesung: protocols.php setzt tms = dol_now() vor jedem create/update von
Protokoll, Geraet und Messung. Damit landet ein gueltiger Zeitstempel im
INSERT, kein NULL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Netzwerk-Diagnose-Modul mit JSON-API für die NetDiag-App:
- 3 Tabellen (protocol/device/measurement), generisches JSON-result
- JSON-API: auth, customers, orders, protocols (idempotenter Sync), pdf
- JWT-Auth (HS256), CORS für die Capacitor-App
- Tabs an Thirdparty + Auftrag, Protokoll-Card, PDF-Generator
- QR-Code zum App-Download in der Modul-Konfiguration
- de_DE + en_US, Rechtesystem netdiag->protocol read/write/delete
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>