diff --git a/api/protocols.php b/api/protocols.php index 6e4da79..5652d25 100644 --- a/api/protocols.php +++ b/api/protocols.php @@ -144,6 +144,18 @@ $protocol->fk_commande = !empty($p['orderId']) ? (int) $p['orderId'] : nul $protocol->date_diag = netdiag_api_timestamp($p['dateDiag'] ?? 0); $protocol->fk_user_techniker = (int) $user->id; $protocol->standort = isset($p['location']) ? (string) $p['location'] : ''; +// Standort leer? Dann aus der Kundenadresse vorbelegen. Der Techniker tippt +// vor Ort selten etwas ein, im Protokoll stand der Ort deshalb fast immer +// leer — obwohl er in Dolibarr am Kunden hinterlegt ist. Eine vom Techniker +// eingetragene Angabe wird NIE überschrieben. +if (trim($protocol->standort) === '' && !empty($protocol->fk_soc)) { + require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + $socTmp = new Societe($db); + if ($socTmp->fetch((int) $protocol->fk_soc) > 0) { + $teile = array_filter(array(trim($socTmp->address), trim($socTmp->zip.' '.$socTmp->town))); + $protocol->standort = substr(implode(', ', $teile), 0, 255); + } +} $protocol->subnet = isset($p['subnet']) ? (string) $p['subnet'] : ''; $protocol->status = isset($p['status']) ? (int) $p['status'] : NetDiagProtocol::STATUS_DRAFT; $protocol->note = isset($p['note']) ? (string) $p['note'] : ''; diff --git a/class/netdiagdevice.class.php b/class/netdiagdevice.class.php index 5aefba5..c5efd10 100644 --- a/class/netdiagdevice.class.php +++ b/class/netdiagdevice.class.php @@ -135,18 +135,24 @@ class NetDiagDevice extends CommonObject */ public function fetchAllByProtocol($fk_protocol) { + // Alle Spalten in EINER Abfrage holen. Vorher wurden nur die rowids + // gelesen und je Zeile ein eigenes fetch() abgesetzt — bei 46 Geräten + // also 47 Abfragen statt einer. Die Kartenansicht und der + // PDF-Generator laden diese Liste jeweils komplett. $result = array(); - $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element; + $sql = "SELECT * FROM ".$this->db->prefix().$this->table_element; $sql .= " WHERE fk_protocol = ".((int) $fk_protocol); + $sql .= " AND entity IN (".getEntity($this->element).")"; $sql .= " ORDER BY INET_ATON(ip), rowid"; $resql = $this->db->query($sql); if ($resql) { while ($obj = $this->db->fetch_object($resql)) { $dev = new self($this->db); - if ($dev->fetch((int) $obj->rowid) > 0) { - $result[] = $dev; - } + $dev->setVarsFromFetchObj($obj); + $dev->id = (int) $obj->rowid; + $result[] = $dev; } + $this->db->free($resql); } return $result; } diff --git a/class/netdiagmeasurement.class.php b/class/netdiagmeasurement.class.php index 85d388b..27c9632 100644 --- a/class/netdiagmeasurement.class.php +++ b/class/netdiagmeasurement.class.php @@ -128,18 +128,21 @@ class NetDiagMeasurement extends CommonObject */ public function fetchAllByProtocol($fk_protocol) { + // Eine Abfrage statt einer je Messung (siehe NetDiagDevice::fetchAllByProtocol) $result = array(); - $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element; + $sql = "SELECT * FROM ".$this->db->prefix().$this->table_element; $sql .= " WHERE fk_protocol = ".((int) $fk_protocol); + $sql .= " AND entity IN (".getEntity($this->element).")"; $sql .= " ORDER BY date_measure, rowid"; $resql = $this->db->query($sql); if ($resql) { while ($obj = $this->db->fetch_object($resql)) { $m = new self($this->db); - if ($m->fetch((int) $obj->rowid) > 0) { - $result[] = $m; - } + $m->setVarsFromFetchObj($obj); + $m->id = (int) $obj->rowid; + $result[] = $m; } + $this->db->free($resql); } return $result; } diff --git a/langs/de_DE/netdiag.lang b/langs/de_DE/netdiag.lang index 5b09fa4..1c2713e 100644 --- a/langs/de_DE/netdiag.lang +++ b/langs/de_DE/netdiag.lang @@ -74,3 +74,4 @@ NetDiagFoundViaPort=Port NetDiagFoundViaArp=ARP NetDiagFoundViaMdns=mDNS NetDiagLastSeen=Zuletzt gesehen +NetDiagOnlyWithFindings=nur mit Befund diff --git a/langs/en_US/netdiag.lang b/langs/en_US/netdiag.lang index 489148e..967956d 100644 --- a/langs/en_US/netdiag.lang +++ b/langs/en_US/netdiag.lang @@ -74,3 +74,4 @@ NetDiagFoundViaPort=Port NetDiagFoundViaArp=ARP NetDiagFoundViaMdns=mDNS NetDiagLastSeen=Last seen +NetDiagOnlyWithFindings=only with findings diff --git a/lib/netdiag.lib.php b/lib/netdiag.lib.php index 49b9c9a..4fa8d38 100644 --- a/lib/netdiag.lib.php +++ b/lib/netdiag.lib.php @@ -430,3 +430,82 @@ function netdiagKundentext($text) } return $text; } + +/** + * Klarname eines Werkzeugs für die Anzeige. + * + * Die kanonischen Namen stehen eigentlich in der App (`Tool.name`), das + * Dolibarr-Modul kann darauf aber nicht zugreifen. Deshalb hier eine bewusst + * kurze Zuordnung statt eines zweiten Satzes Sprachschlüssel — mit Rückfall + * auf die ID, damit ein neues Werkzeug nie einen leeren Titel bekommt. + * + * Enthält auch Werkzeuge, die es in der App nicht mehr gibt („dhcpcheck", + * „wifiscan"): in der Produktionsdatenbank stehen dazu noch Messungen aus + * älteren Protokollen, und die sollen weiterhin lesbar sein. + * + * @param string $tool Werkzeug-ID aus der Messung + * @return string Klarname, oder die ID selbst wenn unbekannt + */ +function netdiagToolName($tool) +{ + $map = array( + 'ipscan' => 'IP-Scanner', + 'portscan' => 'Port-Scan', + 'ping' => 'Ping / Qualität', + 'ipconflict' => 'IP-Konflikt', + 'snmp' => 'SNMP-Switch', + 'traceroute' => 'Traceroute', + 'stresstest' => 'Dauer-/Stresstest', + 'iperf' => 'Durchsatz-Test', + 'wifikanal' => 'WLAN-Kanalanalyse', + 'monitor' => 'Geräte-Monitor', + 'iptest' => 'IP-Test (Dose)', + 'wifitrack' => 'WLAN-Empfang', + // nicht mehr vorhandene Werkzeuge, aber in Altprotokollen enthalten + 'dhcpcheck' => 'DHCP-Prüfung', + 'wifiscan' => 'WLAN-Scan', + ); + return $map[$tool] ?? $tool; +} + +/** + * Messparameter für die Anzeige aufbereiten („Ziel: 192.168.1.1"). + * + * Ohne sie steht im Protokoll zwar das Ergebnis, aber nicht, worauf es sich + * bezieht — bei Prod-Messung #126 war deshalb nicht erkennbar, gegen welches + * Ziel der Dauertest lief. Nur ausgefüllte Parameter erscheinen. + * + * @param string $json JSON-String der Parameter + * @return string lesbarer Text, leer wenn nichts anzuzeigen ist + */ +function netdiagFormatParams($json) +{ + if (empty($json)) { + return ''; + } + $data = json_decode($json, true); + if (!is_array($data) || empty($data)) { + return ''; + } + $labels = array( + 'host' => 'Ziel', + 'subnet' => 'Netzbereich', + 'ip' => 'Gerät', + 'ports' => 'Ports', + 'port' => 'Port', + 'count' => 'Anzahl Proben', + 'durationSec' => 'Dauer (s)', + 'intervalSec' => 'Messabstand (s)', + 'community' => 'SNMP-Community', + 'rounds' => 'Runden', + 'timeoutMs' => 'Zeitgrenze (ms)', + ); + $teile = array(); + foreach ($data as $k => $v) { + if ($v === null || $v === '' || is_array($v)) { + continue; + } + $teile[] = ($labels[$k] ?? ucfirst((string) $k)).': '.$v; + } + return implode(' · ', $teile); +} diff --git a/lib/netdiag_pdf.lib.php b/lib/netdiag_pdf.lib.php index a1de688..674a6f2 100644 --- a/lib/netdiag_pdf.lib.php +++ b/lib/netdiag_pdf.lib.php @@ -181,10 +181,19 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs) // wie ein abgebrochenes Dokument. netdiagPdfEnsureSpace($pdf, 11); $pdf->SetFont('', 'B', 9); - $title = ($m->category ? '['.$m->category.'] ' : '').$m->tool.($m->label ? ' — '.$m->label : ''); + // Klarname statt interner ID — „ipscan" sagt dem Kunden nichts + $title = netdiagToolName($m->tool).($m->label ? ' — '.$m->label : ''); $pdf->Cell(150, 6, dol_trunc($title, 80), 1, 0, 'L'); $pdf->Cell(30, 6, $statusmarks[$st].$statuslabels[$st], 1, 1, 'C', true); $pdf->SetFont('', '', 8); + // Messparameter: ohne sie steht das Ergebnis ohne Bezug da — man sieht + // nicht, gegen welches Ziel gemessen wurde (Prod-Messung #126). + $params = netdiagFormatParams($m->params); + if ($params !== '') { + $pdf->SetFont('', 'I', 7); + $pdf->MultiCell(180, 4, $params, 1, 'L'); + $pdf->SetFont('', '', 8); + } if ($m->tool === 'stresstest') { netdiagPdfStressTest($pdf, $m->result); } elseif ($m->tool === 'wifikanal') { diff --git a/netdiagindex.php b/netdiagindex.php index 36634e2..a87573f 100644 --- a/netdiagindex.php +++ b/netdiagindex.php @@ -67,6 +67,10 @@ if (!$user->hasRight('netdiag', 'protocol', 'read')) { $search_ref = GETPOST('search_ref', 'alpha'); $search_soc = GETPOST('search_soc', 'alpha'); +// „nur mit Befund": zeigt Protokolle, in denen mindestens eine Messung +// Warnung oder Fehler ist — beim Durchsehen vieler Protokolle die eigentliche +// Frage („wo muss ich nochmal ran?") +$search_problem = GETPOST('search_problem', 'int'); $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit; $page = GETPOSTINT('page'); if ($page < 0) { @@ -85,7 +89,16 @@ llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-netdiag page-list'); // Datensätze laden $sql = "SELECT p.rowid, p.ref, p.label, p.date_diag, p.standort, p.subnet, p.status,"; $sql .= " s.rowid as socid, s.nom as socname,"; -$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount"; +$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount,"; +// Schlechteste Einzelmessung als Protokoll-Ampel. Status 3 („nicht messbar") +// darf dabei NICHT als schlechtester Wert gelten — er ist keine Aussage über +// das Kundennetz; deshalb wird er separat gezählt statt in MAX() einzugehen. +$sql .= " (SELECT MAX(m.measure_status) FROM ".$db->prefix()."netdiag_measurement m + WHERE m.fk_protocol = p.rowid AND m.measure_status IN (0,1,2)) as worststatus,"; +$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m + WHERE m.fk_protocol = p.rowid) as meascount,"; +$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m + WHERE m.fk_protocol = p.rowid AND m.measure_status = 3) as unmesscount"; $sql .= " FROM ".$db->prefix()."netdiag_protocol as p"; $sql .= " LEFT JOIN ".$db->prefix()."societe as s ON s.rowid = p.fk_soc"; $sql .= " WHERE p.entity IN (".getEntity('netdiagprotocol').")"; @@ -95,6 +108,10 @@ if ($search_ref) { if ($search_soc) { $sql .= natural_search('s.nom', $search_soc); } +if ($search_problem) { + $sql .= " AND EXISTS (SELECT 1 FROM ".$db->prefix()."netdiag_measurement m2 + WHERE m2.fk_protocol = p.rowid AND m2.measure_status IN (1,2))"; +} $sql .= " ORDER BY p.date_diag DESC, p.rowid DESC"; $sql .= $db->plimit($limit, $offset); @@ -116,7 +133,8 @@ print '
| '; print ' | '; -print ' | '; +print ' | '; +print ' | '; print ' | |||||
| '.$langs->trans("ThirdParty").' | '; print ''.$langs->trans("DateDiag").' | '; print ''.$langs->trans("Location").' | '; print ''.$langs->trans("NetDiagDevices").' | '; +print ''.$langs->trans("NetDiagMeasurements").' | '; print ''.$langs->trans("Status").' | '; print ''.dol_print_date($db->jdate($obj->date_diag), 'dayhour').' | '; print ''.dol_escape_htmltag($obj->standort).' | '; print ''.((int) $obj->devcount).' | '; + // Messungs-Ampel: schlechteste Einzelmessung, plus Hinweis auf nicht + // messbare Punkte. So sieht man in der Liste, wo noch etwas zu tun ist, + // ohne jedes Protokoll einzeln zu öffnen. + print ''; + $meascount = (int) $obj->meascount; + if ($meascount === 0) { + print '-'; + } else { + $worst = ($obj->worststatus === null) ? 3 : (int) $obj->worststatus; + if ($worst < 0 || $worst > 3) { + $worst = 1; + } + $ampellabels = array(0 => 'NetDiagMeasureOk', 1 => 'NetDiagMeasureWarn', 2 => 'NetDiagMeasureFail', 3 => 'NetDiagMeasureUnmeasurable'); + $ampelcss = array(0 => 'badge-status4', 1 => 'badge-status1', 2 => 'badge-status8', 3 => 'badge-status0'); + print ''.$langs->trans($ampellabels[$worst]).''; + print ' '.$meascount; + if ((int) $obj->unmesscount > 0) { + print ', '.((int) $obj->unmesscount).' n.m.'; + } + print ''; + } + print ' | '; print ''.$proto->getLibStatut(3).' | '; print ''; } if (empty($rows)) { - print '
|---|---|---|---|---|---|---|---|---|---|
| '.$langs->trans("NoRecordFound").' | |||||||||
| '.$langs->trans("NoRecordFound").' | |||||||||