Abo (A3): Status und Grund eines Abos sichtbar machen

Neue Spalten user_abos.status_reason / paused_until und
user_abo_orders.error_message. Der Grund wird zentral am Model gepflegt
(saving-Event): Zahlungsfehler, Kuendigung/Deaktivierung mit Akteur aus dem
Formular-Kanal, Loeschen beim Zurueckkehren in den Lauf; die Pause setzt
paused_until und paused_by_*. Cron- und Retry-Zahlung schreiben den Fehlertext
des Anbieters an die Abo-Bestellung.

AboStatusExplainer uebersetzt das in einen Satz ("Pausiert bis ... vom Kunden
am ...", "Zahlung am ... fehlgeschlagen - Grund: ...", "Gekuendigt am ... vom
Berater"), Partial _status_explain in Abo-Detail (CRM, Portal, Admin),
Abo-Uebersicht, Kundenabo-Liste, Team-Kundenabos und Admin-Datatable; dort
neuer Filter "Zustand" (pausiert / Zahlung fehlgeschlagen). Backfill-Kommando
abo:backfill-status-reasons fuer Bestandsdaten, Fallback ueber das
Statusprotokoll und die Pausenfelder.

Sprachdateien de/en/es/fr, Tests in AboStatusExplainTest, Plan-Status.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-09-16 10:24:18 +00:00
parent e8057f9f7b
commit d6efe56cd4
22 changed files with 977 additions and 15 deletions

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Ticket A3 (Entwicklungsplan September 2026): Status und Grund eines Abos
* sichtbar machen. `status_reason` haelt fest, warum ein Abo in seinem Zustand
* ist und wer ihn ausgeloest hat (z. B. `payment_failed`, `paused_by_customer`,
* `cancelled_by_consultant`), `paused_until` das Ende einer Pause, und
* `user_abo_orders.error_message` den Fehlertext des Zahlungsanbieters.
* Bestandsdaten fuellt `abo:backfill-status-reasons`.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('user_abos', function (Blueprint $table) {
$table->string('status_reason', 40)->nullable()->after('status');
$table->date('paused_until')->nullable()->after('last_pause_date');
});
Schema::table('user_abo_orders', function (Blueprint $table) {
$table->string('error_message', 255)->nullable()->after('status');
});
}
public function down(): void
{
Schema::table('user_abos', function (Blueprint $table) {
$table->dropColumn(['status_reason', 'paused_until']);
});
Schema::table('user_abo_orders', function (Blueprint $table) {
$table->dropColumn('error_message');
});
}
};