From 6fb8d20af30eab1c66d165cbc9f289e3bc515323 Mon Sep 17 00:00:00 2001 From: michele Date: Fri, 14 Aug 2026 20:14:56 +0200 Subject: [PATCH] feat(anagrafica-fornitori): materialize consolidated persons, promote legacy fornitori and index custom sidebar pages (task-25f318e499) --- .../GesconSyncAnagraficaConsolidata.php | 146 ++++++++++++++++++ app/Filament/Pages/Gescon/AnagraficaUnica.php | 2 +- .../components/sidebar-search.blade.php | 69 ++++++--- skill-netgescon/control-tower/CURRENT-205.md | 24 ++- tests/Feature/AnagraficaFornitoriSyncTest.php | 65 ++++++++ 5 files changed, 279 insertions(+), 27 deletions(-) create mode 100644 app/Console/Commands/GesconSyncAnagraficaConsolidata.php create mode 100644 tests/Feature/AnagraficaFornitoriSyncTest.php diff --git a/app/Console/Commands/GesconSyncAnagraficaConsolidata.php b/app/Console/Commands/GesconSyncAnagraficaConsolidata.php new file mode 100644 index 0000000..101b3ca --- /dev/null +++ b/app/Console/Commands/GesconSyncAnagraficaConsolidata.php @@ -0,0 +1,146 @@ +argument('amministratore_id'); + $mdb = (string) $this->option('mdb'); + + $this->info("Inizio sincronizzazione Anagrafica Unica e Fornitori per Amministratore ID {$adminId}..."); + + // 1. Promozione Fornitori + if (file_exists($mdb)) { + $this->info("Importazione/Promozione fornitori da MDB ({$mdb})..."); + Artisan::call('gescon:import-fornitori-legacy', [ + 'amministratore_id' => $adminId, + '--mdb' => $mdb, + ]); + $this->info(trim(Artisan::output())); + } else { + $this->warn("File MDB non trovato in {$mdb}, prosiguo con la sincronizzazione delle persone..."); + } + + // 2. Materializzazione Persone in Rubrica Universale + $persone = DB::table('persone')->get(); + $this->info("Sincronizzazione persone consolidate: {$persone->count()} record..."); + + $createdRubrica = 0; + $updatedRubrica = 0; + $createdRuoli = 0; + + foreach ($persone as $p) { + $existing = null; + if (Schema::hasColumn('rubrica_universale', 'persona_id')) { + $existing = RubricaUniversale::where('amministratore_id', $adminId) + ->where('persona_id', $p->id) + ->first(); + } + + if (! $existing && ! empty($p->codice_fiscale)) { + $existing = RubricaUniversale::where('amministratore_id', $adminId) + ->where('codice_fiscale', $p->codice_fiscale) + ->first(); + } + + if (! $existing && ! empty($p->partita_iva)) { + $existing = RubricaUniversale::where('amministratore_id', $adminId) + ->where('partita_iva', $p->partita_iva) + ->first(); + } + + $displayName = trim(($p->cognome ?? '') . ' ' . ($p->nome ?? '')); + if ($displayName === '' && ! empty($p->ragione_sociale)) { + $displayName = $p->ragione_sociale; + } + + $payload = [ + 'amministratore_id' => $adminId, + 'persona_id' => $p->id, + 'cognome' => $p->cognome, + 'nome' => $p->nome, + 'ragione_sociale' => $p->ragione_sociale ?: ($displayName !== '' ? $displayName : 'Persona Consolidata #' . $p->id), + 'codice_fiscale' => $p->codice_fiscale, + 'partita_iva' => $p->partita_iva, + 'telefono_ufficio' => $p->telefono_principale, + 'telefono_cellulare' => $p->telefono_secondario, + 'email' => $p->email_principale, + 'pec' => $p->email_pec, + 'categoria' => 'condomino', + 'stato' => $p->attivo ? 'attivo' : 'inattivo', + 'tipo_contatto' => ($p->tipologia === 'giuridica' || ! empty($p->partita_iva)) ? 'persona_giuridica' : 'persona_fisica', + 'note' => $p->note, + ]; + + if (! Schema::hasColumn('rubrica_universale', 'persona_id')) { + unset($payload['persona_id']); + } + + $rubricaRecord = null; + if ($existing) { + $existing->fill(array_filter($payload, fn($v) => $v !== null && $v !== '')); + if ($existing->isDirty()) { + $existing->save(); + $updatedRubrica++; + } + $rubricaRecord = $existing; + } else { + $rubricaRecord = RubricaUniversale::create($payload); + $createdRubrica++; + } + + // Sync rubrica_ruoli + $rels = DB::table('persone_unita_relazioni')->where('persona_id', $p->id)->get(); + foreach ($rels as $rel) { + $unit = DB::table('unita_immobiliari')->where('id', $rel->unita_id)->first(); + if (! $unit) { + continue; + } + + $standardRole = match ($rel->tipo_relazione) { + 'inquilino' => 'inquilino', + default => 'condomino', + }; + + $roleExisting = DB::table('rubrica_ruoli') + ->where('rubrica_id', $rubricaRecord->id) + ->where('unita_immobiliare_id', $rel->unita_id) + ->where('ruolo_custom', $rel->tipo_relazione) + ->first(); + + if (! $roleExisting) { + DB::table('rubrica_ruoli')->insert([ + 'rubrica_id' => $rubricaRecord->id, + 'stabile_id' => $unit->stabile_id, + 'unita_immobiliare_id' => $rel->unita_id, + 'ruolo_standard' => $standardRole, + 'ruolo_custom' => $rel->tipo_relazione, + 'is_attivo' => $rel->attivo, + 'created_at' => now(), + 'updated_at' => now(), + ]); + $createdRuoli++; + } + } + } + + $this->info("✅ Sincronizzazione anagrafica completata! Rubrica create: {$createdRubrica}, aggiornate: {$updatedRubrica}, ruoli creati: {$createdRuoli}."); + + return self::SUCCESS; + } +} diff --git a/app/Filament/Pages/Gescon/AnagraficaUnica.php b/app/Filament/Pages/Gescon/AnagraficaUnica.php index b0b41b2..4c4cad4 100755 --- a/app/Filament/Pages/Gescon/AnagraficaUnica.php +++ b/app/Filament/Pages/Gescon/AnagraficaUnica.php @@ -16,7 +16,7 @@ class AnagraficaUnica extends Page protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-user-group'; - protected static UnitEnum|string|null $navigationGroup = null; + protected static UnitEnum|string|null $navigationGroup = 'Anagrafica'; protected static ?int $navigationSort = 2; diff --git a/resources/views/filament/components/sidebar-search.blade.php b/resources/views/filament/components/sidebar-search.blade.php index 2495999..3a5e516 100644 --- a/resources/views/filament/components/sidebar-search.blade.php +++ b/resources/views/filament/components/sidebar-search.blade.php @@ -3,7 +3,7 @@
@@ -15,29 +15,42 @@ class="w-full text-xs rounded-lg border border-gray-200 dark:border-gray-800 bg-
diff --git a/skill-netgescon/control-tower/CURRENT-205.md b/skill-netgescon/control-tower/CURRENT-205.md index 91057be..9f2a180 100644 --- a/skill-netgescon/control-tower/CURRENT-205.md +++ b/skill-netgescon/control-tower/CURRENT-205.md @@ -1,29 +1,36 @@ # CURRENT-205 -TASK_ID: task-9da25e193c +TASK_ID: task-25f318e499 MACHINE: .205 STATO: completato ## Obiettivo -Audit read-only completo delle superfici UI Filament per lo stabile 0021 (`stabile_id = 23`). Tracciamento delle query, DB connection, modelli e filtri applicati via `StabileContext` (Amministratore ID `12` / Stabile ID `23`). Verificata la coerenza dell'header context temporale per il caso obbligatorio Scala A Int 11 (`0021-A-11` / Unit ID 13) nelle gestioni `0004` (2026), `0003` (2025) e `0001` (2024). Prodotta la `MATRICE_UI_ARCHIVI` analitica per tutte le superfici richieste. +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. ## Output del Giro Operativo ESITO_205: riuscito -TASK_ID: task-9da25e193c +TASK_ID: task-25f318e499 REPOSITORY: ssh://git@git.netgescon.it:2222/michele/netgescon-day0.git BRANCH: stabilization/205-zero COMMIT: PENDING_COMMIT 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 - skill-netgescon/control-tower/CURRENT-205.md TEST_ESEGUITI: -- ./vendor/bin/pest tests/Feature/UnitaImmobiliarePageTest.php tests/Feature/ReconstructMirror0021Test.php tests/Feature/ImportCondominMirror0021Test.php tests/Feature/ControlTowerPollCommandTest.php (9 passed, 76 assertions) +- ./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) GATE_STATISTICS: -- AUDIT_READ_ONLY_COMPLETED: true (0 modifiche effettuate a DB, file, MDB o UI) -- CONSOLIDATED_UNITS_AUDITED: 230 (100% allineate su NominativiStabile e UnitaImmobiliarePage) -- TEMPORAL_TIMELINE_A11_VERIFIED: true (0004 -> Benedetto Daniela; 0003/0001 -> ATER C/O UFFICIO CONDOMINI PASSIVI) -- MATRICE_UI_ARCHIVI_PRODUCED: true +- 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) BLOCCO_DATI: no BLOCCO_CONTRATTO: no RISCHI_APERTI: nessuno @@ -33,3 +40,4 @@ ## Output del Giro Operativo + diff --git a/tests/Feature/AnagraficaFornitoriSyncTest.php b/tests/Feature/AnagraficaFornitoriSyncTest.php new file mode 100644 index 0000000..12366e4 --- /dev/null +++ b/tests/Feature/AnagraficaFornitoriSyncTest.php @@ -0,0 +1,65 @@ +insertOrIgnore(['id' => 12, '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'); + } +}); + +test('gescon:sync-anagrafica-consolidata materializes persons and promotes fornitori including Nethome 246', function () { + Artisan::call('gescon:sync-anagrafica-consolidata', ['amministratore_id' => 12]); + + $rubricaCount = RubricaUniversale::where('amministratore_id', 12)->count(); + expect($rubricaCount)->toBeGreaterThan(300); + + $fornitoriCount = Fornitore::where('amministratore_id', 12)->count(); + expect($fornitoriCount)->toBeGreaterThan(0); + + $nethome = Fornitore::where('amministratore_id', 12) + ->where(function ($q) { + $q->where('old_id', 246)->orWhere('cod_forn', 246)->orWhere('partita_iva', '10055221005'); + })->first(); + + expect($nethome)->not->toBeNull(); + expect($nethome->ragione_sociale)->toContain('NETHOME'); + expect($nethome->partita_iva)->toBe('10055221005'); + + $stabiliTotal = Stabile::count(); + expect($stabiliTotal)->toBeGreaterThan(0); + + $stabile0021 = Stabile::where('codice_stabile', '0021')->first(); + expect($stabile0021)->not->toBeNull(); + + $unitsCount = UnitaImmobiliare::where('stabile_id', $stabile0021->id)->count(); + expect($unitsCount)->toBe(230); + + $unitA11 = UnitaImmobiliare::where('stabile_id', $stabile0021->id)->where('scala', 'A')->where('interno', '11')->first(); + expect($unitA11)->not->toBeNull(); + + $rels = DB::table('persone_unita_relazioni')->where('unita_id', $unitA11->id)->get(); + expect($rels->count())->toBeGreaterThanOrEqual(2); + + $aterRel = $rels->firstWhere('attivo', 0); + expect($aterRel)->not->toBeNull(); + + $benedettoRel = $rels->firstWhere('attivo', 1); + expect($benedettoRel)->not->toBeNull(); +});