diff --git a/dev/agentur-attribution/entwicklungsplan.md b/dev/agentur-attribution/entwicklungsplan.md new file mode 100644 index 00000000..546a830b --- /dev/null +++ b/dev/agentur-attribution/entwicklungsplan.md @@ -0,0 +1,142 @@ +# Entwicklungsplan: Agentur-Attribution → User-Profil (Umbau) + +> Erstellt **07.07.2026** nach User-Entscheidung. Ziel: Der Firmen-Typ „Agentur" +> verschwindet aus dem Firmen-Modell. Firmen sind ausschließlich die Newsrooms, +> unter denen PMs veröffentlicht werden. Die Übermittler-Angabe („übermittelt +> von …") wird eine **Profil-Eigenschaft des einliefernden Accounts**. +> Geplanter Umfang: **1 Arbeitstag**. + +> **Status 08.07.2026: A1–A5 UMGESETZT, Abnahme erfüllt.** Beide +> Daten-Commands sind lokal scharf gelaufen (Reports unter +> `storage/app/private/migration/`): 4.482 User mit `agency_name` +> (93 Mehrfach-Fälle exakt verglichen — der Plan zählte 77 normalisiert —, +> 10 Owner-lose nur reportet), 2.607 Agentur-Firmen umtypisiert, +> 2.149 soft-gelöscht, 1 geschützter Fall (Zahlungsoptions-/Mitglieds- +> Verknüpfung). Beispiel-PM zeigt die Attributions-Zeile aus dem +> Profilfeld. Deploy-Runbook (Erst-Einrichtung, Schritt 2) ergänzt. +> Offen: nur A6 (nachgelagert). + +--- + +## Ausgangslage (Stand 07.07.2026) + +**Bereits umgesetzt (07.07.):** Die Legacy-Attributions-Zeile („Original-Inhalt +von {Firma} und übermittelt von {Agentur}") ist auf der öffentlichen +Detailseite wieder da — aktuell noch über die Legacy-Datenstruktur: + +- `PressRelease::submittingAgency()` — hasOne auf `companies` + (`owner_user_id = user_id` der PM, `type = agency`, ohne PortalScope) +- Rendering in `resources/views/web/release-detail.blade.php` (unter dem + Pressekontakt-Block), Übersetzungen `web.detail.attribution*` (de/en) +- Eager Loading in `ReleaseDetailPageData` + `PressReleasePreviewController` +- 3 Tests in `tests/Feature/Web/ReleaseDetailTest.php` (Verteiler, gleichnamig, + ohne Agentur) + +**Datenlage (lokal geprüft 07.07.):** + +| Fakt | Wert | +|---|---| +| Agentur-Firmen (`companies.type = agency`, aktiv) | 4.756 | +| … davon selbst Newsroom (PMs mit `company_id` = Agentur) | 2.598 (7.963 PMs) | +| … mit Pressekontakten | 27 | +| … ohne Owner (`owner_user_id IS NULL`) | 10 | +| User mit Agentur-Firma | 4.482 | +| User mit **mehreren** verschieden benannten Agentur-Firmen | 77 | +| connektar: User 14016 (`info@connektar.de`) | Agentur-Firmen 18811 (pe) + 49196 (bp24), 20.830 PMs | + +**Kernproblem des Ist-Zustands:** Die Agentur ist eine Pseudo-Firma — im +Kunden-Panel nicht von echten Firmen unterscheidbar, die Zuordnung +(owner_user_id → user_id der PM) versteht niemand, und mehrere Agentur-Firmen +pro Account sind mehrdeutig. + +--- + +## Arbeitsregeln + +- Test-First bei Verhaltensänderungen; Tests gezielt: + `php artisan test --compact --filter=` (wir sind im Sail-Container). +- Nach PHP-Änderungen: `vendor/bin/pint --dirty --format agent`. +- Jeder Schritt = eigener, fokussierter Commit. +- Daten-Kommandos immer mit `--dry-run` zuerst; nie überschreibend. +- Web-Frontend-Views gehören dem User → Änderungen dort separat committen. + +--- + +## A1 — Profilfeld `users.agency_name` (~30 min) + +- [x] Migration: `agency_name` (string 190, nullable) auf `users`. +- [x] `User`: fillable ergänzen; `UserFactory`-State `withAgency()`. +- [x] Kein eigenes Agentur-Logo/Website fürs Erste (bewusst schlank — + erweiterbar, wenn gebraucht). + +## A2 — Daten-Übernahme Agentur-Firmen → Profil (~1–1,5 h) + +- [x] Artisan-Command `legacy:migrate-agency-names` (`--dry-run`, Report nach + `storage/app/private/migration/`): + - Je User mit Agentur-Firma(en): Namen nach `users.agency_name` übernehmen. + - **Regel Mehrfach-Fälle (77 User):** zuletzt aktualisierter Datensatz + (`updated_at` max) gewinnt; alle Varianten in den Report. + - Nie überschreiben, wenn `agency_name` bereits gesetzt. + - Die 10 Owner-losen Agentur-Firmen nur reporten (manuelle Sichtung). +- [x] Test mit Factories (Einzel-Fall, Mehrfach-Fall, bereits gesetzt). +- [x] **Go-Live-Hinweis:** Command in die WS-7-/Deploy-Checkliste aufnehmen + (läuft NACH dem Firmen-/User-Import). + +## A3 — Attribution auf Profilfeld umstellen (~1 h) + +- [x] `release-detail.blade.php`: `$agencyName = $release->user?->agency_name` + statt `submittingAgency` (Vergleichs-/Fallback-Logik bleibt identisch: + Name ≠ Firma → „Original-Inhalt von … und übermittelt von …"; + Name = Firma → „Inhalt übermittelt von …"; leer → „Original-Inhalt von …"). +- [x] Eager Loading umstellen: `user:id,agency_name` statt `submittingAgency` + (`ReleaseDetailPageData`, `PressReleasePreviewController`). +- [x] `PressRelease::submittingAgency()` entfernen. +- [x] Tests in `ReleaseDetailTest` auf `agency_name` umschreiben; Suite-Ausschnitt + grün (`ReleaseDetail`, `PressReleasePreview`). + +## A4 — Firmen bereinigen: Typ „Agentur" abschaffen (~1,5 h) + +- [x] Artisan-Command `companies:retire-agency-type` (`--dry-run`, Report): + - Agentur-Firmen **mit** PMs oder Kontakten → `type = company` umtypen + (bleiben Newsrooms; 2.598 + 27, Überschneidung möglich). + - Agentur-Firmen **ohne** PMs und Kontakte → Soft-Delete (~2.100). + Vorher prüfen: keine `company_user`-Mitgliedschaften Dritter, keine + `user_payment_option_company`-Verknüpfung — sonst nur umtypen. +- [x] Test mit Factories (umtypen, löschen, geschützter Fall). +- [x] Command ebenfalls in die Deploy-Checkliste (nach A2!). + +## A5 — UI anpassen (~1,5 h) + +- [x] **Kunden-Panel Profil** (`livewire/customer/profile.blade.php`): + neuer Abschnitt „Agentur-Angaben" mit Feld „Agentur-/Verteilername" + und Erklärtext: *„Falls Sie Pressemitteilungen im Auftrag Dritter + einreichen, erscheint diese Angabe öffentlich als Übermittler unter + Ihren Mitteilungen."* Validierung: nullable, max 190. +- [x] **Firma anlegen** (`customer/press-kits/create.blade.php`): Typ-Feld + entfernen, fest `type = company`. +- [x] **Firmen-Übersicht** (`customer/press-kits/index.blade.php`): + Typ-Label aus der Meta-Zeile entfernen (nach A4 gibt es nur noch einen Typ). +- [x] **Admin** (`admin/companies/create|edit.blade.php`): Typ-Auswahl + entfernen; `admin/users/*`: Feld `agency_name` anzeigen/editierbar machen. +- [x] Tests: Profil speichert `agency_name`; Firma-Anlegen ohne Typ-Feld. + +## A6 — Nachgelagert (NICHT morgen) + +- `companies.type`-Spalte + `CompanyType`-Enum komplett entfernen + (erst wenn A4 produktiv gelaufen und verifiziert ist). +- Optional: Agentur-Logo/-Website im Profil, falls die Attributions-Zeile + verlinkt werden soll. +- Admin-Auswertung „PMs je Übermittler" (Tracking-Wunsch aus dem Konzept, + Abschnitt Distribution-Partner). + +--- + +## Abnahme (Ende des Tages) + +- [x] Beispiel-PM `fuehrende-laufblogger-…` zeigt weiterhin + „Original-Inhalt von Trailbloggercamp und übermittelt von + connektar.de Presseverteiler" — jetzt aus `users.agency_name`. +- [x] Kunden-Panel: Agentur-Name im Profil sichtbar/änderbar; Firmenliste + ohne Agentur-Einträge bzw. ohne Typ-Label. +- [x] Beide Daten-Commands mit Report gelaufen (lokal), Deploy-Checkliste ergänzt. +- [x] Testsuite grün, Pint sauber, Commits je Schritt.