> */ protected $queryString = [ 'status' => ['except' => 'all'], 'channel' => ['except' => 'all'], 'page' => ['except' => 1], 'selectedTransactionId' => ['except' => null, 'as' => 'transaction'], ]; public function mount(Company $company, ?int $transaction = null): void { $this->company = $company; if ($transaction) { $this->selectedTransactionId = $this->baseQuery() ->whereKey($transaction) ->value('id'); } if (! $this->selectedTransactionId) { $this->selectedTransactionId = $this->baseQuery() ->orderByDesc('requires_review') ->orderByDesc('risk_score') ->orderByDesc('executed_at') ->value('id'); } } public function with(): array { return [ 'title' => __('Unternehmenstransaktionen'), ]; } public function selectTransaction(int $transactionId): void { $this->selectedTransactionId = $transactionId; } public function updatingStatus(): void { $this->resetPage(); } public function updatedStatus(): void { $this->refreshSelection(); } public function updatingChannel(): void { $this->resetPage(); } public function updatedChannel(): void { $this->refreshSelection(); } public function updatedPage(): void { $this->refreshSelection(); } protected function refreshSelection(): void { $first = $this->filteredQuery() ->orderByDesc('requires_review') ->orderByDesc('risk_score') ->orderByDesc('executed_at') ->forPage($this->getPage(), $this->perPage) ->first(); $this->selectedTransactionId = $first?->id; } #[Computed] public function statusOptions(): array { return [ 'all' => __('Alle Status'), Transaction::STATUS_TRUE_POSITIVE => __('Bestätigte Treffer'), Transaction::STATUS_FALSE_POSITIVE => __('Fehlalarme'), Transaction::STATUS_CLEARED => __('Freigegeben'), ]; } #[Computed] public function channelOptions(): array { $channels = $this->baseQuery() ->select('channel') ->distinct() ->orderBy('channel') ->pluck('channel') ->filter() ->values(); $options = [ 'all' => __('Alle Kanäle'), ]; foreach ($channels as $channel) { $options[$channel] = $channel; } return $options; } #[Computed] public function metrics(): array { $baseQuery = $this->baseQuery(); $totalCount = (clone $baseQuery)->count(); $totalVolume = (clone $baseQuery)->sum('amount'); $openAlertsQuery = $this->baseQuery()->where('requires_review', true); $openAlertsCount = (clone $openAlertsQuery)->count(); $openAlertsVolume = (clone $openAlertsQuery)->sum('amount'); $highRiskCount = $totalCount > 0 ? $this->baseQuery()->where('risk_score', '>=', 80)->count() : 0; $last30DaysQuery = $this->baseQuery()->where('executed_at', '>=', now()->subDays(30)); $last30DaysCount = (clone $last30DaysQuery)->count(); $last30DaysVolume = (clone $last30DaysQuery)->sum('amount'); $statusBreakdown = (clone $baseQuery) ->selectRaw('status, COUNT(*) as total, COALESCE(SUM(amount), 0) as volume') ->groupBy('status') ->get() ->mapWithKeys(fn ($row) => [ $row->status => [ 'count' => (int) $row->total, 'amount' => (float) $row->volume, ], ]) ->toArray(); return [ 'total_count' => $totalCount, 'total_volume' => $totalVolume, 'open_alerts' => [ 'count' => $openAlertsCount, 'amount' => $openAlertsVolume, ], 'high_risk_share' => $totalCount > 0 ? (int) round(($highRiskCount / $totalCount) * 100) : 0, 'last_30_days' => [ 'count' => $last30DaysCount, 'amount' => $last30DaysVolume, ], 'by_status' => $statusBreakdown, ]; } #[Computed] public function transactions(): LengthAwarePaginator { return $this->filteredQuery() ->with('company') ->orderByDesc('requires_review') ->orderByDesc('risk_score') ->orderByDesc('executed_at') ->paginate($this->perPage); } #[Computed] public function selectedTransaction(): ?Transaction { $transaction = $this->transactions->firstWhere('id', $this->selectedTransactionId); if (! $transaction && $this->selectedTransactionId) { $transaction = $this->baseQuery() ->with('company') ->whereKey($this->selectedTransactionId) ->first(); } return $transaction ?? $this->transactions->first(); } #[Computed] public function counterpartyHistory(): Collection { $selected = $this->selectedTransaction; if (! $selected) { return collect(); } return $this->baseQuery() ->where('counterparty', $selected->counterparty) ->orderByDesc('executed_at') ->limit(6) ->get(); } #[Computed] public function recentAlerts(): Collection { return $this->baseQuery() ->where('requires_review', true) ->orderByDesc('executed_at') ->limit(5) ->get(); } #[Computed] public function actionChecklist(): array { $selected = $this->selectedTransaction; $items = [ [ 'title' => __('KYC- und Screening-Daten abgleichen'), 'description' => __('Prüfen Sie Unternehmens- und Gegenparteidaten gegen Sanktionslisten, PEP-Register und interne Sperrlisten.'), ], [ 'title' => __('Transaktionsverlauf analysieren'), 'description' => __('Bewerten Sie Häufigkeit, Muster und Gegenparteien der letzten Monate, um ungewöhnliche Aktivitäten zu erkennen.'), ], [ 'title' => __('Vier-Augen-Prinzip sicherstellen'), 'description' => __('Organisieren Sie eine Zweitprüfung und dokumentieren Sie alle Entscheidungen revisionssicher.'), ], ]; if ($selected?->status === Transaction::STATUS_TRUE_POSITIVE) { array_unshift($items, [ 'title' => __('Verdachtsmeldung vorbereiten'), 'description' => __('Erstellen Sie den Meldeentwurf für die FIU, sammeln Sie Belege und stellen Sie eine Eskalation sicher.'), ]); } return $items; } protected function filteredQuery(): Builder { return $this->baseQuery() ->when($this->status !== 'all', fn (Builder $query) => $query->where('status', $this->status)) ->when($this->channel !== 'all', fn (Builder $query) => $query->where('channel', $this->channel)); } protected function baseQuery(): Builder { return Transaction::query()->where('company_id', $this->company->id); } }; ?> @php /** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $transactions */ $transactions = $this->transactions; /** @var \App\Models\Transaction|null $selected */ $selected = $this->selectedTransaction; $metrics = $this->metrics; $statusOptions = $this->statusOptions; $channelOptions = $this->channelOptions; $counterpartyHistory = $this->counterpartyHistory; $recentAlerts = $this->recentAlerts; $actionChecklist = $this->actionChecklist; $checklistIntro = __('Fokusbereiche für diesen Fall'); $formatCurrency = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €'; $formatAmount = static fn (float $value, string $currency): string => number_format($value, 2, ',', '.') . ' ' . $currency; $statusStyles = [ Transaction::STATUS_TRUE_POSITIVE => 'bg-gradient-to-r from-rose-500/25 via-rose-500/10 to-rose-400/20 text-rose-700 dark:text-rose-200 border border-rose-500/30 shadow-[0_0_25px_-14px_rgba(244,63,94,0.85)]', Transaction::STATUS_FALSE_POSITIVE => 'bg-gradient-to-r from-amber-400/25 via-amber-400/10 to-amber-300/20 text-amber-700 dark:text-amber-200 border border-amber-400/30 shadow-[0_0_25px_-14px_rgba(251,191,36,0.85)]', Transaction::STATUS_CLEARED => 'bg-gradient-to-r from-emerald-400/25 via-emerald-400/10 to-emerald-300/20 text-emerald-700 dark:text-emerald-200 border border-emerald-400/30 shadow-[0_0_25px_-14px_rgba(52,211,153,0.85)]', ]; $statusCopy = [ Transaction::STATUS_TRUE_POSITIVE => __('Ein schwerwiegender Verdacht liegt vor. Priorisieren Sie die Eskalation und bereiten Sie eine Verdachtsmeldung vor.'), Transaction::STATUS_FALSE_POSITIVE => __('Alarm konnte entkräftet werden. Dokumentieren Sie die Begründung und schließen Sie den Fall.'), Transaction::STATUS_CLEARED => __('Keine Auffälligkeiten. Dokumentieren und archivieren Sie den Prüfschritt.'), ]; @endphp
{{ __('Transaktionen gesamt') }}
{{ $metrics['total_count'] }}
{{ __('Volumen') }} {{ $formatCurrency($metrics['total_volume']) }}
{{ __('Offene Alerts') }}
{{ $metrics['open_alerts']['count'] }}
{{ __('Volumen') }} {{ $formatCurrency($metrics['open_alerts']['amount']) }}
{{ __('High-Risk-Anteil') }}
{{ $metrics['high_risk_share'] }}%
{{ __('Letzte 30 Tage') }} {{ $metrics['last_30_days']['count'] }}
{{ __('Bearbeiten Sie die wichtigsten Schritte nacheinander, um den Fallabschluss vorzubereiten.') }}
{{ $item['title'] }}
{{ $item['description'] }}
{{ __('Filtern Sie nach Status und Kanal, um relevante Vorgänge für dieses Unternehmen zu analysieren.') }}
{{ $alert->counterparty }}
{{ $alert->executed_at?->format('d.m.Y H:i') }} • {{ $alert->channel }}
{{ __('Ausgeführt am') }} {{ $executedDate ?? __('Nicht verfügbar') }} @if ($executedTime) • {{ $executedTime }} @endif • {{ $selected->channel ?: __('Kanal offen') }}
{{ __('Stichtag') }}
{{ $executedDate ?? __('Nicht verfügbar') }}
{{ $executedTime ? __('Zeit') . ' ' . $executedTime : __('Zeitpunkt offen') }}
{{ __('Kanal & Referenz') }}
{{ $selected->channel ?: __('Unbekannt') }}
{{ $selected->reference ?: __('Keine Referenz hinterlegt') }}
{{ __('Flagging durch') }}
{{ $selected->flagged_by ?: __('Automatisiertes Monitoring') }}
{{ $selected->flagged_reason ?: __('Flagging-Grund nicht dokumentiert') }}
{{ __('Empfohlene Maßnahme') }}
{{ $statusGuidance }}
{{ __('Sending party') }}
{{ __('Unternehmensprofil') }}{{ $selected->company?->legal_name ?? $company->legal_name ?? $company->name }}
{{ __('Receiving party') }}
{{ __('Verifizierung offen') }}{{ $selected->counterparty }}
{{ __('Transaktionsbewertung') }}
{{ $riskNarrative }}
{{ __('Kontrollaufgaben (Zusammenfassung)') }}
{{ __('Sicherstellen, dass Rechnungskopf, Beneficiary-Daten und 3-Way-Match (PO–GR–Invoice) dokumentiert sind. Interne Listenabgleiche (Sanktionen/PEP) auf den konkreten Rechtsträger erneut ausführen.') }}
{{ __('Narrative Zusammenfassung') }}
{{ __('Die Transaktion verbindet :company (:jurisdiction) mit :counterparty (:country). Der Betrag von :amount wurde über den Kanal :channel ausgeführt. Für die abschließende AML-Beurteilung ist der Nachweis der wirtschaftlich Berechtigten sowie ein Abgleich der Zahlungsempfänger-Daten erforderlich.', [ 'company' => $company->legal_name ?? $company->name, 'jurisdiction' => $company->country ?? __('Jurisdiktion offen'), 'counterparty' => $selected->counterparty, 'country' => $selected->counterparty_country ?? __('Jurisdiktion offen'), 'amount' => $amountValue, 'channel' => $selected->channel ?: __('unbekannt'), ]) }}
{{ __('Hinweis: Interne Quellen (ERP, KYC-Stammdaten, Screening) ergänzen, um den Verifizierungsstatus auf „abgeschlossen“ anzuheben.') }}
{{ __('Verdichten Sie die Prüfung auf drei Themenblöcke. Jeder Block fasst die wesentlichen Anforderungen zusammen.') }}
{{ $cluster['lead'] }}
{{ $context['label'] }}
{{ $context['body'] }}
{{ __('Schlüsselmaßnahmen') }}
{{ $history->executed_at?->format('d.m.Y H:i') }}
{{ $history->channel }} • {{ $history->reference }}