netgescon-day0/resources/views/admin/gescon-import/tabs/mapping.blade.php

390 lines
20 KiB
PHP

{{-- Semplifica: rimuoviamo la sezione duplicata e lavoriamo sulla card sottostante --}}
@php
$legacyCode = request('legacy_code');
$mapping = null;
if ($legacyCode) {
$mapping = \App\Models\GesconImportMapping::where('user_id', auth()->id())
->where('legacy_code', $legacyCode)
->first();
}
$stabileCurrent = $stabileCurrent ?? null;
$gestioneCurrent = $gestioneCurrent ?? null;
if (!$stabileCurrent && $mapping) {
try {
$candidate = $mapping->stabile;
} catch (\Throwable $e) {
$candidate = null;
}
if ($candidate) {
$stabileCurrent = $candidate;
}
}
if (!$stabileCurrent && $legacyCode) {
try {
$candidates = [];
if (\Illuminate\Support\Facades\Schema::hasColumn('stabili', 'codice_stabile')) { $candidates[] = 'codice_stabile'; }
if (\Illuminate\Support\Facades\Schema::hasColumn('stabili', 'codice_interno')) { $candidates[] = 'codice_interno'; }
if (\Illuminate\Support\Facades\Schema::hasColumn('stabili', 'old_id')) { $candidates[] = 'old_id'; }
if (!empty($candidates)) {
$stabileCurrent = \App\Models\Stabile::where(function ($q) use ($candidates, $legacyCode) {
foreach ($candidates as $idx => $col) {
$method = $idx === 0 ? 'where' : 'orWhere';
$q->{$method}($col, $legacyCode);
}
})->first();
}
if (!$stabileCurrent) {
$stabileCurrent = \App\Models\Stabile::where('denominazione', 'like', $legacyCode . '%')->first();
}
} catch (\Throwable $e) {
$stabileCurrent = null;
}
}
if (!$gestioneCurrent && $stabileCurrent) {
try {
$gestioneCurrent = \App\Models\GestioneContabile::where('stabile_id', $stabileCurrent->id)
->orderByDesc('gestione_attiva')
->orderByDesc('anno_gestione')
->orderBy('tipo_gestione')
->orderByDesc('numero_straordinaria')
->first();
} catch (\Throwable $e) {
$gestioneCurrent = null;
}
}
$isSuper = $isSuper ?? (auth()->user()?->hasRole('super-admin') ?? false);
if (!isset($blockMappingUi)) {
$blockMappingUi = !$stabileCurrent && !$isSuper;
}
$globalMapping = \App\Models\GesconImportMapping::where('user_id', auth()->id())
->where(function($q){ $q->whereNull('legacy_code')->orWhere('legacy_code','*'); })
->orderByDesc('updated_at')
->first();
// Elenco stabili legacy per selezione rapida (riusa $legacyPreview passato dalla vista index)
$legacyOptions = $legacyPreview ?? [];
// 1) Preferisci Stabili.mdb per i campi legacy; fallback al dataset
$legacyFields = [];
$legacySource = null;
try {
$hasMdb = trim((string)@shell_exec('command -v mdb-export')) !== '';
$mdbPath = \App\Models\UserSetting::get('gescon.mdb_stabili');
if (!$mdbPath || !@is_file($mdbPath) || !@is_readable($mdbPath)) {
$legacyArchivePath = '/mnt/gescon-archives/gescon/dbc/STABILI.mdb';
if (@is_file($legacyArchivePath) && @is_readable($legacyArchivePath)) {
$mdbPath = $legacyArchivePath;
} else {
$storageFallback = '/var/www/netgescon/storage/gescon/Stabili.mdb';
if (@is_file($storageFallback) && @is_readable($storageFallback)) { $mdbPath = $storageFallback; }
}
}
if ($hasMdb && $mdbPath && @is_file($mdbPath)) {
$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';
$tables = @shell_exec(sprintf('%s -1 %s 2>/dev/null', escapeshellarg($binTables), escapeshellarg($mdbPath))) ?: '';
$tableGuess = 'Stabili';
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 (@filesize($tmp) > 0) {
$fh = fopen($tmp, 'r');
$headers = fgetcsv($fh, 0, '|');
fclose($fh);
@unlink($tmp);
$headers = $headers ? array_map(fn($h) => strtolower(trim((string)$h)), $headers) : [];
$preferred = ['denominazione','indirizzo','numero_civico','cap','comune','provincia','codice_fiscale','cod_fisc_amministratore','codice_comune_catasto','foglio_catasto','particella_catasto','ac_foglio','ac_particella','ac_sub','ac_categoria','ac_classe','ac_consistenza','ac_rendita'];
$keys = array_values(array_unique(array_merge($preferred, $headers)));
foreach ($keys as $k) { if ($k === '' || $k === null) continue; $legacyFields[] = ['name' => ucwords(str_replace(['_','-'],' ', (string)$k)), 'key' => (string)$k]; }
$legacySource = 'mdb';
} else { @unlink($tmp); }
}
} catch (\Throwable $e) { /* fallback below */ }
if (empty($legacyFields)) {
try {
$ds = \App\Models\MigrationDataset::where('key','stabili')->orderByDesc('updated_at')->first();
$sample = $ds ? \App\Models\MigrationRecord::where('dataset_id', $ds->id)
->when($legacyCode, function($q) use ($legacyCode){ $q->where('legacy_key', $legacyCode); })
->orderBy('id')->first() : null;
$keys = array_keys((array)($sample?->data ?? []));
$preferred = ['denominazione','indirizzo','numero_civico','cap','comune','provincia','codice_fiscale','cod_fisc_amministratore','codice_comune_catasto','foglio_catasto','particella_catasto','ac_foglio','ac_particella','ac_sub','ac_categoria','ac_classe','ac_consistenza','ac_rendita'];
$keys = array_values(array_unique(array_merge($preferred, $keys)));
foreach ($keys as $k) { if ($k === '' || $k === null) continue; $legacyFields[] = ['name' => ucwords(str_replace(['_','-'],' ', (string)$k)), 'key' => (string)$k]; }
$legacySource = 'dataset';
} catch (\Throwable $e) {
$legacyFields = [
['name' => 'Denominazione', 'key' => 'denominazione'],
['name' => 'Indirizzo', 'key' => 'indirizzo'],
['name' => 'CAP', 'key' => 'cap'],
['name' => 'Comune', 'key' => 'comune'],
['name' => 'Provincia', 'key' => 'provincia'],
['name' => 'CF Stabile', 'key' => 'codice_fiscale'],
['name' => 'CF Amministratore', 'key' => 'cod_fisc_amministratore'],
];
$legacySource = 'static';
}
}
// 1b) Unisci eventuali colonne da tabelle sorgente avanzate (configurate dal Super Admin)
try {
$srcConn = \App\Models\UserSetting::get('gescon.source_conn');
$srcTablesRaw = \App\Models\UserSetting::get('gescon.source_tables');
$srcTables = [];
if (is_string($srcTablesRaw) && $srcTablesRaw !== '') {
$decoded = null; try { $decoded = json_decode($srcTablesRaw, true, 512, JSON_THROW_ON_ERROR); } catch (\Throwable $e) { $decoded = null; }
if (is_array($decoded)) { $srcTables = array_values(array_filter(array_map('strval', $decoded))); }
else { $srcTables = array_values(array_filter(array_map('trim', explode(',', $srcTablesRaw)))); }
}
$conn = $srcConn ?: 'gescon_import';
if (!empty($srcTables) && \Illuminate\Support\Facades\DB::connection($conn)) {
foreach ($srcTables as $t) {
try {
if (!\Illuminate\Support\Facades\Schema::connection($conn)->hasTable($t)) continue;
$cols = \Illuminate\Support\Facades\Schema::connection($conn)->getColumnListing($t);
foreach ($cols as $col) {
$key = $t.'.'.$col; $label = strtoupper($t).' • '.str_replace('_',' ', $col);
$legacyFields[] = ['name' => $label, 'key' => $key];
}
} catch (\Throwable $e) { /* ignore */ }
}
}
// dedup mantenendo ordine
$seen = [];
$legacyFields = array_values(array_filter($legacyFields, function($f) use (&$seen){ $k = $f['key']; if(isset($seen[$k])) return false; $seen[$k]=1; return true; }));
} catch (\Throwable $e) { /* ignore */ }
$targetFields = [
// Identificazione e indirizzo
'stabile.denominazione' => 'Stabile → Denominazione',
'stabile.codice_interno' => 'Stabile → Codice Interno',
'stabile.indirizzo' => 'Stabile → Indirizzo',
'stabile.numero_civico' => 'Stabile → Numero Civico',
'stabile.scala' => 'Stabile → Scala',
'stabile.cap' => 'Stabile → CAP',
'stabile.comune_id' => 'Stabile → Comune (ID)',
'stabile.comune_cod' => 'Stabile → Comune (cod legacy)',
'stabile.provincia' => 'Stabile → Provincia',
'stabile.regione' => 'Stabile → Regione',
// Dati amministratore
'stabile.cod_fisc_amministratore' => 'Stabile → CF Amministratore',
// Dati catastali
'stabile.codice_comune_catasto' => 'Stabile → Catasto: Codice Comune',
'stabile.foglio' => 'Stabile → Catasto: Foglio',
'stabile.foglio_catasto' => 'Stabile → Catasto: Foglio (campo dedicato)',
'stabile.particella' => 'Stabile → Catasto: Particella',
'stabile.particella_catasto' => 'Stabile → Catasto: Particella (campo dedicato)',
'stabile.mappale' => 'Stabile → Catasto: Mappale',
'stabile.subalterno' => 'Stabile → Catasto: Subalterno',
'stabile.categoria_catastale' => 'Stabile → Catasto: Categoria',
'stabile.classe' => 'Stabile → Catasto: Classe',
'stabile.consistenza' => 'Stabile → Catasto: Consistenza',
'stabile.rendita_catastale' => 'Stabile → Catasto: Rendita',
'stabile.superficie_catastale' => 'Stabile → Catasto: Superficie',
// Caratteristiche edificio
'stabile.anno_costruzione' => 'Stabile → Anno costruzione',
'stabile.numero_piani' => 'Stabile → Numero Piani',
'stabile.ascensore' => 'Stabile → Ascensore (bool)',
'stabile.riscaldamento_centralizzato' => 'Stabile → Riscaldamento Centralizzato (bool)',
'stabile.tipo_edificio' => 'Stabile → Tipo edificio',
// Dati amministrativi
'stabile.codice_fiscale_condominio' => 'Stabile → Codice Fiscale Condominio',
'stabile.partita_iva_condominio' => 'Stabile → Partita IVA Condominio',
'stabile.codice_destinatario_sdi' => 'Stabile → SDI: Codice Destinatario',
'stabile.pec_condominio' => 'Stabile → PEC Condominio',
'stabile.pec_amministratore' => 'Stabile → PEC Amministratore',
'stabile.modalita_rateizzazione' => 'Stabile → Rate: Modalità',
'stabile.inizio_esercizio' => 'Stabile → Rate: Inizio Esercizio',
'stabile.fine_esercizio' => 'Stabile → Rate: Fine Esercizio',
// Dati bancari associati allo stabile
'stabile.iban_principale' => 'Stabile → Banca: IBAN Principale',
'stabile.banca_principale' => 'Stabile → Banca: Istituto Principale',
'stabile.filiale' => 'Stabile → Banca: Filiale Principale',
'stabile.iban_secondario' => 'Stabile → Banca: IBAN Secondario',
'stabile.banca_secondaria' => 'Stabile → Banca: Istituto Secondario',
'stabile.filiale_secondaria' => 'Stabile → Banca: Filiale Secondaria',
// Campi banca dettagliati (tab "Banche")
'banca.denominazione' => 'Banca → Denominazione (mapping avanzato)',
'banca.nome_banca' => 'Banca → Nome banca (legacy compatibile)',
'banca.intestazione' => 'Banca → Intestazione conto',
'banca.numero_conto' => 'Banca → Numero conto',
'banca.iban' => 'Banca → IBAN',
'banca.bic_swift' => 'Banca → BIC/SWIFT',
'banca.abi' => 'Banca → ABI',
'banca.cab' => 'Banca → CAB',
'banca.cuc' => 'Banca → Codice CUC',
'banca.cin' => 'Banca → CIN',
'banca.filiale' => 'Banca → Filiale / sportello',
'banca.valuta' => 'Banca → Valuta',
'banca.saldo_iniziale' => 'Banca → Saldo iniziale',
'banca.data_saldo_iniziale' => 'Banca → Data saldo iniziale',
'banca.stato_conto' => 'Banca → Stato conto',
'banca.note' => 'Banca → Note',
'banca.is_nostro_conto' => 'Banca → Flag conto principale',
'banca.codice_cassa' => 'Banca → Codice cassa legacy',
'banca.tipo_conto' => 'Banca → Tipo conto',
// I dati bancari vengono ora gestiti nel tab dedicato «Banche»
];
// Mapping extra: Unità and Tabelle field mappings
$unitaFieldMapping = [];
$tabelleFieldMapping = [];
$vociFieldMapping = [];
try {
if ($mapping) {
$unitaFieldMapping = (array) data_get($mapping->meta, 'unita_field_mapping', []);
$tabelleFieldMapping = (array) data_get($mapping->meta, 'tabelle_field_mapping', []);
$vociFieldMapping = (array) data_get($mapping->meta, 'voci_field_mapping', []);
}
} catch (\Throwable $e) { /* ignore */ }
// Sample Unità legacy rows (from staging) to infer headers
$legacyUnitaSampleRows = [];
try {
$importConn = \App\Models\UserSetting::get('gescon.import_conn', 'gescon_import');
if ($legacyCode && \Illuminate\Support\Facades\Schema::connection($importConn)->hasTable('condomin')) {
$legacyUnitaSampleRows = \Illuminate\Support\Facades\DB::connection($importConn)
->table('condomin')
->when(\Illuminate\Support\Facades\Schema::connection($importConn)->hasColumn('condomin', 'cod_stabile'), function ($q) use ($legacyCode) {
$q->where('cod_stabile', $legacyCode);
})
->limit(5)
->get()
->map(fn($r) => (array)$r)
->toArray();
}
} catch (\Throwable $e) { $legacyUnitaSampleRows = []; }
@endphp
<div id="gescon-mapping">
<div class="netgescon-card">
<div class="netgescon-card-header flex flex-col gap-3">
@include('admin.gescon-import.tabs.partials._mapping_header', [
'legacyCode' => $legacyCode ?? null,
'legacyOptions' => $legacyOptions ?? [],
'availableYears' => $availableYears ?? [],
'stabileCurrent' => $stabileCurrent ?? null,
'gestioneCurrent' => $gestioneCurrent ?? null,
'isSuper' => $isSuper ?? false,
'blockMappingUi' => $blockMappingUi ?? false,
'targetFields' => $targetFields ?? [],
'savedPresetKey' => $savedPresetKey ?? null,
'legacySource' => $legacySource ?? null,
'mapping' => $mapping ?? null,
'globalMapping' => $globalMapping ?? null,
])
</div>
@include('admin.gescon-import.tabs.partials._segment_stabili', [
'legacyFields' => $legacyFields ?? [],
'savedSources' => $savedSources ?? [],
'targetFields' => $targetFields ?? [],
'mapping' => $mapping ?? [],
'globalMapping' => $globalMapping ?? [],
'autoMap' => $autoMap ?? [],
'isGlobal' => $isGlobal ?? false,
'stabileCurrent' => $stabileCurrent ?? null,
'blockMappingUi' => $blockMappingUi ?? false,
])
@include('admin.gescon-import.tabs.partials._segment_gestioni', [
'legacyCode' => $legacyCode ?? null,
])
@include('admin.gescon-import.tabs.partials._segment_finanziari', [
'legacyCode' => $legacyCode ?? null,
'stabileCurrent' => $stabileCurrent ?? null,
])
@include('admin.gescon-import.tabs.partials._segment_unita', [
'legacyCode' => $legacyCode ?? null,
'legacyUnitaSampleRows' => $legacyUnitaSampleRows,
'unitaFieldMapping' => $unitaFieldMapping,
])
@include('admin.gescon-import.tabs.partials._segment_tabelle', [
'legacyCode' => $legacyCode ?? null,
'tabelleFieldMapping' => $tabelleFieldMapping,
])
<div id="segment-voci" class="segment-container d-none">
@include('admin.gescon-import.partials.voci-field-mapping', [
'vociFieldMapping' => $vociFieldMapping ?? [],
])
</div>
</div>
{{-- Diagnostics are handled inside the extracted JS file --}}
<style>
#gescon-mapping .mapping-two-col {
display: flex !important;
gap: 24px;
align-items: stretch;
width: 100%;
}
#gescon-mapping .mapping-two-col .mapping-pane {
flex: 1 1 0;
min-width: 0;
}
/* Ensure legacy lists stack vertically and scroll predictably */
#gescon-mapping .legacy-field-row,
#gescon-mapping .legacy-tab-row,
#gescon-mapping .legacy-voce-row { display:block; border-bottom:1px solid #f1f1f1; }
#gescon-mapping #legacy-fields-list,
#gescon-mapping #legacy-tabelle-list,
#gescon-mapping #legacy-voci-list { max-height: 24rem; overflow:auto; display:block; }
/* Normalize font to match app; avoid monospace labels */
#gescon-mapping .mapping-row .w-40,
#gescon-mapping .mapping-tab-row .w-40,
#gescon-mapping .mapping-voce-row .w-40 { font-family: inherit; font-variant-ligatures: none; }
#gescon-mapping .mapping-tab-row .w-40,
#gescon-mapping .mapping-row .w-40 { width: 12rem; }
/* Tighten inputs for better alignment */
#gescon-mapping .target-field-input,
#gescon-mapping .target-tab-input { padding:.3rem .45rem; }
/* Card equal height in column layout */
#gescon-mapping .mapping-two-col .netgescon-card { display:flex; flex-direction:column; height:100%; }
#gescon-mapping .mapping-two-col .netgescon-card-body { flex:1 1 auto; }
@media (max-width: 991px) {
#gescon-mapping .mapping-two-col {
flex-direction: column;
gap: 16px;
}
}
/* Steps di navigazione import */
#gescon-mapping .netgescon-steps ul{list-style:none;display:flex;gap:8px;padding:0;margin:0}
#gescon-mapping .netgescon-steps li{padding:6px 10px;border-radius:16px;background:#f2f2f2;color:#666;font-size:12px}
#gescon-mapping .netgescon-steps li.active{background:#0d6efd1a;color:#0d6efd;font-weight:600}
#gescon-mapping .netgescon-steps li.disabled{opacity:.6}
/* Box progress import */
#gescon-mapping .import-progress{border:1px solid #e5e5e5;border-radius:8px;padding:10px 12px;background:#fff;margin-bottom:12px}
/* Bootstrap-free utility */
#gescon-mapping .d-none{display:none !important}
/* Compatta i controlli in questa pagina per una resa più fine (senza impattare altrove) */
#gescon-mapping #mapping-form .netgescon-input,
#gescon-mapping #mapping-form select.mapping-select,
#gescon-mapping #legacy-filter-input,
#gescon-mapping #target-filter-input,
#gescon-mapping .netgescon-card-header select.netgescon-input {
font-size: .875rem; /* text-sm */
line-height: 1.25rem;
padding: .25rem .5rem !important;
}
#gescon-mapping #mapping-rows .mapping-row label small { font-size: .82rem; }
#gescon-mapping .netgescon-card-header .netgescon-btn { padding: .25rem .5rem; font-size: .8rem; }
#gescon-mapping .netgescon-btn-sm { padding: .2rem .45rem; font-size: .72rem; }
/* Stato attivo pulsanti filtro mapping */
#gescon-mapping #mapping-visibility-buttons .netgescon-btn.active { background:#0d6efd; color:#fff; border-color:#0d6efd; }
/* (Dimming rimosso: i campi restano sempre visibili) */
/* Clickable steps */
#gescon-mapping .mapping-steps li.clickable{cursor:pointer;}
#gescon-mapping .mapping-steps li.clickable:hover{background:#e2ecf9;}
#gescon-mapping .segment-container.d-none{display:none !important;}
</style>
{{-- (pannello staging rimosso: le anteprime sono ora integrate nei singoli segmenti) --}}
</div> <!-- /#gescon-mapping -->