235 lines
11 KiB
PHP
235 lines
11 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
$staff = require_admin();
|
||
$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'; ?>
|