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(); $own = !$pdo->inTransaction(); if ($own) $pdo->beginTransaction(); // entweder alles (Zuweisung und Vermerk zur Rückgabe) oder nichts try { $move = $pdo->prepare('UPDATE tickets SET assigned_staff_id = ? WHERE id = ?'); $note = $pdo->prepare('INSERT INTO vacation_moves (ticket_id, from_staff_id, to_staff_id, created_at) VALUES (?,?,?,?)'); foreach ($tickets as $row) { $move->execute([$to, $row['id']]); $note->execute([$row['id'], $staffId, $to, db_now()]); log_activity($pdo, (int) $row['id'], $staffId, 'An die Vertretung ' . $toName . ' weitergegeben (' . $name . ' ist abwesend)'); } if ($own) $pdo->commit(); } catch (Throwable $e) { if ($own && $pdo->inTransaction()) $pdo->rollBack(); throw $e; } if ($tickets) { $list = ''; foreach (array_slice($tickets, 0, 15) as $row) $list .= '
  • #' . e((string) $row['ticket_number']) . ' – ' . e(mb_strimwidth((string) $row['subject'], 0, 70, '…')) . '
  • '; $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( '

    Sie vertreten ' . e($name) . '

    ' . '

    ' . count($tickets) . ' offene Tickets wurden Ihnen zugewiesen. Neue Tickets von ' . e($name) . ' kommen ebenfalls zu Ihnen, solange die Abwesenheit dauert.

    ' . '')); } 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 = []; $ids = array_values(array_filter(array_unique(array_map('intval', $ids)), fn($id) => $id > 0)); if (!$ids) return []; // Wer im Urlaub ist, in einer Abfrage ermitteln (statt eine je Mitarbeiter) $inIds = implode(',', array_fill(0, count($ids), '?')); $away = $pdo->prepare("SELECT id FROM staff WHERE is_on_vacation = 1 AND id IN ($inIds)"); $away->execute($ids); $awayIds = array_flip(array_map('intval', $away->fetchAll(PDO::FETCH_COLUMN))); foreach ($ids as $id) { $target = isset($awayIds[$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)); }