Upload via GUI (360 Dateien)
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* Abwesenheit im Profil: Offene Tickets an die Vertretung weitergeben und nach dem Urlaub zurückgeben.
|
||||
* Das Plugin „Urlaub und Vertretung“ macht dasselbe nach Datum; ist dort eine Abwesenheit aktiv, übernimmt das Plugin und der Server hält sich heraus.
|
||||
* Welche Tickets weitergegeben wurden, steht in vacation_moves: Zurückgegeben wird nur, was noch bei der Vertretung liegt und offen ist
|
||||
* (hat jemand ein Ticket inzwischen umgehängt oder erledigt, bleibt es so).
|
||||
*/
|
||||
|
||||
/** Wer arbeitet an Stelle von $staffId? Folgt Vertretungsketten (höchstens drei Stufen, ohne Schleifen); null ohne nutzbare Vertretung. */
|
||||
function vacation_target(PDO $pdo, int $staffId): ?int
|
||||
{
|
||||
$get = $pdo->prepare('SELECT id, is_active, is_on_vacation, vacation_substitute_id FROM staff WHERE id = ?');
|
||||
$get->execute([$staffId]);
|
||||
$row = $get->fetch();
|
||||
if (!$row || !(int) $row['is_on_vacation']) return null;
|
||||
$seen = [$staffId];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$sub = (int) ($row['vacation_substitute_id'] ?? 0);
|
||||
if ($sub <= 0 || in_array($sub, $seen, true)) return null;
|
||||
$get->execute([$sub]);
|
||||
$row = $get->fetch();
|
||||
if (!$row || !(int) $row['is_active']) return null;
|
||||
if (!(int) $row['is_on_vacation']) return $sub;
|
||||
$seen[] = $sub;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Übernimmt das Plugin „Urlaub und Vertretung“ gerade diese Abwesenheit? */
|
||||
function vacation_plugin_owns(PDO $pdo, int $staffId): bool
|
||||
{
|
||||
try {
|
||||
$q = $pdo->prepare("SELECT COUNT(*) FROM plugin_vertretung_absences WHERE staff_id = ? AND status = 'active'");
|
||||
$q->execute([$staffId]);
|
||||
return (int) $q->fetchColumn() > 0;
|
||||
} catch (Throwable $e) {
|
||||
return false; // Plugin nicht installiert
|
||||
}
|
||||
}
|
||||
|
||||
/** Gibt die offenen Tickets von $staffId an die Vertretung weiter (die Abwesenheit muss schon eingetragen sein). Liefert die Zahl. */
|
||||
function vacation_hand_over(PDO $pdo, int $staffId): int
|
||||
{
|
||||
if (vacation_plugin_owns($pdo, $staffId)) return 0;
|
||||
$to = vacation_target($pdo, $staffId);
|
||||
if ($to === null || $to === $staffId) return 0;
|
||||
$name = (string) $pdo->query('SELECT name FROM staff WHERE id = ' . $staffId)->fetchColumn();
|
||||
$toName = (string) $pdo->query('SELECT name FROM staff WHERE id = ' . $to)->fetchColumn();
|
||||
$t = $pdo->prepare("SELECT id, ticket_number, subject FROM tickets WHERE assigned_staff_id = ? AND is_spam = 0 AND status NOT IN ('resolved','closed') ORDER BY id");
|
||||
$t->execute([$staffId]);
|
||||
$tickets = $t->fetchAll();
|
||||
foreach ($tickets as $row) {
|
||||
$pdo->prepare('UPDATE tickets SET assigned_staff_id = ? WHERE id = ?')->execute([$to, $row['id']]);
|
||||
$pdo->prepare('INSERT INTO vacation_moves (ticket_id, from_staff_id, to_staff_id, created_at) VALUES (?,?,?,?)')->execute([$row['id'], $staffId, $to, db_now()]);
|
||||
log_activity($pdo, (int) $row['id'], $staffId, 'An die Vertretung ' . $toName . ' weitergegeben (' . $name . ' ist abwesend)');
|
||||
}
|
||||
if ($tickets) {
|
||||
$list = '';
|
||||
foreach (array_slice($tickets, 0, 15) as $row) $list .= '<li>#' . e((string) $row['ticket_number']) . ' – ' . e(mb_strimwidth((string) $row['subject'], 0, 70, '…')) . '</li>';
|
||||
$mail = $pdo->prepare('SELECT email FROM staff WHERE id = ?');
|
||||
$mail->execute([$to]);
|
||||
$addr = (string) $mail->fetchColumn();
|
||||
if ($addr !== '') send_mail($addr, 'Vertretung für ' . $name . ': ' . count($tickets) . ' Tickets', email_wrap(
|
||||
'<h2 style="margin:0 0 12px;font-size:20px;color:#1a202c">Sie vertreten ' . e($name) . '</h2>'
|
||||
. '<p style="margin:0 0 12px;color:#334155">' . count($tickets) . ' offene Tickets wurden Ihnen zugewiesen. Neue Tickets von ' . e($name) . ' kommen ebenfalls zu Ihnen, solange die Abwesenheit dauert.</p>'
|
||||
. '<ul style="color:#334155">' . $list . '</ul>'));
|
||||
}
|
||||
return count($tickets);
|
||||
}
|
||||
|
||||
/** Gibt weitergegebene Tickets zurück, die noch bei der Vertretung liegen und offen sind. Liefert die Zahl der zurückgegebenen Tickets. */
|
||||
function vacation_take_back(PDO $pdo, int $staffId): int
|
||||
{
|
||||
if (vacation_plugin_owns($pdo, $staffId)) return 0;
|
||||
$m = $pdo->prepare('SELECT m.id, m.ticket_id, m.to_staff_id, t.assigned_staff_id, t.status FROM vacation_moves m JOIN tickets t ON t.id = m.ticket_id WHERE m.from_staff_id = ? AND m.returned_at IS NULL ORDER BY m.id');
|
||||
$m->execute([$staffId]);
|
||||
$back = 0;
|
||||
$name = (string) $pdo->query('SELECT name FROM staff WHERE id = ' . $staffId)->fetchColumn();
|
||||
foreach ($m->fetchAll() as $row) {
|
||||
$stillThere = (int) $row['assigned_staff_id'] === (int) $row['to_staff_id'] && !in_array($row['status'], ['resolved', 'closed'], true);
|
||||
if ($stillThere) {
|
||||
$pdo->prepare('UPDATE tickets SET assigned_staff_id = ? WHERE id = ?')->execute([$staffId, $row['ticket_id']]);
|
||||
log_activity($pdo, (int) $row['ticket_id'], $staffId, 'Zurück an ' . $name . ' (Abwesenheit beendet)');
|
||||
$back++;
|
||||
}
|
||||
$pdo->prepare('UPDATE vacation_moves SET returned_at = ? WHERE id = ?')->execute([db_now(), $row['id']]);
|
||||
}
|
||||
return $back;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wird nach dem Speichern der Abwesenheit im Profil aufgerufen. $before/$after: [is_on_vacation, vacation_substitute_id].
|
||||
* Beim Speichern mit Haken: weitergeben (auch erneut, es zählen nur Tickets, die noch beim Abwesenden liegen); Ende → zurückgeben; neue Vertretung → erst zurück, dann an die neue.
|
||||
* Liefert [weitergegeben, zurückgegeben].
|
||||
*/
|
||||
function vacation_apply_change(PDO $pdo, int $staffId, array $before, array $after, bool $handOver): array
|
||||
{
|
||||
$handed = $back = 0;
|
||||
$wasOn = (int) $before[0] === 1;
|
||||
$isOn = (int) $after[0] === 1;
|
||||
$changedSub = (int) ($before[1] ?? 0) !== (int) ($after[1] ?? 0);
|
||||
if ($wasOn && (!$isOn || $changedSub)) $back = vacation_take_back($pdo, $staffId);
|
||||
if ($isOn && $handOver) $handed = vacation_hand_over($pdo, $staffId); // idempotent: nur Tickets, die noch beim Abwesenden liegen
|
||||
return [$handed, $back];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersetzt Mitarbeiter im Urlaub durch ihre Vertretung (folgt Ketten), damit Benachrichtigungen dort ankommen, wo jemand arbeitet.
|
||||
* Ohne nutzbare Vertretung entfällt der Eintrag. Ergebnis: eindeutige Mitarbeiter-IDs, die aktiv und anwesend sind.
|
||||
* @param int[] $ids
|
||||
* @return int[]
|
||||
*/
|
||||
function vacation_expand_ids(PDO $pdo, array $ids): array
|
||||
{
|
||||
$out = [];
|
||||
foreach (array_unique(array_map('intval', $ids)) as $id) {
|
||||
if ($id <= 0) continue;
|
||||
$target = staff_is_on_vacation($id) ? vacation_target($pdo, $id) : $id;
|
||||
if ($target !== null) $out[$target] = $target;
|
||||
}
|
||||
if (!$out) return [];
|
||||
$in = implode(',', array_fill(0, count($out), '?'));
|
||||
$q = $pdo->prepare("SELECT id FROM staff WHERE is_active = 1 AND is_on_vacation = 0 AND id IN ($in)");
|
||||
$q->execute(array_values($out));
|
||||
return array_map('intval', $q->fetchAll(PDO::FETCH_COLUMN));
|
||||
}
|
||||
|
||||
/** E-Mail-Adressen der Mitarbeiter-IDs (nur aktive, anwesende). @param int[] $ids @return string[] */
|
||||
function staff_emails_by_ids(PDO $pdo, array $ids): array
|
||||
{
|
||||
$ids = array_values(array_unique(array_map('intval', $ids)));
|
||||
if (!$ids) return [];
|
||||
$in = implode(',', array_fill(0, count($ids), '?'));
|
||||
$q = $pdo->prepare("SELECT DISTINCT email FROM staff WHERE is_active = 1 AND is_on_vacation = 0 AND id IN ($in)");
|
||||
$q->execute($ids);
|
||||
return array_map('strval', $q->fetchAll(PDO::FETCH_COLUMN));
|
||||
}
|
||||
Reference in New Issue
Block a user