# Badge-Query: Überfällige & eskalierte Aufgaben pro Benutzer Gibt die Anzahl offener Aufgaben zurück, die **überfällig** oder **eskaliert** sind — ideal für einen Navigations-Badge. ## Query ```sql SELECT COUNT(*) AS badge_count FROM task_assignment ta INNER JOIN task_definition td ON td.id = ta.task_id AND td.active = 1 WHERE ta.main_contact_id = :contact_id AND ta.status IN ('overdue', 'open', 'in_progress', 'approval_pending', 'rejected') AND ( ta.status = 'overdue' OR ta.escalation_level > 0 OR (ta.due_at IS NOT NULL AND ta.due_at < NOW()) ) ``` ## Warum diese Bedingungen? | Bedingung | Grund | |---|---| | `td.active = 1` | Deaktivierte Aufgaben sollen keinen Badge erzeugen | | `ta.status IN (...)` | Nur nicht-abgeschlossene Status — `completed` und `expired` sind erledigt | | `ta.status = 'overdue'` | Wurde bereits vom EscalationService als überfällig markiert | | `ta.escalation_level > 0` | Eskalierte Aufgaben unabhängig vom Status (z.B. `open` aber schon eskaliert) | | `ta.due_at < NOW()` | Safety-Net: fängt Aufgaben, bei denen der Cronjob den Status noch nicht aktualisiert hat | ## Prepared-Statement (PHP) ```php $count = TaskCertDb::fetchOne( "SELECT COUNT(*) AS badge_count FROM task_assignment ta INNER JOIN task_definition td ON td.id = ta.task_id AND td.active = 1 WHERE ta.main_contact_id = ? AND ta.status IN ('overdue','open','in_progress','approval_pending','rejected') AND ( ta.status = 'overdue' OR ta.escalation_level > 0 OR (ta.due_at IS NOT NULL AND ta.due_at < NOW()) )", 'i', $contactId ); $badgeCount = (int) ($count['badge_count'] ?? 0); ``` ## Variante: Aufschlüsselung nach Typ Falls du im Badge oder Tooltip zwischen überfällig und eskaliert unterscheiden willst: ```sql SELECT SUM(ta.status = 'overdue' OR (ta.due_at IS NOT NULL AND ta.due_at < NOW())) AS overdue_count, SUM(ta.escalation_level > 0) AS escalated_count, COUNT(*) AS total_badge_count FROM task_assignment ta INNER JOIN task_definition td ON td.id = ta.task_id AND td.active = 1 WHERE ta.main_contact_id = :contact_id AND ta.status IN ('overdue', 'open', 'in_progress', 'approval_pending', 'rejected') AND ( ta.status = 'overdue' OR ta.escalation_level > 0 OR (ta.due_at IS NOT NULL AND ta.due_at < NOW()) ) ``` ## Performance Der Index `idx_task_assignment_contact_status_due (main_contact_id, status, due_at)` deckt diese Query optimal ab. Kein Full-Table-Scan nötig.