Zum Hauptinhalt springen

System Resources

Vier Resources für die Systemkonfiguration:

  1. TariffRateResource: Tarifsätze verwalten
  2. SystemLanguageResource: Sprachen verwalten
  3. LegalDocumentResource: Rechtstexte verwalten
  4. ExportResource: Datenexporte anzeigen

TariffRateResource

File: TariffRateResource.php

Verwaltet die Tarifsätze für die Kostenberechnung.

Konfiguration

EigenschaftWert
ModelApp\Models\TariffRate
Navigation GroupSystem
Navigation LabelTarifsätze
Navigation Iconheroicon-o-banknotes
Navigation Sort20

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

FeldTypBeschreibung
effective_fromDatePickerGültig ab (Stichtag)
is_activeToggleAktiv/Inaktiv

Kostensätze

FeldTypSuffixBeschreibung
hourly_rateTextInputStundensatz
kilometer_rateTextInput€/kmKilometersatz

Zusätzliche Bedingungen

FeldTypBeschreibung
minimum_billable_hoursTextInputMindeststunden pro Einsatz
payment_terms_daysTextInputZahlungsziel in Tagen
failure_fee_amountTextInputAusfallgebühr in €
failure_fee_hours_thresholdTextInputStunden-Schwelle für Ausfallgebühr
failure_fee_notesTextareaHinweise zur Ausfallgebühr
notesTextareaAllgemeine Notizen

Table-Spalten

SpalteBeschreibung
effective_fromGültig ab (Datum)
is_currentIcon: ✓ wenn aktuell gültiger Tarif
hourly_rateMit € Suffix
kilometer_rateMit €/km Suffix
minimum_billable_hoursMindeststunden
payment_terms_daysZahlungsziel
failure_feeAusfallgebühr (mit Tooltip für Schwelle)
is_activeIcon: ✓/✗

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

EigenschaftWert
ModelApp\Models\SystemLanguage
Navigation GroupSystem
Navigation LabelSprachen
Navigation Iconheroicon-o-language
Navigation Sort21
public static function getNavigationBadge(): ?string
{
return static::getModel()::count();
}

public static function getNavigationBadgeColor(): ?string
{
return 'primary';
}

Form-Struktur

FeldTypValidierungBeschreibung
codeTextInputrequired, regex:/^[a-z]3$/ISO-Code (2-3 Buchstaben)
name_deTextInputrequiredDeutscher Name
name_enTextInputrequiredEnglischer Name
flag_emojiTextInputnullableFlaggen-Emoji (🇩🇪)
is_activeToggle-Aktiv
sort_orderTextInputnumericSortierung

Table-Spalten

SpalteBeschreibung
sort_order# (Sortierung)
flag_emojiFlagge
codeISO-Code als Badge
name_deDeutscher Name (bold)
name_enEnglischer Name (gray)
is_activeIcon: ✓/✗

Beispiel-Sprachen

Codename_dename_enEmoji
deDeutschGerman🇩🇪
enEnglischEnglish🇬🇧
arArabischArabic🇸🇦
trTürkischTurkish🇹🇷
faPersischPersian🇮🇷
ukUkrainischUkrainian🇺🇦

LegalDocumentResource

File: LegalDocumentResource.php

Verwaltet Rechtstexte (AGB, Datenschutz, etc.).

Konfiguration

EigenschaftWert
ModelApp\Models\LegalDocument
Navigation GroupSystem
Navigation LabelRechtstexte
Navigation Iconheroicon-o-document-text
Navigation Sort22
public static function getNavigationBadge(): ?string
{
return static::getModel()::where('is_active', true)->count();
}

Form-Struktur

Dokument-Informationen

FeldTypBeschreibung
typeSelectDokumenttyp
titleTextInputTitel
descriptionTextareaBeschreibung
acceptance_textTextInputAnnahmetext (oder Standard)
effective_fromDatePickerGültig ab
versionTextInputVersionsnummer

Dokumenttypen

TypLabel
AGBAllgemeine Geschäftsbedingungen
LICENSELizenzvereinbarung
DATA_PROCESSINGDatenverarbeitungsvereinbarung
FRAMEWORKRahmenvertrag
PRIVACYDatenschutzerklä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

SpalteBeschreibung
typeBadge mit Typ-Farben
titleDokumenttitel
versionVersion als Badge
roles_displayKomma-getrennte Rollen
effective_fromGültig ab
is_activeIcon: ✓/✗
acceptances_countAnzahl 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

ActionBeschreibungSichtbarkeit
view_documentDokument in neuem Tab öffnenWenn Dokument existiert
deleteLöschenNur 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

EigenschaftWert
ModelApp\Models\Export
Navigation GroupSystem
Navigation LabelExporte
Navigation Iconheroicon-o-arrow-down-tray
Navigation Sort99

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

SpalteBeschreibung
idExport-ID
exporterExport-Typ (Class-Basename)
user.nameErsteller
total_rowsGesamtzeilen
successful_rowsErfolgreiche Zeilen (Badge success)
completed_atAbschluss-Icon
completed_atAbschlusszeit (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

ExporterResourceBeschreibung
UserExporterUserResourceBenutzer exportieren
OrderExporterOrderResourceAufträ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'))