mivita/app/Console/Commands/GdprDeleteUser.php
Kevin ac3bd6e8ea Statistiken/Abo: Kundenfeedback 2026-07-24 komplett umgesetzt + Steuern: DATEV-Fixes 08+09/2025 (Sammel-Commit Branch-WIP)
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>
2026-07-24 13:56:02 +00:00

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;
}
}
}