has('admin_newsletter_lists_load')) { Route::group([], base_path('routes/domains/crm.php')); app('router')->getRoutes()->refreshNameLookups(); } }); function newsletterSavedAdmin(): User { return User::forceCreate([ 'email' => 'admin-saved-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'admin' => 2, 'lang' => 'de', 'active' => 1, 'blocked' => 0, ]); } /** * @param array $params */ function newsletterStoreRequest(User $user, array $params = []): StoreNewsletterListRequest { $request = StoreNewsletterListRequest::create('/admin/newsletter-lists/save', 'POST', $params); $request->setUserResolver(fn () => $user); $request->setContainer(app())->setRedirector(app(Redirector::class)); $request->validateResolved(); app()->instance('request', $request); return $request; } /** * @param array $filters */ function newsletterSavedList(User $user, array $filters = [], string $name = 'Testliste'): NewsletterList { $list = new NewsletterList(['name' => $name, 'created_by' => $user->id]); $list->applyFilter(NewsletterListFilter::fromArray($filters)); $list->save(); return $list; } function newsletterSavedCustomer(string $email): ShoppingUser { return ShoppingUser::forceCreate([ 'auth_user_id' => 0, 'billing_email' => $email, 'billing_firstname' => 'Erika', 'billing_lastname' => 'Muster', 'billing_salutation' => 'ms', 'billing_country_id' => 1, 'shipping_country_id' => 1, 'language' => 'de', 'subscribed' => 1, 'has_buyed' => 1, 'faker_mail' => 0, ]); } it('speichert die aktuelle Filterkombination unter einem Namen', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); $request = newsletterStoreRequest($admin, ['name' => ' Newsletter DE Kunden ', 'source' => 'both', 'language' => 'de', 'optin' => 'subscribed', 'level_ids' => ['2']]); $response = (new NewsletterListController(app(NewsletterListService::class)))->store($request); $list = NewsletterList::first(); expect($list->name)->toBe('Newsletter DE Kunden') ->and($list->source)->toBe('both') ->and($list->created_by)->toBe($admin->id) ->and($list->filters['optin'])->toBe('subscribed') ->and($list->filters['level_ids'])->toBe([2]) ->and($list->toFilter()->optin)->toBe('subscribed') ->and($response->getTargetUrl())->toContain('/admin/newsletter-lists/load/'.$list->id); }); it('verlangt beim Speichern einen Namen', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); newsletterStoreRequest($admin, ['source' => 'customers']); })->throws(ValidationException::class); it('aktualisiert Name und Filter einer gespeicherten Liste', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); $list = newsletterSavedList($admin, ['source' => 'customers']); $request = newsletterStoreRequest($admin, ['name' => 'Berater ES', 'source' => 'consultants', 'language' => 'es', 'consultant_status' => 'all']); (new NewsletterListController(app(NewsletterListService::class)))->update($request, $list); $list->refresh(); expect($list->name)->toBe('Berater ES') ->and($list->source)->toBe('consultants') ->and($list->filters['language'])->toBe('es') ->and($list->filters['consultant_status'])->toBe('all'); }); it('lädt eine gespeicherte Liste in die Filter-Seite und merkt den Filter', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); $list = newsletterSavedList($admin, ['source' => 'consultants', 'language' => '']); $view = (new NewsletterListController(app(NewsletterListService::class)))->load($list); expect($view->getName())->toBe('admin.newsletter.index') ->and($view->getData()['list']->id)->toBe($list->id) ->and($view->getData()['filter']['source'])->toBe('consultants') ->and($view->getData()['filter']['language'])->toBe('') ->and(get_user_attr('newsletter_list_filter')['source'])->toBe('consultants'); }); it('protokolliert Exporte und aktualisiert die gespeicherte Liste', function () { Excel::fake(); $admin = newsletterSavedAdmin(); $this->actingAs($admin); newsletterSavedCustomer('eins@example.com'); newsletterSavedCustomer('zwei@example.com'); $list = newsletterSavedList($admin, ['source' => 'customers', 'language' => 'de']); $request = NewsletterListFilterRequest::create('/x', 'GET', []); $request->setUserResolver(fn () => $admin); (new NewsletterListController(app(NewsletterListService::class)))->exportSaved($request, $list, 'xlsx'); $export = NewsletterListExport::first(); $list->refresh(); expect($export->newsletter_list_id)->toBe($list->id) ->and($export->user_id)->toBe($admin->id) ->and($export->format)->toBe('xlsx') ->and($export->row_count)->toBe(2) ->and($export->filename)->toBe('newsletter_customers_de_'.now()->format('Y-m-d').'.xlsx') ->and($export->filters['source'])->toBe('customers') ->and($export->stats['total'])->toBe(2) ->and($list->last_row_count)->toBe(2) ->and($list->last_exported_at)->not->toBeNull(); Excel::assertDownloaded($export->filename); }); it('protokolliert Ad-hoc-Exporte ohne Liste und ordnet sie bei list_id der Liste zu', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); newsletterSavedCustomer('csv@example.com'); $list = newsletterSavedList($admin); $controller = new NewsletterListController(app(NewsletterListService::class)); $adhoc = NewsletterListFilterRequest::create('/x', 'GET', ['source' => 'customers']); $adhoc->setUserResolver(fn () => $admin); $adhoc->setContainer(app())->setRedirector(app(Redirector::class)); $adhoc->validateResolved(); $controller->export($adhoc, 'csv'); $linked = NewsletterListFilterRequest::create('/x', 'GET', ['source' => 'customers', 'list_id' => (string) $list->id]); $linked->setUserResolver(fn () => $admin); $linked->setContainer(app())->setRedirector(app(Redirector::class)); $linked->validateResolved(); $controller->export($linked, 'csv'); $exports = NewsletterListExport::orderBy('id')->get(); expect($exports)->toHaveCount(2) ->and($exports[0]->newsletter_list_id)->toBeNull() ->and($exports[0]->format)->toBe('csv') ->and($exports[1]->newsletter_list_id)->toBe($list->id) ->and($list->fresh()->last_row_count)->toBe(1); }); it('löscht eine Liste und behält das Protokoll', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); $list = newsletterSavedList($admin); NewsletterListExport::create([ 'newsletter_list_id' => $list->id, 'user_id' => $admin->id, 'format' => 'csv', 'row_count' => 3, 'filename' => 'x.csv', 'filters' => $list->filters, ]); $response = (new NewsletterListController(app(NewsletterListService::class)))->destroy($list); expect(NewsletterList::count())->toBe(0) ->and(NewsletterListExport::first()->newsletter_list_id)->toBeNull() ->and($response->getTargetUrl())->toContain('/admin/newsletter-lists/saved'); }); it('liefert gespeicherte Listen und Protokoll als Datatable-JSON', function () { $admin = newsletterSavedAdmin(); $this->actingAs($admin); $list = newsletterSavedList($admin, ['source' => 'customers', 'optin' => 'subscribed', 'purchase' => 'months_6'], 'Kunden Opt-in'); NewsletterListExport::create([ 'newsletter_list_id' => $list->id, 'user_id' => $admin->id, 'format' => 'xlsx', 'row_count' => 7, 'filename' => 'newsletter_customers_de.xlsx', 'filters' => $list->filters, 'stats' => ['total' => 7], ]); $request = Request::create('/x', 'GET', ['draw' => 1, 'start' => 0, 'length' => 50]); $request->setUserResolver(fn () => $admin); app()->instance('request', $request); $controller = new NewsletterListController(app(NewsletterListService::class)); $saved = $controller->savedDatatable()->getData(true); $exports = $controller->exportsDatatable()->getData(true); expect($saved['recordsTotal'])->toBe(1) ->and($saved['data'][0]['name'])->toContain('Kunden Opt-in') ->and($saved['data'][0]['source'])->toBe('Kunden') ->and($saved['data'][0]['summary'])->toContain('nur mit Opt-in') ->and($saved['data'][0]['summary'])->toContain('6 Monaten') ->and($saved['data'][0]['actions'])->toContain('/admin/newsletter-lists/saved/'.$list->id.'/export/xlsx') ->and($exports['recordsTotal'])->toBe(1) ->and($exports['data'][0]['format'])->toBe('XLSX') ->and($exports['data'][0]['row_count'])->toBe(7) ->and($exports['data'][0]['list'])->toContain('Kunden Opt-in'); }); it('beschreibt einen Filter lesbar', function () { $service = app(NewsletterListService::class); $text = $service->describe(NewsletterListFilter::fromArray([ 'source' => 'both', 'language' => 'de', 'optin' => 'unsubscribed', 'member_id' => 'none', 'created_from' => '2026-01-01', 'company' => 'with', 'consultant_status' => 'all', 'include_admins' => 1, ])); expect($text)->toBe('Kunden und Berater · Sprache: DE · Newsletter-Opt-in: nur ohne Opt-in · Zugeordneter Berater / Sponsor: ohne Zuordnung · Angelegt von: 01.01.2026 – … · Firma: nur mit Firma · Berater-Status: alle · Admin-Accounts einbeziehen'); });