# Plan: In-App-Benachrichtigungen (Voco)

Stand: 2026-08-07 · Status: **FREIGEGEBEN** (Entscheidungen unten eingearbeitet)

## 0. Freigegebene Entscheidungen

- **Datum-UI:** Toggle „Datum festlegen" statt Löschen-Knopf. Aufgaben-Fälligkeit **mit Uhrzeit**
  (`[.date, .hourAndMinute]`, Speicher als ISO-Datetime `yyyy-MM-ddTHH:mm`). Verkaufschance bleibt
  reines Datum.
- **Notification-Set:** #1, #2, #3, #5, #6. **#4 wird KEINE Notification**, sondern erweitert den
  **Welcome-Screen** (Anzahl Besuche · Kilometer · Gesamtfahrzeit).
- **#3 Zeitstempel:** **Server liefert `bericht_erstellt_am` (Datum + Uhrzeit).** Kein Client-Workaround.
- **#1 Traffic-Frische:** On-Device via `BGAppRefreshTask` (PoC), Server-Push als Go-Live-Thema.

## 1. Kurzfassung

Lokale iOS-Benachrichtigungen (kein Server-Push nötig für den PoC). Drei angefragte
Benachrichtigungen + drei Vorschläge, jede per Thema in den Einstellungen an-/abschaltbar,
jede mit definiertem Tap-Ziel (Deep-Link in die App).

**Wichtig vorab:** „Lokal" heißt, das iPhone plant und feuert die Meldung selbst. Das reicht für
Doku-/Freigabe-Erinnerungen (feste Zeitpunkte) problemlos. Nur die **Traffic-Frische** von Meldung 1
ist die eine echte technische Hürde – siehe §4.

## 2. Die Benachrichtigungen

| # | Thema | Auslöser (Trigger) | Text (Beispiel) | Tap-Ziel |
|---|-------|--------------------|-----------------|----------|
| 1 | **Nächster Besuch / Anfahrt** | Zur berechneten Abfahrtszeit zum nächsten Besuch | „Dein nächster Besuch steht an. Fahrzeit: 1 Std 12 Min. Aktuell +14 Min Verzögerung wegen Verkehr." **oder** „…keine verkehrsbedingte Verzögerung." | **Home** |
| 2 | **Doku-Erinnerung** | 15 Min nach Terminende, wenn `bericht_status == "keiner"` | „Dokumentiere jetzt deinen Besuch bei {Kunde}." | **Briefing** des Datensatzes |
| 3 | **Freigabe-Erinnerung** | 15 Min nach Bericht-Erstellung, wenn `bericht_status == "entwurf"` | „Dein Besuchsbericht für {Kunde} erwartet noch deine Freigabe." | **Bericht** des Datensatzes |
| 4 | *(Vorschlag)* **Tages-Briefing** | Morgens / bei erster Tour-Berechnung | „Guten Morgen, {Vorname}. Heute 4 Besuche, erste Abfahrt 07:15, Gesamtfahrzeit 2 Std 10." | **Home** |
| 5 | *(Vorschlag)* **Verspätungs-Warnung** | Wenn kumulierte Verzögerung eine Schwelle (z. B. > 15 Min) überschreitet | „Du wirst dich bei {Kunde} um ~22 Min verspäten. Kunden informieren?" | **Home** |
| 6 | *(Vorschlag)* **Offene Freigaben (Tagesende)** | Abends (z. B. 18:00), wenn Berichte im Status `entwurf` | „2 Berichte warten noch auf deine Freigabe." | **Offen**-Liste |

Vorschläge 5 und 6 nutzen genau die Verzögerungs-/Statuslogik, die im Home-Screen schon existiert –
also wenig Zusatzaufwand. 4 passt thematisch zum Welcome-Screen.

## 3. Datenlage (bereits vorhanden ✓)

- **`Visit.berichtStatus`**: `"keiner" | "entwurf" | "freigegeben"` → deckt „nicht dokumentiert" (#2)
  und „noch nicht freigegeben" (#3) ab. ✓
- **`Visit.status`**: `"geplant" | "erledigt" | "abgesagt"` → Abgesagte werden übersprungen. ✓
- **`Visit.uhrzeit` / `uhrzeitEnde`** → Termin-/Endzeit für #2. ✓
- **Tagesroute** (MKDirections, traffic-aware) → Fahrzeit + `delaySeconds` für #1/#5. ✓

**Eine offene Datenfrage (#3):** „15 Min nach Bericht-Erstellung" braucht einen Erstellungs-Zeitpunkt.
Der Besuchsindex liefert keinen. **PoC-Lösung:** Client merkt sich den Moment, in dem ein Bericht
erstmals als `entwurf` gesehen wird (direkt nach der Doku ist die App ohnehin offen) und plant von da
+15 Min. Sauberer für Go-Live: Server liefert `bericht_erstellt_am`. → Entscheidung nötig.

## 4. Die eine echte Hürde: frischer Traffic für #1

iOS lässt Apps **nicht** dauerhaft im Hintergrund laufen. Drei Realitäten:

- **Der Feuer-Zeitpunkt der Meldung ist exakt** – wir planen sie auf die berechnete Abfahrtszeit,
  das hält iOS zuverlässig ein.
- **Der eingebettete Traffic-Wert** („+14 Min") ist so frisch wie die letzte Routenberechnung.
- Damit er nah am Feuer-Zeitpunkt aktuell ist, brauchen wir **`BGAppRefreshTask`** (BackgroundTasks):
  iOS gewährt gelegentliche Hintergrund-Fenster, in denen wir Route+Traffic neu ziehen und die
  Meldung neu planen. **Timing dieser Fenster bestimmt iOS** (opportunistisch, nicht garantiert).

**Konsequenz / ehrliche Erwartung:**
- On-Device-PoC: Meldung feuert pünktlich; der Verzögerungs-Wert stammt aus der letzten
  Hintergrund-Aktualisierung (Minuten bis evtl. länger alt). Für die Demo völlig ausreichend.
- **Garantiert live** ginge nur mit **Server + APNs-Push** (Backend rechnet Traffic, schickt Push) –
  außerhalb PoC-Scope, passt aber in die bereits notierte Go-Live-/Prod-Architektur.

## 5. Technische Voraussetzungen (was neu gebaut/konfiguriert wird)

1. **Notification-Permission**: Einmal-Abfrage (`UNUserNotificationCenter.requestAuthorization`).
   Fallback, wenn abgelehnt: Hinweis in den Einstellungen mit Link in die iOS-Systemeinstellungen.
2. **Info.plist** (via xcodegen `project.yml`): `UIBackgroundModes` → `fetch` (+ ggf. `processing`),
   und `BGTaskSchedulerPermittedIdentifiers` mit unserer Task-ID.
3. **`NotificationService`** (neuer, kleiner Baustein): plant/storniert die Meldungen idempotent
   aus (heutige Besuche + Tagesroute + Einstellungen). „Neu berechnen → alles neu planen."
4. **`BackgroundTasks`-Wiring**: Task registrieren + planen; Handler zieht Route/Traffic neu und
   plant die Meldungen neu (nur für #1/#5 relevant).
5. **Deep-Link-Routing**: `userInfo = {typ, visitId}`; `UNUserNotificationCenterDelegate` setzt ein
   „pending Deep-Link", das der App-Root (`RootView`/`MainTabView` + vorhandener `Navigator`/`Route`)
   beim Erscheinen abarbeitet → Home / Briefing / Bericht.
6. **Einstellungen** (Profil-Tab): Abschnitt „Benachrichtigungen" mit **je Thema ein Toggle**
   (persistiert via `@AppStorage`/UserDefaults). Master-Zustand spiegelt die Systemberechtigung.

## 6. Umsetzungs-Reihenfolge (jeweils mit Verifikation)

1. **Gerüst**: `NotificationService` + Permission + Einstellungen-Toggles (Profil).
   → *verify:* Toggles schalten, Permission-Prompt erscheint einmal.
2. **#2 Doku + #3 Freigabe** (die einfachen, festen Zeitpunkte) + Deep-Links Briefing/Bericht.
   → *verify:* Termin datieren, Meldung feuert nach 15 Min (Test: kurze Intervalle), Tap landet richtig.
3. **#1 Nächster Besuch/Anfahrt** als lokale Meldung zur Abfahrtszeit (Traffic aus letzter Berechnung).
   → *verify:* Abfahrtszeit gesetzt, Meldung feuert, Tap → Home.
4. **`BGAppRefreshTask`**: Hintergrund-Neuberechnung + Neuplanung von #1.
   → *verify:* Xcode „Simulate Background Fetch", Route/Traffic aktualisiert.
5. **Vorschläge 4–6**, sofern gewünscht.
   → *verify:* je Thema Toggle + Feuern.

**Visuelle Prüfung** (Simulator-Optik, Toast-Look, Deep-Link-Sprung) übernimmt Elmar; ich baue,
kompiliere, mache headless-Prüfungen und pinge zum Anschauen.

## 7. Offene Entscheidungen (für die Freigabe)

- **A.** Welche Vorschläge (4/5/6) mit rein – oder erst nur die drei angefragten?
- **B.** #1 Traffic-Frische: On-Device via `BGAppRefreshTask` (PoC-tauglich) jetzt, Server-Push später?
- **C.** #3 Erstellungs-Zeitpunkt: Client-seitig (PoC) genügt, oder Server-Feld `bericht_erstellt_am`?
- **D.** Reihenfolge: erst #2/#3 (einfach, hoher Nutzen), dann #1 – ok?
