Carbon::setTestNow()); describe('Abo-Sperrfristen als Setting', function () { it('nutzt die Defaults, wenn kein Setting gesetzt ist (A2: keine Vorlaufsperre)', function () { expect(AboRepository::getLockDaysChange())->toBe(0) ->and(AboRepository::getLockDaysPauseCancel())->toBe(0); }); it('liest die konfigurierten Sperrfristen aus den Settings', function () { Setting::setContentBySlug('abo-lock-days-change', 5, 'int'); Setting::setContentBySlug('abo-lock-days-pause-cancel', 7, 'int'); expect(AboRepository::getLockDaysChange())->toBe(5) ->and(AboRepository::getLockDaysPauseCancel())->toBe(7); }); it('nimmt 0 als ausdrueckliche Sperrfrist und faellt nur bei leer auf den Default zurueck', function () { Setting::setContentBySlug('abo-lock-days-change', 0, 'int'); Setting::setContentBySlug('abo-lock-days-pause-cancel', 4, 'int'); expect(AboRepository::getLockDaysChange())->toBe(0) ->and(AboRepository::getLockDaysPauseCancel())->toBe(4); Setting::setContentBySlug('abo-lock-days-pause-cancel', '', 'int'); expect(AboRepository::getLockDaysPauseCancel())->toBe(0); }); it('gibt eine Änderung frei, die bei laengerer Sperrfrist gesperrt wäre, wenn die Sperrfrist reduziert wird', function () { Carbon::setTestNow('2026-03-19 00:00:00'); // Liefertag 20, next_date in 6 Tagen → Liefertag-WECHSEL auf 25 testen. $abo = makeLockTestAbo([ 'abo_interval' => 20, 'next_date' => '2026-03-25', ]); $this->actingAs($abo->user); $repository = new AboRepository; $repository->setModel($abo); // Mit Sperrfrist 10 ist der Wechsel gesperrt (6 <= 10): Setting::setContentBySlug('abo-lock-days-change', 10, 'int'); expect($repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'me', 'abo_interval' => 25, 'abo_is_active' => 'true', ]))->toBeFalse(); // Sperrfrist auf 3 Tage reduzieren → Wechsel jetzt erlaubt (6 > 3, neues Datum 2026-03-25): Setting::setContentBySlug('abo-lock-days-change', 3, 'int'); expect($repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'me', 'abo_interval' => 25, 'abo_is_active' => 'true', ]))->toBeTrue() ->and($abo->refresh()->abo_interval)->toBe(25); }); it('zeigt die konfigurierte Sperrfrist in der Fehlermeldung (nicht den Default 10)', function () { Carbon::setTestNow('2026-03-19 00:00:00'); app()->setLocale('de'); Setting::setContentBySlug('abo-lock-days-change', 3, 'int'); // Liefertag-WECHSEL (20 → 25), next_date morgen (1 Tag) → bei Sperrfrist 3 weiterhin gesperrt. $abo = makeLockTestAbo(['abo_interval' => 20, 'next_date' => '2026-03-20']); $this->actingAs($abo->user); $repository = new AboRepository; $repository->setModel($abo); expect($repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'me', 'abo_interval' => 25, 'abo_is_active' => 'true', ]))->toBeFalse() ->and(session('alert-error'))->toContain('mindestens 3 Tage') ->and(session('alert-error'))->toContain('20.03.2026') ->and(session('alert-error'))->not->toContain('mindestens 10 Tage'); }); it('erlaubt Pausieren im Change-Lock-Fenster, wenn außerhalb der Pause-Sperre', function () { Carbon::setTestNow('2026-03-19 00:00:00'); Setting::setContentBySlug('abo-min-duration', 0, 'int'); Setting::setContentBySlug('abo-lock-days-change', 10, 'int'); Setting::setContentBySlug('abo-lock-days-pause-cancel', 3, 'int'); // 5 Tage entfernt: innerhalb Change-Lock (10), außerhalb Pause-Lock (3). $abo = makeLockTestAbo(['abo_interval' => 20, 'next_date' => '2026-03-24', 'active' => true, 'status' => 2]); $this->actingAs($abo->user); $repository = new AboRepository; $repository->setModel($abo); $result = $repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'me', 'abo_interval' => 20, 'abo_pause' => 'true', ]); // Die Pause verschiebt nur den Liefertermin auf den Liefertag des Folgemonats; // das Abo bleibt aktiv. expect($result)->toBeTrue() ->and($abo->refresh()->active)->toBeTrue() ->and($abo->status)->toBe(2) ->and($abo->getRawOriginal('next_date'))->toBe('2026-04-20'); }); }); describe('isAboChangeLocked (UI-Sperre Liefertag)', function () { afterEach(fn () => Carbon::setTestNow()); it('sperrt ohne Vorlauf erst am Ausfuehrungstag ab 0:00 Uhr, der Vortag ist frei (A2)', function () { Carbon::setTestNow('2026-03-19 23:59:00'); $abo = makeLockTestAbo(['next_date' => '2026-03-20']); expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeFalse() ->and(AboHelper::isPauseCancelLocked($abo, 'me'))->toBeFalse(); Carbon::setTestNow('2026-03-20 00:00:00'); $abo = $abo->fresh(); expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeTrue() ->and(AboHelper::isPauseCancelLocked($abo, 'me'))->toBeTrue(); // Liegen gebliebener Termin sperrt nicht. Carbon::setTestNow('2026-03-21 09:00:00'); expect(AboHelper::isAboChangeLocked($abo->fresh(), 'me'))->toBeFalse(); }); it('respektiert die konfigurierte Sperrfrist (gesperrt ab 0:00 Uhr des Tages next_date - lock)', function () { Carbon::setTestNow('2026-03-19 00:00:00'); $abo = makeLockTestAbo(['next_date' => '2026-03-24']); // 5 Tage entfernt Setting::setContentBySlug('abo-lock-days-change', 10, 'int'); expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeTrue(); // 5 <= 10 Setting::setContentBySlug('abo-lock-days-change', 5, 'int'); expect(AboHelper::isAboChangeLocked($abo->fresh(), 'me'))->toBeTrue(); // 5 <= 5 Setting::setContentBySlug('abo-lock-days-change', 3, 'int'); expect(AboHelper::isAboChangeLocked($abo->fresh(), 'me'))->toBeFalse(); // 5 > 3 }); it('gilt nicht für Admin und nicht ohne next_date', function () { Carbon::setTestNow('2026-03-19 00:00:00'); $abo = makeLockTestAbo(['next_date' => '2026-03-20']); expect(AboHelper::isAboChangeLocked($abo, 'admin'))->toBeFalse(); $abo->next_date = null; expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeFalse(); }); }); /** * @param array $overrides */ function makeLockTestAbo(array $overrides = []): UserAbo { $country = Country::create([ 'code' => 'DE', 'phone' => '49', 'en' => 'Germany', 'de' => 'Deutschland', 'es' => 'Alemania', 'fr' => 'Allemagne', 'it' => 'Germania', 'ru' => 'Deutschland', ]); $user = User::forceCreate([ 'email' => 'abo-lock-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'admin' => 0, ]); $shoppingUser = ShoppingUser::create([ 'auth_user_id' => $user->id, 'member_id' => $user->id, 'billing_country_id' => $country->id, 'shipping_country_id' => $country->id, 'billing_email' => $user->email, 'is_for' => 'me', 'is_from' => 'user_order', ]); return UserAbo::create(array_merge([ 'user_id' => $user->id, 'member_id' => $user->id, 'shopping_user_id' => $shoppingUser->id, 'is_for' => 'me', 'email' => $user->email, 'payone_userid' => random_int(100000, 999999), 'clearingtype' => 'cc', 'active' => true, 'status' => 2, 'abo_interval' => 5, 'start_date' => '2026-01-05', 'last_date' => '2026-02-05', 'next_date' => '2026-03-05', ], $overrides)); }