Bestandskunden aus dem Altsystem zahlen per Ueberweisung; nach dem 04:30-Lauf fehlte bisher der gesamte Bearbeitungsweg (kein PDF, keine Mail, kein Zahlungseingang, kein Bestandsschutz-Ende). Plan und Befund: dev/lagacy-rechnungen/entwicklungsplan-2026-09-16.md. Bereinigung (legacy:settle-unpaid, wiederholbar): - Legacy-Status hold = nie bezahlt, ersatzlos erledigt. 9 daraus abgeleitete Bestandsschutz-Buendel werden gekuendigt, MAN-00001/00002 storniert. Archivzeilen bleiben erhalten, sind aber in Admin-Archiv und Kundenkonto standardmaessig ausgeblendet. - pending/reminder (recurring) = offene Vorgaenge: Periode auf den Leistungsbeginn der unbezahlten Rechnung zurueckgedreht und als MAN-Rechnung neu ausgestellt (Archiv-Status superseded, replaces_legacy_invoice_number). - pending/reminder (single) = abgeschrieben (uncollectible). - legacy:grandfather-subscriptions wertet den Zahlungsstatus jetzt selbst aus. Workflow in /admin/payments (ManualInvoiceDunningService, je Aktion mit Eintrag im Aktivitaets-Verlauf): - Rechnung senden: Mail mit PDF-Anhang, Bankverbindung aus billing.issuer, Hinweis auf Bestandsschutz-Ende; Zahlungsziel zaehlt ab Versand. - Erinnerung: gleiche Mail im Mahn-Ton, Nachfrist bei verstrichener Frist. - Bezahlt: Zahlungseingang laut Kontoauszug verbuchen. - Bestandsschutz beenden: Rechnung storniert (nie geloescht), Buendel gekuendigt, weitere offene MAN-Rechnungen des Kunden storniert, optionale Kundenmail. - Panel "Blockierte Vereinbarungen": faellige Posten ohne Rechnung (meist fehlende Rechnungsadresse) mit "Jetzt abrechnen" oder "Entfernen". Schema: invoices bekommt user_payment_option_id, sent_at, reminded_at, reminder_count, voided_at, void_reason, replaces_legacy_invoice_number (inkl. Backfill der bestehenden MAN-Rechnungen). PDF-Primitiven des Archiv-Renderers in den Trait DrawsPdf ausgelagert; MAN-PDF on-demand ueber admin.invoices.pdf / me.invoices.manual-pdf (InvoicePolicy). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
133 lines
8.4 KiB
PHP
133 lines
8.4 KiB
PHP
<?php
|
|
|
|
use App\Http\Controllers\Admin\DashboardController;
|
|
use App\Http\Controllers\Admin\LeaveImpersonationController;
|
|
use App\Http\Controllers\InvoicePdfController;
|
|
use App\Http\Controllers\LegacyInvoicePdfController;
|
|
use App\Http\Middleware\BlockAdminDuringImpersonation;
|
|
use App\Http\Middleware\EnsureUserIsAdmin;
|
|
use App\Http\Middleware\LogSlowAdminRequests;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Livewire\Volt\Volt;
|
|
|
|
// Hinweis: Die Root-Route (`/`) auf der Portal-Domain rendert seit dem
|
|
// Hub-Launch die öffentliche Publisher-Landing (web/pressekonto.blade.php).
|
|
// Eingeloggte Admins werden über die Auth-Pipeline direkt nach /dashboard
|
|
// geleitet (siehe FORTIFY_HOME / LoginResponseContract).
|
|
Route::get('dashboard', DashboardController::class)
|
|
->middleware(['auth', 'verified', BlockAdminDuringImpersonation::class, EnsureUserIsAdmin::class, LogSlowAdminRequests::class])
|
|
->name('dashboard');
|
|
|
|
// ========================================
|
|
// Nutzer-eigene Einstellungen (kein Admin erforderlich)
|
|
// ========================================
|
|
Route::middleware(['auth', 'verified'])->group(function () {
|
|
Route::redirect('settings', '/admin/me/profile');
|
|
Route::redirect('settings/profile', '/admin/me/profile')->name('settings.profile');
|
|
Volt::route('settings/password', 'settings.password')->name('settings.password');
|
|
Volt::route('settings/appearance', 'settings.appearance')->name('settings.appearance');
|
|
});
|
|
|
|
Route::post('admin/impersonate/leave', LeaveImpersonationController::class)
|
|
->middleware(['auth', 'throttle:10,1'])
|
|
->name('admin.impersonate.leave');
|
|
|
|
// ========================================
|
|
// Admin-Bereich (nur Rollen admin/editor)
|
|
// ========================================
|
|
Route::middleware(['auth', 'verified', BlockAdminDuringImpersonation::class, EnsureUserIsAdmin::class, LogSlowAdminRequests::class])->group(function () {
|
|
|
|
// Content Management
|
|
Volt::route('admin/press-releases', 'admin.press-releases.index')->name('admin.press-releases.index');
|
|
Volt::route('admin/press-releases/create', 'admin.press-releases.create')->name('admin.press-releases.create');
|
|
Volt::route('admin/press-releases/{id}', 'admin.press-releases.show')->name('admin.press-releases.show');
|
|
Volt::route('admin/press-releases/{id}/edit', 'admin.press-releases.edit')->name('admin.press-releases.edit');
|
|
|
|
// Rubriken-Taxonomie: portalweite Struktur — nur admin (Befund H2).
|
|
Route::middleware('can:categories:manage')->group(function (): void {
|
|
Volt::route('admin/categories', 'admin.categories.index')->name('admin.categories.index');
|
|
Volt::route('admin/categories/create', 'admin.categories.create')->name('admin.categories.create');
|
|
Volt::route('admin/categories/{id}/edit', 'admin.categories.edit')->name('admin.categories.edit');
|
|
});
|
|
|
|
Route::middleware('can:footer-codes:manage')->group(function (): void {
|
|
Volt::route('admin/footer-codes', 'admin.footer-codes.index')->name('admin.footer-codes.index');
|
|
Volt::route('admin/footer-codes/create', 'admin.footer-codes.create')->name('admin.footer-codes.create');
|
|
Volt::route('admin/footer-codes/{id}/edit', 'admin.footer-codes.edit')->name('admin.footer-codes.edit');
|
|
});
|
|
|
|
// CRM
|
|
// Lesen (companies:read) auch für Editoren; Schreiben nur admin (companies:write, Befund H2).
|
|
Volt::route('admin/companies', 'admin.companies.index')->name('admin.companies.index');
|
|
Volt::route('admin/companies/{id}', 'admin.companies.show')->where('id', '[0-9]+')->name('admin.companies.show');
|
|
Volt::route('admin/contacts', 'admin.contacts.index')->name('admin.contacts.index');
|
|
|
|
Route::middleware('can:companies:write')->group(function (): void {
|
|
Volt::route('admin/companies/create', 'admin.companies.create')->name('admin.companies.create');
|
|
Volt::route('admin/companies/{id}/edit', 'admin.companies.edit')->name('admin.companies.edit');
|
|
Volt::route('admin/companies/{companyId}/contacts/create', 'admin.contacts.create')->name('admin.companies.contacts.create');
|
|
Volt::route('admin/contacts/create', 'admin.contacts.create')->name('admin.contacts.create');
|
|
Volt::route('admin/contacts/{id}/edit', 'admin.contacts.edit')->name('admin.contacts.edit');
|
|
});
|
|
|
|
// Recht & Compliance (WS-3): Queue für DSGVO-/Persönlichkeitsrechts-/Meldungen
|
|
// — sensible Vorgänge, nur admin (Befund H2).
|
|
Route::middleware('can:compliance:manage')->group(function (): void {
|
|
Volt::route('admin/legal-requests', 'admin.legal-requests.index')->name('admin.legal-requests.index');
|
|
Volt::route('admin/legal-requests/{id}', 'admin.legal-requests.show')->name('admin.legal-requests.show');
|
|
|
|
// Kontakt: Queue für Anliegen aus dem Kontakt-Widget (Kontaktaufnahme,
|
|
// Fehlermeldung, PM-Meldung, Feedback)
|
|
Volt::route('admin/contact-messages', 'admin.contact-messages.index')->name('admin.contact-messages.index');
|
|
Volt::route('admin/contact-messages/{id}', 'admin.contact-messages.show')->name('admin.contact-messages.show');
|
|
});
|
|
|
|
// Billing: Rechnungen, Zahlungen, Tarife, Gutscheine — nur admin (Befund H2).
|
|
Route::middleware('can:billing:manage')->group(function (): void {
|
|
Volt::route('admin/invoices', 'admin.invoices.index')->name('admin.invoices.index');
|
|
Route::get('admin/legacy-invoices/{legacyInvoice}/pdf', LegacyInvoicePdfController::class)->name('admin.legacy-invoices.pdf');
|
|
Route::get('admin/invoices/{invoice}/pdf', InvoicePdfController::class)->name('admin.invoices.pdf');
|
|
Volt::route('admin/payments', 'admin.payments.index')->name('admin.payments.index');
|
|
Volt::route('admin/payments/plans', 'admin.payments.plans')->name('admin.payments.plans');
|
|
Volt::route('admin/coupons', 'admin.coupons.index')->name('admin.coupons.index');
|
|
Volt::route('admin/newsletter-sync', 'admin.newsletter.sync')->name('admin.newsletter.sync');
|
|
});
|
|
|
|
// Administration
|
|
Volt::route('admin/presets', 'admin.presets.index')->name('admin.presets.index');
|
|
Volt::route('admin/presets/create', 'admin.presets.create')->name('admin.presets.create');
|
|
Volt::route('admin/presets/{id}/edit', 'admin.presets.edit')->name('admin.presets.edit');
|
|
|
|
Volt::route('admin/users', 'admin.users')->name('admin.users.index');
|
|
Volt::route('admin/users/create', 'admin.users.create')->name('admin.users.create');
|
|
Volt::route('admin/users/{id}', 'admin.users.show')->name('admin.users.show');
|
|
Volt::route('admin/users/{id}/edit', 'admin.users.edit')->name('admin.users.edit');
|
|
|
|
// Interner Newsletter: Kampagnen an Bestands-User (Filter + Protokoll).
|
|
// Massenversand an die User-Datenbank → nur mit users:manage (admin).
|
|
Route::middleware('can:users:manage')->group(function (): void {
|
|
// Mail-Sperrliste (Bounce-Suppression, Roadmap 1b.6)
|
|
Volt::route('admin/mail/sperrliste', 'admin.mail.suppressions')->name('admin.mail.suppressions');
|
|
Volt::route('admin/newsletter', 'admin.newsletter.campaigns.index')->name('admin.newsletter.campaigns.index');
|
|
Volt::route('admin/newsletter/abmeldungen', 'admin.newsletter.unsubscribes')->name('admin.newsletter.unsubscribes');
|
|
Volt::route('admin/newsletter/create', 'admin.newsletter.campaigns.edit')->name('admin.newsletter.campaigns.create');
|
|
Volt::route('admin/newsletter/{id}', 'admin.newsletter.campaigns.show')->where('id', '[0-9]+')->name('admin.newsletter.campaigns.show');
|
|
Volt::route('admin/newsletter/{id}/edit', 'admin.newsletter.campaigns.edit')->where('id', '[0-9]+')->name('admin.newsletter.campaigns.edit');
|
|
});
|
|
|
|
Volt::route('admin/roles', 'admin.roles.index')->name('admin.roles.index');
|
|
Volt::route('admin/roles/create', 'admin.roles.create')->name('admin.roles.create');
|
|
Volt::route('admin/roles/{id}/edit', 'admin.roles.edit')->name('admin.roles.edit');
|
|
|
|
// Reports
|
|
Volt::route('admin/reports/activity', 'admin.reports.activity')->name('admin.reports.activity');
|
|
Volt::route('admin/reports/slow-requests', 'admin.reports.slow-requests')->name('admin.reports.slow-requests');
|
|
|
|
// Einlieferer-Bericht: unzustellbare Kontaktdaten und abgelehnte
|
|
// Meldungen je API-Einlieferer (Kontakterreichbarkeit).
|
|
Volt::route('admin/reports/einlieferer', 'admin.reports.submitter-contacts')->name('admin.reports.submitter-contacts');
|
|
|
|
Route::middleware('can:settings:manage')->group(function (): void {
|
|
Volt::route('admin/reports/system-health', 'admin.reports.system-health')->name('admin.reports.system-health');
|
|
});
|
|
});
|