# Entwicklungsplan mein.sterntours.de (2026) **Erstellt:** April 2026 **Basis:** [briefings.md](./briefings.md), [customer-bookings/](./customer-bookings/), [audit-april-2025.md](./audit-april-2025.md), [projekt-empfehlungen-2026-04.md](./projekt-empfehlungen-2026-04.md), [frontend-navigation/](./frontend-navigation/) **Ziel:** Konsolidierung der drei Anwendungen (`mein.sterntours.de` v3, `sterntours.de` Frontend-Backend, `v2.stern-tours.de` Reiseverwaltung) in **ein** modernes Laravel-10-CRM; Ablösung der Altsysteme; neue Kernfunktion „Angebote". --- ## 0. Einordnung und Grundprinzipien ### 0.1 Strategische Leitplanken - **Ein System als Zielbild:** `mein.sterntours.de` (Laravel 10) wird das einzige Backend. `sterntours.de` bleibt als Frontend-Auslieferung bestehen, das Redaktions-/Admin-Backend darin entfällt. `v2.stern-tours.de` wird komplett abgelöst. - **Schrittweise, rückwärtskompatibel:** Jede Phase muss produktiv deploybar sein, ohne dass andere Module brechen. Parallelbetrieb alt/neu wo nötig. - **Migrationen immer mit Rollback:** Jede DB-Migration hat einen `down()`-Pfad, Drop-Migrationen laufen erst nach bestätigter Stabilität. - **UI-Baseline festlegen:** Bevor die Modernisierung in die Breite geht, wird ein verbindlicher UI/UX-Standard definiert (Listen, Detailseiten, Formulare, Modals, Toolbar, Filter, Inline-Edit, Tabs). Alle neuen Module werden daran ausgerichtet; Altmodule ziehen nach. - **Business-Logik in Services, nicht in Controllern.** Neue Module nutzen konsequent den bestehenden Schichtenaufbau (Controller → FormRequest → Repository → Service → Model). ### 0.2 Grob-Roadmap (Reihenfolge, high-level) ``` Block A – Fundament (Voraussetzung für alles Weitere) A1 UI/UX-Baseline definieren A2 Technisches Aufräumen aus Audit (API-Key, Frontend-Tooling-Entscheidung, Pint/Larastan, Queue) A3 Customer/Lead/Booking-Neustrukturierung Phasen 2–4 abschließen Block B – Kerngeschäft (sichtbare Verbesserungen für Mitarbeiter) B1 Backend-Direktbuchung (Reise + Fewo) B2 Organisations-Tab Buchung überarbeiten B3 E-Mail-System vereinheitlichen + Auto-Save B4 Fewo-Doppelbuchung / Belegungs-Bug beheben B5 Angebots-Modul (zentrale neue Funktion) Block C – Ablösung der Altsysteme C1 Admin-Controller sterntours.de → mein.sterntours.de migrieren C2 Navigation + Page-Modell refactoren C3 CMS-Module vereinheitlichen (Navigation / Benutzerführung) C4 Reisenverwaltung v2.stern-tours.de migrieren (größtes Modul) Block D – Betrieb & Langfristpflege D1 Tests, Monitoring, Deployment D2 Framework-/PHP-Upgrade-Pfad (Laravel 11, PHP 8.3+) Block E – Kundenseitige Self-Service-Funktionen (Zukunftsmodul) E1 Kundenportal / Kunden-Login (Modul 15) ``` Die genannten Module sind jeweils unten ausführlich beschrieben. Block A ist zwingende Voraussetzung, danach können B und C teilweise parallelisiert werden. --- ## 1. Modul 1 — UI/UX-Baseline (Block A1) **Ziel:** Einmalig festlegen, wie eine „moderne, einheitliche Seite" im Backend aussieht. Davon leitet sich alles andere ab. ### 1.1 Analyse / Inventar - Screenshots aller Haupt-Listen (Buchungen, Anfragen, Fewo-Buchungen, Kunden, Kontakte, CMS-Listen) aufnehmen und Abweichungen dokumentieren (Filter oben / Filter als Sidebar / Suchfeld, Inline-Edit ja/nein, Pagination-Stil, Table-Buttons). - Detailseiten (Anfrage-Detail, Buchung-Detail, Fewo-Buchung-Detail) strukturell vergleichen: Welche Sektionen / Tabs / Boxen existieren, welche sind identisch, welche unterscheiden sich unnötig? - Typografie, Abstände, Form-Controls, Modal-Varianten auflisten. ### 1.2 Baseline-Definition - Einheitliches Grid, einheitliche Toolbar (links Filter / Suche, rechts Aktionen). - Einheitliches DataTable-Pattern (Filter → Spalten → Badge-Logik → Zeilenaktionen). - Einheitliches Tab-Layout auf Detailseiten (linke Sidebar mit Stammdaten / Zähler, rechte Tab-Area). - Einheitliche Modals (Bestätigung, Formular, History). - Einheitliche Status-/Fehler-/Erfolgs-Toasts. - Komponentenbibliothek als Blade-Components unter `resources/views/components/ui/` (Button, Card, Tabs, Toolbar, FormRow, Modal, Toast). ### 1.3 Pilot-Umsetzung - Bestehende **Contacts-Liste** (bereits gut, Phase 1 abgeschlossen) als Referenz auf den Baseline-Stand anheben. - Eine weitere Ansicht (Vorschlag: Buchungsliste) auf die Baseline portieren, um das Pattern zu validieren, bevor die Breite umgestellt wird. ### 1.4 Frontend-Tooling-Migration (parallel, blockiert den Pilot nicht) **Zielkorridor (Entscheidung Abschnitt 17.7):** Vite + dart-sass + schrittweise Bootstrap 5. - Laravel Mix 2 → **Vite** als Build-Tool. - `node-sass` → **dart-sass**. - **Bootstrap 4 → 5 schrittweise:** erst Infrastruktur umstellen (Vite/Sass), Bootstrap 5 als Ziel im gleichen Branch parallel einziehen; Views ziehen **modulweise** nach, nicht in einem Big-Bang-Rewrite. - Komponentenbibliothek aus 1.2 wird direkt auf Bootstrap 5 aufgesetzt — so zieht jedes neu gebaute Modul (ab Modul 4 aufwärts) automatisch in die neue Welt um. - Alte Views bleiben in Bootstrap 4 lauffähig, bis ihr jeweiliges Modul umgebaut wird. - Als eigener Feature-Branch, erst mergen, wenn Pilot-View auf neuer Pipeline läuft und parallel zu Bootstrap-4-Views ko­existieren kann. **Aufwand:** 2–3 Wochen (inkl. Komponenten-Library, Pilot-View). **Abhängigkeiten:** keine. Startmodul. **Deliverables:** Baseline-Dokument, Komponentenbibliothek, 2 Referenz-Views. --- ## 2. Modul 2 — Technische Hausaufgaben (Block A2) Aus [audit-april-2025.md](./audit-april-2025.md) und [projekt-empfehlungen-2026-04.md](./projekt-empfehlungen-2026-04.md), bevor größere Module starten. Alle klein / mittel, aber Voraussetzung für einen sauberen Weiterbau. | # | Maßnahme | Priorität | Aufwand | |---|----------|-----------|---------| | 2.1 | Composer-Wildcards (`"*"`) durch feste Versionen ersetzen | Hoch | klein | | 2.2 | `navigation/cache/clear`-Endpoint absichern (Auth/Secret/IP) | Hoch | klein | | 2.3 | `booking/import`: GET entfernen, nur POST; Rate-Limit | Hoch | klein | | 2.4 | `MailDirService`-Nutzung in Booking-/Lead-Services konsolidieren (war im Audit offen) | Mittel | mittel | | 2.5 | API-Routen auf Klassen-Syntax (`[Controller::class, 'method']`) | Niedrig | klein, laufend | | 2.6 | Laravel Pint + Larastan + CI-Pipeline | Mittel | mittel | | 2.7 | PHPUnit: zweite SQLite-Connection für `mysql_stern` + Factories für Customer/Booking/Lead | Mittel | mittel | | 2.8 | Queue-Worker (Supervisor) + `Mail::queue()` für nicht-kritische Mails | Mittel | mittel–groß | | 2.9 | `Console\Kernel::schedule()` dokumentieren oder befüllen (keine unsichtbaren Crons) | Mittel | klein | **Abhängigkeiten:** keine. Läuft parallel zu A1. --- ## 3. Modul 3 — Customer / Lead / Booking konsolidieren (Block A3, in Arbeit) **Aktueller Stand:** Phase 1 auf Testsystem abgeschlossen, Phasen 2–4 offen. Siehe [customer-bookings/umsetzung.md](./customer-bookings/umsetzung.md). ### 3.1 Was noch zu tun ist 1. **Phase 1 live deployen** (Duplikat-Merge). Voraussetzung: Backup + Stichprobenprüfung der CSV auf Test. 2. **Phase 2 — Tabellen umbenennen** (`customer` → `contacts`, `lead` → `inquiries`, `booking.lead_id` → `inquiry_id`). Code-Vorarbeit: Model-`$table`, Repositories auf `inquiry_id`, Routen `/lead/` → `/inquiry/` mit 301-Redirects. 3. **Phase 3 — Participants konsolidieren** (`participants_unified`). Code-Anpassung: `LeadRepository::createBooking()` kopiert keine Teilnehmer mehr; PDF-Generierung auf unified-Tabelle umstellen. **Vor dem Drop** der Alttabellen mindestens eine Woche Parallelbetrieb. 4. **Phase 4 — Communications / Notices / Attachments konsolidieren.** Neue Repositories (`CommunicationRepository`, `NoticeRepository`, `AttachmentRepository`), Views teilen, dann Alt-Tabellen droppen. ### 3.2 Ergänzungen / Optimierungen, die in dieser Phase mitlaufen sollten Aus eigener Analyse nachgeschoben: - **Abhängiges Newsletter-Modul** (erwähnt in `umsetzung.md` §Abhängiges Modul): Nach Phase 2 `NewsletterContact`-Model auf `App\Models\Contact` mappen, damit wir keine zwei Model-Pfade für dieselbe Tabelle halten. - **Passport-/API-Scopes:** Nach Umbenennung der Tabellen sind alle API-Responses, die `customer_id`/`lead_id` ausliefern, zu prüfen (Versionierung `v1` behalten, optional `v2` mit neuen Feldnamen). - **Volltextsuche für Contacts:** Mit wachsender Kundenhistorie lohnt sich ein DB-Index auf `email`, `name`, `firstname`, `zip` (oder MySQL-Fulltext) — verbessert die bereits eingebaute Schnellsuche. - **Audit-Log für Merges:** `merge_log`-Tabelle (wer hat wann welchen Contact in welchen gemerged), damit im Support nachvollziehbar bleibt. - **DSGVO-Lösch-Workflow:** `deleted_at` existiert bereits. Braucht UI für „Kontakt komplett löschen" (mit allen Anfragen/Buchungen), inkl. Sperre, falls aktive Buchungen existieren. - **Teilnehmer als Stammdaten:** `participants_unified.contact_id` langfristig konsequent setzen — dann können Teilnehmer aus bisherigen Reisen bei neuer Buchung übernommen werden. **Aufwand:** wie in `umsetzung.md` geschätzt, plus 1 Woche für die Ergänzungen oben. **Abhängigkeiten:** Phase 2 ist Voraussetzung für das neue Angebots-Modul (Modul 6), weil sonst zwei Namenswelten parallel leben. --- ## 4. Modul 4 — Backend-Direktbuchung (Block B1) **Briefing-Punkt:** „Es muss auch möglich sein, direkt im System Buchungen anzulegen. Aktuell gehen Mitarbeiter auf die Webseite und lösen dort eine Bestellung aus." ### 4.1 Ist-Analyse - Alle Buchungen laufen aktuell über das Webseiten-Formular (`sterntours.de/src/AppBundle/Controller` → API-Endpoint `booking/import` auf `mein.sterntours.de`). - Im Backend gibt es keinen vollständigen „Neue Buchung"-Flow; Anlage erfolgt bisher via Konvertierung aus einem Lead (`LeadRepository::createBooking()`). ### 4.2 Teilschritte 1. **Flow definieren (paper design):** - Einstieg 1: „Direktbuchung ohne Anfrage" (z. B. aus `/bookings` → „Neue Buchung"). - Einstieg 2: „Buchung aus Angebot" (kommt aus Modul 6). - Einstieg 3: Bestehend — aus Anfrage (bleibt wie gehabt). 2. **Gemeinsames BookingFormRequest:** Validierung, die sowohl von der Web-API als auch vom Backend-Form genutzt wird (Single Source of Truth). 3. **BookingService::createManual(array $data, ?Inquiry $inquiry = null)** als zentrale Erzeugungsmethode (ersetzt Duplikatlogik zwischen Web-Import und Backend). 4. **UI-Flow im Backend (mehrstufig, nicht ein Riesenformular):** - Schritt 1: Kontakt wählen oder anlegen (Contact-Autocomplete aus neuer Contacts-Tabelle). - Schritt 2: Reise / Fewo wählen, Termin, Zimmer-/Personenbelegung. - Schritt 3: Teilnehmer (aus Kontakt-Historie vorschlagen — hier greift `participants_unified.contact_id`). - Schritt 4: Leistungen / Optionen / Versicherung. - Schritt 5: Zahlmodalitäten, Anmerkungen. - Schritt 6: Übersicht + „Anlegen" + „Anlegen & Bestätigung-Mail senden". 5. **Wiederverwendung des Web-Formulars:** Prüfen, ob Teile des Frontend-Formulars (`sterntours.de`) als Blade-Partials ins Backend gezogen werden können — sonst aber lieber neu auf der UI-Baseline, da das Alt-Frontend auf Twig/Symfony läuft. 6. **E-Mail-Versand optional (Entscheidung Abschnitt 17.4):** - Letzter Schritt im Backend-Flow bietet zwei Buttons: - **„Anlegen & Bestätigung senden"** → nutzt dieselbe Mail-Pipeline wie der Web-Import (Buchungsbestätigung an Kunde + interne Mail). - **„Nur anlegen (keine Mail)"** → legt Buchung an, setzt ein internes Flag `silent_created = 1`, kein Kundenversand. - Das Flag wird im Audit-Log der Buchung vermerkt, damit später nachvollziehbar ist, warum keine Bestätigung rausging. - Nachträglicher manueller Mailversand bleibt jederzeit möglich (bestehende Mail-Funktionalität in Buchungs-Detailseite). 7. **Dokumente wie bei Web-Buchung:** Direktbuchung nutzt dieselbe Dokumenten-Logik wie `app/Http/Controllers/BookingController.php` heute (zentral hinterlegte Dokumente über `hasDocument(...)` + freie Uploads über `BookingFileRepository`) — kein Neubau. ### 4.3 Fewo-Variante - Dasselbe Konzept für Fewo (`TravelUserBookingFewoController`) — mit Verfügbarkeitsprüfung in Echtzeit und harter Kollisions-Sperre (siehe auch Modul 7, Doppelbuchungen). **Aufwand:** ~4 Wochen (Reisebuchung + Fewo + Tests). **Abhängigkeiten:** UI-Baseline (Modul 1), Phase 2 Customer/Lead (damit `contact_id`/`inquiry_id` sauber sind). **Risiko:** Mittel — muss mit Web-API-Import identisch enden, sonst entstehen zwei Wahrheiten. --- ## 5. Modul 5 — Organisations-Tab der Buchung überarbeiten (Block B2) **Briefing-Punkt:** „Unter dem Punkt Buchungen muss der Reiter Organisation verbessert werden. Es muss von der Benutzerführung einfacher und übersichtlicher werden, eine Organisation einer Reise anzulegen und zu verwalten." ### 5.1 Ist-Analyse (TODO vor Start des Moduls) - Screenshots + Workflow-Walkthrough des aktuellen Organisations-Tabs dokumentieren. - Welche Entitäten / Datensätze hängen daran (Transportleistungen, Partner, Zeiten, Dokumente)? Welche Felder sind Pflicht, welche redundant? - Welche Reibungspunkte berichten die Mitarbeiter (Usability-Interview, 30 min). ### 5.2 Teilschritte 1. **Zieldesign** (aus UI-Baseline abgeleitet): Zeitleisten-/Kacheldarstellung je Leistungsposition (statt langer Tabelle), Inline-Edit für häufige Änderungen. 2. **Refactor im Daten-Layer:** Doppelte Felder identifizieren, konsolidieren (vermutlich analog zu Teilnehmer-Konsolidierung). 3. **UI-Umsetzung** auf Basis der neuen Komponentenbibliothek. 4. **Bulk-Aktionen:** Leistungsblock kopieren (für Gruppen-/Serienreisen), Vorlagen speichern. 5. **PDF-Output** prüfen (Organisationsplan als Dokument). **Aufwand:** ~2–3 Wochen. **Abhängigkeiten:** Modul 1. Kein harter Zusammenhang zu Modul 3, aber wirkt sauberer, wenn Modul 3 Phase 3 (Participants) vorher durch ist. --- ## 6. Modul 6 — Angebote / Offers (Block B5, großer neuer Kernbaustein) **Briefing-Zitat (Kunde):** „Wir möchten aus unserem System unbedingt einfach, schnell und qualitativ hochwertig Angebote erstellen können … Theoretisch kann ein solches Angebot aussehen wie unser Buchungsauftrag, nur dass ‚Angebot' darüber steht … Wichtig ist uns, dass es einfach zu erstellen ist und man ggf. bei einer sehr hochwertigen Reise auch weiterführenden Text zum Reiseverlauf für die Reisebeschreibung hinzufügen kann … Anschließend wäre es gut, wenn hieraus direkt eine Buchung generiert werden könnte." ### 6.1 Ziel Aus einer Anfrage (oder direkt) ein **Angebot** erstellen, als PDF ausgeben, an den Kunden versenden und bei Annahme **mit einem Klick in eine Buchung umwandeln**, ohne Daten doppelt zu pflegen. ### 6.2 Datenmodell Neue Tabellen (Entwurf): ```sql -- Logischer Angebotsdatensatz (eine "Angebots-Nummer", mehrere Versionen möglich) CREATE TABLE offers ( id INT PK, inquiry_id INT NULL, -- aus Anfrage erzeugt contact_id INT NOT NULL, booking_id INT NULL, -- gesetzt, sobald Angebot → Buchung wurde offer_number VARCHAR UNIQUE, -- eigene Nummernkreisführung, z. B. 2026-00123 status ENUM('draft','sent','accepted','declined','expired','withdrawn'), current_version_id INT NULL, -- FK auf offer_versions.id (aktiv/zuletzt versendet) created_by INT, created_at, updated_at ); -- Jede versendete (und jede danach geänderte) Fassung ist eine eigene Version CREATE TABLE offer_versions ( id INT PK, offer_id INT FK, version_no INT, -- 1, 2, 3, ... status ENUM('draft','sent','accepted','declined','expired','superseded'), valid_until DATE NULL, total_price DECIMAL(10,2), headline VARCHAR, -- "Persönliches Angebot für Frau XXX" intro_text TEXT, itinerary_text LONGTEXT, -- Reiseverlauf, optional WYSIWYG closing_text TEXT, template_id INT NULL, pdf_path VARCHAR NULL, -- generiertes PDF dieser Version pdf_archived TINYINT(1) DEFAULT 0, -- für spätere Speicher-Bereinigung alter Versionen sent_at DATETIME NULL, accepted_at DATETIME NULL, accepted_via ENUM('customer_link','admin','email') NULL, created_by INT, created_at, updated_at, UNIQUE KEY (offer_id, version_no) ); CREATE TABLE offer_items ( id INT PK, offer_version_id INT FK, -- gehört zu einer Version, nicht zum Angebot direkt position INT, type ENUM('travel','service','option','discount','insurance','custom'), title VARCHAR, description TEXT, quantity INT DEFAULT 1, price_per_unit DECIMAL(10,2), total_price DECIMAL(10,2), travel_program_id INT NULL, fewo_lodging_id INT NULL, metadata JSON ); CREATE TABLE offer_templates ( id, name, description, headline, intro_text, itinerary_text, closing_text, items_json, ... ); -- Kundenseitiger Freigabe-Link (Token-URL, Einmal-Link pro Version) CREATE TABLE offer_access_tokens ( id INT PK, offer_version_id INT FK, token VARCHAR(64) UNIQUE, expires_at DATETIME NULL, opened_at DATETIME NULL, -- wann der Kunde den Link erstmals geöffnet hat accepted_at DATETIME NULL, declined_at DATETIME NULL, ip_address VARCHAR(45) NULL, user_agent VARCHAR(255) NULL ); ``` Angebote sind **eigenständig**, nicht Unterdatensatz einer Buchung. Eine Buchung kann per `offer_id` rückverweisen, und ein Angebot hält per `booking_id` die daraus entstandene Buchung. Die Versionierung ist zwingend (Entscheidung aus Abschnitt 17.1): ab dem ersten Versand ist Änderung = neue Version. ### 6.3 Teilschritte 1. **Datenmodell & Migrationen** (`offers`, `offer_versions`, `offer_items`, `offer_templates`, `offer_access_tokens`). 2. **Model / Repository / Service** (`OfferService::createFromInquiry($inquiryId)`, `::createNewVersion(Offer $offer)`, `::send(OfferVersion $v)`, `::convertToBooking(Offer $offer)`). 3. **Vorlagen-Verwaltung:** - Eigene Liste `/offer-templates`, anlegbar aus einem existierenden Angebot (Button „Als Vorlage speichern"). - Vorlagen pro Reise-Organisation gruppierbar (Briefing: „aus einer der Vorlagen eine Organisation laden"). 4. **Angebots-Editor:** - Einstieg: innerhalb `/inquiries/{id}` Button „Angebot erstellen"; alternativ eigenständiges `/offers/create`. - Vorlage wählen → Felder werden gefüllt → frei bearbeitbar. - Positionen (offer_items) mit Drag&Drop, Kopier-Button, Summe live. - Optionales Feld „Reiseverlauf" als WYSIWYG (TinyMCE/TipTap), ausblendbar für einfache Angebote. - **Dokumente am Angebot** (analog zum bestehenden Buchungs-Pattern in `app/Http/Controllers/BookingController.php`, der `booking.hasDocument('coupon'|'storno'|…)` für zentral hinterlegte Dokumente und `BookingFileRepository` für freie Uploads nutzt): - **Zentral hinterlegte Dokumente** (Briefbögen, AGB, Standard-Anhänge pro Reise-Organisation / Reisetyp) werden auswählbar und automatisch als Anhang angeboten. - **Freie Uploads pro Angebot** (individueller Reiseverlauf, eigener Briefbogen, Bilder) — analog zu `booking_file` über eine `offer_file`-Tabelle bzw. über das in Modul 3 Phase 4 konsolidierte `attachments`-Modul (dann mit `offer_version_id`). - Ab `sent` werden die zu diesem Zeitpunkt ausgewählten/hochgeladenen Dokumente mit der Version „eingefroren" (zur Version gespeichert, damit spätere Änderungen keine alten PDFs verfälschen). 5. **PDF-Generierung:** - Neues Template `pdf/offer.blade.php` analog zum bestehenden Buchungsauftrag-PDF. - Header: „Angebot Nr. 2026-00123 / V2" und / oder „Persönliches Angebot für Frau Musterfrau". - Gleiche Corporate-Identity-Elemente wie Buchungsauftrag. - Anlage: zentral ausgewählte Dokumente + frei hochgeladene Dokumente. 6. **Versionierung (fest verankert, Entscheidung Abschnitt 17.1):** - Solange Status `draft`: Änderungen ändern die aktuelle Version direkt. - Ab dem ersten `send`: jede Änderung erzeugt eine **neue Version** (V2, V3 …); vorherige Version wird `superseded`. - Jede Version hält ihr eigenes PDF + ihre eigenen Dokumente + ihre eigenen `offer_items` + ihren eigenen Freigabe-Token. - **Speicher-/Archivstrategie:** Cron-Job (später, Modul 13) setzt PDFs älterer Versionen `pdf_archived = 1` und verschiebt sie auf kalten Storage; konfigurierbares Retention-Limit (z. B. „nur letzte 3 Versionen im Hot-Storage, ältere PDFs bei Bedarf neu generieren"). 7. **Versand-Workflow:** - Status-Maschine auf `offer_versions`: `draft → sent → accepted/declined/expired/superseded`. - Versand als E-Mail (PDF im Anhang, Textvorlage im CMS pflegbar — siehe Modul 11). - Mail-Thread landet im gemeinsamen Communications-Modul (Modul 3 Phase 4). - Beim Senden wird pro Version **ein neuer `offer_access_token`** erzeugt und in die Mail als Link eingebaut. 8. **Kundenseitiger Freigabe-Link (Entscheidung Abschnitt 17.2):** - Öffentliche Route `GET /angebot/{token}` auf `sterntours.de` (oder Subdomain `angebote.sterntours.de`), die die Angebotsversion anzeigt (PDF-Embed + Anhänge + Buttons „Annehmen" / „Ablehnen" / „Fragen?"). - Route `POST /angebot/{token}/accept` und `POST /angebot/{token}/decline` mit IP/User-Agent-Protokoll. - Beim Annehmen: `offer_versions.status = accepted`, `accepted_at = now()`, `accepted_via = 'customer_link'`, Benachrichtigung (Mail + Dashboard-Hinweis) an den zuständigen Mitarbeiter. - **Admin-Annahme bleibt gleichwertig möglich** (Status händisch auf `accepted` setzen, `accepted_via = 'admin'`), z. B. bei telefonischer Zusage. 9. **Konvertierung Angebot → Buchung:** - Button „In Buchung übernehmen" im Angebot (nur aktiv bei `accepted`). - `OfferService::convertToBooking()` legt Buchung + Teilnehmer + Leistungen auf Basis der angenommenen `offer_version` an; nutzt denselben Service wie Modul 4 (Direktbuchung), damit nur eine Erzeugungslogik existiert. - Angebots-Dokumente (Version-eingefroren) werden automatisch an die Buchung kopiert / verlinkt. - `booking_id` wird auf `offers` gesetzt. 10. **Übersichtsseite / Listen:** `/offers` mit Filtern (Status, Ablaufdatum, Mitarbeiter, Kontakt, Version), Badges, Bulk-Export. 11. **Zähler / Dashboard:** In der Kontakt- und Anfrage-Übersicht jeweils Badge „X Angebote" analog zum Anfragen-/Buchungs-Badge. 12. **Berechtigungen:** Neue Permissions `offers.read`, `offers.create`, `offers.send`, `offers.accept`. ### 6.4 Ergänzungen aus meiner Sicht (nicht im Briefing, aber sinnvoll) - **Ablauf-Automatik:** Cron setzt Angebote nach `valid_until` auf `expired`, optional Erinnerungsmail an Mitarbeiter vor Ablauf. Token wird dabei invalidiert. - **Änderbarkeits-Regel:** Nur `draft`-Versionen sind frei änderbar. Ab `sent` ist Änderung = neue Version. - **Preiskonsistenz:** `offer_items` greifen — sofern verknüpft — auf dieselben Preisquellen wie das bestehende Reise-/Fewo-System (aus v2 migriert, Modul 12). Keine Doppeltpflege von Preislisten. - **Anbindung Kundenportal (Modul 15):** Sobald das Kundenportal steht, sieht der eingeloggte Kunde seine Angebote direkt dort — der Token-Link bleibt aber für nicht eingeloggte Kunden weiterhin der Standardweg. **Aufwand:** ~7–9 Wochen (großes Modul, durch Versionierung + Freigabe-Link leicht gewachsen). **Abhängigkeiten:** - UI-Baseline (Modul 1) muss stehen. - Modul 3 Phase 2 (inquiries-Tabelle), damit `inquiry_id`-Referenzen sauber sind. - Modul 3 Phase 4 (attachments-Tabelle) ideal — sonst legen wir eine separate `offer_file`-Tabelle an, die später fusioniert. - Modul 4 (gemeinsamer BookingService), damit die Konvertierung keinen Copy-Code erzeugt. - Modul 12 Teil „Reiseprogramme" verfügbar in Laravel, falls `offer_items` auf bestehende Programme referenzieren sollen — zur Not über View-basiertes Legacy-Lesemodell überbrückbar. --- ## 7. Modul 7 — E-Mail-System vereinheitlichen & Auto-Save (Block B3) **Briefing-Punkte:** - „Unter dem Punkt Buchungen gibt es E-Mails … wird eine E-Mail geschrieben. Soll diese sofort per Auto-Save als Entwurf gespeichert werden." - „Auch hier [Fewo] müssen die E-Mails, die dem selben Prinzip die Buchung verfolgen, auch ein Auto-Save bekommen." - „Da die E-Mails im System grundsätzlich eine einheitliche Programmierung haben, wäre es sinnvoll, diese Skripte zu migrieren." ### 7.1 Teilschritte 1. **Konsolidierung zuerst erledigen** (Modul 3 Phase 4, `communications`-Tabelle). Solange drei verschiedene Mail-Tabellen existieren, ist jede Erweiterung doppelte Arbeit. 2. **Draft-Feld einführen** in `communications`: Status `draft | queued | sent | failed`, plus `autosaved_at`. 3. **Auto-Save im Editor (Frontend):** - JS-Debounce (alle 3 s) schreibt per `PATCH /communications/{id}/draft` Betreff und Body. - Anlage eines Drafts beim Öffnen des leeren Editors (liefert `id` zurück). - Crash-/Reload-Recovery: Beim Öffnen des Mail-Dialogs prüft das Frontend, ob ein bestehender Draft für diesen Kontext existiert (Anfrage / Buchung / Fewo-Buchung) und bietet ihn zum Weiterbearbeiten an. 4. **Vereinheitlichte Modals** auf Basis der UI-Baseline (ein Mail-Modal für alle drei Kontexte). 5. **Queue-basierter Versand** (siehe Modul 2 / 2.8): `Mail::queue()`, mit `send_after`-Feld (geplanter Versand), Retry-Logik. 6. **CustomerFewoMail → Communications migrieren** (Ausnahme, die in Modul 3 Phase 4 ggf. noch offen ist). 7. **Draft-Aufräumcron** (automatische Löschung von Drafts > 30 Tage ohne Aktivität). **Aufwand:** ~2 Wochen nach Abschluss von Modul 3 Phase 4. **Abhängigkeiten:** Modul 3 Phase 4. --- ## 8. Modul 8 — Fewo-Doppelbuchungen / Belegung fixen (Block B4) **Briefing:** „Bei den Buchungen der Ferienwohnungen tauchen immer wieder Fehler auf bei der Belegung … wenn die Daten geändert werden … Doppelbuchungen teilweise im System vorliegen. Ich glaube nur in der Datenbank, das muss natürlich bereinigt werden." ### 8.1 Analyse - Root-Cause vermutet (aus Briefing): Daten-Änderungen werden nicht synchron in die zweite Speicher-Ebene durchgeschrieben (vermutlich Legacy-Admin in `sterntours.de/AdminController.php` vs. `mein.sterntours.de`). - Ziel von Modul 9 (Admin-Migration) wird diesen Fehler indirekt mit beheben, der Bug muss aber separat gezielt adressiert werden — sonst blutet er weiter. ### 8.2 Teilschritte 1. **Quellen inventarisieren:** Welche Stellen schreiben `FewoReservation` / Belegungen (Legacy AdminController, Webseite, mein.sterntours Fewo-Buchungen, Fewo-Mail-Import, ggf. iCal-Sync)? 2. **Konflikt-Detektor-Cron schreiben:** Stündlicher Job, der Überlappungen pro `fewo_lodging_id` findet und einen Report ins Dashboard + Slack/Mail liefert. 3. **Datenbestand aufräumen:** Einmaliger Report → manuelle / halb-automatische Bereinigung. 4. **Zentrale Schreiblogik einziehen:** Alle Fewo-Reservierungs-Anlagen / -Änderungen laufen künftig durch einen einzigen `FewoReservationService`, der - Überlappungen mit Lock prüft (`SELECT … FOR UPDATE`), - Konsistent in allen relevanten Tabellen schreibt, - Events feuert (für Cache-Invalidierung, Kalender). 5. **Turnustag-Logik (Entscheidung Abschnitt 17.5):** - **Kein harter DB-Constraint** (kein `EXCLUSION`-/Unique-Index auf Tages-Ebene), weil An- und Abreisetag legitimerweise überlappen dürfen (Abreise-Vormittag / Anreise-Nachmittag am selben Tag). - Überlappung wird **im Service** nach klaren Regeln geprüft: - Konflikt = neue Buchung endet **später** als bestehende beginnt UND neue Buchung beginnt **früher** als bestehende endet, **und** die überlappenden Tage sind keine reinen Wechseltage (Turnustage). - „Wechseltag" konfigurierbar pro Lodging (Tagestyp: frei / nur Anreise / nur Abreise / gesperrt). - Der Service liefert bei Konflikt eine sprechende Fehlermeldung („Belegung kollidiert mit Buchung #1234 vom 12.–19.06., kein Wechseltag"), statt stumm abzulehnen. 6. **Reservierungs-Audit-Log:** Jede Änderung protokollieren (alter/neuer Zeitraum, User, Quelle). 7. **UI-Kalender für Mitarbeiter:** Visueller Belegungskalender pro Lodging mit farblich markierten Wechseltagen — macht das „überlappen darf, aber nur am Turnustag" für Mitarbeiter sofort sichtbar. **Aufwand:** ~2 Wochen (Analyse + Fix + Bereinigung). **Abhängigkeiten:** kann früh gestartet werden; überschneidet sich inhaltlich mit Modul 9 (Admin-Migration) und muss dort mitgedacht werden. --- ## 9. Modul 9 — Migration Admin aus sterntours.de (Block C1) **Briefing:** „Zusätzlich gibt es ein weiteres Backend `sterntours.de/src/AppBundle/Controller/AdminController.php`. Dieses muss auch sauber in das Hauptbackend `mein.sterntours.de` (v3) übernommen werden, damit wir das alte abstellen können. Damit wird sich dann vermutlich auch der Fehler erledigen. Grundsätzlich darf gerne die Datenstruktur migriert werden und optimiert werden." ### 9.1 Scope-Analyse `AdminController.php` (~1.300 Zeilen) enthält laut Code u. a.: - Fewo-Lodging-Verwaltung (CRUD, Bilder, Gruppen). - Fewo-Preise, Saisons, Reservierungen. - Fewo-Booking-Requests. ### 9.2 Teilschritte 1. **Inventar:** Jede Route in `AdminController.php` (+ evtl. zugehörige Templates) auflisten, Funktion beschreiben, Äquivalent im Laravel-System suchen. 2. **Datenstruktur-Analyse:** Welche Tabellen werden bedient (`fewo_lodging`, `fewo_price`, `fewo_season`, …)? Welche liegen schon in `mein.sterntours.de`, welche müssen migriert / umbenannt werden? Wo existiert doppelte Datenhaltung (Ursache für Modul 8)? 3. **Zieldesign:** Pro Funktion ein Ticket/Epic (Fewo-Lodging-Verwaltung, Preise, Saisons, …). Jede Funktion zieht auf die UI-Baseline (Modul 1). 4. **Migrationssequenz:** - a) Read-Only-Schattenansicht in `mein.sterntours.de` bauen, die Daten aus der bestehenden Tabelle anzeigt. - b) Schreib-Operationen parallel einbauen (Dual-Write-Modus: Legacy + neu), solange beide laufen. - c) Schreib-Rechte im Legacy-Admin entziehen, sobald die neue UI stabil ist. - d) Legacy-Admin vollständig deaktivieren (Routing/Login weg). 5. **Integrationstests pro Funktion** (zwingend, da sonst Modul 8 erneut entsteht). ### 9.3 Reihenfolge-Empfehlung innerhalb C1 1. Fewo-Lodging-CRUD (niedriges Risiko, häufigste Aufgabe). 2. Fewo-Preise + Saisons (mittel, zentral für Buchungen). 3. Fewo-Reservierungen (hohes Risiko — muss mit Modul 8 zusammen gedacht werden). 4. Fewo-Booking-Requests + Bilder. **Aufwand:** ~8–10 Wochen abhängig von Funktionsumfang. **Abhängigkeiten:** Modul 1 (UI), Modul 2.8 (Queue) optional, eng mit Modul 8. --- ## 10. Modul 10 — Navigation & Page-Modell refactoren (Block C2) **Briefing:** - „Im System, gerade in Fronten `sterntours.de/src/AppBundle/Listener/KernelControllerListener.php`, ist die Struktur der Navigation und der des Seitenbaums sehr eigenwillig und muss verbessert und optimiert werden. `mein.sterntours.de/app/Models/Page.php` — alles liegt in dieser Page-Tabelle und ist sehr schlecht wartbar und undurchschaubar. Hier muss ein deutlich cleaneres Konzept her." - `https://mein.sterntours.test/navigation-api` ist als Ansatz vorhanden, aber noch nicht gut. ### 10.1 Ist-Zustand (aus navigation.md / frontend-navigation/) - Ein Symfony `KernelControllerListener` übernimmt Routing-Entscheidungen basierend auf `Page.realUrlPath` / Slug-Traversierung / hartkodierten Redirects / Template-Namen. - Alles liegt in **einer** Tabelle `page`, die sowohl Inhalte, Reiseprogramme, Fewo-Zuordnungen, Länder, News, Reiseführer referenziert. - Nested-Set-Logik (`lft`/`rgt`/`lvl`) ist unzuverlässig; derzeit wird sie im Listener bewusst umgangen. - Es existiert eine Backend-UI unter `/navigation-api`, aber sie ist read-only und deckt das Frontend-Mental-Modell nicht sauber ab. ### 10.2 Zielbild (Vorschlag) Trennung in klar benannte Entitäten: ``` pages — generische Inhaltsseiten (CMS-Content) menu_items — reine Navigationsknoten (Titel, Link, Sortierung, parent_id) routes/redirects — URL-Routing (Slug → Ziel-Entität) inkl. 301-Redirects content_types — Polymorphe Zuordnung: menu_item → [Page | TravelProgram | FewoLodging | Country | News | TravelGuide | ...] ``` - `menu_items` ist der **einzige** Baum (adjacency list, evtl. mit `nested-set`-Paket gepflegt). - Jede `menu_item` hat einen `linkable_type` + `linkable_id` (Polymorphie) — so wird klar, was hinter einem Link steht, und das Model `Page` ist nicht mehr das Mega-Modell. - `routes`-Tabelle auflöst Slug → `linkable`, ersetzt `realUrlPath`-Suche. ### 10.3 Teilschritte 1. **Konzept-Doku** mit allen Alt-Datenfeldern in `page` und Zielzuordnung (welche bleiben auf `pages`, welche wandern auf `menu_items`, welche werden zu polymorphen Zielen). 2. **Neue Tabellen aufbauen (migrativ):** `menu_items`, `routes`, optionale `redirects`. 3. **Datenmigration:** `page` → `menu_items` + `pages` + `routes` (alles ableitbar; kein Datenverlust). 4. **Laravel-Route-Driver bauen** (für Backend- und API-Lookups). 5. **Symfony-Frontend anpassen:** - `KernelControllerListener` nutzt eine neue Lookup-API (`GET /api/routes/resolve?path=/foo/bar`) aus `mein.sterntours.de`. - Legacy-Queries auf `page`-Tabelle entfallen dort. 6. **Neue Backend-UI** unter `/navigation` (nicht mehr `/navigation-api` read-only): - Drag&Drop-Tree. - Inline-Edit für Titel / Slug / Sichtbarkeit. - „Was ist hier verlinkt?" — direkter Sprung zum Content-Editor der Zielentität. - Live-Preview-Link ins Frontend. 7. **Redirect-Verwaltung** als eigene UI (statt hartkodierter Liste im Listener). 8. **Cache-Strategie** (Events: `RouteChanged` → Cache-Invalidierung; kein manuelles „Cache leeren"-Button mehr als primärer Weg). **Aufwand:** ~6–8 Wochen. **Abhängigkeiten:** Modul 1 (UI-Pattern). Muss vor Modul 11 (CMS-Vereinheitlichung) stehen, damit CMS-Inhalte auf das neue Model angedockt werden. --- ## 11. Modul 11 — CMS-Vereinheitlichung (Block C3) **Briefing:** Es existieren CMS-Ansätze unter `/cms/feedback`, `/cms/fewo/content`, `/cms/travel_guide/content`, `/iq/content/tree/index`, `/cms/news`, `/cms/answer_question`, `/cms/sidebar`, `/cms/content/infos`, `/cms/content/all`. „Die Einzelmodule können so bleiben. Es muss nur klarer in der Benutzerführung werden und auch aus der Hauptnavigation erreichbar sein und deutlich sein, wo was hingehört." ### 11.1 Teilschritte 1. **Inventar:** Jede dieser Routen inhaltlich beschreiben (Was wird gepflegt? Für welchen Frontend-Bereich?). 2. **Dachstruktur:** Eine neue Haupt-Menü-Gruppe „Inhalte / CMS" im Seitenmenü mit Unterpunkten, die klar benannt sind (nicht mehr `/cms/content/infos` vs. `/cms/content/all`). 3. **Landing-Page** `/content` als Dashboard mit Kacheln pro Bereich + Kurzbeschreibung („Was pflegst du hier?"). 4. **Konsistente Listen-/Edit-Views** (Baseline). 5. **Verknüpfung mit Navigation (Modul 10):** Aus der Navigations-UI direkt in den passenden CMS-Bereich springen („Inhalt dieser Seite bearbeiten"). 6. **Berechtigungen vereinheitlichen:** Jedes CMS-Submodul hat eine eigene Permission (`cms.feedback`, `cms.news`, …); aktuell ist das teilweise inkonsistent. 7. **Nicht ändern:** Die Einzellogik der Module (Datenmodelle bleiben). Nur UI / Dachnavigation / Permissions / Benennung. **Aufwand:** ~3 Wochen. **Abhängigkeiten:** Modul 1, Modul 10 (für Verknüpfungen), Modul 3 (nur inhaltlich, keine Abhängigkeit in der Umsetzung). --- ## 12. Modul 12 — Migration Reiseverwaltung v2.stern-tours.de (Block C4, größtes Einzelmodul) **Briefing:** „Jetzt kommt noch ein sehr großer Punkt: `v2.stern-tours.de/application/controllers/acp`. Hier werden Reisen angelegt und auch die Reisezeiträume verwaltet. Dieses ist mittlerweile absolut veraltet und auch fehleranfällig. Ein großes neues Modul wird es sein, dieses auf `mein.sterntours.de` zu migrieren — d. h. der komplette Funktionsumfang muss in das Backend einentwickelt und benutzerfreundlich gemacht werden, sowie müssen die gesamten Skripte etc. deutlich optimiert werden." ### 12.1 Umfang (grob aus Controller-Ordner) ``` aegypten_api flight_period travel_country travel_insurance catalog keyword travel_departure_point travel_option feedback newsletter travel_destination travel_organizer travel_arrival_point page travel_discount travel_period travel_category travel_program travel_period_date travel_period_price travel_class travel_program_image travel_setting travel_general_notes wiki welcome ``` Das sind ~25 Entitäten / Verwaltungsbereiche. Viele hängen miteinander zusammen (Program ↔ Period ↔ PeriodDate ↔ PeriodPrice). ### 12.2 Grundstrategie (Entscheidung Abschnitt 17.6) - **Stück für Stück**, nicht Big Bang. - Pro Teil-Entität wird die **Datenstruktur überarbeitet und bereinigt** — d. h. sinnvolle Umbauten werden _mitgemacht_, nicht auf später verschoben (1:1-Portierung wäre zu teuer in Nachpflege). - Während der Migration läuft das Altsystem auf dem Live-Server **weiter** (Parallelbetrieb, Entscheidung Abschnitt 17.8). Jedes fertig migrierte Teil-Modul übernimmt _sofort_ die Hoheit (Schreibrechte), das Altsystem geht für diesen Bereich auf Read-only bzw. Redirect ins neue Backend. - **Teil-Migration auf Live** ist ausdrücklich vorgesehen: einzelne Entitäten dürfen produktiv auf das neue System umziehen, während andere noch in v2 bleiben. Voraussetzung: bidirektionale Datensicht (View oder Sync) für alle übergreifenden Reports. ### 12.3 Teilschritte (hohes Level — jedes Teil-Modul wird wie ein eigenes Mini-Projekt geführt) 1. **Inventar & Datenmodell-Review:** Pro Entität — Tabellen, Felder, Beziehungen, heute existierende Fehler / Sonderfälle. 2. **Entscheidung pro Tabelle:** Welche Felder bleiben, welche werden umbenannt, welche Tabellen werden zusammengeführt, welche Indizes fehlen. Dokumentiert als kurzes Delta-Dokument pro Entität, bevor die Migration geschrieben wird. 3. **Abhängigkeitsgraph** der Entitäten bauen → Migrationsreihenfolge ableiten. 4. **Schrittweise Migration — empfohlene Reihenfolge:** - **Stammdaten zuerst** (wenige Abhängigkeiten, einfache CRUD): `travel_country`, `travel_class`, `travel_category`, `travel_organizer`, `travel_departure_point` (+ holiday), `travel_arrival_point`, `travel_destination`, `travel_general_notes`, `travel_insurance` + `travel_insurance_price`, `flight_period`, `travel_option`, `travel_discount`, `travel_setting`, `keyword`. - **Reiseprogramme:** `travel_program`, `travel_program_image`, `catalog`, `page`-Zuordnungen (hängt mit Modul 10 zusammen). - **Reisezeiträume:** `travel_period`, `travel_period_date`, `travel_period_price`, `travel_period_price_type` (eng verwoben, gemeinsam migrieren). - **Sonderfälle / Rand:** `aegypten_api`, `newsletter`, `feedback`, `wiki`, `welcome` (Dashboard). 5. **Pro Teil-Modul:** - Datenstruktur als Laravel-Migration (mit Rückwärtskompatibilität). - Model + Repository + Service. - UI auf Baseline (Listen + Edit). - Integrationstests. - Dual-Write während Übergang, bis das Altsystem lesefrei / abgeschaltet ist. 6. **Performance-/Query-Audit:** Laut Briefing „Skripte deutlich optimiert werden". Hier typische Ursachen angehen — N+1-Queries, fehlende Indizes, fehlende Eager Loads, zu viele JOINs in CI-Reports. 7. **Preis-Engine:** Der kritischste Teil. Alle Preisberechnungen (Programm + Periode + Rabatte + Versicherung + Optionen) müssen in **einem** Service gekapselt werden. Dieser wird dann von: - Modul 6 (Angebote) — für `offer_items`, - Modul 4 (Direktbuchung) — für Buchungspreise, - Der Web-Buchungs-API, - Dem aktuell laufenden Altsystem (während Parallelbetrieb) genutzt. **Vermeidet eine Neuauflage des heutigen Preischaos.** 8. **Frontend-Adapter:** `sterntours.de` muss zur Anzeige (Preise, Abfahrten) dieselben Daten bekommen. Entweder API-Call ins neue Laravel oder View-Layer in DB, bis das Frontend gegen die neue Quelle läuft. 9. **Abschaltung v2:** Erst wenn alle Datenflüsse migriert sind, Legacy-Read-Zugriffe eliminiert und das Frontend gegen die neue Quelle läuft. **Aufwand:** ~14–20 Wochen. Durch die Entscheidung „Stück für Stück mit Überarbeitung/Bereinigung" (Abschnitt 17.6) eher am oberen Ende, dafür am Ende kein technischer Schuldenberg. **Abhängigkeiten:** Modul 1 (UI), Modul 2 (Tooling/Tests), Modul 10 (Navigation, weil `travel_program` → `menu_items` verlinkt werden). --- ## 13. Modul 13 — Betrieb, Tests, Monitoring (Block D1) Begleitendes Querschnittsmodul. ### 13.1 Teilschritte 1. **Test-Strategie:** - Pro neues Modul: Unit-Tests für Services, Feature-Tests für kritische Flows (Direktbuchung, Angebot → Buchung, Fewo-Belegung). - Contract-Tests für alle öffentlichen APIs (Booking-Import, Navigation-API). 2. **CI-Pipeline** (Gitlab/GitHub Actions): Pint, Larastan, PHPUnit, optional Dusk für die wichtigsten Flows. 3. **Queue-Monitoring:** Horizon (wenn Redis) oder Log-Channel + Alert bei Failed Jobs. 4. **Error-Tracking:** Sentry / Flare / Bugsnag (eines davon auswählen). 5. **Deployment-Pipeline:** Entweder Deployer/Envoyer-basiert oder Docker-Build; Maintenance-Mode-Script, Migrations- und Cache-Steps dokumentiert. 6. **Rollback-Pläne** pro Migration (ist in Modul 3 bereits vorbildlich gemacht — Standard für alle neuen Module). **Aufwand:** laufend, ca. 4 Wochen verteilt über das Gesamtprojekt. --- ## 14. Modul 14 — Framework- & Sprach-Upgrade (Block D2) Nicht sofort, aber einplanen: - **Laravel 10 → 11 Upgrade-Pfad:** separater Branch, nach Abschluss von Block A + Teilen B. Nicht während aktiver Alt-Migration (Modul 12). - **PHP 8.3** in Docker/CI festschreiben, aus `composer.json` die Obergrenze anheben. - **`jenssegers/date`** entfernen, überall Carbon. - Composer-Pakete konsolidieren (siehe `projekt-empfehlungen-2026-04.md` §2.3). **Aufwand:** ~2 Wochen. --- ## 15. Modul 15 — Kundenportal / Kunden-Login (Zukunftsmodul, Block E) **Briefing (Ergänzung aus den Antworten):** „Ein weiteres Modul für die Zukunft bitte mit aufnehmen: einen Kunden-Login, so dass die Kunden Zugang haben zu ihren eigenen Buchungen und Übersichten." ### 15.1 Ziel Ein öffentlich erreichbarer, eingeschränkter Kunden-Bereich, in dem sich ein Kontakt mit seiner E-Mail-Adresse anmelden und seine eigenen Daten / Anfragen / Angebote / Buchungen einsehen kann. Klar abgegrenzt vom Mitarbeiter-Backend. ### 15.2 Datenmodell - **Kein separates `users`-Model** für Kunden — Auth wird an `contacts` aufgehängt, Erweiterung: ```sql ALTER TABLE contacts ADD COLUMN password VARCHAR NULL, ADD COLUMN remember_token VARCHAR(100) NULL, ADD COLUMN email_verified_at DATETIME NULL, ADD COLUMN portal_enabled TINYINT(1) DEFAULT 0, ADD COLUMN last_login_at DATETIME NULL; CREATE TABLE customer_sessions ( id, contact_id, ip, user_agent, last_active_at, created_at, expires_at ); ``` - Laravel-Auth-Guard `customer` (eigener Guard neben dem Mitarbeiter-Guard `web`). ### 15.3 Teilschritte 1. **Registrierung / Erstanmeldung:** - Kunde erhält Einladung per Mail (Reiseunterlagen / Buchungsbestätigung enthalten einen „Zugang einrichten"-Link). - Alternativ: „Passwort setzen" via E-Mail-Verifizierung ausgehend von einer bestehenden Mail-Adresse im `contacts`-Datensatz. - Kein Self-Signup ohne bestehenden Kontakt — Datenbereinigung würde sonst erneut leiden. 2. **Login / Passwort-Reset / 2FA optional.** 3. **Portal-Views (auf `sterntours.de` oder Subdomain `mein.sterntours.de/portal` — Architekturentscheidung noch offen):** - Dashboard („Ihre nächste Reise in X Tagen"). - Meine Anfragen. - **Meine Angebote** (Integration mit Modul 6: eingeloggter Kunde sieht dort dieselben Dokumente wie über den Token-Link, zusätzlich historische Angebote). - Meine Buchungen (Dokumente, Zahlungsstand, Reiseunterlagen). - Meine Daten (Adresse, Einwilligungen, Newsletter-Status). 4. **Datenschutz / DSGVO:** - Nur eigene Daten sichtbar (strenge Scope-Checks in Controllern + Policies). - Download der eigenen Daten als Export (Art. 15 DSGVO). - Lösch-Anfrage-Flow (verknüpft mit dem `deleted_at`-Flow aus Modul 3). 5. **Integration mit Token-Links (Modul 6):** - Ein Token-Link funktioniert weiterhin ohne Login. - Ist der Kunde eingeloggt und klickt den Link, wird das Angebot in das Portal-Layout eingebettet (nicht doppelte Darstellung). 6. **Permissions & Sicherheit:** - Separate Passport-Scopes für Portal-API. - Rate-Limiting auf Login-Endpunkte. - Audit-Log (wer hat wann was eingesehen) — wichtig bei Angebotsannahmen im Portal. 7. **UI:** Kein Teil der Mitarbeiter-UI-Baseline (andere Zielgruppe). Eigenes, leichtes Layout (Bootstrap 5), das zum `sterntours.de`-Look passt. ### 15.4 Abhängigkeiten - Modul 3 Phase 2 (`contacts`-Tabelle) ist **zwingende** Voraussetzung — sonst liegen die Kundenlogins auf der alten `customer`-Tabelle. - Modul 6 (Angebote) sollte mindestens Grundstand haben, damit Portal-Angebotsansicht sinnvoll Inhalt bekommt. - Profitiert stark von Modul 2.8 (Queue) für Einladungs-/Benachrichtigungsmails. ### 15.5 Einordnung in die Roadmap - **Nicht Teil der ersten Welle.** Frühester sinnvoller Start: nach Abschluss von Modul 6 + Modul 3 Phase 4. - Entspricht einem eigenen Block „E — Kundenseitige Self-Service-Funktionen" und kann zeitlich mit Modul 14 (Framework-Upgrade) parallel laufen, wenn Kapazität da ist. **Aufwand:** ~6–8 Wochen für MVP (Login + Angebote + Buchungen + Daten), plus 2–3 Wochen für DSGVO-/Self-Service-Funktionen. --- ## 16. Zusammenfassung — empfohlene Reihenfolge und Parallelisierung ``` Monat 1–2 A1 UI-Baseline + A2 Technik-Hausaufgaben (+ Start A3 Phase 2) + Frontend-Migration Vite/dart-sass/Bootstrap 5 (Modul 1.4) parallel Monat 2–3 A3 Customer/Lead/Booking Phasen 2+3 abschließen Monat 3 A3 Phase 4 + B4 Fewo-Doppelbuchung (Turnustag-Logik) parallel Monat 4 B1 Direktbuchung (mit Mail-Toggle) + B2 Organisation-Tab Monat 4–5 B3 E-Mail-Auto-Save (nach A3 P4) Monat 5–7 B5 Angebots-Modul (inkl. Versionierung + Freigabe-Link) Monat 6–9 C1 Admin-Migration sterntours.de (zügig, kein harter Cut) Monat 7–9 C2 Navigation + C3 CMS-Vereinheitlichung Monat 8–16 C4 v2.stern-tours.de Reisenverwaltung — Stück für Stück mit Teil-Migration auf Live und Parallelbetrieb Monat ≥10 E1 Kundenportal / Kunden-Login (frühestens nach B5 + A3 P4) Laufend D1 Betrieb/Tests, D2 Framework-Upgrade gegen Ende ``` Einige Module können parallelisiert werden, wenn Kapazität vorhanden ist. Kritischer Pfad: **A1 → A3 → B5 → C4**. Modul 15 (Portal) liegt off-path und kann flexibel eingeordnet werden. --- ## 17. Entscheidungen (geklärt) Die acht zuvor offenen Fragen wurden vom Auftraggeber beantwortet. Die Entscheidungen sind in den jeweiligen Modulen bereits eingearbeitet; hier die konsolidierte Referenz: | # | Thema | Entscheidung | Fundstelle im Plan | |---|-------|-------------|--------------------| | 17.1 | **Angebots-Versionen** | Jede Änderung nach dem Versand erzeugt eine neue Version (V2, V3 …). Speicheroptimierung durch spätere Archivierung/Löschung alter Versionen. | Modul 6.2 (`offer_versions`), 6.3.6 (Versionierung), 6.4 (Archivstrategie) | | 17.2 | **Angebots-Annahme** | Beides: Admin-seitige Statusänderung **und** kundenseitiger Freigabe-Link (Token-URL) mit Bestätigungsseite. | Modul 6.3.8 (`offer_access_tokens`, `/angebot/{token}`) | | 17.2b | **Kundenportal** | Neues Zukunftsmodul (Modul 15): Kunden-Login mit Zugang zu eigenen Buchungen/Angeboten/Daten. | Modul 15 | | 17.3 | **Angebots-Dokumente** | Dual-Modell analog zur heutigen Buchungs-Logik in `app/Http/Controllers/BookingController.php`: zentral hinterlegte Dokumente + freie Uploads pro Angebot. | Modul 6.3.4 | | 17.4 | **Direktbuchung im Backend** | Gesonderter Einstieg mit Auswahl im letzten Schritt: „Anlegen & Bestätigung senden" **oder** „Nur anlegen (keine Mail)". Nachversand bleibt jederzeit manuell möglich. | Modul 4.2.6 | | 17.5 | **Fewo-Überlappungen** | Kein harter DB-Constraint. Überlappungen werden im `FewoReservationService` nach klaren Turnustag-Regeln geprüft (An-/Abreise am selben Tag legitim). | Modul 8.2.5 | | 17.6 | **v2-Migrationsstrategie** | Stück für Stück mit Überarbeitung und Bereinigung (keine 1:1-Portierung). | Modul 12.2 | | 17.7 | **Frontend-Tooling** | Vite + dart-sass + schrittweise Bootstrap 5. Freigegeben. | Modul 1.4 | | 17.8 | **Abschaltplan Altsysteme** | Parallelbetrieb, bis Migration abgeschlossen. `sterntours.de/AdminController` zügig migrieren (Modul 9). `v2.stern-tours.de` mit Teil-Migrationen auf dem Live-Server, damit der Betrieb ununterbrochen weiterläuft. | Modul 12.2 | ### Direkt daraus folgende neue Bausteine - **Neue Tabellen:** `offer_versions`, `offer_access_tokens` (Modul 6). - **Neue Routen (öffentlich):** `/angebot/{token}`, `/angebot/{token}/accept`, `/angebot/{token}/decline` auf `sterntours.de` bzw. Subdomain. - **Neues Modul 15:** Kundenportal / Kunden-Login (eigener Block E, Zukunftsmodul). - **Neue Service-Klasse:** `FewoReservationService` mit Turnustag-Logik (Modul 8). - **Neue UI-Komponente:** Belegungskalender mit Wechseltag-Markierung (Modul 8.2.7). - **Direktbuchungs-Flag:** `silent_created` auf `bookings` (Modul 4). --- ## 18. Nächste konkrete Schritte (Start der Entwicklung) Nach dieser Abstimmung kann die Entwicklung starten. Sinnvolle erste Arbeitspakete, jeweils als eigene PRs / Tickets: 1. **A2 Quick Wins** (sofort, blockiert nichts): - Composer-Wildcards pinnen. - `navigation/cache/clear` absichern. - `booking/import` auf POST-only. - Pint + Larastan + CI aufsetzen. 2. **A3 Phase 1 live deployen** (Customer-Deduplizierung — steht auf Test, braucht Backup + CSV-Review). 3. **A1 UI-Baseline dokumentieren** (Inventar + Baseline-Spec, ohne bereits umzubauen). 4. **Modul 1.4 Frontend-Migration** als eigener Feature-Branch starten (Vite/dart-sass/Bootstrap 5). 5. **A3 Phase 2 vorbereiten** (Code-Anpassungen `Customer::$table`, `Lead::$table`, Repositories auf `inquiry_id`). Schritt 1–3 können parallel angegangen werden; Schritt 4 läuft im Hintergrund, bis der Pilot-View auf der neuen Pipeline läuft. Sobald A3 Phase 2 produktiv ist, ist der Weg frei für Modul 4 (Direktbuchung) und anschließend Modul 6 (Angebote).