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

336 lines
16 KiB
PHP
Executable File

<?php
namespace App\Filament\Pages\Condomini;
use App\Filament\Pages\UnitaImmobiliarePage;
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\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Schema as DbSchema;
use UnitEnum;
class UnitaImmobiliariArchivio extends Page implements HasTable
{
use InteractsWithTable;
protected static ?string $navigationLabel = 'Unità immobiliari';
protected static ?string $title = 'Unità immobiliari';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-home';
protected static UnitEnum|string|null $navigationGroup = 'Stabile';
protected static ?int $navigationSort = 31;
protected static ?string $slug = 'condomini/unita-immobiliari';
protected string $view = 'filament.pages.condomini.unita-immobiliari-archivio';
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 UnitaImmobiliare::query()->whereRaw('1 = 0');
}
$activeStabileId = StabileContext::resolveActiveStabileId($user);
if (! $activeStabileId) {
return UnitaImmobiliare::query()->whereRaw('1 = 0');
}
$today = now()->toDateString();
$rubricaNameSubquery = "(\n"
. " SELECT MIN(\n"
. " CASE\n"
. " WHEN ru.tipo_contatto = 'persona_giuridica' THEN ru.ragione_sociale\n"
. " ELSE CONCAT_WS(' ', ru.nome, ru.cognome)\n"
. " END\n"
. " )\n"
. " FROM rubrica_ruoli rr\n"
. " JOIN rubrica_universale ru ON ru.id = rr.rubrica_id\n"
. " WHERE rr.unita_immobiliare_id = unita_immobiliari.id\n"
. " AND rr.is_attivo = 1\n"
. " AND (rr.data_inizio IS NULL OR rr.data_inizio <= ?)\n"
. " AND (rr.data_fine IS NULL OR rr.data_fine >= ?)\n"
. ")";
$nominativoSortExpr = "COALESCE(\n"
. " CASE\n"
. " WHEN unita_immobiliari.denominazione IS NULL THEN NULL\n"
. " WHEN TRIM(unita_immobiliari.denominazione) = '' THEN NULL\n"
. " WHEN LOWER(TRIM(unita_immobiliari.denominazione)) = 'area condominiale' THEN NULL\n"
. " ELSE unita_immobiliari.denominazione\n"
. " END,\n"
. " $rubricaNameSubquery,\n"
. " unita_immobiliari.codice_unita\n"
. ")";
$stabileCols = ['id', 'codice_stabile', 'cod_stabile'];
if (DbSchema::hasColumn('stabili', 'codice_operatore')) {
$stabileCols[] = 'codice_operatore';
}
return UnitaImmobiliare::query()
->where('stabile_id', $activeStabileId)
->with([
'stabile' => function ($q) use ($stabileCols) {
$q->select($stabileCols);
},
'rubricaRuoliAttivi.contatto:id,tipo_contatto,ragione_sociale,nome,cognome,codice_fiscale',
'soggetti:id,nome,cognome,ragione_sociale,codice_fiscale',
])
->orderBy('palazzina')
->orderBy('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('interno')
->orderBy('id');
}
public function getUnitaCount(): int
{
$user = Auth::user();
if (! $user instanceof User) {
return 0;
}
$activeStabileId = StabileContext::resolveActiveStabileId($user);
if (! $activeStabileId) {
return 0;
}
return UnitaImmobiliare::query()
->where('stabile_id', $activeStabileId)
->count();
}
public function table(Table $table): Table
{
$unitaPageUrl = UnitaImmobiliarePage::getUrl(panel: 'admin-filament');
return $table
->striped()
->columns([
TextColumn::make('codice_unita')
->label('Codice')
->searchable()
->sortable()
->formatStateUsing(function ($state, UnitaImmobiliare $record): string {
$code = trim((string) ($state ?? ''));
if ($code === '') {
return '—';
}
$legacy = trim((string) ($record->stabile?->codice_stabile ?? ''));
$mnemo = trim((string) ($record->stabile?->codice_operatore ?? $record->stabile?->cod_stabile ?? ''));
if ($legacy !== '' && $mnemo !== '' && str_starts_with($code, $legacy . '-')) {
return $mnemo . substr($code, strlen($legacy));
}
return $code;
}),
TextColumn::make('denominazione')
->label('Soggetti / Nominativi (C o I)')
->html()
->searchable(query: function (Builder $query, string $search): Builder {
$s = trim($search);
if ($s === '') {
return $query;
}
return $query
->where(function (Builder $q) use ($s): void {
$q->where('denominazione', 'like', '%' . $s . '%')
->orWhereHas('rubricaRuoliAttivi.contatto', function (Builder $qq) use ($s): void {
$qq->where('ragione_sociale', 'like', '%' . $s . '%')
->orWhere('nome', 'like', '%' . $s . '%')
->orWhere('cognome', 'like', '%' . $s . '%')
->orWhere('codice_fiscale', 'like', '%' . $s . '%');
})
->orWhereHas('soggetti', function (Builder $qq) use ($s): void {
$qq->where('ragione_sociale', 'like', '%' . $s . '%')
->orWhere('cognome', 'like', '%' . $s . '%')
->orWhere('nome', 'like', '%' . $s . '%')
->orWhere('codice_fiscale', 'like', '%' . $s . '%');
});
});
})
->sortable(query: function (Builder $query, string $direction): Builder {
$today = now()->toDateString();
$rubricaNameSubquery = "(\n"
. " SELECT MIN(\n"
. " CASE\n"
. " WHEN ru.tipo_contatto = 'persona_giuridica' THEN ru.ragione_sociale\n"
. " ELSE CONCAT_WS(' ', ru.nome, ru.cognome)\n"
. " END\n"
. " )\n"
. " FROM rubrica_ruoli rr\n"
. " JOIN rubrica_universale ru ON ru.id = rr.rubrica_id\n"
. " WHERE rr.unita_immobiliare_id = unita_immobiliari.id\n"
. " AND rr.is_attivo = 1\n"
. " AND (rr.data_inizio IS NULL OR rr.data_inizio <= ?)\n"
. " AND (rr.data_fine IS NULL OR rr.data_fine >= ?)\n"
. ")";
$nominativoSortExpr = "COALESCE(\n"
. " CASE\n"
. " WHEN unita_immobiliari.denominazione IS NULL THEN NULL\n"
. " WHEN TRIM(unita_immobiliari.denominazione) = '' THEN NULL\n"
. " WHEN LOWER(TRIM(unita_immobiliari.denominazione)) = 'area condominiale' THEN NULL\n"
. " ELSE unita_immobiliari.denominazione\n"
. " END,\n"
. " $rubricaNameSubquery,\n"
. " unita_immobiliari.codice_unita\n"
. ")";
return $query->orderByRaw($nominativoSortExpr . ' ' . $direction, [$today, $today]);
})
->state(function (UnitaImmobiliare $record): string {
$den = trim((string) ($record->denominazione ?? ''));
$isArea = $den !== '' && preg_match('/^\s*area\s+condominiale\s*$/i', $den);
if ($isArea) {
return '<span class="text-gray-500 italic">Area condominiale</span>';
}
$condomini = [];
$inquilini = [];
$altri = [];
$ruoli = ($record->rubricaRuoliAttivi ?? collect())->unique(fn($r) => $r->rubrica_id . '|' . $r->ruolo_standard);
foreach ($ruoli as $ruolo) {
$nome = $ruolo->contatto?->nome_completo ?? '—';
$cf = $ruolo->contatto?->codice_fiscale ? " ({$ruolo->contatto->codice_fiscale})" : '';
$roleLabel = strtolower(trim((string) $ruolo->ruolo_standard));
if (in_array($roleLabel, ['condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'usufrutto'], true)) {
$condomini[] = "<span class='px-1.5 py-0.5 rounded bg-emerald-50 text-emerald-700 border border-emerald-200 text-xxs font-medium mr-1'>C</span><strong>" . e($nome) . "</strong>" . e($cf);
} elseif (in_array($roleLabel, ['inquilino', 'locatario', 'conduttore'], true)) {
$inquilini[] = "<span class='px-1.5 py-0.5 rounded bg-sky-50 text-sky-700 border border-sky-200 text-xxs font-medium mr-1'>I</span><strong>" . e($nome) . "</strong>" . e($cf);
} else {
$altri[] = "<span class='px-1.5 py-0.5 rounded bg-slate-50 text-slate-700 border border-slate-200 text-xxs font-medium mr-1'>" . e(ucfirst($ruolo->ruolo_standard ?: 'Soggetto')) . "</span><strong>" . e($nome) . "</strong>" . e($cf);
}
}
// Fallback proprietari da soggetti (relazione diretta)
if (empty($condomini)) {
$soggetti = $record->soggetti ?? collect();
foreach ($soggetti as $s) {
$nome = $s->ragione_sociale ?: trim($s->nome . ' ' . $s->cognome);
$cf = $s->codice_fiscale ? " ({$s->codice_fiscale})" : '';
if ($nome !== '') {
$condomini[] = "<span class='px-1.5 py-0.5 rounded bg-emerald-50 text-emerald-700 border border-emerald-200 text-xxs font-medium mr-1'>C</span><strong>" . e($nome) . "</strong>" . e($cf);
}
}
}
if ($den !== '' && empty($condomini) && empty($inquilini) && empty($altri)) {
return '<strong>' . e($den) . '</strong>';
}
$all = array_merge($condomini, $inquilini, $altri);
if (empty($all)) {
return '<span class="text-gray-400">—</span>';
}
return '<div class="space-y-1 text-xs py-1">' . implode('<br>', $all) . '</div>';
})
->wrap(),
TextColumn::make('palazzina')
->label('Pal.')
->sortable(query: function (Builder $query, string $direction): Builder {
return $query->orderBy('palazzina', $direction)
->orderBy('scala', $direction)
->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('interno', $direction);
})
->toggleable(),
TextColumn::make('scala')
->label('Scala')
->sortable()
->toggleable(),
TextColumn::make('piano')
->label('Piano')
->sortable()
->toggleable(),
TextColumn::make('interno')
->label('Int.')
->sortable(query: function (Builder $query, string $direction): Builder {
return $query->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('interno', $direction);
})
->formatStateUsing(function ($state, UnitaImmobiliare $record): string {
$interno = trim((string) ($state ?? ''));
if ($interno === '') {
return '—';
}
$legacy = trim((string) ($record->stabile?->codice_stabile ?? $record->stabile?->cod_stabile ?? ''));
$scala = trim((string) ($record->scala ?? ''));
if ($legacy !== '' && $scala !== '') {
$prefix = $legacy . '-' . $scala . '-';
if (str_starts_with($interno, $prefix)) {
$short = trim((string) substr($interno, strlen($prefix)));
return $short !== '' ? $short : $interno;
}
}
if ($record->codice_unita && $interno === $record->codice_unita && str_contains($interno, '-')) {
$pos = strrpos($interno, '-');
if ($pos !== false) {
$short = trim((string) substr($interno, $pos + 1));
return $short !== '' ? $short : $interno;
}
}
return $interno;
})
->toggleable(),
IconColumn::make('attiva')
->label('Attiva')
->boolean(),
])
->actions([
Action::make('apri_dettaglio')
->label('Apri')
->icon('heroicon-o-arrow-right')
->url(fn(UnitaImmobiliare $record) => $unitaPageUrl . '?unita_id=' . $record->id),
])
->defaultSort('palazzina', 'asc');
}
}