Reopen- & Lifecycle-Governance
Reopen-Governance regelt einheitlich, ob, wie lange, durch wen und wie oft eine geschlossene Entität wieder geöffnet werden darf — über Ticket, Incident und Problem hinweg. Die Regeln sind für alle drei Entitätstypen gleich und je Typ in der Lifecycle-Config einstellbar. Dazu gehören Auto-Close (zeitgesteuertes Schließen gelöster Tickets), WC-Auto-Resolve (Auflösen unbeantworteter Waiting-Customer-Tickets), Stale-Reminder (Inaktivitäts-Eskalation über alle drei Domänen), Reopen-Eskalation (Qualitätssignal bei zu häufigem Wiederöffnen) und ein eigenes Analytics-Dashboard.
🎫 Übersicht für Agents: automatische Ticket-Aktionen
Drei zeitgesteuerte Automatismen wirken auf Tickets — alle vom Admin in den System-Settings konfigurierbar und abschaltbar:
- 1. WC-Auto-Resolve (Waiting for Customer → Resolved): Antwortet der Kunde X Tage nicht, wird das Ticket automatisch auf Resolved gesetzt (Code „Auto-Resolved — No Response"); optional Y Tage vorher eine Vorwarnung an Kunde und Agent. Der Timer läuft ab der letzten öffentlichen Nachricht — egal ob vom Kunden oder vom Agent: eine Erinnerungs-Mail an den Kunden startet ihn neu. Interne Notizen und Feld-Änderungen zählen nicht.
- 2. Stale-Reminder (Inaktivität): Aktive Tickets (Open, In Progress, Waiting for Support), die zu lange unbearbeitet liegen, lösen eine Erinnerung aus (gestaffelt: Bearbeiter → Gruppen-Lead → Manager). Hier setzt jede Änderung am Ticket den Timer zurück — Status-Wechsel, Kommentar, Feld-Änderung, E-Mail-Reply. Status oder SLA werden dabei nie verändert.
- 3. Auto-Close (Resolved → Closed): Tickets im Status Resolved werden nach der konfigurierten Frist automatisch geschlossen — ebenfalls optional mit Vorwarnung.
Alle Fristen liegen in der Lifecycle-Config je Entitätstyp und sind per Default deaktiviert. Technische Details (Config-Keys, Notifications, Jobs) siehe unten.
Ablauf eines Reopens
Ob wiedergeöffnet werden darf, entscheidet eine zentrale Prüfung anhand der Lifecycle-Config. Danach setzt das System die Abschlussfelder zurück (u. a. Resolution-Code, closedAt/resolvedAt), erhöht reopenCount und schreibt Timeline-Eintrag, Audit und Benachrichtigung.
Lifecycle-Config (je Entität, admin-editierbar)
│ reopenEnabled · reopenWindowDays · reopenWindowMode · maxReopenCount
│ reasonRequired · notifyOnReopen · slaOnReopenFrom{Resolved,Closed}
│ autoCloseEnabled · autoResolveWaitingEnabled · staleReminderEnabled · reopenEscalationThreshold
▼
Reopen-Prüfung (Fenster, Limit, Grund, Genehmigung) │ → erlaubt / abgelehnt mit Fehlercode ▼
Zurücksetzen je Entitätstyp Ticket
Incident (+ Genehmigung)
Problem
│ reset closedAt/resolvedAt/resolutionCode · reopenCount++ · reopenedAt
▼
Audit REOPENED · sichtbarer Timeline-Eintrag · Notification (*_REOPENED)
Reopen-Auswertung (Policy-Schritte)
Die Prüfung läuft in fester Reihenfolge ab. reopenOverride umgeht NUR Fenster und Limit — NICHT die Grund-Pflicht und NICHT die Approval-Pflicht (Incident). Auch ein Admin braucht einen Grund.
| Schritt | Prüfung | Override? | Error-Code |
|---|---|---|---|
| 1 | Reopen aktiviert? (reopenEnabled) | — | REOPEN_DISABLED |
| 2 | Quelle terminal? (CLOSED/SPAM/…) | — | REOPEN_NOT_TERMINAL |
| 3 | Innerhalb Reopen-Fenster? (reopenWindowDays ab closedAt) | ✅ umgangen | REOPEN_WINDOW_EXPIRED |
| 4 | Unter Maximalzahl? (reopenCount < maxReopenCount) | ✅ umgangen | REOPEN_LIMIT_REACHED |
| 5 | Gültiger Reopen-Grund? (+ Notiz wenn requiresNote) | ❌ immer Pflicht | REOPEN_REASON_REQUIRED / REOPEN_REASON_INVALID / REOPEN_NOTE_REQUIRED |
| 6 | Approval nötig? (nur Incident, Default an) | ❌ nie umgangen | REOPEN_REQUIRES_APPROVAL |
onWindowExpired (Ticket): Ist das Reopen-Fenster eines Tickets abgelaufen, erzeugt eine Kundenantwort per E-Mail ein neues, mit dem alten verknüpftes Ticket (originContext EMAIL_FOLLOWUP) statt eines Reopens — der Kunde wird nie ausgesperrt.
reopenWindowMode: Das Reopen-Fenster zählt wahlweise in Kalendertagen (CALENDAR, Default) oder Business-Tagen (BUSINESS). BUSINESS zählt nach den Geschäftszeiten der Standard-SLA-Policy des jeweiligen entityType; fehlt eine Default-Policy bzw. BusinessHours-Config, fällt es sicher auf CALENDAR zurück.
Verhalten je Domäne
| Domäne | Quelle → Ziel | Reopen-Pfade | Approval |
|---|---|---|---|
| Ticket | CLOSED/SPAM → OPEN |
Manuell (PATCH), E-Mail-Antwort, Web-Kommentar (System-Reopen) | — |
| Incident | CLOSED → ACKNOWLEDGED |
Dedizierte Route POST /:id/reopen | Ja (Default) — incidents.approveReopen |
| Problem | CLOSED/RESOLVED → INVESTIGATING |
Manuell (PATCH) | — |
Wichtig (Ticket): RESOLVED ist KEINE Reopen-Quelle — der Wechsel von RESOLVED zurück in einen aktiven Status ist ein normaler editStatus-Wechsel (kein Grund, kein Counter), damit die Reopen-KPI sauber bleibt. Reopen-Quellen sind ausschließlich CLOSED und SPAM. Ein Reopen eines gemergten Tickets wird auf das lebende Ziel-Ticket umgeleitet (mergedToTicketId).
Feature-Matrix je Domäne
| Feature | Ticket | Problem | Incident |
|---|---|---|---|
| Reopen-Governance | ✅ (3 Pfade) | ✅ (manuell) | ✅ (manuell + Approval) |
| Auto-Close | ✅ | — | — |
| WC-Auto-Resolve | ✅ | — | — |
| Stale-Reminder | ✅ | ✅ | ✅ |
| Reopen-Eskalation | ✅ | ✅ | ✅ |
| Anzeige Reopen-Fenster | ✅ | ✅ | ✅ |
| SLA config-gesteuert | ✅ | ✅ | ✅ |
Alle Lifecycle-Automationen (Auto-Close, WC-Auto-Resolve, Stale-Reminder, Reopen-Eskalation) sind per Default AUS und werden je Entität in der Lifecycle-Config aktiviert. Die zugehörigen Jobs laufen planmäßig, tun aber nichts, solange die Funktion nicht aktiviert ist.
Permissions
Reopen ist ein eigenes Recht; editStatus und editAll schließen es nicht ein. Eine Rolle mit editAll, aber ohne reopen, kann nicht wiederöffnen.
| Permission-Key | Wirkung |
|---|---|
tickets.reopen · incidents.reopen · problems.reopen | Darf die jeweilige Entität wiederöffnen (mit Grund, im Fenster/Limit) |
tickets.reopenOverride · incidents.reopenOverride · problems.reopenOverride | Umgeht Fenster + Limit (NICHT Grund-Pflicht, NICHT Approval). Standardmäßig nur Admin. |
Die mitgelieferten Standardrollen sind so vorbelegt: Agent hat reopen, Admin hat reopen und reopenOverride, Endanwender und Genehmiger haben keines der beiden Rechte. Einer neu angelegten Rolle müssen beide Rechte ausdrücklich gegeben werden. Permissions & RBAC
Konfiguration (Admin)
Zwei Bereiche steuern die Governance — Admin-Center → Service-Konfiguration → Reopen & Lifecycle (/admin/lifecycle), Recht settings.editGeneral:
- Reopen-Gründe — CRUD je Entität (Label mehrsprachig, Notiz-Pflicht je Grund, Sortierung, Soft-Delete → 204). Fehler: doppelter Code 409 REOPEN_REASON_CODE_EXISTS; letzter aktiver Grund einer Entität 409 REOPEN_REASON_LAST_ACTIVE; unbekannte ID 404 REOPEN_REASON_NOT_FOUND. Inaktive Gründe mitzulesen (?includeInactive=true) erfordert settings.editGeneral; ein Entzug dieses Rechts wirkt sofort.
GET/POST/PATCH/DELETE /api/reopen-reasons - Lifecycle-Config — Fenster (Tage + Modus CALENDAR/BUSINESS), Maximalzahl, Grund-Pflicht je Rolle, notifyOnReopen, SLA-Verhalten bei Reopen (slaOnReopenFromResolved/Closed), Auto-Close, WC-Auto-Resolve, Stale-Reminder-Tiers, Eskalations-Schwelle. Die Settings-UI zeigt je Tab nur die für die Entität sinnvollen Felder (z.B. Auto-Close/WC-Auto-Resolve nur bei Ticket).
GET /api/lifecycle-config·PUT /api/lifecycle-config/:entityType
Restlaufzeit des Reopen-Fensters: GET /api/lifecycle-config/:entityType/reopen-window liefert reopenEnabled/Days/From und braucht nur eine Anmeldung. Die Detail-Seitenleiste zeigt die Restlaufzeit an. Die vollständige Config erfordert settings.editGeneral.
Auto-Close
Auto-Close schließt fällige Tickets zeitgesteuert wie ein normaler Statuswechsel (closedAt, SLA und Verlauf werden gesetzt). Auto-Close gibt es nur für Tickets (autoCloseEnabled, standardmäßig aus). Incidents und Problems werden immer von Hand geschlossen, weil dort eine Genehmigung bzw. ein PIR den Abschluss bestimmt.
| Aspekt | Detail |
|---|---|
| Job | lifecycle_auto_close (täglich ~02:30) |
| Fälligkeit | GREATEST(resolvedAt, neueste nicht-interne Nachricht, config.updatedAt) |
| Grace | Die Frist zählt frühestens ab dem Speichern der Config (config.updatedAt), damit beim ersten Aktivieren nicht der gesamte Altbestand auf einmal geschlossen wird. |
| Vorwarnung | autoCloseWarnDays → Notification TICKET_AUTO_CLOSE_WARNING (Kunde primär); Kundenantwort verschiebt die Fälligkeit → Re-Warn |
WC-Auto-Resolve (Waiting-Customer)
Als Vor-Stufe zu Auto-Close: Tickets im Status WAITING_CUSTOMER, auf die der Kunde nicht reagiert, werden nach einer konfigurierten Frist automatisch auf RESOLVED gesetzt (danach greift ggf. der normale Auto-Close-Pfad). Nur TICKET, opt-in.
| Aspekt | Detail |
|---|---|
| Job | lifecycle_wc_auto_resolve (täglich ~02:00) |
| Fälligkeit | letzte nicht-interne (öffentliche) Nachricht — Kunde ODER Agent (Fallback updatedAt) + autoResolveWaitingDays (Default 14) |
| Vorwarnung | autoResolveWaitingWarnDays (Default 3) → TICKET_WC_RESOLVE_WARNING (Kunde primär) |
| Resolution-Code | autoResolveWaitingCode (Default AUTO_RESOLVED_NO_RESPONSE — notifyCustomer=false, EXCLUDE_FROM_REPORTING) |
| Reset | Kundenantwort → Status WAITING_SUPPORT + wcResolveWarnedAt=null (Timer startet neu) |
Stale-Reminder (Inaktivität)
Inaktivitäts-Erinnerung für TICKET, PROBLEM und INCIDENT. Meldet zu lange unbewegte Vorgänge gestaffelt an Assignee (Stufe 1) → Gruppen-Lead (Stufe 2) → Manager (Stufe 3), jeweils nur an Personen mit Sicht auf den Vorgang. Ändert nie Status oder SLA.
- Job:
lifecycle_stale_entity_reminder(täglich ~01:30) - Config:
staleReminderEnabled,staleReminderDaysTier1/2/3(je Entität) - Notification:
TICKET_STALE_REMINDER/PROBLEM_STALE_REMINDER/INCIDENT_STALE_REMINDER - Ausschlüsse: terminal + je Typ (Ticket: ON_HOLD/WAITING_CUSTOMER/SLA-paused/offener Incident-Link; Problem: ON_HOLD/WAITING_VENDOR; Incident: ON_HOLD/PENDING_CLOSURE)
Reopen-Eskalation
Wird eine Entität öfter als die konfigurierte Schwelle wiederöffnet (reopenCount ≥ reopenEscalationThreshold), werden die Verantwortlichen proaktiv alarmiert — ein Qualitätssignal. Empfänger sind der Assignee plus Gruppen-Lead/Manager, jeweils nur, wenn sie den Vorgang sehen dürfen (wie bei SLA- und Stale-Erinnerungen). Findet sich niemand Berechtigtes, läuft die Eskalation die Management-Kette hoch; findet sich auch dort niemand, wird der Audit-Eintrag REOPEN_ESCALATION_NO_RECIPIENT geschrieben.
- Job:
lifecycle_reopen_escalation(täglich ~03:00) - Schwelle:
reopenEscalationThreshold(null = aus; opt-in je Entität) - Dedup:
reopenEscalatedCount— erneute Eskalation erst bei jedem weiteren Reopen über der Schwelle - Notification:
REOPEN_ESCALATION(intern, nicht customerFacing)
Notifications
| Typ | Empfänger | Hinweis |
|---|---|---|
TICKET_REOPENED | Agent / Gruppe / Kunde / Follower | Kunde erhält generische Variante OHNE internen Grund/Notiz (Redaction) |
INCIDENT_REOPENED | Assignee / Gruppe / Reporter | intern |
PROBLEM_REOPENED | Agent / Gruppe / Reporter | intern |
TICKET_AUTO_CLOSE_WARNING | Kunde primär | Vorwarnung vor Auto-Close |
REOPEN_ESCALATION | Assignee + Lead/Manager (nur mit Sicht auf den Vorgang) | intern, Qualitätssignal |
TICKET_WC_RESOLVE_WARNING | Kunde primär | Vorwarnung vor WC-Auto-Resolve |
TICKET_STALE_REMINDER · PROBLEM_STALE_REMINDER · INCIDENT_STALE_REMINDER | Assignee → Lead → Manager (nur mit Sicht auf den Vorgang) | intern, Inaktivitäts-Eskalation |
Keine doppelte Benachrichtigung: Ein Reopen ist auch ein Statuswechsel. Empfänger erhalten dafür nur die Benachrichtigung *_REOPENED, nicht zusätzlich die allgemeine Statuswechsel-Benachrichtigung. Verlinkte Vorgänge werden über das Cascading-System informiert (LINKED_*_REOPENED). Cascading System
Felder & Analytics
Jede Entität trägt closedAt, reopenCount, reopenedAt und lastReopenReasonCode. Jeder Reopen erzeugt einen sichtbaren REOPENED-Timeline-Eintrag (Grund + Notiz) und ein Audit-Event. Daraus speist sich die Reopen-Analytik:
| Endpoint | Recht | Zweck |
|---|---|---|
GET /api/analytics/reopen | reports.viewAll | viewTeam | KPI über alle 3 Typen: everClosed/reopened/reopenRate/cumulativeReopens/Top-Gründe |
GET /api/analytics/reopen/detail | reports.viewAll | viewTeam | Zeitraum + Breakdown je Agent/Gruppe/Kategorie + Monats-Trend |
In der UI: das Lifecycle-Dashboard unter /lifecycle-analytics (Reporting-Navigation) mit Entity-Tabs, Zeitraum, Summary-Cards, Trend und Ranked-Breakdowns; zusätzlich ein kompaktes Widget zur Reopen-Rate auf dem Haupt-Dashboard.
- Tickets API — Reopen-Pfade, Merge-Redirect, Auto-Close
- Incidents API — POST /:id/reopen + Approval-Flow
- Problems API — manueller Reopen (PATCH)
- Permissions & RBAC — reopen / reopenOverride
- CronJobs API — lifecycle_auto_close / lifecycle_wc_auto_resolve / lifecycle_stale_entity_reminder / lifecycle_reopen_escalation
- Settings API — Reopen-Gründe + Lifecycle-Config
- Cascading System — LINKED_*_REOPENED
- Notifications — *_REOPENED, REOPEN_ESCALATION