mivita/tests/Unit/Services/PersonalStatisticsServiceTest.php
Kevin 7a620d66ae Backoffice-Statistik: Punkte-Fix (kumulative Snapshots) & Meine Statistik
Punkte/Umsatz korrekt aus per-Zeile points/total_net je status berechnen
statt SUM der kumulativen month_*-Snapshots (zaehlte massiv mehrfach).
Zentralisiert in SalesVolumePoints, genutzt von Dashboard- & Drilldown-Service.

Detail-Tabelle neu gegliedert: Eigenpunkte | Shop | Abo | Sonstige |
Kundenpunkte (=Summe) | Gesamt, mit gruener Hervorhebung der Basis-/Summenspalten.

Neue VIP-Seite "Meine Statistik" (Untermenue neben Team-Statistik):
KPI-Karten + nachvollziehbare Detailtabellen, gruppiert nach eigenen
Bestellungen, Shop-Bestellungen und Abos (PersonalStatisticsService).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:31:14 +00:00

122 lines
5.1 KiB
PHP

<?php
use App\Models\UserSalesVolume;
use App\Services\Backoffice\PersonalStatisticsService;
use App\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
uses(TestCase::class);
beforeEach(function () {
Schema::dropIfExists('user_sales_volumes');
Schema::dropIfExists('shopping_orders');
Schema::dropIfExists('shopping_users');
Schema::dropIfExists('users');
Schema::create('users', function ($table) {
$table->increments('id');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
$table->softDeletes();
});
Schema::create('shopping_users', function ($table) {
$table->increments('id');
$table->string('billing_firstname')->nullable();
$table->string('billing_lastname')->nullable();
$table->string('billing_email')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('shopping_orders', function ($table) {
$table->increments('id');
$table->unsignedInteger('shopping_user_id')->nullable();
$table->boolean('is_abo')->default(false);
$table->timestamps();
$table->softDeletes();
});
Schema::create('user_sales_volumes', function ($table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('shopping_order_id')->nullable();
$table->unsignedTinyInteger('month')->nullable();
$table->unsignedSmallInteger('year')->nullable();
$table->decimal('points', 13, 2)->nullable();
$table->decimal('total_net', 13, 2)->nullable();
$table->unsignedTinyInteger('status')->nullable();
$table->unsignedTinyInteger('status_turnover')->nullable();
$table->string('date')->nullable();
$table->string('info')->nullable();
$table->string('message')->nullable();
$table->timestamps();
});
});
it('gruppiert die Monatspunkte nach Eigen, Shop, Abo und Sonstige', function () {
$user = User::forceCreate(['email' => 'me@test.test', 'password' => 'secret']);
$customer = DB::table('shopping_users')->insertGetId([
'billing_firstname' => 'Erika', 'billing_lastname' => 'Musterfrau', 'billing_email' => 'erika@test.test',
'created_at' => now(), 'updated_at' => now(),
]);
$shopOrderId = DB::table('shopping_orders')->insertGetId([
'shopping_user_id' => $customer, 'is_abo' => false, 'created_at' => now(), 'updated_at' => now(),
]);
$aboOrderId = DB::table('shopping_orders')->insertGetId([
'shopping_user_id' => $customer, 'is_abo' => true, 'created_at' => now(), 'updated_at' => now(),
]);
$ownOrderId = DB::table('shopping_orders')->insertGetId([
'is_abo' => false, 'created_at' => now(), 'updated_at' => now(),
]);
$make = function (array $attributes) use ($user): void {
UserSalesVolume::forceCreate(array_merge([
'user_id' => $user->id, 'month' => 6, 'year' => 2026, 'date' => '01.06.2026',
], $attributes));
};
// Eigene Bestellungen (status 1): 150 + 250 = 400
$make(['shopping_order_id' => $ownOrderId, 'status' => 1, 'points' => 150, 'total_net' => 40]);
$make(['shopping_order_id' => $ownOrderId, 'status' => 1, 'points' => 250, 'total_net' => 60]);
// Shop-Bestellung (status 2, keine Abo-Order): 300
$make(['shopping_order_id' => $shopOrderId, 'status' => 2, 'points' => 300, 'total_net' => 120]);
// Abo (status 2, Abo-Order): 500
$make(['shopping_order_id' => $aboOrderId, 'status' => 2, 'points' => 500, 'total_net' => 200]);
// Sonstige (status 5, ohne Order): 50
$make(['shopping_order_id' => null, 'status' => 5, 'points' => 50, 'total_net' => 10]);
$result = (new PersonalStatisticsService)->monthly($user, 6, 2026);
expect($result['totals']['own_points'])->toBe(400.0);
expect($result['totals']['shop_points'])->toBe(300.0);
expect($result['totals']['abo_points'])->toBe(500.0);
expect($result['totals']['other_points'])->toBe(50.0);
expect($result['totals']['total_points'])->toBe(1250.0);
expect($result['totals']['turnover_net'])->toBe(430.0);
expect($result['groups']['own']['rows'])->toHaveCount(2);
expect($result['groups']['shop']['rows'])->toHaveCount(1);
expect($result['groups']['abo']['rows'])->toHaveCount(1);
expect($result['groups']['other']['rows'])->toHaveCount(1);
expect($result['groups']['own']['rows'][0]['order_type'])->toBe('own');
expect($result['groups']['shop']['rows'][0]['order_type'])->toBe('shop');
expect($result['groups']['shop']['rows'][0]['customer_name'])->toBe('Erika Musterfrau');
expect($result['groups']['abo']['rows'][0]['customer_email'])->toBe('erika@test.test');
});
it('liefert leere Gruppen ohne Umsaetze', function () {
$user = User::forceCreate(['email' => 'empty@test.test', 'password' => 'secret']);
$result = (new PersonalStatisticsService)->monthly($user, 6, 2026);
expect($result['totals']['total_points'])->toBe(0.0);
expect($result['groups']['own']['rows'])->toBe([]);
expect($result['groups']['shop']['rows'])->toBe([]);
});