resolveAnnoFolder($legacy, $annoOrFolder); $folder = $resolve['folder'] ?? (is_numeric($annoOrFolder) ? str_pad((string)intval($annoOrFolder), 4, '0', STR_PAD_LEFT) : (string)$annoOrFolder); $anno = $resolve['anno'] ?? (is_numeric($annoOrFolder) && (int)$annoOrFolder >= 1900 ? (int)$annoOrFolder : null); $period = $resolve['period'] ?? null; $dir = rtrim($base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $legacy . DIRECTORY_SEPARATOR . $folder; $singoloAnno = $dir . DIRECTORY_SEPARATOR . 'singolo_anno.mdb'; $generale = rtrim($base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $legacy . DIRECTORY_SEPARATOR . 'generale_stabile.mdb'; $hasMdbTools = trim((string)@shell_exec('command -v mdb-export')) !== ''; $ordinaria = null; $riscaldamento = null; $straord = []; if ($hasMdbTools && is_file($singoloAnno) && is_readable($singoloAnno)) { try { $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $tablesRaw = @shell_exec(sprintf('%s -1 %s 2>/dev/null', 'mdb-tables', escapeshellarg($singoloAnno))) ?: ''; $tableCandidates = []; foreach (preg_split('/\r?\n/', trim($tablesRaw)) as $t) { if ($t === '') continue; if (preg_match('/(gest|rate|piano)/i', $t)) $tableCandidates[] = trim($t); } $table = $tableCandidates[0] ?? null; if ($table) { $cmd = sprintf( '%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($singoloAnno), escapeshellarg($table), escapeshellarg($tmp) ); @shell_exec($cmd); if (@filesize($tmp) > 0) { $fh = fopen($tmp, 'r'); $headers = fgetcsv($fh, 0, '|'); $headers = $headers ? array_map(fn($h) => strtolower(trim((string)$h)), $headers) : []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null]) continue; if (!$headers) break; if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $row = @array_combine($headers, $cols) ?: []; $tipo = $row['tipo'] ?? ($row['tipo_gestione'] ?? null); $isRisc = false; if ($tipo) { $isRisc = preg_match('/ris|risc|cald/i', $tipo); } $rate = []; foreach ($row as $k => $v) { if (preg_match('/^(rata|rata_)(\d{1,2})_(ord|ris)/i', $k, $m)) { $num = (int)$m[2]; $flag = (string)$v !== '0' && $v !== '' && $v !== null; if ($flag) $rate[$num] = 1; } elseif (preg_match('/^(ord|ris)_rata_(\d{1,2})$/i', $k, $m)) { $num = (int)$m[2]; $flag = (string)$v !== '0' && $v !== '' && $v !== null; if ($flag) $rate[$num] = 1; $isRisc = strtolower($m[1]) === 'ris'; } } $gest = ['anno' => $anno, 'stato' => ($row['stato'] ?? $row['stato_gestione'] ?? 'aperta'), 'rate' => $rate]; if ($isRisc) { if (!$riscaldamento) $riscaldamento = $gest; } else { if (!$ordinaria) $ordinaria = $gest; } if ($ordinaria && $riscaldamento) break; } fclose($fh); } } @unlink($tmp); } catch (\Throwable $e) { } } if ($hasMdbTools && is_file($generale) && is_readable($generale)) { try { $tmpS = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $tablesRaw = @shell_exec(sprintf('%s -1 %s 2>/dev/null', 'mdb-tables', escapeshellarg($generale))) ?: ''; $cand = null; foreach (preg_split('/\r?\n/', trim($tablesRaw)) as $t) { if (preg_match('/(straord|lavori|interventi)/i', $t)) { $cand = trim($t); break; } } if ($cand) { $cmd = sprintf( '%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($generale), escapeshellarg($cand), escapeshellarg($tmpS) ); @shell_exec($cmd); if (@filesize($tmpS) > 0) { $fh = fopen($tmpS, 'r'); $headers = fgetcsv($fh, 0, '|'); $headers = $headers ? array_map(fn($h) => strtolower(trim((string)$h)), $headers) : []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if (!$headers) break; if ($cols === [null]) continue; if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $row = @array_combine($headers, $cols) ?: []; $dataInizio = $row['data'] ?? ($row['data_inizio'] ?? null); $yr = null; if ($dataInizio && preg_match('/^(\d{4})/', $dataInizio, $m)) $yr = (int)$m[1]; if ($yr && $yr != $anno) continue; $descr = $row['descrizione'] ?? ($row['lavoro'] ?? ($row['oggetto'] ?? 'Straordinaria')); $rateTot = (int)($row['rate_totali'] ?? ($row['num_rate'] ?? 0)); $rateEmesse = (int)($row['rate_emesse'] ?? ($row['rate_emessa'] ?? 0)); $straord[] = ['id' => $row['id'] ?? ($row['codice'] ?? null), 'descrizione' => $descr, 'data_inizio' => $dataInizio, 'data_fine' => $row['data_fine'] ?? null, 'rate_totali' => $rateTot, 'rate_emesse' => $rateEmesse]; if (count($straord) >= 50) break; } fclose($fh); } @unlink($tmpS); } } catch (\Throwable $e) { } } $meta = ['path_singolo_anno' => (is_file($singoloAnno) ? $singoloAnno : null), 'path_generale' => (is_file($generale) ? $generale : null), 'folder' => $folder, 'period' => $period]; return [ 'ok' => true, 'anno' => $anno, 'ordinaria' => $ordinaria, 'riscaldamento' => $riscaldamento, 'straordinarie' => array_map(function ($s) { $s['periodicita'] = $s['periodicita'] ?? null; if (isset($s['data_inizio']) && preg_match('/^(\d{4})-(\d{2})/', $s['data_inizio'], $m)) { $s['anno_inizio'] = (int)$m[1]; $s['mese_inizio'] = (int)$m[2]; } $s['codice'] = $s['id'] ?? null; return $s; }, $straord), 'meta' => $meta, ]; } public function unitaYear(string $legacy, $annoOrFolder, int $limit = 20): array { $base = UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon'); $resolve = $this->resolveAnnoFolder($legacy, $annoOrFolder); $folder = $resolve['folder'] ?? (is_numeric($annoOrFolder) ? str_pad((string)intval($annoOrFolder), 4, '0', STR_PAD_LEFT) : (string)$annoOrFolder); $anno = $resolve['anno'] ?? (is_numeric($annoOrFolder) && (int)$annoOrFolder >= 1900 ? (int)$annoOrFolder : null); $period = $resolve['period'] ?? null; $dir = rtrim($base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $legacy . DIRECTORY_SEPARATOR . $folder; $singoloAnno = $dir . DIRECTORY_SEPARATOR . 'singolo_anno.mdb'; $hasMdbTools = trim((string)@shell_exec('command -v mdb-export')) !== '' && trim((string)@shell_exec('command -v mdb-tables')) !== ''; if (!$hasMdbTools || !is_file($singoloAnno) || !is_readable($singoloAnno)) { try { $primaryConn = UserSetting::get('gescon.import_conn', 'gescon_import'); $connCandidates = array_values(array_unique([$primaryConn, 'gescon_import_00000000'])); foreach ($connCandidates as $conn) { $candidates = ['condomin', 'condomini', 'cond', 'condo', 'unita', 'unita_immobili', 'unita_immobiliari', 'ac_condomin']; $table = null; foreach ($candidates as $t) { if (Schema::connection($conn)->hasTable($t)) { $table = $t; break; } } if ($table) { $cols = Schema::connection($conn)->getColumnListing($table); $prefer = ['cod_stabile', 'codice_stabile', 'codstab', 'cod_condominio', 'cod_cond', 'scala', 'interno', 'piano', 'cognome', 'nome', 'codice_fiscale', 'telefono', 'cellulare', 'email', 'pec', 'indirizzo_corrispondenza', 'millesimi_generali', 'millesimi_risc', 'millesimi_proprieta', 'millesimi_riscaldamento', 'millesimi_ascensore', 'proprietario', 'inquilino', 'note']; $stableCol = null; foreach ($cols as $c) { $lc = strtolower($c); if (in_array($lc, ['cod_stabile', 'codice_stabile', 'codstab', 'cod_condominio', 'codice_condominio'], true)) { $stableCol = $c; break; } } $headers = array_values(array_unique(array_merge($prefer, $cols))); $select = array_values(array_filter($headers, function ($c) use ($cols) { return in_array($c, $cols, true); })); if (empty($select)) $select = $cols; $select = array_slice($select, 0, 24); $q = DB::connection($conn)->table($table)->select($select); if ($stableCol) { $q->where($stableCol, $legacy); } $rows = $q->limit(max(1, $limit))->get()->map(fn($r) => (array)$r)->toArray(); if (!empty($rows)) { return ['ok' => true, 'legacy_code' => $legacy, 'anno' => $anno ?: null, 'headers' => $select, 'rows' => $rows, 'staging_connection' => $conn, 'staging_table' => $table]; } } } } catch (\Throwable $e) { } return ['ok' => false, 'error' => 'Dati Unità non disponibili: singolo_anno.mdb non leggibile e staging assente']; } try { $tablesRaw = @shell_exec(sprintf('%s -1 %s 2>/dev/null', 'mdb-tables', escapeshellarg($singoloAnno))) ?: ''; $cand = []; foreach (preg_split('/\r?\n/', trim($tablesRaw)) as $t) { $tt = trim($t); if ($tt === '') continue; if (preg_match('/^condomin$/i', $tt)) { $cand = [$tt]; break; } if (preg_match('/condo|min|unita|unit/i', $tt)) $cand[] = $tt; } $table = $cand[0] ?? null; if (!$table) return ['ok' => false, 'error' => 'Tabella condomin non trovata']; $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $bin = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($bin), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($singoloAnno), escapeshellarg($table), escapeshellarg($tmp)); @shell_exec($cmd); if (!@filesize($tmp)) return ['ok' => false, 'error' => 'Nessun dato disponibile']; $fh = fopen($tmp, 'r'); $headers = fgetcsv($fh, 0, '|') ?: []; $headers = array_map(fn($h) => (string)$h, $headers); $rows = []; $count = 0; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null]) continue; if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $row = @array_combine($headers, $cols) ?: []; $rows[] = $row; $count++; if ($count >= max(1, $limit)) break; } fclose($fh); @unlink($tmp); return ['ok' => true, 'legacy_code' => $legacy, 'anno' => $anno, 'headers' => $headers, 'rows' => $rows, 'meta' => ['folder' => $folder, 'period' => $period]]; } catch (\Throwable $e) { return ['ok' => false, 'error' => $e->getMessage()]; } } public function casse(string $legacy): array { $base = UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon'); $generale = rtrim($base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $legacy . DIRECTORY_SEPARATOR . 'generale_stabile.mdb'; $hasMdbTools = trim((string)@shell_exec('command -v mdb-export')) !== ''; $out = []; if ($hasMdbTools && is_file($generale) && is_readable($generale)) { try { $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $tablesRaw = @shell_exec(sprintf('%s -1 %s 2>/dev/null', 'mdb-tables', escapeshellarg($generale))) ?: ''; $cand = null; foreach (preg_split('/\r?\n/', trim($tablesRaw)) as $t) { if (preg_match('/(casse|banche|conti|conto)/i', $t)) { $cand = trim($t); break; } } if ($cand) { $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($generale), escapeshellarg($cand), escapeshellarg($tmp)); @shell_exec($cmd); if (@filesize($tmp) > 0) { $fh = fopen($tmp, 'r'); $headers = fgetcsv($fh, 0, '|'); $headers = $headers ? array_map(fn($h) => strtolower(trim((string)$h)), $headers) : []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if (!$headers) break; if ($cols === [null]) continue; if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $row = @array_combine($headers, $cols) ?: []; $cod = trim((string)($row['codice'] ?? ($row['cod_cassa'] ?? ($row['cod'] ?? '')))); if ($cod === '') continue; $desc = $row['descrizione'] ?? ($row['des'] ?? ($row['cassa'] ?? '')); $iban = $row['iban'] ?? null; $abi = $row['abi'] ?? null; $cab = $row['cab'] ?? null; $out[$cod] = ['codice' => $cod, 'descrizione' => $desc, 'abi' => $abi, 'cab' => $cab, 'iban' => $iban]; } fclose($fh); } @unlink($tmp); } } catch (\Throwable $e) { } } if (empty($out)) { try { $primaryConn = UserSetting::get('gescon.import_conn', 'gescon_import'); $connCandidates = array_values(array_unique([$primaryConn, 'gescon_import_00000000'])); foreach ($connCandidates as $conn) { $table = null; foreach (['Anag_casse', 'anag_casse', 'casse', 'banche'] as $t) { if (Schema::connection($conn)->hasTable($t)) { $table = $t; break; } } if ($table) { $cols = Schema::connection($conn)->getColumnListing($table); $colCode = null; $colDesc = null; $colIban = null; $colAbi = null; $colCab = null; foreach ($cols as $c) { $lc = strtolower($c); if (in_array($lc, ['codice', 'cod_cassa', 'cod'], true)) { $colCode = $c; break; } } foreach ($cols as $c) { $lc = strtolower($c); if (in_array($lc, ['descrizione', 'des', 'cassa', 'banca'], true)) { $colDesc = $c; break; } } foreach ($cols as $c) { if (strtolower($c) === 'iban') { $colIban = $c; break; } } foreach ($cols as $c) { if (strtolower($c) === 'abi') { $colAbi = $c; break; } } foreach ($cols as $c) { if (strtolower($c) === 'cab') { $colCab = $c; break; } } $q = DB::connection($conn)->table($table); if (Schema::connection($conn)->hasColumn($table, 'cod_stabile')) { $q->where('cod_stabile', $legacy); } $rows = $q->limit(500)->get(); foreach ($rows as $r) { $cod = trim((string)($r->{$colCode} ?? '')); if ($cod === '') continue; $out[$cod] = ['codice' => $cod, 'descrizione' => (string)($r->{$colDesc} ?? ''), 'abi' => $colAbi ? (string)($r->{$colAbi} ?? '') : null, 'cab' => $colCab ? (string)($r->{$colCab} ?? '') : null, 'iban' => $colIban ? (string)($r->{$colIban} ?? '') : null]; } if (!empty($out)) break; } } } catch (\Throwable $e) { } } if (!isset($out['CON'])) { $out['CON'] = ['codice' => 'CON', 'descrizione' => 'CASSA CONTANTI', 'abi' => null, 'cab' => null, 'iban' => null]; } $rows = array_values($out); usort($rows, fn($a, $b) => strnatcasecmp($a['codice'], $b['codice'])); return $rows; } public function sampleLegacy(?string $legacy, int $index = 0): array { try { $hasMdb = trim((string)@shell_exec('command -v mdb-export')) !== ''; $mdbPath = UserSetting::get('gescon.mdb_stabili'); if (!$mdbPath || !@is_file($mdbPath) || !@is_readable($mdbPath)) { $storageFallback = '/var/www/netgescon/storage/gescon/Stabili.mdb'; if (@is_file($storageFallback) && @is_readable($storageFallback)) { $mdbPath = $storageFallback; } } if ($hasMdb && $mdbPath && is_file($mdbPath)) { $row = $this->sampleStabileFromMdb($mdbPath, $legacy, $index); if (!empty($row)) { $legacyKey = $row['legacy_key'] ?? null; unset($row['legacy_key']); return ['ok' => true, 'index' => $index, 'data' => $row, 'legacy_key' => $legacyKey]; } } $ds = MigrationDataset::where('key', 'stabili')->orderByDesc('updated_at')->first(); if ($ds) { $rec = MigrationRecord::where('dataset_id', $ds->id)->when($legacy, function ($q) use ($legacy) { $q->where('legacy_key', $legacy); })->orderBy('id')->skip($index)->take(1)->first(); if ($rec) { return ['ok' => true, 'index' => $index, 'data' => (array)($rec->data ?? []), 'legacy_key' => $rec->legacy_key]; } } return ['ok' => false, 'error' => 'Anteprima non disponibile: nessun dataset e MDB non accessibile', 'index' => $index]; } catch (\Throwable $e) { return ['ok' => false, 'error' => 'Errore: ' . $e->getMessage()]; } } public function sampleUnita(?string $legacy, int $index = 0): array { try { $rows = []; if ($legacy && Schema::connection('gescon_import')->hasTable('condomin')) { $rows = DB::connection('gescon_import')->table('condomin')->when($legacy, function ($q) use ($legacy) { $q->where('cod_stabile', $legacy); })->select(['cod_stabile', 'cod_cond', 'scala', 'interno', 'cognome', 'nome', 'codice_fiscale', 'millesimi_generali', 'millesimi_risc'])->orderBy('cod_cond')->skip($index)->take(1)->get()->map(fn($r) => (array)$r)->toArray(); } if (empty($rows)) { $rows[] = ['cod_stabile' => $legacy, 'cod_cond' => 'U' . str_pad((string)$index, 4, '0', STR_PAD_LEFT), 'scala' => 'A', 'interno' => '1', 'cognome' => 'Rossi', 'nome' => 'Mario', 'codice_fiscale' => 'RSSMRA80A01H501Z', 'millesimi_generali' => 45.67, 'millesimi_risc' => 12.34]; } return ['ok' => true, 'index' => $index, 'data' => $rows[0]]; } catch (\Throwable $e) { return ['ok' => false, 'error' => $e->getMessage()]; } } public function sampleTabelle(?string $legacy, int $index = 0): array { return ['ok' => true, 'index' => $index, 'data' => ['cod_stabile' => $legacy, 'tabella' => 'Generali', 'voce' => 'Scala A (app1)', 'millesimi' => 45.6700, 'nord_x10' => null]]; } public function sampleVoci(?string $legacy, int $index = 0): array { return ['ok' => true, 'index' => $index, 'data' => ['cod_stabile' => $legacy, 'cod_voce' => 'A' . str_pad((string)$index, 3, '0', STR_PAD_LEFT), 'descrizione' => 'Spese generali condominio', 'gruppo' => 'MANUT', 'tipo' => 'ordinaria']]; } // Discovery helpers used by index/config views public function scanStabiliFromMasterIndex(string $indexMdbPath, string $legacyBasePath): array { $binTables = trim((string)@shell_exec('command -v mdb-tables')) ?: '/usr/bin/mdb-tables'; $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; if (!is_file($indexMdbPath) || !is_readable($indexMdbPath)) return []; $tableGuess = null; $tables = @shell_exec(sprintf('%s -1 %s 2>/dev/null', escapeshellarg($binTables), escapeshellarg($indexMdbPath))) ?: ''; if ($tables) { foreach (preg_split('/\r?\n/', trim($tables)) as $t) { if ($t === '') continue; if (preg_match('/(cartelle|stabil|anni)/i', $t)) { $tableGuess = trim($t); break; } } } if (!$tableGuess) { $lines = array_filter(preg_split('/\r?\n/', trim($tables))); $tableGuess = $lines ? trim(array_values($lines)[0]) : null; } if (!$tableGuess) return []; $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($indexMdbPath), escapeshellarg($tableGuess), escapeshellarg($tmp)); @shell_exec($cmd); if (!is_file($tmp) || filesize($tmp) === 0) { @unlink($tmp); return []; } $fh = fopen($tmp, 'r'); if (!$fh) { @unlink($tmp); return []; } $headers = fgetcsv($fh, 0, '|'); if (!$headers) { fclose($fh); @unlink($tmp); return []; } $headers = array_map(fn($h) => strtolower(trim((string)$h)), $headers); $byCodeYears = []; $byCodeName = []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null]) continue; $cols = array_map(fn($v) => is_string($v) ? trim($v) : $v, $cols); if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $row = @array_combine($headers, $cols) ?: []; if (!$row) continue; $code = $row['nome_directory'] ?? $row['cod_stabile'] ?? $row['internet_cod_stab'] ?? $row['codice'] ?? null; $denom = $row['denominazione'] ?? $row['fe_denominazione'] ?? null; $anno = $row['anno'] ?? $row['year'] ?? null; if (!$code) continue; $code = (string)$code; if (!isset($byCodeYears[$code])) $byCodeYears[$code] = []; if ($anno && preg_match('/^\d{4}$/', (string)$anno)) { $byCodeYears[$code][(string)$anno] = true; } if ($denom) $byCodeName[$code] = (string)$denom; } fclose($fh); @unlink($tmp); $items = []; foreach ($byCodeYears as $code => $yearsSet) { $den = $byCodeName[$code] ?? null; $years = array_keys($yearsSet); sort($years, SORT_STRING); $items[] = ['legacy_code' => $code, 'denominazione' => $den, 'years_count' => count($years), 'years' => $years]; } if (empty($items)) { $items = $this->scanLegacyCodesFromDir($legacyBasePath); $items = $this->enrichLegacyWithArchives($items, $legacyBasePath); } usort($items, fn($a, $b) => strnatcasecmp((string)($a['legacy_code'] ?? ''), (string)($b['legacy_code'] ?? ''))); return $items; } public function scanStabiliFromMdb(string $mdbPath): array { $binTables = trim((string)@shell_exec('command -v mdb-tables')) ?: '/usr/bin/mdb-tables'; $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $tableGuess = 'Stabili'; $tables = @shell_exec(sprintf('%s -1 %s 2>/dev/null', escapeshellarg($binTables), escapeshellarg($mdbPath))) ?: ''; if ($tables) { foreach (preg_split('/\r?\n/', trim($tables)) as $t) { if ($t === '') continue; if (preg_match('/stabil/i', $t)) { $tableGuess = trim($t); break; } } } $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($mdbPath), escapeshellarg($tableGuess), escapeshellarg($tmp)); @shell_exec($cmd); if (!is_file($tmp) || filesize($tmp) === 0) { @unlink($tmp); return []; } $fh = fopen($tmp, 'r'); if (!$fh) { @unlink($tmp); return []; } $headers = fgetcsv($fh, 0, '|'); if (!$headers) { fclose($fh); @unlink($tmp); return []; } $headers = array_map(fn($h) => strtolower(trim((string)$h)), $headers); $items = []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null] || $cols === false) continue; $cols = array_map(fn($v) => is_string($v) ? trim($v) : $v, $cols); $countH = count($headers); if (count($cols) < $countH) $cols = array_pad($cols, $countH, null); if (count($cols) > $countH) $cols = array_slice($cols, 0, $countH); $row = @array_combine($headers, $cols); if (!$row) continue; $legacyCode = $row['nome_directory'] ?? ($row['internet_cod_stab'] ?? ($row['cod_stabile'] ?? null)); $denom = $row['denominazione'] ?? ($row['fe_denominazione'] ?? null); if ($legacyCode) { $items[] = ['legacy_code' => trim((string)$legacyCode), 'denominazione' => $denom ? trim((string)$denom) : null]; } } fclose($fh); @unlink($tmp); $byCode = []; foreach ($items as $it) { if (!empty($it['legacy_code'])) $byCode[$it['legacy_code']] = $it; } ksort($byCode, SORT_NATURAL); return array_values($byCode); } public function sampleStabileFromMdb(string $mdbPath, ?string $legacyCode, int $index = 0): array { $binTables = trim((string)@shell_exec('command -v mdb-tables')) ?: '/usr/bin/mdb-tables'; $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $tableGuess = 'Stabili'; $tables = @shell_exec(sprintf('%s -1 %s 2>/dev/null', escapeshellarg($binTables), escapeshellarg($mdbPath))) ?: ''; if ($tables) { foreach (preg_split('/\r?\n/', trim($tables)) as $t) { if ($t === '') continue; if (preg_match('/stabil/i', $t)) { $tableGuess = trim($t); break; } } } $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($mdbPath), escapeshellarg($tableGuess), escapeshellarg($tmp)); @shell_exec($cmd); if (!is_file($tmp) || filesize($tmp) === 0) { @unlink($tmp); return []; } $fh = fopen($tmp, 'r'); if (!$fh) { @unlink($tmp); return []; } $headers = fgetcsv($fh, 0, '|'); if (!$headers) { fclose($fh); @unlink($tmp); return []; } $headers = array_map(fn($h) => strtolower(trim((string)$h)), $headers); $rowData = []; $i = 0; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null]) continue; $cols = array_map(fn($v) => is_string($v) ? trim($v) : $v, $cols); $countH = count($headers); if (count($cols) < $countH) $cols = array_pad($cols, $countH, null); if (count($cols) > $countH) $cols = array_slice($cols, 0, $countH); $row = @array_combine($headers, $cols) ?: []; $legacyKey = $row['nome_directory'] ?? ($row['internet_cod_stab'] ?? ($row['cod_stabile'] ?? null)); if ($legacyCode) { if ((string)$legacyKey === (string)$legacyCode) { $rowData = $row; break; } } else { if ($i++ >= $index) { $rowData = $row; break; } } } fclose($fh); @unlink($tmp); if (empty($rowData)) return []; $rowData['legacy_key'] = (string)($rowData['nome_directory'] ?? ($rowData['internet_cod_stab'] ?? ($rowData['cod_stabile'] ?? ''))); return $rowData; } public function scanLegacyCodesFromDir(string $basePath): array { $items = []; if (!is_dir($basePath)) return $items; try { $dh = opendir($basePath); if ($dh) { while (($file = readdir($dh)) !== false) { if ($file === '.' || $file === '..') continue; $full = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file; if (!is_dir($full)) continue; $code = null; if (preg_match('/^(\d{2,5})/', $file, $m)) { $code = $m[1]; } elseif (preg_match('/(\d{2,5})/', $file, $m)) { $code = $m[1]; } $hasGenerale = is_file($full . DIRECTORY_SEPARATOR . 'generale_stabile.mdb'); $hasYears = false; if (is_dir($full)) { try { $sub = opendir($full); if ($sub) { while (($yf = readdir($sub)) !== false) { if ($yf === '.' || $yf === '..') continue; if (!preg_match('/^\d{4}$/', $yf)) continue; $ya = $full . DIRECTORY_SEPARATOR . $yf . DIRECTORY_SEPARATOR . 'singolo_anno.mdb'; if (is_file($ya)) { $hasYears = true; break; } } closedir($sub); } } catch (\Throwable $e) { } } if ($code || $hasGenerale || $hasYears) { $items[] = ['legacy_code' => $code ?: $file, 'denominazione' => null]; } } closedir($dh); } } catch (\Throwable $e) { } $byCode = []; foreach ($items as $it) { $c = (string)($it['legacy_code'] ?? ''); if ($c !== '') { $byCode[$c] = $it; } } uksort($byCode, 'strnatcasecmp'); return array_values($byCode); } public function enrichLegacyWithArchives(array $items, string $basePath): array { $enriched = []; foreach ($items as $it) { $code = trim((string)($it['legacy_code'] ?? '')); if ($code === '') { $enriched[] = $it; continue; } $dir = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $code; $hasGenerale = is_file($dir . DIRECTORY_SEPARATOR . 'generale_stabile.mdb'); $yearsCount = 0; if (is_dir($dir)) { try { $dh = opendir($dir); if ($dh) { while (($f = readdir($dh)) !== false) { if ($f === '.' || $f === '..') continue; if (!preg_match('/^\d{4}$/', $f)) continue; $sub = $dir . DIRECTORY_SEPARATOR . $f . DIRECTORY_SEPARATOR . 'singolo_anno.mdb'; if (is_file($sub)) $yearsCount++; } closedir($dh); } } catch (\Throwable $e) { } } $it['has_generale'] = $hasGenerale; $it['years_count'] = $yearsCount; $enriched[] = $it; } return $enriched; } private function resolveAnnoFolder(string $legacy, $annoOrFolder): array { $base = UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon'); $legacyDir = rtrim($base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $legacy; $folder = null; $anno = null; // If provided a string like '0001' or '2024' if (is_string($annoOrFolder)) { $cand = trim($annoOrFolder); if ($cand !== '') { if (is_dir($legacyDir . DIRECTORY_SEPARATOR . $cand) && is_file($legacyDir . DIRECTORY_SEPARATOR . $cand . DIRECTORY_SEPARATOR . 'singolo_anno.mdb')) { $folder = $cand; } elseif (ctype_digit($cand) && strlen($cand) <= 4) { $pad = str_pad($cand, 4, '0', STR_PAD_LEFT); if (is_dir($legacyDir . DIRECTORY_SEPARATOR . $pad) && is_file($legacyDir . DIRECTORY_SEPARATOR . $pad . DIRECTORY_SEPARATOR . 'singolo_anno.mdb')) { $folder = $pad; } } if (ctype_digit($cand) && strlen($cand) === 4 && (int)$cand >= 1900) { $anno = (int)$cand; } } } // If provided a numeric like 1 or 2024 if ($folder === null && is_numeric($annoOrFolder)) { $n = (int)$annoOrFolder; if ($n >= 1900) { $yr = (string)$n; if (is_dir($legacyDir . DIRECTORY_SEPARATOR . $yr) && is_file($legacyDir . DIRECTORY_SEPARATOR . $yr . DIRECTORY_SEPARATOR . 'singolo_anno.mdb')) { $folder = $yr; $anno = $n; } } if ($folder === null) { $pad = str_pad((string)$n, 4, '0', STR_PAD_LEFT); if (is_dir($legacyDir . DIRECTORY_SEPARATOR . $pad) && is_file($legacyDir . DIRECTORY_SEPARATOR . $pad . DIRECTORY_SEPARATOR . 'singolo_anno.mdb')) { $folder = $pad; } } } // If still unknown, pick first available folder containing singolo_anno.mdb if ($folder === null && is_dir($legacyDir)) { $cands = []; foreach (scandir($legacyDir) as $f) { if ($f === '.' || $f === '..') continue; $p = $legacyDir . DIRECTORY_SEPARATOR . $f . DIRECTORY_SEPARATOR . 'singolo_anno.mdb'; if (is_file($p)) $cands[] = $f; } if (!empty($cands)) { natsort($cands); $cands = array_values($cands); $folder = end($cands); } } // Try map folder->anno from generale_stabile.mdb: table ANNI $generale = $legacyDir . DIRECTORY_SEPARATOR . 'generale_stabile.mdb'; $hasMdbTools = trim((string)@shell_exec('command -v mdb-export')) !== ''; if ($hasMdbTools && is_file($generale) && is_readable($generale)) { try { $tablesRaw = @shell_exec(sprintf('%s -1 %s 2>/dev/null', 'mdb-tables', escapeshellarg($generale))) ?: ''; $anniTable = null; foreach (preg_split('/\r?\n/', trim($tablesRaw)) as $t) { if (preg_match('/^anni$/i', trim($t))) { $anniTable = trim($t); break; } } if ($anniTable) { $tmp = tempnam(sys_get_temp_dir(), 'mdbcsv_'); $binExport = trim((string)@shell_exec('command -v mdb-export')) ?: '/usr/bin/mdb-export'; $cmd = sprintf('%s -D %s -d %s -q %s -R "\\n" %s %s > %s 2>/dev/null', escapeshellarg($binExport), escapeshellarg('%Y-%m-%d'), escapeshellarg('|'), escapeshellarg('"'), escapeshellarg($generale), escapeshellarg($anniTable), escapeshellarg($tmp)); @shell_exec($cmd); if (@filesize($tmp) > 0) { $fh = fopen($tmp, 'r'); $headers = fgetcsv($fh, 0, '|'); $headers = $headers ? array_map(fn($h) => strtolower(trim((string)$h)), $headers) : []; $rows = []; while (($cols = fgetcsv($fh, 0, '|')) !== false) { if ($cols === [null]) continue; if (count($cols) < count($headers)) $cols = array_pad($cols, count($headers), null); if (count($cols) > count($headers)) $cols = array_slice($cols, 0, count($headers)); $rows[] = @array_combine($headers, $cols) ?: []; } fclose($fh); // Try to find mapping by folder code $folderCols = ['nome_dir', 'cartella', 'id_cartella', 'dir', 'directory', 'idcart', 'id_dir', 'num', 'numero']; $annoCols = ['anno_o', 'anno', 'year']; $startCols = ['ordinarie_dal', 'data_inizio', 'inizio', 'dal', 'data_dal']; $endCols = ['ordinarie_al', 'data_fine', 'fine', 'al', 'data_al']; $riscStartCols = ['riscald_dal']; $riscEndCols = ['riscald_al']; $folderCol = null; $annoCol = null; $startCol = null; $endCol = null; foreach ($headers as $h) { if (in_array($h, $folderCols, true)) { $folderCol = $h; break; } } foreach ($headers as $h) { if (in_array($h, $annoCols, true)) { $annoCol = $h; break; } } foreach ($headers as $h) { if (in_array($h, $startCols, true)) { $startCol = $h; break; } } foreach ($headers as $h) { if (in_array($h, $endCols, true)) { $endCol = $h; break; } } $riscStartCol = null; $riscEndCol = null; foreach ($headers as $h) { if (in_array($h, $riscStartCols, true)) { $riscStartCol = $h; break; } } foreach ($headers as $h) { if (in_array($h, $riscEndCols, true)) { $riscEndCol = $h; break; } } if ($folderCol || $annoCol) { foreach ($rows as $r) { $rFolder = isset($r[$folderCol]) ? trim((string)$r[$folderCol]) : null; $rAnno = isset($r[$annoCol]) ? (int)preg_replace('/[^0-9]/', '', (string)$r[$annoCol]) : null; // Normalize folder zero-padded if ($rFolder !== null && ctype_digit($rFolder)) { $rFolder = str_pad($rFolder, 4, '0', STR_PAD_LEFT); } $period = [ 'ordinaria_dal' => $startCol ? ($r[$startCol] ?? null) : null, 'ordinaria_al' => $endCol ? ($r[$endCol] ?? null) : null, 'riscald_dal' => $riscStartCol ? ($r[$riscStartCol] ?? null) : null, 'riscald_al' => $riscEndCol ? ($r[$riscEndCol] ?? null) : null, ]; // If a folder is already picked but ANNO is specified and maps to a different row, prefer the ANNO mapping if ($anno && $rAnno && $rAnno === $anno && $rFolder) { if ($folder !== $rFolder) { $folder = $rFolder; } return ['folder' => $folder, 'anno' => $anno, 'period' => $period]; } if ($folder && $rFolder && $rFolder === $folder) { $anno = $rAnno ?: $anno; return ['folder' => $folder, 'anno' => $anno, 'period' => $period]; } } } } @unlink($tmp); } } catch (\Throwable $e) { } } return ['folder' => $folder, 'anno' => $anno]; } }