📋 Commit iniziale con: - ✅ Documentazione unificata in docs/ - ✅ Codice Laravel in netgescon-laravel/ - ✅ Script automazione in scripts/ - ✅ Configurazione sync rsync - ✅ Struttura organizzata e pulita 🔄 Versione: 2025.07.19-1644 🎯 Sistema pronto per Git distribuito
19 KiB
19 KiB
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
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
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
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
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
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)
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
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
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
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
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
// 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
-- 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
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
<!-- 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