Calculation errors for refunded amounts

This commit is contained in:
2026-09-04 01:15:37 +02:00
parent 5beb2b1d97
commit c1c1a4f143
28 changed files with 1436 additions and 26 deletions
+16
View File
@@ -32,11 +32,13 @@ class InvoiceType extends CommonModel {
'name',
'sort_order',
'selectable',
'counts_as_expense',
];
protected $casts = [
'sort_order' => 'integer',
'selectable' => 'boolean',
'counts_as_expense' => 'boolean',
];
/**
@@ -51,4 +53,18 @@ class InvoiceType extends CommonModel {
{
return self::where('selectable', true)->orderBy('sort_order')->get();
}
/**
* Die Typen, die in der Ausgabenrechnung einer Veranstaltung zählen -- nach Sortierung.
*
* Ausgenommen ist, was fachlich keine Ausgabe ist, sondern die Rücknahme einer Einnahme: Eine
* Beitragserstattung mindert bereits die Einnahmenseite, weil der abgemeldete Teili dort herausfällt.
* Als Ausgabe gezählt, ginge derselbe Vorgang ein zweites Mal in die Bilanz.
*
* @return \Illuminate\Database\Eloquent\Collection<int, self>
*/
public static function countingAsExpense(): \Illuminate\Database\Eloquent\Collection
{
return self::where('counts_as_expense', true)->orderBy('sort_order')->get();
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace App\Enumerations;
use App\Scopes\CommonModel;
/**
* Gründe, aus denen ein Teil des gezahlten Teilnahmebeitrags beim Verband bleibt.
*
* Gegenstück zu {@see RefundReason}: Jener sagt, warum erstattet wird, dieser, warum nicht alles.
*
* @property string $slug
* @property string $name
* @property string|null $document_text
* @property bool $requires_note
* @property int $sort_order
*/
class RetentionReason extends CommonModel
{
public const string CANCELLATION_FEE = 'cancellation_fee';
public const string INCURRED_COSTS = 'incurred_costs';
public const string MATERIAL = 'material';
public const string CUSTOM = 'custom';
protected $table = 'retention_reasons';
protected $primaryKey = 'slug';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'slug',
'name',
'document_text',
'requires_note',
'sort_order',
];
protected $casts = [
'requires_note' => 'boolean',
'sort_order' => 'integer',
];
/**
* Der Text, der auf dem Beleg unter „Einbehalten" steht. Bei einem Grund, der einen Freitext
* verlangt, ist es der Text der Aktionsleitung.
*/
public function documentText(?string $note = null): string
{
return $this->requires_note ? trim((string) $note) : (string) $this->document_text;
}
/**
* Optionen für das Frontend. `requiresNote` steuert dort das Freitextfeld.
*
* @return array<int, array{value: string, label: string, requiresNote: bool}>
*/
public static function options(): array
{
return self::orderBy('sort_order')->get()
->map(static fn (self $reason): array => [
'value' => $reason->slug,
'label' => $reason->name,
'requiresNote' => $reason->requires_note,
])
->all();
}
}