Files
deskhub/DeskHub-Webseite/staff/recurring_tickets.php
T
2026-11-05 21:21:52 +00:00

235 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = require_admin_or_permission('manage_automation');
$pdo = get_db();
$departments = departments_for_select($pdo, array_map('intval', $pdo->query('SELECT department_id FROM recurring_tickets')->fetchAll(PDO::FETCH_COLUMN)));
$priorities = $pdo->query('SELECT * FROM priorities ORDER BY sort_order')->fetchAll();
$agents = $pdo->query('SELECT id, name FROM staff WHERE is_active=1 ORDER BY name')->fetchAll();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$action = $_POST['action'] ?? '';
if ($action === 'create' || $action === 'update') {
$subject = trim($_POST['subject'] ?? '');
$message = trim($_POST['message'] ?? '');
$deptId = (int) ($_POST['department_id'] ?? 0);
$prioId = (int) ($_POST['priority_id'] ?? 0);
$assignTo = ($_POST['assigned_staff_id'] ?? '') !== '' ? (int)$_POST['assigned_staff_id'] : null;
$days = max(1, (int) ($_POST['interval_days'] ?? 30));
$nextRun = trim($_POST['next_run'] ?? date('Y-m-d'));
$isActive = !empty($_POST['is_active']) ? 1 : 0;
if ($subject === '') $errors[] = 'Bitte einen Betreff angeben.';
if ($message === '') $errors[] = 'Bitte eine Nachricht eingeben.';
if (!$deptId) $errors[] = 'Bitte eine Abteilung wählen.';
if (!$prioId) $errors[] = 'Bitte eine Priorität wählen.';
if (!$errors) {
if ($action === 'create') {
$pdo->prepare('INSERT INTO recurring_tickets (subject,message,department_id,priority_id,assigned_staff_id,interval_days,next_run,is_active,created_by,created_at) VALUES (?,?,?,?,?,?,?,?,?,?)')
->execute([$subject, $message, $deptId, $prioId, $assignTo, $days, $nextRun, $isActive, $staff['id'], db_now()]);
audit_log($staff['id'], "Wiederkehrendes Ticket erstellt: {$subject}");
flash('success', 'Wiederkehrendes Ticket erstellt.');
} else {
$id = (int) $_POST['id'];
$pdo->prepare('UPDATE recurring_tickets SET subject=?,message=?,department_id=?,priority_id=?,assigned_staff_id=?,interval_days=?,next_run=?,is_active=? WHERE id=?')
->execute([$subject, $message, $deptId, $prioId, $assignTo, $days, $nextRun, $isActive, $id]);
audit_log($staff['id'], "Wiederkehrendes Ticket bearbeitet: {$subject}");
flash('success', 'Gespeichert.');
}
redirect('/staff/recurring_tickets.php');
}
}
if ($action === 'delete') {
$id = (int) $_POST['id'];
$pdo->prepare('DELETE FROM recurring_tickets WHERE id=?')->execute([$id]);
audit_log($staff['id'], 'Wiederkehrendes Ticket gelöscht');
flash('success', 'Gelöscht.');
redirect('/staff/recurring_tickets.php');
}
if ($action === 'toggle') {
$id = (int) $_POST['id'];
$pdo->prepare('UPDATE recurring_tickets SET is_active = 1 - is_active WHERE id=?')->execute([$id]);
redirect('/staff/recurring_tickets.php');
}
if ($action === 'run_now') {
$id = (int) $_POST['id'];
$rt = $pdo->prepare('SELECT * FROM recurring_tickets WHERE id=?');
$rt->execute([$id]); $rt = $rt->fetch();
if ($rt) {
$tn = generate_ticket_number();
$now = db_now();
$nextRun = (new DateTimeImmutable('today'))
->modify('+' . (int) $rt['interval_days'] . ' days')
->format('Y-m-d');
try {
$pdo->beginTransaction();
$pdo->prepare(
'INSERT INTO tickets (ticket_number,subject,requester_name,requester_email,department_id,priority_id,assigned_staff_id,status,source,created_at,updated_at)
VALUES (?,?,?,?,?,?,?,\'open\',\'staff\',?,?)'
)->execute([$tn, $rt['subject'], 'System', 'system@recurring', $rt['department_id'], $rt['priority_id'], $rt['assigned_staff_id'], $now, $now]);
$ticketId = (int) $pdo->lastInsertId();
$pdo->prepare('INSERT INTO ticket_threads (ticket_id,author_type,staff_id,message,is_internal_note,created_at) VALUES (?,\'staff\',?,?,0,?)')
->execute([$ticketId, $staff['id'], $rt['message'], $now]);
$pdo->prepare('UPDATE recurring_tickets SET next_run = ? WHERE id = ?')
->execute([$nextRun, $id]);
$pdo->commit();
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
throw $e;
}
flash('success', "Ticket #{$tn} sofort erstellt.");
}
redirect('/staff/recurring_tickets.php');
}
}
$items = $pdo->query(
'SELECT rt.*, d.name AS dept_name, p.name AS prio_name, s.name AS agent_name
FROM recurring_tickets rt
JOIN departments d ON d.id=rt.department_id
JOIN priorities p ON p.id=rt.priority_id
LEFT JOIN staff s ON s.id=rt.assigned_staff_id
ORDER BY rt.next_run ASC'
)->fetchAll();
$editing = null;
if (!empty($_GET['edit'])) {
$stmt = $pdo->prepare('SELECT * FROM recurring_tickets WHERE id=?');
$stmt->execute([(int)$_GET['edit']]);
$editing = $stmt->fetch();
}
$pageTitle = 'Wiederkehrende Tickets';
$activePage = 'recurring';
require __DIR__ . '/_header.php';
?>
<div class="topbar">
<h1>Wiederkehrende Tickets</h1>
<div class="btn-row">
<a href="?new=1" class="btn">+ Neues Muster</a>
</div>
</div>
<p class="muted">Tickets die automatisch nach einem festen Intervall neu erstellt werden (z.B. monatliche Wartung). Erstellung per Cronjob: <code>php cron/process_recurring.php</code></p>
<?php foreach ($errors as $e): ?><div class="flash error"><?= e($e) ?></div><?php endforeach; ?>
<?php if (!empty($_GET['new']) || $editing): ?>
<div class="card">
<h3><?= $editing ? 'Muster bearbeiten' : 'Neues Muster' ?></h3>
<form method="post">
<?= csrf_field() ?>
<input type="hidden" name="action" value="<?= $editing ? 'update' : 'create' ?>">
<?php if ($editing): ?><input type="hidden" name="id" value="<?= $editing['id'] ?>"><?php endif; ?>
<label>Betreff</label>
<input type="text" name="subject" value="<?= e($editing['subject'] ?? '') ?>" required>
<label>Nachricht (erster Thread-Eintrag)</label>
<textarea name="message" rows="5" required><?= e($editing['message'] ?? '') ?></textarea>
<div class="field-row">
<div>
<label>Abteilung</label>
<select name="department_id" required>
<?php foreach ($departments as $d): ?>
<option value="<?= $d['id'] ?>" <?= ($editing['department_id'] ?? '') == $d['id'] ? 'selected' : '' ?>><?= e($d['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label>Priorität</label>
<select name="priority_id" required>
<?php foreach ($priorities as $p): ?>
<option value="<?= $p['id'] ?>" <?= ($editing['priority_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="field-row">
<div>
<label>Zugewiesen an (optional)</label>
<select name="assigned_staff_id">
<option value="">– nicht zugewiesen –</option>
<?php foreach ($agents as $a): ?>
<option value="<?= $a['id'] ?>" <?= ($editing['assigned_staff_id'] ?? '') == $a['id'] ? 'selected' : '' ?>><?= e($a['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label>Intervall (Tage)</label>
<input type="number" name="interval_days" min="1" value="<?= e($editing['interval_days'] ?? 30) ?>" required>
</div>
<div>
<label>Nächste Ausführung</label>
<input type="date" name="next_run" value="<?= e($editing['next_run'] ?? date('Y-m-d')) ?>" required>
</div>
</div>
<label style="display:flex; align-items:center; gap:8px; margin-top:12px">
<input type="checkbox" name="is_active" style="width:auto" <?= ($editing['is_active'] ?? 1) ? 'checked' : '' ?>>
Aktiv
</label>
<div class="btn-row" style="margin-top:16px">
<button type="submit" class="btn">Speichern</button>
<a href="/staff/recurring_tickets.php" class="btn secondary">Abbrechen</a>
</div>
</form>
</div>
<?php endif; ?>
<div class="card">
<?php if (!$items): ?>
<div class="empty-state">Noch keine Muster angelegt.</div>
<?php else: ?>
<table>
<thead><tr><th>Betreff</th><th>Abteilung</th><th>Intervall</th><th>Nächste Ausführung</th><th>Status</th><th></th></tr></thead>
<tbody>
<?php foreach ($items as $rt): ?>
<tr>
<td><strong><?= e($rt['subject']) ?></strong><br><span class="muted" style="font-size:.78rem"><?= e($rt['agent_name'] ?? '–') ?></span></td>
<td><?= e($rt['dept_name']) ?></td>
<td>Alle <?= $rt['interval_days'] ?> Tage</td>
<td class="<?= $rt['next_run'] <= date('Y-m-d') ? 'muted' : '' ?>"><?= e($rt['next_run']) ?></td>
<td><span class="badge" style="background:<?= $rt['is_active'] ? '#16a34a' : '#6b7280' ?>"><?= $rt['is_active'] ? 'Aktiv' : 'Pausiert' ?></span></td>
<td style="white-space:nowrap">
<a href="?edit=<?= $rt['id'] ?>" class="btn secondary" style="font-size:.78rem; padding:4px 10px">Bearbeiten</a>
<form method="post" style="display:inline">
<?= csrf_field() ?>
<input type="hidden" name="action" value="toggle">
<input type="hidden" name="id" value="<?= $rt['id'] ?>">
<button type="submit" class="btn secondary" style="font-size:.78rem; padding:4px 10px"><?= $rt['is_active'] ? 'Pausieren' : 'Aktivieren' ?></button>
</form>
<form method="post" style="display:inline">
<?= csrf_field() ?>
<input type="hidden" name="action" value="run_now">
<input type="hidden" name="id" value="<?= $rt['id'] ?>">
<button type="submit" class="btn secondary" style="font-size:.78rem; padding:4px 10px" title="Jetzt sofort ein Ticket erstellen">▶ Jetzt</button>
</form>
<form method="post" style="display:inline" onsubmit="return confirm('Wirklich löschen?')">
<?= csrf_field() ?>
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $rt['id'] ?>">
<button type="submit" style="background:none;border:1.5px solid var(--danger);color:var(--danger);border-radius:var(--radius);padding:4px 10px;font-size:.78rem;cursor:pointer">Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php require __DIR__ . '/_footer.php'; ?>