press-releases: Publish-Rechte, Quota-Races & Slug vereinheitlichen

- Publish/Reject/Archive und changeStatus erzwingen konsistent
  press-releases:publish; changeStatus schloss die Lücke, über die das
  Status-Modal die Prüfung umging (Fix#1/H2).
- publish()/changeStatusFromAdmin sperren PM- und User-Zeile
  transaktional; QuotaExceededException wird in Admin-Publish und im
  Auto-Publish-Job abgefangen (Warnung statt 500 / bleibt in Review, Fix#3/M2).
- Slug-Erzeugung nur noch über HasUniqueSlug (generateUniqueSlug +
  saveWithUniqueSlugRetry), redundanter Controller-Helper entfernt (M3).
- API-/Legacy-Text wird vor der Persistenz sanitisiert (M6);
  Scheduler/Legacy-Tracking ignorieren soft-gelöschte PMs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-14 13:47:51 +00:00
parent 7043ea8ad5
commit 0b35c3b04d
19 changed files with 536 additions and 91 deletions

View file

@ -60,6 +60,29 @@ test('press release editing keeps own slug stable', function () {
expect($regenerated)->toBe('meine-mitteilung');
});
test('a stale slug candidate is regenerated after a unique constraint collision', function () {
/** @var TestCase $this */
PressRelease::factory()->create([
'portal' => Portal::Presseecho->value,
'language' => 'de',
'slug' => 'gleichzeitig',
]);
$pressRelease = PressRelease::factory()->make([
'portal' => Portal::Presseecho->value,
'language' => 'de',
'title' => 'Gleichzeitig',
'slug' => 'gleichzeitig',
]);
$pressRelease->saveWithUniqueSlugRetry('Gleichzeitig', [
'portal' => Portal::Presseecho->value,
'language' => 'de',
]);
expect($pressRelease->exists)->toBeTrue()
->and($pressRelease->slug)->toBe('gleichzeitig-2');
});
test('company slug is unique within portal scope', function () {
/** @var TestCase $this */
Company::factory()->create([