Approven von Beitragserstattungen

This commit is contained in:
2026-09-03 23:08:41 +02:00
parent b01288b5e0
commit efee20c16b
15 changed files with 994 additions and 39 deletions
@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* Der Auslagentyp für automatisch eingereichte Beitragserstattungen.
*
* `invoices.type` ist ein Fremdschlüssel auf `invoice_types.slug` -- ohne diese Zeile scheitert schon das
* Anlegen der Abrechnung. Da `invoice_types` im Repository nirgends geseedet wird, ist die Migration der
* einzige verlässliche Weg; sie schreibt nur, was noch fehlt.
*
* Zur neuen Spalte `selectable`: Der Typ entsteht ausschließlich aus einem Erstattungsvorgang und darf in
* keinem Formular zur Auswahl stehen. Bisher wurden solche Ausnahmen als hartkodierte `continue`-Zweige
* in GlobalDataProvider gelöst (dort für `travelling`) -- eine dritte Sonderregel an zwei Stellen wäre die
* Sorte Code, die beim nächsten Typ wieder wächst. Das Flag sagt selbst, was es tut.
*/
return new class extends Migration {
private const string SLUG = 'participation_refund';
public function up(): void
{
Schema::table('invoice_types', function (Blueprint $table) {
$table->boolean('selectable')->default(true)->after('sort_order');
});
if (DB::table('invoice_types')->where('slug', self::SLUG)->exists()) {
return;
}
DB::table('invoice_types')->insert([
'slug' => self::SLUG,
'name' => 'Beitragserstattung',
// Hinter "Verwaltung" (2) und vor "Sonstige Kosten" (3) wäre die Reihenfolge unklar; der Typ
// taucht ohnehin nur in Summen und Auswertungen auf.
'sort_order' => 4,
'selectable' => false,
'created_at' => now(),
'updated_at' => now(),
]);
}
public function down(): void
{
// Nur löschen, wenn keine Abrechnung daran hängt -- der Fremdschlüssel würde es sonst verhindern,
// und ein Rollback soll nicht an fremden Daten scheitern.
if (!DB::table('invoices')->where('type', self::SLUG)->exists()) {
DB::table('invoice_types')->where('slug', self::SLUG)->delete();
}
Schema::table('invoice_types', function (Blueprint $table) {
$table->dropColumn('selectable');
});
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Verbindet den Erstattungsvorgang mit der Abrechnung, die aus ihm entstanden ist.
*
* Damit liest die Aktionsleitung den Auszahlungsstand dort ab, wo er entsteht -- in der Abrechnung --,
* statt ihn am Vorgang zu doppeln und synchron halten zu müssen.
*
* `nullOnDelete`: wird eine Abrechnung gelöscht, bleibt der Erstattungsvorgang bestehen. Er ist die
* Erklärung des Teilis und hat einen eigenen Beleg; er hängt nicht am Leben der Abrechnung.
*/
return new class extends Migration {
public function up(): void
{
Schema::table('participant_refunds', function (Blueprint $table) {
$table->foreignId('invoice_id')->nullable()->after('account_iban')
->constrained('invoices', 'id')->nullOnDelete()->cascadeOnUpdate();
});
}
public function down(): void
{
Schema::table('participant_refunds', function (Blueprint $table) {
$table->dropForeign(['invoice_id']);
$table->dropColumn('invoice_id');
});
}
};