AmountCast::class, 'retained_amount' => AmountCast::class, 'released_at' => 'datetime', 'accepted_at' => 'datetime', 'cancelled_at' => 'datetime', ]; public function participant(): BelongsTo { return $this->belongsTo(EventParticipant::class, 'event_participant_id'); } public function event(): BelongsTo { return $this->belongsTo(Event::class); } /** * Der Grund als Stammdatensatz. Nicht `reason()`, weil das die Spalte `reason` verdecken würde. */ public function reasonRelation(): BelongsTo { return $this->belongsTo(RefundReason::class, 'reason', 'slug'); } /** * Die Abrechnung, die aus diesem Vorgang entstanden ist. Der Auszahlungsstand steht dort und wird * hier nicht gedoppelt. */ public function invoice(): BelongsTo { return $this->belongsTo(Invoice::class); } /** * Wer die Bankverbindung aufgenommen hat -- leer, wenn der Teili sie selbst eingetragen hat. */ public function capturedBy(): BelongsTo { return $this->belongsTo(User::class, 'captured_by'); } /** Ob die Angaben von der Aktionsleitung stammen und nicht vom Teili selbst. */ public function wasCapturedByManagement(): bool { return $this->captured_by !== null; } public function isPending(): bool { return $this->status === self::STATUS_PENDING; } public function isAccepted(): bool { return $this->status === self::STATUS_ACCEPTED; } /** Der auf dem Beleg auszuweisende Grundtext -- bei Freitext-Gründen der Text der Aktionsleitung. */ public function reasonText(): string { return $this->reasonRelation()->first()?->documentText($this->reason_note) ?? ''; } public function reasonLabel(): string { return (string) ($this->reasonRelation()->first()?->name ?? ''); } /** Der Einbehaltungsgrund als Stammdatensatz -- leer, wenn voll erstattet wurde. */ public function retentionReasonRelation(): BelongsTo { return $this->belongsTo(RetentionReason::class, 'retention_reason', 'slug'); } public function retentionReasonLabel(): string { return (string) ($this->retentionReasonRelation()->first()?->name ?? ''); } /** Der auf dem Beleg auszuweisende Text zur Einbehaltung. */ public function retentionReasonText(): string { return $this->retentionReasonRelation()->first()?->documentText($this->retention_reason_note) ?? ''; } /** * Ob etwas beim Verband bleibt -- die halbe Cent-Toleranz fängt die Float-Rundung ab. * * Der Betrag steht in `retained_amount` und wird beim Einreichen festgeschrieben. Ihn zur Laufzeit * aus `amount_paid` zu rechnen ginge schief: Danach führt das Feld bereits den Rest. */ public function hasRetention(): bool { return ($this->retained_amount?->getAmount() ?? 0.0) > 0.005; } }