System Resources
Vier Resources für die Systemkonfiguration:
- TariffRateResource: Tarifsätze verwalten
- SystemLanguageResource: Sprachen verwalten
- LegalDocumentResource: Rechtstexte verwalten
- ExportResource: Datenexporte anzeigen
TariffRateResource
File: TariffRateResource.php
Verwaltet die Tarifsätze für die Kostenberechnung.
Konfiguration
| Eigenschaft | Wert |
|---|---|
| Model | App\Models\TariffRate |
| Navigation Group | System |
| Navigation Label | Tarifsätze |
| Navigation Icon | heroicon-o-banknotes |
| Navigation Sort | 20 |
Tarif-Snapshot-System
important
Bei Auftragserstellung wird der aktuelle Tarif als Snapshot in der Order gespeichert. Nachträgliche Tarifänderungen beeinflussen bestehende Aufträge nicht.
// Bei Order-Erstellung
$order->tariff_snapshot = TariffRate::current()->toSnapshot();
// Snapshot-Struktur
[
'hourly_rate' => 45.00,
'kilometer_rate' => 0.30,
'minimum_billable_hours' => 1,
'failure_fee_amount' => 45.00,
'failure_fee_hours_threshold' => 24,
'effective_from' => '2024-01-01',
]
Form-Struktur
Tarif-Details
| Feld | Typ | Beschreibung |
|---|---|---|
| effective_from | DatePicker | Gültig ab (Stichtag) |
| is_active | Toggle | Aktiv/Inaktiv |
Kostensätze
| Feld | Typ | Suffix | Beschreibung |
|---|---|---|---|
| hourly_rate | TextInput | € | Stundensatz |
| kilometer_rate | TextInput | €/km | Kilometersatz |
Zusätzliche Bedingungen
| Feld | Typ | Beschreibung |
|---|---|---|
| minimum_billable_hours | TextInput | Mindeststunden pro Einsatz |
| payment_terms_days | TextInput | Zahlungsziel in Tagen |
| failure_fee_amount | TextInput | Ausfallgebühr in € |
| failure_fee_hours_threshold | TextInput | Stunden-Schwelle für Ausfallgebühr |
| failure_fee_notes | Textarea | Hinweise zur Ausfallgebühr |
| notes | Textarea | Allgemeine Notizen |
Table-Spalten
| Spalte | Beschreibung |
|---|---|
| effective_from | Gültig ab (Datum) |
| is_current | Icon: ✓ wenn aktuell gültiger Tarif |
| hourly_rate | Mit € Suffix |
| kilometer_rate | Mit €/km Suffix |
| minimum_billable_hours | Mindeststunden |
| payment_terms_days | Zahlungsziel |
| failure_fee | Ausfallgebühr (mit Tooltip für Schwelle) |
| is_active | Icon: ✓/✗ |
Aktueller Tarif ermitteln
// In TariffRate Model
public static function current(): ?self
{
return static::query()
->where('is_active', true)
->where('effective_from', '<=', now())
->orderBy('effective_from', 'desc')
->first();
}
// Verwendung
$currentRate = TariffRate::current();
$hourlyRate = $currentRate->hourly_rate; // 45.00
SystemLanguageResource
File: SystemLanguageResource.php
Verwaltet die verfügbaren Sprachen im System.
Konfiguration
| Eigenschaft | Wert |
|---|---|
| Model | App\Models\SystemLanguage |
| Navigation Group | System |
| Navigation Label | Sprachen |
| Navigation Icon | heroicon-o-language |
| Navigation Sort | 21 |
Navigation Badge
public static function getNavigationBadge(): ?string
{
return static::getModel()::count();
}
public static function getNavigationBadgeColor(): ?string
{
return 'primary';
}
Form-Struktur
| Feld | Typ | Validierung | Beschreibung |
|---|---|---|---|
| code | TextInput | required, regex:/^[a-z]3$/ | ISO-Code (2-3 Buchstaben) |
| name_de | TextInput | required | Deutscher Name |
| name_en | TextInput | required | Englischer Name |
| flag_emoji | TextInput | nullable | Flaggen-Emoji (🇩🇪) |
| is_active | Toggle | - | Aktiv |
| sort_order | TextInput | numeric | Sortierung |
Table-Spalten
| Spalte | Beschreibung |
|---|---|
| sort_order | # (Sortierung) |
| flag_emoji | Flagge |
| code | ISO-Code als Badge |
| name_de | Deutscher Name (bold) |
| name_en | Englischer Name (gray) |
| is_active | Icon: ✓/✗ |
Beispiel-Sprachen
| Code | name_de | name_en | Emoji |
|---|---|---|---|
| de | Deutsch | German | 🇩🇪 |
| en | Englisch | English | 🇬🇧 |
| ar | Arabisch | Arabic | 🇸🇦 |
| tr | Türkisch | Turkish | 🇹🇷 |
| fa | Persisch | Persian | 🇮🇷 |
| uk | Ukrainisch | Ukrainian | 🇺🇦 |
LegalDocumentResource
File: LegalDocumentResource.php
Verwaltet Rechtstexte (AGB, Datenschutz, etc.).
Konfiguration
| Eigenschaft | Wert |
|---|---|
| Model | App\Models\LegalDocument |
| Navigation Group | System |
| Navigation Label | Rechtstexte |
| Navigation Icon | heroicon-o-document-text |
| Navigation Sort | 22 |
Navigation Badge
public static function getNavigationBadge(): ?string
{
return static::getModel()::where('is_active', true)->count();
}
Form-Struktur
Dokument-Informationen
| Feld | Typ | Beschreibung |
|---|---|---|
| type | Select | Dokumenttyp |
| title | TextInput | Titel |
| description | Textarea | Beschreibung |
| acceptance_text | TextInput | Annahmetext (oder Standard) |
| effective_from | DatePicker | Gültig ab |
| version | TextInput | Versionsnummer |
Dokumenttypen
| Typ | Label |
|---|---|
| AGB | Allgemeine Geschäftsbedingungen |
| LICENSE | Lizenzvereinbarung |
| DATA_PROCESSING | Datenverarbeitungsvereinbarung |
| FRAMEWORK | Rahmenvertrag |
| PRIVACY | Datenschutzerklärung |
Dokument-Quelle
Forms\Components\FileUpload::make('document_path')
->label('PDF-Dokument')
->acceptedFileTypes(['application/pdf'])
->maxSize(10240) // 10 MB
->disk(config('filesystems.default')) // S3 oder public
->directory('legal-documents')
->visibility('private')
Forms\Components\TextInput::make('document_url')
->label('Oder: Externe URL')
->url()
->placeholder('https://...')
Zuweisungen
Forms\Components\CheckboxList::make('roles')
->label('Gilt für Rollen')
->options([
'requester' => 'Auftraggeber',
'interpreter' => 'Sprachmittler',
])
->required()
Table-Spalten
| Spalte | Beschreibung |
|---|---|
| type | Badge mit Typ-Farben |
| title | Dokumenttitel |
| version | Version als Badge |
| roles_display | Komma-getrennte Rollen |
| effective_from | Gültig ab |
| is_active | Icon: ✓/✗ |
| acceptances_count | Anzahl Annahmen |
Typ-Farben
Tables\Columns\TextColumn::make('type')
->badge()
->color(fn (string $state) => match ($state) {
'AGB' => 'primary',
'PRIVACY' => 'success',
'DATA_PROCESSING' => 'warning',
'LICENSE' => 'info',
'FRAMEWORK' => 'gray',
})
Actions
| Action | Beschreibung | Sichtbarkeit |
|---|---|---|
| view_document | Dokument in neuem Tab öffnen | Wenn Dokument existiert |
| delete | Löschen | Nur wenn acceptances_count === 0 |
Relation Manager: PendingUsersRelationManager
Zeigt Benutzer, die das Dokument noch nicht akzeptiert haben:
public function table(Table $table): Table
{
return $table
->query(function () {
$document = $this->getOwnerRecord();
return User::query()
->whereIn('role', $document->roles)
->whereDoesntHave('legalDocumentAcceptances', function ($q) use ($document) {
$q->where('legal_document_id', $document->id);
});
})
->columns([
Tables\Columns\TextColumn::make('full_name'),
Tables\Columns\TextColumn::make('email'),
Tables\Columns\TextColumn::make('role')->badge(),
]);
}
ExportResource
File: ExportResource.php
Zeigt alle durchgeführten Datenexporte (Read-Only).
Konfiguration
| Eigenschaft | Wert |
|---|---|
| Model | App\Models\Export |
| Navigation Group | System |
| Navigation Label | Exporte |
| Navigation Icon | heroicon-o-arrow-down-tray |
| Navigation Sort | 99 |
Berechtigungen
public static function canCreate(): bool
{
return false; // Exporte werden über andere Resources erstellt
}
public static function canEdit(Model $record): bool
{
return false;
}
Table-Spalten
| Spalte | Beschreibung |
|---|---|
| id | Export-ID |
| exporter | Export-Typ (Class-Basename) |
| user.name | Ersteller |
| total_rows | Gesamtzeilen |
| successful_rows | Erfolgreiche Zeilen (Badge success) |
| completed_at | Abschluss-Icon |
| completed_at | Abschlusszeit (since) |
Download-Action
Action::make('download')
->label('Herunterladen')
->icon('heroicon-o-arrow-down-tray')
->visible(fn (Export $record) => $record->completed_at !== null)
->action(function (Export $record) {
$disk = Storage::disk(config('filament.exports.disk', 'local'));
if ($disk->getAdapter() instanceof S3Adapter) {
// S3: Temporäre URL (5 Minuten)
return redirect($disk->temporaryUrl(
$record->file_path,
now()->addMinutes(5)
));
}
// Lokal: Stream-Download
return response()->download(
$disk->path($record->file_path),
$record->file_name
);
})
Verfügbare Exporter
| Exporter | Resource | Beschreibung |
|---|---|---|
| UserExporter | UserResource | Benutzer exportieren |
| OrderExporter | OrderResource | Aufträge exportieren |
| InvoiceExporter | - | Rechnungen exportieren |
Export erstellen (in anderen Resources)
// In UserResource Header Actions
ExportAction::make()
->exporter(UserExporter::class)
->formats([
ExportFormat::Xlsx,
ExportFormat::Csv,
])
->fileName(fn () => 'benutzer-' . now()->format('Y-m-d'))