From 39dd7e515cc2a6dc275c992569a2112bd6cdeda7 Mon Sep 17 00:00:00 2001 From: michele Date: Thu, 27 Aug 2026 16:43:03 +0200 Subject: [PATCH] fix(millesimi): align comproprietari matching across all stabili (OTTAVIANO B/1), load voc_spe voci di spesa, fix dropdown owner display and canonicalabile code folder paths (task-d095b0e23b) --- .../Condomini/TabelleMillesimaliArchivio.php | 153 ++++++++++++++++-- app/Filament/Pages/UnitaImmobiliarePage.php | 130 ++++++--------- app/Services/TenantArchivePathService.php | 2 +- 3 files changed, 191 insertions(+), 94 deletions(-) diff --git a/app/Filament/Pages/Condomini/TabelleMillesimaliArchivio.php b/app/Filament/Pages/Condomini/TabelleMillesimaliArchivio.php index f146cdc..08c35c8 100755 --- a/app/Filament/Pages/Condomini/TabelleMillesimaliArchivio.php +++ b/app/Filament/Pages/Condomini/TabelleMillesimaliArchivio.php @@ -1089,7 +1089,23 @@ protected function loadDettaglioTabella(): void $dettagliByUnita = $tabella->dettagliMillesimali->keyBy('unita_immobiliare_id'); - $periodOccupants = \Illuminate\Support\Facades\DB::table('unita_anagrafica_periodo') + $rubricaOccupants = DB::table('rubrica_ruoli as rr') + ->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id') + ->whereIn('rr.unita_immobiliare_id', $tutteUnita->pluck('id')->all()) + ->where('rr.stabile_id', $this->stabileId) + ->where('rr.is_attivo', 1) + ->select([ + 'rr.unita_immobiliare_id', + 'rr.ruolo_standard', + 'ru.nome_completo', + 'ru.ragione_sociale', + 'ru.cognome', + 'ru.nome', + ]) + ->get() + ->groupBy('unita_immobiliare_id'); + + $periodOccupants = DB::table('unita_anagrafica_periodo') ->join('anagrafiche', 'anagrafiche.id', '=', 'unita_anagrafica_periodo.anagrafica_id') ->whereIn('unita_anagrafica_periodo.unita_immobiliare_id', $tutteUnita->pluck('id')->all()) ->select([ @@ -1113,27 +1129,58 @@ protected function loadDettaglioTabella(): void (int) $u->id ); }) - ->map(function ($u) use ($dettagliByUnita, $periodOccupants, $totale) { + ->map(function ($u) use ($dettagliByUnita, $rubricaOccupants, $periodOccupants, $totale) { $d = $dettagliByUnita->get($u->id); $millesimi = $d ? (float) ($d->millesimi ?? 0) : 0.0; $percentuale = $totale > 0 ? ($millesimi / $totale) * 100 : 0; - $occupants = $periodOccupants->get($u->id, collect()); - - $proprietarioList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'C'); + $rOcc = $rubricaOccupants->get($u->id, collect()); + $proprietarioList = $rOcc->filter(fn($p) => in_array(strtolower(trim((string)$p->ruolo_standard)), ['condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario'], true)); $propNames = []; foreach ($proprietarioList as $p) { - $propNames[] = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); + $n = trim((string) ($p->nome_completo ?: ($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)))); + if ($n !== '' && ! in_array($n, $propNames, true)) { + $propNames[] = $n; + } } + + if ($propNames === []) { + $occupants = $periodOccupants->get($u->id, collect()); + $pList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'C'); + foreach ($pList as $p) { + $n = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); + if ($n !== '' && ! in_array($n, $propNames, true)) { + $propNames[] = $n; + } + } + } + + if ($propNames === [] && trim((string)$u->denominazione) !== '') { + $propNames[] = trim((string)$u->denominazione); + } + $proprietarioName = count($propNames) > 0 ? implode(' / ', $propNames) : '—'; - $inquilinoList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'I'); + $inquilinoList = $rOcc->filter(fn($p) => in_array(strtolower(trim((string)$p->ruolo_standard)), ['inquilino', 'locatario', 'conduttore'], true)); $inqNames = []; foreach ($inquilinoList as $p) { - $inqNames[] = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); + $n = trim((string) ($p->nome_completo ?: ($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)))); + if ($n !== '' && ! in_array($n, $inqNames, true)) { + $inqNames[] = $n; + } + } + if ($inqNames === []) { + $occupants = $periodOccupants->get($u->id, collect()); + $iList = $occupants->filter(fn($p) => strtoupper(trim((string)$p->ruolo_occupazione)) === 'I'); + foreach ($iList as $p) { + $n = trim($p->ragione_sociale ?: ($p->cognome . ' ' . $p->nome)); + if ($n !== '' && ! in_array($n, $inqNames, true)) { + $inqNames[] = $n; + } + } } - $inquilinoName = count($inqNames) > 0 ? implode(' / ', $inqNames) : '—'; + $inquilinoName = count($inqNames) > 0 ? implode(' / ', $inqNames) : '—'; $ruolo = $d ? ($d->ruolo_legacy ?: 'C') : 'C'; return [ @@ -1196,9 +1243,97 @@ protected function loadVociSpesa(): void ]; })->all(); + if (empty($this->vociSpesa)) { + $this->vociSpesa = $this->loadVociSpesaFromLegacyMdb(); + } + $this->presetDirty = false; } + protected function loadVociSpesaFromLegacyMdb(): array + { + if (! $this->stabileId || ! $this->tabellaId) { + return []; + } + + $codStabile = $this->codiceStabile; + if (! $codStabile) { + return []; + } + + $tabellaCod = $this->tabellaInfo['codice'] ?? null; + if (! $tabellaCod) { + return []; + } + + $gYear = sprintf('%04d', $this->annoGestione ?: 2026); + $candidatePaths = [ + "/mnt/gescon-archives/gescon/{$codStabile}/{$gYear}/singolo_anno.mdb", + "/mnt/gescon-archives/gescon/{$codStabile}/0914/singolo_anno.mdb", + "/mnt/gescon-archives/gescon/{$codStabile}/0015/singolo_anno.mdb", + "/mnt/gescon-archives/gescon/{$codStabile}/0004/singolo_anno.mdb", + ]; + + $mdbPath = null; + foreach ($candidatePaths as $p) { + if (file_exists($p) && is_readable($p)) { + $mdbPath = $p; + break; + } + } + + if (! $mdbPath) { + return []; + } + + try { + $proc = new \Symfony\Component\Process\Process(['mdb-export', $mdbPath, 'voc_spe']); + $proc->run(); + if (! $proc->isSuccessful()) { + return []; + } + + $csvContent = $proc->getOutput(); + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $csvContent); + rewind($stream); + + $header = fgetcsv($stream); + if (! $header) { + fclose($stream); + return []; + } + + $res = []; + while (($data = fgetcsv($stream)) !== false) { + if (count($data) === count($header)) { + $row = array_combine($header, $data); + $tCod = trim((string) ($row['tabella'] ?? '')); + if (strcasecmp($tCod, $tabellaCod) === 0 || strcasecmp($tCod, 'TAB.' . $tabellaCod) === 0) { + $pInq = is_numeric($row['Perc_Inquilino'] ?? null) ? (float) $row['Perc_Inquilino'] : 0.0; + $pProp = is_numeric($row['Perc_proprietario'] ?? null) && (float) $row['Perc_proprietario'] > 0 ? (float) $row['Perc_proprietario'] : (100.0 - $pInq); + + $res[] = [ + 'id' => null, + 'codice' => trim((string) ($row['cod'] ?? 'VOC')), + 'descrizione' => trim((string) ($row['descriz'] ?? 'Voce di spesa')), + 'categoria' => trim((string) ($row['v_ors'] ?? 'O')), + 'tipo_gestione' => trim((string) ($row['v_ors'] ?? 'O')), + 'percentuale_condomino' => $pProp, + 'percentuale_inquilino' => $pInq, + 'attiva' => true, + 'modifica' => false, + ]; + } + } + } + fclose($stream); + return $res; + } catch (\Throwable $e) { + return []; + } + } + protected function loadPresetOptions(): void { $this->presetOptions = []; diff --git a/app/Filament/Pages/UnitaImmobiliarePage.php b/app/Filament/Pages/UnitaImmobiliarePage.php index f2a2703..14bd40f 100755 --- a/app/Filament/Pages/UnitaImmobiliarePage.php +++ b/app/Filament/Pages/UnitaImmobiliarePage.php @@ -758,68 +758,61 @@ protected function refreshUnitaOptions(): void } $currentNominativoByUnita = []; + + if (DbSchema::hasTable('rubrica_ruoli') && DbSchema::hasTable('rubrica_universale')) { + try { + $rows = DB::table('rubrica_ruoli as rr') + ->join('rubrica_universale as ru', 'rr.rubrica_id', '=', 'ru.id') + ->whereIn('rr.unita_immobiliare_id', $unita->pluck('id')) + ->where('rr.stabile_id', $this->stabileId) + ->where('rr.is_attivo', 1) + ->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), ['condomino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario']) + ->select('rr.unita_immobiliare_id', 'ru.nome_completo', 'ru.cognome', 'ru.nome', 'ru.ragione_sociale') + ->get(); + + foreach ($rows as $row) { + $unitId = (int) ($row->unita_immobiliare_id ?? 0); + if ($unitId <= 0) { + continue; + } + + $name = trim((string) ($row->nome_completo ?: ($row->ragione_sociale ?: ($row->cognome . ' ' . $row->nome)))); + if ($name !== '') { + if (! isset($currentNominativoByUnita[$unitId])) { + $currentNominativoByUnita[$unitId] = []; + } + if (! in_array($name, $currentNominativoByUnita[$unitId], true)) { + $currentNominativoByUnita[$unitId][] = $name; + } + } + } + } catch (\Throwable $e) { + // Ignore fallback error + } + } + if (DbSchema::hasTable('unita_immobiliare_nominativi')) { try { - $today = now()->toDateString(); - $rows = DB::table('unita_immobiliare_nominativi') + $rows = DB::table('unita_immobiliare_nominativi') ->whereIn('unita_immobiliare_id', $unita->pluck('id')) - ->where(function ($query) use ($today): void { - $query->whereNull('data_inizio')->orWhere('data_inizio', '<=', $today); - }) - ->where(function ($query) use ($today): void { - $query->whereNull('data_fine')->orWhere('data_fine', '>=', $today); - }) - ->orderByRaw("CASE WHEN ruolo = 'C' THEN 0 ELSE 1 END") - ->orderByDesc('data_inizio') + ->whereIn('ruolo', ['C', 'P', 'CP']) ->orderByDesc('id') ->get(['unita_immobiliare_id', 'nominativo']); foreach ($rows as $row) { $unitId = (int) ($row->unita_immobiliare_id ?? 0); - if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) { + if ($unitId <= 0) { continue; } $name = trim((string) ($row->nominativo ?? '')); if ($name !== '') { - $currentNominativoByUnita[$unitId] = $name; - } - } - } catch (\Throwable $e) { - $currentNominativoByUnita = []; - } - } - - if (DbSchema::hasTable('persone_unita_relazioni') && DbSchema::hasTable('persone')) { - try { - $today = now()->toDateString(); - $rows = DB::table('persone_unita_relazioni as pur') - ->join('persone as p', 'pur.persona_id', '=', 'p.id') - ->whereIn('pur.unita_id', $unita->pluck('id')) - ->where(function ($query) use ($today): void { - $query->whereNull('pur.data_inizio')->orWhere('pur.data_inizio', '<=', $today); - }) - ->where(function ($query) use ($today): void { - $query->whereNull('pur.data_fine')->orWhere('pur.data_fine', '>=', $today); - }) - ->where('pur.attivo', 1) - ->orderByRaw("CASE WHEN pur.tipo_relazione = 'proprietario' THEN 0 ELSE 1 END") - ->orderByDesc('pur.data_inizio') - ->select('pur.unita_id', 'p.cognome', 'p.nome', 'p.ragione_sociale') - ->get(); - - foreach ($rows as $row) { - $unitId = (int) ($row->unita_id ?? 0); - if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) { - continue; - } - - $name = trim((string) ($row->ragione_sociale ?? '')); - if ($name === '') { - $name = trim((string) ($row->cognome ?? '') . ' ' . (string) ($row->nome ?? '')); - } - if ($name !== '') { - $currentNominativoByUnita[$unitId] = $name; + if (! isset($currentNominativoByUnita[$unitId])) { + $currentNominativoByUnita[$unitId] = []; + } + if (! in_array($name, $currentNominativoByUnita[$unitId], true)) { + $currentNominativoByUnita[$unitId][] = $name; + } } } } catch (\Throwable $e) { @@ -827,42 +820,11 @@ protected function refreshUnitaOptions(): void } } - if (DbSchema::hasTable('rubrica_ruoli') && DbSchema::hasTable('rubrica_universale')) { - try { - $today = now()->toDateString(); - $rows = DB::table('rubrica_ruoli as rr') - ->join('rubrica_universale as ru', 'rr.rubrica_id', '=', 'ru.id') - ->whereIn('rr.unita_id', $unita->pluck('id')) - ->where(function ($query) use ($today): void { - $query->whereNull('rr.data_inizio')->orWhere('rr.data_inizio', '<=', $today); - }) - ->where(function ($query) use ($today): void { - $query->whereNull('rr.data_fine')->orWhere('rr.data_fine', '>=', $today); - }) - ->where('rr.attivo', 1) - ->orderByRaw("CASE WHEN rr.ruolo = 'proprietario' THEN 0 ELSE 1 END") - ->orderByDesc('rr.data_inizio') - ->select('rr.unita_id', 'ru.cognome', 'ru.nome', 'ru.ragione_sociale') - ->get(); - - foreach ($rows as $row) { - $unitId = (int) ($row->unita_id ?? 0); - if ($unitId <= 0 || isset($currentNominativoByUnita[$unitId])) { - continue; - } - - $name = trim((string) ($row->ragione_sociale ?? '')); - if ($name === '') { - $name = trim((string) ($row->cognome ?? '') . ' ' . (string) ($row->nome ?? '')); - } - if ($name !== '') { - $currentNominativoByUnita[$unitId] = $name; - } - } - } catch (\Throwable $e) { - // Ignore fallback error - } + $formattedNominativoByUnita = []; + foreach ($currentNominativoByUnita as $uId => $namesArr) { + $formattedNominativoByUnita[$uId] = is_array($namesArr) ? implode(' / ', $namesArr) : (string) $namesArr; } + $currentNominativoByUnita = $formattedNominativoByUnita; $sorted = $unita->sort(function (UnitaImmobiliare $a, UnitaImmobiliare $b): int { $pa = (int) ($a->palazzina_id ?? 0); diff --git a/app/Services/TenantArchivePathService.php b/app/Services/TenantArchivePathService.php index a6f8bc2..017eaa9 100755 --- a/app/Services/TenantArchivePathService.php +++ b/app/Services/TenantArchivePathService.php @@ -42,7 +42,7 @@ public function stabileCode(Stabile | string $stabile): string return $code; } - $code = trim((string) ($stabile->codice_univoco ?: $stabile->codice_stabile ?: '')); + $code = trim((string) ($stabile->codice_stabile ?: $stabile->codice_univoco ?: '')); if ($code === '') { throw new InvalidArgumentException('Stabile senza codice archivio canonico.');