mivita/tests/Feature/BackofficeStatisticsAccessTest.php
2026-05-18 17:23:28 +02:00

342 lines
13 KiB
PHP

<?php
use App\Http\Controllers\User\BackofficeStatisticsController;
use App\Models\ShoppingOrder;
use App\Services\Backoffice\BackofficeDashboardService;
use App\Services\Backoffice\BackofficeDrilldownService;
use App\User;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Tests\TestCase;
uses(TestCase::class);
function makeBackofficeStatisticsUser(int $admin): User
{
return (new User)->forceFill([
'admin' => $admin,
'lang' => 'de',
'active' => 1,
'blocked' => 0,
'wizard' => 100,
]);
}
function makeBackofficeStatisticsRequest(User $user): Request
{
$request = Request::create('/user/backoffice/statistics', 'GET');
$request->setUserResolver(fn () => $user);
return $request;
}
function makeBackofficeStatisticsController(?BackofficeDashboardService $dashboardService = null, ?BackofficeDrilldownService $drilldownService = null): BackofficeStatisticsController
{
$dashboardService ??= Mockery::mock(BackofficeDashboardService::class);
$drilldownService ??= Mockery::mock(BackofficeDrilldownService::class);
return new BackofficeStatisticsController($dashboardService, $drilldownService);
}
function backofficeStatisticsStreamedContent(StreamedResponse $response): string
{
ob_start();
$response->sendContent();
return (string) ob_get_clean();
}
it('zeigt die Backoffice-Statistik fuer VIP-User', function () {
$vip = makeBackofficeStatisticsUser(1);
$dashboardService = Mockery::mock(BackofficeDashboardService::class);
$dashboardService
->shouldReceive('overview')
->once()
->andReturn([
'month' => now()->month,
'year' => now()->year,
'metric_labels' => [],
'lines' => [],
'totals' => [],
'_meta' => [
'source_label' => 'Live',
'calculated_at' => null,
],
]);
$controller = makeBackofficeStatisticsController($dashboardService);
$response = $controller->index(makeBackofficeStatisticsRequest($vip));
expect($response->getName())->toBe('user.backoffice.statistics.index');
expect($response->getData())->toHaveKeys(['selectedMonth', 'selectedYear', 'statistics', 'performance']);
expect($response->getData()['performance']['source_label'])->toBe('Live');
});
it('behaelt den zuletzt gewaehlten Statistik-Zeitraum in der Session', function () {
$vip = makeBackofficeStatisticsUser(1);
$dashboardService = Mockery::mock(BackofficeDashboardService::class);
$dashboardService
->shouldReceive('overview')
->twice()
->andReturn([
'month' => 4,
'year' => 2026,
'metric_labels' => [],
'lines' => [],
'totals' => [],
'_meta' => [
'source_label' => 'Snapshot',
'calculated_at' => '17.05.2026 04:45',
],
]);
$controller = makeBackofficeStatisticsController($dashboardService);
$firstRequest = makeBackofficeStatisticsRequest($vip);
$firstRequest->query->set('month', 4);
$firstRequest->query->set('year', 2026);
$controller->index($firstRequest);
$response = $controller->index(makeBackofficeStatisticsRequest($vip));
expect($response->getData()['selectedMonth'])->toBe(4);
expect($response->getData()['selectedYear'])->toBe(2026);
expect($response->getData()['performance']['source_label'])->toBe('Snapshot');
});
it('blockiert die Backoffice-Statistik fuer normale aktive User', function () {
$user = makeBackofficeStatisticsUser(0);
$controller = makeBackofficeStatisticsController();
expect(fn () => $controller->index(makeBackofficeStatisticsRequest($user)))
->toThrow(NotFoundHttpException::class);
});
it('erstellt einen CSV-Export fuer die Statistik-Uebersicht', function () {
$vip = makeBackofficeStatisticsUser(1);
$dashboardService = Mockery::mock(BackofficeDashboardService::class);
$dashboardService
->shouldReceive('overview')
->once()
->andReturn([
'month' => 5,
'year' => 2026,
'metric_labels' => [],
'lines' => [
[
'label' => 'Linie 1',
'consultants' => 2,
'new_partners' => 1,
'team_partner_abos' => 1,
'team_partner_abos_new' => 1,
'team_customer_abos' => 1,
'team_customer_abos_new' => 1,
'own_points' => 400,
'external_points' => 700,
'customer_abo_points' => 700,
'customer_single_order_points' => 0,
'customer_other_points' => 0,
'total_points' => 1100,
'shop_1000' => 1,
'turnover_net' => 300,
],
],
'totals' => [
'label' => 'Summe',
'consultants' => 2,
'new_partners' => 1,
'team_partner_abos' => 1,
'team_partner_abos_new' => 1,
'team_customer_abos' => 1,
'team_customer_abos_new' => 1,
'own_points' => 400,
'external_points' => 700,
'customer_abo_points' => 700,
'customer_single_order_points' => 0,
'customer_other_points' => 0,
'total_points' => 1100,
'shop_1000' => 1,
'turnover_net' => 300,
],
]);
$controller = makeBackofficeStatisticsController($dashboardService);
$request = makeBackofficeStatisticsRequest($vip);
$request->query->set('month', 5);
$request->query->set('year', 2026);
$response = $controller->overviewExport($request);
$content = backofficeStatisticsStreamedContent($response);
expect($response)->toBeInstanceOf(StreamedResponse::class);
expect($response->headers->get('content-disposition'))->toContain('backoffice-statistik-uebersicht-05-2026.csv');
expect($content)->toContain('Linie;Berater;Neupartner;Teamabos');
expect($content)->toContain('Neue Teamabos');
expect($content)->toContain('"Linie 1";2;1;1;1;1;1;400;700;700;0;0;1100;1;300');
expect($content)->toContain('Summe;2;1;1;1;1;1;400;700;700;0;0;1100;1;300');
});
it('erstellt einen CSV-Export fuer Detaildaten mit Karriere-Level und Summenzeile', function () {
$vip = makeBackofficeStatisticsUser(1);
$drilldownService = Mockery::mock(BackofficeDrilldownService::class);
$drilldownService
->shouldReceive('details')
->once()
->with($vip, 0, 'shop_1000', 5, 2026)
->andReturn([
'metric' => 'shop_1000',
'metric_label' => '1000 Punkte Shop',
'line' => 0,
'line_label' => 'Alle Linien',
'month' => 5,
'year' => 2026,
'rows' => [
[
'name' => 'Max Mustermann',
'email' => 'max@example.test',
'career_level' => 'Partner',
'own_points' => 400,
'external_points' => 700,
'customer_abo_points' => 700,
'customer_single_order_points' => 0,
'customer_other_points' => 0,
'total_points' => 1100,
],
],
'summary' => [
'count' => 1,
'points' => 0,
'own_points' => 400,
'external_points' => 700,
'customer_abo_points' => 700,
'customer_single_order_points' => 0,
'customer_other_points' => 0,
'total_points' => 1100,
'deliveries' => 0,
],
]);
$controller = makeBackofficeStatisticsController(null, $drilldownService);
$request = makeBackofficeStatisticsRequest($vip);
$request->query->set('line', 0);
$request->query->set('metric', 'shop_1000');
$request->query->set('month', 5);
$request->query->set('year', 2026);
$response = $controller->export($request);
$content = backofficeStatisticsStreamedContent($response);
expect($response)->toBeInstanceOf(StreamedResponse::class);
expect($response->headers->get('content-disposition'))->toContain('backoffice-statistik-shop_1000-linie-alle-05-2026.csv');
expect($content)->toContain('Name;E-Mail;Karriere-Level;Eigenpunkte;"Externe Punkte"');
expect($content)->toContain('"Max Mustermann";max@example.test;Partner;400;700;700;0;0;1100');
expect($content)->toContain('Summe;"1 Eintraege";;400;700;700;0;0;1100');
});
it('nutzt den gespeicherten Zeitraum fuer Detailansicht und Export', function () {
$vip = makeBackofficeStatisticsUser(1);
$dashboardService = Mockery::mock(BackofficeDashboardService::class);
$drilldownService = Mockery::mock(BackofficeDrilldownService::class);
$dashboardService
->shouldReceive('overview')
->once()
->andReturn([
'month' => 4,
'year' => 2026,
'metric_labels' => [],
'lines' => [],
'totals' => [],
]);
$drilldownService
->shouldReceive('details')
->once()
->with($vip, 1, 'consultants', 4, 2026)
->andReturn([
'metric' => 'consultants',
'metric_label' => 'Berater',
'line' => 1,
'line_label' => 'Linie 1',
'month' => 4,
'year' => 2026,
'rows' => [],
'summary' => ['count' => 0],
]);
$drilldownService
->shouldReceive('details')
->once()
->with($vip, 1, 'consultants', 4, 2026)
->andReturn([
'metric' => 'consultants',
'metric_label' => 'Berater',
'line' => 1,
'line_label' => 'Linie 1',
'month' => 4,
'year' => 2026,
'rows' => [],
'summary' => ['count' => 0],
]);
$controller = makeBackofficeStatisticsController($dashboardService, $drilldownService);
$indexRequest = makeBackofficeStatisticsRequest($vip);
$indexRequest->query->set('month', 4);
$indexRequest->query->set('year', 2026);
$controller->index($indexRequest);
$detailsRequest = makeBackofficeStatisticsRequest($vip);
$detailsRequest->query->set('line', 1);
$detailsRequest->query->set('metric', 'consultants');
$detailsResponse = $controller->details($detailsRequest);
$exportRequest = makeBackofficeStatisticsRequest($vip);
$exportRequest->query->set('line', 1);
$exportRequest->query->set('metric', 'consultants');
$exportResponse = $controller->export($exportRequest);
expect($detailsResponse->getData()['selectedMonth'])->toBe(4);
expect($detailsResponse->getData()['selectedYear'])->toBe(2026);
expect($exportResponse->headers->get('content-disposition'))->toContain('backoffice-statistik-consultants-linie-1-04-2026.csv');
});
it('rendert eine Suche in der Detailtabelle', function () {
$html = file_get_contents(resource_path('views/user/backoffice/statistics/details.blade.php'));
expect($html)->toContain('backoffice-statistics-detail-search');
expect($html)->toContain('backoffice-statistics-detail-table');
expect($html)->toContain('data-sortable="true"');
expect($html)->toContain('getSortValue');
expect($html)->toContain('user_backoffice_statistics_export');
expect($html)->toContain('Datenschutz-Hinweis');
expect($html)->toContain('Karriere-Level');
expect($html)->not->toContain('Qualifikation');
$indexHtml = file_get_contents(resource_path('views/user/backoffice/statistics/index.blade.php'));
expect($indexHtml)->toContain('user_backoffice_statistics_overview_export');
expect($indexHtml)->toContain('Datenquelle:');
expect($indexHtml)->toContain('Berechnet in');
});
it('erfasst die Herkunft bei Kundenbestellungen im Shop-Checkout', function () {
$checkoutView = file_get_contents(resource_path('views/web/templates/checkout.blade.php'));
$checkoutController = file_get_contents(app_path('Http/Controllers/Web/CheckoutController.php'));
$checkoutRepository = file_get_contents(app_path('Repositories/CheckoutRepository.php'));
$orderDetail = file_get_contents(resource_path('views/admin/sales/_detail.blade.php'));
expect($checkoutView)->toContain('customer_order_source');
expect($checkoutView)->toContain('Wie bist du auf uns aufmerksam geworden?');
expect($checkoutController)->toContain("Request::get('is_from') === 'shopping'");
expect($checkoutController)->toContain('customer_order_source');
expect($checkoutRepository)->toContain('$shopping_user->is_from === \'shopping\'');
expect($checkoutRepository)->toContain('customer_order_source_comment');
expect($orderDetail)->toContain('getCustomerOrderSourceLabel');
$shoppingOrder = (new ShoppingOrder)->forceFill([
'customer_order_source' => 'social_media',
]);
expect($shoppingOrder->getCustomerOrderSourceLabel())->toBe('Social Media');
});