- published_at = Legacy-created_at (Publikationsdatum, Frontend-Sortier- schlüssel) statt updated_at, das bei Alt-Daten oft den Massen-/Migrations- stempel trägt und ein falsches "frisches" Datum erzeugte. - created_at/updated_at per forceFill direkt im Import gesetzt (waren nicht fillable, wurden still verworfen) – Import allein ist jetzt datumssauber. - legacy:fix-timestamps korrigiert zusätzlich published_at (status=published). - PM-UUID bleibt beim Re-Import/Delta-Lauf erhalten (kein Neu-Würfeln). - MIGRATION-STEPS auf Zwei-Phasen-Strategie umgestellt: migrate, Phase 1 mit --force, Phase 2 (Delta) ohne --force, Grandfather-Re-Run, Idempotenz-Tabelle. - Tests: LegacyPressReleaseDateImportTest (published_at-Quelle, Entwurf, Force-Re-Import erhält UUID + Datum). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
127 lines
6.8 KiB
Markdown
127 lines
6.8 KiB
Markdown
# Migration Steps – aktuelles Runbook
|
||
|
||
Stand: 2026-06-17. Spiegelt den aktuell implementierten Command-Stand. Details und Go-Live-Kontext stehen in `05-DATABASE-MERGE.md` und `08-PROGRESS.md`; die Live-Settings/Commands rund um den Go-Live in `../../docs/weiteres/Live-Deployment-Checkliste (WS-7).md`.
|
||
|
||
## Zwei-Phasen-Strategie (kurzer Ausfall)
|
||
|
||
Der Import ist **wiederholbar**. Vorgesehener Ablauf:
|
||
|
||
1. **Phase 1 – Voll-Import (jetzt):** Neues System auf `pressekonto.com` aufsetzen, Schema migrieren, kompletten Bestand mit `--force` importieren, Frontends auf Test-Domain verifizieren.
|
||
2. **Phase 2 – Delta-Lauf (beim Cutover):** Altsystem kurz einfrieren, dieselben Commands **ohne `--force`** erneut laufen lassen → bestehende Datensätze bleiben unangetastet, nur neue/zwischenzeitlich entstandene werden nachgezogen. Danach DNS umstellen.
|
||
|
||
> **Die `--force`-Disziplin ist entscheidend:**
|
||
> - **MIT `--force`** (nur Phase 1): überschreibt bestehende Datensätze.
|
||
> - **OHNE `--force`** (Phase 2): überspringt bereits importierte Datensätze (`legacy_import_map`), importiert ausschließlich Neue. Genau das Verhalten für einen sauberen Delta-Lauf.
|
||
|
||
---
|
||
|
||
## Phase 0 – Schema (einmalig, vor dem ersten Import)
|
||
|
||
```bash
|
||
php artisan migrate --force
|
||
```
|
||
|
||
Ohne das Schema schlägt jeder Import-Schritt fehl. Auf dem Live-System ist das Teil der WS-7-Checkliste.
|
||
|
||
## Dry-Run (vor jedem echten Lauf)
|
||
|
||
```bash
|
||
php artisan legacy:import --source=all --dry-run
|
||
php artisan legacy:archive-invoices --dry-run
|
||
php artisan legacy:grandfather-subscriptions --dry-run
|
||
php artisan legacy:verify --no-report
|
||
php artisan legacy:migrate-media --portal=all --type=all --base-path=dev/migration --dry-run
|
||
```
|
||
|
||
Hinweis: `legacy:archive-invoices` importiert die Legacy-Rechnungen vollständig in `legacy_invoices`, inkl. Status/User-Zuordnung, `raw_snapshot`, `pdf_payload` und Report. Die PDF-Erzeugung erfolgt im Customer-Bereich bei Abruf aus diesen Archivdaten.
|
||
|
||
---
|
||
|
||
## Phase 1 – Voll-Import (mit `--force`, korrekte Reihenfolge)
|
||
|
||
```bash
|
||
php artisan legacy:import --source=presseecho --step=categories --force
|
||
php artisan legacy:import --source=all --step=users --force
|
||
php artisan legacy:import --source=presseecho --step=companies --force
|
||
php artisan legacy:import --source=businessportal24 --step=companies --force
|
||
php artisan legacy:import --source=presseecho --step=contacts --force
|
||
php artisan legacy:import --source=businessportal24 --step=contacts --force
|
||
php artisan legacy:import --source=presseecho --step=press-releases --force
|
||
php artisan legacy:import --source=businessportal24 --step=press-releases --force
|
||
php artisan legacy:import --step=link-associations --force
|
||
php artisan legacy:archive-invoices
|
||
php artisan legacy:grandfather-subscriptions
|
||
php artisan legacy:fix-timestamps
|
||
php artisan legacy:migrate-media --portal=all --type=all --base-path=dev/migration
|
||
php artisan legacy:verify
|
||
```
|
||
|
||
Reihenfolge ist wegen FKs Pflicht (companies vor contacts vor press-releases; users zuerst). Der Schritt `--step=users` importiert neben `sf_guard_user` auch `sf_guard_user_profile` in die neue Tabelle `profiles`.
|
||
|
||
### Datums-Migration der Pressemitteilungen (wichtig für die Frontend-Sortierung)
|
||
|
||
Das Frontend sortiert/clustert nach `published_at`. Der Importer setzt daher:
|
||
|
||
- `created_at`/`updated_at` = Legacy-`created_at`/`updated_at` (per `forceFill`, da nicht fillable) — das echte Anlage-/Bearbeitungsdatum, **nicht** das Importdatum.
|
||
- `published_at` = Legacy-`created_at` (Publikationsdatum) für veröffentlichte Meldungen. Bewusst **nicht** `updated_at` — dort steht bei vielen Alt-Datensätzen der Massen-/Migrationsstempel (z. B. `2026-04-…`), der sonst als „frisches" Veröffentlichungsdatum erschiene.
|
||
|
||
`legacy:fix-timestamps` ist der schnelle Cross-DB-Resync (gleicher MySQL-Server) und korrigiert `created_at`, `updated_at` **und** `published_at` (für `status=published`) authoritativ. Bei einem reinen Delta-Lauf ohne Re-Import diesen Schritt mitlaufen lassen.
|
||
|
||
---
|
||
|
||
## Phase 2 – Delta-Lauf beim Cutover (OHNE `--force`)
|
||
|
||
Altsystem einfrieren, dann:
|
||
|
||
```bash
|
||
# Vorab sehen, was neu dazukommt
|
||
php artisan legacy:import --source=all --dry-run
|
||
|
||
# Nur neue Datensätze nachziehen (kein --force!)
|
||
php artisan legacy:import --source=all
|
||
php artisan legacy:archive-invoices
|
||
php artisan legacy:grandfather-subscriptions # replay-fähig: aktualisiert bestehende, ergänzt neue
|
||
php artisan legacy:fix-timestamps # korrigiert created_at/updated_at/published_at
|
||
php artisan legacy:verify
|
||
|
||
# Medien NICHT mit --force – würde im neuen System gepflegte Logos/Bilder überschreiben.
|
||
# Nur falls neue Medien fehlen, gezielt ohne --force nachladen:
|
||
# php artisan legacy:migrate-media --portal=all --type=all --base-path=dev/migration
|
||
```
|
||
|
||
Danach DNS auf das neue System umstellen. `legacy:grandfather-subscriptions` läuft **nach** `legacy:archive-invoices`, weil es die aktiven, jährlich wiederkehrenden Zahlungsvereinbarungen aus dem Rechnungsarchiv ableitet (jüngste Rechnung pro Vereinbarung mit `payment_option.type = recurring` und `user_payment_option.status = active`) und als `grandfathered` in `user_payment_options` schreibt (Beträge netto in `legacy_conditions.net_cents`). Die nächste Rechnung stellt danach der tägliche MAN-Kreis-Lauf (`billing:generate-manual-invoices`) zum gewohnten Rhythmus aus. Optionen: `--dry-run`, `--as-of=`, `--grace-months=12` (älter überfällige Vereinbarungen gelten als stale und bleiben reines Archiv).
|
||
|
||
---
|
||
|
||
## Idempotenz / Replay-Sicherheit (Kurzreferenz)
|
||
|
||
Alle Schritte schreiben per `updateOrCreate`/`insertOrIgnore` über `(legacy_portal, legacy_id)` bzw. `legacy_import_map`; **kein Truncate, kein Delete**. Re-Run-Verhalten:
|
||
|
||
| Schritt | ohne `--force` | mit `--force` | zieht Neue nach |
|
||
|---|---|---|---|
|
||
| categories / users / companies / contacts | SKIP | UPDATE | ja |
|
||
| press-releases | SKIP | UPDATE (uuid + slug bleiben erhalten) | ja |
|
||
| link-associations | `insertOrIgnore` (idempotent) | — | ja |
|
||
| archive-invoices | SKIP | UPDATE | ja |
|
||
| grandfather-subscriptions | UPDATE/INSERT je Vereinbarung | — | ja |
|
||
| fix-timestamps | Raw-UPDATE (idempotent) | — | ja |
|
||
| migrate-media | SKIP (Pfad-Check) | **OVERWRITE** (Vorsicht) | ja |
|
||
|
||
Vor Phase 2 immer `legacy:verify` laufen lassen, um Lücken aus Phase 1 (z. B. fehlgeschlagene User-Importe → verwaiste Rechnungen) zu erkennen.
|
||
|
||
---
|
||
|
||
## Noch nicht finalisiert
|
||
|
||
- Medien-/Bilddateien-Transfer: Scope/Command vorhanden (`legacy:migrate-media`), finale Base-Path-Strategie auf Live noch festzulegen.
|
||
- Staging-Rehearsal mit aktuellem Produktiv-Snapshot bleibt Pflicht vor Go-Live.
|
||
|
||
## Legacy-Rechnungsreport
|
||
|
||
`legacy:archive-invoices` schreibt standardmäßig:
|
||
|
||
```bash
|
||
storage/app/private/migration/legacy-invoices-*.json
|
||
```
|
||
|
||
Der Report enthält pro Portal: Source-Count, importierte/übersprungene/fehlerhafte Rechnungen, Summe in Cent, Statusverteilung, Anzahl unzugeordneter Legacy-User, Anzahl erzeugter PDF-Payloads.
|