> */ 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 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; } #[Computed] public function transactionDetailStructure(): array { return [ [ 'level' => 1, 'title' => 'Transaktion', 'sections' => [ [ 'title' => 'Analyse Transaktionsbetrag', 'description' => null, 'fields' => [ [ 'label' => 'Plausibilität Betrag', 'key' => 'tranx_TX_AMOUNT', 'prompt_id' => '51', ], [ 'label' => 'Plausibilität Transaktionszeitpunkt', 'key' => 'tranx_seasonality', 'prompt_id' => '59', ], [ 'label' => 'Plausibilität Transaktionsbetrag umsatzbezogen', 'key' => 'tranx_revenueimpact', 'prompt_id' => '67', ], [ 'label' => 'Plausibilität Transaktionsbetrag ergebnisbezogen', 'key' => 'tranx_profitimpact', 'prompt_id' => '68', ], [ 'label' => 'Plausibilität Transaktionsbetrag Branchüblichkeit', 'key' => 'tranx_turnoverimpact', 'prompt_id' => '69', ], ], ], [ 'title' => 'Analyse Transaktionszweck & außerbetragliche Parameter', 'description' => null, 'fields' => [ [ 'label' => 'Verwendungszweck', 'key' => 'tranx_TX_PURPOSE', 'prompt_id' => '53', ], [ 'label' => 'Plausibilität Zahlungszweck', 'key' => 'tranx_PURPOSEbase', 'prompt_id' => '57', ], [ 'label' => 'Plausibilität Geschäftsbeziehung', 'key' => 'tranx_businesslogic', 'prompt_id' => '62', ], [ 'label' => 'Plausibilität Transaktionsbetrag Branchenadäquanz', 'key' => 'tranx_plausibilty', 'prompt_id' => '64', ], [ 'label' => 'Bewertung', 'key' => 'tranx_report', 'prompt_id' => '56', ], [ 'label' => 'Plausibilität Transaktionsumfang', 'key' => 'tranx_performanceperiod', 'prompt_id' => '58', ], [ 'label' => 'Auffälligkeiten Transaktion', 'key' => 'tranx_pattern2', 'prompt_id' => '80', ], [ 'label' => 'Plausibilität Transaktion Marktüblichkeit', 'key' => 'tranx_contracttenor', 'prompt_id' => '83', ], [ 'label' => 'Transaktion Namensabgleich', 'key' => 'tranx_mismatch', 'prompt_id' => '85', ], [ 'label' => 'Titel fehlt', 'key' => 'tranx_outliers', 'prompt_id' => '65', ], [ 'label' => 'Plausibilität Instruktion Transaktion', 'key' => 'tranx_weekday', 'prompt_id' => '95', ], [ 'label' => 'Prüfung letztendliche wirtschaftliche Eigentümer', 'key' => 'tranx_holdingobfuscation', 'prompt_id' => '96', ], ], ], [ 'title' => 'Anzeige historischer Transaktionen mit Sender und/oder Empfänger aus der Transaktion inklusive der dokumentierten Auffälligkeiten zu AML pro Datensatz', 'description' => null, 'fields' => [ [ 'label' => 'Transaktionshistorie', 'key' => 'tranx_historical', 'prompt_id' => '52', ], [ 'label' => 'Smurfing', 'key' => 'tranx_duplicate', 'prompt_id' => '79', ], [ 'label' => 'Historische Transaktionen', 'key' => 'tranx_patterns', 'prompt_id' => '66', ], [ 'label' => 'Bankverbindung', 'key' => 'tranx_newbankaccount', 'prompt_id' => '93', ], ], ], [ 'title' => 'Zusätzliche Feststellungen (KYC/EDD)', 'description' => null, 'fields' => [ [ 'label' => 'Gegenpartei', 'key' => 'tranx_counterpartyassessment', 'prompt_id' => '54', ], ], ], [ 'title' => 'Export controls/trade restrictions', 'description' => null, 'fields' => [ [ 'label' => 'Sanktionen (Entity)', 'key' => 'entity_corporate_exportcontrol', 'prompt_id' => '81', ], [ 'label' => 'Sanktionen (Counterparty)', 'key' => 'counterparty_corporate_exportcontrol', 'prompt_id' => '81', ], ], ], ], ], [ 'level' => 2, 'title' => 'Stammdaten', 'sections' => [ [ 'title' => 'Rechtlicher Name (aktuell) / Firma', 'description' => null, 'fields' => [ [ 'label' => 'Name', 'key' => 'entity_corporate_name', 'prompt_id' => '9', ], [ 'label' => 'Name der Gegenpartei', 'key' => 'counterparty_corporate_name', 'prompt_id' => '9', ], [ 'label' => 'Warnmitteilung', 'key' => 'entity_corporate_warnings', 'prompt_id' => '42', ], [ 'label' => 'Warnmitteilung der Gegenpartei', 'key' => 'counterparty_corporate_warnings', 'prompt_id' => '42', ], ], ], [ 'title' => 'Rechtsform', 'description' => null, 'fields' => [ [ 'label' => 'Rechtsform', 'key' => 'entity_corporate_form', 'prompt_id' => '10', ], [ 'label' => 'Rechtsform d. Gegenpartei', 'key' => 'counterparty_corporate_form', 'prompt_id' => '10', ], ], ], [ 'title' => 'Registergericht & Nummer', 'description' => null, 'fields' => [ [ 'label' => 'Registernummer', 'key' => 'entity_corporate_forum', 'prompt_id' => '11', ], [ 'label' => 'Registernummer d. Gegenpartei', 'key' => 'counterparty_corporate_forum', 'prompt_id' => '11', ], ], ], [ 'title' => 'Sitz / eingetragene Adresse', 'description' => null, 'fields' => [ [ 'label' => 'Sitzadresse', 'key' => 'entity_corporate_HQ', 'prompt_id' => '12', ], [ 'label' => 'Sitzadresse d. Gegenpartei', 'key' => 'counterparty_corporate_HQ', 'prompt_id' => '12', ], [ 'label' => 'Korruption', 'key' => 'entity_corporate_corruptionexposure', 'prompt_id' => '48', ], [ 'label' => 'Korruption d. Gegenpartei', 'key' => 'counterparty_corporate_corruptionexposure', 'prompt_id' => '48', ], [ 'label' => 'Länderrisiko', 'key' => 'entity_corporate_CTYexposure', 'prompt_id' => '50', ], [ 'label' => 'Länderrisiko d. Gegenpartei', 'key' => 'counterparty_corporate_CTYexposure', 'prompt_id' => '50', ], [ 'label' => 'Korruption Land', 'key' => 'entity_corruption_country', 'prompt_id' => '74', ], [ 'label' => 'Korruption Land d. Gegenpartei', 'key' => 'counterparty_corruption_country', 'prompt_id' => '74', ], [ 'label' => 'Risikoländer', 'key' => 'entity_country_risk', 'prompt_id' => '86', ], [ 'label' => 'Risikoländer d. Gegenpartei', 'key' => 'counterparty_country_risk', 'prompt_id' => '86', ], ], ], [ 'title' => 'Handelsname/Marke', 'description' => null, 'fields' => [ [ 'label' => 'Handelsnamen', 'key' => 'entity_corporate_brands', 'prompt_id' => '34', ], [ 'label' => 'Handelsnamen d. Gegenpartei', 'key' => 'counterparty_corporate_brands', 'prompt_id' => '34', ], [ 'label' => 'Warnmitteilungen', 'key' => 'entity_corporate_warnings', 'prompt_id' => '42', ], [ 'label' => 'Warnmitteilungen d. Gegenpartei', 'key' => 'counterparty_corporate_warnings', 'prompt_id' => '42', ], ], ], [ 'title' => 'Summary Geschäftsmodell', 'description' => null, 'fields' => [ [ 'label' => 'Geschäft', 'key' => 'entity_corporate_purpose', 'prompt_id' => '3', ], [ 'label' => 'Geschäft d. Gegenpartei', 'key' => 'counterparty_corporate_purpose', 'prompt_id' => '3', ], ], ], [ 'title' => 'Brief History of the company', 'description' => null, 'fields' => [ [ 'label' => 'Historie', 'key' => 'entity_corporate_history', 'prompt_id' => '2', ], [ 'label' => 'Historie d. Gegenpartei', 'key' => 'counterparty_corporate_history', 'prompt_id' => '2', ], ], ], [ 'title' => 'IBAN/BIC der Firma (alle, Listenform)', 'description' => null, 'fields' => [ [ 'label' => 'IBAN / BIC', 'key' => 'entity_corporate_IBAN', 'prompt_id' => '37', ], [ 'label' => 'IBAN / BIC d. Gegenpartei', 'key' => 'counterparty_corporate_IBAN', 'prompt_id' => '37', ], ], ], [ 'title' => 'Mitarbeiteranzahl', 'description' => null, 'fields' => [ [ 'label' => 'Mitarbeiteranzahl', 'key' => 'entity_corporate_employeecount', 'prompt_id' => '22', ], [ 'label' => 'Mitarbeiteranzahl d. Gegenpartei', 'key' => 'counterparty_corporate_employeecount', 'prompt_id' => '22', ], ], ], [ 'title' => 'LEI (Legal Entity Identification Number)', 'description' => null, 'fields' => [ [ 'label' => 'LEI', 'key' => 'entity_corporate_LEI', 'prompt_id' => '20', ], [ 'label' => 'LEI d. Gegenpartei', 'key' => 'counterparty_corporate_LEI', 'prompt_id' => '20', ], ], ], [ 'title' => 'USt-IdNr. (VAT)', 'description' => null, 'fields' => [ [ 'label' => 'Umsatzsteuer-Identifikationsnummer', 'key' => 'entity_corporate_taxID', 'prompt_id' => '19', ], [ 'label' => 'Umsatzsteuer-Identifikationsnummer d. Gegenpartei', 'key' => 'counterparty_corporate_taxID', 'prompt_id' => '19', ], ], ], [ 'title' => 'Offizielle Website', 'description' => null, 'fields' => [ [ 'label' => 'Website', 'key' => 'entity_corporate_website', 'prompt_id' => '35', ], [ 'label' => 'Website d. Gegenpartei', 'key' => 'counterparty_corporate_website', 'prompt_id' => '35', ], ], ], [ 'title' => 'Domain(s) & WHOIS', 'description' => null, 'fields' => [ [ 'label' => 'Domains', 'key' => 'entity_corporate_domain', 'prompt_id' => '36', ], [ 'label' => 'Domains d. Gegenpartei', 'key' => 'counterparty_corporate_domain', 'prompt_id' => '36', ], ], ], ], ], ]; } 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'); $detailStructure = $this->transactionDetailStructure; $formatCurrency = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €'; $formatCurrencyCompact = static function (float $value): string { if ($value >= 1000000) { return number_format($value / 1000000, 0, ',', '.') . ' Mio. €'; } return 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') }} {{ $formatCurrencyCompact($metrics['total_volume']) }}
{{ __('Offene Alerts') }}
{{ $metrics['open_alerts']['count'] }}
{{ __('Volumen') }} {{ $formatCurrencyCompact($metrics['open_alerts']['amount']) }}
{{ __('High-Risk-Anteil') }}
{{ $metrics['high_risk_share'] }}%
{{ __('Letzte 30 Tage') }} {{ $metrics['last_30_days']['count'] }}
{{ __('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') }}
{{ $field['label'] }}
{{ $answer }}
{{ __('Quellen') }}:
{{ __('Nicht verfügbar') }}
@endif{{ __('Feld') }}: {{ $field['key'] }}
{{ __('Hinweis zur Datenstruktur') }}
{{ __('Diese Felder basieren auf der strukturierten Analyse-Konfiguration. Fehlende Werte können durch erweiterte Datenquellen oder manuelle Eingaben ergänzt werden.') }}
{{ $history->executed_at?->format('d.m.Y H:i') }}
{{ $history->channel }} • {{ $history->reference }}