Creating Participation refunds
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Gründe für die Erstattung eines Teilnahmebeitrags -- DB-gestützt wie die Befreiungsgründe der
|
||||
* Umsatzsteuer, damit Label und der auf dem Beleg erscheinende Text zentral pflegbar sind.
|
||||
*
|
||||
* App-weit und ohne `tenant`-Spalte: die Gründe sind Stammdaten, mandantenspezifisch ist nur der
|
||||
* einzelne Erstattungsvorgang.
|
||||
*/
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('refund_reasons', function (Blueprint $table) {
|
||||
$table->string('slug')->primary();
|
||||
$table->string('name');
|
||||
$table->text('document_text')->nullable();
|
||||
$table->boolean('requires_note')->default(false);
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
DB::table('refund_reasons')->insert([
|
||||
[
|
||||
'slug' => 'sickness',
|
||||
'name' => 'Krankheitsbedingte Absage',
|
||||
'document_text' => 'Die Teilnahme konnte krankheitsbedingt nicht angetreten werden.',
|
||||
'requires_note' => false,
|
||||
'sort_order' => 10,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'slug' => 'event_cancelled',
|
||||
'name' => 'Ausfall der Veranstaltung',
|
||||
'document_text' => 'Die Veranstaltung wurde abgesagt und konnte nicht stattfinden.',
|
||||
'requires_note' => false,
|
||||
'sort_order' => 20,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
// Der Text entsteht erst aus dem Freitext der Aktionsleitung -- deshalb hier leer.
|
||||
'slug' => 'other',
|
||||
'name' => 'Sonstiger Grund',
|
||||
'document_text' => null,
|
||||
'requires_note' => true,
|
||||
'sort_order' => 30,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('refund_reasons');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Ein Erstattungsvorgang je Anmeldung.
|
||||
*
|
||||
* Der Vorgang läuft in zwei Schritten: die Aktionsleitung gibt Betrag und Grund frei (`pending`), der
|
||||
* Teili ergänzt über den Token-Link seine Bankverbindung (`accepted`). Solange nicht bestätigt wurde,
|
||||
* kann die Aktionsleitung abbrechen (`cancelled`) -- der Link verhält sich danach so, als hätte es nie
|
||||
* eine Freigabe gegeben.
|
||||
*
|
||||
* `event_participants.amount_paid` bleibt in jedem Fall unangetastet: der Vorgang ist freigegeben, aber
|
||||
* noch nicht ausgezahlt. Die Verrechnung kommt mit der Auszahlung.
|
||||
*
|
||||
* Bewusst KEIN Unique auf `event_participant_id`: abgebrochene Vorgänge bleiben als Historie stehen, und
|
||||
* danach darf eine neue Freigabe entstehen. Dass es höchstens einen offenen Vorgang gibt, setzt
|
||||
* ParticipantRefundRepository::openFor() durch.
|
||||
*/
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('participant_refunds', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('tenant');
|
||||
|
||||
$table->foreignId('event_id')->constrained('events', 'id')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->foreignId('event_participant_id')->constrained('event_participants', 'id')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
|
||||
// Der öffentliche Link. Eigener Token statt des Teilnehmer-Identifiers, damit ein Abbruch den
|
||||
// Link tötet, ohne andere per Identifier erreichbare Funktionen (GiroCode) mitzunehmen.
|
||||
$table->string('token', 32)->unique();
|
||||
$table->string('status');
|
||||
|
||||
$table->float('amount', 2)->default(0);
|
||||
$table->string('reason')->nullable();
|
||||
$table->text('reason_note')->nullable();
|
||||
|
||||
$table->string('account_owner')->nullable();
|
||||
$table->string('account_iban')->nullable();
|
||||
|
||||
$table->foreignId('released_by')->nullable()->constrained('users', 'id')->nullOnDelete();
|
||||
$table->dateTime('released_at')->nullable();
|
||||
$table->dateTime('accepted_at')->nullable();
|
||||
$table->dateTime('cancelled_at')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('tenant')->references('slug')->on('tenants')->restrictOnDelete()->cascadeOnUpdate();
|
||||
$table->foreign('reason')->references('slug')->on('refund_reasons')->restrictOnDelete()->cascadeOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('participant_refunds');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Der Erklärungssatz, den der Teili auf der Erstattungsseite bestätigt und der anschließend auf dem
|
||||
* Eigenbeleg steht.
|
||||
*
|
||||
* Er liegt in `page_texts` und nicht in der Dokumentvorlage, damit Seite und Beleg denselben Wortlaut
|
||||
* zeigen: was der Teili gelesen hat, muss wörtlich das sein, was das PDF ihm zuschreibt. Zwei getrennte
|
||||
* Quellen würden früher oder später auseinanderlaufen.
|
||||
*
|
||||
* Als Migration und nicht als Seed-Skript, weil ohne diesen Text weder die Seite noch der Beleg
|
||||
* vollständig sind -- anders als bei den Dokumentvorlagen, wo ein Release gepflegte Texte nicht
|
||||
* überschreiben soll. `updateOrInsert` lässt eine bereits angepasste Fassung deshalb auch in Ruhe:
|
||||
* geschrieben wird nur, was noch fehlt.
|
||||
*/
|
||||
return new class extends Migration {
|
||||
private const string NAME = 'CONFIRMATION_PARTICIPANT_REFUND';
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
$existing = DB::table('page_texts')->where('name', self::NAME)->first();
|
||||
|
||||
if ($existing !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::table('page_texts')->insert([
|
||||
'name' => self::NAME,
|
||||
'content' => 'Ich versichere, dass ich den genannten Betrag beglichen habe und nicht '
|
||||
. 'anderweitig zurückerstattet bekomme.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('page_texts')->where('name', self::NAME)->delete();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user