Commit graph

133 commits

Author SHA1 Message Date
26ce2544f0 UI: Agentur-Angaben im Profil, Firmen-Typ-Auswahl entfernt
A5 des Umbaus Agentur-Attribution → User-Profil: Kunden-Profil erhält
den Abschnitt „Agentur-Angaben" (Feld Agentur-/Verteilername, öffentlich
als Übermittler sichtbar). Firma-Anlegen (Kunde + Admin) und
Admin-Firma-Bearbeiten kommen ohne Typ-Feld aus — Neuanlagen sind immer
type=company. Firmen-Übersicht zeigt kein Typ-Label mehr. Admin-User-
Formulare pflegen agency_name; die Detailseite zeigt den Übermittler
als Badge. CompanyFactory erzeugt keine zufälligen Agentur-Typen mehr.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:44:17 +00:00
0b3c6ccac9 companies:retire-agency-type: Firmen-Typ „Agentur" abschaffen
A4 des Umbaus Agentur-Attribution → User-Profil: Agentur-Firmen mit
PMs oder Pressekontakten werden zu type=company umtypisiert (sie sind
faktisch Newsrooms), leere Agentur-Firmen soft-gelöscht. Geschützt vor
Löschung (nur Umtypisierung): Mitgliedschaften Dritter in company_user
oder Verknüpfungen in user_payment_option_company. --dry-run +
JSON-Report; im Deploy-Runbook nach legacy:migrate-agency-names
eingereiht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:33:39 +00:00
56fe5ea6f4 Attribution: Übermittler aus users.agency_name statt Agentur-Firma
A3 des Umbaus Agentur-Attribution → User-Profil: Eager Loading der
Detailseite/Vorschau lädt user:id,agency_name; die Legacy-Relation
PressRelease::submittingAgency() (Pseudo-Firma type=agency via
owner_user_id) entfällt. Tests decken Verteiler-Fall, Namensgleichheit
und PM ohne Übermittler ab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:30:41 +00:00
621fc0569f legacy:migrate-agency-names: Agentur-Namen → users.agency_name
Übernimmt je Owner den Namen seiner Legacy-Agentur-Firma(en) ins neue
Profilfeld (A2 des Umbaus Agentur-Attribution → User-Profil). Regeln:
zuletzt aktualisierter Datensatz gewinnt bei Mehrfach-Namen (alle
Varianten im Report), bereits gesetzte Werte werden nie überschrieben,
Owner-lose Agentur-Firmen werden nur reportet. --dry-run + JSON-Report
nach storage/app/private/migration/; im Deploy-Runbook (Erst-Einrichtung)
nach dem Firmen-/User-Import eingereiht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:25:04 +00:00
1ef1e13c19 Depublikation: abgebrochener Checkout blockiert nicht mehr
Der Klick auf "Kostenpflichtig depublizieren" legte sofort einen
Pending-Kauf an; wer bei Stripe abbrach, sah danach dauerhaft "liegt
bereits ein Depublikations-Vorgang vor". Jetzt sperrt nur ein bezahlter
Vorgang — ein offener Pending-Kauf wird beim nächsten Klick
wiederverwendet (kein Duplikat), und die Detailseite zeigt das Angebot
weiterhin an.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:07:32 +00:00
9b55e11159 PM-Detailseite: Direktlinks zur öffentlichen Seite je Portal
Veröffentlichte PMs zeigten im Backend nur den Status-Badge, ohne Weg
zur öffentlichen Seite. Im Seitenkopf stehen jetzt Direktlinks — bei
Portal::Both einer je Portal (QA-Befund 07.07.).

Die URL-Logik ist als PressRelease::publicPortalLinks() zentralisiert
(vorher doppelt in der Published-Mail und fehlerhaft im Nachweis-PDF:
dort fehlte das /de/press-release/-Segment, die dokumentierte URL lief
ins Leere; bei Both nennt der Nachweis jetzt beide URLs). Edition folgt
der PM-Sprache statt hart „de".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:00:40 +00:00
a21d635ed6 PM-Detailseite: Depublikations-Sektion ans Seitenende verschoben
Der seltene Ausnahme-Service stand zwischen Add-ons und Inhalt; jetzt
steht er als letzte Sektion unter dem Inhalt (QA-Befund 07.07.). Dazu
zwei UI-Tests: Sektion + Unternehmer-Bestätigungs-Protokoll auf der
Detailseite, Ausblenden ohne Preis bzw. bei laufendem Vorgang.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:35:42 +00:00
dfb8de9e45 Add-ons: Modal statt Toast bei fehlendem Credit-Guthaben
Scheiterte ein Boost- oder Nachweis-Kauf am Wallet-Guthaben, gab es nur
einen flüchtigen Toast ohne Weg zur Lösung. Jetzt öffnet sich ein Modal
mit Kosten, Guthaben und fehlender Differenz sowie einem Direktlink
"Credits aufladen" zur Wallet-Sektion der Buchungsseite (neuer
#credits-Anker).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:30:57 +00:00
2a46aed1a9 Einreichen-Modal: Panel-Struktur und fixierter Kontingent-Badge
Das Modal war ein unstrukturierter Stapel aus zwei Boxen und losen
Switches; der Kontingent-Badge wurde vom Flex-Layout zusammengedrückt
und brach um ("3 /" / "3"). Jetzt: drei beschriftete Panels (Rechtliche
Hinweise, PM-Kontingent, Ihre Bestätigungen) im portalweiten
panel/panel-head-Stil, die Switches als getrennte Zeilen mit
Fortschrittszähler (x / 4) im Panel-Kopf, Badge mit shrink-0 und
whitespace-nowrap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:16:56 +00:00
fcfbbd4918 PM-Editor: Ablehnungsbegründung direkt auf der Bearbeiten-Seite anzeigen
Bei abgelehnten Pressemitteilungen stand die Begründung bisher nur auf
der Detailseite. Der Editor zeigt jetzt dasselbe Hinweis-Panel (Begründung
+ Ablehnungsdatum) über der Schreibfläche, damit der Kunde beim
Überarbeiten sieht, was zu ändern ist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:09:42 +00:00
c040f394e3 Rechnungs-Seite: Archiv-Filter entfernt, Altsystem-Archiv nur für Bestandskunden
QA-Befund 07.07.: Die Filter-/Suchzeile unter den aktuellen Rechnungen
entfällt; das Rechnungsarchiv (Altsystem) erscheint nur noch, wenn der
User dort tatsächlich importierte Rechnungen hat — Neukunden sehen es
nicht mehr. Seitenbeschreibung passt sich entsprechend an.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:53:05 +00:00
ef2b4fe2ad Downgrades ohne Gutschrift: Tarifwechsel nach unten erst zum Periodenende
QA-Entscheidung 07.07.: Eine Buchung ist nicht rückabwickelbar — es darf
nie eine negative Proration-Rechnung oder ein Stripe-Guthaben entstehen.

- StripeCheckoutService::isDowngrade: sofortiger Wechsel nur, wenn der
  Periodenpreis des Ziel-Tarifs den aktuellen erreicht (Wechsel-Rechnung
  damit garantiert >= 0); sonst Downgrade
- scheduleDowngrade: Stripe Subscription Schedule stellt das Abo zum Ende
  der bezahlten Periode um (proration_behavior none), hebt eine
  vorgemerkte Kündigung auf und spiegelt die Vormerkung lokal
  (neue Spalten scheduled_stripe_price/scheduled_change_at/
  stripe_schedule_id an subscriptions)
- Sofort-Upgrade löst eine bestehende Vormerkung vorher auf (release)
- Webhook customer.subscription.updated räumt die Vormerkung nach
  Vollzug ab
- Buchungs-Modal unterscheidet Upgrade (sofort, Differenz-Rechnung) und
  Downgrade (Vormerkung, keine Zahlung heute); Banner + Hinweis auf der
  Paket-Karte zeigen den vorgemerkten Wechsel mit Datum
- Kontingent-Sperre aus 4b7f6d3 entfernt — ohne Gutschrift gegenstandslos

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:38:57 +00:00
4b7f6d3f0f Downgrade-Vorkehrungen: Kontingent-Sperre beim Tarifwechsel, Gutschriften nicht spiegeln
QA-Befund 07.07.: Der Wechsel zurück in einen kleineren Tarif erzeugte
eine Proration-Gutschrift (negative Stripe-Rechnung) — die STR-Spiegelung
crashte an den unsigned-Spalten (Webhook-500), und der Wechsel war trotz
verbrauchtem Monatskontingent möglich.

- ProcessStripeWebhook: Belege ohne positiven Rechnungsbetrag
  (Gutschrift/Null-Rechnung) werden geloggt übersprungen statt gespiegelt
- CheckoutController::swap: Wechsel gesperrt, wenn der Monatsverbrauch
  das Kontingent des Ziel-Tarifs übersteigt (möglich ab Monatserstem)
- Buchungs-Modal: erklärt die Sperre inkl. Datum statt Bestätigungs-
  Button; Proration-Hinweis deckt jetzt auch die Guthaben-Richtung ab

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:22:30 +00:00
090a893cc6 PM-Vorschau: echtes Portal-Design, Direkt-Button und Portal-Switch
Der Vorschau-Link (/pm-vorschau/{token}) renderte eine schlichte
Standalone-Seite, die dem spaeteren Erscheinungsbild nicht entsprach.
Jetzt rendert er die echte Detailseite (web.release-detail) im Theme
des Zielportals:

- Controller wendet Domain-/Theme-Konfig des Zielportals an (Sprache
  der PM, Links zeigen auf die Portal-Domain), kein Hit-Zaehler
- Gelbes Vorschau-Banner mit Status und Link-Gueltigkeit; noindex per
  Meta-Tag + X-Robots-Tag-Header, Canonical entfaellt bei noindex
  (neue robots-Section im Web-Layout)
- Teilen-Buttons teilen den Vorschau-Link selbst (oeffentliche URL
  existiert vor Veroeffentlichung noch nicht)
- PMs fuer beide Portale: ?portal=... waehlt die Ansicht, Umschalter
  im Banner; im Kundenpanel je Portal ein Vorschau-Button, sonst ein
  einzelner "Vorschau oeffnen"-Button (Link-Feld jetzt kopierbar)
- Alte press-release-preview-View entfernt

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:11:57 +00:00
9a95a6937d Suche: Volltext-Index + Fix fuer doppelte Alpine-Instanz
Archiv-Suche lief auf grossen Datenbestaenden langsam und war im Browser
komplett tot: app.js startete ein eigenes Alpine, Livewire injizierte
zusaetzlich seins - der DOM-Morph brach mit "Alpine.transaction is not a
function" ab (keine Rubrik klickbar, Suchbegriff nicht aenderbar).

- app.js buendelt jetzt Livewire + Alpine aus livewire.esm,
  @livewireScriptConfig im Web-Layout stoppt die Auto-Injektion
- MySQL-Volltext-Index (Boolean-Mode, +wort*) statt LIKE ueber alles;
  LIKE-Fallback fuer SQLite-Tests und kurze Tokens
- Firmen-/Rubrik-Treffer als ID-Listen statt orWhereHas (OR + EXISTS
  hebelte den Volltext-Index aus)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:03:14 +00:00
9e76e24014 Tarifwechsel im Buchungs-Modal: Abo-Swap mit anteiliger Verrechnung
Tarif-Karten bieten bei bestehendem Abo jetzt 'Zu diesem Tarif
wechseln' (pro Intervall, aktueller Tarif deaktiviert). Der Wechsel
laeuft ueber das bekannte Bestaetigungs-Modal (T1neu-Checkbox) als
POST auf die neue checkout.swap-Route: Cashier swapAndInvoice stellt
sofort um, verrechnet anteilig und hebt eine vorgemerkte Kuendigung
auf. SCA-Faelle leiten auf die Cashier-Bezahlseite. QA-Befund 07.07.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:58:32 +00:00
256aca6112 Checkout: Wallet-Daten duerfen Profil-Rechnungsadresse nicht ueberschreiben
customer_update: auto liess Apple Pay den zuvor synchronisierten
Stripe-Customer ueberschreiben (Kartenname + nur Land) — die Rechnung
trug den falschen Empfaenger. Mit Profil-Rechnungsadresse bleibt der
Customer jetzt unangetastet; nur ohne Adresse erfasst Stripe sie
selbst im Checkout (inkl. USt-ID-Abfrage).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:33:48 +00:00
6eedaa474e Checkout: Profil-Rechnungsadresse als Stripe-Rechnungsempfaenger
Die lokal gepflegte Rechnungsadresse stand nur bei der Erstanlage am
Stripe-Customer; der Checkout erzwang die Neueingabe und ueberschrieb
sie — auf der Rechnung stand die getippte Adresse statt der Profildaten
(QA-Befund 07.07.). Jetzt: Customer-Sync (Name/Firma, Adresse, USt-ID)
vor jedem Checkout, stripeName()-Hook liefert Firma bzw. Rechnungsname
statt Login-Anzeigename, billing_address_collection auf 'auto' (keine
Zwangs-Neueingabe bei hinterlegter Adresse).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:09:05 +00:00
7e571eed94 Rechnungsseite: neue STR/MAN-Rechnungen gelistet, STR bei Stripe verlinkt
Die Kundenseite /admin/me/invoices zeigte bisher nur das Legacy-Archiv.
Neue Sektion "Aktuelle Rechnungen" mit Netto/USt/Gesamt und Status;
STR-Rechnungen oeffnen die gehostete Stripe-Rechnungsansicht ueber
einen Redirect-Endpunkt (URL wird je Aufruf frisch geholt, Zugriff nur
fuer den Eigentuemer). KPIs zaehlen neu + Archiv zusammen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:01:12 +00:00
4cda4d3bda STR-Spiegelung: USt aus total_taxes lesen (Stripe-API ohne tax-Feld)
Aktuelle Stripe-API-Versionen liefern kein Top-Level-`tax` mehr auf der
Invoice — die Spiegelung buchte 0 USt und markierte die Rechnung als
netto (QA-Befund 07.07., STR-00001). Fallback-Kette: tax (alt) →
total_taxes (aktuell) → total_tax_amounts (Übergangsname).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 11:42:31 +00:00
af9f238978 2FA-Challenge: Fehlversuch crasht nicht mehr (Route two-factor.login fehlt)
Fortifys FailedTwoFactorLoginResponse leitet auf die Route
two-factor.login um, die bei views => false nie registriert wird
(RouteNotFoundException). Eigener Response leitet zurück auf die
Volt-Challenge-Seite und meldet den Fehler auf Deutsch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:58:17 +00:00
62fbafa4ce QA-Befunde A4: 2FA-Bestätigungsschritt + sicherer E-Mail-Wechsel
- 2FA: Einrichtung verlangt jetzt die Bestätigung mit einem App-Code
  (Fortify confirm-Semantik); vorher blieb two_factor_confirmed_at
  leer und der Login fragte nie nach der Challenge.
- Recovery-Codes nur noch einmalig sichtbar (nach Bestätigung bzw.
  Neu-Erzeugung), nicht mehr bei jedem Seitenaufruf.
- E-Mail-Änderung als Pending-Flow: Bestätigungslink geht an die NEUE
  Adresse, die alte bleibt bis zum Klick aktiv — kein Aussperren durch
  Tippfehler und keine Registrierungs-Notice mehr nach dem Ändern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:53:24 +00:00
b3474a36e9 Spoke-Frontend-Audit: Fake-Inhalte raus, Newsletter-Rueckbau, echte Boersendaten, EN-Lokalisierung
Frontend-Umbau (Kevin, 06.07.2026), Ergebnis des businessportal24-Audits:

- Fake-Anzeigen und Mock-/Fallback-Daten entfernt (focus-ad, feed-ad,
  highlight-card, press-release-card, featured-releases, press-releases-grid);
  Sektionen rendern nur noch mit echten Daten oder gar nicht
- Newsletter-Strip und Newsletter-Box zurueckgebaut (Feature nachgelagert,
  vor Reaktivierung DOI-Flow noetig)
- Echte Boersendaten: MarketDataService (Yahoo-Finance-Quotes, config/market.php,
  Cache) + market:refresh alle 15 Min. im Scheduler; live-ticker blendet sich
  ohne Daten aus
- EN-Lokalisierung der Kern-Seiten via lang/de/web.php + lang/en/web.php;
  Carbon-Locale folgt app()->getLocale()
- Web-Tests auf den neuen Frontend-Stand nachgezogen (Suite: 993 gruen)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:36:23 +00:00
5d199f0130 Rechnungsadressen der Bestandskunden aus dem Legacy-Archiv uebernehmen
Befund 06.07.: Der MAN-Rechnungslauf ueberspringt Vereinbarungen ohne
Rechnungsadresse — der Bestandsschutz laeuft dann am Periodenende faktisch
aus. Der Grandfather-Import nahm die Adressen bisher nicht mit, weil die
Altportale sie nur als Freitext-Blob fuehren.

- legacy:import-billing-addresses: uebernimmt Adressen der grandfathered
  Kunden aus der Quell-Rechnung im Archiv (pdf_payload.billing_address),
  nie ueberschreibend, mit Dry-Run und Report der manuell nachzupflegenden
  Faelle (leere/unparsebare Blobs, unbekannte Laender)
- LegacyAddressParser: Best-Effort-Strukturierung der Blobs (mehrzeilig,
  kommasepariert, CH-PLZ-Praefix, Firmen-/Zusatzzeilen); raet nie
- Laenderaufloesung ueber die country-Tabellen der Legacy-Verbindungen
- config/countries.php: CY ergaenzt (reale Bestandskunden aus Zypern)
- WS-7- und QA-Checkliste: Import-Schritt nach dem Grandfathering

Lokal: 11 von 21 Bestandskunden importiert, 9 im Report (7 leere Blobs);
MAN-Lauf rechnet zuvor blockierte Vereinbarungen jetzt ab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:19:50 +00:00
616b7847d4 Uebernahme bestehender Legacy-API-Integrationen (Keys, Auth, Pfade)
Bestehende Kunden-Integrationen der Altportale laufen nach dem Relaunch
weiter, ohne dass Kunden etwas umstellen muessen:

1. legacy:import-api-keys uebernimmt die api_keys aus den Legacy-DBs
   (sf_guard_user_profile) als Sanctum-Tokens mit identischem Secret
   (Sanctum loest Tokens ohne id|-Praefix per sha256-Hash auf) — nur
   fuer berechtigte Nutzer (aktiver Zahlungsstatus/Bestandsschutz,
   ApiAccessEligibilityService); idempotent, mit Dry-Run. Probe gegen
   die lokalen Snapshots: 406 Keys wuerden uebernommen.
2. Die Auth-Middleware uebersetzt die alten Uebergabeformen (X-ApiKey-
   Header, api_key-Parameter) fuer gueltige Keys in Bearer; unbekannte
   Keys erhalten weiterhin 410 mit Migrationshinweis.
3. Die dokumentierten /service/...-Pfade der Alt-API sind als Aliasse
   auf die v1-Controller gelegt (pressreleases inkl. Bilder, companies
   lesend); nicht uebernommene Alt-Endpoints (Pressemappen/Kontakte
   anlegen) antworten 410 mit Hinweis auf Kundenkonto und neue Doku.

API-Seite + OpenAPI entsprechend aktualisiert; Go-Live-Checkliste um den
Import-Schritt (nach dem Grandfathering) ergaenzt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:55:22 +00:00
3364fd13b8 Fix: Bestandsschutz ohne explizites Vertragsende zaehlte nie als aktiv
Der grandfatheredNow-Scope verlangte ein gesetztes grandfathered_until —
das ist aber das explizite Legacy-Vertragsende (valid_until_date) und im
Import-Regelfall NULL (offene Dauervereinbarung). Ergebnis: praktisch
kein Legacy-Kunde hatte eine aktive Zahlungsoption; eingeloggte
Bestandskunden sahen "Buchung erforderlich" und konnten nicht einreichen.
Jetzt gilt: grandfathered_until deckelt, wenn gesetzt; sonst zaehlt die
laufende, vom MAN-Rechnungslauf fortgerollte Periode (current_period_end
= next_due_date). Tests decken beide NULL-Faelle und den Deckel ab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:40:39 +00:00
d0ffe664b8 QA-Luecke geschlossen: Legacy-Medien-URLs redirecten wieder (301)
Die Scope-Doku fuehrte den /thumbnails-Redirect als "ANGEPASST", im Code
existierte er nicht — alte Symfony-Bild-URLs (/thumbnails/..., /uploads/
...) liefen ins 404. Neuer LegacyMediaRedirectController mappt den
Original-Dateinamen (bleibt beim Medien-Import erhalten) auf die neue
Storage-URL fuer PM-Bilder und Firmenlogos; unbekannte Dateien bleiben
404. Gefunden bei der R6.1-Vorpruefung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:12:35 +00:00
9a9e31360e Depublikations-Preis festgelegt: 35 Euro netto (O6, User 06.07.)
Default 3500 Cent in config/billing.php (per Env ueberschreibbar,
0 deaktiviert) — Checkout und /melden-Preisanzeige (Doppelausweis
35,00 netto / 41,65 brutto) sind damit ohne weiteren Env-Handgriff
aktiv. O6 aus der Entscheidungsliste geschlossen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:57:11 +00:00
cc77e604f4 T12: Depublikations-Checkout mit Tombstone (config-gated)
Kunden koennen eigene veroeffentlichte PMs kostenpflichtig aus der
oeffentlichen Ansicht nehmen: Panel auf der PM-Detailseite (inkl.
T1neu-Unternehmer-Checkbox und Doppelausweis netto/brutto), Stripe-
Ad-hoc-Checkout wie Credit-Topups, Erfuellung im Webhook — die PM
wechselt auf archived + depublished_at, die oeffentliche URL zeigt
dauerhaft einen Tombstone (410 Gone, ohne Inhalte, mit ehrlichem
Drittarchive-Hinweis). Doppelbuchungs-Bremse pro PM; Status-Log mit
Quelle "depublication". Gate: ohne BILLING_DEPUBLICATION_PRICE_CENTS
bleibt der Weg deaktiviert und /melden zeigt die neutrale
Preisformulierung [EXTERN: Preis festlegen, O6].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:46:25 +00:00
979e6b1ee1 T10: DSA-Meldeformulare mit URL-Feld, Vorgangsnummer und Eingangsbestaetigung
Zwei eigenstaendige Formulare unter /melden/rechtswidrig (DSA Art. 16:
URL, Name, E-Mail, Begruendung, Wahrheitsversicherung) und
/melden/kontaktdaten (DSGVO-Antrag) auf allen drei Domains. Gemeldete
URLs werden automatisch der passenden veroeffentlichten PM zugeordnet;
ohne Treffer bleibt die URL im payload. Jede Meldung erhaelt eine
Vorgangsnummer (M-{id}, analog K-{id} der Kuendigung), die meldende
Person unverzueglich eine Eingangsbestaetigung (DSA Art. 16 Abs. 4) und
das Melde-Postfach einen internen Hinweis — auch im bestehenden
Per-PM-Flow. Admin-Queue zeigt Vorgangsnummer, Name und gemeldete URL;
/melden verlinkt die neuen Formulare als ersten Weg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:35:30 +00:00
5337a14bf0 T13: Melde-Links sichtbar unter jeder Pressemitteilung
Unter dem Artikelinhalt steht jetzt eine eigene Zeile mit "Inhalt melden"
und "Eigene Daten entfernen" (inkl. Kostenlos-Hinweis) — bisher waren die
Wege nur in der Sidebar-Box "Weiterfuehrend" auffindbar. Erfuellt die
DSA-Anforderung eines leicht zugaenglichen Meldewegs direkt am Inhalt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:26:58 +00:00
c54c6943b7 Ablehnungskategorien live aus der DB (rejection_categories)
/richtlinien rendert jetzt alle aktiven Kategorien aus der neuen
rejection_categories-Tabelle (stabiler code als Anker, public_label/desc,
optionale Beispiele) statt der statischen K1-K6-Liste. Kategorien werden
nie geloescht, nur deaktiviert; neue Kategorien erscheinen ohne
Textaenderung und ohne neue AGB-Version (AGB par. 7 Abs. 3 verweist nicht
abschliessend). Seeder legt K1-K6 idempotent an; Versionszeile der Seite
kommt aus legal_documents. Go-Live-Checkliste um beide Seeder ergaenzt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:25:16 +00:00
196f83a377 E3: Rechtstext-Versionierung in der DB (legal_documents)
Jede veroeffentlichte Fassung eines Rechtstexts ist eine Zeile mit
type/version/effective_from — alte Fassungen sind unloeschbar, zukuenftige
werden erst ab Stichtag wirksam (LegalDocument::current). Seeder legt die
fuenf aktuellen Fassungen an (AGB 0.6, Datenschutz 0.3, Richtlinien 0.1,
Meldeverfahren 0.1, Impressum 0.1). Die Unternehmer-Bestaetigung (T1neu)
referenziert jetzt die konkrete Fassung (legal_document_id am
Buchungsdatensatz, restrictOnDelete); AGB-Seite und Buchungs-Modal ziehen
Version/Stand aus der DB. config/legal.php bleibt reiner Fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:21:52 +00:00
b90e59a383 Umami-Webanalyse eingebaut, bis zum Go-Live deaktiviert
Selbst gehostetes Umami (sys.adametz.media) je Brand: Website-IDs aus
dev/release/umami.md in config/domains.php, Snippet via x-web.umami im
web-master-Layout auf allen oeffentlichen Seiten der drei Domains.
UMAMI_ENABLED=false haelt es aus, bis die Webseiten live gehen
(User-Vorgabe 06.07.; Go-Live-Checkliste WS-7 ergaenzt). Damit stimmt
Datenschutz Abschnitt 5 mit dem Code ueberein. User bestaetigt ausserdem:
Log-/Backup-Fristen und Stripe-Entitaet passen wie eingetragen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:56:14 +00:00
4f07104239 Zentrale Rechts-E-Mail info@pressekonto.com fuer alle Rechtstexte
User-Entscheidung 06.07.: info@pressekonto.com ist die Haupt-E-Mail fuer
alle rechtlichen Anliegen. Zentral in config/legal.php (LEGAL_CONTACT_EMAIL),
verwendet von Impressum, AGB, Datenschutz, /melden und /richtlinien auf
allen drei Domains. Support-Kontakt bleibt je Brand (CONTACT_EMAIL_*),
REPORT_EMAIL_* ist nur noch optionales separates DSA-Postfach. Nebenbei
den verunglueckten Enterprise-Hinweis auf der Buchungsseite repariert
(E-Mail war ohne Trennung angeklebt).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:32:59 +00:00
c523224da8 System-Mails im pressekonto-CI: zentrales E-Mail-Layout fuer alle Transaktionsmails
- Neues Layout components/emails/layout (Wortmarke, 'Publisher - Hub'-
  Tagline, Brand-Familien-Fusszeile) + Button-Komponente; alle System-
  Mails (Magic-Link, Go-Live-Reset, Kontakt, PM published/rejected,
  Kuendigung, Widerruf) darauf umgestellt
- Framework-Auth-Mails (Passwort-Reset, E-Mail-Verifizierung) via
  toMailUsing auf eigene Views im gleichen CI statt Laravel-Default
- Anmeldelink-Betreff vereinheitlicht; EmailBrandingTest sichert das
  Branding ueber alle Mails ab

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:26:30 +00:00
6f9a68b63d T1neu: Unternehmer-Pflicht-Checkbox im Checkout, protokolliert mit AGB-Version
B2B-Gate (AGB v0.6 par. 1 Abs. 4): Das Buchungs-Modal verlangt vor jedem
Stripe-Checkout die Bestaetigung 'Ich bestelle in Ausuebung meiner
gewerblichen oder selbstaendigen beruflichen Taetigkeit (par. 14 BGB)'.

- booking_confirmations: user, Buchungstyp/-ref, AGB-Version, IP,
  confirmed_at (Nachweis laut Datenschutz Abschnitt 7)
- Checkout-Link erscheint erst nach der Checkbox; CheckoutController
  verlangt zusaetzlich serverseitig eine zeitnahe Bestaetigung (30 Min.)
  fuer Abo/Einzel-PM/Credit-Topup — Direktaufrufe laufen ins Leere
- config/legal.php als zentrale AGB-Versionsquelle (Seite + Protokoll,
  Uebergangsloesung bis zum legal_documents-System E3)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:17:59 +00:00
6438444a80 Datenschutzerklaerung v0.3 eingebaut, Cookie-Seite zurueckgebaut
- Datenschutz: vollstaendige v0.3 aus docs/legal, host-aware Bausteine
  (Hub: Kundenkonto/Login-Magic-Link/Stripe; Spokes: Medienprivileg,
  Newsroom, Aenderungslink-Variante); Systemwerte aufgeloest (Magic-Link
  15 Min. aus MagicLinkGenerator, Logs 14 Tage, Stripe Payments Europe);
  ersetzt den Platzhalter samt falscher Google-Analytics-Behauptung
- Cookie-Seite raus (Cookies stehen in Abschnitt 4 der Datenschutz-
  erklaerung, kein Banner noetig): Route -> 301 auf /datenschutz,
  Legacy-Alias cookies.html -> datenschutz, Footer + Sitemap bereinigt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:17:59 +00:00
110f97db6b T2neu: Archiv-Hinweis vor Submit ersetzt Erloeschens-Checkbox (B2B, E2)
Die 356-BGB-Erlöschensbestätigung entfiel mit der B2B-Umstellung; an ihre
Stelle tritt der von AGB par. 13 Abs. 1 geforderte Hinweis auf die
dauerhafte Archivierung (Depublikation nur als kostenpflichtiger Service,
gesetzliche Ansprueche unberuehrt). Bestätigungszeitpunkt wird weiter
protokolliert (Spalte withdrawal_waiver_consented_at, historischer Name).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:53:22 +00:00
c195878295 R3.1: Rechtstexte eingebaut — Impressum, AGB v0.6, Richtlinien, Meldeverfahren
- Impressum: echte Anbieterdaten (adametz.media, Bielefeld, USt-ID) statt
  erfundener GmbH; DDG statt TMG, MStV-Verantwortlicher, Verbund-Absatz,
  DSA-Kontaktpunkt (Art. 11/12) mit neuem REPORT_EMAIL_*-Melde-Postfach
- AGB: B2B-Fassung v0.6 (03.07.2026) vollstaendig gerendert (par. 1-16,
  interne Anwalts-Anmerkungen entfernt, Variablen je Host aufgeloest);
  ersetzt den widerspruechlichen Platzhalter (30-Tage-Frist etc.)
- Neu: /richtlinien (Rahmentext + Ablehnungskategorien K1-K6 mit Ankern,
  statisch bis zum rejection_categories-System) und /melden (DSA/DSGVO/
  Aenderung/Depublikation, verdrahtet mit den bestehenden Melde-Links an
  jeder Mitteilung; eigenstaendige Formulare = Backlog T10/T12)
- Footer beider Gerueste + Sitemap um Richtlinien/Melden ergaenzt
- Versioniertes legal_documents-System (E3) bleibt R3.1-Folgeausbau

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:53:06 +00:00
43f563c67f API-Doku: echte Referenz statt Fantasie-Features
- /de/api komplett neu: reale Endpunkte, Felder, Token-Abilities,
  Rate-Limit und Statuscodes der API v1 (angelehnt an die Legacy-Doku
  von businessportal24.com/service/documentation.html); OAuth/SDKs/
  Webhooks/Integrations-Behauptungen und api.example.com entfernt
- docs/api/v1.yml (OpenAPI 3.1) erstellt — die docs.api.v1-Route lief
  bisher auf eine nie vorhandene Datei (500er aus Hub-Footer/FAQ)
- Token-Vergabe korrekt beschrieben: Selfservice im Konto mit
  Buchungs-Gate statt 'auf Anfrage'; X-ApiKey-Migrationshinweis
- ApiDocumentationTest entskippt + Seiten-Test ergaenzt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:36:19 +00:00
136ecc8f43 R3.2: Kontakt-/Ueber-uns-Routen zurueckgebaut, Spoke-Footer bereinigt
- GET kontakt/ueber-uns -> 301 auf die Ausgaben-Startseite;
  kontakt.store-POST bleibt fuer das Hub-Kontakt-Modal
- Legacy-Aliases (contact/kontakt/ueber-uns) buendeln auf die Startseite
- Spoke-Footer: Kontakt als mailto (Betreiber-Fallback bis R2.1),
  Ueber-uns-Link raus, Selfservice-Kuendigungslink ergaenzt
- Sitemap-Konstanten bereinigt, Tests angepasst

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:36:13 +00:00
b36ecf7465 Hub-Inhalte: Claims korrigiert + Widerrufs-UI zurueckgebaut (B2B-only, E2)
- Registrierungs-Schritt ehrlich beschrieben (E-Mail/Google, Freischaltung
  nach E-Mail-Bestaetigung statt 'direkt freigeschaltet'; Magic-Link ist
  nur Anmeldeweg)
- Newsroom-Karte: 'getrennte Berechtigungen'/'mehrere Redakteure' durch
  belegbare Aussagen ersetzt (keine Team-Verwaltung im Panel)
- Kuendigungs-FAQ nach E5 (Jahreszahlweise 12 Monate, danach monatlich)
  + Link auf die Selfservice-Kuendigungsseite
- B2B-Erkennbarkeits-Satz im Tarife-Intro (AGB v0.6 / T14)
- Wording T16: 'Inhaltsprüfung' statt Redakteurs-Versprechen; 'kein
  automatisierter Durchlauf'-Behauptung entfernt
- Widerrufs-Button + Modal-Includes entfernt (Entscheidung E2 B2B-only,
  T3neu: Kuendigungsbutton bleibt freiwillig); Backend/Route bleibt
  schlafend erhalten, reaktivierbar bei B2C-Oeffnung
- Hub-FAQ/Footer verlinken die API-Seite statt der toten docs-Route

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:35:03 +00:00
5c2ef283e8 Security (R6.2): Haertung nach Audit
- Security-Header-Middleware (nosniff, X-Frame-Options, Referrer-/
  Permissions-Policy) global, proxy-unabhaengig; CSP bewusst separat.
- Firmenlogo-Upload im Create-Formular durch den ImageService-Pfad
  ersetzt (Mime-Whitelist ohne SVG, Re-Encode, UUID-Name) - vorher
  rohe store()-Ablage ohne Server-Validierung (SVG/XSS moeglich).
- Rate-Limits ergaenzt: Passwort-Reset (3/E-Mail + 15/IP pro Std.),
  Registrierung (10/IP pro Std.), throttle auf magic-login- und
  pm-vorschau-Consume-Routen (Defense-in-Depth).
- EnsureUserIsCustomer als persistente Livewire-Middleware
  (symmetrisch zum Admin-Gate, greift auch bei Folge-Requests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:43:59 +00:00
7d2a312713 Web (R3.2): Marketing-Platzhalterseiten zurueckgebaut
team/partner/karriere/presse (erfundene Personen, Fake-Partner,
Bewerben-Buttons ohne Ziel) samt Routen entfernt (User-Entscheidung
03.07.: lieber weniger Seiten, aber ehrlich). Altes Scaffold
(livewire web.burger-menu/header/footer) wurde nur von diesen Seiten
genutzt und ist mit raus. Legacy-.html-Links: team/partner/karriere
-> 301 Startseite; presse/* behaelt die bestehende 301-Regel auf /faq.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:22:50 +00:00
c2d22c6d65 SEO: host-aware sitemap.xml + dynamische robots.txt je Domain
Sitemap pro Host (1h-Cache): Hub listet Landing + Rechts-/Info-Seiten,
Spokes zusaetzlich Kategorien (lokalisierte Slugs), oeffentliche
Newsrooms und alle im Archiv sichtbaren PMs des Portals (explizit ohne
PortalScope, forPortalValues). Suche und Marketing-Platzhalterseiten
bleiben draussen. Statische public/robots.txt entfernt (haette die
Route verschattet); robots.txt jetzt dynamisch mit Sitemap-Verweis
und Disallow fuer /admin, /login, /register, Suche.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:34:12 +00:00
244c2abd72 Recht: Erloeschensklausel-Checkbox im Publikationsflow (Par. 356 BGB)
Vierte Bestaetigung im Einreichungs-Modal (sofortige Ausfuehrung,
Erloeschen des Widerrufsrechts mit Vertragserfuellung); Zeitpunkt wird
beim Submit als withdrawal_waiver_consented_at an der PM protokolliert.
Index-Schnell-Submit (wire:confirm) durch das gemeinsame Modal ersetzt -
der Pfad umging bisher alle rechtlichen Bestaetigungen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:41:31 +00:00
8b8f2991c6 Recht: Kuendigungsbutton nach Par. 312k BGB - Backend + Admin-Queue
Oeffentliche Bestaetigungsseite /de/kuendigung ohne Login: Art der
Kuendigung (Grund nur bei ausserordentlich), Zuordnungsdaten,
Beendigungszeitpunkt; Honeypot + Rate-Limit wie beim Widerruf.
Sofortige automatische Zugangsbestaetigung per E-Mail (Par. 312k
Abs. 4 BGB) + Betreiber-Mail. Kuendigungen laufen in der
Recht-&-Compliance-Queue unter den Widerrufen auf (K-Vorgaenge,
eigener Filter, Erledigt/Wieder-oeffnen, vierte KPI-Karte).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:32:43 +00:00
c7450da301 Hub (Web): statische Seiten (Rechtstexte, API, FAQ ...) rendern das Hub-Geruest
x-web.static-page ist jetzt host-aware: Auf der pressekonto-Domain laden
Utility-Bar + Hub-Header/-Footer (inkl. Kontakt- und Widerrufs-Modal,
damit die Footer-Buttons ueberall funktionieren) statt des
Spoke-Geruests; Breadcrumb fuehrt zur Hub-Landing. Hub-Anker-Navigation
(#tarife/#funktion/#familie/#faq) absolut auf die Landing, damit sie
auch von Unterseiten aus funktioniert. Spokes unveraendert (Test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 10:47:47 +00:00
6621b030e6 Admin: Widerrufe laufen in der Recht-&-Compliance-Queue auf
Eigene Widerrufe-Sektion unter der Anfragen-Tabelle in
/admin/legal-requests: Vorgangsnummer, Zuordnungsdaten, Statusfilter
(offen/alle/erledigt, eigener Paginator), Erledigt/Wieder-oeffnen-Aktion
mit resolved_at; KPI-Karte "Offene Widerrufe". Dazu
WithdrawalRequest-Factory (+resolved-State) fuer die Tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 10:29:32 +00:00