*/ public array $editForm = []; public static function canAccess(): bool { $user = Auth::user(); return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin']) && Schema::hasTable('amministratori'); } public function mount(): void { $this->mountInteractsWithTable(); $defaultAmm = Amministratore::query()->where('id', 13)->first() ?: Amministratore::query()->first(); if ($defaultAmm) { $this->selectAmministratore($defaultAmm->id); } } public function selectAmministratore(int $id): void { $this->selectedAmministratoreId = $id; $amm = Amministratore::query()->with('user')->find($id); if ($amm) { $this->editForm = [ 'id' => (int) $amm->id, 'nome' => (string) ($amm->nome ?? ''), 'cognome' => (string) ($amm->cognome ?? ''), 'denominazione_studio' => (string) ($amm->denominazione_studio ?? ''), 'codice_fiscale_studio' => (string) ($amm->codice_fiscale_studio ?? ''), 'partita_iva' => (string) ($amm->partita_iva ?? ''), 'indirizzo_studio' => (string) ($amm->indirizzo_studio ?? ''), 'cap_studio' => (string) ($amm->cap_studio ?? ''), 'citta_studio' => (string) ($amm->citta_studio ?? ''), 'provincia_studio' => (string) ($amm->provincia_studio ?? ''), 'telefono_studio' => (string) ($amm->telefono_studio ?? ''), 'email_studio' => (string) ($amm->email_studio ?? ''), 'pec_studio' => (string) ($amm->pec_studio ?? ''), 'attivo' => (bool) ($amm->attivo ?? true), 'codice_univoco' => (string) ($amm->codice_univoco ?? '00000001'), 'user_email' => (string) ($amm->user?->email ?? $amm->email_studio ?? ''), ]; } } public function deselezionaAmministratore(): void { $this->selectedAmministratoreId = null; $this->editForm = []; } public function salvaSchedaInPlace(): void { if (! $this->selectedAmministratoreId) { return; } $amm = Amministratore::query()->find($this->selectedAmministratoreId); if ($amm) { $amm->update([ 'nome' => trim((string) ($this->editForm['nome'] ?? '')), 'cognome' => trim((string) ($this->editForm['cognome'] ?? '')), 'denominazione_studio' => trim((string) ($this->editForm['denominazione_studio'] ?? '')), 'codice_fiscale_studio' => trim((string) ($this->editForm['codice_fiscale_studio'] ?? '')), 'partita_iva' => trim((string) ($this->editForm['partita_iva'] ?? '')), 'indirizzo_studio' => trim((string) ($this->editForm['indirizzo_studio'] ?? '')), 'cap_studio' => trim((string) ($this->editForm['cap_studio'] ?? '')), 'citta_studio' => trim((string) ($this->editForm['citta_studio'] ?? '')), 'provincia_studio' => trim((string) ($this->editForm['provincia_studio'] ?? '')), 'telefono_studio' => trim((string) ($this->editForm['telefono_studio'] ?? '')), 'email_studio' => trim((string) ($this->editForm['email_studio'] ?? '')), 'pec_studio' => trim((string) ($this->editForm['pec_studio'] ?? '')), 'attivo' => (bool) ($this->editForm['attivo'] ?? true), ]); if ($amm->user && ! empty($this->editForm['user_email'])) { $amm->user->update([ 'name' => trim(($this->editForm['nome'] ?? '') . ' ' . ($this->editForm['cognome'] ?? '')), 'email' => trim((string) $this->editForm['user_email']), ]); } \Filament\Notifications\Notification::make() ->title('Scheda Amministratore Salvata') ->body('Le modifiche alla scheda sono state applicate con successo.') ->success() ->send(); } } public function impersonaSelected(): void { if (! $this->selectedAmministratoreId) { return; } $amm = Amministratore::query()->with('user')->find($this->selectedAmministratoreId); if ($amm && $amm->user) { session(['impersonator_id' => Auth::id()]); Auth::login($amm->user); $this->redirect('/admin-filament'); } } public function inviaCredenzialiSelected(): void { if (! $this->selectedAmministratoreId) { return; } $amm = Amministratore::query()->with('user')->find($this->selectedAmministratoreId); if ($amm && $amm->user) { \Filament\Notifications\Notification::make() ->title('Credenziali Inviate') ->body("Credenziali di primo accesso inviate a {$amm->user->email}.") ->success() ->send(); } } public function resetPasswordSelected(): void { if (! $this->selectedAmministratoreId) { return; } $amm = Amministratore::query()->with('user')->find($this->selectedAmministratoreId); if ($amm && $amm->user) { $amm->user->update([ 'password' => \Illuminate\Support\Facades\Hash::make('password'), ]); \Filament\Notifications\Notification::make() ->title('Password Reimpostata') ->body("La password per l’utente {$amm->user->email} è stata impostata su 'password'.") ->success() ->send(); } } protected function getTableQuery(): Builder { return Amministratore::query()->withCount('stabili'); } public function table(Table $table): Table { return $table ->striped() ->defaultSort('id', 'desc') ->columns([ TextColumn::make('id')->label('ID')->sortable(), TextColumn::make('codice_univoco')->label('Codice')->searchable()->sortable(), TextColumn::make('nome')->label('Nome')->searchable()->sortable(), TextColumn::make('cognome')->label('Cognome')->searchable()->sortable(), TextColumn::make('stabili_count')->label('Stabili')->sortable(), TextColumn::make('denominazione_studio')->label('Studio')->searchable()->toggleable(), TextColumn::make('email_studio')->label('Email')->searchable()->toggleable(isToggledHiddenByDefault: true), TextColumn::make('pec_studio')->label('PEC')->searchable()->toggleable(isToggledHiddenByDefault: true), IconColumn::make('attivo')->label('Attivo')->boolean()->sortable()->toggleable(), IconColumn::make('abilita_import_stabili')->label('Import')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true), TextColumn::make('fe_cassetto_status') ->label('FE Stato') ->state(function (Amministratore $record): string { if (! (bool) ($record->fe_cassetto_enabled ?? false)) { return 'Disabilitato'; } $expiresAt = $record->fe_cassetto_expires_at; if ($expiresAt instanceof Carbon && $expiresAt->startOfDay()->lt(now()->startOfDay())) { return 'Scaduto'; } return 'Attivo'; }) ->badge() ->color(function (Amministratore $record): string { if (! (bool) ($record->fe_cassetto_enabled ?? false)) { return 'gray'; } $expiresAt = $record->fe_cassetto_expires_at; if ($expiresAt instanceof Carbon && $expiresAt->startOfDay()->lt(now()->startOfDay())) { return 'danger'; } return 'success'; }) ->sortable(), IconColumn::make('fe_cassetto_enabled')->label('FE Abilitato')->boolean()->sortable()->toggleable(), TextColumn::make('fe_cassetto_expires_at')->label('FE Scadenza')->date('d/m/Y')->sortable()->toggleable(isToggledHiddenByDefault: true), TextColumn::make('updated_at')->label('Aggiornato')->dateTime('d/m/Y H:i')->toggleable(isToggledHiddenByDefault: true), ]) ->headerActions([ Action::make('create') ->label('Nuovo') ->icon('heroicon-o-plus') ->form([ Select::make('user_id') ->label('Utente collegato') ->options(fn() => User::query() ->whereDoesntHave('amministratore') ->orderBy('name') ->get() ->mapWithKeys(fn(User $u) => [ $u->id => trim($u->name . ' <' . $u->email . '>'), ]) ->all()) ->searchable() ->required(), TextInput::make('nome')->label('Nome')->required()->maxLength(100), TextInput::make('cognome')->label('Cognome')->required()->maxLength(100), TextInput::make('denominazione_studio')->label('Denominazione studio')->maxLength(255), TextInput::make('partita_iva')->label('Partita IVA')->maxLength(30), TextInput::make('codice_fiscale_studio')->label('Codice fiscale')->maxLength(30), TextInput::make('indirizzo_studio')->label('Indirizzo')->maxLength(255), TextInput::make('cap_studio')->label('CAP')->maxLength(10), TextInput::make('citta_studio')->label('Città')->maxLength(60), TextInput::make('provincia_studio')->label('Provincia')->maxLength(5), TextInput::make('telefono_studio')->label('Telefono')->maxLength(50), TextInput::make('email_studio')->label('Email')->maxLength(255), TextInput::make('pec_studio')->label('PEC')->maxLength(255), Toggle::make('attivo')->label('Attivo')->default(true), Toggle::make('abilita_import_stabili')->label('Abilita import stabili')->default(true), Toggle::make('fe_cassetto_enabled') ->label('Abilita servizio scarico Fatture (Cassetto Fiscale)') ->helperText('Servizio a pagamento: abilita solo per gli amministratori autorizzati.') ->default(false), DatePicker::make('fe_cassetto_expires_at') ->label('Scadenza servizio (opzionale)') ->helperText('Se valorizzata, dopo questa data lo scarico sarà bloccato finché non rinnovato.') ->native(false) ->displayFormat('d/m/Y') ->nullable(), ]) ->action(function (array $data): void { $amm = Amministratore::query()->create([ 'user_id' => (int) ($data['user_id'] ?? 0), 'nome' => trim((string) ($data['nome'] ?? '')), 'cognome' => trim((string) ($data['cognome'] ?? '')), 'denominazione_studio' => (string) ($data['denominazione_studio'] ?? ''), 'partita_iva' => (string) ($data['partita_iva'] ?? ''), 'codice_fiscale_studio' => (string) ($data['codice_fiscale_studio'] ?? ''), 'indirizzo_studio' => (string) ($data['indirizzo_studio'] ?? ''), 'cap_studio' => (string) ($data['cap_studio'] ?? ''), 'citta_studio' => (string) ($data['citta_studio'] ?? ''), 'provincia_studio' => (string) ($data['provincia_studio'] ?? ''), 'telefono_studio' => (string) ($data['telefono_studio'] ?? ''), 'email_studio' => (string) ($data['email_studio'] ?? ''), 'pec_studio' => (string) ($data['pec_studio'] ?? ''), 'attivo' => (bool) ($data['attivo'] ?? true), 'abilita_import_stabili' => (bool) ($data['abilita_import_stabili'] ?? true), 'fe_cassetto_enabled' => (bool) ($data['fe_cassetto_enabled'] ?? false), 'fe_cassetto_expires_at' => $data['fe_cassetto_expires_at'] ?? null, ]); // Best-effort: provisioning cartelle. try { $amm->provisionArchiveIfMissing(); } catch (\Throwable) { } }), ]) ->actions([ Action::make('select') ->label('Dettaglio Scheda') ->icon('heroicon-o-pencil-square') ->color('primary') ->action(function (Amministratore $record): void { $this->selectAmministratore($record->id); }), Action::make('edit') ->label('Modifica') ->icon('heroicon-o-pencil-square') ->form([ Select::make('user_id') ->label('Utente collegato') ->options(fn(Amministratore $record) => User::query() ->where(function (Builder $q) use ($record) { $q->whereDoesntHave('amministratore') ->orWhere('id', (int) $record->user_id); }) ->orderBy('name') ->get() ->mapWithKeys(fn(User $u) => [ $u->id => trim($u->name . ' <' . $u->email . '>'), ]) ->all()) ->searchable() ->required(), TextInput::make('nome')->label('Nome')->required()->maxLength(100), TextInput::make('cognome')->label('Cognome')->required()->maxLength(100), TextInput::make('denominazione_studio')->label('Denominazione studio')->maxLength(255), TextInput::make('partita_iva')->label('Partita IVA')->maxLength(30), TextInput::make('codice_fiscale_studio')->label('Codice fiscale')->maxLength(30), TextInput::make('indirizzo_studio')->label('Indirizzo')->maxLength(255), TextInput::make('cap_studio')->label('CAP')->maxLength(10), TextInput::make('citta_studio')->label('Città')->maxLength(60), TextInput::make('provincia_studio')->label('Provincia')->maxLength(5), TextInput::make('telefono_studio')->label('Telefono')->maxLength(50), TextInput::make('email_studio')->label('Email')->maxLength(255), TextInput::make('pec_studio')->label('PEC')->maxLength(255), Toggle::make('attivo')->label('Attivo'), Toggle::make('abilita_import_stabili')->label('Abilita import stabili'), Toggle::make('fe_cassetto_enabled') ->label('Abilita servizio scarico Fatture (Cassetto Fiscale)') ->helperText('Servizio a pagamento: abilita solo per gli amministratori autorizzati.'), DatePicker::make('fe_cassetto_expires_at') ->label('Scadenza servizio (opzionale)') ->native(false) ->displayFormat('d/m/Y') ->nullable(), ]) ->fillForm(fn(Amministratore $record): array => [ 'user_id' => (int) $record->user_id, 'nome' => (string) ($record->nome ?? ''), 'cognome' => (string) ($record->cognome ?? ''), 'denominazione_studio' => (string) ($record->denominazione_studio ?? ''), 'partita_iva' => (string) ($record->partita_iva ?? ''), 'codice_fiscale_studio' => (string) ($record->codice_fiscale_studio ?? ''), 'indirizzo_studio' => (string) ($record->indirizzo_studio ?? ''), 'cap_studio' => (string) ($record->cap_studio ?? ''), 'citta_studio' => (string) ($record->citta_studio ?? ''), 'provincia_studio' => (string) ($record->provincia_studio ?? ''), 'telefono_studio' => (string) ($record->telefono_studio ?? ''), 'email_studio' => (string) ($record->email_studio ?? ''), 'pec_studio' => (string) ($record->pec_studio ?? ''), 'attivo' => (bool) ($record->attivo ?? true), 'abilita_import_stabili' => (bool) ($record->abilita_import_stabili ?? true), 'fe_cassetto_enabled' => (bool) ($record->fe_cassetto_enabled ?? false), 'fe_cassetto_expires_at' => $record->fe_cassetto_expires_at?->format('Y-m-d'), ]) ->action(function (Amministratore $record, array $data): void { $update = [ 'user_id' => (int) ($data['user_id'] ?? 0), 'nome' => trim((string) ($data['nome'] ?? '')), 'cognome' => trim((string) ($data['cognome'] ?? '')), 'denominazione_studio' => (string) ($data['denominazione_studio'] ?? ''), 'partita_iva' => (string) ($data['partita_iva'] ?? ''), 'codice_fiscale_studio' => (string) ($data['codice_fiscale_studio'] ?? ''), 'indirizzo_studio' => (string) ($data['indirizzo_studio'] ?? ''), 'cap_studio' => (string) ($data['cap_studio'] ?? ''), 'citta_studio' => (string) ($data['citta_studio'] ?? ''), 'provincia_studio' => (string) ($data['provincia_studio'] ?? ''), 'telefono_studio' => (string) ($data['telefono_studio'] ?? ''), 'email_studio' => (string) ($data['email_studio'] ?? ''), 'pec_studio' => (string) ($data['pec_studio'] ?? ''), 'attivo' => (bool) ($data['attivo'] ?? true), 'abilita_import_stabili' => (bool) ($data['abilita_import_stabili'] ?? true), 'fe_cassetto_enabled' => (bool) ($data['fe_cassetto_enabled'] ?? false), 'fe_cassetto_expires_at' => $data['fe_cassetto_expires_at'] ?? null, ]; $record->fill($update); $record->save(); try { $record->provisionArchiveIfMissing(); } catch (\Throwable) { } }), Action::make('impersonate') ->label('Impersona') ->icon('heroicon-o-user-plus') ->color('warning') ->requiresConfirmation() ->modalHeading('Conferma Impersonificazione Amministratore') ->modalDescription('Stai per accedere al sistema impersonando lo studio dell’amministratore selezionato. Tutte le azioni saranno tracciate nell’audit log.') ->visible(fn(Amministratore $record): bool => (bool) $record->user_id && $record->user_id !== Auth::id()) ->action(function (Amministratore $record): void { if ($record->user) { session(['impersonator_id' => Auth::id()]); Auth::login($record->user); redirect()->to('/admin-filament'); } }), Action::make('delete') ->label('Elimina') ->icon('heroicon-o-trash') ->color('danger') ->requiresConfirmation() ->action(function (Amministratore $record): void { if ($record->stabili()->exists()) { throw new \RuntimeException('Impossibile eliminare: ci sono stabili collegati.'); } if ($record->fornitori()->exists()) { throw new \RuntimeException('Impossibile eliminare: ci sono fornitori collegati.'); } $record->delete(); }), ]); } }