diff --git a/app/Console/Commands/GesconBonificaDuplicatiBenedettoCommand.php b/app/Console/Commands/GesconBonificaDuplicatiBenedettoCommand.php new file mode 100644 index 0000000..35de7db --- /dev/null +++ b/app/Console/Commands/GesconBonificaDuplicatiBenedettoCommand.php @@ -0,0 +1,97 @@ +info('Starting bonifica idempotente Benedetto Daniela...'); + + $canonicalPersona = DB::table('persone')->where('codice_fiscale', 'BNDDNL86M58H224O')->first() + ?? DB::table('persone')->find(323); + $canonicalPersonaId = $canonicalPersona?->id ?? 323; + + $canonicalRubrica = DB::table('rubrica_universale')->where('codice_fiscale', 'BNDDNL86M58H224O')->first() + ?? DB::table('rubrica_universale')->where('codice_univoco', '000000JC')->first() + ?? DB::table('rubrica_universale')->find(697); + $canonicalRubricaId = $canonicalRubrica?->id ?? 697; + + $duplicatePersonaIds = DB::table('persone') + ->where('cognome', 'BENEDETTO') + ->where('id', '!=', $canonicalPersonaId) + ->pluck('id') + ->all(); + + $duplicateRubricaIds = DB::table('rubrica_universale') + ->where('cognome', 'BENEDETTO') + ->where('id', '!=', $canonicalRubricaId) + ->pluck('id') + ->all(); + + // 1. Audit before counts + $purBefore = DB::table('persone_unita_relazioni')->whereIn('persona_id', $duplicatePersonaIds)->count(); + $rrBefore = DB::table('rubrica_ruoli')->whereIn('rubrica_id', $duplicateRubricaIds)->count(); + + $this->info(sprintf('BEFORE: Duplicate PUR=%d, Duplicate RR=%d', $purBefore, $rrBefore)); + + // 2. Remap relations in persone_unita_relazioni + if (! empty($duplicatePersonaIds)) { + DB::table('persone_unita_relazioni') + ->whereIn('persona_id', $duplicatePersonaIds) + ->update([ + 'persona_id' => $canonicalPersonaId, + 'updated_at' => now(), + ]); + + DB::table('persone') + ->whereIn('id', $duplicatePersonaIds) + ->update([ + 'attivo' => 0, + 'note' => '[ARCHIVIATO_DUPLICATO] Riconfigurato verso Persona ' . $canonicalPersonaId . ' CF BNDDNL86M58H224O', + 'updated_at' => now(), + ]); + } + + // 3. Remap roles in rubrica_ruoli & soft archive rubrica + if (! empty($duplicateRubricaIds)) { + DB::table('rubrica_ruoli') + ->whereIn('rubrica_id', $duplicateRubricaIds) + ->update([ + 'rubrica_id' => $canonicalRubricaId, + 'updated_at' => now(), + ]); + + DB::table('rubrica_universale') + ->whereIn('id', $duplicateRubricaIds) + ->update([ + 'stato' => 'inattivo', + 'note' => '[ARCHIVIATO_DUPLICATO] Soft archived verso Rubrica ' . $canonicalRubricaId . ' (CF BNDDNL86M58H224O)', + 'updated_at' => now(), + ]); + } + + // 4. Ensure canonical persona & rubrica remain active + if ($canonicalPersonaId) { + DB::table('persone')->where('id', $canonicalPersonaId)->update(['attivo' => 1]); + } + if ($canonicalRubricaId) { + DB::table('rubrica_universale')->where('id', $canonicalRubricaId)->update(['stato' => 'attivo']); + } + + // 5. Audit after counts + $purAfter = DB::table('persone_unita_relazioni')->whereIn('persona_id', $duplicatePersonaIds)->count(); + $rrAfter = DB::table('rubrica_ruoli')->whereIn('rubrica_id', $duplicateRubricaIds)->count(); + + $this->info(sprintf('AUDIT: Remaining Duplicate PUR=%d, Remaining Duplicate RR=%d, Canonical Persona Active=1, Canonical Rubrica Status=attivo', $purAfter, $rrAfter)); + + return Command::SUCCESS; + } +} diff --git a/app/Filament/Pages/UnitaImmobiliarePage.php b/app/Filament/Pages/UnitaImmobiliarePage.php index bc80470..498f889 100755 --- a/app/Filament/Pages/UnitaImmobiliarePage.php +++ b/app/Filament/Pages/UnitaImmobiliarePage.php @@ -104,6 +104,8 @@ public static function canAccess(): bool public array $relazioniPerTipo = []; + public bool $mostraStorico = false; + public array $canaliComunicazione = []; public array $nominativiStorici = []; @@ -2297,9 +2299,16 @@ private function formatLegacyDate(mixed $value): ?string private function relationIdentityKey(array $rel): string { - $legacyPersonaId = is_numeric($rel['legacy_persona_id'] ?? null) ? (int) $rel['legacy_persona_id'] : 0; - if ($legacyPersonaId > 0) { - return 'legacy:' . $legacyPersonaId; + $cf = strtoupper(trim((string) ($rel['codice_fiscale'] ?? ''))); + if ($cf !== '') { + return 'cf:' . $cf; + } + + $rawName = trim((string) ($rel['nome'] ?? '')); + if ($rawName !== '') { + $parts = preg_split('/\s+/', mb_strtolower($rawName)); + sort($parts); + return 'name:' . implode(' ', $parts); } $personaId = is_numeric($rel['persona_id'] ?? null) ? (int) $rel['persona_id'] : 0; @@ -2307,12 +2316,13 @@ private function relationIdentityKey(array $rel): string return 'persona:' . $personaId; } - $cf = strtoupper(trim((string) ($rel['codice_fiscale'] ?? ''))); - if ($cf !== '') { - return 'cf:' . $cf; - } + return 'id:0'; + } - return 'name:' . strtolower(trim((string) ($rel['nome'] ?? ''))); + public function toggleMostraStorico(): void + { + $this->mostraStorico = ! $this->mostraStorico; + $this->hydrateRelazioni(); } private function loadCurrentArchiveRelazioni(string $ruolo): array @@ -2944,126 +2954,69 @@ protected function hydrateRelazioni(): void })->values(); } + $existingKeys = $proprietari->concat($inquilini) + ->map(fn(array $r) => $this->relationIdentityKey($r)) + ->all(); + $altri = $relazioniMapped - ->filter(function ($rel) { - return $rel['attivo'] && ( - ! in_array(strtolower($rel['tipo_raw'] ?? ''), [ - 'condomino', - 'proprietario', - 'comproprietario', - 'nudo_proprietario', - 'usufruttuario', - 'usufrutto', - 'inquilino', - 'locatario', - 'conduttore', - ], true) - || ! empty($rel['has_custom_role']) - ); + ->filter(function ($rel) use ($existingKeys) { + $key = $this->relationIdentityKey($rel); + if (in_array($key, $existingKeys, true)) { + return false; + } + + $tipoRaw = strtolower(trim((string) ($rel['tipo_raw'] ?? ''))); + if (in_array($tipoRaw, [ + 'condomino', + 'proprietario', + 'comproprietario', + 'nudo_proprietario', + 'usufruttuario', + 'usufrutto', + 'inquilino', + 'locatario', + 'conduttore', + ], true)) { + return false; + } + + return (bool) ($rel['attivo'] ?? false); }) - ->unique(fn(array $r) => strtolower((string) ($r['tipo_raw'] ?? 'altro')) . '|' . strtolower((string) ($r['ruolo_custom'] ?? '')) . '|' . (int) ($r['persona_id'] ?? 0)) + ->unique(fn(array $r) => $this->relationIdentityKey($r)) ->values(); - if ($proprietari->isEmpty()) { - $proprietari = $relazioniMapped - ->filter(function ($rel) { - return $rel['attivo'] && in_array(strtolower($rel['tipo_raw'] ?? ''), [ - 'condomino', - 'proprietario', - 'comproprietario', - 'nudo_proprietario', - 'usufruttuario', - 'usufrutto', - ], true); - }) + $isRelazioneCorrenteNelAnno = function (array $r, int $targetAnno): bool { + $dFine = $r['data_fine'] ?? null; + $endY = null; + if (! empty($dFine) && preg_match('/(\d{4})/', (string) $dFine, $m)) { + $endY = (int) $m[1]; + } + if ($endY !== null && $endY < $targetAnno) { + return false; + } + if ($endY !== null && $endY === $targetAnno && ! ($r['attivo'] ?? false)) { + return false; + } + return true; + }; + + $relazioniStoriche = []; + if (! $this->mostraStorico) { + $relazioniStoriche = $proprietari->filter(fn($r) => ! $isRelazioneCorrenteNelAnno($r, $activeAnno)) + ->concat($inquilini->filter(fn($r) => ! $isRelazioneCorrenteNelAnno($r, $activeAnno))) ->unique(fn(array $r) => $this->relationIdentityKey($r)) - ->values(); - } + ->values() + ->all(); - $legacyOwnerStartC = $this->parseLegacyDateToCarbon($legacyOwnerStart); - $legacyOwnerEndC = $this->parseLegacyDateToCarbon($legacyOwnerEnd); - $legacyOwnerNewStartC = $legacyOwnerEndC ? $legacyOwnerEndC->copy()->addDay() : $legacyOwnerStartC; - - if ($legacyOwnerStartC || $legacyOwnerEndC) { - $proprietari = $proprietari->values(); - $count = $proprietari->count(); - if ($count === 1) { - $proprietari = $proprietari->map(function (array $p) use ($legacyOwnerStartC, $legacyOwnerEndC): array { - if (empty($p['data_inizio'])) { - $p['data_inizio'] = $legacyOwnerStartC?->format('d/m/Y') ?? null; - } - if (empty($p['data_fine'])) { - $p['data_fine'] = $legacyOwnerEndC?->format('d/m/Y') ?? null; - } - return $p; - }); - } elseif ($count >= 2) { - $idxNew = 0; - if ($legacyOwnerName !== '') { - foreach ($proprietari as $idx => $p) { - $nome = trim((string) ($p['nome'] ?? '')); - if ($nome !== '' && str_contains(mb_strtolower($nome), mb_strtolower($legacyOwnerName))) { - $idxNew = $idx; - break; - } - } - } - $idxOld = $idxNew === 0 ? ($count - 1) : 0; - $proprietari = $proprietari->map(function (array $p, int $idx) use ($idxNew, $idxOld, $legacyOwnerEndC, $legacyOwnerNewStartC): array { - if ($idx === $idxNew && $legacyOwnerNewStartC && empty($p['data_inizio'])) { - $p['data_inizio'] = $legacyOwnerNewStartC->format('d/m/Y'); - } - if ($idx === $idxOld && $legacyOwnerEndC && empty($p['data_fine'])) { - $p['data_fine'] = $legacyOwnerEndC->format('d/m/Y'); - } - return $p; - })->values(); - } - } - - $legacyInqStartC = $this->parseLegacyDateToCarbon($legacyInqStart); - $legacyInqEndC = $this->parseLegacyDateToCarbon($legacyInqEnd); - if ($legacyInqStartC || $legacyInqEndC) { - $inquilini = $inquilini->values(); - $count = $inquilini->count(); - if ($count === 1) { - $inquilini = $inquilini->map(function (array $i) use ($legacyInqStartC, $legacyInqEndC): array { - if (empty($i['data_inizio'])) { - $i['data_inizio'] = $legacyInqStartC?->format('d/m/Y') ?? null; - } - if (empty($i['data_fine'])) { - $i['data_fine'] = $legacyInqEndC?->format('d/m/Y') ?? null; - } - return $i; - }); - } elseif ($count >= 2) { - $idxNew = 0; - if ($legacyInqName !== '') { - foreach ($inquilini as $idx => $i) { - $nome = trim((string) ($i['nome'] ?? '')); - if ($nome !== '' && str_contains(mb_strtolower($nome), mb_strtolower($legacyInqName))) { - $idxNew = $idx; - break; - } - } - } - $idxOld = $idxNew === 0 ? ($count - 1) : 0; - $inquilini = $inquilini->map(function (array $i, int $idx) use ($idxNew, $idxOld, $legacyInqEndC, $legacyInqStartC): array { - if ($idx === $idxNew && $legacyInqStartC && empty($i['data_inizio'])) { - $i['data_inizio'] = $legacyInqStartC->format('d/m/Y'); - } - if ($idx === $idxOld && $legacyInqEndC && empty($i['data_fine'])) { - $i['data_fine'] = $legacyInqEndC->format('d/m/Y'); - } - return $i; - })->values(); - } + $proprietari = $proprietari->filter(fn($r) => $isRelazioneCorrenteNelAnno($r, $activeAnno))->values(); + $inquilini = $inquilini->filter(fn($r) => $isRelazioneCorrenteNelAnno($r, $activeAnno))->values(); } $this->relazioniPerTipo = [ 'proprietari' => $proprietari->values()->all(), 'inquilini' => $inquilini->values()->all(), 'altri' => $altri->values()->all(), + 'storico' => $relazioniStoriche, ]; } @@ -3082,6 +3035,19 @@ private function ownershipTipoDiritti(): array private function isRelazioneAttivaNelAnno(array $rel, int $targetAnno): bool { + $note = (string) ($rel['note_relazione'] ?? ''); + if (! empty($note) && preg_match('/gestione:\s*(000[1-4])/i', $note, $m)) { + $gYear = match ($m[1]) { + '0004' => 2026, + '0003' => 2025, + '0001' => 2024, + default => null, + }; + if ($gYear !== null && $gYear !== $targetAnno) { + return false; + } + } + $dInizio = $rel['data_inizio'] ?? null; $dFine = $rel['data_fine'] ?? null; diff --git a/resources/views/filament/pages/unita-immobiliare.blade.php b/resources/views/filament/pages/unita-immobiliare.blade.php index 2d2e503..cb1e504 100755 --- a/resources/views/filament/pages/unita-immobiliare.blade.php +++ b/resources/views/filament/pages/unita-immobiliare.blade.php @@ -66,7 +66,14 @@ class="inline-flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-s