'apt-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'm_level' => 1, 'payment_account' => now()->addYear(), ]); } function aptMakeAbo(User $user, int $paidDeliveries = 6, string $nextDate = '2026-08-05', ?string $lastPauseDate = null): UserAbo { $abo = UserAbo::create([ 'user_id' => $user->id, 'member_id' => $user->id, 'shopping_user_id' => 0, 'email' => 'apt-abo-'.uniqid('', true).'@example.com', 'is_for' => 'me', 'payone_userid' => 0, 'clearingtype' => 'cc', 'amount' => 5000, 'active' => true, 'status' => 2, 'abo_interval' => 5, 'start_date' => '2026-01-05', 'last_date' => '2026-07-05', 'next_date' => $nextDate, 'last_pause_date' => $lastPauseDate, ]); for ($i = 0; $i < $paidDeliveries; $i++) { UserAboOrder::create([ 'user_abo_id' => $abo->id, 'shopping_order_id' => 0, 'status' => 2, 'paid' => true, ]); } return $abo->fresh(); } function aptPause(UserAbo $abo, string $view = 'portal'): bool { $repository = new AboRepository; $repository->setModel($abo); $data = [ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => $view, 'abo_interval' => $abo->abo_interval, 'abo_pause' => 'true', ]; if ($view === 'admin') { // Das Admin-Formular sendet den Ausfuehrungsmonat immer mit $data['abo_next_month'] = now()->addMonth()->format('Y-m'); } return $repository->update($data); } it('pausiert ein Abo um einen Monat und protokolliert die Aktion', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user); expect(aptPause($abo))->toBeTrue(); $abo = $abo->fresh(); expect(Carbon::parse($abo->getRawOriginal('next_date'))->format('Y-m-d'))->toBe('2026-09-05') ->and(Carbon::parse($abo->getRawOriginal('last_pause_date'))->format('Y-m-d'))->toBe('2026-07-22') ->and($abo->pause_count)->toBe(1) ->and((int) $abo->status)->toBe(2) ->and($abo->active)->toBeTrue(); $history = UserAboStatusHistory::where('user_abo_id', $abo->id)->get(); expect($history)->toHaveCount(1) ->and($history[0]->action)->toBe(UserAboStatusHistory::ACTION_PAUSED) ->and($history[0]->view)->toBe('portal') ->and($history[0]->previous_next_date->format('Y-m-d'))->toBe('2026-08-05') ->and($history[0]->new_next_date->format('Y-m-d'))->toBe('2026-09-05'); }); it('lehnt die Pause vor Erreichen der Mindestlaufzeit ab', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user, paidDeliveries: 3); // Default-Mindestlaufzeit ist 6 expect(aptPause($abo))->toBeFalse(); expect(Carbon::parse($abo->fresh()->getRawOriginal('next_date'))->format('Y-m-d'))->toBe('2026-08-05') ->and($abo->fresh()->pause_count)->toBe(0) ->and(UserAboStatusHistory::count())->toBe(0); }); it('lehnt die Pause innerhalb der Sperrfrist vor der Ausfuehrung ab', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user, nextDate: '2026-07-24'); // in 2 Tagen, Sperre = 3 Tage expect(aptPause($abo))->toBeFalse() ->and($abo->fresh()->pause_count)->toBe(0); }); it('erlaubt die Pause nur einmal pro rollierendem Intervall', function (): void { $user = aptMakeUser(); // Letzte Pause vor 2 Monaten, Default-Intervall 6 Monate -> gesperrt $abo = aptMakeAbo($user, lastPauseDate: '2026-05-22'); expect(AboHelper::canPauseAbo($abo, 'portal'))->toBeFalse() ->and(aptPause($abo))->toBeFalse() ->and(AboHelper::nextPossiblePauseDate($abo)->format('Y-m-d'))->toBe('2026-11-22'); // Verkuerztes Intervall per Setting -> wieder erlaubt Setting::setContentBySlug('abo-pause-interval-months', 2, 'int'); expect(AboHelper::getPauseIntervalMonths())->toBe(2) ->and(AboHelper::canPauseAbo($abo->fresh(), 'portal'))->toBeTrue() ->and(aptPause($abo->fresh()))->toBeTrue(); }); it('erlaubt Admins die Pause ohne Regelpruefung', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user, paidDeliveries: 0, lastPauseDate: '2026-07-01'); expect(AboHelper::canPauseAbo($abo, 'admin'))->toBeTrue() ->and(aptPause($abo, 'admin'))->toBeTrue() ->and(Carbon::parse($abo->fresh()->getRawOriginal('next_date'))->format('Y-m-d'))->toBe('2026-09-05'); }); it('lehnt die Pause fuer inaktive oder gekuendigte Abos ab', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user); $abo->update(['status' => 6, 'active' => false]); expect(AboHelper::canPauseAbo($abo->fresh(), 'portal'))->toBeFalse() ->and(aptPause($abo->fresh()))->toBeFalse(); }); it('protokolliert die Kuendigung in der Status-Historie', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user); $repository = new AboRepository; $repository->setModel($abo); $repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'portal', 'abo_interval' => $abo->abo_interval, 'abo_cancel' => 'true', ]); expect((int) $abo->fresh()->status)->toBe(4); $history = UserAboStatusHistory::where('user_abo_id', $abo->id)->get(); expect($history)->toHaveCount(1) ->and($history[0]->action)->toBe(UserAboStatusHistory::ACTION_CANCELLED); }); it('zeigt im Selfservice-Modal die Pause und Outline-Kuendigung ohne Aktiv-Schalter', function (): void { app('request')->setLaravelSession(app('session.store')); $user = aptMakeUser(); $abo = aptMakeAbo($user); $html = view('admin.abo.modal_abo_update', [ 'user_abo' => $abo, 'data' => ['action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'portal'], 'route' => '/portal/abo/update', ])->render(); expect($html) ->toContain('abo_pause') ->toContain('btn-outline-danger') ->toContain(__('abo.cancel_abo')) ->not->toContain('switcher-input'); $adminHtml = view('admin.abo.modal_abo_update', [ 'user_abo' => $abo, 'data' => ['action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'admin'], 'route' => '/admin/abo/update', ])->render(); expect($adminHtml)->toContain('switcher-input') ->not->toContain('abo_pause'); }); it('zeigt die Pause im Admin-Aenderungsprotokoll', function (): void { $user = aptMakeUser(); $abo = aptMakeAbo($user); aptPause($abo); $html = view('admin.abo._change_history', [ 'user_abo' => $abo->fresh(), 'isAdmin' => true, ])->render(); expect($html) ->toContain(e(__('abo_history.status_history_title'))) ->toContain(__('abo_history.status_paused')) ->toContain(__('abo_history.status_shift', ['from' => '05.08.2026', 'to' => '05.09.2026'])) ->toContain(__('abo_history.channel_portal')); });