312 lines
12 KiB
PHP
Executable File
312 lines
12 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Import diretto da MDB → MySQL (staging) senza CSV
|
|
* Richiede mdb-tools installati (mdb-tables, mdb-export, mdb-sql) oppure ODBC
|
|
*/
|
|
class ImportGesconMdbCommand extends Command
|
|
{
|
|
protected $signature = 'import:gescon-mdb
|
|
{tenant : Tenant ID}
|
|
{--mdb= : Percorso file MDB}
|
|
{--anno= : Anno gestione}
|
|
{--tipo=O : Tipo gestione O|R|S}
|
|
{--n-stra= : Identificativo gestione straordinaria}
|
|
{--table=* : Tabelle da importare (default: auto)}
|
|
{--preview : Mostra anteprima righe}
|
|
{--limit=100 : Limite preview}';
|
|
|
|
protected $description = 'Import diretto da MDB a tabelle staging MySQL (multi-anno, multi-gestione)';
|
|
|
|
public function handle()
|
|
{
|
|
$tenant = $this->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);
|
|
}
|
|
}
|