Enth\u00e4lt gemischt: Laravel-10-Upgrade + Phase 1 (Contacts-Modul, Duplicats-Commands, Soft-Delete+Merge-Fields) + Phase 2 Code-Umstellungen (inquiry_id, $table='contacts'/'inquiries') + Offers-Modul (Migrationen, Models, offer_id in Booking, offer-Disk in filesystems.php). Phase 2 + Offers werden im folgenden Commit nach dev/backups/phase2-offers-2026-04-17/ verschoben, damit der Workspace auf Phase-1-only (= Test-System-Stand) reduziert ist und direkt auf Live deploybar wird. Tarball-Backup zus\u00e4tzlich unter: ../backups-safety/workspace-pre-phase1-rollback-2026-04-17.tar.gz Made-with: Cursor
732 lines
50 KiB
Markdown
732 lines
50 KiB
Markdown
# 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 koexistieren 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).
|