Abo (A1+A2): Liefertag frei waehlbar 1-31, Aenderung nur nach vorn, Sperre erst am Ausfuehrungstag
A1: Jeder Monatstag ist als Liefertag erlaubt (UserAbo::allowedDeliveryDays), Tage 29-31 liefern in kuerzeren Monaten am letzten Tag. Eine Stelle AboHelper::dateForDayInMonth klammert; setNextDate, Erstausfuehrung, Admin-Termin und Pause rechnen darueber, der Folgemonat immer aus dem Liefertag (31.01 -> 28.02 -> 31.03). Liefertag-Aenderung gilt fuer den laufenden Zyklus und nur nach vorn: ist der Tag im Referenzmonat vorbei, wird abgelehnt, der Monat verschiebt sich nie (Rueckfrage R1). Admin-Filter "naechste Ausfuehrung" als Datumsbereich. Prognose-Kacheln bekommen die Kachel Monatsende, Team-Abos rechnen bis Monatsende. A2: Sperrfristen Default 0, Setting 0 ist ausdruecklich 0 (fiel bisher auf den Default zurueck). Bedingung Kalendertage <= Sperre, zentral in AboHelper::isWithinLock: am Ausfuehrungstag ab 0:00 Uhr fixiert, Vortag bis 23:59 Uhr frei, fuer Liefertag, Produkte, Pause und Kuendigung (R2). Texte mit _zero-Varianten ueber AboHelper::lockMessage. Sprachdateien de/en/es/fr, neuer AboDeliveryDayTest, bestehende Tests angepasst, Plan-Status mit Deploy-Schritt (Settings auf 0). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
d6efe56cd4
commit
cf0311afc1
30 changed files with 638 additions and 185 deletions
204
tests/Feature/AboDeliveryDayTest.php
Normal file
204
tests/Feature/AboDeliveryDayTest.php
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\UserAbo;
|
||||
use App\Repositories\AboRepository;
|
||||
use App\Services\AboHelper;
|
||||
use App\Services\HTMLHelper;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Tickets A1/A2 (Entwicklungsplan September 2026): jeder Monatstag 1-31 als
|
||||
* Liefertag, Klammerung auf das Monatsende, Aenderung nur "nach vorn" im
|
||||
* laufenden Zyklus (Rueckfrage R1), Sperre erst am Ausfuehrungstag ab 0:00 Uhr.
|
||||
*/
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
afterEach(fn () => Carbon::setTestNow());
|
||||
|
||||
function addUser(): User
|
||||
{
|
||||
return User::forceCreate([
|
||||
'email' => 'add-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
'm_level' => 1,
|
||||
'payment_account' => now()->addYear(),
|
||||
]);
|
||||
}
|
||||
|
||||
function addAbo(User $user, int $day, string $nextDate): UserAbo
|
||||
{
|
||||
return UserAbo::create([
|
||||
'user_id' => $user->id,
|
||||
'member_id' => $user->id,
|
||||
'shopping_user_id' => 0,
|
||||
'email' => 'add-abo-'.uniqid('', true).'@example.com',
|
||||
'is_for' => 'me',
|
||||
'payone_userid' => 0,
|
||||
'clearingtype' => 'cc',
|
||||
'amount' => 5000,
|
||||
'active' => true,
|
||||
'status' => 2,
|
||||
'abo_interval' => $day,
|
||||
'start_date' => '2026-01-05',
|
||||
'last_date' => null,
|
||||
'next_date' => $nextDate,
|
||||
]);
|
||||
}
|
||||
|
||||
function addChange(UserAbo $abo, int $day, string $view = 'me'): bool
|
||||
{
|
||||
$repository = new AboRepository;
|
||||
$repository->setModel($abo);
|
||||
|
||||
return $repository->update([
|
||||
'action' => 'abo_update_settings',
|
||||
'id' => $abo->id,
|
||||
'view' => $view,
|
||||
'abo_interval' => $day,
|
||||
'abo_is_active' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
it('erlaubt jeden Monatstag 1-31 und lehnt alles andere ab', function () {
|
||||
expect(UserAbo::allowedDeliveryDays())->toBe(range(1, 31))
|
||||
->and(UserAbo::isAllowedDeliveryDay(1))->toBeTrue()
|
||||
->and(UserAbo::isAllowedDeliveryDay('31'))->toBeTrue()
|
||||
->and(UserAbo::isAllowedDeliveryDay(0))->toBeFalse()
|
||||
->and(UserAbo::isAllowedDeliveryDay(32))->toBeFalse()
|
||||
->and(UserAbo::isAllowedDeliveryDay('5.5'))->toBeFalse()
|
||||
->and(UserAbo::isAllowedDeliveryDay(null))->toBeFalse();
|
||||
});
|
||||
|
||||
it('bietet 31 Liefertage an und kennzeichnet 29-31 als letzter Tag in kuerzeren Monaten', function () {
|
||||
Carbon::setTestNow('2026-09-16');
|
||||
|
||||
$options = HTMLHelper::getAboDeliveryOptions(31);
|
||||
|
||||
expect(substr_count($options, '<option'))->toBe(31)
|
||||
->and($options)->toContain('value="1"')
|
||||
->and($options)->toContain('value="31" data-days="45" data-date="31.10.2026" selected="selected"')
|
||||
->and(HTMLHelper::getAboStrLang(31))->toBe('31. '.__('abo.of_month').' '.__('abo.of_month_short_hint'))
|
||||
->and(HTMLHelper::getAboStrLang(28))->toBe('28. '.__('abo.of_month'));
|
||||
});
|
||||
|
||||
it('klammert Tag 29-31 auf das Monatsende und rechnet den Folgemonat wieder aus dem Liefertag', function () {
|
||||
expect(AboHelper::dateForDayInMonth('2026-02-01', 31)->format('Y-m-d'))->toBe('2026-02-28')
|
||||
->and(AboHelper::dateForDayInMonth('2028-02-01', 30)->format('Y-m-d'))->toBe('2028-02-29')
|
||||
->and(AboHelper::dateForDayInMonth('2026-04-15', 31)->format('Y-m-d'))->toBe('2026-04-30')
|
||||
->and(AboHelper::dateForDayInMonth('2026-06-01', 31)->format('Y-m-d'))->toBe('2026-06-30')
|
||||
->and(AboHelper::dateForDayInMonth('2026-09-01', 31)->format('Y-m-d'))->toBe('2026-09-30')
|
||||
->and(AboHelper::dateForDayInMonth('2026-11-01', 30)->format('Y-m-d'))->toBe('2026-11-30')
|
||||
->and(AboHelper::dateForDayInMonth('2026-03-01', 31)->format('Y-m-d'))->toBe('2026-03-31');
|
||||
|
||||
// Ein 31er-Abo: Januar 31 -> Februar 28 -> Maerz 31 -> April 30 (kein Monat doppelt, keiner uebersprungen).
|
||||
$next = AboHelper::setNextDate(Carbon::parse('2026-01-31 04:00:00'), 31);
|
||||
expect($next->format('Y-m-d'))->toBe('2026-02-28');
|
||||
$next = AboHelper::setNextDate($next->copy()->setTime(4, 0), 31);
|
||||
expect($next->format('Y-m-d'))->toBe('2026-03-31');
|
||||
$next = AboHelper::setNextDate($next->copy()->setTime(4, 0), 31);
|
||||
expect($next->format('Y-m-d'))->toBe('2026-04-30');
|
||||
|
||||
// Tag 1: Ausfuehrung am 1. um 04:00 -> naechster Termin ist der 1. des Folgemonats, nicht uebernaechster.
|
||||
expect(AboHelper::setNextDate(Carbon::parse('2026-09-01 04:00:00'), 1)->format('Y-m-d'))->toBe('2026-10-01')
|
||||
->and(AboHelper::setNextDate(Carbon::parse('2026-09-16 10:00:00'), 20)->format('Y-m-d'))->toBe('2026-09-20')
|
||||
->and(AboHelper::setNextDate(Carbon::parse('2026-09-16 10:00:00'), 10)->format('Y-m-d'))->toBe('2026-10-10');
|
||||
});
|
||||
|
||||
it('setzt die Erstausfuehrung fuer Tag 31 im Folgemonat auf dessen letzten Tag', function () {
|
||||
Carbon::setTestNow('2026-01-15');
|
||||
|
||||
expect(AboHelper::getFirstAboDate(now(), 31)->format('Y-m-d'))->toBe('2026-02-28')
|
||||
->and(AboHelper::getFirstAboDate(now(), 1)->format('Y-m-d'))->toBe('2026-02-01');
|
||||
|
||||
// Mindestabstand 10 Tage: Bestellung am 25.01. fuer Tag 1 -> nicht 01.02. (7 Tage), sondern 01.03.
|
||||
Carbon::setTestNow('2026-01-25');
|
||||
expect(AboHelper::getFirstAboDate(now(), 1)->format('Y-m-d'))->toBe('2026-03-01');
|
||||
});
|
||||
|
||||
it('verschiebt den Liefertag im laufenden Zyklus nur nach vorn und nie in einen anderen Monat', function () {
|
||||
Carbon::setTestNow('2026-09-16 10:00:00');
|
||||
app('request')->setLaravelSession(app('session.store'));
|
||||
$user = addUser();
|
||||
$this->actingAs($user);
|
||||
|
||||
// 10. -> 24.: Lieferung dieses Monats ist noch offen (next_date 20.09.), neuer Termin 24.09.
|
||||
$abo = addAbo($user, 20, '2026-09-20');
|
||||
expect(addChange($abo, 24))->toBeTrue()
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-09-24')
|
||||
->and($abo->fresh()->abo_interval)->toBe(24);
|
||||
|
||||
// 24. -> 5.: der 5. ist im September vorbei -> Ablehnung, Termin bleibt.
|
||||
expect(addChange($abo->fresh(), 5))->toBeFalse()
|
||||
->and(session('alert-error'))->toBe(__('abo.error_abo_interval_passed', ['day' => 5, 'next' => '24.09.2026']))
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-09-24');
|
||||
|
||||
// Heute selbst (16.) ist ebenfalls vorbei: Aenderung muss nach heute liegen.
|
||||
expect(addChange($abo->fresh(), 16))->toBeFalse();
|
||||
|
||||
// 17. (morgen) ist erlaubt.
|
||||
expect(addChange($abo->fresh(), 17))->toBeTrue()
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-09-17');
|
||||
|
||||
// Lieferung schon gelaufen (next_date im Oktober): Referenzmonat Oktober, 5. erlaubt.
|
||||
$delivered = addAbo($user, 10, '2026-10-10');
|
||||
expect(addChange($delivered, 5))->toBeTrue()
|
||||
->and($delivered->fresh()->getRawOriginal('next_date'))->toBe('2026-10-05');
|
||||
|
||||
// Tag 31 im Referenzmonat November -> 30.11.
|
||||
$november = addAbo($user, 10, '2026-11-10');
|
||||
expect(addChange($november, 31))->toBeTrue()
|
||||
->and($november->fresh()->getRawOriginal('next_date'))->toBe('2026-11-30')
|
||||
->and($november->fresh()->abo_interval)->toBe(31);
|
||||
});
|
||||
|
||||
it('sperrt die Liefertag-Aenderung erst am Ausfuehrungstag, am Vortag bis 23:59 Uhr ist sie frei (A2)', function () {
|
||||
app('request')->setLaravelSession(app('session.store'));
|
||||
$user = addUser();
|
||||
$this->actingAs($user);
|
||||
$abo = addAbo($user, 20, '2026-09-20');
|
||||
|
||||
Carbon::setTestNow('2026-09-19 23:59:00');
|
||||
expect(addChange($abo, 25))->toBeTrue()
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-09-25');
|
||||
|
||||
Carbon::setTestNow('2026-09-25 00:00:00');
|
||||
expect(addChange($abo->fresh(), 28))->toBeFalse()
|
||||
->and(session('alert-error'))->toBe(__('abo.error_change_locked_zero', ['lock' => 0, 'days' => 0, 'date' => '25.09.2026']))
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-09-25');
|
||||
|
||||
// Mit gepflegter Sperrfrist 3: am 22. (3 Tage vorher) bereits gesperrt, am 21. frei.
|
||||
Setting::setContentBySlug('abo-lock-days-change', 3, 'int');
|
||||
Carbon::setTestNow('2026-09-22 08:00:00');
|
||||
expect(addChange($abo->fresh(), 28))->toBeFalse()
|
||||
->and(session('alert-error'))->toContain('mindestens 3 Tage');
|
||||
|
||||
Carbon::setTestNow('2026-09-21 08:00:00');
|
||||
// Neuer Termin 28.09. liegt 7 Tage entfernt (> 3) -> erlaubt.
|
||||
expect(addChange($abo->fresh(), 28))->toBeTrue();
|
||||
// Neuer Termin 23.09. laege nur 2 Tage entfernt (<= 3) -> zu frueh.
|
||||
expect(addChange($abo->fresh(), 23))->toBeFalse()
|
||||
->and(session('alert-error'))->toBe(__('abo.error_abo_interval_too_soon', ['days' => 2, 'lock' => 3]));
|
||||
});
|
||||
|
||||
it('klammert auch den Admin-Termin und laesst den Admin frei waehlen', function () {
|
||||
Carbon::setTestNow('2026-09-16 10:00:00');
|
||||
app('request')->setLaravelSession(app('session.store'));
|
||||
$admin = addUser();
|
||||
$this->actingAs($admin);
|
||||
$abo = addAbo($admin, 5, '2026-10-05');
|
||||
|
||||
$repository = new AboRepository;
|
||||
$repository->setModel($abo);
|
||||
|
||||
expect($repository->update([
|
||||
'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'admin',
|
||||
'abo_interval' => 31, 'abo_next_month' => '2026-11', 'abo_is_active' => 'true',
|
||||
]))->toBeTrue()
|
||||
->and($abo->fresh()->getRawOriginal('next_date'))->toBe('2026-11-30')
|
||||
->and($abo->fresh()->abo_interval)->toBe(31);
|
||||
});
|
||||
|
|
@ -16,9 +16,9 @@ uses(TestCase::class, RefreshDatabase::class);
|
|||
afterEach(fn () => Carbon::setTestNow());
|
||||
|
||||
describe('Abo-Sperrfristen als Setting', function () {
|
||||
it('nutzt die Defaults, wenn kein Setting gesetzt ist', function () {
|
||||
expect(AboRepository::getLockDaysChange())->toBe(10)
|
||||
->and(AboRepository::getLockDaysPauseCancel())->toBe(3);
|
||||
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 () {
|
||||
|
|
@ -29,13 +29,19 @@ describe('Abo-Sperrfristen als Setting', function () {
|
|||
->and(AboRepository::getLockDaysPauseCancel())->toBe(7);
|
||||
});
|
||||
|
||||
it('fällt bei 0 / leer auf den Default zurück', function () {
|
||||
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(10);
|
||||
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 Default gesperrt wäre, wenn die Sperrfrist reduziert wird', function () {
|
||||
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.
|
||||
|
|
@ -48,7 +54,8 @@ describe('Abo-Sperrfristen als Setting', function () {
|
|||
$repository = new AboRepository;
|
||||
$repository->setModel($abo);
|
||||
|
||||
// Mit Default (10) ist der Wechsel gesperrt (6 < 10):
|
||||
// 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,
|
||||
|
|
@ -57,7 +64,7 @@ describe('Abo-Sperrfristen als Setting', function () {
|
|||
'abo_is_active' => 'true',
|
||||
]))->toBeFalse();
|
||||
|
||||
// Sperrfrist auf 3 Tage reduzieren → Wechsel jetzt erlaubt (6 ≥ 3, neues Datum 2026-03-25):
|
||||
// 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([
|
||||
|
|
@ -89,12 +96,15 @@ describe('Abo-Sperrfristen als Setting', function () {
|
|||
'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);
|
||||
|
|
@ -121,24 +131,36 @@ describe('Abo-Sperrfristen als Setting', function () {
|
|||
describe('isAboChangeLocked (UI-Sperre Liefertag)', function () {
|
||||
afterEach(fn () => Carbon::setTestNow());
|
||||
|
||||
it('ist gesperrt im Fenster, frei außerhalb (User-Ansicht)', function () {
|
||||
Carbon::setTestNow('2026-03-19 00:00:00');
|
||||
$abo = makeLockTestAbo(['next_date' => '2026-03-20']); // 1 Tag → < Default 10
|
||||
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'))->toBeTrue();
|
||||
expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeFalse()
|
||||
->and(AboHelper::isPauseCancelLocked($abo, 'me'))->toBeFalse();
|
||||
|
||||
$abo->next_date = '2026-04-19'; // weit entfernt
|
||||
expect(AboHelper::isAboChangeLocked($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', function () {
|
||||
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
|
||||
|
||||
expect(AboHelper::isAboChangeLocked($abo, 'me'))->toBeTrue(); // 5 < 10 (Default)
|
||||
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, 'me'))->toBeFalse(); // 5 >= 3
|
||||
expect(AboHelper::isAboChangeLocked($abo->fresh(), 'me'))->toBeFalse(); // 5 > 3
|
||||
});
|
||||
|
||||
it('gilt nicht für Admin und nicht ohne next_date', function () {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ it('zeigt vor der Mindestlaufzeit nur den Fortschritt und den Hinweis auf die ve
|
|||
|
||||
it('zeigt innerhalb der Sperrfrist vor der Ausfuehrung den Hinweis statt der Buttons', function () {
|
||||
// Ausfuehrung in 2 Tagen, Sperre pausieren/kuendigen = 3 Tage
|
||||
Setting::setContentBySlug('abo-lock-days-pause-cancel', 3, 'int');
|
||||
$abo = pcsAbo(pcsUser(), nextDate: '2026-09-18');
|
||||
|
||||
expect(AboHelper::isPauseCancelLocked($abo, 'me'))->toBeTrue()
|
||||
|
|
@ -119,11 +120,23 @@ it('zeigt innerhalb der Sperrfrist vor der Ausfuehrung den Hinweis statt der But
|
|||
$html = pcsRender($abo);
|
||||
|
||||
expect($html)
|
||||
->toContain(__('abo.pause_cancel_locked_hint', ['date' => '18.09.2026', 'lock' => AboRepository::getLockDaysPauseCancel()]))
|
||||
->toContain(__('abo.pause_cancel_locked_hint', ['date' => '18.09.2026', 'lock' => 3, 'days' => 2]))
|
||||
->not->toContain('id="modal-abo-cancel"')
|
||||
->not->toContain('name="abo_pause"');
|
||||
});
|
||||
|
||||
it('zeigt ohne Vorlaufsperre am Ausfuehrungstag den Vortag-Hinweis (A2)', function () {
|
||||
$abo = pcsAbo(pcsUser(), nextDate: '2026-09-16');
|
||||
|
||||
$html = pcsRender($abo);
|
||||
|
||||
expect($html)
|
||||
->toContain(__('abo.pause_cancel_locked_hint_zero', ['date' => '16.09.2026', 'lock' => 0, 'days' => 0]))
|
||||
->not->toContain('id="modal-abo-cancel"');
|
||||
|
||||
expect(pcsRender(pcsAbo(pcsUser(), nextDate: '2026-09-17')))->toContain('id="modal-abo-cancel"');
|
||||
});
|
||||
|
||||
it('bietet im Kuendigungs-Modal keine Pause an, wenn die Pause gerade nicht moeglich ist', function () {
|
||||
$abo = pcsAbo(pcsUser(), attributes: ['last_pause_date' => '2026-08-01']);
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ it('lehnt die Pause vor Erreichen der Mindestlaufzeit ab', function (): void {
|
|||
});
|
||||
|
||||
it('lehnt die Pause innerhalb der Sperrfrist vor der Ausfuehrung ab', function (): void {
|
||||
Setting::setContentBySlug('abo-lock-days-pause-cancel', 3, 'int');
|
||||
$user = aptMakeUser();
|
||||
$abo = aptMakeAbo($user, nextDate: '2026-07-24'); // in 2 Tagen, Sperre = 3 Tage
|
||||
|
||||
|
|
@ -126,6 +127,16 @@ it('lehnt die Pause innerhalb der Sperrfrist vor der Ausfuehrung ab', function (
|
|||
->and($abo->fresh()->pause_count)->toBe(0);
|
||||
});
|
||||
|
||||
it('lehnt die Pause ohne Vorlaufsperre nur am Ausfuehrungstag selbst ab (A2)', function (): void {
|
||||
$user = aptMakeUser();
|
||||
$tomorrow = aptMakeAbo($user, nextDate: '2026-07-23');
|
||||
$today = aptMakeAbo(aptMakeUser(), nextDate: '2026-07-22');
|
||||
|
||||
expect(aptPause($tomorrow))->toBeTrue()
|
||||
->and(aptPause($today))->toBeFalse()
|
||||
->and(session('alert-error'))->toBe(__('abo.error_pause_locked_zero', ['lock' => 0, 'days' => 0, 'date' => '22.07.2026']));
|
||||
});
|
||||
|
||||
it('erlaubt die Pause nur einmal pro rollierendem Intervall', function (): void {
|
||||
$user = aptMakeUser();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\Setting;
|
||||
use App\Models\ShoppingUser;
|
||||
use App\Models\UserAbo;
|
||||
use App\Repositories\AboRepository;
|
||||
|
|
@ -118,8 +119,9 @@ it('erlaubt Admins den nächsten Ausführungstermin ohne User-Sperrfrist direkt
|
|||
expect($abo->getRawOriginal('next_date'))->toBe('2026-03-20');
|
||||
});
|
||||
|
||||
it('behält die User-Sperrfrist für normale Abo-Änderungen bei', function () {
|
||||
it('behält eine gepflegte User-Sperrfrist für normale Abo-Änderungen bei', function () {
|
||||
Carbon::setTestNow('2026-03-19 12:00:00');
|
||||
Setting::setContentBySlug('abo-lock-days-change', 10, 'int');
|
||||
|
||||
$abo = createRepositoryTestAbo([
|
||||
'abo_interval' => 5,
|
||||
|
|
@ -142,6 +144,30 @@ it('behält die User-Sperrfrist für normale Abo-Änderungen bei', function () {
|
|||
expect($abo->getRawOriginal('next_date'))->toBe('2026-03-20');
|
||||
});
|
||||
|
||||
it('erlaubt ohne Vorlaufsperre die Aenderung noch am Vortag der Lieferung (A2)', function () {
|
||||
Carbon::setTestNow('2026-03-19 12:00:00');
|
||||
|
||||
$abo = createRepositoryTestAbo([
|
||||
'abo_interval' => 5,
|
||||
'next_date' => '2026-03-20',
|
||||
]);
|
||||
|
||||
$this->actingAs($abo->user);
|
||||
$this->repository->setModel($abo);
|
||||
|
||||
$result = $this->repository->update([
|
||||
'action' => 'abo_update_settings',
|
||||
'id' => $abo->id,
|
||||
'view' => 'me',
|
||||
'abo_interval' => 20,
|
||||
'abo_is_active' => 'true',
|
||||
]);
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
expect($abo->refresh()->abo_interval)->toBe(20);
|
||||
expect($abo->getRawOriginal('next_date'))->toBe('2026-03-20');
|
||||
});
|
||||
|
||||
/**
|
||||
* Hilfsfunktion zum Aufruf privater Methoden.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -131,11 +131,29 @@ it('berechnet die Prognose kumuliert je Ausfuehrungstermin inklusive eigener Abo
|
|||
$forecast = app(PaylineForecastService::class)->forecastDays($root, 100.0);
|
||||
|
||||
expect($forecast['base'])->toBe(100.0)
|
||||
->and($forecast['days'])->toHaveCount(4)
|
||||
->and($forecast['days'][0])->toMatchArray(['day' => 5, 'upcoming' => 20.0, 'total' => 120.0, 'is_past' => false])
|
||||
->and($forecast['days'])->toHaveCount(5)
|
||||
->and($forecast['days'][0])->toMatchArray(['day' => 5, 'upcoming' => 20.0, 'total' => 120.0, 'is_past' => false, 'is_month_end' => false])
|
||||
->and($forecast['days'][1])->toMatchArray(['day' => 10, 'upcoming' => 50.0, 'total' => 150.0])
|
||||
->and($forecast['days'][2])->toMatchArray(['day' => 20, 'upcoming' => 50.0, 'total' => 150.0])
|
||||
->and($forecast['days'][3])->toMatchArray(['day' => 25, 'upcoming' => 60.0, 'total' => 160.0]);
|
||||
->and($forecast['days'][3])->toMatchArray(['day' => 25, 'upcoming' => 60.0, 'total' => 160.0])
|
||||
->and($forecast['days'][4])->toMatchArray(['day' => 31, 'upcoming' => 60.0, 'total' => 160.0, 'is_month_end' => true]);
|
||||
});
|
||||
|
||||
it('nimmt Abos nach dem 25. in die Monatsende-Kachel auf (A1: freie Liefertage)', function (): void {
|
||||
Carbon::setTestNow(Carbon::parse('2026-07-02'));
|
||||
|
||||
$root = pflMakeUser();
|
||||
$anna = pflMakeUser($root->id);
|
||||
pflSeedContext($root, [$anna->id]);
|
||||
|
||||
pflMakeAbo($anna->id, 'ot', '2026-07-25', 20);
|
||||
pflMakeAbo($anna->id, 'ot', '2026-07-28', 30);
|
||||
pflMakeAbo($anna->id, 'ot', '2026-07-31', 40);
|
||||
|
||||
$forecast = app(PaylineForecastService::class)->forecastDays($root, 0.0);
|
||||
|
||||
expect($forecast['days'][3])->toMatchArray(['day' => 25, 'upcoming' => 20.0])
|
||||
->and($forecast['days'][4])->toMatchArray(['day' => 31, 'date' => '31.07.2026', 'upcoming' => 90.0, 'total' => 90.0, 'is_month_end' => true]);
|
||||
});
|
||||
|
||||
it('markiert vergangene Termine und zaehlt nur noch offene Ausfuehrungen', function (): void {
|
||||
|
|
@ -497,7 +515,7 @@ it('prognostiziert die Liefertage ausschliesslich aus den eigenen Kundenabos', f
|
|||
$forecast = app(PaylineForecastService::class)->personalCustomerAboForecast($root);
|
||||
|
||||
expect($forecast['base'])->toBe(0.0)
|
||||
->and($forecast['days'])->toHaveCount(4)
|
||||
->and($forecast['days'])->toHaveCount(5)
|
||||
->and($forecast['days'][0])->toMatchArray(['day' => 5, 'upcoming' => 0.0, 'total' => 0.0])
|
||||
->and($forecast['days'][1])->toMatchArray(['day' => 10, 'upcoming' => 30.0, 'total' => 30.0])
|
||||
->and($forecast['days'][2])->toMatchArray(['day' => 20, 'upcoming' => 30.0, 'total' => 30.0])
|
||||
|
|
@ -557,7 +575,8 @@ it('haengt Buchungen nach dem letzten Liefertag an die letzte Kachel', function
|
|||
$forecast = app(PaylineForecastService::class)->personalCustomerAboForecast($berater);
|
||||
|
||||
expect($forecast['days'][0]['total'])->toBe(10.0)
|
||||
->and($forecast['days'][3]['total'])->toBe(35.0)
|
||||
->and($forecast['days'][3]['total'])->toBe(10.0)
|
||||
->and($forecast['days'][4]['total'])->toBe(35.0)
|
||||
->and($forecast['base'])->toBe(35.0);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue