netgescon-day0/app/Filament/Pages/Gescon/StabiliArchivio.php

836 lines
41 KiB
PHP

<?php
namespace App\Filament\Pages\Gescon;
use App\Filament\Pages\Condomini\CruscottoStabile;
use App\Models\Amministratore;
use App\Models\Palazzina;
use App\Models\Persona;
use App\Models\PersonaUnitaRelazione;
use App\Models\PianoContiCondominio;
use App\Models\Stabile;
use App\Models\StabileAmministratoreTransfer;
use App\Models\UnitaImmobiliare;
use App\Models\User;
use App\Services\GesconImport\EssentialImportService;
use App\Services\Stabili\StabileTransferService;
use App\Support\StabileContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Schema as SchemaFacade;
use Illuminate\Support\Str;
use UnitEnum;
class StabiliArchivio extends Page implements HasTable
{
use InteractsWithTable;
protected static ?string $navigationLabel = 'Elenco stabili';
protected static ?string $title = 'Elenco stabili';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-building-office';
protected static UnitEnum|string|null $navigationGroup = 'Stabile';
protected static ?int $navigationSort = 11;
protected static ?string $slug = 'gescon/anagrafica/stabili';
protected string $view = 'filament.pages.gescon.table';
public static function canAccess(): bool
{
$user = Auth::user();
return $user instanceof User
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
}
public function mount(): void
{
$this->mountInteractsWithTable();
}
protected function getTableQuery(): Builder
{
$user = Auth::user();
if (! $user instanceof User) {
return Stabile::query()->whereRaw('1 = 0');
}
if ($user->hasAnyRole(['super-admin'])) {
return Stabile::query();
}
$amm = $user->amministratore;
if ($amm) {
return Stabile::query()->where('amministratore_id', $amm->id);
}
$allowedIds = StabileContext::accessibleStabili($user)
->pluck('id')
->map(fn($v) => (int) $v)
->values();
if ($allowedIds->isEmpty()) {
return Stabile::query()->whereRaw('1 = 0');
}
return Stabile::query()->whereIn('id', $allowedIds);
}
public function table(Table $table): Table
{
return $table
->striped()
->defaultSort('denominazione')
->headerActions([
Action::make('sync_all_gescon')
->label('Sync GESCON (tutti)')
->icon('heroicon-o-arrow-path')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->can('gescon-import.execute');
})
->form([
TextInput::make('path')
->label('Percorso archivio GESCON')
->helperText('Cartella base che contiene /<codice_stabile>/... e /dbc/Stabili.mdb')
->default('/mnt/gescon-archives/gescon')
->maxLength(500),
TextInput::make('anno')
->label('Anno gestione (opzionale)')
->helperText('Inserisci un anno (es. 2025) o "all" per tutte le cartelle.'),
Checkbox::make('dry')->label('Dry-run (solo simulazione)')->default(false),
Checkbox::make('with_unita')->label('Includi unità immobiliari')->default(false),
Checkbox::make('with_soggetti')->label('Includi anagrafiche (soggetti)')->default(false),
Checkbox::make('with_diritti')->label('Includi diritti (proprietà/inquilini)')->default(false),
Checkbox::make('with_millesimi')->label('Includi tabelle millesimali')->default(false),
Checkbox::make('with_voci')->label('Includi voci (piano dei conti / voci spesa)')->default(false),
Checkbox::make('with_gestioni')->label('Includi gestioni')->default(false),
Checkbox::make('with_banche')->label('Includi banche')->default(false),
])
->requiresConfirmation()
->action(function (array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
return;
}
$amm = $user->amministratore;
if (! $amm) {
Notification::make()
->title('Amministratore non associato')
->body('Impossibile eseguire sync senza amministratore associato all\'utente.')
->danger()
->send();
return;
}
$options = [
'path' => (string) ($data['path'] ?? '/mnt/gescon-archives/gescon'),
'anno' => isset($data['anno']) && trim((string) $data['anno']) !== '' ? trim((string) $data['anno']) : null,
'with_unita' => (bool) ($data['with_unita'] ?? false),
'with_soggetti' => (bool) ($data['with_soggetti'] ?? false),
'with_diritti' => (bool) ($data['with_diritti'] ?? false),
'with_millesimi' => (bool) ($data['with_millesimi'] ?? false),
'with_voci' => (bool) ($data['with_voci'] ?? false),
'with_gestioni' => (bool) ($data['with_gestioni'] ?? false),
'with_banche' => (bool) ($data['with_banche'] ?? false),
'dry' => (bool) ($data['dry'] ?? false),
];
$query = $this->getTableQuery();
$stabili = $query->get();
$svc = new EssentialImportService();
foreach ($stabili as $record) {
$legacyCode = trim((string) ($record->codice_stabile ?? ''));
if ($legacyCode === '') {
$legacyCode = trim((string) ($record->codice_interno ?? ''));
}
if ($legacyCode === '') {
continue;
}
$svc->syncStabile($amm, $legacyCode, $options);
}
Notification::make()
->title('Sync completato')
->body('Sincronizzati tutti gli stabili visibili.')
->success()
->send();
}),
Action::make('crea_stabile')
->label('Crea stabile')
->icon('heroicon-o-plus')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore']);
})
->form([
TextInput::make('codice_stabile')
->label('Codice stabile (cartella legacy)')
->helperText('Esempio: 0021. Serve per collegare la cartella in /mnt/gescon-archives/gescon')
->required()
->maxLength(20),
TextInput::make('denominazione')
->label('Denominazione')
->required()
->maxLength(255),
TextInput::make('indirizzo')
->label('Indirizzo')
->maxLength(255),
TextInput::make('cap')
->label('CAP')
->maxLength(10),
TextInput::make('citta')
->label('Città')
->maxLength(100),
TextInput::make('provincia')
->label('Provincia')
->maxLength(5),
Checkbox::make('entra_subito')
->label('Entra subito nello stabile')
->default(true),
])
->action(function (array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
Notification::make()->title('Utente non valido')->danger()->send();
return;
}
$amm = $user->amministratore;
if (! $amm) {
$amm = Amministratore::query()->orderBy('id')->first();
}
if (! $amm) {
Notification::make()
->title('Nessun amministratore disponibile')
->body('Serve almeno un record in tabella amministratori per creare uno stabile.')
->danger()
->send();
return;
}
$codice = trim((string) ($data['codice_stabile'] ?? ''));
if ($codice === '') {
Notification::make()->title('Codice mancante')->danger()->send();
return;
}
if (Stabile::query()->where('codice_stabile', $codice)->exists()) {
Notification::make()
->title('Codice già presente')
->body('Esiste già uno stabile con codice ' . $codice)
->danger()
->send();
return;
}
$stabile = Stabile::query()->create([
'amministratore_id' => $amm->id,
'codice_stabile' => $codice,
'denominazione' => (string) ($data['denominazione'] ?? $codice),
'indirizzo' => (string) ($data['indirizzo'] ?? ''),
'cap' => (string) ($data['cap'] ?? ''),
'citta' => (string) ($data['citta'] ?? ''),
'provincia' => (string) ($data['provincia'] ?? ''),
'stato' => 'attivo',
'attivo' => true,
]);
Notification::make()
->title('Stabile creato')
->body('Codice: ' . $codice)
->success()
->send();
StabileContext::setActiveStabileId($user, (int) $stabile->id);
if (! empty($data['entra_subito'])) {
$this->redirect(\App\Filament\Pages\Condomini\CruscottoStabile::getUrl(panel: 'admin-filament'));
}
}),
Action::make('crea_demo')
->label('Crea stabile demo')
->icon('heroicon-o-sparkles')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin']);
})
->form([
TextInput::make('denominazione')
->label('Denominazione')
->default('Condominio Demo')
->required(),
Checkbox::make('entra_subito')
->label('Entra subito nello stabile')
->default(true),
])
->requiresConfirmation()
->action(function (array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
Notification::make()->title('Utente non valido')->danger()->send();
return;
}
$amm = $user->amministratore;
if (! $amm) {
$amm = Amministratore::query()->orderBy('id')->first();
}
if (! $amm) {
Notification::make()
->title('Nessun amministratore disponibile')
->body('Serve almeno un record in tabella amministratori per creare uno stabile.')
->danger()
->send();
return;
}
$codice = 'DEMO' . Str::upper(Str::random(4));
while (Stabile::query()->where('codice_stabile', $codice)->exists()) {
$codice = 'DEMO' . Str::upper(Str::random(4));
}
$stabile = Stabile::query()->create([
'amministratore_id' => $amm->id,
'codice_stabile' => $codice,
'denominazione' => (string) ($data['denominazione'] ?? 'Condominio Demo'),
'indirizzo' => 'Via Demo 1',
'cap' => '00000',
'citta' => 'Demo',
'provincia' => 'RM',
'stato' => 'attivo',
'attivo' => true,
]);
$palazzina = Palazzina::query()->create([
'stabile_id' => $stabile->id,
'codice_palazzina' => 'A',
'denominazione' => 'Palazzina A',
'numero_scale' => 1,
'numero_piani_fuori_terra' => 3,
'numero_piani_interrati' => 0,
'ha_piano_terra' => true,
'appartamenti_per_piano' => 2,
'attiva' => true,
'created_by' => $user->id,
]);
$unita = UnitaImmobiliare::query()->create([
'stabile_id' => $stabile->id,
'palazzina_id' => $palazzina->id,
'codice_unita' => 'A-1-0-01',
'palazzina' => 'A',
'scala' => '1',
'piano' => 0,
'interno' => '01',
'denominazione' => 'Unità Demo',
'tipo_unita' => 'appartamento',
'attiva' => true,
'unita_demo' => true,
'created_by' => $user->id,
]);
$persona = Persona::query()->create([
'tipologia' => 'fisica',
'cognome' => 'Demo',
'nome' => 'Condomino',
'codice_fiscale' => null,
'telefono_principale' => null,
'email_principale' => null,
'attivo' => true,
]);
PersonaUnitaRelazione::query()->create([
'persona_id' => $persona->id,
'unita_id' => $unita->id,
'tipo_relazione' => 'proprietario',
'quota_relazione' => 100,
'attivo' => true,
'riceve_comunicazioni' => true,
'riceve_convocazioni' => true,
'vota_assemblea' => true,
]);
Notification::make()
->title('Stabile demo creato')
->body('Codice: ' . $codice)
->success()
->send();
StabileContext::setActiveStabileId($user, (int) $stabile->id);
if (! empty($data['entra_subito'])) {
$this->redirect(CruscottoStabile::getUrl(panel: 'admin-filament'));
}
}),
])
->columns([
TextColumn::make('codice_stabile')
->label('Codice stabile')
->searchable()
->sortable()
->toggleable(),
TextColumn::make('cod_stabile')
->label('Codice mnemonico')
->searchable()
->sortable()
->toggleable(),
TextColumn::make('denominazione')
->label('Denominazione')
->searchable()
->sortable()
->wrap(),
TextColumn::make('amministratore_corrente')
->label('Amministratore')
->getStateUsing(function (Stabile $record): string {
$amministratore = $record->amministratore;
if (! $amministratore instanceof Amministratore) {
return '-';
}
$label = trim((string) ($amministratore->denominazione_studio ?: $amministratore->nome_completo));
if ($label === '') {
$label = 'Amministratore';
}
return $label . ' (ID ' . (int) $amministratore->id . ')';
})
->searchable(false)
->toggleable()
->wrap(),
TextColumn::make('indirizzo_completo')
->label('Indirizzo')
->toggleable()
->wrap(),
TextColumn::make('stato')
->label('Stato')
->sortable()
->toggleable(),
IconColumn::make('attivo')
->label('Attivo')
->boolean()
->toggleable(),
TextColumn::make('updated_at')
->label('Aggiornato')
->dateTime('d/m/Y H:i')
->sortable()
->toggleable(),
])
->actions([
Action::make('scheda')
->label('Scheda')
->icon('heroicon-o-identification')
->url(fn(Stabile $record) => StabileScheda::getUrl([
'record' => (int) $record->id,
], panel: 'admin-filament')),
Action::make('modifica')
->label('Modifica')
->icon('heroicon-o-pencil-square')
->visible(function (Stabile $record): bool {
$user = Auth::user();
if (! $user instanceof User) {
return false;
}
if ($user->hasAnyRole(['super-admin', 'admin'])) {
return true;
}
// Amministratore: può modificare solo i propri.
$ammId = (int) ($user->amministratore?->id ?? 0);
return $ammId > 0 && (int) ($record->amministratore_id ?? 0) === $ammId;
})
->form([
TextInput::make('codice_stabile')
->label('Codice stabile (legacy)')
->helperText('Codice cartella GESCON (es. 0021).')
->required()
->maxLength(20),
TextInput::make('cod_stabile')
->label('Codice operatore (mnemonico)')
->helperText('Codice breve mostrato in Filament (es. 148).')
->maxLength(20),
TextInput::make('denominazione')->label('Denominazione')->required()->maxLength(255),
TextInput::make('indirizzo')->label('Indirizzo')->maxLength(255),
TextInput::make('cap')->label('CAP')->maxLength(10),
TextInput::make('citta')->label('Città')->maxLength(100),
TextInput::make('provincia')->label('Provincia')->maxLength(5),
Select::make('stato')
->label('Stato')
->options([
'attivo' => 'Attivo',
'inattivo' => 'Inattivo',
])
->default('attivo')
->required(),
Checkbox::make('attivo')->label('Attivo')->default(true),
])
->fillForm(fn(Stabile $record): array=> [
'codice_stabile' => (string) ($record->codice_stabile ?? ''),
'cod_stabile' => (string) ($record->cod_stabile ?? ''),
'denominazione' => (string) ($record->denominazione ?? ''),
'indirizzo' => (string) ($record->indirizzo ?? ''),
'cap' => (string) ($record->cap ?? ''),
'citta' => (string) ($record->citta ?? ''),
'provincia' => (string) ($record->provincia ?? ''),
'stato' => (string) ($record->stato ?? 'attivo'),
'attivo' => (bool) ($record->attivo ?? true),
])
->action(function (Stabile $record, array $data): void {
$codice = trim((string) ($data['codice_stabile'] ?? ''));
if ($codice === '') {
Notification::make()->title('Codice mancante')->danger()->send();
return;
}
$codOperatore = trim((string) ($data['cod_stabile'] ?? ''));
$codOperatore = $codOperatore !== '' ? $codOperatore : null;
$dupe = Stabile::query()
->where('codice_stabile', $codice)
->whereKeyNot((int) $record->id)
->exists();
if ($dupe) {
Notification::make()->title('Codice già presente')->danger()->send();
return;
}
$record->fill([
'codice_stabile' => $codice,
'cod_stabile' => $codOperatore,
'denominazione' => (string) ($data['denominazione'] ?? $codice),
'indirizzo' => (string) ($data['indirizzo'] ?? ''),
'cap' => (string) ($data['cap'] ?? ''),
'citta' => (string) ($data['citta'] ?? ''),
'provincia' => (string) ($data['provincia'] ?? ''),
'stato' => (string) ($data['stato'] ?? 'attivo'),
'attivo' => (bool) ($data['attivo'] ?? true),
]);
$record->save();
Notification::make()->title('Stabile aggiornato')->success()->send();
}),
Action::make('piano_conti_standard')
->label('Piano conti standard')
->icon('heroicon-o-clipboard-document-list')
->requiresConfirmation()
->action(function (Stabile $record): void {
if (! SchemaFacade::hasTable('piano_conti_condominio')) {
Notification::make()->title('Tabella piano conti non disponibile')->warning()->send();
return;
}
$exists = PianoContiCondominio::query()
->where('stabile_id', $record->id)
->exists();
if ($exists) {
Notification::make()->title('Piano conti già presente')->warning()->send();
return;
}
$standard = [
['codice' => 'SP.AMM', 'descrizione' => 'Spese di amministrazione', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.PUL', 'descrizione' => 'Spese pulizia e igiene', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.ILL', 'descrizione' => 'Spese illuminazione', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.ACQ', 'descrizione' => 'Spese acqua', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.RIS', 'descrizione' => 'Spese riscaldamento', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.ASC', 'descrizione' => 'Spese ascensore', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.ASS', 'descrizione' => 'Assicurazione fabbricato', 'tipo_conto' => 'ECONOMICO_COSTO'],
['codice' => 'SP.MAN', 'descrizione' => 'Manutenzione ordinaria', 'tipo_conto' => 'ECONOMICO_COSTO'],
];
foreach ($standard as $row) {
PianoContiCondominio::firstOrCreate(
['stabile_id' => $record->id, 'codice' => $row['codice']],
['descrizione' => $row['descrizione'], 'tipo_conto' => $row['tipo_conto']]
);
}
Notification::make()->title('Piano conti standard creato')->success()->send();
}),
Action::make('trasferisci')
->label('Cambia amministratore')
->icon('heroicon-o-arrow-right-circle')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin']);
})
->form([
Select::make('to_amministratore_id')
->label('Nuovo amministratore')
->options(function (): array {
return Amministratore::query()
->orderBy('id')
->get()
->mapWithKeys(function (Amministratore $a): array {
$label = trim((string) ($a->denominazione_studio ?: $a->nome_completo));
if ($label === '') {
$label = 'Amministratore';
}
$code = (string) ($a->codice_univoco ?: $a->codice_amministratore ?: '');
$suffix = $code !== '' ? (' [' . $code . ']') : '';
return [$a->id => $label . $suffix . ' (ID ' . $a->id . ')'];
})
->all();
})
->searchable()
->required(),
Textarea::make('reason')
->label('Motivazione')
->helperText('Traccia il motivo del passaggio: riallineamento, presa in carico interna, correzione operativa, ecc.')
->rows(3)
->required()
->maxLength(5000),
Checkbox::make('also_rubrica')
->label('Aggiorna anche Rubrica collegata (se presente)')
->default(true),
])
->requiresConfirmation()
->action(function (Stabile $record, array $data): void {
$user = Auth::user();
$toId = (int) ($data['to_amministratore_id'] ?? 0);
if ($toId <= 0) {
Notification::make()->title('Amministratore non valido')->danger()->send();
return;
}
if ((int) ($record->amministratore_id ?? 0) === $toId) {
Notification::make()->title('Nessuna modifica')->body('Stabile già assegnato a questo amministratore.')->warning()->send();
return;
}
$dest = Amministratore::query()->find($toId);
if (! $dest) {
Notification::make()->title('Amministratore non trovato')->danger()->send();
return;
}
$transfer = app(StabileTransferService::class)->transfer(
stabile: $record,
destination: $dest,
actor: $user instanceof User ? $user : null,
alsoRubrica: (bool) ($data['also_rubrica'] ?? true),
reason: trim((string) ($data['reason'] ?? '')),
source: 'portal-superadmin',
ipAddress: request()->ip(),
meta: [
'panel' => 'admin-filament',
'action' => 'stabili-archivio-trasferisci',
],
);
Notification::make()
->title('Trasferimento completato')
->body('Nuovo amministratore: ' . ($dest->codice_univoco ?: ('ID ' . $dest->id)) . ' · storico #' . (int) $transfer->id)
->success()
->send();
}),
Action::make('storico_trasferimenti')
->label('Storico passaggi')
->icon('heroicon-o-clock')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin']);
})
->modalHeading('Storico passaggi amministratore')
->modalSubmitAction(false)
->modalCancelActionLabel('Chiudi')
->modalContent(function (Stabile $record): View {
$transfers = StabileAmministratoreTransfer::query()
->with(['fromAmministratore', 'toAmministratore', 'changedByUser'])
->where('stabile_id', (int) $record->id)
->latest('id')
->limit(25)
->get();
return view('filament.pages.gescon.partials.stabile-transfer-history', [
'stabile' => $record,
'transfers' => $transfers,
]);
}),
Action::make('entra')
->label('Entra')
->icon('heroicon-o-arrow-right')
->action(function (Stabile $record): void {
$user = Auth::user();
if (! $user instanceof User) {
return;
}
if (! StabileContext::setActiveStabileId($user, (int) $record->id)) {
Notification::make()->title('Accesso negato')->danger()->send();
return;
}
$this->redirect(CruscottoStabile::getUrl(panel: 'admin-filament'));
}),
Action::make('sync_gescon')
->label('Sync GESCON')
->icon('heroicon-o-arrow-path')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->can('gescon-import.execute');
})
->form([
TextInput::make('path')
->label('Percorso archivio GESCON')
->helperText('Cartella base che contiene /<codice_stabile>/... e /dbc/Stabili.mdb')
->default('/mnt/gescon-archives/gescon')
->maxLength(500),
TextInput::make('anno')
->label('Anno gestione (opzionale)')
->helperText('Inserisci un anno (es. 2025) o "all" per tutte le cartelle.'),
Checkbox::make('dry')->label('Dry-run (solo simulazione)')->default(false),
Checkbox::make('with_unita')->label('Includi unità immobiliari')->default(false),
Checkbox::make('with_soggetti')->label('Includi anagrafiche (soggetti)')->default(false),
Checkbox::make('with_diritti')->label('Includi diritti (proprietà/inquilini)')->default(false),
Checkbox::make('with_millesimi')->label('Includi tabelle millesimali')->default(false),
Checkbox::make('with_voci')->label('Includi voci (piano dei conti / voci spesa)')->default(false),
Checkbox::make('with_gestioni')->label('Includi gestioni')->default(false),
Checkbox::make('with_banche')->label('Includi banche')->default(false),
])
->requiresConfirmation()
->action(function (Stabile $record, array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
Notification::make()->title('Utente non valido')->danger()->send();
return;
}
$amm = $user->amministratore;
if (! $amm) {
Notification::make()
->title('Amministratore non associato')
->body('Impossibile eseguire sync senza amministratore associato all\'utente.')
->danger()
->send();
return;
}
$legacyCode = trim((string) ($record->codice_stabile ?? ''));
if ($legacyCode === '') {
$legacyCode = trim((string) ($record->codice_interno ?? ''));
}
if ($legacyCode === '') {
Notification::make()
->title('Codice legacy mancante')
->body('Lo stabile non ha codice_stabile/codice_interno: non so quale cartella GESCON usare.')
->danger()
->send();
return;
}
$svc = new EssentialImportService();
$res = $svc->syncStabile($amm, $legacyCode, [
'path' => (string) ($data['path'] ?? '/mnt/gescon-archives/gescon'),
'anno' => isset($data['anno']) && trim((string) $data['anno']) !== '' ? trim((string) $data['anno']) : null,
'with_unita' => (bool) ($data['with_unita'] ?? false),
'with_soggetti' => (bool) ($data['with_soggetti'] ?? false),
'with_diritti' => (bool) ($data['with_diritti'] ?? false),
'with_millesimi' => (bool) ($data['with_millesimi'] ?? false),
'with_voci' => (bool) ($data['with_voci'] ?? false),
'with_gestioni' => (bool) ($data['with_gestioni'] ?? false),
'with_banche' => (bool) ($data['with_banche'] ?? false),
'dry' => (bool) ($data['dry'] ?? false),
]);
$title = ($res['changed'] ?? false)
? 'Sync completato'
: 'Nessuna variazione (hash invariato)';
Notification::make()
->title($title)
->body('Stabile: ' . $legacyCode)
->success()
->send();
}),
Action::make('elimina')
->label('Elimina')
->icon('heroicon-o-trash')
->color('danger')
->visible(function (Stabile $record): bool {
$user = Auth::user();
if (! $user instanceof User) {
return false;
}
if ($user->hasAnyRole(['super-admin', 'admin'])) {
return true;
}
// Amministratore: può eliminare solo i propri, e solo se non ci sono dipendenze.
$ammId = (int) ($user->amministratore?->id ?? 0);
return $ammId > 0 && (int) ($record->amministratore_id ?? 0) === $ammId;
})
->requiresConfirmation()
->action(function (Stabile $record): void {
$checks = [
'Palazzine' => fn() => $record->palazzine()->exists(),
'Unità immobiliari' => fn() => $record->unitaImmobiliari()->exists(),
'Tabelle millesimali' => fn() => $record->tabelleMillesimali()->exists(),
'Voci spesa' => fn() => $record->vociSpesa()->exists(),
'Gestioni contabili' => fn() => $record->gestioniContabili()->exists(),
'Gestioni condominiali' => fn() => $record->gestioniCondominiali()->exists(),
'Documenti collegati' => fn() => $record->documentiCollegati()->exists(),
'Dati bancari' => fn() => $record->datiBancari()->exists(),
'Tickets' => fn() => $record->tickets()->exists(),
];
$blocked = [];
foreach ($checks as $label => $fn) {
try {
if ($fn()) {
$blocked[] = $label;
}
} catch (\Throwable) {
// ignore
}
}
if (! empty($blocked)) {
Notification::make()
->title('Eliminazione bloccata')
->body('Lo stabile ha dati collegati: ' . implode(', ', $blocked))
->danger()
->send();
return;
}
$record->delete();
Notification::make()->title('Stabile eliminato')->success()->send();
}),
]);
}
}