Feedback-Umsetzung (dev/2026-07-22/feedback-analyse-2026-07-24.md, alle 10 Punkte):
- VIP-Statistik: eigener Berater ('ich', Linie 0) in Neue-Kundenabos- und beiden Top-10-Listen; Top-10-Umsatz sortiert nach abgerechneten Ist-Punkten + Abo-Anzahl-Spalte; Fußnote nicht-payline-wirksame Punkte (SalesVolumePoints::nonPaylinePoints)
- Führungskräfte-Liste: beide Sichten 'direkt' (an nächster FK abgeschnitten, additiv) und 'gesamt' (komplette Downline), Gesamt-Spalte nach Kundenzählweise
- Team-Abos: je Ebene 'abgerechnet (Monat)' und 'bis zum 25. erwartet'; Payline-Karte mit Teamabo-/Kundenabo-Zählern
- Incentive-Live-Ranking: Qualifizierte zuerst nummeriert (fixt auch isWinner rank<=max_winners)
- Porto: Netto-/Brutto-Kennzeichnung (Berater netto, Endkunden inkl. MwSt., portal_view-Flag; auch CRM-/Shop-Warenkorb + Checkout), Yard::shipping_tax-Fix
- Struktur-Ansicht: Abo-Badges 'A' (eigenes Abo) und '(n)' (Kundenabos) je Teampartner, gebatcht
- Abo-Löschung: Softdelete setzt active=0 (UserAbo deleting-Hook) + Bereinigungs-Migration für gelöschte-aber-aktive Abos
- Doku: technische Analyse + Kundinnen-Feedback-Dokument unter dev/2026-07-22/
- Tests: IncentiveRankingTest, TeamStructureAboBadgesTest, YardShippingTaxTest (neu); BackofficeStatisticsListsTest, TeamAboOverviewTest, PaylineForecastServiceTest, AboAdminDeleteTest, AboOneTimeViewTest erweitert
Außerdem enthalten (Branch-WIP vor dem Feedback): DATEV-Abgleich-Fixes 08+09/2025, USt-ID-Live-Validierung sowie restliche unkommittete Stände (u. a. Abo-Pause-Sprachkeys).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
2.2 KiB
PHP
72 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Exceptions\GdprDeletionException;
|
|
use App\Models\ShoppingUser;
|
|
use App\Services\GdprDeletionService;
|
|
use App\User;
|
|
use Illuminate\Console\Command;
|
|
|
|
class GdprDeleteUser extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'gdpr:delete
|
|
{id : User-ID (Berater) bzw. mit --customer die ShoppingUser-ID (Endkunde)}
|
|
{--customer : Endkunde (shopping_users) statt Berater (users) löschen}
|
|
{--force : Sicherheitsabfrage überspringen}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'DSGVO-Löschung (Art. 17): Anonymisiert alle personenbezogenen Daten eines Beraters oder Endkunden unwiderruflich. Aufbewahrungspflichtige Daten (Rechnungen, Gutschriften, Provisionen) bleiben erhalten.';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle(GdprDeletionService $gdprDeletionService): int
|
|
{
|
|
$id = (int) $this->argument('id');
|
|
$isCustomer = (bool) $this->option('customer');
|
|
|
|
if ($isCustomer) {
|
|
$model = ShoppingUser::withTrashed()->find($id);
|
|
$label = $model ? 'Endkunde #'.$model->id.' ('.$model->billing_email.')' : null;
|
|
} else {
|
|
$model = User::withTrashed()->find($id);
|
|
$label = $model ? 'Berater #'.$model->id.' ('.$model->email.')' : null;
|
|
}
|
|
|
|
if (! $model) {
|
|
$this->error('ERROR: Datensatz nicht gefunden, id: '.$id);
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (! $this->option('force') && ! $this->confirm('DSGVO-Löschung für '.$label.' UNWIDERRUFLICH ausführen?')) {
|
|
$this->info('Abgebrochen.');
|
|
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
$summary = $isCustomer
|
|
? $gdprDeletionService->deleteShoppingUser($model)
|
|
: $gdprDeletionService->deleteUser($model);
|
|
|
|
$this->info('SUCCESS: DSGVO-Löschung ausgeführt: '.json_encode($summary));
|
|
|
|
return 0;
|
|
} catch (GdprDeletionException $e) {
|
|
$this->error('ERROR: '.$e->getMessage());
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|