Commit graph

514 commits

Author SHA1 Message Date
6a45ebf607 Legacy-API-Paritaet S1+S2: connektar-Flow verhaltensgleich + KI-Schalter
Eigene Compat-Controller (Api/Legacy) fuer die 5 real genutzten
/service-Endpoints (Prod-Log-Beleg): Company-/Kontakt-Create (liefen
410!), PM-Create, Bild-Upload, published-only-GET — verschachtelte
Alt-Params, 201+Location+{}, Alt-Fehlerformat, Hit-Zaehlung,
Legacy-Land-Mapping.

- Anlegen = Einreichen (Alt-Verhalten); Sofort-Publish via neuer
  Permission api:immediate-publication (WS-7: connektar zuweisen)
- Admin-Schalter 'KI-Pruefung Legacy-API' (app_settings-Tabelle,
  Toggle auf /admin/press-releases): an = review + KI-Routing
- Admin-PM-Liste: KI-Spalte (Ampel-Badge) + manueller Pruef-Button
  ohne Routing fuer ungepruefte PMs
- Mails bei API-Einlieferern an Pressekontakt, sonst Firmen-Mail,
  Fallback Einlieferer (User-Entscheidung 10.07.)
- Tests: LegacyServiceParityTest (connektar-Flow end-to-end);
  Suite 1182 gruen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:09:40 +00:00
5a352425d7 API-Legacy-Paritaet: Arbeitsdokument mit Alt-Vertrag, Log-Auswertung, Schrittplan
- Alt-Vertrag komplett aus dem Symfony-Code extrahiert (Routen, Params
  verschachtelt, Response-Shapes, Sofort-Publish-Logik, Fehlerformat)
- Prod-Logs (dev/api, User) ausgewertet: ein Client (connektar),
  5 Endpoints; Company-/Kontakt-Create heute 410 -> muessen nachgebaut
  werden; Sofort-Publish beim POST belegt
- Entscheidungen 10.07.: Sofort-Publish + Admin-Schalter KI-Pruefung,
  getrennte Legacy-/v1-API, Ablehnungs-Rueckkanal spaeter
- dev/api/ enttrackt (.gitignore, Produktivdaten analog SQL-Dumps)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 13:40:24 +00:00
b18598584c Web-Frontend: /api-Doku mit KI-Deklarationsfeldern (curl-Beispiele + Hinweis)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 12:44:20 +00:00
92af015532 API: KI-Deklaration als v1-Pflichtfeld, Legacy-Default fuer /service (Entscheidung 10.07.)
Befund: v1 hatte keine KI-Deklarationsfelder — Submit lief seit der
KI-Kennzeichnungspflicht (09.07.) immer auf 422 und haette alle
Legacy-API-Kunden blockiert (Suite uebersah es: Factory deklariert).

- v1-Submit verlangt ai_text_declaration; der Call gilt dokumentiert
  als verbindliche Bestaetigung (Art. 50 KI-VO) und schliesst die
  Archiv-Kenntnisnahme (AGB par. 13) ein
- Bild-Upload verlangt ai_declaration (original/ai_generated/ai_edited)
- Legacy-/service-Aliasse defaulten auf none/original — das
  API-Kunden-Mailing (R6.4) weist auf die Erklaerungswirkung hin
- OpenAPI v1.yml + lang-Hinweis aktualisiert; Tests erweitert
  (Submit/Image/LegacyCompatibility)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 12:41:24 +00:00
3843e534da Web-Frontend: Kartenkomponenten auf PressRelease-Helper umgestellt (R4.3)
@php-Bloecke von feed-item, feed-top-item, focus-side-item, focus-hero,
kategorie, newsroom, release-detail, veroeffentlichen nutzen die neuen
Model-Helper — gerendertes Markup unveraendert (Web-Suite 215 gruen).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:57:26 +00:00
01e175e138 R4.3: Karten-Metadaten-Logik als PressRelease-Praesentations-Helper
localizedCategoryName/cardImageUrl/teaserText/wordCount/readTimeMinutes/
compactPublishedLabel ersetzen die 5x kopierte @php-Logik der
Kartenkomponenten; Lesezeit-Formel vereinheitlicht (ceil -> round in
HomePageData-Spotlight). Tests: Web/PressReleaseCardHelpersTest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:57:26 +00:00
459b339a9b Dev-Stack: Queue-Worker lauscht auch auf der classification-Queue
composer run dev startete queue:listen ohne --queue-Angabe, also nur
default — die KI-Klassifikationsjobs (Queue classification) blieben
lokal liegen und PMs steckten in der manuellen Review fest, obwohl
das Auto-Routing (Rot -> Ablehnung, Gelb/Gruen -> Auto-Publish)
laengst implementiert ist. Prod-Runbook war bereits korrekt
(--queue=classification,default).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:50:34 +00:00
460e092c02 QA-Checkliste B-G vertieft (Nachpruefung 10.07.)
- Alle referenzierten Beleg-Tests verifiziert, Suite 1156 gruen
- B-G um Features 07.-10.07. ergaenzt: Kontakt-Queue/-Widget,
  Ablehnungsbegruendung, KI-Kennzeichnung (Submit/API/Label),
  Tarifwechsel, Add-ons, USt/eVatR, Rechnungsseite, Depublikation
  komplett, PM-Tracking, Boersenticker, Monitoring-Alerts
- Frontend-Umbau-Vorbehalt in F aufgehoben (Umbau committet 09.07.)
- Befund dokumentiert: Page-Data-Cache ohne Publish-Invalidierung
  (max. 5 Min. Verzug, bewusster Tradeoff, kein Blocker)
- Release-Plan: naechste-Schritte-Block nachgezogen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:54:56 +00:00
0603fe5368 R2.5: legal-request-Hinweistexte final (Wortlaut aus Meldeverfahren v0.1)
- Meldung/DSGVO/Persoenlichkeitsrecht auf die am 06.07. freigegebenen
  /melden-Texte gezogen: kostenlose manuelle Pruefung, unverzuegliche
  Eingangsbestaetigung mit Vorgangsnummer (sofern E-Mail), Entscheidung
  mit Begruendung; DSGVO ohne pauschale 30-Tage-Zusage, stattdessen
  Bestaetigungslink/Nachweis wie im Meldeverfahren
- ENTWURF-TODO entfernt, Querverweis auf /melden ergaenzt
- Render-Test erweitert (Vorgangsnummer, kostenlos, melden-Link)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:11:28 +00:00
34f0e0f134 Web-Frontend: Aufrufe-Anzeige + Share-Tracking auf der PM-Detailseite
- Meta-Zeile zeigt Aufrufe (hits inkl. aktuellem Aufruf) in beiden
  Hero-Varianten; in der Vorschau ausgeblendet
- Teilen-Buttons melden den Kanal an release.share (fetch keepalive,
  CSRF-Meta); Link-kopieren trackt nur bei erfolgreichem Copy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:56:55 +00:00
8f5970436b PM-Tracking: Ereignis-Log fuer Aufrufe + Shares (press_release_events)
- press_release_events (type view/share, channel, ohne Personenbezug):
  Model/Enums/Factory, Cascade-Delete an der PM
- Detailaufruf loggt View-Event zusaetzlich zum Legacy-hits-Zaehler
  (bleibt bewusst einfacher Page-Request-Zaehler, User-Vorgabe 10.07.)
- POST press-release/{slug}/share (throttle 30/min, nur archiv-sichtbare
  PMs des Portals) loggt Teilen-Klicks je Kanal (LinkedIn/X/E-Mail/Copy)
- Kundenpanel-PM-Seite: neue Kachel Geteilt mit Kanal-Aufschluesselung
- lang: web.detail.views (de/en)
- Tests: Web/PressReleaseTrackingTest (7)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:56:42 +00:00
60d0d9b103 Release-Doku: Commit-Session vermerkt + Wiedereinstieg fuer morgen
7-Commit-WIP-Aufraeumung + R5.3/Buchungsdaten im Stand-Block, R6.5-WIP-Punkt
abgehakt, neuer Block 'Wo es morgen weitergeht'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:54:16 +00:00
d1939f6f2a Web-Frontend: Umbau Spokes/Hub, Kontakt-Widget (contact-launcher)
Startseiten/Feeds/Kategorie/Rechtsseiten ueberarbeitet, Kontakt-Modal durch
schwebendes contact-launcher-Widget ersetzt, legal-language-notice ergaenzt;
Platzhalter-SVGs + Theme-CSS angepasst.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:45:58 +00:00
fd8e050cb7 Backend-WIP: Kontakt-Queue, Page-Cache, Validierungs-Feedback, i18n
Gebuendelt, da ueber gemeinsame lang-/Service-Dateien verschraenkt:
- Kontakt-Nachrichten: contact_messages-Tabelle + Model/Enums/Factory,
  Admin-Queue (livewire/admin/contact-messages), ContactController
  persistiert das Anliegen zusaetzlich zur Brand-Mail
- Page-Data-Cache (Services/Web/*PageData) + web-archive-Indizes auf
  press_releases
- Validierungs-Feedback + Company-Portal-Restriction (Feature-Tests)
- i18n-Auslagerung lang/de|en (auth, passwords, validation, static)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:38:11 +00:00
c6b5ec8479 Release-Doku: Stand 09.07. nachgezogen; Runbook FULLTEXT-Fenster + Monitoring
Stand-Block 07.-09.07. (Billing/USt, Tarifwechsel, Agentur-Attribution,
KI-Kennzeichnung, Tages-Fixes), R5.3 abgehakt, Commit-Zahlen aktualisiert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:34:40 +00:00
6984846233 Monitoring (R5.3): Betriebs-Alerts fuer Scheduler-/Queue-Ausfaelle
OperationalAlert loggt critical + Best-Effort-Mail an MONITORING_ALERT_EMAIL
(Fallback LEGAL_CONTACT_EMAIL, feature-flagged). Alle 6 Scheduler-Tasks mit
onFailure, Queue::failing-Listener; .env.example: LOG_STACK=daily als Prod-Soll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:34:40 +00:00
ac870a4076 Buchungsseite: Buchungs- und Verlaengerungsdatum des Abos aus Stripe
Cashier spiegelt die Periode nicht lokal; neuer SubscriptionPeriodResolver
holt start_date + current_period_end (Item-Ebene) gecacht, Fallback null.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:34:28 +00:00
a254bbd6a3 Depublikation: Credit-Preis auf 35 (O6), Preis-Tests nachgezogen
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:34:28 +00:00
07e0bc598e API-Doku: OpenAPI-Spec aus public/ statt geloeschtem docs/ ausliefern
Der docs/-Mount kam nicht mit; die Route /docs/api/v1 lief dadurch auf 500.
Liest jetzt public/api/v1.yml mit 404-Guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:34:28 +00:00
f11e8aab7c PM-Editor UX: Kategorie in die Schreibfläche, Bild-Upload als Modal-Flow
Kategorie-Pflichtfeld direkt unter die Firma statt in die Sidebar.
Titelbild-Upload öffnet ein Modal: erst Bild wählen, dann erscheinen
die gestrafften Schritte (Herkunft & Bildnachweis mit Vorschlag aus
dem Urheber, Personen & Rechte, Bestätigung), Optionales eingeklappt.
Bildrechte-Checkbox im Einreichen-Modal nur noch mit eigenem Titelbild.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 13:55:21 +00:00
807fbc1b80 KI-Kennzeichnung (Art. 50 KI-VO): Pflicht-Deklaration für Text & Bilder
Selbstdeklaration des KI-Einsatzes je PM (Text) und je Bild inkl.
Submit-Gate im PressReleaseService, versioniertem Audit-Trail
(press_release_ai_declarations) und öffentlichem KI-Label in
Einreicher-Stimme. Bestätigung im Submit-Modal ist Pflicht.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 13:54:43 +00:00
74e82eb643 Form-Hooks: Fehl-Scroll aufs erste Feld bei Live-Validation behoben
Zwei Bugs im Scroll-zum-Fehler-Helper: '[wire\:submit]' matchte das
<form> selbst, sodass jeder Klick in ein Feld den Scroll armierte
(sichtbar beim USt-ID-Live-Check im Profil); und der leere, versteckte
Flux-Error-Container unter jedem Feld galt wegen Whitespace als
':not(:empty)' — fokussiert wurde dadurch grundlos das erste Feld.
Enter-Submit wird jetzt über das submit-Event erkannt, Fehlerfelder
nur noch bei echtem Fehlertext.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 13:29:12 +00:00
3297179226 USt-ID vor jeder Buchung per eVatR gegenprüfen, Stripe-Tax-IDs abgleichen
USt-IDs laufen ab — Kunden bestellten anderswo über veraltete IDs
steuerfrei. Vor jedem Checkout wird die Profil-USt-ID jetzt einmal per
eVatR (BZSt) geprüft (Ergebnis 6 h gecacht): Eine definitiv abgelehnte
ID geht nicht an Stripe, nicht mehr passende eu_vat-Einträge am
Stripe-Customer werden entfernt — Stripe Tax rechnet dann wieder mit
Steuer statt Reverse Charge. Fail-open bei API-Ausfall und deutschen
IDs (eVatR prüft nur ausländische EU-IDs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 13:28:56 +00:00
fe8bd2b931 Buchungsseite: Verlauf mit Kauftyp, Erfolgsmeldungen grün
„Eingelöste Käufe" nennt jetzt Kauftyp und PM-Titel (z. B.
„Depublikation: <Titel>") über das neue kompakte shortLabel() am
SinglePurchaseType. Die Checkout-Erfolgsbanner (Buchung, Tarifwechsel,
vorgemerkter Wechsel) und die Extra-PM-Bestätigung nutzen das
etablierte grüne Success-Muster (ok-soft) statt Hub-Blau.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 11:25:24 +00:00
f6882b621a Depublikation: Kauf als eingelöst markieren, Rechnung & Bestätigungs-Mail
Drei Lücken im bezahlten Depublikations-Flow (T12) geschlossen:

- Webhook setzt den Kauf jetzt auf status=consumed (vorher nur
  consumed_at) — erst damit erscheint er unter „Eingelöste Käufe".
- Einmalkäufe (Depublikation, Einzel-PM, Credit-Topup) aktivieren
  invoice_creation im Stripe-Checkout: Ohne das Flag erzeugt Stripe im
  Payment-Mode keine Rechnung und die STR-Spiegelung lief ins Leere.
  Abo-Checkouts bleiben unverändert (Parameter dort unzulässig).
- Neue Bestätigungs-Mail PressReleaseDepublished an den Käufer
  (nicht zwingend der PM-Autor) nach vollzogener Depublikation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 11:25:12 +00:00
d57f6b5c66 Agentur-Attribution: Plan abgehakt, Abnahme-Status 08.07. dokumentiert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:51:56 +00:00
3ff95bc93a 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
59ac67a4c8 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
85fddbfdf8 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
a62a5eea6e PM-Detailseite: Attributions-Zeile Urheber/Übermittler
Rendert unter dem Inhalt „Original-Inhalt von {Firma} und übermittelt
von {Übermittler}" (Legacy-Konvention), Übermittler aus dem Profilfeld
users.agency_name; bei Namensgleichheit kollabiert die Zeile zu
„Inhalt übermittelt von …", ohne Übermittler zu „Original-Inhalt von …".

Nur die Attributions-Hunks aus dem laufenden Web-Umbau; die übrigen
Working-Tree-Änderungen an View/Sprachdateien bleiben unangetastet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:30:20 +00:00
402fa4a38b 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
e457e7a81c Profilfeld users.agency_name für Übermittler-Attribution
Erster Schritt des Umbaus Agentur-Attribution → User-Profil
(dev/agentur-attribution/entwicklungsplan.md, A1): Die Übermittler-
Angabe wird Profil-Eigenschaft des einliefernden Accounts statt
Pseudo-Firma vom Typ „agency".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:20:32 +00:00
881a583bec 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
28de92e18b 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
930760a056 single_purchases: type-ENUM um depublication erweitern
Die create-Migration baut das ENUM dynamisch aus
SinglePurchaseType::cases(); Datenbanken von vor dem T12-Case kannten
depublication daher nicht und der Depublikations-Checkout brach mit
"Data truncated for column 'type'" ab. Die Migration gleicht das ENUM
per MODIFY COLUMN auf den aktuellen Case-Stand ab (nur MySQL; frische
DBs bekommen die volle Liste ohnehin über die create-Migration).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:51:56 +00:00
d7f17bbc76 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
9600a4f887 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
0f8f0b2d31 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
acd241b3f2 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
c4ccc751e9 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
80e04e26e0 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
388ca0e620 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
14994e4748 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
db65c2ed7d 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
b3780e0fea 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
af156608b1 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
304c5817b5 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
5166fc7b40 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
2a8fd0179e 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
54213cac64 Stripe Checkout: automatic_tax aktiviert (USt fehlte in der Session)
Cashier::calculateTaxes() greift nur bei direkter Abo-Erstellung, nicht
bei Checkout-Sessions — Stripe zeigte darum nur den Nettopreis
(QA-Befund 07.07.). Session-Optionen setzen jetzt automatic_tax +
tax_id_collection; Ad-hoc-Buchungen (Credits, Depublikation) laufen
statt ueber checkoutCharge ueber price_data mit tax_behavior=exclusive,
da automatic_tax das verlangt. Stripe Tax im Testmode eingerichtet
(Origin Bielefeld, DE-Registrierung); Live-Schritt in WS-7 ergaenzt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 11:25:28 +00:00