netgescon-day0/app/Filament/Pages/Condomini/NominativiStabile.php

1189 lines
52 KiB
PHP
Executable File

<?php
namespace App\Filament\Pages\Condomini;
use App\Filament\Pages\Gescon\RubricaUniversaleScheda;
use App\Filament\Pages\UnitaImmobiliarePage;
use App\Models\LegacyCondominNominativo;
use App\Models\RubricaUniversale;
use App\Models\Stabile;
use App\Models\UnitaImmobiliare;
use App\Models\User;
use App\Support\StabileContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Pages\Page;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema as DbSchema;
use UnitEnum;
class NominativiStabile extends Page implements HasTable
{
use InteractsWithTable;
public ?int $stabileId = null;
public bool $cumulato = false;
/** @var array<int, string> */
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)),
]);
}
}