netgescon-day0/docs/00-IMPORT/02-MDB-CONNECTION/mdb-import-guide.md

603 lines
18 KiB
Markdown

# 🗃️ CONNESSIONE DATABASE MDB - Configurazione Import
## 📋 **Setup Connessione Microsoft Access (.mdb)**
### **1. Prerequisiti Sistema**
```bash
# Ubuntu/Debian - Installazione driver MDB
sudo apt-get update
sudo apt-get install mdbtools unixodbc unixodbc-dev
sudo apt-get install libmdbodbc1
# Per PHP - Estensioni necessarie
sudo apt-get install php-odbc php-pdo
```
### **2. Configurazione ODBC**
```ini
# /etc/odbc.ini
[Gescon_Stabili]
Description=Database Gescon Stabili
Driver=MDBTools
Database=/path/to/stabili.mdb
Readonly=yes
```
### **3. Test Connessione**
```bash
# Test connessione ODBC
isql -v Gescon_Stabili
# Test con mdb-tools
mdb-tables /path/to/stabili.mdb
mdb-schema /path/to/stabili.mdb
```
---
## 🔄 **Script PHP Import MDB**
### **Classe MDBImporter**
```php
<?php
// app/Services/MDBImporter.php
namespace App\Services;
use PDO;
use PDOException;
use Illuminate\Support\Facades\Log;
use App\Models\Stabile;
use App\Models\ComuneItaliano;
use App\Models\User;
class MDBImporter
{
protected $mdbPath;
protected $connection;
protected $batchSize;
public function __construct(string $mdbPath, int $batchSize = 50)
{
$this->mdbPath = $mdbPath;
$this->batchSize = $batchSize;
}
/**
* Connessione al database MDB
*/
public function connect(): bool
{
try {
// Prova prima con ODBC
$dsn = "odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ={$this->mdbPath};";
$this->connection = new PDO($dsn);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Log::info("Connessione MDB stabilita", ['file' => $this->mdbPath]);
return true;
} catch (PDOException $e) {
// Fallback con mdb-tools
Log::warning("Connessione ODBC fallita, utilizzo mdb-tools", [
'error' => $e->getMessage()
]);
return $this->connectWithMdbTools();
}
}
/**
* Fallback con mdb-tools per sistemi Linux
*/
protected function connectWithMdbTools(): bool
{
if (!file_exists($this->mdbPath)) {
Log::error("File MDB non trovato", ['path' => $this->mdbPath]);
return false;
}
// Test con mdb-tables
$output = shell_exec("mdb-tables -1 '{$this->mdbPath}' 2>/dev/null");
if (strpos($output, 'Stabili') !== false) {
Log::info("Connessione MDB-Tools stabilita", ['tables' => trim($output)]);
return true;
}
return false;
}
/**
* Importa tabella Stabili
*/
public function importStabili(): array
{
$risultato = [
'importati' => 0,
'errori' => 0,
'duplicati' => 0,
'dettagli' => []
];
try {
$stabiliData = $this->readStabiliFromMDB();
Log::info("Lettura MDB completata", [
'records_found' => count($stabiliData)
]);
foreach (array_chunk($stabiliData, $this->batchSize) as $batch) {
$batchResult = $this->processBatch($batch);
$risultato['importati'] += $batchResult['importati'];
$risultato['errori'] += $batchResult['errori'];
$risultato['duplicati'] += $batchResult['duplicati'];
$risultato['dettagli'] = array_merge($risultato['dettagli'], $batchResult['dettagli']);
}
} catch (\Exception $e) {
Log::error("Errore import MDB", ['error' => $e->getMessage()]);
$risultato['errori']++;
$risultato['dettagli'][] = [
'tipo' => 'errore_critico',
'messaggio' => $e->getMessage()
];
}
return $risultato;
}
/**
* Legge dati dalla tabella Stabili
*/
protected function readStabiliFromMDB(): array
{
if ($this->connection) {
return $this->readWithPDO();
} else {
return $this->readWithMdbTools();
}
}
/**
* Lettura con PDO/ODBC
*/
protected function readWithPDO(): array
{
$sql = "SELECT * FROM Stabili ORDER BY cod_stabile";
$stmt = $this->connection->prepare($sql);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Lettura con mdb-tools
*/
protected function readWithMdbTools(): array
{
$command = "mdb-export '{$this->mdbPath}' Stabili";
$csvOutput = shell_exec($command);
if (!$csvOutput) {
throw new \Exception("Impossibile leggere tabella Stabili da MDB");
}
// Parsing CSV output
$lines = explode("\n", trim($csvOutput));
$headers = str_getcsv(array_shift($lines));
$data = [];
foreach ($lines as $line) {
if (trim($line)) {
$values = str_getcsv($line);
$data[] = array_combine($headers, $values);
}
}
return $data;
}
/**
* Processa batch di record
*/
protected function processBatch(array $batch): array
{
$risultato = [
'importati' => 0,
'errori' => 0,
'duplicati' => 0,
'dettagli' => []
];
foreach ($batch as $record) {
try {
$recordResult = $this->processStabileRecord($record);
if ($recordResult['success']) {
if ($recordResult['type'] === 'created') {
$risultato['importati']++;
} else {
$risultato['duplicati']++;
}
} else {
$risultato['errori']++;
}
$risultato['dettagli'][] = $recordResult;
} catch (\Exception $e) {
$risultato['errori']++;
$risultato['dettagli'][] = [
'success' => false,
'cod_stabile' => $record['cod_stabile'] ?? 'N/A',
'error' => $e->getMessage()
];
}
}
return $risultato;
}
/**
* Processa singolo record Stabile
*/
protected function processStabileRecord(array $record): array
{
$codStabile = $record['cod_stabile'];
// Controlla duplicati
$esistente = Stabile::where('cod_stabile_old', $codStabile)->first();
if ($esistente) {
return [
'success' => true,
'type' => 'duplicate',
'cod_stabile' => $codStabile,
'message' => 'Record già esistente'
];
}
// Mapping dati
$stabileData = $this->mapStabileData($record);
// Validazione
$validation = $this->validateStabileData($stabileData);
if (!$validation['valid']) {
return [
'success' => false,
'cod_stabile' => $codStabile,
'error' => 'Validazione fallita: ' . implode(', ', $validation['errors'])
];
}
// Creazione record
$stabile = Stabile::create($stabileData);
return [
'success' => true,
'type' => 'created',
'cod_stabile' => $codStabile,
'netgescon_id' => $stabile->id,
'message' => 'Importato con successo'
];
}
/**
* Mapping dati da MDB a NetGescon
*/
protected function mapStabileData(array $mdbRecord): array
{
// Trova comune
$comune = ComuneItaliano::where('nome', $mdbRecord['citta'])
->where('provincia', $mdbRecord['provincia'])
->first();
// Trova amministratore
$amministratore = null;
if (!empty($mdbRecord['cf_amministratore'])) {
$amministratore = User::where('codice_fiscale', $mdbRecord['cf_amministratore'])->first();
}
// Prepara dati bancari JSON
$datiBancari = [
'iban_principale' => $mdbRecord['iban_bancario'] ?? null,
'iban_postale' => $mdbRecord['iban_postale'] ?? null,
'banca_denominazione' => $mdbRecord['banca'] ?? null,
'abi' => $mdbRecord['abi'] ?? null,
'cab' => $mdbRecord['cab'] ?? null,
'cc_postale' => $mdbRecord['conto_postale'] ?? null
];
// Prepara dati catastali JSON
$datiCatastali = [
'foglio' => $mdbRecord['AC_Foglio'] ?? null,
'particella' => $mdbRecord['AC_partic'] ?? null
];
// Prepara configurazione rate JSON
$configRate = [
'ordinarie' => [
'q1' => ($mdbRecord['ORD_trimestre1'] ?? false) === true,
'q2' => ($mdbRecord['ORD_trimestre2'] ?? false) === true,
'q3' => ($mdbRecord['ORD_trimestre3'] ?? false) === true,
'q4' => ($mdbRecord['ORD_trimestre4'] ?? false) === true
],
'riserva' => [
'q1' => ($mdbRecord['RIS_trimestre1'] ?? false) === true,
'q2' => ($mdbRecord['RIS_trimestre2'] ?? false) === true,
'q3' => ($mdbRecord['RIS_trimestre3'] ?? false) === true,
'q4' => ($mdbRecord['RIS_trimestre4'] ?? false) === true
]
];
return [
'cod_stabile_old' => $mdbRecord['cod_stabile'],
'directory_name' => $mdbRecord['nome_directory'] ?? null,
'denominazione' => $mdbRecord['denominazione'],
'indirizzo' => $mdbRecord['indirizzo'],
'cap' => $mdbRecord['cap'],
'citta' => $mdbRecord['citta'],
'provincia' => $mdbRecord['provincia'],
'codice_fiscale' => $mdbRecord['codice_fiscale'] ?? null,
'comune_id' => $comune?->id,
'amministratore_id' => $amministratore?->id,
'totale_unita_immobiliari' => (int)($mdbRecord['num_condomini'] ?? 0),
'numero_scale' => (int)($mdbRecord['num_scale'] ?? 0),
'data_nomina_amministratore' => $this->parseDate($mdbRecord['data_nomina'] ?? null),
'dati_bancari' => json_encode($datiBancari),
'dati_catastali' => json_encode($datiCatastali),
'configurazione_rate' => json_encode($configRate),
'note_migrazione' => $mdbRecord['note'] ?? null,
'stato_attivo' => ($mdbRecord['attivo'] ?? true) === true,
'data_creazione_old' => $this->parseDate($mdbRecord['data_creazione'] ?? null),
'data_modifica_old' => $this->parseDate($mdbRecord['data_modifica'] ?? null),
'migrazione_completata_at' => now()
];
}
/**
* Validazione dati stabile
*/
protected function validateStabileData(array $data): array
{
$errors = [];
if (empty($data['denominazione'])) {
$errors[] = 'Denominazione obbligatoria';
}
if (empty($data['indirizzo'])) {
$errors[] = 'Indirizzo obbligatorio';
}
if (empty($data['cap']) || !preg_match('/^\d{5}$/', $data['cap'])) {
$errors[] = 'CAP non valido';
}
if (empty($data['citta'])) {
$errors[] = 'Città obbligatoria';
}
if (empty($data['provincia']) || strlen($data['provincia']) !== 2) {
$errors[] = 'Provincia non valida';
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* Parse date da formato MDB
*/
protected function parseDate($dateString): ?string
{
if (empty($dateString)) {
return null;
}
try {
$date = new \DateTime($dateString);
return $date->format('Y-m-d');
} catch (\Exception $e) {
return null;
}
}
/**
* Chiude connessione
*/
public function disconnect(): void
{
$this->connection = null;
}
}
```
---
## 🚀 **Comando Artisan Import**
```php
<?php
// app/Console/Commands/ImportMdbStabili.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\MDBImporter;
use Illuminate\Support\Facades\DB;
class ImportMdbStabili extends Command
{
protected $signature = 'import:mdb-stabili
{file : Path al file stabili.mdb}
{--batch-size=50 : Dimensione batch per import}
{--validate-only : Solo validazione senza import}
{--force : Forza import anche con errori}';
protected $description = 'Importa stabili da file MDB del vecchio programma Gescon';
public function handle()
{
$filePath = $this->argument('file');
$batchSize = (int)$this->option('batch-size');
$validateOnly = $this->option('validate-only');
$force = $this->option('force');
$this->info("🗃️ Avvio import stabili da MDB");
$this->info("File: {$filePath}");
$this->info("Batch size: {$batchSize}");
if (!file_exists($filePath)) {
$this->error("File MDB non trovato: {$filePath}");
return 1;
}
try {
$importer = new MDBImporter($filePath, $batchSize);
$this->info("🔌 Connessione al database MDB...");
if (!$importer->connect()) {
$this->error("Impossibile connettersi al database MDB");
return 1;
}
$this->info("✅ Connessione stabilita");
if ($validateOnly) {
$this->info("🔍 Modalità validazione - nessun dato verrà importato");
// TODO: Implementare validazione
$this->info("✅ Validazione completata");
return 0;
}
if (!$force && !$this->confirm('Procedere con l\'import?')) {
$this->info("Import annullato");
return 0;
}
$this->info("📦 Avvio import in transazione...");
DB::beginTransaction();
try {
$risultato = $importer->importStabili();
$this->newLine();
$this->info("📊 RISULTATI IMPORT:");
$this->info("✅ Importati: {$risultato['importati']}");
$this->info("🔄 Duplicati: {$risultato['duplicati']}");
$this->info("❌ Errori: {$risultato['errori']}");
if ($risultato['errori'] > 0) {
$this->warn("⚠️ Import completato con errori");
if (!$force) {
$this->error("Import interrotto per errori. Usa --force per procedere");
DB::rollback();
return 1;
}
}
DB::commit();
$this->info("✅ Import completato con successo");
// Mostra dettagli se richiesto
if ($this->option('verbose')) {
$this->showDetails($risultato['dettagli']);
}
} catch (\Exception $e) {
DB::rollback();
$this->error("Errore durante import: " . $e->getMessage());
return 1;
}
} catch (\Exception $e) {
$this->error("Errore critico: " . $e->getMessage());
return 1;
} finally {
$importer->disconnect();
}
return 0;
}
protected function showDetails(array $dettagli): void
{
$this->newLine();
$this->info("📋 DETTAGLI IMPORT:");
foreach ($dettagli as $dettaglio) {
$status = $dettaglio['success'] ? '✅' : '❌';
$this->line("{$status} {$dettaglio['cod_stabile']}: {$dettaglio['message'] ?? $dettaglio['error'] ?? 'N/A'}");
}
}
}
```
---
## 🔧 **Script di Test e Validazione**
```bash
#!/bin/bash
# test-mdb-connection.sh
echo "🧪 Test connessione database MDB"
MDB_FILE="$1"
if [ -z "$MDB_FILE" ]; then
echo "❌ Specificare il path del file MDB"
echo "Uso: $0 /path/to/stabili.mdb"
exit 1
fi
if [ ! -f "$MDB_FILE" ]; then
echo "❌ File non trovato: $MDB_FILE"
exit 1
fi
echo "📁 File: $MDB_FILE"
# Test 1: mdb-tools
echo "🔍 Test 1: Verifica tabelle con mdb-tools"
if command -v mdb-tables >/dev/null 2>&1; then
echo "Tabelle trovate:"
mdb-tables -1 "$MDB_FILE"
echo -e "\n📊 Schema tabella Stabili:"
mdb-schema "$MDB_FILE" -T Stabili
echo -e "\n📈 Conteggio record:"
mdb-export "$MDB_FILE" Stabili | wc -l
else
echo "⚠️ mdb-tools non installato"
fi
# Test 2: ODBC
echo -e "\n🔍 Test 2: Verifica connessione ODBC"
if command -v isql >/dev/null 2>&1; then
echo "SELECT COUNT(*) FROM Stabili;" | isql -v "Driver={Microsoft Access Driver (*.mdb)};DBQ=$MDB_FILE;" 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅ Connessione ODBC riuscita"
else
echo "⚠️ Connessione ODBC fallita"
fi
else
echo "⚠️ isql non disponibile"
fi
# Test 3: Preview dati
echo -e "\n📋 Preview primi 5 record:"
mdb-export "$MDB_FILE" Stabili | head -6
echo -e "\n✅ Test completato"
```