# 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
<!-- Component: address-autocomplete.blade.php -->
<div class="form-group">
    <label for="comune">Comune</label>
    <input type="text" id="comune-search" class="form-control" 
           placeholder="Digita nome comune o CAP..." autocomplete="off">
    <input type="hidden" id="comune-id" name="comune_id">
    
    <div id="comuni-dropdown" class="dropdown-menu" style="display:none;">
        <!-- Risultati via AJAX -->
    </div>
</div>

<script>
$('#comune-search').on('input', function() {
    const query = $(this).val();
    
    if (query.length >= 2) {
        $.ajax({
            url: '/api/comuni/search',
            data: { q: query },
            success: function(comuni) {
                let html = '';
                comuni.forEach(comune => {
                    html += `
                        <a href="#" class="dropdown-item comune-option" 
                           data-id="${comune.codice_istat}"
                           data-denominazione="${comune.denominazione_ita}"
                           data-cap="${comune.cap}"
                           data-provincia="${comune.sigla_provincia}">
                            ${comune.denominazione_ita} (${comune.cap}) - ${comune.sigla_provincia}
                        </a>
                    `;
                });
                $('#comuni-dropdown').html(html).show();
            }
        });
    }
});

$(document).on('click', '.comune-option', function(e) {
    e.preventDefault();
    const $this = $(this);
    
    $('#comune-search').val($this.data('denominazione'));
    $('#comune-id').val($this.data('id'));
    $('#cap').val($this.data('cap')); // Auto-compila CAP
    $('#provincia').val($this.data('provincia')); // Auto-compila Provincia
    $('#comuni-dropdown').hide();
});
</script>
```

## 📊 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
