previousHttpHost = $_SERVER['HTTP_HOST'] ?? null; Mail::fake(); $_SERVER['HTTP_HOST'] = config('domains.domains.crm.host'); $this->refreshApplication(); $this->artisan('migrate'); }); afterEach(function () { // Host wieder zurücksetzen, sonst booten nachfolgende Tests mit den CRM-Routen. if ($this->previousHttpHost === null) { unset($_SERVER['HTTP_HOST']); } else { $_SERVER['HTTP_HOST'] = $this->previousHttpHost; } }); function deleteTestUser(array $attributes = []): User { return User::forceCreate(array_merge([ 'email' => 'user-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'active' => 1, 'wizard' => 100, 'payment_account' => now()->addYear(), ], $attributes)); } function deleteTestCustomer(User $member, string $email, array $attributes = []): ShoppingUser { $country = Country::first() ?? Country::create([ 'code' => 'DE', 'phone' => '49', 'en' => 'Germany', 'de' => 'Deutschland', 'es' => 'Alemania', 'fr' => 'Allemagne', 'it' => 'Germania', 'ru' => 'Германия', ]); return ShoppingUser::create(array_merge([ 'auth_user_id' => 0, 'member_id' => $member->id, 'number' => random_int(1000, 99999), 'billing_firstname' => 'Erika', 'billing_lastname' => 'Muster', 'billing_address' => 'Musterweg 1', 'billing_zipcode' => '12345', 'billing_city' => 'Musterstadt', 'billing_phone' => '0123', 'billing_email' => $email, 'billing_country_id' => $country->id, 'shipping_country_id' => $country->id, ], $attributes)); } function deleteTestOrder(ShoppingUser $customer): ShoppingOrder { $shop = UserShop::first() ?? UserShop::create(['user_id' => $customer->member_id, 'name' => 'Shop', 'slug' => 'shop-'.uniqid()]); return ShoppingOrder::create([ 'shopping_user_id' => $customer->id, 'auth_user_id' => 0, 'member_id' => $customer->member_id, 'user_shop_id' => $shop->id, 'country_id' => $customer->billing_country_id, 'total' => 10, 'txaction' => 'appointed', 'mode' => 'test', ]); } it('fasst alle Einträge eines Kunden über Kundennummer und E-Mail zusammen', function () { $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); $sameNumber = deleteTestCustomer($member, 'anders@example.com', ['number' => 61009]); $sameEmailOtherNumber = deleteTestCustomer($member, 'Erika@Example.com', ['number' => 61010]); $consultantRow = deleteTestCustomer($member, 'erika@example.com', ['number' => 99, 'auth_user_id' => $member->id]); $other = deleteTestCustomer($member, 'jemand@example.com', ['number' => 61011]); $ids = app(GdprDeletionService::class)->getCustomerRows($first)->pluck('id')->all(); expect($ids)->toBe([$first->id, $sameNumber->id, $sameEmailOtherNumber->id]) ->and($ids)->not->toContain($consultantRow->id) ->and($ids)->not->toContain($other->id); }); it('gruppiert nicht über Platzhalter-Adressen', function () { $member = deleteTestUser(); $faker = deleteTestCustomer($member, 'abc-faker@mivita.care', ['number' => 1, 'faker_mail' => true]); $otherFaker = deleteTestCustomer($member, 'abc-faker@mivita.care', ['number' => 2, 'faker_mail' => true]); $service = app(GdprDeletionService::class); expect($service->groupingEmail($faker))->toBeNull() ->and($service->getCustomerRows($faker)->pluck('id')->all())->toBe([$faker->id]) ->and($service->getCustomerRows($otherFaker)->pluck('id')->all())->toBe([$otherFaker->id]); }); it('löscht einen Kunden ohne Bestellungen komplett, inklusive Mehrfach-Anlagen, Portal-Login und Abo-Daten', function () { $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); $duplicate = deleteTestCustomer($member, 'erika@example.com', ['number' => 61010]); $abo = UserAbo::create([ 'user_id' => null, 'member_id' => $member->id, 'shopping_user_id' => $duplicate->id, 'is_for' => 'ot', 'email' => 'erika@example.com', 'payone_userid' => 4711, 'clearingtype' => 'cc', 'carddata' => ['last4' => '4242'], 'active' => false, 'status' => 4, 'abo_interval' => 1, ]); $portal = Customer::create(['email' => 'erika@example.com', 'shopping_user_id' => $first->id, 'member_id' => $member->id, 'number' => 61009]); $summary = app(GdprDeletionService::class)->purgeShoppingUser($first, $member->id); expect($summary['shopping_users_purged'])->toBe(2) ->and($summary['portal_customers_deleted'])->toBe(1) ->and($summary['abos_anonymized'])->toBe(1); foreach ([$first->id, $duplicate->id] as $id) { expect(ShoppingUser::find($id))->toBeNull(); $row = ShoppingUser::withTrashed()->find($id); expect($row->deleted_at)->not->toBeNull() ->and($row->billing_firstname)->toBe(GdprDeletionService::ANONYMIZED_FIRSTNAME) ->and($row->billing_lastname)->toBe(GdprDeletionService::ANONYMIZED_LASTNAME) ->and($row->billing_address)->toBeNull() ->and($row->billing_phone)->toBeNull() ->and($row->billing_email)->not->toContain('erika'); } expect(Customer::find($portal->id))->toBeNull(); $abo = UserAbo::withTrashed()->find($abo->id); expect($abo->email)->toBeNull() ->and($abo->carddata)->toBeNull() ->and((int) $abo->payone_userid)->toBe(0); }); it('verweigert die Komplett-Löschung, sobald eine Mehrfach-Anlage eine Bestellung hat', function () { $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); $duplicate = deleteTestCustomer($member, 'erika@example.com', ['number' => 61010]); deleteTestOrder($duplicate); expect(fn () => app(GdprDeletionService::class)->purgeShoppingUser($first, $member->id)) ->toThrow(GdprDeletionException::class, __('msg.gdpr_error_customer_has_orders')); expect($first->fresh()->billing_email)->toBe('erika@example.com') ->and($duplicate->fresh()->deleted_at)->toBeNull(); }); it('verweigert die Komplett-Löschung bei Berater-Datensätzen und aktiven Abos', function () { $member = deleteTestUser(); $consultantRow = deleteTestCustomer($member, 'berater@example.com', ['auth_user_id' => $member->id]); expect(fn () => app(GdprDeletionService::class)->purgeShoppingUser($consultantRow, $member->id)) ->toThrow(GdprDeletionException::class, __('msg.gdpr_error_customer_is_member')); $customer = deleteTestCustomer($member, 'abo@example.com'); UserAbo::create([ 'user_id' => null, 'member_id' => $member->id, 'shopping_user_id' => $customer->id, 'is_for' => 'ot', 'email' => 'abo@example.com', 'payone_userid' => 1, 'clearingtype' => 'cc', 'active' => true, 'status' => 2, 'abo_interval' => 1, ]); expect(fn () => app(GdprDeletionService::class)->purgeShoppingUser($customer, $member->id)) ->toThrow(GdprDeletionException::class, __('msg.gdpr_error_active_abo')); }); it('anonymisiert bei der DSGVO-Löschung auch Mehrfach-Anlagen mit anderer Kundennummer und entfernt den Portal-Login', function () { $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); deleteTestOrder($first); $duplicate = deleteTestCustomer($member, 'erika@example.com', ['number' => 61010]); Customer::create(['email' => 'erika@example.com', 'shopping_user_id' => $duplicate->id, 'member_id' => $member->id]); $summary = app(GdprDeletionService::class)->deleteShoppingUser($first, $member->id); expect($summary['shopping_users_anonymized'])->toBe(2) ->and($summary['portal_customers_deleted'])->toBe(1); $first = $first->fresh(); expect($first->billing_firstname)->toBe('Erika') ->and($first->billing_email)->toBe('geloescht_'.$first->id.'@dsgvo.invalid'); $duplicate = $duplicate->fresh(); expect($duplicate->billing_firstname)->toBe(GdprDeletionService::ANONYMIZED_FIRSTNAME) ->and($duplicate->billing_email)->not->toContain('erika'); expect(Customer::where('email', 'erika@example.com')->count())->toBe(0); }); it('bietet im Modal die Komplett-Löschung an, wenn keine Bestellung vorliegt, und führt sie nach Bestätigung aus', function () { $admin = deleteTestUser(['admin' => 2]); $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); $duplicate = deleteTestCustomer($member, 'erika@example.com', ['number' => 61010]); $this->actingAs($admin)->get(route('admin_customer_detail', $first->id)) ->assertOk() ->assertSee('id="modals-customer-delete"', false) ->assertSee('2-mal') ->assertSee(__('msg.gdpr_purge_customer_button')) ->assertDontSee(__('msg.gdpr_delete_customer_button')); $this->actingAs($admin)->post(route('admin_customer_delete', $first->id)) ->assertRedirect(route('admin_customer_detail', $first->id)) ->assertSessionHas('alert-error', __('msg.delete_confirm_checkbox_missing')); expect($first->fresh())->not->toBeNull(); $this->actingAs($admin)->post(route('admin_customer_delete', $first->id), ['delete_customer' => 1]) ->assertRedirect(route('admin_customers')) ->assertSessionHas('alert-success'); expect(ShoppingUser::find($first->id))->toBeNull() ->and(ShoppingUser::find($duplicate->id))->toBeNull(); }); it('bietet im Modal nur die DSGVO-Löschung an, wenn eine Bestellung vorliegt', function () { $superadmin = deleteTestUser(['admin' => 3]); $member = deleteTestUser(); $first = deleteTestCustomer($member, 'erika@example.com', ['number' => 61009]); $duplicate = deleteTestCustomer($member, 'erika@example.com', ['number' => 61010]); deleteTestOrder($duplicate); $this->actingAs($superadmin)->get(route('admin_customer_detail', $first->id)) ->assertOk() ->assertSee('Es ist nur') ->assertSee(__('msg.gdpr_delete_customer_button')) ->assertDontSee(__('msg.gdpr_purge_customer_button')); $this->actingAs($superadmin)->post(route('admin_customer_delete', $first->id), ['delete_customer' => 1]) ->assertRedirect(route('admin_customer_detail', $first->id)) ->assertSessionHas('alert-error', __('msg.gdpr_error_customer_has_orders')); expect($first->fresh()->billing_email)->toBe('erika@example.com'); $this->actingAs($superadmin)->post(route('admin_customer_gdpr_delete', $first->id), ['gdpr_delete_customer' => 1]) ->assertRedirect(route('admin_customers')) ->assertSessionHas('alert-success'); expect($duplicate->fresh()->billing_email)->toBe('geloescht_'.$duplicate->id.'@dsgvo.invalid') ->and($first->fresh()->billing_firstname)->toBe(GdprDeletionService::ANONYMIZED_FIRSTNAME); }); it('zeigt normalen Admins bei Bestellungen den Hinweis auf den Superadmin', function () { $admin = deleteTestUser(['admin' => 2]); $member = deleteTestUser(); $customer = deleteTestCustomer($member, 'erika@example.com'); deleteTestOrder($customer); $this->actingAs($admin)->get(route('admin_customer_detail', $customer->id)) ->assertOk() ->assertSee('nur von einem Superadmin') ->assertDontSee('id="form-customer-gdpr-delete"', false); });