# NETGESCON - DATABASE COMUNI ITALIANI ## ๐Ÿ“‹ OVERVIEW Integrazione del database completo dei comuni italiani con licenza MIT per gestire indirizzi, calcolo codice fiscale, CAP e dati geografici. Sistema auto-aggiornante collegato alle fonti ISTAT ufficiali. ## ๐Ÿ—‚๏ธ STRUTTURA DATI FORNITA ### Tabelle Principali (6 normalizzate + 2 aggregate) #### 1. gi_nazioni **Scopo**: Calcolo codice fiscale per persone nate all'estero ```sql CREATE TABLE gi_nazioni ( sigla_nazione VARCHAR(10) PRIMARY KEY, -- IT, F, D, USA codice_belfiore VARCHAR(4), -- Codice per CF denominazione_nazione VARCHAR(255), -- ITALIA, FRANCIA denominazione_cittadinanza VARCHAR(255) -- Italiana, Francese ); ``` #### 2. gi_regioni **Scopo**: Classificazione geografica e menรน a tendina ```sql CREATE TABLE gi_regioni ( codice_regione VARCHAR(3) PRIMARY KEY, -- Codice ISTAT ripartizione_geografica VARCHAR(50), -- Nord-ovest, Centro, Sud, Isole denominazione_regione VARCHAR(100), tipologia_regione VARCHAR(50), -- statuto ordinario/speciale numero_province INT, numero_comuni INT, superficie_kmq DECIMAL(10,2) ); ``` #### 3. gi_province **Scopo**: Unitร  territoriali sovracomunali ```sql CREATE TABLE gi_province ( codice_sovracomunale VARCHAR(6) PRIMARY KEY, -- Codice ISTAT codice_regione VARCHAR(3), -- FK sigla_provincia VARCHAR(2), -- MI, RM, BO denominazione_provincia VARCHAR(255), tipologia_provincia VARCHAR(100), -- Provincia, Cittร  metropolitana, etc numero_comuni INT, superficie_kmq DECIMAL(10,2) ); ``` #### 4. gi_comuni โญ (PRINCIPALE) **Scopo**: Tutti i comuni italiani attivi ```sql CREATE TABLE gi_comuni ( codice_istat VARCHAR(6) PRIMARY KEY, -- Codice ISTAT alfanumerico sigla_provincia VARCHAR(2), -- FK denominazione_ita_altra VARCHAR(255), -- Bolzano/Bozen denominazione_ita VARCHAR(255), -- Bolzano denominazione_altra VARCHAR(255), -- Bozen flag_capoluogo ENUM('SI','NO'), codice_belfiore VARCHAR(4), -- PER CALCOLO CODICE FISCALE โญ lat DECIMAL(10,8), -- Coordinate GPS lon DECIMAL(11,8), superficie_kmq DECIMAL(10,2), codice_sovracomunale VARCHAR(6) -- FK ); ``` #### 5. gi_comuni_validita โญ (STORICO) **Scopo**: Comuni cessati - FONDAMENTALE per CF persone anziane ```sql CREATE TABLE gi_comuni_validita ( id BIGINT PRIMARY KEY AUTO_INCREMENT, codice_istat VARCHAR(6), sigla_provincia VARCHAR(2), denominazione_ita VARCHAR(255), codice_belfiore VARCHAR(4), -- CHIAVE per CF data_inizio_validita DATE, data_fine_validita DATE, -- NULL = attivo stato_validita ENUM('Attivo','Inattivo') ); ``` #### 6. gi_cap **Scopo**: Relazione Comuni-CAP (molti a molti) ```sql CREATE TABLE gi_cap ( id BIGINT PRIMARY KEY AUTO_INCREMENT, codice_istat VARCHAR(6), -- FK cap VARCHAR(5) ); ``` ### Tabelle Aggregate (Per performance) #### 7. gi_comuni_nazioni_cf โญ (PER CALCOLO CF) **Scopo**: Unisce comuni + nazioni per calcolo CF completo ```sql CREATE TABLE gi_comuni_nazioni_cf ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sigla_provincia VARCHAR(2), -- EE per estero denominazione_ita VARCHAR(255), -- Comune o Nazione codice_belfiore VARCHAR(4), -- CODICE PER CF โญ data_inizio_validita DATE, data_fine_validita DATE ); ``` #### 8. gi_comuni_cap โญ (PER AUTOCOMPLETAMENTO) **Scopo**: Tutti i dati aggregati per ricerca rapida ```sql CREATE TABLE gi_comuni_cap ( id BIGINT PRIMARY KEY AUTO_INCREMENT, codice_istat VARCHAR(6), denominazione_ita_altra VARCHAR(255), denominazione_ita VARCHAR(255), cap VARCHAR(5), sigla_provincia VARCHAR(2), denominazione_provincia VARCHAR(255), codice_regione VARCHAR(3), denominazione_regione VARCHAR(100), lat DECIMAL(10,8), lon DECIMAL(11,8), codice_belfiore VARCHAR(4), -- โญ SEMPRE PRESENTE flag_capoluogo ENUM('SI','NO') ); ``` ## ๐Ÿงฎ ALGORITMO CALCOLO CODICE FISCALE ### Struttura Codice Fiscale (16 caratteri) ``` RSSMRA80E15H501T โ””โ”€โ”ฌโ”€โ”˜โ””โ”ฌโ”˜โ””โ”ฌโ”˜โ””โ”ฌโ”€โ”€โ”˜โ””โ”ฌ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€ Carattere controllo (T) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€ Codice Belfiore comune (H501) โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Giorno nascita + 40 se donna (15) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Mese nascita (E = Maggio) โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Anno nascita (80 = 1980) ``` ### Implementazione Algoritmo PHP ```php class CodiceFiscaleCalculator { // Tabelle conversione per algoritmo private static $mesi = [ 1 => 'A', 2 => 'B', 3 => 'C', 4 => 'D', 5 => 'E', 6 => 'H', 7 => 'L', 8 => 'M', 9 => 'P', 10 => 'R', 11 => 'S', 12 => 'T' ]; private static $caratteriDispari = [ '0' => 1, '1' => 0, '2' => 5, '3' => 7, '4' => 9, '5' => 13, '6' => 15, '7' => 17, '8' => 19, '9' => 21, 'A' => 1, 'B' => 0, 'C' => 5, 'D' => 7, 'E' => 9, 'F' => 13, 'G' => 15, 'H' => 17, 'I' => 19, 'J' => 21, 'K' => 2, 'L' => 4, 'M' => 18, 'N' => 20, 'O' => 11, 'P' => 3, 'Q' => 6, 'R' => 8, 'S' => 12, 'T' => 14, 'U' => 16, 'V' => 10, 'W' => 22, 'X' => 25, 'Y' => 24, 'Z' => 23 ]; private static $caratteriPari = [ '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25 ]; private static $caratteriControllo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; /** * Calcola il codice fiscale completo */ public static function calcola($nome, $cognome, $dataNascita, $sesso, $codiceBelfiore) { $cf = ''; // 1. Cognome (3 caratteri) $cf .= self::calcolaCognome($cognome); // 2. Nome (3 caratteri) $cf .= self::calcolaNome($nome); // 3. Anno nascita (2 caratteri) $cf .= substr($dataNascita->format('Y'), -2); // 4. Mese nascita (1 carattere) $cf .= self::$mesi[(int)$dataNascita->format('n')]; // 5. Giorno nascita (2 caratteri) + 40 se donna $giorno = (int)$dataNascita->format('d'); if (strtoupper($sesso) === 'F') { $giorno += 40; } $cf .= str_pad($giorno, 2, '0', STR_PAD_LEFT); // 6. Codice Belfiore (4 caratteri) $cf .= strtoupper($codiceBelfiore); // 7. Carattere di controllo $cf .= self::calcolaCarattereControllo($cf); return $cf; } /** * Calcola le prime 3 consonanti del cognome */ private static function calcolaCognome($cognome) { return self::estraiCaratteri($cognome, 3); } /** * Calcola caratteri nome (regole particolari) */ private static function calcolaNome($nome) { $consonanti = self::estraiConsonanti($nome); $vocali = self::estraiVocali($nome); // Se consonanti >= 4, prendi 1ยฐ, 3ยฐ, 4ยฐ if (strlen($consonanti) >= 4) { return substr($consonanti, 0, 1) . substr($consonanti, 2, 2); } // Altrimenti segui regola standard return self::estraiCaratteri($nome, 3); } /** * Estrae caratteri secondo regole CF */ private static function estraiCaratteri($stringa, $lunghezza) { $stringa = strtoupper(preg_replace('/[^A-Za-z]/', '', $stringa)); $consonanti = self::estraiConsonanti($stringa); $vocali = self::estraiVocali($stringa); $risultato = $consonanti . $vocali; // Pad con X se necessario return str_pad(substr($risultato, 0, $lunghezza), $lunghezza, 'X'); } /** * Estrae consonanti */ private static function estraiConsonanti($stringa) { return preg_replace('/[AEIOU]/', '', strtoupper($stringa)); } /** * Estrae vocali */ private static function estraiVocali($stringa) { return preg_replace('/[^AEIOU]/', '', strtoupper($stringa)); } /** * Calcola carattere di controllo */ private static function calcolaCarattereControllo($codice15Caratteri) { $somma = 0; for ($i = 0; $i < 15; $i++) { $carattere = $codice15Caratteri[$i]; if ($i % 2 === 0) { // Posizione dispari (0, 2, 4, ...) $somma += self::$caratteriDispari[$carattere]; } else { // Posizione pari (1, 3, 5, ...) $somma += self::$caratteriPari[$carattere]; } } $resto = $somma % 26; return self::$caratteriControllo[$resto]; } /** * Valida un codice fiscale esistente */ public static function valida($codiceFiscale, $nome = null, $cognome = null, $dataNascita = null, $sesso = null, $codiceBelfiore = null) { // Verifica lunghezza if (strlen($codiceFiscale) !== 16) { return false; } // Verifica carattere controllo $primi15 = substr($codiceFiscale, 0, 15); $carattereControllo = substr($codiceFiscale, 15, 1); if (self::calcolaCarattereControllo($primi15) !== $carattereControllo) { return false; } // Se sono forniti i dati anagrafici, verifica coerenza if ($nome && $cognome && $dataNascita && $sesso && $codiceBelfiore) { $cfCalcolato = self::calcola($nome, $cognome, $dataNascita, $sesso, $codiceBelfiore); return $codiceFiscale === $cfCalcolato; } return true; } /** * Trova codice Belfiore per comune e data nascita */ public static function trovaCodiceBelfiore($comuneId, $dataNascita) { // Cerca in gi_comuni_validita per gestire comuni cessati $comune = DB::table('gi_comuni_validita') ->where('codice_istat', $comuneId) ->where(function($query) use ($dataNascita) { $query->where('data_inizio_validita', '<=', $dataNascita) ->where(function($q) use ($dataNascita) { $q->whereNull('data_fine_validita') ->orWhere('data_fine_validita', '>=', $dataNascita); }); }) ->first(); return $comune ? $comune->codice_belfiore : null; } } ``` ## ๐Ÿ”„ SISTEMA AUTO-AGGIORNAMENTO ### Fonti Ufficiali - **ISTAT**: Dataset comuni italiani - **Agenzia Entrate**: Codici Belfiore - **Poste Italiane**: Database CAP ### Procedura Aggiornamento Automatico ```php class AggiornamentoComuni { /** * Download e import automatico da ISTAT */ public function aggiornaDatasetCompleto() { Log::info('Inizio aggiornamento dataset comuni'); // 1. Download file ufficiali $dataset = $this->downloadDatasetISTAT(); // 2. Parsing e validazione $datiValidati = $this->validaEparsaDati($dataset); // 3. Confronto con dati esistenti $differenze = $this->confrontaConEsistente($datiValidati); // 4. Applicazione modifiche if ($differenze['nuovi'] || $differenze['modificati'] || $differenze['cessati']) { $this->applicaModifiche($differenze); $this->inviaReportAggiornamento($differenze); } Log::info('Aggiornamento completato', ['differenze' => $differenze]); } /** * Aggiornamento incrementale (solo nuovi/modificati) */ public function aggiornamentoIncrementale() { // Check solo comuni modificati nell'ultimo mese $ultimoAggiornamento = Setting::get('ultimo_aggiornamento_comuni'); // Logica di confronto e import selettivo } } ``` ### Scheduling Automatico ```php // In app/Console/Kernel.php protected function schedule(Schedule $schedule) { // Aggiornamento completo mensile $schedule->command('comuni:aggiorna-completo') ->monthlyOn(1, '02:00') ->emailOutputOnFailure('admin@netgescon.it'); // Check incrementale settimanale $schedule->command('comuni:aggiorna-incrementale') ->weeklyOn(1, '03:00'); } ``` ## ๐Ÿ—๏ธ INTEGRAZIONE IN NETGESCON ### Tabelle NetGesCon da Modificare ```sql -- Modifica tabella persone per FK a comuni ALTER TABLE persone ADD COLUMN luogo_nascita_comune_id VARCHAR(6), ADD COLUMN residenza_comune_id VARCHAR(6), ADD COLUMN domicilio_comune_id VARCHAR(6), ADD COLUMN corrispondenza_comune_id VARCHAR(6), ADD FOREIGN KEY fk_nascita_comune (luogo_nascita_comune_id) REFERENCES gi_comuni(codice_istat), ADD FOREIGN KEY fk_residenza_comune (residenza_comune_id) REFERENCES gi_comuni(codice_istat); -- Modifica tabella stabili ALTER TABLE stabili ADD COLUMN comune_id VARCHAR(6), ADD FOREIGN KEY fk_stabile_comune (comune_id) REFERENCES gi_comuni(codice_istat); ``` ### Helper Class per Comune ```php class ComuneHelper { /** * Ricerca comuni con autocompletamento */ public static function ricercaComuni($query, $limit = 10) { return DB::table('gi_comuni_cap') ->where('denominazione_ita', 'LIKE', "%$query%") ->orWhere('cap', 'LIKE', "$query%") ->orWhere('sigla_provincia', '=', strtoupper($query)) ->limit($limit) ->get(); } /** * Dati completi comune per codice ISTAT */ public static function datiComune($codiceIstat) { return DB::table('gi_comuni_cap') ->where('codice_istat', $codiceIstat) ->first(); } /** * Calcola distanza tra due comuni */ public static function distanzaComuni($comune1Id, $comune2Id) { $c1 = self::datiComune($comune1Id); $c2 = self::datiComune($comune2Id); return self::calcolaDistanzaGPS($c1->lat, $c1->lon, $c2->lat, $c2->lon); } /** * Trova comuni limitrofi */ public static function comuniLimitrofi($comuneId, $raggioKm = 25) { $comune = self::datiComune($comuneId); // Query con calcolo distanza GPS return DB::table('gi_comuni_cap') ->select('*') ->selectRaw(" ( 6371 * acos( cos( radians(?) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(?) ) + sin( radians(?) ) * sin( radians( lat ) ) ) ) AS distanza_km ", [$comune->lat, $comune->lon, $comune->lat]) ->having('distanza_km', '<', $raggioKm) ->orderBy('distanza_km') ->get(); } } ``` ## ๐Ÿ“ฑ COMPONENTI UI ### Autocompletamento Indirizzo ```html
``` ## ๐Ÿ“Š UTILIZZI IN NETGESCON ### 1. Calcolo Codice Fiscale - **Input persona** โ†’ Verifica CF automatico - **Persone anziane** โ†’ Cerca comune validitร  storica - **Stranieri** โ†’ Gestione nazioni estere ### 2. Gestione Indirizzi - **Autocompletamento** indirizzi completi - **Validazione** CAP-Comune - **Geolocalizzazione** stabili ### 3. Reportistica Geografica - **Distribuzione** condomini per area - **Analisi** bacino utenza amministratori - **Ricerca** fornitori zona ### 4. Comunicazioni Legali - **Indirizzi** conformi normative - **CAP** sempre corretti - **Certificazioni** conformitร  dati ## ๐Ÿš€ ROADMAP INTEGRAZIONE ### Fase 1 - Import Database (Sprint 1) - [ ] Download dataset MIT completo - [ ] Import tabelle in NetGesCon - [ ] Setup auto-aggiornamento mensile - [ ] Test algoritmo codice fiscale ### Fase 2 - Integrazione Base (Sprint 2) - [ ] Modifica tabelle esistenti con FK - [ ] Helper class ComuneHelper - [ ] API endpoint ricerca comuni - [ ] Component autocompletamento ### Fase 3 - Validazioni (Sprint 3) - [ ] Validazione CF all'inserimento persona - [ ] Controllo coerenza CAP-Comune - [ ] Import massivo con validazione - [ ] Dashboard errori dati ### Fase 4 - Funzionalitร  Avanzate (Sprint 4) - [ ] Geolocalizzazione e mappe - [ ] Ricerca per prossimitร  - [ ] Analytics geografiche - [ ] Export dati conformi --- **Data Analisi:** 14/07/2025 **Stato:** PRONTO PER IMPLEMENTAZIONE **Prioritร :** ALTA - Richiesto per anagrafica e CF