Widerruf: Gesetzliche Online-Widerrufsfunktion (Formular, Bestätigungsmail, Admin-Verwaltung)

- Öffentliches Widerrufsformular /widerruf mit Bestell-Lookup (throttled)
- Bestätigungsmail an Kunde + Weiterleitung an bestellungen@ (config app.withdrawal_mail)
- Admin-Übersicht/-Detail unter /admin/withdrawals inkl. Sidenav-Eintrag
- Widerruf-Button in Portal-Bestellansicht und Footer-Links
- Model OrderWithdrawal + Migration + Factory + Tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-07-08 15:56:02 +00:00
parent eac923968d
commit 63c36555a5
24 changed files with 1607 additions and 92 deletions

View file

@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('order_withdrawals', function (Blueprint $table) {
$table->id();
$table->foreignId('shopping_order_id')->nullable()->index();
$table->unsignedBigInteger('user_shop_id')->nullable()->index();
$table->string('type')->default('full');
$table->string('status')->default('received')->index();
$table->string('order_reference')->nullable();
$table->string('customer_email')->index();
$table->string('customer_name')->nullable();
$table->text('reason')->nullable();
$table->json('items')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('admin_note')->nullable();
$table->timestamp('declared_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('order_withdrawals');
}
};