$name, 'pos' => 1, 'active' => 1, 'default' => 0, ]); } function afdMakeUser(?int $sponsorId, ?string $paymentAccount, ?int $levelId = null, array $accountAttributes = []): User { $account = UserAccount::create(array_merge([ 'first_name' => 'Vorname', 'last_name' => 'Nachname', 'phone' => '', 'mobil' => '', ], $accountAttributes)); return User::forceCreate([ 'email' => 'afd-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'account_id' => $account->id, 'm_level' => $levelId ?? 1, 'm_sponsor' => $sponsorId, 'payment_account' => $paymentAccount, ]); } it('gruppiert Teampartner nach Jahresgebühr-Dringlichkeit mit korrekter Linie und Sortierung', function (): void { $reference = Carbon::parse('2026-08-15 12:00:00'); $level = afdMakeLevel('Berater'); $root = afdMakeUser(null, '2027-05-01', $level->id); // Linie 1 $overdueLater = afdMakeUser($root->id, '2026-08-10', $level->id, [ 'first_name' => 'Anna', 'last_name' => 'Alt', 'phone' => '030 123', 'mobil' => '0171 111', ]); $overdueEarlier = afdMakeUser($root->id, '2026-06-20', $level->id, [ 'first_name' => 'Bernd', 'last_name' => 'Blum', ]); $dueOneMonth = afdMakeUser($root->id, '2026-09-10', $level->id, [ 'first_name' => 'Clara', 'last_name' => 'Cato', ]); $outsideWindow = afdMakeUser($root->id, '2026-12-15', $level->id); // +4 Monate: nicht anzeigen $withoutPaymentAccount = afdMakeUser($root->id, null, $level->id); // ohne payment_account: weglassen // Linie 2 (unter Anna) $dueTwoMonths = afdMakeUser($overdueLater->id, '2026-10-10', $level->id, [ 'first_name' => 'Doris', 'last_name' => 'Dahl', ]); $dueThreeMonths = afdMakeUser($overdueLater->id, '2026-11-10', $level->id, [ 'first_name' => 'Emil', 'last_name' => 'Eck', ]); $groups = app(AnnualFeeDueService::class)->dueGroups($root->id, $reference); // Gruppen-Reihenfolge: überfällig zuerst expect(array_keys($groups))->toBe([ AnnualFeeDueService::GROUP_OVERDUE, AnnualFeeDueService::GROUP_DUE_1_MONTH, AnnualFeeDueService::GROUP_DUE_2_MONTHS, AnnualFeeDueService::GROUP_DUE_3_MONTHS, ]); // Überfällig: beide User, sortiert nach Fälligkeitsdatum aufsteigend (ältestes zuerst) $overdue = $groups[AnnualFeeDueService::GROUP_OVERDUE]; expect($overdue)->toHaveCount(2) ->and($overdue[0]['user_id'])->toBe($overdueEarlier->id) ->and($overdue[1]['user_id'])->toBe($overdueLater->id) ->and($overdue[1]['first_name'])->toBe('Anna') ->and($overdue[1]['last_name'])->toBe('Alt') ->and($overdue[1]['email'])->toBe($overdueLater->email) ->and($overdue[1]['phone'])->toBe('030 123') ->and($overdue[1]['mobil'])->toBe('0171 111') ->and($overdue[1]['line'])->toBe(1) ->and($overdue[1]['level_name'])->toBe('Berater') ->and($overdue[1]['due_date']->format('Y-m-d'))->toBe('2026-08-10'); // ≤ 1 Monat $dueOne = $groups[AnnualFeeDueService::GROUP_DUE_1_MONTH]; expect($dueOne)->toHaveCount(1) ->and($dueOne[0]['user_id'])->toBe($dueOneMonth->id) ->and($dueOne[0]['line'])->toBe(1); // ≤ 2 Monate (Linie 2) $dueTwo = $groups[AnnualFeeDueService::GROUP_DUE_2_MONTHS]; expect($dueTwo)->toHaveCount(1) ->and($dueTwo[0]['user_id'])->toBe($dueTwoMonths->id) ->and($dueTwo[0]['line'])->toBe(2); // ≤ 3 Monate (Linie 2) $dueThree = $groups[AnnualFeeDueService::GROUP_DUE_3_MONTHS]; expect($dueThree)->toHaveCount(1) ->and($dueThree[0]['user_id'])->toBe($dueThreeMonths->id) ->and($dueThree[0]['line'])->toBe(2); // +4 Monate und ohne payment_account tauchen nirgends auf $allUserIds = collect($groups)->flatten(1)->pluck('user_id'); expect($allUserIds)->not->toContain($outsideWindow->id) ->and($allUserIds)->not->toContain($withoutPaymentAccount->id) ->and($allUserIds)->not->toContain($root->id); }); it('liefert leere Gruppen für ein Team ohne fällige Jahresgebühren', function (): void { $reference = Carbon::parse('2026-08-15 12:00:00'); $root = afdMakeUser(null, '2027-05-01'); afdMakeUser($root->id, '2027-04-01'); $groups = app(AnnualFeeDueService::class)->dueGroups($root->id, $reference); expect(collect($groups)->flatten(1))->toBeEmpty() ->and(array_keys($groups))->toBe(AnnualFeeDueService::GROUP_ORDER); }); it('rendert die Jahresgebühr-Seite mit Gruppen und Hinweistext', function (): void { $reference = Carbon::parse('2026-08-15 12:00:00'); $level = afdMakeLevel('Berater'); $root = afdMakeUser(null, '2027-05-01', $level->id); afdMakeUser($root->id, '2026-08-10', $level->id, [ 'first_name' => 'Anna', 'last_name' => 'Alt', 'phone' => '030 123', ]); $groups = app(AnnualFeeDueService::class)->dueGroups($root->id, $reference); $html = view('user.team._annual_fee_groups', [ 'groups' => $groups, 'totalCount' => array_sum(array_map('count', $groups)), ])->render(); expect($html)->toContain(__('team.annual_fee_title')) ->toContain(__('team.annual_fee_hint')) ->toContain(__('team.annual_fee_group_overdue')) ->toContain('Anna') ->toContain('Alt') ->toContain('030 123'); });