phase 2 dev
This commit is contained in:
parent
5a7478907e
commit
ba48745809
59 changed files with 2692 additions and 1994 deletions
|
|
@ -110,7 +110,13 @@ echo " git status (sollte Phase-2 + Offers-Dateien als modifiziert zeigen):"
|
|||
git status --short | head -40
|
||||
echo
|
||||
echo " Verbliebene Phase-2-Marker im Code (sollten nun wieder da sein):"
|
||||
grep -rn "inquiry_id" app/ 2>/dev/null | head -5 || echo " (keine gefunden — Restore eventuell nicht vollständig)"
|
||||
PHASE2_HITS=$(grep -rn "inquiry_id" app/ 2>/dev/null | head -5 || true)
|
||||
if [[ -n "${PHASE2_HITS}" ]]; then
|
||||
echo "${PHASE2_HITS}"
|
||||
echo " → OK, Phase-2-Code ist wieder aktiv."
|
||||
else
|
||||
echo " (keine gefunden — Restore eventuell nicht vollständig)"
|
||||
fi
|
||||
echo
|
||||
|
||||
echo "=========================================="
|
||||
|
|
|
|||
305
dev/customer-bookings/phase-2-live-deploy.md
Normal file
305
dev/customer-bookings/phase-2-live-deploy.md
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
# Phase-2-Live-Deploy — Anleitung
|
||||
|
||||
**Ausgangssituation (nach Phase-1-Live-Deploy am 2026-04-17):**
|
||||
- Live: Phase 1 ✅ (Code + DB-Migrationen 25, 26)
|
||||
- Test: Phase 1 ✅ + Phase 2 ✅ (Code + DB-Migrationen 25–29, verifiziert)
|
||||
- Workspace (lokal): Phase 1 + Phase 2 + Offers-Code ready
|
||||
|
||||
Dieses Handbuch beschreibt, wie Phase 2 vom Test-Stand auf Live übertragen wird. Phase 2 umfasst zwei atomar zusammengehörige Teile plus eine kleine Ergänzung aus Phase 1:
|
||||
|
||||
1. **DB-Schema-Änderungen** (3 Migrationen, breaking):
|
||||
- `RENAME TABLE customer → contacts`
|
||||
- `RENAME TABLE lead → inquiries`
|
||||
- Spalte `booking.lead_id → booking.inquiry_id` (inkl. FK-Index-Neuaufbau)
|
||||
2. **Application-Code-Änderungen** (23 modifizierte Dateien):
|
||||
- Models `Customer`, `Contact`, `Lead`, `Booking` mit neuen `$table`-Werten bzw. Spaltennamen
|
||||
- Repositories, Controllers, Services, Commands, Views — alle Referenzen auf `booking.lead_id` → `inquiry_id` umgestellt; Raw-SQL auf `contacts`/`inquiries` umgestellt
|
||||
- Smoke-Test-Fixes (2026-04-17): `Lead::bookings()` mit explizitem FK `inquiry_id`; `orderColumn`-SQL in `ContactController`/`ReportController`/`ReportBookingController`/`ReportLeadsController` auf `contacts.*` gezogen.
|
||||
3. **Phase-1-Hotfixes — nullable Parameter in den Mail-Dir-Services** (3 modifizierte Dateien):
|
||||
- `app/Services/Booking.php`, `app/Services/Lead.php`, `app/Services/MailDirService.php`
|
||||
- `int $mailDirId` → `?int $mailDirId` bei `getCustomerMailName()` / `getCustomerMailEmails()` / `resolveModel()` (behebt Mail-Dialog-Crash wenn `customer_mails.mail_dir_id` / `lead_mails.mail_dir_id` NULL ist).
|
||||
- `string $subdir` → `?string $subdir` bei `setOutputDirs()` / `setOutputDir()` (behebt Mail-Dialog-Crash wenn `$mail_sdir_id` NULL ist, z.B. bei Entwürfen oder Top-Level-Ordnern).
|
||||
- Fachlich eigentlich Phase-1-Bugs (Laravel-10/PHP-8-strict-typing vs. `null`-Werte aus nullable DB-Spalten), die aber erst durch die Test-Durchläufe am 2026-04-17 sichtbar wurden. Wir liefern die Fixes zusammen mit Phase 2 aus, weil ein zweites separates Wartungsfenster für drei Service-Dateien nicht sinnvoll wäre und die Änderungen keinerlei DB-Abhängigkeit haben (also auch außerhalb der DB-Migration risikoarm sind).
|
||||
|
||||
> **Wichtig:** DB und Phase-2-Code müssen atomar gemeinsam live gehen. Zwischen den beiden gibt es kein kompatibles Fenster, deswegen ist ein **Maintenance-Window von ~10–20 min Pflicht**. Die 3 Phase-1-Hotfix-Services werden im selben rsync-Schritt mit hochgeladen.
|
||||
|
||||
---
|
||||
|
||||
## Vorbereitung (am Tag vor dem Deploy)
|
||||
|
||||
1. **Timing**: Wartungsfenster einplanen, idealerweise außerhalb der Geschäftszeiten. ~20 Minuten reichen erfahrungsgemäß.
|
||||
2. **Deploy-Freigabe**: Phase 1 auf Live läuft stabil (mindestens 24–48 h), keine Regressions-Meldungen.
|
||||
3. **Team-Kommunikation**: Mitarbeiter:innen informieren — während des Wartungsfensters keine Mails, keine Buchungen anlegen, keine PDFs drucken.
|
||||
|
||||
### Vorab-Kontrolle auf Live (lesend, keine Änderungen)
|
||||
|
||||
```bash
|
||||
# Live-Server: aktueller Stand?
|
||||
php artisan --version
|
||||
# → sollte 10.50+ sein (Phase 1 hat Laravel 10 gebracht)
|
||||
|
||||
php artisan migrate:status | grep phase1
|
||||
# → sollte beide phase1-Migrationen mit "Ran" zeigen
|
||||
|
||||
mysql -e "SHOW TABLES LIKE 'customer';"
|
||||
mysql -e "SHOW TABLES LIKE 'lead';"
|
||||
# → beide müssen noch existieren (Phase 2 NICHT ausgeführt)
|
||||
|
||||
mysql -e "SHOW COLUMNS FROM booking LIKE 'lead_id';"
|
||||
# → lead_id muss existieren
|
||||
```
|
||||
|
||||
Wenn einer dieser Checks nicht das erwartete Ergebnis liefert: **STOP**, klären bevor weitergemacht wird.
|
||||
|
||||
### Smoke-Tests auf Test (am Deploy-Tag, vor dem Live-Deploy)
|
||||
|
||||
Diese Liste einmal komplett auf `https://mein.sterntours.test` durchgehen. Wenn einer fehlschlägt → Fehler fixen, Live-Deploy verschieben.
|
||||
|
||||
- [ ] Login funktioniert, Dashboard lädt
|
||||
- [ ] **Buchungen**: `/booking` lädt, Filter- und Sortier-Funktionen arbeiten, Anfrage-Nr-Spalte zeigt korrekte Werte
|
||||
- [ ] **Buchungsdetail** öffnen, alle Tabs laden (Buchung, Mails, Notizen, Dokumente)
|
||||
- [ ] **PDF-Generierung** aus einer Buchung: Buchungsauftrag, Reisebestätigung, Voucher (Kunde + Agentur), Storno
|
||||
- [ ] **Anfragen**: `/lead` lädt, einzelne Anfrage öffnen
|
||||
- [ ] **Anfrage → Buchung erzeugen** (legt `booking.inquiry_id` korrekt an)
|
||||
- [ ] **Kontakte**: `/contacts` lädt, einzelner Kontakt öffnet, `leads_count` + `bookings_count` zeigen korrekte Werte (testet die Beziehungen über die neuen Tabellennamen)
|
||||
- [ ] **Kontakt-Duplikate**: `/contact/duplicates` lädt, Zähler stimmen
|
||||
- [ ] **Mail-Versand** aus einer Buchung (Eintrag in `customer_mails.lead_id` muss identisch zur `booking.inquiry_id` sein)
|
||||
- [ ] **Mail-Dialog öffnen** bei Buchung und Anfrage (Modal "Neue Mail schreiben" / "Mail anzeigen") — testet den nullable-`mail_dir_id`-Fix in `Services\Booking::getCustomerMailName()` / `Services\Lead::getCustomerMailName()`.
|
||||
- [ ] **Admin-Reports**: `/admin/report`, `/admin/report-bookings`, `/admin/report-provider`, `/admin/report-leads` laden UND jeweils einmal nach Kundenname sortieren (testet die `orderColumn`-Fixes für `contacts.firstname/name`) + CSV-Export
|
||||
- [ ] **BookingImport über API** (falls testbar — feuert gegen `/api/booking/import`) legt Datensatz mit korrekter `inquiry_id` an
|
||||
- [ ] Newsletter-Sync-Command als Dry-Run: `php artisan newsletter:sync-kulturreisen --dry-run` (falls implementiert)
|
||||
|
||||
---
|
||||
|
||||
## Deploy-Ablauf Live
|
||||
|
||||
### Schritt 1 — Upload-Liste generieren (lokal)
|
||||
|
||||
Der Phase-2-Delta gegen Phase 1: **3 neue Migrationen + 24 modifizierte Dateien (Phase 2) + 3 Phase-1-Hotfix-Services = 30 Dateien.** Exakte Liste:
|
||||
|
||||
```bash
|
||||
cd /workspace/mein.sterntours.de
|
||||
cat > /tmp/phase2-files-to-upload.txt <<'EOF'
|
||||
database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php
|
||||
database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php
|
||||
database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php
|
||||
app/Models/Booking.php
|
||||
app/Models/Customer.php
|
||||
app/Models/Contact.php
|
||||
app/Models/Lead.php
|
||||
app/Repositories/BookingPDFRepository.php
|
||||
app/Repositories/LeadRepository.php
|
||||
app/Repositories/CustomerMailRepository.php
|
||||
app/Http/Controllers/RequestController.php
|
||||
app/Http/Controllers/API/BookingController.php
|
||||
app/Http/Controllers/Admin/ReportController.php
|
||||
app/Http/Controllers/Admin/ReportBookingController.php
|
||||
app/Http/Controllers/Admin/ReportProviderController.php
|
||||
app/Http/Controllers/Admin/ReportLeadsController.php
|
||||
app/Http/Controllers/LeadController.php
|
||||
app/Http/Controllers/CustomerController.php
|
||||
app/Http/Controllers/ContactController.php
|
||||
app/Services/BookingImport.php
|
||||
app/Services/Booking.php
|
||||
app/Services/Lead.php
|
||||
app/Services/MailDirService.php
|
||||
app/Console/Commands/SyncNewsletterKulturreisen.php
|
||||
app/Console/Commands/ContactsFindDuplicates.php
|
||||
app/Console/Commands/ContactsMergeDuplicates.php
|
||||
resources/views/customer/mail/modal-show-mail-inner.blade.php
|
||||
resources/views/contact/index.blade.php
|
||||
resources/views/pdf/components/booking_head.blade.php
|
||||
resources/views/pdf/components/booking_header.blade.php
|
||||
EOF
|
||||
|
||||
wc -l /tmp/phase2-files-to-upload.txt
|
||||
# → sollte 30 zeigen (3 Migrationen + 24 Phase-2-Code + 3 Phase-1-Hotfix-Services)
|
||||
```
|
||||
|
||||
**Erklärung zu den zusätzlichen Dateien gegenüber einer rein strukturellen Phase-2-Liste:**
|
||||
- `ReportBookingController.php` — Smoke-Test-Fix am 2026-04-17: `orderColumn('customer.firstname/name', …)` → `contacts.*`. War schon vor dem Fix nicht perfekt, fiele aber beim ersten Sortier-Klick nach Phase-2-Rename hart auf.
|
||||
- `resources/views/contact/index.blade.php` — Smoke-Test-Fix am 2026-04-17: DataTables-Column-Definition `name: 'customer.id'` → `name: 'contacts.id'` (+ passender `orderColumn('contacts.id', …)` / `filterColumn('contacts.id', …)` im `ContactController`). Yajra verwendet `name:` als raw SQL-Spalte, wenn kein `orderColumn` matched — deswegen muss der Identifier zwischen Blade und Controller konsistent sein und dem neuen Tabellennamen entsprechen.
|
||||
- `app/Services/Booking.php`, `Lead.php`, `MailDirService.php` — Phase-1-Hotfix: nullable-Parameter (`?int $mailDirId`, `?string $subdir`). Behebt Laravel-10/PHP-8-strict-typing-Fehler bei Mail-Dialogen mit NULL-Werten aus `customer_mails` / `lead_mails`.
|
||||
|
||||
> **Ausdrücklich NICHT mit hochladen:** Phase-3-/Phase-4-Migrationen (5+6 Stück), Offers-Migrationen (7 Stück), Offer-Models (6 Stück), `config/filesystems.php` (offer-Disk). Diese kommen erst mit späteren Modulen und würden auf Live ungewollt Tabellen anlegen oder Code-Erwartungen brechen.
|
||||
|
||||
### Schritt 1b — Upload-Liste gegen den Workspace verifizieren (Sanity-Check)
|
||||
|
||||
Damit sich hier kein Tippfehler einschleicht, einmal prüfen, dass alle 29 Einträge tatsächlich im Workspace existieren:
|
||||
|
||||
```bash
|
||||
cd /workspace/mein.sterntours.de
|
||||
MISSING=0
|
||||
while read f; do
|
||||
if [[ ! -f "$f" ]]; then
|
||||
echo "FEHLT: $f"
|
||||
MISSING=$((MISSING+1))
|
||||
fi
|
||||
done < /tmp/phase2-files-to-upload.txt
|
||||
echo "---"
|
||||
echo "Fehlend: $MISSING (sollte 0 sein)"
|
||||
echo "Gesamt: $(wc -l < /tmp/phase2-files-to-upload.txt)"
|
||||
```
|
||||
|
||||
### Schritt 2 — DB-Backup auf Live
|
||||
|
||||
```bash
|
||||
# Auf dem Live-Server, vor dem Deploy
|
||||
TIMESTAMP=$(date +%Y-%m-%d_%H%M)
|
||||
mysqldump --single-transaction --routines --triggers \
|
||||
--databases DEIN_DB_NAME \
|
||||
| gzip > /backup/live-db-before-phase2-${TIMESTAMP}.sql.gz
|
||||
|
||||
# Größe prüfen:
|
||||
ls -lh /backup/live-db-before-phase2-${TIMESTAMP}.sql.gz
|
||||
```
|
||||
|
||||
Falls `mysqldump` nicht zur Hand: das Hoster-Tool nutzen (phpMyAdmin-Export, Plesk/cPanel-Backup-Feature, etc.).
|
||||
|
||||
### Schritt 3 — Maintenance-Mode an
|
||||
|
||||
```bash
|
||||
cd /pfad/zu/mein.sterntours.de
|
||||
php artisan down --render="errors::503" --secret="phase-2-deploy-$(date +%s)"
|
||||
# → Secret-URL wird ausgegeben, z.B.:
|
||||
# https://domain/phase-2-deploy-1746123456
|
||||
# Damit kannst du während der Wartung selbst noch als Admin auf die Seite.
|
||||
```
|
||||
|
||||
### Schritt 4 — Upload per rsync
|
||||
|
||||
```bash
|
||||
# Vom lokalen Workspace (oder Test-Server) auf Live
|
||||
rsync -av --files-from=/tmp/phase2-files-to-upload.txt \
|
||||
/workspace/mein.sterntours.de/ \
|
||||
user@live-server:/pfad/zu/mein.sterntours.de/
|
||||
```
|
||||
|
||||
Alternative mit scp, falls rsync nicht verfügbar:
|
||||
```bash
|
||||
while read f; do
|
||||
scp "/workspace/mein.sterntours.de/$f" "user@live-server:/pfad/zu/mein.sterntours.de/$f"
|
||||
done < /tmp/phase2-files-to-upload.txt
|
||||
```
|
||||
|
||||
### Schritt 5 — Code-Caches leeren
|
||||
|
||||
Auf dem Live-Server:
|
||||
|
||||
```bash
|
||||
cd /pfad/zu/mein.sterntours.de
|
||||
php artisan cache:clear
|
||||
php artisan config:clear
|
||||
php artisan route:clear
|
||||
php artisan view:clear
|
||||
composer dump-autoload --optimize
|
||||
```
|
||||
|
||||
### Schritt 6 — DB-Migrationen gezielt ausführen
|
||||
|
||||
> **Wichtig: KEIN `php artisan migrate` ohne `--path`!**
|
||||
> Wenn man nacktes `migrate` aufruft, werden auch die im Workspace liegenden Phase-3-, Phase-4- und Offer-Migrationen ausgeführt, die noch nicht deployreif sind. Deswegen gezielt per `--path`:
|
||||
|
||||
```bash
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php --force
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php --force
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php --force
|
||||
|
||||
# Verifikation
|
||||
php artisan migrate:status | grep phase2
|
||||
# → alle drei sollten jetzt "Ran" zeigen
|
||||
```
|
||||
|
||||
Erwartete Dauer: wenige Sekunden. `RENAME TABLE` ist in MySQL eine Metadaten-Operation und läuft unabhängig von der Tabellengröße fast instantan. Der Spaltenrename `booking.lead_id → inquiry_id` ist je nach MySQL-Version ebenfalls schnell (MySQL 8+ mit `ALGORITHM=INSTANT`) oder dauert ein paar Sekunden (mit Fallback).
|
||||
|
||||
### Schritt 7 — Production-Caches neu aufbauen
|
||||
|
||||
```bash
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
```
|
||||
|
||||
### Schritt 8 — Maintenance-Mode aus
|
||||
|
||||
```bash
|
||||
php artisan up
|
||||
```
|
||||
|
||||
### Schritt 9 — Live-Smoke-Tests
|
||||
|
||||
Dieselbe Checkliste wie bei Test (oben). Besonders aufpassen:
|
||||
- PDF-Erzeugung (Voucher einer aktuellen Buchung)
|
||||
- Mail-Versand aus einer Buchung
|
||||
- Anfrage-Erstellung + daraus Buchung ableiten
|
||||
- `/contacts/duplicates` öffnen — zeigt korrekte Counts
|
||||
|
||||
Bei Problemen sofort zu **Rollback-Plan** (unten) wechseln.
|
||||
|
||||
---
|
||||
|
||||
## Rollback-Plan (falls Phase 2 auf Live scheitert)
|
||||
|
||||
Der Rollback muss ebenfalls atomar passieren: **Code und DB zusammen zurück.**
|
||||
|
||||
### Schritt A — Maintenance-Mode an
|
||||
|
||||
```bash
|
||||
php artisan down --render="errors::503"
|
||||
```
|
||||
|
||||
### Schritt B — DB zurückrollen
|
||||
|
||||
Option 1 (sauber, per Migration):
|
||||
```bash
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php --force
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php --force
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php --force
|
||||
```
|
||||
(Die Migrationen haben symmetrische `down()`-Methoden, die die Renames rückgängig machen.)
|
||||
|
||||
Option 2 (Notfall, falls `migrate:rollback` fehlschlägt):
|
||||
```bash
|
||||
gunzip < /backup/live-db-before-phase2-${TIMESTAMP}.sql.gz | mysql DEIN_DB_NAME
|
||||
```
|
||||
|
||||
### Schritt C — Code zurück
|
||||
|
||||
Entweder die pre-Phase-2-Versionen der 22 Dateien zurückspielen (z.B. aus dem DB-Backup-Zeitpunkt-Dateisystem-Backup), oder mit git Revert-Commit:
|
||||
```bash
|
||||
# Wenn Live-Server ein Git-Checkout ist:
|
||||
git checkout <phase-1-commit-id> -- app/ resources/ config/
|
||||
```
|
||||
|
||||
### Schritt D — Caches + Up
|
||||
|
||||
```bash
|
||||
php artisan cache:clear && php artisan config:clear && php artisan view:clear && php artisan route:clear
|
||||
composer dump-autoload
|
||||
php artisan config:cache && php artisan route:cache && php artisan view:cache
|
||||
php artisan up
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dokumentation nach erfolgreichem Live-Deploy
|
||||
|
||||
1. `dev/customer-bookings/umsetzung.md` updaten: Phase 2 auf Live per 2026-MM-DD.
|
||||
2. Dieses Handbuch als "durchgeführt" markieren, ggf. Learnings ergänzen.
|
||||
3. Git-Commit auf Live-Server (falls Git-basiert): mit Tag `deploy/phase-2-live-2026-MM-DD`.
|
||||
4. Nächster Schritt: Entscheidung, wann Phase 3 (Participants-Unification), Phase 4 (Communications-Unification) oder das Offers-Modul an die Reihe kommen.
|
||||
|
||||
---
|
||||
|
||||
## Hintergrund: warum das Maintenance-Window nötig ist
|
||||
|
||||
Zwischen DB-Rename und Code-Deploy ist der Zustand inkompatibel:
|
||||
|
||||
| Zustand | DB hat | Code erwartet | Resultat |
|
||||
|---|---|---|---|
|
||||
| Vor Deploy | `customer`, `lead`, `booking.lead_id` | `customer`, `lead`, `booking.lead_id` | OK |
|
||||
| DB-migriert, Code alt | `contacts`, `inquiries`, `booking.inquiry_id` | `customer`, `lead`, `booking.lead_id` | **Fehler: Table doesn't exist** |
|
||||
| DB alt, Code neu | `customer`, `lead`, `booking.lead_id` | `contacts`, `inquiries`, `booking.inquiry_id` | **Fehler: Table doesn't exist** |
|
||||
| Nach Deploy | `contacts`, `inquiries`, `booking.inquiry_id` | `contacts`, `inquiries`, `booking.inquiry_id` | OK |
|
||||
|
||||
Deswegen: Maintenance-Mode an → Code + DB gleichzeitig → Maintenance-Mode aus. Alles innerhalb eines Fensters, in dem keine Requests an die App laufen.
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
# Umsetzung: Neustrukturierung Customer / Lead / Booking
|
||||
|
||||
**Status:** Phase 1 auf Testsystem abgeschlossen — Contacts-Modul live; **Phase-2-App-Code vorbereitet (deploy-bereit)**
|
||||
**Status:** Phase 1 auf **Live** abgeschlossen (2026-04-17); Phase 2 auf **Test** erfolgreich migriert und verifiziert — bereit für Live-Deploy.
|
||||
**Erstellt:** April 2025
|
||||
**Konzept:** [konzept.md](konzept.md)
|
||||
**Letzte Aktualisierung:** 2026-04-17
|
||||
**Konzept:** [konzept.md](konzept.md)
|
||||
**Deploy-Handbücher:** [phase-1-live-deploy.md](phase-1-live-deploy.md) · [phase-2-live-deploy.md](phase-2-live-deploy.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -10,12 +12,70 @@
|
|||
|
||||
| Phase | Status | Deployed auf Test? | Deployed auf Live? |
|
||||
|-------|--------|-------------------|-------------------|
|
||||
| Phase 1 — Contact-Deduplizierung | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein |
|
||||
| Phase 1 — Contacts-Modul (neuer Code) | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein |
|
||||
| Phase 2 — App-Code (Models/Repos/Controller/Views auf `contacts`/`inquiries`/`inquiry_id`) | ✅ Abgeschlossen | ⬜ Nein | ⬜ Nein |
|
||||
| Phase 2 — Tabellen-Migrationen (3 Migrationsdateien) | ⬜ Ausstehend (Code ist deploy-ready) | ⬜ Nein | ⬜ Nein |
|
||||
| Phase 3 — Participants konsolidieren | ⬜ Ausstehend | ⬜ Nein | ⬜ Nein |
|
||||
| Phase 4 — Communications / Notices / Attachments | ⬜ Ausstehend | ⬜ Nein | ⬜ Nein |
|
||||
| Phase 1 — Contact-Deduplizierung | ✅ Abgeschlossen | ✅ Ja | ✅ **2026-04-17** |
|
||||
| Phase 1 — Contacts-Modul (neuer Code) | ✅ Abgeschlossen | ✅ Ja | ✅ **2026-04-17** |
|
||||
| Phase 2 — App-Code (Models/Repos/Controller/Views auf `contacts`/`inquiries`/`inquiry_id`) | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein (Deploy-Handbuch fertig) |
|
||||
| Phase 2 — Tabellen-Migrationen (3 Migrationsdateien) | ✅ Abgeschlossen | ✅ **Ja, Batch 27–29** | ⬜ Nein (Deploy-Handbuch fertig) |
|
||||
| Phase 2 — Smoke-Test-Fixes 2026-04-17 (`Lead::bookings()` FK; `orderColumn`-SQL in 4 Controllern; Blade-Column-`name:` in `contact/index.blade.php`) | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein (in Phase-2-Deploy gebündelt) |
|
||||
| Phase-1-Hotfix — nullable Parameter (`?int $mailDirId`, `?string $subdir`) in 3 Service-Klassen (Mail-Dialoge mit NULL-Werten) | ✅ Abgeschlossen | ✅ Ja | ⬜ Nein (in Phase-2-Deploy gebündelt, siehe Handbuch) |
|
||||
| Phase 3 — Participants konsolidieren | ⬜ Ausstehend (Migrationen geschrieben, Code noch nicht) | ⬜ Nein | ⬜ Nein |
|
||||
| Phase 4 — Communications / Notices / Attachments | ⬜ Ausstehend (Migrationen geschrieben, Code noch nicht) | ⬜ Nein | ⬜ Nein |
|
||||
|
||||
### Verifikation Phase 2 auf Test (2026-04-17)
|
||||
|
||||
Smoke-Queries nach erfolgter Migration:
|
||||
|
||||
```
|
||||
Tabellen:
|
||||
customer NOT FOUND ← umbenannt ✓
|
||||
contacts EXISTS ← neu ✓
|
||||
lead NOT FOUND ← umbenannt ✓
|
||||
inquiries EXISTS ← neu ✓
|
||||
booking EXISTS ✓
|
||||
|
||||
booking-Spalten:
|
||||
lead_id NOT FOUND ← umbenannt ✓
|
||||
inquiry_id EXISTS ← neu ✓
|
||||
|
||||
Eloquent-Queries:
|
||||
Contact::count() = 19.156 (ohne merged)
|
||||
Customer::count() = 22.283 (alle, inkl. merged — Legacy-Model)
|
||||
Lead::count() = 19.543
|
||||
Booking::count() = 10.648
|
||||
Booking->inquiry_id funktioniert
|
||||
Booking->lead() Relation mit expl. FK inquiry_id funktioniert
|
||||
```
|
||||
|
||||
### Smoke-Test-Findings 2026-04-17 (Test-Durchlauf nach Phase-2-Restore)
|
||||
|
||||
Während der UI-Smoke-Tests auf `mein.sterntours.test` traten drei unterschiedliche Bug-Klassen auf, alle auf Test gefixt und in die Phase-2-Deploy-Liste aufgenommen.
|
||||
|
||||
**1. Eloquent-Relation ohne expliziten FK — `Lead::bookings()`**
|
||||
- Fehler: `/leads` → `Column not found: 'booking.lead_id'`.
|
||||
- Ursache: `$this->hasMany(Booking::class)` ohne 2. Argument ⇒ Laravel leitet den FK aus dem Model-Namen ab (`Lead` → `lead_id`), der nach dem Rename nicht mehr existiert.
|
||||
- Fix: `app/Models/Lead.php` Zeile 249–253: `$this->hasMany(Booking::class, 'inquiry_id')`.
|
||||
|
||||
**2. Hartcodierte Tabellenqualifier in `orderColumn`-SQL-Strings + Blade-Column-`name:`**
|
||||
- Fehler A: `/contacts` → `Column not found: 'customer.id' in 'order clause'`. Behoben durch SQL-Umstellung im Controller — blieb aber bestehen, weil Yajra bei fehlendem `orderColumn`-Match den `name:`-String aus der Blade-Column-Definition als raw SQL verwendet.
|
||||
- Fehler B (gleiche Ursache, 2. Runde): `/contacts` → derselbe Fehler trotz Controller-Fix. Root Cause: In `resources/views/contact/index.blade.php` Zeile 188 stand `name: 'customer.id'`. Yajra matched `orderColumn()` auf diesen `name:`-String — mein erster Fix mit `orderColumn('id', …)` hat also nicht getroffen.
|
||||
- Ursache zusammengefasst: Yajra-DataTables-`orderColumn()` bekommt als 2. Argument eine Raw-SQL-Expression; **und** der `name:`-Wert einer DataTable-Column muss exakt zum 1. Argument von `orderColumn()` / `filterColumn()` passen — sonst fällt Yajra zurück auf "Name wörtlich als SQL einsetzen".
|
||||
- Fix in 5 Dateien:
|
||||
- `app/Http/Controllers/ContactController.php` — `orderColumn('contacts.id' / 'deleted_at', …)`, `filterColumn('contacts.id', …)`
|
||||
- `resources/views/contact/index.blade.php` — Column-Name `customer.id` → `contacts.id`
|
||||
- `app/Http/Controllers/Admin/ReportController.php` — 2 Blöcke (`customer.firstname $1` → `contacts.firstname $1` etc.)
|
||||
- `app/Http/Controllers/Admin/ReportBookingController.php` — 2 Blöcke
|
||||
- `app/Http/Controllers/Admin/ReportLeadsController.php` — `$orderByNum`-Array
|
||||
- Bewusst nicht angefasst: die 1. Argumente von `addColumn` / `rawColumns` (`customer.fullName`, `lead.status_id`) — das sind reine DataTables-Frontend-Identifier, die Yajra über den Eloquent-Relation-Mechanismus auflöst (Model `Customer`/`Lead` → `$table = 'contacts'`/`'inquiries'`), und die 3-teiligen `booking.customer.firstname`-Strings in `ReportController.php` und `ReportProviderController.php` (Yajra-Relation-Notation bzw. seit jeher kein gültiges SQL, durch Phase 2 nicht schlimmer).
|
||||
|
||||
**3. Strict-Type-Regression aus Phase 1 (Laravel-10/PHP-8) — zwei Varianten**
|
||||
- Fehler A: `App\Services\Booking::getCustomerMailName(): Argument #2 ($mailDirId) must be of type int, null given` in `modal-show-mail-inner.blade.php` Zeile 92.
|
||||
- Fehler B: `App\Services\Booking::setOutputDirs(): Argument #2 ($subdir) must be of type string, null given` in derselben kompilierten View, Zeile 101.
|
||||
- Ursache: Blade-Views übergeben `$mail_dir_id` bzw. `$mail_sdir_id` direkt aus DB-Feldern, die nullable sein können (Entwurfs-Mails, Top-Level-Ordner, Mails ohne Subdir). Unter Laravel 8 / PHP 7 wurde `null` stillschweigend zu `0` / `""` gecastet; PHP 8 strict types verweigern das.
|
||||
- Fix in drei Dateien:
|
||||
- `app/Services/Booking.php` — `getCustomerMailName()`, `getCustomerMailEmails()`: `int $mailDirId` → `?int`; `setOutputDirs()`: `string $subdir` → `?string`.
|
||||
- `app/Services/Lead.php` — identische Änderungen (symmetrische API).
|
||||
- `app/Services/MailDirService.php` — `getCustomerMailName()`, `getCustomerMailEmails()`, `resolveModel()` auf `?int`; `setOutputDir()` auf `?string`; in `resolveModel()` zusätzlich ein früher `return null`-Guard.
|
||||
- Das ist **fachlich ein Phase-1-Bug**, der nichts mit Customer→Contacts zu tun hat. Wir deployen ihn trotzdem zusammen mit Phase 2, weil die Services keine DB-Abhängigkeit haben und ein separates Wartungsfenster unnötig wäre.
|
||||
|
||||
### Was in Phase 1 umgesetzt wurde
|
||||
|
||||
|
|
@ -178,33 +238,46 @@ php artisan migrate:rollback --path=database/migrations/2025_04_15_100001_phase1
|
|||
- Routen-Namen (`lead_detail`, `lead_index`) — bleiben als Aliase, um Links in Views/Mails/Logs nicht zu brechen.
|
||||
|
||||
### Schritt 1: Migrationen einspielen
|
||||
|
||||
**Auf Test (2026-04-17 erfolgreich durchgeführt, Batch 27–29):**
|
||||
```bash
|
||||
# Backup erstellen!
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php --force
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php --force
|
||||
php artisan migrate --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php --force
|
||||
```
|
||||
|
||||
> **Wichtig — `--path` verwenden!** Ohne `--path` würde `php artisan migrate` auch die noch im Workspace liegenden Phase-3-, Phase-4- und Offers-Migrationen ausführen, die noch nicht deployreif sind. Einzeln per `--path` hält den Deploy auf exakt diese drei Dateien beschränkt.
|
||||
|
||||
**Auf Live:** Ablauf siehe [phase-2-live-deploy.md](phase-2-live-deploy.md). Der Live-Deploy muss innerhalb eines Wartungsfensters passieren, weil DB und Code atomar zusammengehen müssen.
|
||||
|
||||
### Ergebnis-Prüfung
|
||||
```sql
|
||||
-- Tabellen vorhanden?
|
||||
SHOW TABLES LIKE 'contacts';
|
||||
SHOW TABLES LIKE 'inquiries';
|
||||
-- Spalte umbenannt?
|
||||
SHOW COLUMNS FROM booking LIKE 'inquiry_id';
|
||||
-- FK vorhanden?
|
||||
SELECT * FROM information_schema.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_NAME = 'booking' AND COLUMN_NAME = 'inquiry_id';
|
||||
```
|
||||
|
||||
Oder via Laravel-Tinker:
|
||||
```bash
|
||||
php artisan tinker --execute="
|
||||
echo Schema::hasTable('contacts') ? 'contacts OK' : 'contacts FEHLT'; echo PHP_EOL;
|
||||
echo Schema::hasTable('inquiries') ? 'inquiries OK' : 'inquiries FEHLT'; echo PHP_EOL;
|
||||
echo Schema::hasColumn('booking', 'inquiry_id') ? 'booking.inquiry_id OK' : 'FEHLT'; echo PHP_EOL;
|
||||
"
|
||||
```
|
||||
|
||||
### Rollback Phase 2
|
||||
```bash
|
||||
# In umgekehrter Reihenfolge
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200003_phase2_rename_booking_lead_id_to_inquiry_id.php --force
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200002_phase2_rename_lead_to_inquiries.php --force
|
||||
php artisan migrate:rollback --path=database/migrations/2025_04_15_200001_phase2_rename_customer_to_contacts.php --force
|
||||
```
|
||||
|
||||
> **Wichtig:** Rollback MUSS zusammen mit Code-Revert passieren (Code erwartet sonst die falschen Tabellennamen). Details im [phase-2-live-deploy.md](phase-2-live-deploy.md#rollback-plan-falls-phase-2-auf-live-scheitert).
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Participants konsolidieren
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue