*/ private array $rubricaTitoloCache = []; protected function getHeaderActions(): array { $user = Auth::user(); $activeStabileId = $user instanceof User ? (int) (StabileContext::resolveActiveStabileId($user) ?: 0): 0; if ($activeStabileId <= 0) { return []; } if (! config('netgescon.legacy_import_enabled', true)) { return []; } $stabile = Stabile::find($activeStabileId); if (!$stabile) { return []; } $codStabile = $this->resolveLegacyStabileCode($activeStabileId); if ($codStabile === '') { return []; } return [ Action::make('sync_legacy_nominativi') ->label('Sincronizza da Legacy') ->icon('heroicon-m-arrow-path') ->color('warning') ->requiresConfirmation() ->modalHeading('Sincronizza Nominativi e Unità da Legacy') ->modalDescription('Questo processo importerà le unità e i soggetti dal database legacy di staging del Gescon per lo stabile corrente.') ->action(function () { $user = Auth::user(); $activeStabileId = $user instanceof User ? (int) (StabileContext::resolveActiveStabileId($user) ?: 0): 0; $stabile = Stabile::find($activeStabileId); $codStabile = $this->resolveLegacyStabileCode($activeStabileId); try { $path = app(\App\Services\TenantArchivePathService::class)->stabileAbsolutePath($stabile); // Trova l'anno legacy più recente importato in staging $anno = (string) (DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $codStabile) ->max('legacy_year') ?: ''); if ($anno === '') { \Filament\Notifications\Notification::make() ->title('Errore Sincronizzazione') ->body('Nessun anno legacy trovato per lo stabile corrente in staging.') ->danger() ->send(); return; } // Esegui gli step: unita, relazioni, soggetti, diritti $steps = ['unita', 'relazioni', 'soggetti', 'diritti']; foreach ($steps as $step) { \Illuminate\Support\Facades\Artisan::call('gescon:import-full', [ '--stabile' => $codStabile, '--solo' => $step, '--path' => $path, '--anno' => $anno, '--update-only' => true, ]); } // Forza la rigenerazione della vista try { DB::connection('gescon_import')->statement("DROP VIEW IF EXISTS vw_legacy_condomin_nominativi"); } catch (\Throwable $e) {} $this->ensureLegacyViewExists(); \Filament\Notifications\Notification::make() ->title('Sincronizzazione completata') ->body('Le unità e i soggetti dello stabile sono stati sincronizzati con successo dal legacy.') ->success() ->send(); } catch (\Throwable $e) { \Filament\Notifications\Notification::make() ->title('Errore Sincronizzazione') ->body($e->getMessage()) ->danger() ->send(); } }), ]; } private function applyDomainNominativiSearch(Builder $query, string $search, int $activeStabileId): Builder { $search = trim($search); if ($search == '') { return $query; } $like = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $search) . '%'; $ownerRoles = [ 'condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'usufrutto', ]; $tenantRoles = [ 'inquilino', 'locatario', 'conduttore', ]; return $query->where(function (Builder $q) use ($like, $activeStabileId, $ownerRoles, $tenantRoles) { $q ->where('unita_immobiliari.denominazione', 'like', $like) ->where('unita_immobiliari.interno', 'like', $like) ->orWhere('unita_immobiliari.scala', 'like', $like) ->orWhere('unita_immobiliari.piano', 'like', $like) ->orWhereExists(function ($sub) use ($like, $activeStabileId, $ownerRoles) { $sub ->selectRaw('1') ->from('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $activeStabileId) ->where('rr.is_attivo', true) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $ownerRoles) ->where(function ($w) use ($like) { $w ->where('ru.ragione_sociale', 'like', $like) ->orWhere('ru.nome', 'like', $like) ->orWhere('ru.cognome', 'like', $like) ->orWhereRaw("TRIM(CONCAT(COALESCE(ru.nome, ''), ' ', COALESCE(ru.cognome, ''))) LIKE ?", [$like]); }) ->limit(1); }) ->orWhereExists(function ($sub) use ($like, $activeStabileId, $tenantRoles) { $sub ->selectRaw('1') ->from('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $activeStabileId) ->where('rr.is_attivo', true) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $tenantRoles) ->where(function ($w) use ($like) { $w ->where('ru.ragione_sociale', 'like', $like) ->orWhere('ru.nome', 'like', $like) ->orWhere('ru.cognome', 'like', $like) ->orWhereRaw("TRIM(CONCAT(COALESCE(ru.nome, ''), ' ', COALESCE(ru.cognome, ''))) LIKE ?", [$like]); }) ->limit(1); }); }); } private function formatLegacyDenominazione(?string $titolo, ?string $denominazione): string { $titolo = trim((string) ($titolo ?? '')); $den = trim((string) ($denominazione ?? '')); if ($den === '') { return '—'; } if ($titolo === '') { return $den; } // Caso tipico legacy: "ATER C" + "O UFFICIO ..." => "ATER C/O UFFICIO ..." if (preg_match('/\bC\b/i', $titolo) && preg_match('/^O\b/i', $den)) { return rtrim($titolo, '/') . '/' . ltrim($den, '/'); } // Default: concat con spazio return trim($titolo . ' ' . $den); } private function resolveUnitaIdForRow(mixed $record, int $activeStabileId): ?int { if ($record instanceof UnitaImmobiliare) { return (int) $record->getKey(); } $codStabileLegacy = trim((string) ($record->cod_stabile ?? '')); $scala = trim((string) ($record->scala ?? '')); $interno = trim((string) ($record->interno ?? '')); if ($codStabileLegacy === '' || $interno === '') { return null; } $codiceUnita = $codStabileLegacy . '-' . ($scala !== '' ? $scala : 'A') . '-' . $interno; $id = UnitaImmobiliare::query() ->where('stabile_id', $activeStabileId) ->where('codice_unita', $codiceUnita) ->value('id'); return is_numeric($id) ? (int) $id : null; } private function resolveRubricaIdForRow(mixed $record): ?int { $cf = trim((string) ($record->cond_cod_fisc ?? $record->codice_fiscale ?? '')); if ($cf !== '') { $id = RubricaUniversale::query() ->where('codice_fiscale', strtoupper(preg_replace('/[^A-Z0-9]/i', '', $cf))) ->value('id'); if (is_numeric($id)) { return (int) $id; } } // Fallback best-effort su ragione sociale/nome completo (può essere costoso, quindi lo teniamo leggero) $name = trim((string) ($record->nom_cond ?? '')); if ($name === '') { return null; } $id = RubricaUniversale::query() ->where('ragione_sociale', $name) ->orWhereRaw("TRIM(CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, ''))) = ?", [$name]) ->value('id'); return is_numeric($id) ? (int) $id : null; } private function resolveRubricaIdForInquilinoRow(mixed $record): ?int { $cf = trim((string) ($record->inquil_cod_fisc ?? '')); if ($cf !== '') { $id = RubricaUniversale::query() ->where('codice_fiscale', strtoupper(preg_replace('/[^A-Z0-9]/i', '', $cf))) ->value('id'); if (is_numeric($id)) { return (int) $id; } } $name = trim((string) ($record->inquil_nome ?? '')); if ($name === '') { return null; } $id = RubricaUniversale::query() ->where('ragione_sociale', $name) ->orWhereRaw("TRIM(CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, ''))) = ?", [$name]) ->value('id'); return is_numeric($id) ? (int) $id : null; } private function resolveRubricaTitoloById(?int $rubricaId): string { if (! $rubricaId) { return ''; } if (array_key_exists($rubricaId, $this->rubricaTitoloCache)) { return $this->rubricaTitoloCache[$rubricaId]; } $val = ''; if (DB::getSchemaBuilder()->hasTable('titoli') && DB::getSchemaBuilder()->hasColumn('rubrica_universale', 'titolo_id')) { $val = (string) (DB::table('rubrica_universale as ru') ->leftJoin('titoli as t', 't.id', '=', 'ru.titolo_id') ->where('ru.id', $rubricaId) ->value(DB::raw("COALESCE(t.sigla, t.nome, '')")) ?: ''); } return $this->rubricaTitoloCache[$rubricaId] = trim($val); } private function resolveTitoloOwnerForRow(mixed $record): string { $rubricaId = $this->resolveRubricaIdForRow($record); $titolo = $this->resolveRubricaTitoloById($rubricaId); if ($titolo !== '') { return $titolo; } return trim((string) ($record->titolo_cond ?? '')); } private function resolveTitoloInquilinoForRow(mixed $record): string { $rubricaId = $this->resolveRubricaIdForInquilinoRow($record); $titolo = $this->resolveRubricaTitoloById($rubricaId); if ($titolo !== '') { return $titolo; } return trim((string) ($record->titolo_inq ?? '')); } private function resolveCellulareForRow(mixed $record, bool $inquilino = false): string { $keys = $inquilino ? ['tenant_cellulare', 'cell_inq', 'inquil_cell', 'telefono_inq', 'cell_inquilino', 'telefono_cellulare_inq'] : ['owner_cellulare', 'cell_cond', 'cond_cell', 'telefono_cond', 'cell_condomino', 'telefono_cellulare_cond']; foreach ($keys as $key) { $val = trim((string) ($record->{$key} ?? '')); if ($val !== '') { return $val; } } return '—'; } private function resolveLegacyStabileCode(int $stabileId): string { $row = DB::table('stabili') ->where('id', $stabileId) ->first(['codice_stabile', 'cod_stabile']); if (! $row) { return ''; } $legacy = trim((string) ($row->codice_stabile ?? '')); if ($legacy !== '') { return $legacy; } return trim((string) ($row->cod_stabile ?? '')); } protected static ?string $navigationLabel = 'Nominativi (condomini / inquilini)'; protected static ?string $title = 'Nominativi (condomini e inquilini)'; protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-users'; protected static UnitEnum|string|null $navigationGroup = 'Stabile'; protected static ?int $navigationSort = 29; protected static ?string $slug = 'condomini/nominativi'; protected string $view = 'filament.pages.condomini.nominativi-stabile'; public static function canAccess(): bool { $user = Auth::user(); return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']); } public function mount(): void { $user = Auth::user(); if ($user instanceof User) { $requestedStabileId = request()->integer('stabile_id'); if ($requestedStabileId > 0) { StabileContext::setActiveStabileId($user, $requestedStabileId); } $this->stabileId = StabileContext::resolveActiveStabileId($user); if (! $this->stabileId) { $fallbackStabileId = StabileContext::accessibleStabili($user)->first()?->id; if ($fallbackStabileId) { StabileContext::setActiveStabileId($user, (int) $fallbackStabileId); $this->stabileId = (int) $fallbackStabileId; } } } $this->mountInteractsWithTable(); } public function getStabiliOptions(): array { $user = Auth::user(); if (! $user instanceof User) { return []; } return StabileContext::accessibleStabili($user) ->mapWithKeys(fn(Stabile $s) => [(int) $s->id => trim((string) ($s->codice_stabile . ' - ' . $s->denominazione))]) ->all(); } public function updatedStabileId($value): void { $user = Auth::user(); if (! $user instanceof User) { return; } $id = is_numeric($value) ? (int) $value : 0; if ($id <= 0) { return; } if (! StabileContext::setActiveStabileId($user, $id)) { return; } $this->stabileId = $id; $this->resetPage(); $this->dispatch('$refresh'); } protected function getTableQuery(): Builder { $user = Auth::user(); if (! $user instanceof User) { return LegacyCondominNominativo::query()->whereRaw('1 = 0'); } $activeStabileId = StabileContext::resolveActiveStabileId($user); if (! $activeStabileId) { return LegacyCondominNominativo::query()->whereRaw('1 = 0'); } $codStabile = $this->resolveLegacyStabileCode((int) $activeStabileId); if ($codStabile === '') { return LegacyCondominNominativo::query()->whereRaw('1 = 0'); } $this->ensureLegacyViewExists(); if (! $this->hasLegacyNominativiForStabile($codStabile)) { return $this->buildDomainFallbackQuery((int) $activeStabileId); } $legacyTable = 'vw_legacy_condomin_nominativi'; $base = LegacyCondominNominativo::query() ->where($legacyTable . '.cod_stabile', $codStabile) ->where(function (Builder $q): void { $q->whereRaw("TRIM(COALESCE(vw_legacy_condomin_nominativi.nom_cond, '')) <> ''") ->orWhereRaw("TRIM(COALESCE(vw_legacy_condomin_nominativi.inquil_nome, '')) <> ''"); }); $latestPerUnit = DB::connection('gescon_import') ->table('vw_legacy_condomin_nominativi') ->select('cod_stabile', 'scala', 'interno') ->selectRaw('MAX(legacy_year) as legacy_year') ->where('cod_stabile', $codStabile) ->groupBy('cod_stabile', 'scala', 'interno'); return $base ->joinSub($latestPerUnit, 'mx', function ($join): void { $join->on('mx.cod_stabile', '=', 'vw_legacy_condomin_nominativi.cod_stabile') ->on('mx.scala', '=', 'vw_legacy_condomin_nominativi.scala') ->on('mx.interno', '=', 'vw_legacy_condomin_nominativi.interno') ->on('mx.legacy_year', '=', 'vw_legacy_condomin_nominativi.legacy_year'); }) ->when($this->cumulato, function (Builder $q) use ($legacyTable): void { $q->where(function (Builder $sub) use ($legacyTable) { $sub->whereNull($legacyTable . '.cumulo_cond') ->orWhere($legacyTable . '.cumulo_cond', '') ->orWhereColumn($legacyTable . '.cumulo_cond', $legacyTable . '.cod_cond'); }); }) ->orderBy($legacyTable . '.scala') // Ordine richiesto: per interno (con fallback robusto) ->orderByRaw("CASE WHEN vw_legacy_condomin_nominativi.interno IS NULL OR vw_legacy_condomin_nominativi.interno = '' THEN 1 ELSE 0 END") ->orderByRaw("CASE WHEN vw_legacy_condomin_nominativi.interno REGEXP '^[0-9]+' THEN CAST(vw_legacy_condomin_nominativi.interno AS UNSIGNED) ELSE 999999 END") ->orderBy($legacyTable . '.interno') ->orderBy($legacyTable . '.cod_cond') ->orderBy($legacyTable . '.id'); } protected function hasLegacyNominativiForStabile(string $codStabile): bool { return DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $codStabile) ->limit(1) ->exists(); } private function getAccumulatedUnits(string $codStabile, string $legacyYear, string $codCond): array { $units = DB::connection('gescon_import') ->table('vw_legacy_condomin_nominativi') ->where('cod_stabile', $codStabile) ->where('legacy_year', $legacyYear) ->where('cumulo_cond', $codCond) ->where('cod_cond', '<>', $codCond) ->orderBy('scala') ->orderBy('interno') ->get(['scala', 'interno', 'piano']); $result = []; foreach ($units as $u) { $parts = []; if (!empty($u->scala)) $parts[] = 'Sc. ' . trim($u->scala); if (!empty($u->interno)) $parts[] = 'Int. ' . trim($u->interno); if ($u->piano !== '' && $u->piano !== null) $parts[] = 'P. ' . trim($u->piano); $result[] = $parts === [] ? '—' : implode(' · ', $parts); } return $result; } private function getAccumulatedDomainUnits(int $mainUnitId): array { $units = DB::table('unita_pertinenze as up') ->join('unita_immobiliari as ui', 'ui.id', '=', 'up.unita_accessoria_id') ->where('up.unita_principale_id', $mainUnitId) ->where('up.cumulo', true) ->orderBy('ui.scala') ->orderBy('ui.interno') ->get(['ui.scala', 'ui.interno', 'ui.piano']); $result = []; foreach ($units as $u) { $parts = []; if (!empty($u->scala)) $parts[] = 'Sc. ' . trim($u->scala); if (!empty($u->interno)) $parts[] = 'Int. ' . trim($u->interno); if ($u->piano !== '' && $u->piano !== null) $parts[] = 'P. ' . trim($u->piano); $result[] = $parts === [] ? '—' : implode(' · ', $parts); } return $result; } private function ensureLegacyViewExists(): void { $conn = DB::connection('gescon_import'); $hasCumulo = false; try { $hasCumulo = DbSchema::connection('gescon_import')->hasColumn('vw_legacy_condomin_nominativi', 'cumulo_cond'); } catch (\Throwable $e) {} if (!$hasCumulo) { $cols = DbSchema::connection('gescon_import')->getColumnListing('condomin'); $desiredFields = [ 'id', 'cod_stabile', 'legacy_year', 'legacy_period_label', 'legacy_file', 'id_cond', 'cod_cond', 'scala', 'interno', 'piano', 'tipo_pr', 'nom_cond', 'titolo_cond', 'ind', 'cap', 'citta', 'pr', 'tel1', 'tel2', 'cell_cond', 'fax_cond', 'e_mail_condomino', 'pec_condomino', 'cond_cod_fisc', 'cond_dt_nasc', 'cond_luogo_nasc', 'cond_pr_nasc', 'inquil_nome', 'titolo_inq', 'inquil_indir', 'inquil_cap', 'inquil_citta', 'inquil_pr', 'inquil_tel1', 'inquil_tel2', 'cell_inq', 'fax_inq', 'e_mail_inquilino', 'pec_inquilino', 'inquil_cod_fisc', 'inquil_dal', 'inquil_al', 'inquil_contratto_dal', 'perc_diritto_reale', 'diritto_reale', 'diritto_godimento', 'note_cond', 'inquil_note', 'note', 'cumulo_cond', 'cumulo_inq', 'cumulo_cond_ec', 'cumulo_inq_ec' ]; $selects = []; foreach ($desiredFields as $field) { if (in_array($field, $cols, true)) { if ($field === 'id') { $selects[] = "id AS id"; } else { $selects[] = $field; } } else { $selects[] = "NULL AS {$field}"; } } $selectSql = implode(', ', $selects); try { $conn->statement("DROP VIEW IF EXISTS vw_legacy_condomin_nominativi"); } catch (\Throwable $e) {} $sql = $conn->getDriverName() === 'sqlite' ? "CREATE VIEW IF NOT EXISTS vw_legacy_condomin_nominativi AS SELECT {$selectSql} FROM condomin" : "CREATE OR REPLACE VIEW vw_legacy_condomin_nominativi AS SELECT {$selectSql} FROM condomin"; $conn->statement($sql); } } protected function buildDomainFallbackQuery(int $stabileId): Builder { $today = now()->toDateString(); $ownerRoles = [ 'condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'usufrutto', ]; $tenantRoles = [ 'inquilino', 'locatario', 'conduttore', ]; $nameExpr = "CASE " . "WHEN ru.tipo_contatto = 'persona_giuridica' AND COALESCE(ru.ragione_sociale, '') <> '' THEN ru.ragione_sociale "; // Caso legacy frequente: denominazione azienda salvata come nome='O ...' e cognome='ATER C' $nameExpr .= "WHEN TRIM(COALESCE(ru.nome, '')) LIKE 'O %' AND TRIM(COALESCE(ru.cognome, '')) LIKE '% C' THEN TRIM(CONCAT(COALESCE(ru.cognome, ''), '/', COALESCE(ru.nome, ''))) " . "ELSE TRIM(CONCAT(COALESCE(ru.nome, ''), ' ', COALESCE(ru.cognome, ''))) END"; $phoneExpr = "COALESCE(NULLIF(TRIM(ru.telefono_cellulare), ''), NULLIF(TRIM(ru.telefono_ufficio), ''), NULLIF(TRIM(ru.telefono_casa), ''))"; $ownerNameSub = DB::table('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->where(function ($q) use ($today) { $q->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today); }) ->where(function ($q) use ($today) { $q->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today); }) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $ownerRoles) ->orderByDesc('rr.is_preferito') ->orderByDesc('rr.data_inizio') ->orderByDesc('rr.id') ->selectRaw($nameExpr) ->limit(1); $tenantNameSub = DB::table('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->where(function ($q) use ($today) { $q->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today); }) ->where(function ($q) use ($today) { $q->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today); }) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $tenantRoles) ->orderByDesc('rr.is_preferito') ->orderByDesc('rr.data_inizio') ->orderByDesc('rr.id') ->selectRaw($nameExpr) ->limit(1); $ownerPhoneSub = DB::table('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->where(function ($q) use ($today) { $q->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today); }) ->where(function ($q) use ($today) { $q->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today); }) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $ownerRoles) ->orderByDesc('rr.is_preferito') ->orderByDesc('rr.data_inizio') ->orderByDesc('rr.id') ->selectRaw($phoneExpr) ->limit(1); $tenantPhoneSub = DB::table('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->where(function ($q) use ($today) { $q->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today); }) ->where(function ($q) use ($today) { $q->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today); }) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $tenantRoles) ->orderByDesc('rr.is_preferito') ->orderByDesc('rr.data_inizio') ->orderByDesc('rr.id') ->selectRaw($phoneExpr) ->limit(1); return UnitaImmobiliare::query() ->where('stabile_id', $stabileId) ->whereNull('deleted_at') ->when($this->cumulato, function (Builder $query) use ($stabileId): void { $query->whereNotExists(function ($sub) use ($stabileId) { $sub->selectRaw('1') ->from('unita_pertinenze') ->where('stabile_id', $stabileId) ->whereColumn('unita_accessoria_id', 'unita_immobiliari.id') ->where('cumulo', true); }); }) ->where(function (Builder $q) use ($stabileId, $ownerRoles, $tenantRoles): void { $q->whereExists(function ($sub) use ($stabileId, $ownerRoles) { $sub->selectRaw('1') ->from('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $ownerRoles) ->where(function ($w) { $w->whereRaw("TRIM(COALESCE(ru.ragione_sociale, '')) <> ''") ->orWhereRaw("TRIM(COALESCE(ru.nome, '')) <> ''") ->orWhereRaw("TRIM(COALESCE(ru.cognome, '')) <> ''"); }) ->limit(1); })->orWhereExists(function ($sub) use ($stabileId, $tenantRoles) { $sub->selectRaw('1') ->from('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->whereColumn('rr.unita_immobiliare_id', 'unita_immobiliari.id') ->where('rr.stabile_id', $stabileId) ->where('rr.is_attivo', true) ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), $tenantRoles) ->where(function ($w) { $w->whereRaw("TRIM(COALESCE(ru.ragione_sociale, '')) <> ''") ->orWhereRaw("TRIM(COALESCE(ru.nome, '')) <> ''") ->orWhereRaw("TRIM(COALESCE(ru.cognome, '')) <> ''"); }) ->limit(1); }); }) ->select([ 'unita_immobiliari.id', 'unita_immobiliari.scala', 'unita_immobiliari.piano', 'unita_immobiliari.interno', ]) ->selectRaw('unita_immobiliari.codice_unita as cod_cond') ->selectSub($ownerNameSub, 'nom_cond') ->selectSub($tenantNameSub, 'inquil_nome') ->selectSub($ownerPhoneSub, 'owner_cellulare') ->selectSub($tenantPhoneSub, 'tenant_cellulare') ->selectRaw('NULL as perc_diritto_reale') ->orderBy('unita_immobiliari.scala') ->orderByRaw("CASE WHEN unita_immobiliari.interno IS NULL OR unita_immobiliari.interno = '' THEN 1 ELSE 0 END") ->orderByRaw("CASE WHEN unita_immobiliari.interno REGEXP '^[0-9]+' THEN CAST(unita_immobiliari.interno AS UNSIGNED) ELSE 999999 END") ->orderBy('unita_immobiliari.interno') ->orderBy('unita_immobiliari.id'); } public function table(Table $table): Table { $user = Auth::user(); $activeStabileId = $user instanceof User ? (int) (StabileContext::resolveActiveStabileId($user) ?: 0): 0; $legacyTable = 'vw_legacy_condomin_nominativi'; $codStabile = $activeStabileId > 0 ? $this->resolveLegacyStabileCode((int) $activeStabileId) : ''; $useLegacy = $codStabile !== '' && $this->hasLegacyNominativiForStabile($codStabile); $legacyYear = $useLegacy ? (string) (DB::connection('gescon_import')->table('condomin')->where('cod_stabile', $codStabile)->max('legacy_year') ?: '') : ''; return $table ->striped() ->filters([ SelectFilter::make('legacy_year') ->label('Anno legacy') ->visible(fn(): bool => (bool) $useLegacy) ->options(function () use ($codStabile): array { if ($codStabile === '') { return []; } return DB::connection('gescon_import') ->table('condomin') ->where('cod_stabile', $codStabile) ->whereNotNull('legacy_year') ->where('legacy_year', '<>', '') ->distinct() ->orderByDesc('legacy_year') ->pluck('legacy_year', 'legacy_year') ->all(); }) ->query(function (Builder $query, array $data) use ($codStabile): Builder { if (! isset($data['value']) || $data['value'] === '') { return $query; } if (! $this->hasLegacyNominativiForStabile($codStabile)) { return $query; } $table = $query->getModel()?->getTable(); if (! $table || ! DbSchema::hasColumn($table, 'legacy_year')) { return $query; } return $query->where($table . '.legacy_year', $data['value']); }), SelectFilter::make('scala') ->label('Scala') ->options(function () use ($codStabile, $legacyYear, $activeStabileId): array { if ($codStabile === '') { return []; } if (! $this->hasLegacyNominativiForStabile($codStabile)) { if ($activeStabileId <= 0) { return []; } return UnitaImmobiliare::query() ->where('stabile_id', $activeStabileId) ->whereNull('deleted_at') ->whereNotNull('scala') ->where('scala', '<>', '') ->distinct() ->orderBy('scala') ->pluck('scala', 'scala') ->all(); } return LegacyCondominNominativo::query() ->where('cod_stabile', $codStabile) ->when($legacyYear !== '', fn(Builder $q) => $q->where('legacy_year', $legacyYear)) ->whereNotNull('scala') ->where('scala', '<>', '') ->distinct() ->orderBy('scala') ->pluck('scala', 'scala') ->all(); }) ->query(function (Builder $query, array $data) use ($legacyTable): Builder { if (! isset($data['value']) || $data['value'] === '') { return $query; } $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->where($legacyTable . '.scala', $data['value']); } return $query->where('scala', $data['value']); }) ->searchable(), SelectFilter::make('piano') ->label('Piano') ->options(function () use ($codStabile, $legacyYear, $activeStabileId): array { if ($codStabile === '') { return []; } if (! $this->hasLegacyNominativiForStabile($codStabile)) { if ($activeStabileId <= 0) { return []; } return UnitaImmobiliare::query() ->where('stabile_id', $activeStabileId) ->whereNull('deleted_at') ->whereNotNull('piano') ->where('piano', '<>', '') ->distinct() ->orderBy('piano') ->pluck('piano', 'piano') ->all(); } return LegacyCondominNominativo::query() ->where('cod_stabile', $codStabile) ->when($legacyYear !== '', fn(Builder $q) => $q->where('legacy_year', $legacyYear)) ->whereNotNull('piano') ->where('piano', '<>', '') ->distinct() ->orderBy('piano') ->pluck('piano', 'piano') ->all(); }) ->searchable(), TernaryFilter::make('ha_inquilino') ->label('Inquilino') ->trueLabel('Con inquilino') ->falseLabel('Senza inquilino') ->queries( true: fn(Builder $query) => $query->whereNotNull('inquil_nome')->where('inquil_nome', '<>', ''), false: fn(Builder $query) => $query->where(function (Builder $q) { $q->whereNull('inquil_nome')->orWhere('inquil_nome', ''); }), ), SelectFilter::make('soggetto') ->label('Cerca condomino') ->searchable() ->options(function () use ($codStabile, $legacyYear, $activeStabileId, $useLegacy): array { if ($useLegacy) { if ($codStabile === '') { return []; } return DB::connection('gescon_import') ->table('vw_legacy_condomin_nominativi') ->where('cod_stabile', $codStabile) ->where('legacy_year', $legacyYear) ->whereNotNull('nom_cond') ->where('nom_cond', '<>', '') ->distinct() ->orderBy('nom_cond') ->pluck('nom_cond', 'nom_cond') ->all(); } else { if ($activeStabileId <= 0) { return []; } return DB::table('rubrica_ruoli as rr') ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') ->where('rr.stabile_id', $activeStabileId) ->where('rr.is_attivo', true) ->distinct() ->orderByRaw("TRIM(CONCAT(COALESCE(ru.nome, ''), ' ', COALESCE(ru.cognome, '')))") ->get(['ru.nome', 'ru.cognome', 'ru.ragione_sociale']) ->mapWithKeys(function($row) { $nome = trim(($row->nome ?? '') . ' ' . ($row->cognome ?? '')); $label = $nome !== '' ? $nome : ($row->ragione_sociale ?? '—'); return [$label => $label]; }) ->all(); } }) ->query(function (Builder $query, array $data) use ($legacyTable, $useLegacy, $activeStabileId): Builder { if (! isset($data['value']) || $data['value'] === '') { return $query; } if ($useLegacy) { return $query->where($legacyTable . '.nom_cond', $data['value']); } else { return $this->applyDomainNominativiSearch($query, $data['value'], $activeStabileId); } }), SelectFilter::make('inquilino') ->label('Cerca inquilino') ->searchable() ->options(function () use ($codStabile, $legacyYear, $useLegacy): array { if ($useLegacy) { if ($codStabile === '') { return []; } return DB::connection('gescon_import') ->table('vw_legacy_condomin_nominativi') ->where('cod_stabile', $codStabile) ->where('legacy_year', $legacyYear) ->whereNotNull('inquil_nome') ->where('inquil_nome', '<>', '') ->distinct() ->orderBy('inquil_nome') ->pluck('inquil_nome', 'inquil_nome') ->all(); } else { return []; } }) ->query(function (Builder $query, array $data) use ($legacyTable, $useLegacy, $activeStabileId): Builder { if (! isset($data['value']) || $data['value'] === '') { return $query; } if ($useLegacy) { return $query->where($legacyTable . '.inquil_nome', $data['value']); } else { return $this->applyDomainNominativiSearch($query, $data['value'], $activeStabileId); } }), ]) ->columns([ TextColumn::make('unita_ref') ->label('Unità') ->formatStateUsing(function ($state, $record) use ($useLegacy, $codStabile, $legacyYear): string { $scala = trim((string) ($record->scala ?? '')); $interno = trim((string) ($record->interno ?? '')); $piano = trim((string) ($record->piano ?? '')); $parts = []; if ($scala !== '') { $parts[] = 'Sc. ' . $scala; } if ($interno !== '') { $parts[] = 'Int. ' . $interno; } if ($piano !== '') { $parts[] = 'P. ' . $piano; } $mainLabel = $parts === [] ? '—' : implode(' · ', $parts); if ($this->cumulato) { if ($useLegacy) { $codCond = $record->cod_cond ?? null; if ($codCond) { $accumulated = $this->getAccumulatedUnits($codStabile, $record->legacy_year ?? $legacyYear, $codCond); if (!empty($accumulated)) { return $mainLabel . ' (+ ' . implode(', ', $accumulated) . ')'; } } } else { $accumulated = $this->getAccumulatedDomainUnits((int) $record->id); if (!empty($accumulated)) { return $mainLabel . ' (+ ' . implode(', ', $accumulated) . ')'; } } } return $mainLabel; }) ->wrap(), TextColumn::make('scala') ->label('Sc.') ->sortable(query: function (Builder $query, string $direction) use ($legacyTable): Builder { $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->orderBy($legacyTable . '.scala', $direction); } return $query->orderBy('scala', $direction); }), TextColumn::make('interno') ->label('Int.') ->sortable(query: function (Builder $query, string $direction) use ($legacyTable): Builder { $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->orderBy($legacyTable . '.interno', $direction); } return $query->orderBy('interno', $direction); }) ->searchable(query: function (Builder $query, string $search) use ($activeStabileId, $legacyTable): Builder { $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->where($legacyTable . '.interno', 'like', '%' . $search . '%'); } return $this->applyDomainNominativiSearch($query, $search, $activeStabileId); }), TextColumn::make('nom_cond') ->label('Condomino') ->formatStateUsing(function ($state, $record): string { $titolo = $this->resolveTitoloOwnerForRow($record); return $this->formatLegacyDenominazione($titolo, $record->nom_cond ?? (string) $state); }) ->searchable(query: function (Builder $query, string $search) use ($activeStabileId, $legacyTable): Builder { $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->where($legacyTable . '.nom_cond', 'like', '%' . $search . '%'); } return $this->applyDomainNominativiSearch($query, $search, $activeStabileId); }) ->wrap(), TextColumn::make('periodo') ->label('Periodo') ->formatStateUsing(function ($state, $record): string { $da = $record->data_inizio ?? null; $a = $record->data_fine ?? null; $format = static function ($value): ?string { if (empty($value)) { return null; } try { return \Carbon\Carbon::parse($value)->format('d/m/Y'); } catch (\Throwable $e) { return (string) $value; } }; $daLabel = $format($da); $aLabel = $format($a); if (! $daLabel && ! $aLabel) { return '—'; } $parts = []; if ($daLabel) { $parts[] = 'Da ' . $daLabel; } if ($aLabel) { $parts[] = 'fino a ' . $aLabel; } return '(' . implode(' · ', $parts) . ')'; }) ->wrap(), TextColumn::make('inquil_nome') ->label('Inquilino') ->formatStateUsing(function ($state, $record): string { $v = trim((string) ($state ?? '')); if ($v === '') { return '—'; } $titolo = $this->resolveTitoloInquilinoForRow($record); return $this->formatLegacyDenominazione($titolo, $v); }) ->searchable(query: function (Builder $query, string $search) use ($activeStabileId, $legacyTable): Builder { $model = $query->getModel(); if ($model instanceof LegacyCondominNominativo) { return $query->where($legacyTable . '.inquil_nome', 'like', '%' . $search . '%'); } return $this->applyDomainNominativiSearch($query, $search, $activeStabileId); }) ->wrap(), TextColumn::make('cell_cond') ->label('Cell. Cond.') ->formatStateUsing(fn($state, $record): string => $this->resolveCellulareForRow($record, false)) ->wrap(), TextColumn::make('cell_inq') ->label('Cell. Inq.') ->formatStateUsing(fn($state, $record): string => $this->resolveCellulareForRow($record, true)) ->wrap(), TextColumn::make('titolo_cond') ->label('Tit. Cond.') ->formatStateUsing(fn($state, $record): string => $this->resolveTitoloOwnerForRow($record) ?: '—') ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('titolo_inq') ->label('Tit. Inq.') ->formatStateUsing(fn($state, $record): string => $this->resolveTitoloInquilinoForRow($record) ?: '—') ->toggleable(isToggledHiddenByDefault: true), ]) ->actions([ Action::make('apri_unita') ->label('Unità') ->icon('heroicon-o-home') ->url(function ($record) use ($activeStabileId): ?string { if ($activeStabileId <= 0) { return null; } $unitaId = $this->resolveUnitaIdForRow($record, $activeStabileId); if (! $unitaId) { return null; } return UnitaImmobiliarePage::getUrl(panel: 'admin-filament') . '?unita_id=' . $unitaId; }) ->visible(fn($record): bool => (bool) $this->resolveUnitaIdForRow($record, $activeStabileId)), Action::make('apri_scheda_unica') ->label('Scheda unica') ->icon('heroicon-o-identification') ->url(function ($record): ?string { $rubricaId = $this->resolveRubricaIdForRow($record); if (! $rubricaId) { return null; } return RubricaUniversaleScheda::getUrl(['record' => $rubricaId], panel: 'admin-filament'); }) ->visible(fn($record): bool => (bool) $this->resolveRubricaIdForRow($record)), ]); } }