*/ public array $unitaOptions = []; protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-archive-box'; protected static ?string $navigationLabel = 'Unità immobiliare (old)'; protected static UnitEnum|string|null $navigationGroup = 'Archivio'; protected static bool $shouldRegisterNavigation = false; protected string $view = 'filament.pages.unita-immobiliare'; protected static ?string $slug = 'unita-immobiliare-old'; public ?UnitaImmobiliare $unita = null; public string $tab = 'riepilogo'; public array $millesimiPerTabella = []; public array $dirittiProprieta = []; public array $relazioniPerTipo = []; public array $ripartizioniPerTabella = []; public array $preventiviPerTabella = []; public array $totaliPerGestione = []; public function mount(): void { $user = Auth::user(); if (! $user instanceof User) { abort(403); } $stabileId = StabileContext::resolveActiveStabileId($user); if (! $stabileId) { $this->unita = null; Notification::make() ->title('Nessuno stabile disponibile') ->body('Non risulta alcuno stabile accessibile per questo utente.') ->warning() ->send(); return; } $this->stabileId = $stabileId; $this->refreshUnitaOptions(); $requestedId = request()->integer('unita_id') ?: null; $this->unitaId = $this->pickUnitaId($requestedId); $this->loadUnita(); } public function updatedUnitaId(): void { $this->unitaId = $this->pickUnitaId($this->unitaId); $this->loadUnita(); } public function setTab(string $tab): void { $this->tab = $tab; } protected function refreshUnitaOptions(): void { $this->unitaOptions = UnitaImmobiliare::query() ->where('stabile_id', $this->stabileId) ->orderBy('palazzina_id') ->orderBy('scala') ->orderBy('piano') ->orderBy('interno') ->orderBy('id') ->limit(1000) ->get(['id', 'codice_unita', 'denominazione', 'scala', 'piano', 'interno']) ->mapWithKeys(function (UnitaImmobiliare $u) { $codice = $u->codice_unita ?: ('ID ' . $u->id); $nome = $u->denominazione ?: 'Unità'; $pos = trim('Scala ' . ($u->scala ?? '-') . ' · Piano ' . ($u->piano ?? '-') . ' · Int. ' . ($u->interno ?? '-')); return [(int) $u->id => trim($codice . ' — ' . $nome . ' (' . $pos . ')')]; }) ->all(); } protected function pickUnitaId(?int $candidate): ?int { if ($candidate && array_key_exists($candidate, $this->unitaOptions)) { return $candidate; } $first = array_key_first($this->unitaOptions); return is_int($first) ? $first : null; } protected function loadUnita(): void { $this->unita = null; $this->millesimiPerTabella = []; $this->dirittiProprieta = []; $this->relazioniPerTipo = []; $this->ripartizioniPerTabella = []; $this->preventiviPerTabella = []; $this->totaliPerGestione = []; if (! $this->unitaId) { $this->tab = 'riepilogo'; return; } $this->unita = UnitaImmobiliare::with([ 'palazzinaObj', 'soggetti', 'stabile', 'dettagliMillesimi.tabellaMillesimale', ]) ->where('stabile_id', $this->stabileId) ->whereKey($this->unitaId) ->first(); if ($this->unita) { $this->hydrateMillesimi(); $this->hydrateDiritti(); $this->hydrateRelazioni(); $this->hydrateRipartizioni(); $this->hydratePreventivi(); } $this->tab = 'riepilogo'; } protected function hydrateMillesimi(): void { $this->millesimiPerTabella = $this->unita->dettagliMillesimi ->sortBy(function ($d) { $tabella = $d->tabellaMillesimale; return $d->nord ?? $tabella?->nord ?? $tabella?->ordinamento ?? 999999; }) ->map(function ($d) { $tabella = $d->tabellaMillesimale; return [ 'id' => $tabella?->id, 'codice' => $tabella?->codice_tabella ?? $tabella?->nome_tabella ?? 'TAB', 'nome' => $tabella?->denominazione ?? $tabella?->nome_tabella_millesimale ?? 'Tabella millesimale', 'millesimi' => (float) ($d->millesimi ?? 0), 'percentuale' => round($d->percentuale ?? 0, 4), ]; }) ->values() ->all(); } protected function hydrateDiritti(): void { $formatDate = static function ($value) { if (empty($value)) { return null; } try { return Carbon::parse($value)->format('d/m/Y'); } catch (\Throwable $e) { return $value; } }; $this->dirittiProprieta = Proprieta::with('soggetto') ->where('unita_immobiliare_id', $this->unita->id) ->orderByDesc('percentuale_possesso') ->get() ->map(function (Proprieta $diritto) use ($formatDate) { $soggetto = $diritto->soggetto; $nome = $soggetto?->ragione_sociale ?: trim(($soggetto?->cognome ?? '') . ' ' . ($soggetto?->nome ?? '')); if ($nome === '') { $nome = $soggetto?->nome ?? ($soggetto ? 'Soggetto #' . $soggetto->id : 'Soggetto non definito'); } $percentuale = is_numeric($diritto->percentuale_possesso) ? (float) $diritto->percentuale_possesso : null; return [ 'id' => $diritto->id, 'soggetto_id' => $soggetto?->id, 'nome' => $nome, 'codice_fiscale' => $soggetto?->codice_fiscale, 'tipo' => $diritto->tipo_diritto, 'percentuale' => $percentuale, 'percentuale_label' => $percentuale !== null ? number_format($percentuale, 2, ',', '.') : null, 'data_inizio' => $formatDate($diritto->data_inizio), 'data_fine' => $formatDate($diritto->data_fine), ]; }) ->values() ->all(); } protected function hydrateRelazioni(): void { $formatDate = static function ($value) { if (empty($value)) { return null; } try { return Carbon::parse($value)->format('d/m/Y'); } catch (\Throwable $e) { return $value; } }; $relazioniMapped = PersonaUnitaRelazione::with(['persona', 'tipoRelazioneDettagli']) ->where('unita_id', $this->unita->id) ->orderByDesc('attivo') ->orderBy('tipo_relazione') ->orderByDesc('quota_relazione') ->get() ->map(function (PersonaUnitaRelazione $relazione) use ($formatDate) { $persona = $relazione->persona; $nome = $persona?->nome_completo ?? $persona?->ragione_sociale ?? trim(($persona?->cognome ?? '') . ' ' . ($persona?->nome ?? '')); if ($nome === '') { $nome = $persona ? 'Persona #' . $persona->id : 'Persona non definita'; } $quota = is_numeric($relazione->quota_relazione) ? (float) $relazione->quota_relazione : null; return [ 'id' => $relazione->id, 'persona_id' => $persona?->id, 'nome' => $nome, 'codice_fiscale' => $persona?->codice_fiscale, 'tipo_raw' => $relazione->tipo_relazione, 'tipo' => $relazione->tipoRelazioneDettagli?->descrizione ?? ucfirst(str_replace('_', ' ', $relazione->tipo_relazione ?? '')), 'quota' => $quota, 'quota_label' => $quota !== null ? number_format($quota, 2, ',', '.') : null, 'data_inizio' => $formatDate($relazione->data_inizio), 'data_fine' => $formatDate($relazione->data_fine), 'attivo' => $relazione->isAttiva(), 'riceve_comunicazioni' => (bool) $relazione->riceve_comunicazioni, 'riceve_convocazioni' => (bool) $relazione->riceve_convocazioni, 'vota_assemblea' => (bool) $relazione->vota_assemblea, ]; }); $this->relazioniPerTipo = [ 'proprietari' => $relazioniMapped->filter(function ($rel) { return $rel['attivo'] && in_array(strtolower($rel['tipo_raw'] ?? ''), [ 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'usufrutto', ]); })->values()->all(), 'inquilini' => $relazioniMapped->filter(function ($rel) { return $rel['attivo'] && in_array(strtolower($rel['tipo_raw'] ?? ''), [ 'inquilino', 'locatario', 'conduttore', ]); })->values()->all(), 'altri' => $relazioniMapped->filter(function ($rel) { return $rel['attivo'] && !in_array(strtolower($rel['tipo_raw'] ?? ''), [ 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'usufrutto', 'inquilino', 'locatario', 'conduttore', ]); })->values()->all(), ]; } protected function hydrateRipartizioni(): void { if (!Schema::hasTable('dettaglio_ripartizione_spese')) { $this->ripartizioniPerTabella = $this->fallbackVociSpesa(); return; } $details = DettaglioRipartizioneSpese::with([ 'ripartizioneSpese.voceSpesa', 'ripartizioneSpese.tabellaMillesimale', ]) ->where('unita_immobiliare_id', $this->unita->id) ->orderByDesc('created_at') ->limit(100) ->get(); $grouped = $details->map(function (DettaglioRipartizioneSpese $d) { $rip = $d->ripartizioneSpese; $tabella = $rip?->tabellaMillesimale; $voce = $rip?->voceSpesa; return [ 'tabella_id' => $tabella?->id, 'tabella_codice' => $tabella?->codice_tabella ?? $tabella?->nome_tabella ?? 'TAB', 'tabella_nome' => $tabella?->denominazione ?? $tabella?->nome_tabella_millesimale ?? 'Tabella millesimale', 'voce_codice' => $voce?->codice, 'voce_descrizione' => $voce?->descrizione, 'tipo_gestione' => $voce?->tipo_gestione, 'calcolo' => $rip?->tipo_ripartizione, 'percentuale_applicata' => (float) ($d->percentuale_applicata ?? 0), 'percentuale_inquilino' => (float) ($d->percentuale_inquilino ?? 0), 'quota_finale' => (float) ($d->quota_finale ?? 0), 'importo_proprietario' => (float) ($d->importo_proprietario ?? 0), 'importo_inquilino' => (float) ($d->importo_inquilino ?? 0), ]; })->groupBy('tabella_id'); $this->ripartizioniPerTabella = $grouped->map(function ($items) { return [ 'tabella_codice' => $items->first()['tabella_codice'] ?? 'TAB', 'tabella_nome' => $items->first()['tabella_nome'] ?? 'Tabella millesimale', 'righe' => $items->values()->all(), ]; })->values()->all(); } protected function fallbackVociSpesa(): array { if (!Schema::hasTable('voci_spesa')) { return []; } $voci = VoceSpesa::query() ->where('stabile_id', $this->stabileId) ->with('tabellaMillesimaleDefault') ->orderBy('ordinamento') ->get(); $grouped = $voci->map(function (VoceSpesa $voce) { $tabella = $voce->tabellaMillesimaleDefault; return [ 'tabella_id' => $tabella?->id, 'tabella_codice' => $tabella?->codice_tabella ?? $tabella?->nome_tabella ?? 'TAB', 'tabella_nome' => $tabella?->denominazione ?? $tabella?->nome_tabella_millesimale ?? 'Tabella millesimale', 'voce_codice' => $voce->codice, 'voce_descrizione' => $voce->descrizione, 'tipo_gestione' => $voce->tipo_gestione, 'calcolo' => $voce->categoria, 'percentuale_applicata' => 0.0, 'percentuale_inquilino' => 0.0, 'quota_finale' => 0.0, 'importo_proprietario' => 0.0, 'importo_inquilino' => 0.0, ]; })->groupBy('tabella_id'); return $grouped->map(function ($items) { return [ 'tabella_codice' => $items->first()['tabella_codice'] ?? 'TAB', 'tabella_nome' => $items->first()['tabella_nome'] ?? 'Tabella millesimale', 'righe' => $items->values()->all(), ]; })->values()->all(); } protected function hydratePreventivi(): void { $this->preventiviPerTabella = []; $this->totaliPerGestione = []; $importConn = 'gescon_import'; $hasVocSpe = Schema::connection($importConn)->hasTable('voc_spe'); $hasDetTab = Schema::connection($importConn)->hasTable('dett_tab'); if (!$hasVocSpe || !$hasDetTab) { return; } $codiceStabile = $this->unita->stabile?->codice_stabile ?? $this->unita->stabile?->codice_interno ?? $this->unita->stabile?->old_id; $unitaLegacyId = $this->unita->id; $voci = DB::connection($importConn) ->table('voc_spe') ->when($codiceStabile && Schema::connection($importConn)->hasColumn('voc_spe', 'cod_stabile'), function ($q) use ($codiceStabile) { $q->where('cod_stabile', $codiceStabile); }) ->get(); if ($voci->isEmpty()) { return; } $tabellaCodes = $voci->pluck('tabella')->filter()->unique()->values()->all(); $detTabPerUnita = DB::connection($importConn) ->table('dett_tab') ->when($codiceStabile && Schema::connection($importConn)->hasColumn('dett_tab', 'cod_stabile'), function ($q) use ($codiceStabile) { $q->where('cod_stabile', $codiceStabile); }) ->where('id_cond', $unitaLegacyId) ->whereIn('cod_tab', $tabellaCodes) ->get() ->groupBy('cod_tab'); $detTabTotali = DB::connection($importConn) ->table('dett_tab') ->select('cod_tab', DB::raw('SUM(mm) as total_mm'), DB::raw('SUM(prev_euro) as total_prev_euro')) ->when($codiceStabile && Schema::connection($importConn)->hasColumn('dett_tab', 'cod_stabile'), function ($q) use ($codiceStabile) { $q->where('cod_stabile', $codiceStabile); }) ->whereIn('cod_tab', $tabellaCodes) ->groupBy('cod_tab') ->get() ->keyBy('cod_tab'); $millesimiPerCodice = $this->unita->dettagliMillesimi ->filter(function ($d) { return $d->tabellaMillesimale !== null; }) ->keyBy(function ($d) { $tabella = $d->tabellaMillesimale; return $tabella?->codice_tabella ?? $tabella?->nome_tabella ?? null; }); $totaliGestione = ['O' => 0.0, 'R' => 0.0, 'S' => 0.0]; $this->preventiviPerTabella = collect($voci) ->groupBy('tabella') ->map(function ($items, $tabellaCode) use ($detTabPerUnita, $detTabTotali, $millesimiPerCodice, &$totaliGestione) { $totalPreventivo = (float) $items->sum(function ($v) { return (float) ($v->preventivo_euro ?? 0); }); $detUnit = $detTabPerUnita->get($tabellaCode) ?? collect(); $quotaProprietario = (float) $detUnit ->where('cond_inquil', '!=', 'I') ->sum(function ($row) { return (float) ($row->prev_euro ?? 0); }); $quotaInquilino = (float) $detUnit ->where('cond_inquil', 'I') ->sum(function ($row) { return (float) ($row->prev_euro ?? 0); }); $percentualeInquilino = ($quotaProprietario + $quotaInquilino) > 0 ? ($quotaInquilino / ($quotaProprietario + $quotaInquilino)) * 100 : 0; $totaliGestioneKey = strtoupper(substr($items->first()->tipo_gestione ?? 'O', 0, 1)); if (array_key_exists($totaliGestioneKey, $totaliGestione)) { $totaliGestione[$totaliGestioneKey] += $quotaProprietario + $quotaInquilino; } $rows = $items->map(function ($voce) use ($detUnit) { $quotaUnita = (float) $detUnit ->where('cod_voce', $voce->codice) ->sum(function ($row) { return (float) ($row->prev_euro ?? 0); }); $quotaInquilinoVoce = (float) $detUnit ->where('cod_voce', $voce->codice) ->where('cond_inquil', 'I') ->sum(function ($row) { return (float) ($row->prev_euro ?? 0); }); $quotaProprietarioVoce = $quotaUnita - $quotaInquilinoVoce; $percentualeInquilinoVoce = $quotaUnita > 0 ? ($quotaInquilinoVoce / $quotaUnita) * 100 : 0; return [ 'codice' => $voce->codice, 'descrizione' => $voce->descrizione, 'importo_preventivato' => (float) ($voce->preventivo_euro ?? 0), 'quota_unita' => $quotaUnita, 'importo_proprietario' => $quotaProprietarioVoce, 'importo_inquilino' => $quotaInquilinoVoce, 'percentuale_inquilino' => $percentualeInquilinoVoce, ]; })->values()->all(); $tabellaMillesimi = $millesimiPerCodice->get($tabellaCode); $unitMillesimi = (float) ($tabellaMillesimi->millesimi ?? 0); $tabTotalMm = (float) ($detTabTotali->get($tabellaCode)->total_mm ?? 0); return [ 'tabella_codice' => $tabellaCode, 'tabella_nome' => $items->first()->tabella_descrizione ?? $tabellaCode, 'tipo' => $items->first()->tipo_gestione ?? 'O', 'total_preventivo' => $totalPreventivo, 'quota_unit' => $quotaProprietario + $quotaInquilino, 'importo_proprietario' => $quotaProprietario, 'importo_inquilino' => $quotaInquilino, 'percentuale_inquilino' => $percentualeInquilino, 'tabella_millesimi' => $tabTotalMm, 'unit_millesimi' => $unitMillesimi, 'voci' => $rows, ]; }) ->values() ->all(); $this->totaliPerGestione = $totaliGestione; } }