mivita/dev/_tickets/2026-09-08.md
Kevin aa52b02a2a Doku: Plaene, Kundenbriefe, Log-Analyse und Ledger-Plan versioniert, Stand 23.09.
- September-Plan: B2, E1 (Gleichstand, Zahlungsdatum), A07 und die
  Abo-Warnung nachgetragen; Audit-Stand A07 erledigt.
- Bisher nur im Arbeitsbaum: Entwicklungsplan und Kundenbrief vom 07.09.
  samt PDF, Tickets vom 08.09., Log-Analyse 26.08., Ledger-Plan,
  August-Anerkennungen. E-Mail-Adressen von Kunden und Beratern durch
  Platzhalter bzw. Namen ersetzt.

Nicht versioniert (personenbezogene Daten): Kundenliste CSV/XLSX und die
Produktions-Logs in dev/2026-08-26-logs/*.log.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 15:45:10 +00:00

9.3 KiB
Raw Permalink Blame History

Ticket #1 Wir brauchen einen neuen Navigationspunkt, indem wir Berater übertragen können, d.h. die Kunden und den ganzen Berater Stamm auf einen einzelnen Berater. Hintergrund ist, wenn zum Beispiel ein Berater aufhört, soll es die Möglichkeit geben, dass ein anderer Berater sein ganzen Berater und Kundenstamm bekommt ähnlich ist es wenn ein Berater nicht mehr zahlt, geht es an seinen Sponsor über.

Beispiel: Nadine Ried: Der ganze Baum soll an Claudia Zürn übertragen werden.

Ticket #2 Kunden löschen. Wenn ein Kunde eine Bestellung hat, kann man ihn so nicht mehr aus dem System löschen. Das ist etwas unglücklich unten gibt es einen Button DSGVO löschen, Hier werden die Sachen unkenntlich gemacht, das ist auch noch etwas unglücklich. Ich würde es so machen kriegt man auf Kunden löschen geht ein Modal Fenster auf wo man die Möglichkeit hat den Kunden komplett zu löschen falls er noch keine Bestellung hat oder automatisch dort steht. Der Kunde hat schon eine Bestellung es geht nur DSGVO löschen. Wenn dann ein Kunde gelöscht wird oder entfernt wird, ist es wichtig, dass seine personenbezogenen Daten raus sind und auch weitere Kundenanlagen denn es ist möglich, dass der Kunde mehrfach angelegt ist. Wenn er mehrere Bestellung hat, wird jeweils ein Kunde angelegt d.h. hier müsste schon im Vorfeld geprüft werden. Wie oft der Kunde würde ich über die E-Mail-Adresse machen dies Unique in diesem Augenblick auswerten und sagen hier und hier wird der Eintrag entsprechend entfernt.

Ein Beispiel ist der Kunde 61009 Hier gibt es mehr Anlagen.


Umsetzung (14.09.2026)

Ticket #1 – Berater übertragen

Neuer Navigationspunkt im Superadmin-Bereich: Berater übertragen (unterhalb von User Bereinigung), URL /admin/user/transfer.

Ablauf auf der Seite

  1. Von Berater (gibt den Stamm ab, alle nicht gelöschten Berater wählbar) und An Berater (übernimmt; nur aktive, nicht gesperrte Berater mit gültigem Account) über Live-Suche auswählen → Vorschau anzeigen.
  2. Vorschau zeigt: Firstline-Berater (mit Liste), gesamte Downline (alle Ebenen), deaktivierte Berater mit geparktem Sponsor-Bezug, Kunden-Datensätze (davon gelöscht), Kunden-Abos (davon aktiv), Portal-Logins.
  3. Checkbox bestätigen → Übertragung ausführen. Erfolgsmeldung mit Anzahl der bewegten Firstline-Berater, Kunden und Abos.

Was übertragen wird (in einer DB-Transaktion, App\Services\ConsultantTransferService)

  • Firstline: users.m_sponsor = Quelle → Ziel. Die Teilbäume darunter folgen automatisch (sie hängen an ihrer Firstline).
  • Geparkte Sponsor-Bezüge deaktivierter Berater: users.pre_sponsor = Quelle → Ziel (Reaktivierung landet dann unter dem Zielberater).
  • Kunden: shopping_users.member_id = Quelle → Ziel, auch soft-gelöschte Einträge.
  • Kunden-Abos: user_abos.member_id = Quelle → Ziel (künftige Abo-Bestellungen werden dem Zielberater zugeordnet).
  • Kunden-Portal-Logins: customers.member_id = Quelle → Ziel.

Was bewusst nicht übertragen wird

  • Bestehende Bestellungen (shopping_orders.member_id), Umsätze und Provisionsdaten bleiben als Historie beim bisherigen Berater. Bereits berechnete Monats-Snapshots (user_businesses, user_business_structures) werden nicht rückwirkend geändert; der laufende Monat wird bei der nächsten Provisionsberechnung (business:store-optimized) auf Basis der neuen Struktur gerechnet.
  • Der Quellberater selbst bleibt unverändert (Sponsor, Status). Deaktivieren/Löschen erfolgt wie bisher separat (User-Verwaltung / DSGVO).

Schutzregeln

  • Quelle ≠ Ziel; Ziel muss aktiv, nicht gesperrt, nicht gelöscht und mit gültigem Account sein.
  • Ziel darf nicht unterhalb der Firstline der Quelle liegen (würde einen Kreis im Baum erzeugen) → Fehlermeldung.
  • Ziel in der Firstline der Quelle ist erlaubt: Der Zielberater behält seinen Sponsor und bekommt die übrigen Firstline-Berater als Downline (Hinweis in der Vorschau).

Protokollierung: wie bei der automatischen User-Bereinigung – user_clean_up_logs (je umgehängtem Berater) und shopping_user_member_logs (je Kunde), damit die Übertragung in User Bereinigung → Downline-Übertragungen / Kunden-Übertragungen sichtbar ist. Zusätzlich SysLog-Eintrag consultant-transfer und Log-Kanal cleanup.

Beispiel aus dem Ticket (Nadine Ried → Claudia Zürn): Von Berater = Nadine Ried, An Berater = Claudia Zürn, Vorschau prüfen, bestätigen.

Dateien

  • neu: app/Services/ConsultantTransferService.php, app/Exceptions/ConsultantTransferException.php, app/Http/Controllers/AdminUserTransferController.php, app/Http/Requests/UserTransfer/{Preview,Execute}ConsultantTransferRequest.php, resources/views/admin/user/transfer/index.blade.php, tests/Feature/ConsultantTransferTest.php
  • geändert: routes/domains/crm.php (3 Routen im Superadmin-Block), resources/views/layouts/includes/layout-sidenav.blade.php, resources/lang/{de,en,es,fr}/navigation.php (user_transfer), resources/lang/{de,en,es,fr}/msg.php (transfer_*)

Ticket #2 – Kunden löschen

Neues Verhalten auf der Kunden-Detailseite (Admin): Der Button Kunden löschen öffnet ein Modal statt direkt zu löschen. Der alte Direkt-Link (GET) ist entfernt, die Löschung läuft jetzt per POST mit Bestätigungs-Checkbox.

Das Modal zeigt

  • wie oft der Kunde angelegt ist, ermittelt über Kundennummer ODER E-Mail-Adresse (Groß-/Kleinschreibung egal; Platzhalter-Adressen wie Faker-/bereits anonymisierte Mails werden nicht zur Gruppierung benutzt, sonst würden fremde Kunden zusammengezogen). Beispiel Kunde 61009: alle Einträge mit derselben Kundennummer plus alle Endkunden-Einträge mit derselben E-Mail, auch mit anderer Kundennummer.
  • eine Tabelle aller betroffenen Einträge (ID, Kd-Nr., Name, Berater, Anzahl Bestellungen, angelegt am, ggf. „gelöscht“).
  • Fall A – keine Bestellung in keinem Eintrag: Hinweis „kann komplett gelöscht werden“ + Checkbox + Button Kunde komplett löschen (Admin-Recht genügt).
  • Fall B – mindestens ein Eintrag hat eine Bestellung: Hinweis „Der Kunde hat schon N Bestellung(en). Es ist nur DSGVO löschen möglich“ + Checkbox + Button Kunde DSGVO-löschen (wie bisher nur Superadmin; normale Admins sehen den Hinweis).
  • Bei aktivem Abo wird jede Löschung blockiert (Abo zuerst kündigen), bei Berater-Datensätzen wird auf die User-Verwaltung verwiesen.

Komplett löschen (GdprDeletionService::purgeShoppingUser), nur ohne Bestellungen:

  • alle Einträge der Gruppe werden vollständig anonymisiert (Name, Adressen, Telefon, E-Mail, Notizen, Newsletter) und soft-gelöscht → verschwinden aus allen Listen;
  • inaktive Abos verlieren E-Mail, Kartendaten und Zahlungs-Tokens (Payone/Stripe);
  • Portal-Logins (customers) werden gelöscht;
  • Stripe-Customer-Objekte werden gelöscht; Löschnachweis im SysLog (gdpr-purge-customer) und Log-Kanal cleanup.
  • Kein Hard-Delete der Zeilen: Fremdschlüssel (Abos, Checkout-Funnel, Logs) zeigen auf shopping_users, und der Lösch-Nachweis bleibt erhalten. Personenbezogene Daten sind vollständig entfernt.

DSGVO löschen (bestehend, erweitert): arbeitet jetzt ebenfalls auf der E-Mail-Gruppe (nicht mehr nur Kundennummer) und entfernt zusätzlich die Portal-Logins. Wie bisher bleiben bei Einträgen mit Bestellung Name und Rechnungsadresse (Aufbewahrungspflicht), alle Kontaktdaten werden entfernt.

Entfernt: die alte „Löschung“ (CustomerRepository::deleteCustomer), die nur den einen Datensatz mit „Deleted User“ überschrieb und ihn in der Liste stehen ließ.

Nebenkorrektur: Fehlermeldungen der DSGVO-Löschung wurden mit dem Session-Key alert-danger gesetzt, den kein Layout anzeigt – jetzt alert-error (wird als Growl-Meldung angezeigt).

Offener Punkt / Hinweis: DHL-Versanddatensätze (dhl_package_shipments) enthalten Empfängerdaten je Bestellung und werden – wie Bestellungen und Rechnungen – bewusst nicht angefasst (Beleg zur Bestellung). Falls gewünscht, könnte dort bei DSGVO-Löschung zusätzlich E-Mail/Telefon geleert werden.

Dateien

  • geändert: app/Services/GdprDeletionService.php (getCustomerRows, groupingEmail, analyzeShoppingUser, purgeShoppingUser, deletePortalCustomers), app/Http/Controllers/CustomerController.php (delete → POST + Purge, detail liefert Analyse), app/Repositories/CustomerRepository.php, routes/domains/crm.php (admin_customer_delete GET → POST), resources/views/admin/customer/detail.blade.php (Modal), resources/views/admin/customer/_detail.blade.php (Button), resources/lang/{de,en,es,fr}/msg.php (gdpr_purge_*, gdpr_error_customer_has_orders, delete_confirm_checkbox_missing)
  • neu: tests/Feature/CustomerDeleteTest.php

Tests

php artisan test --compact tests/Feature/ConsultantTransferTest.php tests/Feature/CustomerDeleteTest.php tests/Feature/GdprDeletionTest.php

Hinweis für HTTP-Tests auf Admin-Routen: Die CRM-Routen werden host-abhängig geladen. Die beiden neuen Testdateien setzen deshalb in beforeEach $_SERVER['HTTP_HOST'] auf den CRM-Host, starten die App neu, migrieren die In-Memory-DB und stellen den Host in afterEach wieder her (sonst booten nachfolgende Tests mit den CRM-Routen).

Stand der Gesamt-Suite am 14.09.2026: 92 Fehlschläge bestehen unabhängig von diesen Tickets bereits vorher (identische Liste mit und ohne die neuen Dateien); alle 22 neuen Tests sind grün.