336 lines
16 KiB
PHP
Executable File
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');
|
|
}
|
|
}
|