prepare('INSERT INTO saved_reports (staff_id, name, config, is_shared, created_at, updated_at) VALUES (?,?,?,?,?,?)') ->execute([$staff['id'], mb_substr($name, 0, 120), json_encode($cfg, JSON_UNESCAPED_UNICODE), !empty($_POST['shared']) ? 1 : 0, $now, $now]); flash('success', 'Bericht gespeichert.'); } redirect('/staff/report_builder.php?' . ltrim((string) ($_POST['return_query'] ?? ''), '?')); } if ($action === 'delete') { $row = $pdo->prepare('SELECT * FROM saved_reports WHERE id = ?'); $row->execute([(int) $_POST['id']]); if (($r = $row->fetch()) && ((int) $r['staff_id'] === (int) $staff['id'] || $staff['role'] === 'admin')) { $pdo->prepare('DELETE FROM saved_reports WHERE id = ?')->execute([$r['id']]); $pdo->prepare('DELETE FROM report_schedules WHERE report_id = ?')->execute([$r['id']]); flash('success', 'Bericht gelöscht.'); } redirect('/staff/report_builder.php'); } if (in_array($action, ['schedule_add', 'schedule_delete', 'schedule_toggle', 'schedule_test', 'schedule_flag'], true)) { $sid = (int) ($_POST['schedule_id'] ?? 0); $own = $pdo->prepare('SELECT * FROM report_schedules WHERE id = ?'); $own->execute([$sid]); $sch = $own->fetch(); $mine = $sch && ((int) $sch['staff_id'] === (int) $staff['id'] || $staff['role'] === 'admin'); if ($action === 'schedule_add') { $err = report_schedule_create($pdo, $staff, $_POST); $err ? flash('error', $err) : flash('success', 'Zeitplan angelegt. Der Bericht wird zum nächsten Termin per E-Mail gesendet.'); } elseif (!$mine) { flash('error', 'Zeitplan nicht gefunden.'); } elseif ($action === 'schedule_delete') { $pdo->prepare('DELETE FROM report_schedules WHERE id = ?')->execute([$sid]); flash('success', 'Zeitplan gelöscht.'); } elseif ($action === 'schedule_flag') { $flag = (string) ($_POST['flag'] ?? ''); if (!in_array($flag, ['only_if_data', 'skip_unchanged', 'weekdays_only'], true)) { flash('error', 'Unbekannte Option.'); } else { $new = (int) $sch[$flag] === 1 ? 0 : 1; $upd = $sch; $upd[$flag] = $new; $pdo->prepare("UPDATE report_schedules SET {$flag} = ?, next_run_at = ? WHERE id = ?")->execute([$new, (int) $sch['enabled'] === 1 ? report_schedule_next($upd) : $sch['next_run_at'], $sid]); flash('success', 'Option gespeichert.'); } } elseif ($action === 'schedule_toggle') { $on = (int) $sch['enabled'] === 1 ? 0 : 1; $pdo->prepare('UPDATE report_schedules SET enabled = ?, next_run_at = ? WHERE id = ?')->execute([$on, $on ? report_schedule_next($sch) : $sch['next_run_at'], $sid]); flash('success', $on ? 'Zeitplan aktiviert.' : 'Zeitplan pausiert.'); } else { $r = report_schedule_send($pdo, $sch); $r['error'] ? flash('error', $r['error']) : flash('success', 'Testversand an ' . $r['sent'] . ' Empfänger gesendet.'); } redirect('/staff/report_builder.php#versand'); } } // ── Konfiguration: aus gespeichertem Bericht oder aus der URL ─────────────── $saved = $pdo->prepare('SELECT r.*, s.name AS owner FROM saved_reports r LEFT JOIN staff s ON s.id = r.staff_id WHERE r.staff_id = ? OR r.is_shared = 1 ORDER BY r.name'); $saved->execute([$staff['id']]); $savedReports = $saved->fetchAll(); $schedStmt = $pdo->prepare("SELECT sc.*, r.name AS report_name FROM report_schedules sc JOIN saved_reports r ON r.id = sc.report_id WHERE sc.staff_id = ? OR ? = 'admin' ORDER BY r.name, sc.id"); $schedStmt->execute([$staff['id'], $staff['role']]); $schedules = $schedStmt->fetchAll(); $ownReports = array_values(array_filter($savedReports, fn($r) => (int) $r['staff_id'] === (int) $staff['id'] || $staff['role'] === 'admin')); $cfgIn = $_GET; if (isset($_GET['saved'])) { foreach ($savedReports as $sr) { if ((int) $sr['id'] === (int) $_GET['saved']) { $cfgIn = json_decode($sr['config'], true) ?: []; break; } } } $cfg = report_clean_config($pdo, $cfgIn); $report = report_run($pdo, $cfg, $scope); $queryOf = fn(array $c) => http_build_query([ 'dimension' => $c['dimension'], 'from' => $c['from'], 'to' => $c['to'], 'sort' => $c['sort'], 'desc' => $c['desc'] ? 1 : 0, 'metrics' => $c['metrics'], 'filters' => $c['filters'], ]); if (($_GET['export'] ?? '') === 'csv') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="bericht-' . $cfg['dimension'] . '-' . $cfg['from'] . '_' . $cfg['to'] . '.csv"'); echo report_to_csv($report); exit; } $departments = $pdo->query('SELECT id, name FROM departments ORDER BY name')->fetchAll(); $priorities = $pdo->query('SELECT id, name FROM priorities ORDER BY sort_order')->fetchAll(); $agents = $pdo->query('SELECT id, name FROM staff WHERE is_active = 1 ORDER BY name')->fetchAll(); $topics = help_topic_options($pdo, false, false); $fmtValue = function ($v, string $unit) { if ($v === null) return '–'; return ($unit === '%' || $unit === 'Std' ? number_format((float) $v, 1, ',', '.') : (string) $v) . ($unit === '%' ? ' %' : ($unit === 'Std' ? ' Std' : '')); }; $maxByMetric = []; foreach (array_slice($report['columns'], 1) as $c) $maxByMetric[$c['key']] = max(1, ...array_map(fn($r) => (float) ($r[$c['key']] ?? 0), $report['rows'] ?: [[$c['key'] => 0]])); $pageTitle = 'Bericht-Baukasten'; $activePage = 'report_builder'; require __DIR__ . '/_header.php'; ?>

Bericht-Baukasten

Standard-Berichte CSV exportieren

Wähle Gruppierung, Kennzahlen, Zeitraum und Filter – nach dem Erstellungsdatum der Tickets. Berichte lassen sich speichern und mit allen teilen.

Gespeichert
$m): ?>
Keine Tickets im gewählten Zeitraum.
Gesamt Ø' : '' ?>
Diesen Bericht speichern
> Versand per E-Mail (Zeitplan)

Ein gespeicherter Bericht wird regelmäßig neu berechnet und als Tabelle an die Empfänger gemailt. Der Zeitraum ist relativ (z. B. „Letzte Kalenderwoche“), die Datumsangaben im gespeicherten Bericht werden dafür ersetzt. Berechnet wird mit Ihren Abteilungsrechten.

BerichtRhythmusZeitraumEmpfängerWeniger MailsNächster VersandZuletzt
, 'Nur bei Daten', 'skip_unchanged' => 'Nur bei Änderung', 'weekdays_only' => 'Nur Mo–Fr'] as $flag => $flagLabel): if ($flag === 'weekdays_only' && $sc['frequency'] !== 'daily') continue; ?>
' . e($sc['last_error']) . '' : ($sc['last_sent_at'] ? e(format_dt((string) $sc['last_sent_at'])) : '–') ?>' . e($sc['last_note']) . '' : '' ?> 'Test senden', 'schedule_toggle' => (int) $sc['enabled'] ? 'Pausieren' : 'Aktivieren', 'schedule_delete' => 'Löschen'] as $act => $lbl): ?>
>
Nur Adressen aktiver Mitarbeiter; externe Empfänger kann ein Administrator eintragen.

Speichern Sie zuerst einen Bericht (unten), um einen Versand einzurichten.