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([]); });