argument('tenant'); $mdb = $this->option('mdb'); $anno = (int)($this->option('anno') ?: date('Y')); $tipo = strtoupper($this->option('tipo') ?: 'O'); $nStra = $this->option('n-stra'); if (!file_exists($mdb)) { $this->error("File MDB non trovato: {$mdb}"); return Command::FAILURE; } $this->info("📥 Import MDB → staging | tenant={$tenant} anno={$anno} tipo={$tipo} n_stra={$nStra}"); $this->line("Sorgente: {$mdb}"); // Determina tabelle $tables = $this->option('table'); if (empty($tables)) { $tables = ['Inc_da_ec', 'Nettovers_RDA', 'EMESS_GEN', 'EMESS_DET', 'EMESS_DET_2', 'FE_caricate_aut', 'operazioni']; } // Crea gestione (o recupera) $gestioneId = $this->upsertGestione($tenant, $anno, $tipo, $nStra); $stats = []; foreach ($tables as $table) { $imported = $this->importTable($tenant, $mdb, $table, $gestioneId, $anno, $tipo, $nStra); $stats[$table] = $imported; } $this->newLine(); $this->info('✅ Import completato'); foreach ($stats as $tbl => $num) { $this->line(" - {$tbl}: {$num} righe importate"); } return Command::SUCCESS; } private function upsertGestione(string $tenant, int $anno, string $tipo, ?string $nStra): int { // Allinea al modello CANONICO: anno_gestione, tipo_gestione (string), numero_straordinaria $tipoMap = [ 'O' => 'ordinaria', 'R' => 'riscaldamento', 'S' => 'straordinaria', ]; $tipoGest = $tipoMap[$tipo] ?? 'ordinaria'; $numeroStra = $tipoGest === 'straordinaria' ? (int)($nStra ?? 0) : null; $row = DB::table('gestioni_contabili') ->where('tenant_id', $tenant) ->where('anno_gestione', $anno) ->where('tipo_gestione', $tipoGest) ->where(function ($q) use ($numeroStra) { if ($numeroStra === null) { $q->whereNull('numero_straordinaria'); } else { $q->where('numero_straordinaria', $numeroStra); } }) ->first(); if ($row) return $row->id; // Valori di default ragionevoli $dataInizio = sprintf('%d-01-01', $anno); $dataFine = sprintf('%d-12-31', $anno); $denom = 'Gestione ' . $anno . ' ' . $tipoGest . ($numeroStra ? ' n.' . $numeroStra : ''); $prefixBase = ($tipo === 'R' ? 'R' : ($tipo === 'S' ? 'S' : 'O')) . $anno; $protocolloPrefix = $prefixBase . ($tipo === 'S' && $numeroStra ? '-' . str_pad((string)$numeroStra, 3, '0', STR_PAD_LEFT) : ''); return DB::table('gestioni_contabili')->insertGetId([ 'tenant_id' => $tenant, 'anno_gestione' => $anno, 'tipo_gestione' => $tipoGest, 'numero_straordinaria' => $numeroStra, 'denominazione' => $denom, 'data_inizio' => $dataInizio, 'data_fine' => $dataFine, 'protocollo_prefix' => $protocolloPrefix, 'ultimo_protocollo' => 0, 'stato' => 'aperta', 'created_at' => now(), 'updated_at' => now(), ]); } private function importTable(string $tenant, string $mdb, string $table, int $gestioneId, int $anno, string $tipo, ?string $nStra): int { $this->line("→ Import tabella {$table}…"); $csv = $this->runShell("mdb-export -D '%m/%d/%y %H:%M:%S' -d '|' -Q -R '\n' " . escapeshellarg($mdb) . ' ' . escapeshellarg($table)); if ($csv === null) { $this->warn(" (skipped) tabella non trovata"); return 0; } $rows = $this->parseDelimited($csv, '|'); if (count($rows) === 0) { $this->line(" 0 righe"); return 0; } $count = 0; switch ($table) { case 'operazioni': foreach ($rows as $r) { DB::table('stg_operazioni_gescon')->insert([ 'tenant_id' => $tenant, 'sorgente_archivio' => $mdb, 'gestione_id' => $gestioneId, 'anno' => $anno, 'gestione_tipo' => $tipo, 'n_stra' => $nStra, 'compet' => $r['COMPET'] ?? null, 'natura2' => $r['NATURA2'] ?? null, 'conto_dare' => $r['CONTO_DARE'] ?? null, 'conto_avere' => $r['CONTO_AVERE'] ?? null, 'protocollo' => $r['NUM_PROTOCOLLO'] ?? ($r['NUMERO'] ?? null), 'data_registrazione' => $this->date($r['DATA_REG'] ?? null), 'data_competenza' => $this->date($r['DATA_COMP'] ?? null), 'importo' => $this->money($r['IMPORTO'] ?? 0), 'valuta' => 'EUR', 'fornitore_cod' => $r['COD_FORN'] ?? null, 'fornitore_nome' => $r['NOME_FORN'] ?? null, 'descrizione' => $r['DESCR'] ?? null, 'cod_cassa' => $r['COD_CASSA'] ?? null, 'n_riferimento' => $r['N_RIF'] ?? null, 'numero_fattura' => $r['NUM_FATT'] ?? null, 'data_fattura' => $this->date($r['DATA_FATT'] ?? null), 'gestione_raw' => $r['GESTIONE'] ?? null, 'raw' => json_encode($r), 'created_at' => now(), 'updated_at' => now(), ]); $count++; } break; case 'Inc_da_ec': foreach ($rows as $r) { DB::table('stg_incassi_da_estratto')->insert([ 'tenant_id' => $tenant, 'sorgente_archivio' => $mdb, 'gestione_id' => $gestioneId, 'anno' => $anno, 'cod_cassa' => $r['COD_CASSA'] ?? null, 'riferimento_banca' => $r['RIF_EC'] ?? ($r['TRN'] ?? null), 'data_banca' => $this->date($r['DATA_EC'] ?? null), 'importo' => $this->money($r['IMPORTO'] ?? 0), 'note' => $r['NOTE'] ?? null, 'raw' => json_encode($r), 'created_at' => now(), 'updated_at' => now(), ]); $count++; } break; case 'Nettovers_RDA': foreach ($rows as $r) { DB::table('stg_ritenute_rda')->insert([ 'tenant_id' => $tenant, 'sorgente_archivio' => $mdb, 'gestione_id' => $gestioneId, 'anno' => $anno, 'rif_rda' => $r['RIF_RDA'] ?? null, 'imponibile' => $this->money($r['IMPONIBILE'] ?? 0), 'aliquota' => $this->decimal($r['ALIQ'] ?? 0), 'ritenuta' => $this->money($r['RITENUTA'] ?? 0), 'data_sorgente' => $this->date($r['DATA_SORG'] ?? null), 'data_versamento' => $this->date($r['DATA_VERS'] ?? null), 'fornitore_cod' => $r['COD_FORN'] ?? null, 'fornitore_nome' => $r['NOME_FORN'] ?? null, 'raw' => json_encode($r), 'created_at' => now(), 'updated_at' => now(), ]); $count++; } break; case 'EMESS_GEN': case 'EMESS_DET': case 'EMESS_DET_2': foreach ($rows as $r) { DB::table('stg_rate_emesse')->insert([ 'tenant_id' => $tenant, 'sorgente_archivio' => $mdb, 'gestione_id' => $gestioneId, 'anno' => $anno, 'cod_cond' => $r['COD_COND'] ?? null, 'unita' => $r['UNITA'] ?? ($r['UI'] ?? null), 'o_r_s' => $r['O_R_S'] ?? null, 'n_mese' => $r['N_MESE'] ?? ($r['MESE'] ?? null), 'importo' => $this->money($r['IMPORTO'] ?? ($r['IMPORTO_EURO'] ?? 0)), 'data_emissione' => $this->date($r['DATA_EMISS'] ?? null), 'raw' => json_encode(['table' => $table, 'row' => $r]), 'created_at' => now(), 'updated_at' => now(), ]); $count++; } break; case 'FE_caricate_aut': foreach ($rows as $r) { DB::table('stg_fatture_fe')->insert([ 'tenant_id' => $tenant, 'sorgente_archivio' => $mdb, 'gestione_id' => $gestioneId, 'anno' => $anno, 'numero' => $r['NUMERO'] ?? null, 'data' => $this->date($r['DATA'] ?? null), 'fornitore_cod' => $r['COD_FORN'] ?? null, 'fornitore_nome' => $r['NOME_FORN'] ?? null, 'imponibile' => $this->money($r['IMPONIBILE'] ?? 0), 'iva' => $this->money($r['IVA'] ?? 0), 'totale' => $this->money($r['TOTALE'] ?? 0), 'raw' => json_encode($r), 'created_at' => now(), 'updated_at' => now(), ]); $count++; } break; default: $this->warn(" Tabella non gestita: {$table}"); } return $count; } private function runShell(string $cmd): ?string { $out = shell_exec($cmd . ' 2>/dev/null'); if ($out === null || $out === '') return null; return $out; } private function parseDelimited(string $text, string $sep): array { $lines = preg_split("/\r?\n/", trim($text)); if (count($lines) === 0) return []; $headers = str_getcsv(array_shift($lines), $sep); $rows = []; foreach ($lines as $line) { if ($line === '') continue; $cols = str_getcsv($line, $sep); if (count($cols) !== count($headers)) { // padding $cols = array_pad($cols, count($headers), null); } $rows[] = array_combine($headers, $cols); } return $rows; } private function date($v): ?string { if (!$v) return null; // support "MM/DD/YY HH:MM:SS" & "DD/MM/YYYY" $v = trim($v); if (preg_match('/^\d{2}\/\d{2}\/\d{2} /', $v)) { $dt = \DateTime::createFromFormat('m/d/y H:i:s', $v); return $dt?->format('Y-m-d'); } if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $v)) { $dt = \DateTime::createFromFormat('d/m/Y', $v); return $dt?->format('Y-m-d'); } if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $v)) return $v; return null; } private function money($v): float { if ($v === null || $v === '') return 0.0; $s = str_replace(['.', ' '], ['', ''], (string)$v); $s = str_replace(',', '.', $s); return (float)$s; } private function decimal($v): float { return $this->money($v); } }