CluPilotCloud/app/Livewire/Admin/PaymentProblems.php

81 lines
2.7 KiB
PHP

<?php
namespace App\Livewire\Admin;
use App\Models\DunningCase;
use App\Models\FailedCheckout;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Component;
/**
* Wo Geld ausbleibt — beide Arten an einer Stelle.
*
* 1. **Mahnfälle.** Ein laufender Vertrag, dessen Abbuchung gescheitert ist.
* Der Fall wandert durch die Stufen des DunningSchedule bis zur Begleichung
* oder zur Sperre.
* 2. **Geplatzte Käufe.** Bei SEPA oder Sofortüberweisung klärt die Zahlung
* erst nach dem Abschluss der Kasse. Klärt sie nicht, entsteht kein Auftrag
* — richtig so — und bis zum 31.7.2026 auch sonst nichts. Ein verlorener
* Verkauf, von dem niemand hörte.
*
* Eine Seite und nicht zwei: beide Male ist die Frage dieselbe — wer schuldet
* was, und was ist der nächste Schritt. Zwei Seiten hiessen zwei Orte, an denen
* man nachsehen muss, und einer davon wird vergessen.
*
* Hinter `billing.manage`, nicht hinter `hosts.manage`: hier stehen Kundennamen,
* Beträge und offene Schulden. Die Rolle `Billing` hat genau dieses Recht und
* sonst nur `console.view` — die Buchhaltung sieht das, ohne Admin zu sein.
*/
#[Layout('layouts.admin')]
class PaymentProblems extends Component
{
public function mount(): void
{
abort_unless(Gate::allows('billing.manage'), 403);
}
/**
* Einen geplatzten Kauf als erledigt abhaken.
*
* Mit Begründung, nicht nur mit einem Haken: „erledigt" allein ist beim
* nächsten Nachfragen wertlos — überwiesen? neu bestellt? nicht erreichbar?
* Wer es war, steht mit dabei.
*/
#[On('failed-checkout-resolved')]
public function onResolved(int $id, string $note = ''): void
{
$this->authorize('billing.manage');
$problem = FailedCheckout::query()->findOr($id, fn () => abort(404));
$begruendung = trim($note);
$wer = Auth::guard('operator')->user()?->email ?? 'console';
$problem->update([
'resolved_at' => Carbon::now(),
'note' => $begruendung !== '' ? $begruendung.' — '.$wer : $wer,
]);
$this->dispatch('notify', message: __('payment_problems.resolved'));
}
public function render()
{
return view('livewire.admin.payment-problems', [
'cases' => DunningCase::query()
->whereNull('settled_at')
->with('subscription.customer')
->orderBy('next_step_at')
->get(),
'failed' => FailedCheckout::query()
->whereNull('resolved_at')
->orderByDesc('failed_at')
->get(),
]);
}
}