mivita/resources/views/admin/customer/detail.blade.php
Kevin 3d0864713b Kunden loeschen: DSGVO-geloeschte Kunden ausblenden und sperren; Checkout-Herkunft freiwillig
Kunden loeschen (Rueckmeldung "es wird nichts geloescht"):
- DSGVO-geloeschte Eintraege blieben mit Name in den Listen stehen; Kunde
  61009 wurde deshalb achtmal geloescht, bei Eintrag 45013 wurde die E-Mail
  danach ueber ein offenes Bearbeiten-Formular zurueckgeschrieben
- neue Spalte shopping_users.gdpr_deleted_at (Bestand per Migration markiert)
- Admin-Kundenliste blendet geloeschte Kunden aus (Filter: ausblenden /
  mit anzeigen / nur geloeschte), Berater-Kundenliste immer
- Bearbeiten/Speichern im Admin und beim Berater gesperrt; ShoppingUser
  verweigert das Zurueckschreiben von E-Mail und Telefon
- Modal und Erfolgsmeldung (de/en/es/fr) sagen klar, dass Name und
  Rechnungsadresse wegen der Aufbewahrungspflicht bleiben
- gdpr:repair-customers [--execute] zieht protokollierte Loeschungen nach
- DSGVO-Loeschung prueft Superadmin serverseitig; Berater duerfen eine
  Kunden-E-Mail nur beim eigenen Kunden nachtragen
- setFakerMail setzt faker_mail bei geloeschten Eintraegen nicht zurueck

Checkout: "Wie bist du auf uns aufmerksam geworden?" ist kein Pflichtfeld
mehr und wird farbig hervorgehoben.

Deploy: php artisan migrate && php artisan gdpr:repair-customers --execute

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 12:36:02 +00:00

123 lines
No EOL
8.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
<a href="{{route('admin_customers')}}" class="btn btn-sm btn-default float-right">{{ __('back') }}</a>
{{ __('customer.customer_details') }}
</h4>
@include('admin.customer._detail')
<a href="{{route('admin_customers')}}" class="btn btn-sm btn-default mt-2 float-right">{{ __('back') }}</a>
{{-- Kunden löschen: ein Modal für beide Wege (dev/_tickets/2026-09-08.md, Ticket #2) --}}
@php($canGdprDelete = Auth::user()->isSuperAdmin())
<div class="modal fade" id="modals-customer-delete">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ __('customer.delete_customer') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
</div>
<div class="modal-body">
@if ($deletion['is_member'])
<div class="alert alert-warning mb-0">{{ __('msg.gdpr_error_customer_is_member') }}</div>
@else
<p>
Der Kunde ist <b>{{ $deletion['rows']->count() }}-mal</b> angelegt
@if ($deletion['email'])
(E-Mail <b>{{ $deletion['email'] }}</b>, Kundennummer {{ $shopping_user->number ?: '-' }}).
@else
(Kundennummer {{ $shopping_user->number ?: '-' }}; keine gültige E-Mail zur Duplikat-Suche).
@endif
Alle folgenden Einträge werden gemeinsam behandelt:
</p>
<div class="table-responsive">
<table class="table table-sm table-bordered" id="customer-delete-rows">
<thead>
<tr>
<th>ID</th>
<th>Kd-Nr.</th>
<th>Name</th>
<th>Berater</th>
<th>Bestellungen</th>
<th>Angelegt</th>
</tr>
</thead>
<tbody>
@foreach ($deletion['rows'] as $row)
<tr class="{{ $row->id === $shopping_user->id ? 'font-weight-bold' : '' }}">
<td>{{ $row->id }}</td>
<td>{{ $row->number ?: '-' }}</td>
<td>{{ trim($row->billing_firstname.' '.$row->billing_lastname) }}
@if ($row->deleted_at) <span class="badge badge-secondary">gelöscht</span>
@elseif ($row->isGdprDeleted()) <span class="badge badge-secondary">DSGVO-gelöscht</span> @endif
</td>
<td>{{ $row->member->email ?? '-' }}</td>
<td>{{ $deletion['orders_by_row'][$row->id] ?? 0 }}</td>
<td>{{ $row->created_at ? $row->created_at->format('d.m.Y') : '-' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if ($deletion['all_gdpr_deleted'])
<div class="alert alert-success mb-0">
Der Kunde wurde bereits am <b>{{ $deletion['gdpr_deleted_at']->format('d.m.Y H:i') }}</b> DSGVO-gelöscht.
Alle Kontaktdaten (E-Mail, Telefon, Notizen, Newsletter) und Portal-Logins sind entfernt. Name und
Rechnungsadresse bleiben wegen der gesetzlichen Aufbewahrungspflicht an den Bestellungen erhalten.
Die Einträge sind in der Kundenliste ausgeblendet (Filter „DSGVO-gelöschte Kunden“) und gegen
Bearbeiten gesperrt.
</div>
@elseif ($deletion['orders_count'] > 0)
<div class="alert alert-warning">
Der Kunde hat schon <b>{{ $deletion['orders_count'] }} Bestellung(en)</b>. Es ist nur <b>DSGVO löschen</b> möglich:
Alle Kontaktdaten (E-Mail, Telefon, Notizen, Newsletter) sowie die Portal-Logins werden unwiderruflich entfernt.
<b>Name und Rechnungsadresse bleiben</b> bei Einträgen mit Bestellung wegen der gesetzlichen
Aufbewahrungspflicht erhalten. Die Einträge werden danach in der Kundenliste ausgeblendet und gegen
Bearbeiten gesperrt.
@if ($deletion['gdpr_deleted_rows'] > 0)
<br>{{ $deletion['gdpr_deleted_rows'] }} von {{ $deletion['rows']->count() }} Einträgen sind bereits gelöscht.
@endif
@if ($deletion['active_abos'] > 0)
<br><b>{{ __('msg.gdpr_error_active_abo') }}</b>
@endif
</div>
@if ($canGdprDelete)
<form action="{{ route('admin_customer_gdpr_delete', [$shopping_user->id]) }}" method="post" id="form-customer-gdpr-delete">
@csrf
<label class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" name="gdpr_delete_customer" value="1">
<span class="custom-control-label">DSGVO-Löschung unwiderruflich ausführen?</span>
</label>
<button type="submit" class="btn btn-danger" {{ $deletion['active_abos'] > 0 ? 'disabled' : '' }}>{{ __('msg.gdpr_delete_customer_button') }}</button>
</form>
@else
<p class="text-muted mb-0">Die DSGVO-Löschung kann nur von einem Superadmin ausgeführt werden.</p>
@endif
@elseif ($deletion['active_abos'] > 0)
<div class="alert alert-warning mb-0">{{ __('msg.gdpr_error_active_abo') }}</div>
@else
<div class="alert alert-info">
Der Kunde hat <b>keine Bestellungen</b> und kann <b>komplett gelöscht</b> werden: Alle oben aufgeführten Einträge
werden vollständig anonymisiert und entfernt, inklusive Portal-Login und inaktiver Abos.
</div>
<form action="{{ route('admin_customer_delete', [$shopping_user->id]) }}" method="post" id="form-customer-delete">
@csrf
<label class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" name="delete_customer" value="1">
<span class="custom-control-label">Kunden mit allen {{ $deletion['rows']->count() }} Einträgen unwiderruflich löschen?</span>
</label>
<button type="submit" class="btn btn-danger">{{ __('msg.gdpr_purge_customer_button') }}</button>
</form>
@endif
@endif
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ __('close') }}</button>
</div>
</div>
</div>
</div>
@endsection