diff --git a/app/Filament/Pages/UnitaImmobiliarePage.php b/app/Filament/Pages/UnitaImmobiliarePage.php index e5ce021..8d0c475 100755 --- a/app/Filament/Pages/UnitaImmobiliarePage.php +++ b/app/Filament/Pages/UnitaImmobiliarePage.php @@ -2676,17 +2676,33 @@ protected function hydrateRelazioni(): void ->unique(fn(array $d) => (int) ($d['soggetto_id'] ?? 0)) ->values(); - $today = now()->toDateString(); + $activeAnno = \App\Support\AnnoGestioneContext::resolveActiveAnno(\Illuminate\Support\Facades\Auth::user()); + $today = sprintf('%04d-12-31', $activeAnno); + $startOfYear = sprintf('%04d-01-01', $activeAnno); + + $purList = PersonaUnitaRelazione::with('persona') + ->where('unita_id', $this->unita->id) + ->get(); + $cfDatesMap = []; + foreach ($purList as $pur) { + $cf = strtoupper(trim((string) ($pur->persona?->codice_fiscale ?? ''))); + if ($cf !== '') { + $cfDatesMap[$cf] = [ + 'data_inizio' => $pur->data_inizio ?: $pur->subentrato_dal, + 'data_fine' => $pur->data_fine ?: $pur->attivo_fino_al, + ]; + } + } + $relazioniMapped = RubricaRuolo::query() ->with(['contatto']) ->where('stabile_id', $this->stabileId) ->where('unita_immobiliare_id', $this->unita->id) - ->where('is_attivo', true) ->where(function (\Illuminate\Database\Eloquent\Builder $q) use ($today) { $q->whereNull('data_inizio')->orWhere('data_inizio', '<=', $today); }) - ->where(function (\Illuminate\Database\Eloquent\Builder $q) use ($today) { - $q->whereNull('data_fine')->orWhere('data_fine', '>=', $today); + ->where(function (\Illuminate\Database\Eloquent\Builder $q) use ($startOfYear) { + $q->whereNull('data_fine')->orWhere('data_fine', '>=', $startOfYear); }) ->orderByDesc('is_preferito') ->orderByDesc('data_inizio') @@ -2694,13 +2710,18 @@ protected function hydrateRelazioni(): void ->limit(200) ->get() ->filter(fn(RubricaRuolo $relazione) => (bool) $relazione->contatto) - ->map(function (RubricaRuolo $relazione) use ($formatDate, $labelsRuoli) { + ->map(function (RubricaRuolo $relazione) use ($formatDate, $labelsRuoli, $cfDatesMap) { $contatto = $relazione->contatto; $nome = trim((string) ($contatto?->nome_completo ?? '')); if ($nome === '') { $nome = $contatto ? 'Contatto #' . $contatto->id : 'Contatto non definito'; } + $cf = strtoupper(trim((string) ($contatto?->codice_fiscale ?? ''))); + $purDates = $cf !== '' ? ($cfDatesMap[$cf] ?? null) : null; + $dInizioRaw = $relazione->data_inizio ?: ($purDates['data_inizio'] ?? null); + $dFineRaw = $relazione->data_fine ?: ($purDates['data_fine'] ?? null); + $tipoRaw = strtolower(trim((string) ($relazione->ruolo_standard ?? ''))); $customRaw = trim((string) ($relazione->ruolo_custom ?? '')); $hasMeaningfulCustomRole = $customRaw !== '' && ! in_array(strtolower($customRaw), ['p', 'i', 'c', 'u'], true); @@ -2740,12 +2761,12 @@ protected function hydrateRelazioni(): void 'ruolo_rate' => $ruoloRate, 'quota' => $quota, 'quota_label' => $quota !== null ? number_format($quota, 2, ',', '.') : null, - 'data_inizio' => $formatDate($relazione->data_inizio), - 'data_fine' => $formatDate($relazione->data_fine), - 'attivo' => true, + 'data_inizio' => $formatDate($dInizioRaw), + 'data_fine' => $formatDate($dFineRaw), + 'attivo' => (bool) $relazione->is_attivo, 'telefono' => trim((string) ($contatto?->telefono_cellulare ?: $contatto?->telefono_ufficio ?: $contatto?->telefono_casa ?: '')), 'email' => trim((string) ($contatto?->email ?? '')), - 'rubrica_url' => $contatto ? RubricaUniversaleScheda::getUrl(['record' => (int) $contatto->id], panel : 'admin-filament'): null, + 'rubrica_url' => $contatto ? RubricaUniversaleScheda::getUrl(['record' => (int) $contatto->id], panel: 'admin-filament'): null, 'riceve_comunicazioni' => false, 'riceve_convocazioni' => false, 'vota_assemblea' => false, @@ -2784,8 +2805,8 @@ protected function hydrateRelazioni(): void 'ruolo_rate' => $relazione->ruolo_rate ?: PersonaUnitaRelazione::deriveRuoloRate($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), + 'data_inizio' => $formatDate($relazione->data_inizio ?: $relazione->subentrato_dal), + 'data_fine' => $formatDate($relazione->data_fine ?: $relazione->attivo_fino_al), 'attivo' => $relazione->isAttiva(), 'telefono' => trim((string) ($persona?->telefono ?? '')), 'email' => trim((string) ($persona?->email ?? '')), @@ -2796,21 +2817,21 @@ protected function hydrateRelazioni(): void ]; }); - $relazioniKeys = $relazioniMapped + $fallbackKeys = $fallbackRelations ->map(fn(array $rel) => strtolower(trim((string) ($rel['tipo_raw'] ?? ''))) . '|' . $this->relationIdentityKey($rel)) ->all(); - $relazioniMapped = $relazioniMapped - ->concat($fallbackRelations->reject(function (array $rel) use ($relazioniKeys): bool { + $relazioniMapped = $fallbackRelations + ->concat($relazioniMapped->reject(function (array $rel) use ($fallbackKeys): bool { $key = strtolower(trim((string) ($rel['tipo_raw'] ?? ''))) . '|' . $this->relationIdentityKey($rel); - return in_array($key, $relazioniKeys, true); + return in_array($key, $fallbackKeys, true); })) ->values(); $proprietariDaRelazioni = $relazioniMapped - ->filter(function ($rel) { - return $rel['attivo'] && in_array(strtolower($rel['tipo_raw'] ?? ''), [ + ->filter(function ($rel) use ($activeAnno) { + $isProp = in_array(strtolower($rel['tipo_raw'] ?? ''), [ 'condomino', 'proprietario', 'comproprietario', @@ -2818,7 +2839,14 @@ protected function hydrateRelazioni(): void 'usufruttuario', 'usufrutto', ], true); + + if (! $isProp) { + return false; + } + + return $this->isRelazioneAttivaNelAnno($rel, $activeAnno); }) + ->sortByDesc(fn(array $r) => ($r['attivo'] ? 1 : 0)) ->unique(fn(array $r) => $this->relationIdentityKey($r)) ->values(); @@ -2827,13 +2855,20 @@ protected function hydrateRelazioni(): void } $inquilini = $relazioniMapped - ->filter(function ($rel) { - return $rel['attivo'] && in_array(strtolower($rel['tipo_raw'] ?? ''), [ + ->filter(function ($rel) use ($activeAnno) { + $isInq = in_array(strtolower($rel['tipo_raw'] ?? ''), [ 'inquilino', 'locatario', 'conduttore', ], true); + + if (! $isInq) { + return false; + } + + return $this->isRelazioneAttivaNelAnno($rel, $activeAnno); }) + ->sortByDesc(fn(array $r) => ($r['attivo'] ? 1 : 0)) ->unique(fn(array $r) => $this->relationIdentityKey($r)) ->values(); @@ -3043,6 +3078,32 @@ private function ownershipTipoDiritti(): array ]; } + private function isRelazioneAttivaNelAnno(array $rel, int $targetAnno): bool + { + $dInizio = $rel['data_inizio'] ?? null; + $dFine = $rel['data_fine'] ?? null; + + $startY = null; + if (! empty($dInizio) && preg_match('/(\d{4})/', (string) $dInizio, $m)) { + $startY = (int) $m[1]; + } + + $endY = null; + if (! empty($dFine) && preg_match('/(\d{4})/', (string) $dFine, $m)) { + $endY = (int) $m[1]; + } + + if ($startY !== null && $startY > $targetAnno) { + return false; + } + + if ($endY !== null && $endY < $targetAnno) { + return false; + } + + return true; + } + private function extractLegacyRoleFromRateNote(string $note): ?string { $note = trim($note); diff --git a/skill-netgescon/control-tower/CURRENT-205.md b/skill-netgescon/control-tower/CURRENT-205.md index 9f2a180..c411652 100644 --- a/skill-netgescon/control-tower/CURRENT-205.md +++ b/skill-netgescon/control-tower/CURRENT-205.md @@ -1,43 +1,35 @@ # CURRENT-205 -TASK_ID: task-25f318e499 +TASK_ID: task-c1f4a4c534 MACHINE: .205 STATO: completato ## Obiettivo -Materializzazione Anagrafica Unica e promozione Fornitori legacy. Sincronizzate idempotentemente le 322 persone consolidate in `rubrica_universale` e `rubrica_ruoli`. Promossi i fornitori da `Fornitori.mdb` (incluso Nethome sas di Barone `old_id = 246` / `P.IVA 10055221005`). Implementata la ricerca menu sidebar delegata per le custom Page (`Anagrafica Unica`, `NominativiStabile`, `UnitaImmobiliarePage`). Preservati 16/16 stabili e 230/230 selettore unità per lo stabile 0021. Preservata la timeline A/11. +Hotfix contesto gestione scheda unità A/11 (`0021-A-11` / Unit ID 13). Aggiornato `UnitaImmobiliarePage.php` per filtrare la relazione attiva in base all'anno/gestione selezionato nell'header (`AnnoGestioneContext`): +- 0004 / 2026 -> BENEDETTO DANIELA +- 0003 / 2025 -> ATER C/O UFFICIO CONDOMINI PASSIVI +- 0001 / 2024 -> ATER C/O UFFICIO CONDOMINI PASSIVI +Timeline storica completa preservata. Creato test dedicato `UnitaGestioneTemporaleTest.php`. ## Output del Giro Operativo ESITO_205: riuscito -TASK_ID: task-25f318e499 +TASK_ID: task-c1f4a4c534 REPOSITORY: ssh://git@git.netgescon.it:2222/michele/netgescon-day0.git BRANCH: stabilization/205-zero -COMMIT: PENDING_COMMIT +COMMIT: 23191ae6fb01f5c5bf35a84b30f2b02669cd1a22 FILE_O_AREE_TOCCATE: -- app/Console/Commands/GesconSyncAnagraficaConsolidata.php -- app/Filament/Pages/Gescon/AnagraficaUnica.php -- resources/views/filament/components/sidebar-search.blade.php -- tests/Feature/AnagraficaFornitoriSyncTest.php +- app/Filament/Pages/UnitaImmobiliarePage.php +- tests/Feature/UnitaGestioneTemporaleTest.php - skill-netgescon/control-tower/CURRENT-205.md TEST_ESEGUITI: -- ./vendor/bin/pest tests/Feature/AnagraficaFornitoriSyncTest.php tests/Feature/UnitaImmobiliarePageTest.php tests/Feature/ReconstructMirror0021Test.php tests/Feature/ImportCondominMirror0021Test.php tests/Feature/ControlTowerPollCommandTest.php (10 passed, 89 assertions) +- ./vendor/bin/pest tests/Feature/UnitaGestioneTemporaleTest.php tests/Feature/AnagraficaFornitoriSyncTest.php tests/Feature/UnitaImmobiliarePageTest.php tests/Feature/ReconstructMirror0021Test.php tests/Feature/ImportCondominMirror0021Test.php tests/Feature/ControlTowerPollCommandTest.php (11 passed, 105 assertions) GATE_STATISTICS: -- ANAGRAFICA_UNICA_MATERIALIZED: 974 (322 persone consolidate + 366 fornitori + contatti admin) -- FORNITORI_PROMOTED: 366 (incluso Nethome sas di Barone old_id 246 / P.IVA 10055221005) -- NETHOME_VERIFIED_246: true -- STABILI_COUNT: 16/16 -- UNITA_SELETTORE_COUNT: 230/230 -- TIMELINE_A11_UNTOUCHED: true (0004 Benedetto Daniela; 0003/0001 ATER Passivi) -- SIDEBAR_CUSTOM_PAGES_INDEXED: true (Anagrafica Unica, NominativiStabile, UnitaImmobiliarePage) +- A11_2026_OWNER: BENEDETTO DANIELA +- A11_2025_OWNER: ATER C/O UFFICIO CONDOMINI PASSIVI +- A11_2024_OWNER: ATER C/O UFFICIO CONDOMINI PASSIVI +- TIMELINE_PRESERVED: true BLOCCO_DATI: no BLOCCO_CONTRATTO: no RISCHI_APERTI: nessuno - - - - - - - diff --git a/tests/Feature/UnitaGestioneTemporaleTest.php b/tests/Feature/UnitaGestioneTemporaleTest.php new file mode 100644 index 0000000..6505feb --- /dev/null +++ b/tests/Feature/UnitaGestioneTemporaleTest.php @@ -0,0 +1,80 @@ +insertOrIgnore(['id' => 1, 'nome' => 'Admin Test', 'cognome' => 'Test', 'created_at' => now(), 'updated_at' => now()]); + + $stabile = Stabile::where('codice_stabile', '0021')->first(); + if (! $stabile) { + $mirrorCount = DB::connection('gescon_import') + ->table('condomin_mirror') + ->where('cod_stabile', '0021') + ->count(); + if ($mirrorCount === 0) { + Artisan::call('gescon:import-mirror-0021'); + } + Artisan::call('gescon:reconstruct-mirror-0021'); + $stabile = Stabile::where('codice_stabile', '0021')->first(); + } +}); + +test('unita immobiliare page filters active owner by selected anno gestione context (2026, 2025, 2024)', function () { + $user = User::first(); + if (! $user) { + $user = User::factory()->create(); + } + expect($user)->not->toBeNull(); + + try { + if (method_exists($user, 'assignRole')) { + \Spatie\Permission\Models\Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']); + $user->assignRole('admin'); + } + } catch (\Throwable $e) {} + + $stabile = Stabile::where('codice_stabile', '0021')->first(); + expect($stabile)->not->toBeNull(); + + $unit = UnitaImmobiliare::where('stabile_id', $stabile->id)->where('scala', 'A')->where('interno', '11')->first(); + expect($unit)->not->toBeNull(); + + Auth::login($user); + StabileContext::setActiveStabileId($user, $stabile->id); + request()->merge(['unita_id' => $unit->id]); + + // Test Gestione 0004 / 2026 -> BENEDETTO DANIELA + AnnoGestioneContext::setActiveAnno(2026); + $page2026 = new UnitaImmobiliarePage(); + $page2026->mount(); + + $props2026 = $page2026->relazioniPerTipo['proprietari'] ?? []; + expect($props2026)->not->toBeEmpty(); + expect($props2026[0]['nome'])->toContain('BENEDETTO'); + + // Test Gestione 0003 / 2025 -> ATER + AnnoGestioneContext::setActiveAnno(2025); + $page2025 = new UnitaImmobiliarePage(); + $page2025->mount(); + + $props2025 = $page2025->relazioniPerTipo['proprietari'] ?? []; + expect($props2025)->not->toBeEmpty(); + expect($props2025[0]['nome'])->toContain('ATER'); + + // Test Gestione 0001 / 2024 -> ATER + AnnoGestioneContext::setActiveAnno(2024); + $page2024 = new UnitaImmobiliarePage(); + $page2024->mount(); + + $props2024 = $page2024->relazioniPerTipo['proprietari'] ?? []; + expect($props2024)->not->toBeEmpty(); + expect($props2024[0]['nome'])->toContain('ATER'); +});