[FEAT] Inventario: esportazione CSV in formato import-compatibile (#441)
Nuovo pulsante "Esporta" in assets.html: scarica l'intero inventario (tutte le pagine) in CSV con le stesse colonne riconosciute dall'import (name, asset_type, criticality, data_classification, internet_facing, dependencies_count, regulated, external_ref, vendor, location). I criteri data_classification/internet_facing/ dependencies_count/regulated sono ricostruiti da relevance_criteria con mappatura reversibile (round-trip stabile, verificato 100/100 casi). Parser import reso quote-aware (RFC-style) per round-trip lossless di nomi con virgola; retro-compatibile con CSV non quotato. help.js + cache-buster (?v=20260625b) su tutte le pagine. version 1.24.14. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b33e0d051e
commit
87fc5bf959
@@ -1095,7 +1095,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="js/i18n.js?v=20260631"></script>
|
<script src="js/i18n.js?v=20260631"></script>
|
||||||
<script src="js/help.js?v=20260625a"></script>
|
<script src="js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
loadSidebar();
|
loadSidebar();
|
||||||
|
|||||||
@@ -167,7 +167,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth check ───────────────────────────────────────────
|
// ── Auth check ───────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
+80
-3
@@ -283,6 +283,7 @@
|
|||||||
<span class="text-muted" style="font-size:0.8rem; margin-right:8px;">Art. 21.2.i NIS2</span>
|
<span class="text-muted" style="font-size:0.8rem; margin-right:8px;">Art. 21.2.i NIS2</span>
|
||||||
<button class="btn btn-secondary" onclick="showDependencyMap()" title="Mappa delle dipendenze tra i beni">Mappa Dipendenze</button>
|
<button class="btn btn-secondary" onclick="showDependencyMap()" title="Mappa delle dipendenze tra i beni">Mappa Dipendenze</button>
|
||||||
<button class="btn btn-secondary" onclick="openImportModal()" title="Importa beni da CSV/CMDB">Importa</button>
|
<button class="btn btn-secondary" onclick="openImportModal()" title="Importa beni da CSV/CMDB">Importa</button>
|
||||||
|
<button class="btn btn-secondary" onclick="exportAssets()" title="Esporta l'inventario in CSV (stesso formato dell'importazione)">Esporta</button>
|
||||||
<button class="btn btn-primary" onclick="showCreateAssetModal()">+ Nuovo bene</button>
|
<button class="btn btn-primary" onclick="showCreateAssetModal()">+ Nuovo bene</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -375,7 +376,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ─────────────────────────────────────────
|
// ── Auth & Init ─────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
@@ -1089,13 +1090,32 @@ function impFile(e){
|
|||||||
const f=e.target.files[0]; if(!f)return;
|
const f=e.target.files[0]; if(!f)return;
|
||||||
const r=new FileReader(); r.onload=()=>{document.getElementById('imp-csv').value=r.result;}; r.readAsText(f);
|
const r=new FileReader(); r.onload=()=>{document.getElementById('imp-csv').value=r.result;}; r.readAsText(f);
|
||||||
}
|
}
|
||||||
|
// Split di una riga CSV gestendo virgolette ed escape (""), così nomi con
|
||||||
|
// virgola/virgolette importano correttamente (round-trip con l'export). Le righe
|
||||||
|
// non quotate si comportano come prima (retro-compatibile).
|
||||||
|
function splitCsvLine(line){
|
||||||
|
const out=[]; let cur=''; let inQ=false;
|
||||||
|
for(let i=0;i<line.length;i++){
|
||||||
|
const ch=line[i];
|
||||||
|
if(inQ){
|
||||||
|
if(ch==='"'){ if(line[i+1]==='"'){ cur+='"'; i++; } else inQ=false; }
|
||||||
|
else cur+=ch;
|
||||||
|
} else {
|
||||||
|
if(ch==='"') inQ=true;
|
||||||
|
else if(ch===',') { out.push(cur); cur=''; }
|
||||||
|
else cur+=ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.push(cur);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
function parseCsv(text){
|
function parseCsv(text){
|
||||||
const lines=text.trim().split(/\r?\n/).filter(l=>l.trim());
|
const lines=text.trim().split(/\r?\n/).filter(l=>l.trim());
|
||||||
if(lines.length<2)return [];
|
if(lines.length<2)return [];
|
||||||
const headers=lines[0].split(',').map(h=>h.trim().toLowerCase());
|
const headers=splitCsvLine(lines[0]).map(h=>h.trim().toLowerCase());
|
||||||
const truthy=v=>/^(1|true|si|sì|yes|y)$/i.test((v||'').trim());
|
const truthy=v=>/^(1|true|si|sì|yes|y)$/i.test((v||'').trim());
|
||||||
return lines.slice(1).map(line=>{
|
return lines.slice(1).map(line=>{
|
||||||
const cells=line.split(','); const o={};
|
const cells=splitCsvLine(line); const o={};
|
||||||
headers.forEach((h,i)=>{ o[h]=(cells[i]||'').trim(); });
|
headers.forEach((h,i)=>{ o[h]=(cells[i]||'').trim(); });
|
||||||
const a={name:o.name, asset_type:o.asset_type, criticality:o.criticality,
|
const a={name:o.name, asset_type:o.asset_type, criticality:o.criticality,
|
||||||
data_classification:o.data_classification, vendor:o.vendor, location:o.location,
|
data_classification:o.data_classification, vendor:o.vendor, location:o.location,
|
||||||
@@ -1120,6 +1140,63 @@ async function doImport(){
|
|||||||
}catch(e){showNotification('Errore di connessione','error');}
|
}catch(e){showNotification('Errore di connessione','error');}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Export inventario (stesso formato dell'import) #441 ──── */
|
||||||
|
// Stesse colonne riconosciute dall'import: re-importando il file si ricostruisce
|
||||||
|
// l'inventario (lo scoring di rilevanza NIS2 viene ricalcolato in import).
|
||||||
|
const EXPORT_COLS = ['name','asset_type','criticality','data_classification','internet_facing','dependencies_count','regulated','external_ref','vendor','location'];
|
||||||
|
function csvCell(v){
|
||||||
|
const s = (v===null||v===undefined) ? '' : String(v).replace(/[\r\n]+/g,' ');
|
||||||
|
return /[",]/.test(s) ? '"'+s.replace(/"/g,'""')+'"' : s;
|
||||||
|
}
|
||||||
|
// Ricostruisce le colonne di import dai criteri di scoring salvati (relevance_criteria).
|
||||||
|
// Le coppie sono reversibili: re-importando si riottengono gli stessi criteri.
|
||||||
|
function assetToExportRow(a){
|
||||||
|
let crit = {};
|
||||||
|
try { crit = (typeof a.relevance_criteria === 'string') ? JSON.parse(a.relevance_criteria||'{}') : (a.relevance_criteria||{}); } catch(e){ crit = {}; }
|
||||||
|
const dcMap = { gdpr_art9:'special', personal_large:'personal', personal_fin:'financial', confidential:'confidential', public:'public' };
|
||||||
|
const depMap = { ge5_critical:5, n3_4_critical:3, n2_critical:2, n1_critical:1, none:0 };
|
||||||
|
const c4 = crit.c4_dependencies;
|
||||||
|
return {
|
||||||
|
name: a.name || '',
|
||||||
|
asset_type: a.asset_type || '',
|
||||||
|
criticality: a.criticality || '',
|
||||||
|
data_classification: dcMap[crit.c3_data_processed] || '',
|
||||||
|
internet_facing: /^internet/.test(crit.c5_exposure || '') ? 'true' : 'false',
|
||||||
|
dependencies_count: (c4 in depMap) ? depMap[c4] : 0,
|
||||||
|
regulated: (crit.c6_regulatory && crit.c6_regulatory !== 'none') ? 'true' : 'false',
|
||||||
|
external_ref: a.external_ref || '',
|
||||||
|
vendor: a.vendor || '',
|
||||||
|
location: a.location || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function exportAssets(){
|
||||||
|
showNotification('Preparazione esportazione…','info');
|
||||||
|
let items=[], page=1, total=Infinity;
|
||||||
|
try{
|
||||||
|
while(items.length < total){
|
||||||
|
const res = await api.listAssets({ per_page:200, page });
|
||||||
|
if(!res || !res.success){ showNotification((res&&res.message)||'Errore durante l\'esportazione','error'); return; }
|
||||||
|
const d = res.data || {};
|
||||||
|
const batch = d.items || (Array.isArray(d) ? d : []);
|
||||||
|
total = (typeof d.total === 'number') ? d.total : batch.length;
|
||||||
|
items = items.concat(batch);
|
||||||
|
if(!batch.length || page>50) break;
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
}catch(e){ showNotification('Errore di connessione durante l\'esportazione','error'); return; }
|
||||||
|
if(!items.length){ showNotification('Inventario vuoto: niente da esportare','warning'); return; }
|
||||||
|
const lines = [ EXPORT_COLS.join(',') ];
|
||||||
|
items.map(assetToExportRow).forEach(r => lines.push(EXPORT_COLS.map(c => csvCell(r[c])).join(',')));
|
||||||
|
const csv = '' + lines.join('\r\n'); // BOM per Excel
|
||||||
|
const stamp = new Date().toISOString().slice(0,10);
|
||||||
|
const blob = new Blob([csv], { type:'text/csv;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const el = document.createElement('a');
|
||||||
|
el.href = url; el.download = `inventario-nis2-${stamp}.csv`;
|
||||||
|
document.body.appendChild(el); el.click(); el.remove(); URL.revokeObjectURL(url);
|
||||||
|
showNotification(`Esportati ${items.length} beni nel formato di importazione`,'success');
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -156,7 +156,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -356,7 +356,7 @@
|
|||||||
<script>if (window.bootstrap && bootstrap.loadFonts) { bootstrap.loadFonts('vendor/bootstrap-italia/dist/fonts'); }</script>
|
<script>if (window.bootstrap && bootstrap.loadFonts) { bootstrap.loadFonts('vendor/bootstrap-italia/dist/fonts'); }</script>
|
||||||
<script src="js/common-bi.js?v=20260624f"></script>
|
<script src="js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="js/i18n.js?v=20260631"></script>
|
<script src="js/i18n.js?v=20260631"></script>
|
||||||
<script src="js/help.js?v=20260625a"></script>
|
<script src="js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script src="/js/competenze.js?v=20260617"></script>
|
<script src="/js/competenze.js?v=20260617"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */
|
/* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */
|
||||||
|
|||||||
@@ -156,7 +156,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth check ───────────────────────────────────────────
|
// ── Auth check ───────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
+1
-1
@@ -1154,7 +1154,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// La guida è accessibile anche da non autenticati (utile per onboarding)
|
// La guida è accessibile anche da non autenticati (utile per onboarding)
|
||||||
// ma se sei loggato carichi sidebar + i18n normalmente. Stessa logica
|
// ma se sei loggato carichi sidebar + i18n normalmente. Stessa logica
|
||||||
|
|||||||
@@ -364,7 +364,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ──────────────────────────────────────────────
|
// ── Auth & Init ──────────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
+1
-1
@@ -197,7 +197,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script src="/js/isms.js?v=20260620b"></script>
|
<script src="/js/isms.js?v=20260620b"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -952,6 +952,14 @@ const HelpSystem = (function () {
|
|||||||
'Il campo <code>external_ref</code> abilita la deduplica: re-importare lo stesso ID aggiorna il bene invece di duplicarlo.',
|
'Il campo <code>external_ref</code> abilita la deduplica: re-importare lo stesso ID aggiorna il bene invece di duplicarlo.',
|
||||||
'Gli stessi dati possono arrivare via connettore automatico (API <code>POST /api/services/assets-ingest</code>, scope ingest:assets).'
|
'Gli stessi dati possono arrivare via connettore automatico (API <code>POST /api/services/assets-ingest</code>, scope ingest:assets).'
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Esporta (pulsante "Esporta")',
|
||||||
|
items: [
|
||||||
|
'Scarica l\'intero inventario in un file <strong>CSV</strong> con le <strong>stesse colonne dell\'importazione</strong>, così puoi salvarne una copia, modificarlo fuori e re-importarlo senza riformattare nulla.',
|
||||||
|
'Il file usa le colonne riconosciute dall\'import; re-importandolo lo <strong>scoring di rilevanza NIS2</strong> viene ricalcolato e i beni con lo stesso <code>external_ref</code> vengono aggiornati (nessun duplicato).',
|
||||||
|
'Utile per backup, migrazioni e modifiche massive in foglio di calcolo (il file è in UTF-8, apribile direttamente in Excel).'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
references: [
|
references: [
|
||||||
|
|||||||
+1
-1
@@ -163,7 +163,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script src="/js/kb.js"></script>
|
<script src="/js/kb.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Gate auth + chrome come le altre pagine -bi.
|
// Gate auth + chrome come le altre pagine -bi.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -172,7 +172,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
loadSidebar();
|
loadSidebar();
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script src="/js/organigramma.js?v=20260617"></script>
|
<script src="/js/organigramma.js?v=20260617"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -346,7 +346,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ─────────────────────────────────────────
|
// ── Auth & Init ─────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
+1
-1
@@ -136,7 +136,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script src="/js/raci.js?v=20260619"></script>
|
<script src="/js/raci.js?v=20260619"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -456,7 +456,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ─────────────────────────────────────────
|
// ── Auth & Init ─────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
@@ -146,7 +146,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
+1
-1
@@ -507,7 +507,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ──────────────────────────────────────────────
|
// ── Auth & Init ──────────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
@@ -701,7 +701,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth check ───────────────────────────────────────────
|
// ── Auth check ───────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
@@ -199,7 +199,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -196,7 +196,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -490,7 +490,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260621sp"></script>
|
<script src="/js/i18n.js?v=20260621sp"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ─────────────────────────────────────────
|
// ── Auth & Init ─────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
@@ -305,7 +305,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
// ── Auth & Init ─────────────────────────────────────────
|
// ── Auth & Init ─────────────────────────────────────────
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"version": "1.24.13", "build": "2026-06-25-v1.24.13", "date": "2026-06-25", "changelog": "Inventario beni — Aggiunta la Mappa delle Dipendenze: nuovo pulsante 'Mappa Dipendenze' che mostra le relazioni tra i beni, e nel form di ogni bene un campo per selezionare i beni da cui dipende (supporta l'analisi del rischio, art. 24 D.Lgs. 138/2024). Aggiunto il pulsante per eliminare un bene dall'inventario (lista e dettaglio). Risolti ticket #440 e #439."}
|
{"version": "1.24.14", "build": "2026-06-25-v1.24.14", "date": "2026-06-25", "changelog": "Inventario beni — Aggiunto il pulsante 'Esporta': scarica l'intero inventario in CSV con le stesse colonne dell'importazione, cosi puoi salvarne una copia, modificarlo in foglio di calcolo e re-importarlo senza riformattare nulla (re-import: scoring NIS2 ricalcolato, deduplica per external_ref). Il parser di importazione ora gestisce le virgolette CSV, cosi nomi con virgola si re-importano correttamente. Risolve ticket #441."}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="/js/i18n.js?v=20260631"></script>
|
<script src="/js/i18n.js?v=20260631"></script>
|
||||||
<script src="/js/help.js?v=20260625a"></script>
|
<script src="/js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
loadSidebar();
|
loadSidebar();
|
||||||
|
|||||||
@@ -326,7 +326,7 @@
|
|||||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||||
<script src="/js/common-bi.js?v=20260624f"></script>
|
<script src="/js/common-bi.js?v=20260624f"></script>
|
||||||
<script src="js/i18n.js?v=20260631"></script>
|
<script src="js/i18n.js?v=20260631"></script>
|
||||||
<script src="js/help.js?v=20260625a"></script>
|
<script src="js/help.js?v=20260625b"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!checkAuth()) throw new Error('Not authenticated');
|
if (!checkAuth()) throw new Error('Not authenticated');
|
||||||
loadSidebar();
|
loadSidebar();
|
||||||
|
|||||||
Reference in New Issue
Block a user