1/** 2 * tec-acam-piano.js â IL PIANO DELLE AZIONI CORRETTIVE E DI MIGLIORAMENTO (ACAM). 3 * 4 * ââ LA RICHIESTA, NELLE SUE PAROLE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 5 * Beniamino Macchiarola: «apprezzerei se la integrassi anche con il piano di ACAM. Attendo al 6 * contempo la tua proposta che costituirà l'allegato nuovo al pacchetto DVR. Questa dovrà essere 7 * corredata anche dalle ACAM. Naturalmente, la versione allegata al pacchetto dovrà essere ed 8 * apparire outstanding!» 9 * 10 * ââ CHE COSA E' ACAM QUI DENTRO, E DA DOVE VIENE âââââââââââââââââââââââââââââââââââââââââââââ 11 * Non e' una parola nuova: e' il modello che questo progetto si e' dato nell'assessment ACAM 12 * (v20.373.118). Cinque requisiti, e questo file esiste per renderli MISURABILI e VISIBILI: 13 * R1 ogni deviazione propone un'azione â un rilievo senza azione e' un'accusa che non porta 14 * da nessuna parte 15 * R2 l'azione e' assegnabile e formalizzabile: chi, entro quando, con quale gravita' 16 * (art. 28 c.2 lett. c e d) 17 * R3 l'azione aggredisce la CAUSA e sceglie il livello PIU' ALTO PRATICABILE della gerarchia 18 * (art. 15 c.1 lett. c, e, f, i · ISO 45001 §10.2 d) 19 * R4 la chiusura e' DIMOSTRATA efficace, non dichiarata (ISO 45001 §10.2 f) 20 * R5 il tutto protegge il cliente: linguaggio da consulente, qualificazione giuridica corretta 21 * 22 * ââ LA RIGA DELLA LEGGE CHE NESSUNO CITA, E CHE E' LA PIU' PESANTE ââââââââââââââââââââââââââ 23 * Art. 28 c.2 lett. d): i ruoli che devono provvedere alle misure vanno assegnati «unicamente a 24 * soggetti in possesso di adeguate COMPETENZE E POTERI». Un campo «responsabile» con dentro un 25 * nome non basta: un'azione che costa, assegnata a chi non ha potere di spesa, e' formalmente 26 * viziata. E' il requisito piu' trascurato del prodotto e del mercato. 27 * Art. 55 c.3: il programma di miglioramento mancante e' una contravvenzione autonomamente 28 * sanzionata â l'unico gap di questo modulo che ha un capo d'imputazione. 29 * 30 * ââ NON CALCOLA: INTERROGA. E NON SCRIVE ââââââââââââââââââââââââââââââââââââââââââââââââââââ 31 * Ogni numero ha gia' il suo proprietario e questo file non ne fabbrica di nuovi: 32 * · l'elenco unico delle azioni â _tecDvrProgramma.collectAzioniUnified 33 * · puntualita', efficacia, cause â _tecProgrammaMisura.calcola 34 * · la gerarchia delle misure â _tecMisuraGerarchia.distribuzione 35 * · la copertura dei rilievi â _tecProgrammaCopertura.copertura 36 * · corsia, stato reale, blocco 55.3 â _tecAzioneProposta 37 * · la scala di gravita' e i colori â _tecDashboardRischi.FASCE / coloreGravita 38 * Se un proprietario manca, l'indicatore si DICHIARA non misurabile invece di valere zero: uno 39 * zero inventato e' peggio di un buco dichiarato, perche' sembra un risultato. 40 * 41 * ââ UNA VISTA, DUE LETTORI ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 42 * `vista(d)` produce la struttura; `html(d)` la disegna per il tecnico; `righeDocumento(v)` la 43 * traduce per l'allegato del pacchetto. Nessuno dei due ricalcola: e' la regola nata il giorno in 44 * cui abbiamo misurato 23 motori che parlavano solo al documento. 45 * 46 * Espone window._tecAcamPiano. 47 */ 48(function (win) { 49 'use strict'; 50 if (typeof win === 'undefined') return; 51 52 var VERSION = '20.373.142'; 53 54 function _s(v) { return String(v == null ? '' : v).trim(); } 55 function _n(v) { var x = Number(v); return isNaN(x) ? 0 : x; } 56 function _arr(v) { return Array.isArray(v) ? v.filter(Boolean) : []; } 57 function _pct(a, b) { return b > 0 ? Math.round((a / b) * 100) : null; } 58 59 /** 60 * I NOVE INDICATORI, CIASCUNO CON IL SUO BERSAGLIO DICHIARATO. 61 * ISO 45001 §9.1.1 c) impone di determinare i CRITERI rispetto ai quali valutare, non solo di 62 * misurare. Un indicatore senza bersaglio e' un numero: non dice se va bene. I bersagli sono 63 * qui, leggibili e discutibili â un numero che nessuno puo' discutere e' un numero di cui 64 * nessuno si fida. 65 */ 66 var KPI_DEF = [ 67 { id: 'copertura', label: 'Copertura degli obblighi', verso: 'su', 68 definizione: '% di obblighi di trattamento con almeno unâazione', target: 100, 69 norma: 'art. 28 c.2 lett. b, c e d · art. 29 c.1 · R1', 70 perche: 'Il denominatore sono TUTTI gli obblighi: rischi oltre soglia, non conformità rilevate e adempimenti di legge. Sotto il 100% câè un obbligo orfano â nominato dalla valutazione e chiuso da nessuno.' }, 71 { id: 'formalizzazione', label: 'Formalizzazione', verso: 'su', 72 definizione: '% di azioni con responsabile E scadenza', target: 100, 73 norma: 'art. 28 c.2 lett. c e d · art. 55 c.3 · R2', 74 perche: 'à lâunico gap penalmente sanzionato: un impegno senza nome e senza data non impegna nessuno.' }, 75 { id: 'poteri', label: 'Competenze e poteri', verso: 'su', 76 definizione: '% di azioni onerose il cui responsabile ha potere di spesa', target: 100, 77 norma: 'art. 28 c.2 lett. d, inciso finale', 78 perche: 'La legge non chiede un nome: chiede un soggetto con poteri adeguati. Unâazione che costa, assegnata a chi non può spendere, è formalmente viziata.' }, 79 { id: 'causa', label: 'Analisi della causa', verso: 'su', 80 definizione: '% di azioni con la causa registrata', target: 80, 81 norma: 'ISO 45001 §10.2 b) · R3', 82 perche: 'Senza causa ogni azione è una correzione travestita da azione correttiva, e la ricorrenza resta invisibile.' }, 83 { id: 'indiceGerarchico', label: 'Indice gerarchico', verso: 'su', 84 definizione: '% di azioni sopra il livello amministrativo (gradini 1-3)', target: 50,
85 norma: 'art. 15 c.1 lett. c), f) e i) · ISO 45001 §8.1.2', 86 perche: 'à lâunica metrica che misura la QUALITà dellâazione anziché la sua puntualità . Una coda concentrata su procedure e DPI è il segnale precoce di un programma giuridicamente esposto.', 87 proposta: true }, 88 { id: 'puntualita', label: 'Chiusura puntuale', verso: 'su', 89 definizione: '% di azioni attuate entro la data attesa', target: 85, 90 norma: 'art. 28 c.2 lett. c', 91 perche: 'Sotto questa soglia il programma non governa più le proprie scadenze: le subisce.' }, 92 { id: 'scadute', label: 'Scadute', verso: 'giu', 93 definizione: '% di azioni aperte oltre la scadenza', target: 10, 94 norma: 'art. 28 c.2 lett. c · art. 29 c.3', 95 perche: 'Una scadenza passata resta scritta nel documento, ed è la prima cosa che un ispettore confronta con la data odierna.' }, 96 { id: 'efficacia', label: 'Efficacia verificata', verso: 'su', 97 definizione: '% di azioni verificate che risultano efficaci', target: 90, 98 norma: 'ISO 45001 §10.2 f) · Cass. pen. Sez. 4 n. 12122/2023 · R4', 99 perche: 'La Cassazione misura la responsabilità del datore sullâIDONEITà della misura, non sulla sua adozione. Questo è lâunico indicatore che la documenta.' }, 100 { id: 'ricorrenza', label: 'Ricorrenza', verso: 'giu', 101 definizione: '% di azioni riaperte dopo la chiusura', target: 5, 102 norma: 'ISO 45001 §10.2 b) 2)', 103 perche: 'Ricorrenza alta con puntualità alta significa che si stanno chiudendo CORREZIONI spacciandole per azioni correttive.' } 104 ]; 105 106 function _kpiDef(id) { 107 for (var i = 0; i < KPI_DEF.length; i++) if (KPI_DEF[i].id === id) return KPI_DEF[i]; 108 return null; 109 } 110 111 /** 112 * Un indicatore. `valore` null significa NON MISURABILE, e in quel caso porta il perche': 113 * la differenza fra «zero» e «non lo so» e' la differenza fra un cruscotto e un oroscopo. 114 */ 115 function _kpi(id, valore, base, perche) { 116 var def = _kpiDef(id) || {}; 117 var v = (valore == null || base === 0) ? null : Math.round(_n(valore)); 118 var esito = 'non_misurabile'; 119 if (v != null) { 120 esito = (def.verso === 'giu') ? (v <= _n(def.target) ? 'centrato' : 'sotto') 121 : (v >= _n(def.target) ? 'centrato' : 'sotto'); 122 } 123 return { 124 id: id, label: def.label, definizione: def.definizione, norma: def.norma, perche: def.perche, 125 proposta: !!def.proposta, verso: def.verso, target: _n(def.target), 126 targetTesto: (def.verso === 'giu' ? '⤠' : '⥠') + _n(def.target) + '%', 127 valore: v, base: _n(base), esito: esito, 128 nonMisurabilePerche: v == null ? _s(perche || 'nessuna azione su cui misurare') : '' 129 }; 130 } 131 132 // ââ LE AZIONI: UN SOLO ELENCO = Registro Unico (stesso codice di Tab 11 e Quadro) â 133 function azioniDi(d) { 134 var RR = win.RegistroRilievi || win.TecRegistroRilievi; 135 if (RR) { 136 try { 137 if (typeof RR.migra === 'function') RR.migra(d || {}); 138 if (typeof RR.backfillCodici === 'function') RR.backfillCodici(d || {}); 139 if (typeof RR.lista === 'function') { 140 /* Piano = solo accettate (le proposte restano nella Decisione). */ 141 return _arr(RR.lista(d, { accettato: true })); 142 } 143 } catch (_rr) {} 144 } 145 var P = win._tecDvrProgramma; 146 if (P && typeof P.collectAzioniUnified === 'function') { 147 try { return _arr(P.collectAzioniUnified(d, null, {})); } catch (_e) {} 148 } 149 return _arr(d && d.azioniMiglioramento); 150 } 151 152 function _haResp(a) { return !!_s(a && (a.responsabile || a.chi || (a.ownerOperativo && a.ownerOperativo.nome))); } 153 function _haScad(a) { return !!_s(a && (a.scadenza || a.dataPrevista || a.entro)); } 154 function _costo(a) { 155 var c = a && (a.costoStimato != null ? a.costoStimato : a.costo); 156 var n = parseFloat(String(c == null ? '' : c).replace(/[^\d.,-]/g, '').replace(',', '.')); 157 return isNaN(n) ? 0 : n; 158 } 159 /** 160 * La gravita' di un'azione, letta solo da FATTI. `priorita` non e' un fatto: l'elenco unico la 161 * normalizza a «Bassa» quando manca il rischio residuo, e quel ripiego, letto come gravita', 162 * mandava in corsia rapida azioni di cui nessuno sapeva nulla. Il rischio residuo invece e' 163 * misurato, e la scala e' quella del prodotto (P x D). 164 */ 165 function _gravitaDi(a) { 166 var g = _s(a && a.gravita).toLowerCase(); 167 if (['bassa', 'media', 'alta', 'critica'].indexOf(g) >= 0) return g; 168 var r = Number(a && (a.rResiduoPre != null ? a.rResiduoPre : a.R)); 169 if (!isNaN(r) && r > 0) { 170 if (r >= 12) return 'critica'; 171 if (r >= 8) return 'alta'; 172 if (r >= 4) return 'media'; 173 return 'bassa'; 174 } 175 return ''; 176 } 177 178 function _poteriOk(a) { 179 var o = a && a.ownerDecisore; 180 if (o && typeof o.potereDiSpesa === 'boolean') return o.potereDiSpesa; 181 return null; // non dichiarato: non e' un no, e' un buco 182 } 183 184 /** 185 * LA VISTA. Un solo calcolo, due lettori. 186 */ 187 function vista(d) { 188 d = d || {}; 189 var az = azioniDi(d); 190 var out = { 191 VERSION: VERSION, 192 norma: 'art. 28 c.2 lett. c e d · art. 15 c.1 · art. 55 c.3 D.Lgs. 81/08 · ISO 45001 §10.2', 193 totale: az.length, 194 kpi: [], allarmi: [], corsie: { rapida: 0, standard: 0, estesa: 0, nonDeterminata: 0 }, 195 gerarchia: null, misura: null, copertura: null, generabile: null, 196 responsabilita: { conResponsabile: 0, conScadenza: 0, formalizzate: 0, 197 onerose: 0, oneroseConPotere: 0, onerosePotereNonDichiarato: 0 } 198 }; 199 200 // corsie â il rigore proporzionato alla gravita' (ICH Q10, ripreso nell'assessment) 201 var AP = win._tecAzioneProposta; 202 az.forEach(function (a) { 203 var g = _gravitaDi(a); 204 var c = null; 205 // senza gravita' NON si sceglie una corsia: si dichiara di non averla. Il difetto misurato 206 // il 21/08/2026 in browser: l'elenco unico normalizza la priorita' a «Bassa» quando manca 207 // il rischio residuo, e quel valore di ripiego finiva letto come gravita' bassa â quindi 208 // corsia RAPIDA, la meno rigorosa, su azioni di cui nessuno conosceva la gravita'. Un
209 // ripiego di una colonna non puo' diventare il criterio di un'altra. 210 if (g && AP && typeof AP.corsiaDi === 'function') { 211 try { c = AP.corsiaDi(g, { ricorrente: _n(a && a.riaperture) > 0 }); } catch (_eC) { c = null; } 212 } 213 if (c && out.corsie[c] !== undefined) out.corsie[c]++; else out.corsie.nonDeterminata++; 214 if (_haResp(a)) out.responsabilita.conResponsabile++; 215 if (_haScad(a)) out.responsabilita.conScadenza++; 216 if (_haResp(a) && _haScad(a)) out.responsabilita.formalizzate++; 217 if (_costo(a) > 0) { 218 out.responsabilita.onerose++; 219 var p = _poteriOk(a); 220 if (p === true) out.responsabilita.oneroseConPotere++; 221 else if (p === null) out.responsabilita.onerosePotereNonDichiarato++; 222 } 223 }); 224 225 // gli indicatori del programma â proprietario: _tecProgrammaMisura 226 var PM = win._tecProgrammaMisura; 227 if (PM && typeof PM.calcola === 'function') { 228 try { out.misura = PM.calcola(az, {}); } catch (_eM) { out.misura = null; } 229 } 230 if (out.misura) out.gerarchia = out.misura.gerarchia || null; 231 232 // ââ v20.373.139 â LA COPERTURA SI LEGGE DAL REGISTRO DEGLI OBBLIGHI ââââââââââââââââââââ 233 // Prima si chiedeva la copertura dei soli RILIEVI. Ma un rilievo non e' l'unico modo in cui 234 // nasce un obbligo: ci sono i rischi oltre soglia e gli adempimenti di legge, e quelli non 235 // comparivano nel denominatore. Risultato: «copertura 100%» era possibile con undici rischi 236 // in banda ALARP e tre registri estintori mai aperti. 237 // Ora il denominatore e' l'UNIVERSO DEGLI OBBLIGHI, e questo file smette di avere una propria 238 // idea di che cosa vada coperto: la chiede a chi la possiede. 239 var OB = win._tecAcamObblighi; 240 if (OB && typeof OB.obblighi === 'function') { 241 try { 242 var reg = OB.obblighi(d); 243 out.obblighi = reg; 244 out.copertura = { rilievi: reg.totale, coperte: reg.coperti, scoperte: reg.scoperti, 245 dovuti: reg.dovuti, dovutiScoperti: reg.dovutiScoperti }; 246 } catch (_eK) { out.obblighi = null; out.copertura = null; } 247 } else { 248 out.obblighi = null; 249 out.copertura = null; // dichiarato non misurabile: il proprietario degli obblighi non c'e' 250 } 251 252 // il blocco dell'art. 55 c.3 â proprietario: _tecAzioneProposta 253 if (AP && typeof AP.programmaGenerabile === 'function') { 254 try { out.generabile = AP.programmaGenerabile(az); } catch (_eG) { out.generabile = null; } 255 } 256 257 var m = out.misura, R = out.responsabilita; 258 var sopra = out.gerarchia ? (_n(out.gerarchia.totale) - _n(out.gerarchia.quotaBassa) - _n(out.gerarchia.senzaLivello)) : null; 259 260 out.kpi = [ 261 _kpi('copertura', out.copertura ? _pct(out.copertura.coperte, out.copertura.rilievi) : null, 262 out.copertura ? out.copertura.rilievi : 0, 263 out.copertura ? 'nessun obbligo di trattamento aperto' : 'il registro degli obblighi non è caricato'), 264 _kpi('formalizzazione', _pct(R.formalizzate, out.totale), out.totale, 'nessuna azione programmata'), 265 _kpi('poteri', R.onerose ? _pct(R.oneroseConPotere, R.onerose) : null, R.onerose, 266 R.onerose ? '' : 'nessuna azione dichiara un costo: il potere di spesa non è ancora in gioco'),
267 _kpi('causa', m ? (m.indicatori.causaRegistrata.su ? m.indicatori.causaRegistrata.valore : null) : null, 268 m ? m.totale : 0, 'nessuna azione su cui misurare'), 269 _kpi('indiceGerarchico', (out.gerarchia && out.gerarchia.totale) ? _pct(sopra, out.gerarchia.totale) : null, 270 out.gerarchia ? out.gerarchia.totale : 0, 'la gerarchia non è classificata su alcuna azione'), 271 _kpi('puntualita', (m && m.attuate) ? m.indicatori.puntualita.valore : null, m ? m.attuate : 0, 272 'nessuna azione è ancora stata attuata: la puntualità non ha base'), 273 _kpi('scadute', (m && m.totale) ? _pct(m.scadute, m.totale) : null, m ? m.totale : 0, 'nessuna azione programmata'), 274 _kpi('efficacia', (m && m.verificate) ? _pct(m.verificateEfficaci, m.verificate) : null, m ? m.verificate : 0, 275 'nessuna verifica di efficacia è ancora stata registrata'), 276 _kpi('ricorrenza', (m && m.attuate) ? _pct(m.conRiaperture, m.attuate) : null, m ? m.attuate : 0, 277 'nessuna azione chiusa: la ricorrenza non ha base') 278 ]; 279 280 out.nCentrati = out.kpi.filter(function (k) { return k.esito === 'centrato'; }).length; 281 out.nSotto = out.kpi.filter(function (k) { return k.esito === 'sotto'; }).length; 282 out.nNonMisurabili = out.kpi.filter(function (k) { return k.esito === 'non_misurabile'; }).length; 283 out.allarmi = allarmi(out); 284 out.nAllarmiGravi = out.allarmi.filter(function (a) { return a.gravita === 1; }).length; 285 return out; 286 } 287 288 /** 289 * Gli allarmi ACAM. Stessa forma e stessa porta di quelli del cruscotto rischi â il tecnico 290 * non deve imparare due linguaggi per due elenchi che gli chiedono la stessa cosa: vai li' e 291 * chiudila. Il colore lo possiede _tecDashboardRischi: qui NON si apre una seconda tavolozza. 292 */ 293 function allarmi(v) { 294 var out = []; 295 var D = win._tecDashboardRischi; 296 function colore(g) { 297 if (D && typeof D.coloreGravita === 'function') return D.coloreGravita(g); 298 return '#64748B'; // il proprietario del colore non c'e': grigio dichiarato, non un rosso inventato 299 } 300 function tabProgramma() { 301 return (D && D.PORTE && D.PORTE.programma) ? D.PORTE.programma.tab : null; 302 } 303 function A(g, t, p, n) { 304 var spec = { tab: tabProgramma(), campo: 'azioniMiglioramento', ancora: 'programma-azioni', 305 colore: colore(g), titolo: t, norma: n }; 306 out.push({ gravita: g, titolo: t, perche: p, norma: n, colore: spec.colore, 307 dove: 'Linguetta «11. Programma Miglioramento»', tab: spec.tab, 308 vai: spec.tab == null ? '' : ('window._tecDashboardRischi.apri(' + JSON.stringify(spec) + ')') }); 309 } 310 311 if (v.generabile && v.generabile.ok === false) { 312 _arr(v.generabile.motivi).forEach(function (mo) { 313 A(1, 'Il programma non è generabile', mo, v.generabile.norma || 'art. 55 c.3'); 314 }); 315 } 316 var senzaForm = _n(v.totale) - _n(v.responsabilita.formalizzate); 317 if (senzaForm > 0) { 318 A(1, senzaForm + ' azioni su ' + v.totale + ' senza responsabile o senza data', 319 'Lâart. 28 c.2 lett. c) chiede le misure «per il miglioramento nel tempo» e la lett. d) i ruoli che vi debbono ' 320 + 'provvedere. Un impegno senza nome e senza data non impegna nessuno, e la carenza è sanzionata dallâart. 55 c.3.', 321 'art. 28 c.2 lett. c e d · art. 55 c.3'); 322 } 323 if (v.responsabilita.onerosePotereNonDichiarato > 0) { 324 A(2, v.responsabilita.onerosePotereNonDichiarato + ' azioni che costano, senza potere di spesa dichiarato', 325 'Lâart. 28 c.2 lett. d) chiede soggetti «in possesso di adeguate competenze E POTERI». Un nome non basta: ' 326 + 'unâazione onerosa assegnata a chi non può spendere è formalmente viziata, ed è il requisito che nessuno controlla.', 327 'art. 28 c.2 lett. d'); 328 } 329 var ig = null; 330 v.kpi.forEach(function (k) { if (k.id === 'indiceGerarchico') ig = k; }); 331 if (ig && ig.esito === 'sotto') { 332 A(2, 'Indice gerarchico al ' + ig.valore + '%: le misure stanno in basso nella gerarchia', 333 'Meno di metà delle azioni agisce sopra il livello amministrativo. Il rischio viene gestito con il ' 334 + 'comportamento del lavoratore invece che rimosso: è la scelta che lâart. 15 c.1 lett. i) mette per ultima.', 335 'art. 15 c.1 lett. c), f) e i)'); 336 } 337 var eff = null, ric = null; 338 v.kpi.forEach(function (k) { if (k.id === 'efficacia') eff = k; if (k.id === 'ricorrenza') ric = k; }); 339 if (eff && eff.esito === 'non_misurabile' && _n(v.totale) > 0 && v.misura && _n(v.misura.attuate) > 0) { 340 A(1, v.misura.attuate + ' azioni risultano attuate, nessuna verificata efficace',
341 'La chiusura dichiarata non è una chiusura dimostrata. à esattamente il punto su cui la Cassazione misura ' 342 + 'la responsabilità del datore: non lâadozione della misura, la sua IDONEITÃ.', 343 'ISO 45001 §10.2 f) · Cass. pen. Sez. 4 n. 12122/2023'); 344 } 345 if (ric && ric.esito === 'sotto') { 346 A(2, 'Ricorrenza al ' + ric.valore + '%: azioni chiuse e riaperte', 347 'Ogni riapertura è la prova che la causa individuata non era quella. Si stanno chiudendo correzioni ' 348 + 'spacciandole per azioni correttive.', 'ISO 45001 §10.2 b) 2)'); 349 } 350 var ca = null; 351 v.kpi.forEach(function (k) { if (k.id === 'causa') ca = k; }); 352 if (ca && ca.esito === 'sotto') { 353 A(3, 'Causa registrata solo sul ' + ca.valore + '% delle azioni', 354 'Senza la causa, lâazione agisce sullâeffetto e il rilievo tornerà .', 'ISO 45001 §10.2 b)'); 355 } 356 return out.sort(function (a, b) { return a.gravita - b.gravita; }); 357 } 358 359 /** 360 * Le righe per il PACCHETTO. Stessa vista, seconda lettura: il documento non ricalcola nulla. 361 * @returns Array<[indicatore, valore, bersaglio, norma]> 362 */ 363 function righeDocumento(v) { 364 v = v || {}; 365 return _arr(v.kpi).map(function (k) { 366 return [ 367 k.label + ' â ' + k.definizione, 368 k.valore == null ? ('non misurabile: ' + k.nonMisurabilePerche) 369 : (k.valore + '%' + (k.base ? ' (base: ' + k.base + ')' : '')), 370 k.targetTesto, 371 k.norma 372 ]; 373 }); 374 } 375 376 /** Le azioni, pronte per la tabella dell'allegato: i cinque dati minimi dell'art. 28 c.2 c/d. */ 377 function righeAzioni(d) { 378 return azioniDi(d).map(function (a, i) { 379 var G = win._tecMisuraGerarchia; 380 var liv = ''; 381 var _urgTempo = (!_s(a.scadenza || a.dataPrevista || a.entro) && win._tecAzioniRilievi && win._tecAzioniRilievi.classificaUrgenza) 382 ? win._tecAzioniRilievi.classificaUrgenza(Number(a.rResiduoPre != null ? a.rResiduoPre : a.R)) : null; 383 if (G && typeof G.livelloEffettivo === 'function') { 384 try { liv = _s(G.livelloEffettivo(a).livello); } catch (_e) { liv = ''; } 385 } 386 var lm = null; 387 if (liv && G && typeof G.livello === 'function') { try { lm = G.livello(liv); } catch (_e2) { lm = null; } } 388 return { 389 codice: _s(a.codice) || ('AM-' + String(i + 1).padStart(2, '0')), 390 cosa: _s(a.descrizione || a.azione || a.misura), 391 nelTempo: _s(a.scadenza || a.dataPrevista || a.entro), 392 come: lm ? _s(lm.label) : (liv || ''), 393 comeRango: lm ? _n(lm.rango) : null, 394 chi: _s(a.responsabile || a.chi || (a.ownerOperativo && a.ownerOperativo.nome)), 395 potereDiSpesa: _poteriOk(a), 396 oneroso: _costo(a) > 0, 397 stato: _s(a.stato), 398 rischio: _s(a.rischioCod), norma: _s(a.norma), 399 causa: _s(a.causa && a.causa.testo), causaTipo: _s(a.causa && a.causa.tipo), 400 assegnazione: (win._tecAcamPersone ? win._tecAcamPersone.risolvi(_s(a.responsabile || a.chi || (a.ownerOperativo && a.ownerOperativo.nome)), d) : null), 401 avvisiAssegnazione: (win._tecAcamPersone ? win._tecAcamPersone.avvisi(a, d) : []), 402 nelTempoPropostoGiorni: _urgTempo ? _urgTempo.giorni : null, 403 nelTempoPropostoMotivo: _urgTempo ? _urgTempo.motivo : '', 404 efficaciaSulRischio: (a.efficaciaSulRischio || null), 405 efficaciaEsito: _s(a.verifica && a.verifica.esito) 406 }; 407 }); 408 } 409 410 function selfTest() { 411 var e = []; 412 if (KPI_DEF.length !== 9) e.push('gli indicatori dichiarati non sono nove'); 413 KPI_DEF.forEach(function (k) { 414 if (!_s(k.norma)) e.push('l indicatore ' + k.id + ' non porta la norma che lo fonda'); 415 if (!_s(k.perche)) e.push('l indicatore ' + k.id + ' non dice perche il bersaglio e quello'); 416 if (['su', 'giu'].indexOf(k.verso) < 0) e.push('l indicatore ' + k.id + ' non dichiara in che verso si legge'); 417 }); 418 // un indicatore senza base NON vale zero: vale «non lo so», e lo dice 419 var vuoto = _kpi('puntualita', null, 0, 'nessuna azione attuata'); 420 if (vuoto.valore !== null) e.push('un indicatore senza base produce un numero inventato'); 421 if (vuoto.esito !== 'non_misurabile') e.push('un indicatore senza base si pronuncia lo stesso'); 422 if (!_s(vuoto.nonMisurabilePerche)) e.push('un indicatore non misurabile non dice perche'); 423 // il verso conta: 10% di scadute e' un successo, 10% di efficacia e' un disastro 424 if (_kpi('scadute', 5, 10).esito !== 'centrato') e.push('un indicatore al ribasso viene letto al rialzo'); 425 if (_kpi('scadute', 40, 10).esito !== 'sotto') e.push('il 40% di azioni scadute risulta accettabile'); 426 if (_kpi('efficacia', 40, 10).esito !== 'sotto') e.push('un efficacia del 40% risulta accettabile'); 427 if (_kpi('efficacia', 95, 10).esito !== 'centrato') e.push('un efficacia del 95% risulta insufficiente'); 428 429 // il colore NON e' di questo file: se il proprietario c'e', si usa il suo 430 var D = win._tecDashboardRischi; 431 if (D && typeof D.coloreGravita === 'function') { 432 var vf = { totale: 4, responsabilita: { formalizzate: 0, onerose: 0, oneroseConPotere: 0, onerosePotereNonDichiarato: 0 }, 433 kpi: [], generabile: null, misura: null }; 434 var a = allarmi(vf); 435 if (!a.length) e.push('quattro azioni senza responsabile ne data non producono alcun allarme'); 436 if (a[0].colore !== D.coloreGravita(a[0].gravita)) e.push('il colore dell allarme ACAM non viene dal proprietario della scala'); 437 if (!_s(a[0].vai)) e.push('un allarme ACAM non porta la porta per andarci'); 438 if (!/art\. 55/.test(a[0].norma + a[0].perche)) e.push('l allarme sulla formalizzazione non cita la contravvenzione che la sanziona'); 439 } 440 // la gravita si legge dai fatti: la priorita di ripiego NON e un fatto 441 if (_gravitaDi({ priorita: 'Bassa' }) !== '') e.push('la priorita di ripiego viene letta come gravita: azioni ignote finirebbero in corsia rapida'); 442 if (_gravitaDi({ gravita: 'critica' }) !== 'critica') e.push('una gravita dichiarata non viene letta'); 443 if (_gravitaDi({ rResiduoPre: 12 }) !== 'critica') e.push('un rischio residuo 12 non produce gravita critica'); 444 if (_gravitaDi({ rResiduoPre: 2 }) !== 'bassa') e.push('un rischio residuo 2 non produce gravita bassa'); 445 if (_gravitaDi({}) !== '') e.push('un azione senza alcun segnale di gravita ne riceve una comunque'); 446 // il potere di spesa NON dichiarato non e' un no 447 if (_poteriOk({}) !== null) e.push('un potere di spesa non dichiarato viene letto come asse
447nte'); 448 if (_poteriOk({ ownerDecisore: { potereDiSpesa: false } }) !== false) e.push('un potere di spesa negato non viene letto'); 449 // le righe del documento devono dichiarare la base: una percentuale senza base non e verificabile 450 var v = vista({}); 451 var rd = righeDocumento(v); 452 if (rd.length !== 9) e.push('le righe per il documento non sono nove'); 453 if (!rd.every(function (r) { return _s(r[2]); })) e.push('una riga del documento non porta il proprio bersaglio'); 454 return { ok: e.length === 0, errors: e }; 455 } 456 457 /** 458 * ââ IL PIANO OPERATIVO (onda 6) â «CUI PRODEST?» ââââââââââââââââââââââââââââââââââââââââ 459 * 460 * Il committente, davanti all'allegato precedente: «non trovo alcuna altra tabella sulle ACAM 461 * se non quella. Non dice niente di concreto. Non capisco il fine di quel documento, a parte 462 * centinaia di parole e bla bla bla sulle norme. Va bene le norme, ma alla fine, se ho un 463 * documento che come consulente voglio leggere e magari condividere con il cliente, genero 464 * quel documento? Cui prodest?» 465 * 466 * Risposta: a nessuno. Quell'allegato era un'AUTOVALUTAZIONE DEL CONSULENTE travestita da 467 * deliverable per il cliente. I nove indicatori misurano quanto bene il consulente ha 468 * formalizzato il programma: un datore di lavoro che li legge impara solo che il suo 469 * consulente ha preso 4 su 9. Non gli serve, e al consulente fa danno. E c'era l'aggravante: 470 * nella v20.213 i KPI di controllo erano stati tolti dal DVR proprio perche' sono strumenti 471 * del consulente â decisione che avevo citato con approvazione e poi violato chiamando 472 * l'allegato «aggiuntivo». L'etichetta non cambia chi lo legge. 473 * 474 * ââ CHE COSA IL DVR NON DA', E CHE UN CONSULENTE VORREBBE CONSEGNARE âââââââââââââââââââ 475 * Il §8 del DVR e' un ATTO GIURIDICO: elenca le misure perche' l'art. 28 c.2 lett. c) lo 476 * impone. Non e' uno strumento di lavoro, e nessun datore di lavoro lo usa per lavorare. 477 * Quello che userebbe e' un piano operativo: che cosa devo fare, in che ordine, entro quando, 478 * chi ci mette la firma, che cosa scade nei prossimi 30-60-90 giorni. 479 * ORDINATO PER SCADENZA, non per articolo di legge. E' l'unica cosa che cambia davvero. 480 * 481 * ââ DUE PARTI, E LA PRIMA E' QUELLA CHE MANCA SEMPRE ââââââââââââââââââââââââââââââââââ 482 * 1. DA ASSEGNARE â gli obblighi dovuti che nessuna azione prende in carico. Nel documento 483 * del cliente non ci va la pagella; ci va la richiesta: queste cose aspettano da te un 484 * nome e una data. 485 * 2. SCADENZARIO â cio' che e' gia' assegnato, raggruppato per finestra temporale. 486 * 487 * La data si INIETTA (`opts.oggi`): un piano che dipende dall'orologio non e' riproducibile, 488 * e due letture dello stesso progetto darebbero due documenti diversi. 489 */ 490 var FINESTRE = [ 491 { id: 'scadute', label: 'Scadute', da: null, a: -1, urgenza: 1, 492 dice: 'La data è passata e lâintervento non risulta chiuso. Resta scritto nel documento, ed è la prima cosa che un controllo confronta con la data odierna.' }, 493 { id: 'entro30', label: 'Entro 30 giorni', da: 0, a: 30, urgenza: 2, 494 dice: 'Da chiudere adesso: è il mese in corso.' }, 495 { id: 'entro90', label: 'Entro 90 giorni', da: 31, a: 90, urgenza: 3, 496 dice: 'Il trimestre: va organizzato ora, non alla scadenza.' }, 497 { id: 'oltre', label: 'Oltre i 90 giorni', da: 91, a: 99999, urgenza: 4, 498 dice: 'Programmato più avanti: si riguarda al prossimo riesame.' }, 499 { id: 'senzaData', label: 'Senza data', da: null, a: null, urgenza: 9, 500 dice: 'Un intervento senza data non è programmato: lâart. 28 c.2 lett. c) chiede il miglioramento NEL TEMPO, e senza una data quel tempo non esiste.' } 501 ]; 502 function finestra(id) { 503 for (var i = 0; i < FINESTRE.length; i++) if (FINESTRE[i].id === id) return FINESTRE[i]; 504 return null; 505 } 506 507 function _data(v) { 508 var t = _s(v); 509 if (!t) return null; 510 var iso = t.match(/^(\d{4})-(\d{2})-(\d{2})/); 511 if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3]); 512 var ita = t.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{4})$/); 513 if (ita) return new Date(+ita[3], +ita[2] - 1, +ita[1]); 514 var d = new Date(t); 515 return isNaN(d.getTime()) ? null : d; 516 } 517 function _giorni(scad, oggi) { 518 if (!scad) return null; 519 return Math.round((scad.getTime() - oggi.getTime()) / 86400000); 520 } 521 function _finestraDi(gg, chiuso) { 522 if (gg == null) return 'senzaData';
523 if (gg < 0) return chiuso ? 'oltre' : 'scadute'; 524 if (gg <= 30) return 'entro30'; 525 if (gg <= 90) return 'entro90'; 526 return 'oltre'; 527 } 528 529 /** 530 * IL PIANO OPERATIVO. Non calcola: riordina cio' che il registro degli obblighi ha concluso. 531 */ 532 /** Un titolo che comincia in minuscolo, in una colonna di un documento consegnato, sembra un 533 * frammento incollato. Non si riscrive il testo: gli si da' la maiuscola iniziale. */ 534 function _titolo(t) { 535 var x = _s(t); 536 return x ? x.charAt(0).toUpperCase() + x.slice(1) : x; 537 } 538 539 function pianoOperativo(d, opts) { 540 /* v20.373.921 â ancora usato da generate-chunk Sez.VIII; non rimuovere finché il generatore non legge RegistroRilievi. */ 541 opts = opts || {}; 542 var oggi = opts.oggi || new Date(); 543 var OB = win._tecAcamObblighi; 544 if (!OB || typeof OB.obblighi !== 'function') { 545 return { disponibile: false, perche: 'il registro degli obblighi di trattamento non è caricato' }; 546 } 547 var reg; 548 try { reg = OB.obblighi(d); } catch (_e) { return { disponibile: false, perche: 'il registro degli obblighi non è calcolabile' }; } 549 if (!reg || !reg.disponibile) return { disponibile: false, perche: 'il registro degli obblighi non è disponibile' }; 550 551 var daAssegnare = [], voci = []; 552 _arr(reg.righe).forEach(function (o) { 553 var dovuto = (o.soglia && o.soglia.obbliga) || o.sorgente === 'obbligo_legge' || o.sorgente === 'non_conformita'; 554 if (!o.coperto) { 555 // ââ UNA COSA DA DECIDERE, UNA RIGA âââââââââââââââââââââââââââââââââââââââââââââââââ 556 // Lo stesso pericolo su due mansioni sono due obblighi distinti â nel REGISTRO devono 557 // restare due righe, ognuna col suo verdetto. In un PIANO no: chi deve decidere legge 558 // «Stress lavoro-correlato» quattro volte e conclude che il documento e' gonfiato. 559 // Si accorpa e si dice DOVE, esattamente come fa lo scadenzario piu' sotto. 560 var kA = _s(o.titolo) + '|' + _s(o.norma); 561 var giaA = null; 562 for (var ai = 0; ai < daAssegnare.length; ai++) if (daAssegnare[ai]._chiave === kA) { giaA = daAssegnare[ai]; break; } 563 var dv = _s(o.entitaTipo) + ' · ' + _s(o.entitaNome); 564 if (giaA) { 565 if (giaA.dovePiu.indexOf(dv) < 0) giaA.dovePiu.push(dv); 566 if (dovuto) giaA.dovuto = true; 567 if (o.soglia && _n(o.soglia.rango) < giaA.urgenza) { 568 giaA.urgenza = _n(o.soglia.rango); 569 giaA.entroSuggerito = _s(o.soglia.termine); 570 giaA.chiAccetta = _s(o.soglia.accetta); 571 } 572 return; 573 } 574 daAssegnare.push({ 575 _chiave: kA, 576 cosa: _titolo(o.titolo), dove: dv, dovePiu: [dv], 577 // la banda di rischio puo' mancare (un rilievo non ha P x D), il TERMINE no: sono due 578 // fatti diversi, e tenerli legati stampava «da definire» sulle righe piu' urgenti. 579 entroSuggerito: o.soglia ? _s(o.soglia.termine) : (_s(o.termineDovuto) || 'da definire'), 580 chiAccetta: o.soglia ? _s(o.soglia.accetta) : '', 581 norma: _s(o.norma), dovuto: dovuto, urgenza: o.soglia ? _n(o.soglia.rango) : 3 582 }); 583 return; 584 }
585 _arr(o.azioni).forEach(function (a) { 586 var scad = _data(a.scadenza || a.dataPrevista || a.entro); 587 var StF = win.TecAcamStato; 588 var chiuso = StF ? (StF.fatta({ stato: o.stato }) || StF.chiusa({ stato: o.stato })) : false; 589 var gg = _giorni(scad, oggi); 590 var fid = _finestraDi(gg, chiuso); 591 // ââ UNA COSA DA FARE, UNA RIGA ââââââââââââââââââââââââââââââââââââââââââââââââââââ 592 // La stessa misura puo' coprire piu' obblighi (due mansioni esposte allo stesso rischio). 593 // Nel REGISTRO e' giusto che compaia due volte â sono due obblighi distinti, e ognuno ha 594 // il suo verdetto. In un PIANO OPERATIVO no: chi deve eseguire non vuole leggere due 595 // volte lo stesso intervento. Si accorpa, e si dice a quanti obblighi risponde. 596 var chiave = _s(a.codice) + '|' + _s(a.descrizione || a.azione || a.misura) + '|' + _s(a.scadenza); 597 var gia = null; 598 for (var vi = 0; vi < voci.length; vi++) if (voci[vi]._chiave === chiave) { gia = voci[vi]; break; } 599 if (gia) { 600 gia.obblighi.push(_s(o.titolo)); 601 gia.dovePiu.push(_s(o.entitaTipo) + ' · ' + _s(o.entitaNome)); 602 if (dovuto) gia.dovuto = true; 603 if (o.efficaciaSulRischio && o.efficaciaSulRischio.esito === 'non_sceso') gia.esitoRischio = o.efficaciaSulRischio; 604 return; 605 } 606 voci.push({ 607 _chiave: chiave, 608 cosa: _titolo(a.descrizione || a.azione || a.misura), 609 dove: _s(o.entitaTipo) + ' · ' + _s(o.entitaNome), 610 dovePiu: [_s(o.entitaTipo) + ' · ' + _s(o.entitaNome)], 611 obbligo: _s(o.titolo), obblighi: [_s(o.titolo)], 612 chi: (o.assegnazione && o.assegnazione.persona) ? (o.assegnazione.persona.nome + ' (' + o.assegnazione.persona.ruolo + ')') 613 : (_s(a.responsabile) || ''), 614 chiRisolto: !!(o.assegnazione && o.assegnazione.esito === 'trovata'), 615 entro: _s(a.scadenza || a.dataPrevista || a.entro), 616 giorni: gg, finestra: fid, 617 stato: o.stato, dovuto: dovuto, norma: _s(a.norma) || _s(o.norma), 618 prova: _s(a.verifica && a.verifica.evidenza) || _s(a.verificaEfficacia) || '', 619 esitoRischio: o.efficaciaSulRischio || null, 620 avvisi: _arr(o.avvisiAssegnazione) 621 }); 622 }); 623 }); 624 625 var gruppi = FINESTRE.map(function (f) { 626 return { finestra: f, voci: voci.filter(function (v) { return v.finestra === f.id; }) }; 627 }).filter(function (g) { return g.voci.length; }); 628 gruppi.sort(function (a, b) { return a.finestra.urgenza - b.finestra.urgenza; }); 629 630 daAssegnare.sort(function (a, b) { return (a.dovuto === b.dovuto ? 0 : (a.dovuto ? -1 : 1)) || a.urgenza - b.urgenza; }); 631 632 var n = function (id) { var g = gruppi.filter(function (x) { return x.finestra.id === id; })[0]; return g ? g.voci.length : 0; }; 633 return { 634 disponibile: true, 635 norma: 'art. 28 c.2 lett. c e d D.Lgs. 81/08', 636 oggi: oggi, 637 daAssegnare: daAssegnare, nDaAssegnare: daAssegnare.length, 638 nDaAssegnareDovuti: daAssegnare.filter(function (x) { return x.dovuto; }).length, 639 gruppi: gruppi, voci: voci, totale: voci.length, 640 scadute: n('scadute'), entro30: n('entro30'), entro90: n('entro90'), 641 oltre: n('oltre'), senzaData: n('senzaData'), 642 senzaChi: voci.filter(function (v) { return !_s(v.chi); }).length, 643 chiNonRisolto: voci.filter(function (v) { return _s(v.chi) && !v.chiRisolto; }).length, 644 // un piano vuoto non e' un piano: si dichiara invece di stampare una copertina 645 vuoto: (voci.length + daAssegnare.length) === 0 646 }; 647 } 648 649 win._tecAcamPiano = { 650 VERSION: VERSION, KPI_DEF: KPI_DEF, FINESTRE: FINESTRE, finestra: finestra, 651 azioniDi: azioniDi, vista: vista, allarmi: allarmi, pianoOperativo: pianoOperativo, 652 righeDocumento: righeDocumento, righeAzioni: righeAzioni, selfTest: selfTest 653 }; 654})(typeof window !== 'undefined' ? window : this); 655 656/** 657 * IL DISEGNO DEL PIANO ACAM, PER IL TECNICO. 658 * Nove indicatori, ciascuno con il proprio bersaglio ACCANTO al valore: un numero senza bersaglio 659 * non dice se va bene, e un cruscotto che non dice se va bene e' decorazione. Chi non ha base lo 660 * dichiara in grigio invece di mostrare uno zero che sembra un risultato. 661 */ 662(function (win) { 663 'use strict'; 664 var A = win._tecAcamPiano; 665 if (!A) return; 666 function _e(s) { 667 return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { 668 return ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]; 669 }); 670 } 671 var NAVY = '#1e3a5f', GRIGIO = '#64748b', BORDO = '#e2e8f0'; 672 // helper locali del disegno: il primo IIFE non li esporta, e usarne uno che qui non esiste 673 // produce un ReferenceError ingoiato dal try/catch del chiamante â il difetto della .134. 674 function _arr(v) { return Array.isArray(v) ? v.filter(Boolean) : []; } 675 // La meta' che DISEGNA non aveva un normalizzatore di stringhe: quella che calcola si', ma 676 // vivono in due IIFE diverse e non si vedono. Presumerlo era il difetto della .134 â una 677 // funzione muta che nessuno sente tacere, finche' non esplode su una riga qualunque. 678 function _s(v) { return String(v == null ? '' : v).trim(); } 679 // stesso motivo: la meta' che disegna non aveva nemmeno un normalizzatore di numeri. 680 function _num(v) { var x = Number(v); return isNaN(x) ? 0 : x; } 681 var ESITO = { 682 centrato: { colore: '#16a34a', sfondo: '#f0fdf4', bordo: '#bbf7d0', eti: 'bersaglio centrato' }, 683 sotto: { colore: '#dc2626', sfondo: '#fff7f7', bordo: '#fecaca', eti: 'sotto il bersaglio' }, 684 non_misurabile: { colore: '#94a3b8', sfondo: '#f8fafc', bordo: '#e2e8f0', eti: 'non misurabile' } 685 }; 686 687 function _cardKpi(k) { 688 var s = ESITO[k.esito] || ESITO.non_misurabile;
689 return '<div data-acam-kpi="1" style="border:1px solid ' + s.bordo + ';background:' + s.sfondo + ';border-radius:12px;padding:12px 14px">' 690 + '<div style="display:flex;align-items:baseline;gap:8px">' 691 + '<span style="font-size:26px;font-weight:900;line-height:1;color:' + s.colore + '">' 692 + (k.valore == null ? 'â' : k.valore + '%') + '</span>' 693 + '<span style="font-size:10px;font-weight:800;color:' + GRIGIO + '">bersaglio ' + _e(k.targetTesto) + '</span>' 694 + (k.proposta ? '<span title="Metrica proposta da noi, derivata dalle norme: non è una prassi di mercato verificata" style="margin-left:auto;font-size:8.5px;font-weight:900;text-transform:uppercase;letter-spacing:.05em;color:#7c3aed;background:#f5f3ff;border:1px solid #ddd6fe;border-radius:4px;padding:1px 5px">proposta</span>' : '') 695 + '</div>' 696 + '<div style="font-size:12.5px;font-weight:800;color:' + NAVY + ';margin-top:6px">' + _e(k.label) + '</div>' 697 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:2px;line-height:1.4">' + _e(k.definizione) 698 + (k.base ? ' · base ' + k.base : '') + '</div>' 699 + (k.valore == null 700 ? '<div style="font-size:10px;color:#94a3b8;font-style:italic;margin-top:5px">' + _e(k.nonMisurabilePerche) + '</div>' 701 : (k.esito === 'sotto' 702 ? '<div style="font-size:10.5px;color:#991b1b;margin-top:5px;line-height:1.45">' + _e(k.perche) + '</div>' 703 : '')) 704 + '<div style="font-size:9.5px;color:#94a3b8;margin-top:6px;padding-top:5px;border-top:1px solid ' + s.bordo + '">' 705 + _e(k.norma) + '</div></div>'; 706 } 707 708 function htmlAllarmi(v) { 709 if (!v.allarmi.length) {
710 return '<div data-acam-blocco="carenze" style="border:1px solid #bbf7d0;background:#f0fdf4;border-radius:14px;padding:15px 17px">' 711 + '<div style="font-size:13px;font-weight:800;color:#166534">✓ Nessuna carenza ACAM aperta</div>' 712 + '<div style="font-size:11.5px;color:#166534;margin-top:3px">Ogni azione ha un nome, una data, un gradino della gerarchia e una prova di efficacia.</div></div>'; 713 } 714 var ETI = { 1: 'Dovuto', 2: 'Da chiudere', 3: 'Qualità ' }; 715 var h = '<div data-acam-blocco="carenze" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;overflow:hidden">' 716 + '<div style="padding:13px 16px;border-top:3px solid ' + (v.allarmi[0].colore || '#dc2626') + ';border-bottom:1px solid ' + BORDO + '">' 717 + '<div style="font-size:14.5px;font-weight:800;color:' + NAVY + '">Che cosa manca al programma per reggere un controllo</div>' 718 + '<div style="font-size:11px;color:' + GRIGIO + ';font-style:italic;margin-top:2px">' 719 + v.allarmi.length + ' voci · ' + v.nAllarmiGravi + ' dovute per legge</div></div>'; 720 v.allarmi.forEach(function (a) { 721 h += '<div style="padding:11px 16px;border-top:1px solid #f1f5f9">' 722 + '<div style="display:flex;gap:9px;align-items:baseline;flex-wrap:wrap">' 723 + '<span style="font-size:8.5px;font-weight:900;text-transform:uppercase;letter-spacing:.06em;color:#fff;background:' 724 + (a.colore || '#64748b') + ';border-radius:4px;padding:2px 7px">' + (ETI[a.gravita] || '') + '</span>' 725 + '<span style="font-size:12.5px;font-weight:800;color:' + NAVY + '">' + _e(a.titolo) + '</span></div>' 726 + '<div style="font-size:11px;color:#475569;line-height:1.5;margin-top:4px">' + _e(a.perche) + '</div>' 727 + '<div style="display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:5px">' 728 + '<div style="font-size:10px;color:' + GRIGIO + '"><b>' + _e(a.norma) + '</b> · ' + _e(a.dove) + '</div>' 729 + (a.vai 730 ? ('<button type="button" onclick="' + _e(a.vai) + '" style="margin-left:auto;cursor:pointer;border:1.5px solid ' 731 + (a.colore || '#64748b') + ';background:#fff;color:' + (a.colore || '#64748b') 732 + ';border-radius:7px;font-size:10.5px;font-weight:800;padding:4px 11px;font-family:inherit">Vai →</button>') 733 : '') 734 + '</div></div>'; 735 }); 736 return h + '</div>'; 737 } 738 739 /** Le corsie: il rigore e' proporzionato alla gravita', e si vede. */ 740 function htmlCorsie(v) { 741 var c = v.corsie, tot = v.totale || 0; 742 if (!tot) return ''; 743 var voci = [ 744 { id: 'rapida', label: 'Rapida', n: c.rapida, colore: '#16a34a', chiede: 'azione · chi · entro quando' }, 745 { id: 'standard', label: 'Standard', n: c.standard, colore: '#ca8a04', chiede: '+ causa · gradino della gerarchia · verifica di efficacia' }, 746 { id: 'estesa', label: 'Estesa', n: c.estesa, colore: '#dc2626', chiede: '+ metodo RCA · escape point · propagazione · rischio introdotto' } 747 ];
748 var h = '<div data-acam-blocco="corsie" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;padding:14px 16px">' 749 + '<div style="font-size:13.5px;font-weight:800;color:' + NAVY + '">Il rigore è proporzionato alla gravità </div>' 750 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:2px;line-height:1.45">' 751 + 'Imporre lâindagine completa su una segnaletica mancante distrugge lâadozione; permettere un «5 perché» ' 752 + 'frettoloso su un quasi-infortunio grave distrugge la difendibilità . La corsia si sceglie da sola: puoi alzarla, ' 753 + 'e abbassarla richiede una motivazione che resta agli atti.</div>' 754 + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-top:11px">'; 755 voci.forEach(function (x) { 756 h += '<div style="border-left:4px solid ' + x.colore + ';background:#fbfcfe;border-radius:8px;padding:9px 12px">' 757 + '<div style="font-size:20px;font-weight:900;color:' + x.colore + ';line-height:1">' + (x.n || 0) + '</div>' 758 + '<div style="font-size:11.5px;font-weight:800;color:' + NAVY + ';margin-top:2px">Corsia ' + x.label + '</div>' 759 + '<div style="font-size:9.5px;color:' + GRIGIO + ';margin-top:2px;line-height:1.4">' + _e(x.chiede) + '</div></div>'; 760 }); 761 if (c.nonDeterminata) { 762 h += '<div style="border-left:4px solid #94a3b8;background:#f8fafc;border-radius:8px;padding:9px 12px">' 763 + '<div style="font-size:20px;font-weight:900;color:#94a3b8;line-height:1">' + c.nonDeterminata + '</div>' 764 + '<div style="font-size:11.5px;font-weight:800;color:' + NAVY + ';margin-top:2px">Corsia non determinata</div>' 765 + '<div style="font-size:9.5px;color:' + GRIGIO + ';margin-top:2px;line-height:1.4">Manca la gravità : il rigore non può essere scelto</div></div>'; 766 } 767 return h + '</div></div>'; 768 } 769 770 /** I cinque dati minimi dell'art. 28 c.2 lett. c e d, uno per colonna. */ 771 // v20.373.231 â COLONNA CAUSA (Root Cause). Il dato (causa/tipo) esisteva gia' in righeAzioni 772 // ma la tabella non lo mostrava. Senza causa, l'azione agisce sull'effetto: e' una correzione 773 // travestita (ISO 45001 10.2 b). La colonna la NOMINA; il tecnico la compila (Virgilio propone). 774 function _causaCell(r) { 775 var ok = !!(r && r.causa); 776 var tipo = r && r.causaTipo; 777 var chip = tipo ? ('<span style="font-size:8px;font-weight:900;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:' 778 + (tipo === 'sistemica' ? '#166534' : '#b45309') + ';border-radius:4px;padding:1px 5px;margin-left:5px">' + _e(tipo) + '</span>') : ''; 779 return '<td data-acam-causa="' + (ok ? '1' : '0') + '" style="padding:7px 10px;line-height:1.4;color:' 780 + (ok ? '#1e293b' : '#b45309') + '">' 781 + (ok ? (_e(r.causa) + chip) 782 : '<span style="font-weight:700">— senza causa</span>' 783 + '<div style="font-size:9px;margin-top:1px">agisce sull'effetto: è una correzione travestita</div>') 784 + '</td>'; 785 } 786 787 // v20.373.231 â TEMPISTICA PROPOSTA. Se la scadenza manca, il tool la propone dalla gravita' 788 // (classificaUrgenza, SSOT in tec-azioni-rilievi-sync); il tecnico conferma o corregge. 789 function _tempoCell(r) { 790 if (r && r.nelTempo) return '<td style="padding:7px 10px;white-space:nowrap;color:#1e293b">' + _e(r.nelTempo) + '</td>'; 791 if (r && r.nelTempoPropostoGiorni) { 792 var _d = new Date(); _d.setDate(_d.getDate() + (parseInt(r.nelTempoPropostoGiorni, 10) || 180)); 793 var _iso = String(_d.getDate()).padStart(2, '0') + '/' + String(_d.getMonth() + 1).padStart(2, '0') + '/' + _d.getFullYear(); 794 return '<td data-acam-tempo-proposto="1" style="padding:7px 10px;white-space:nowrap;line-height:1.35">' 795 + '<span style="font-size:8px;font-weight:900;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:#7c3aed;border-radius:4px;padding:1px 5px">proposta</span> ' 796 + '<span style="color:#7c3aed;font-weight:700">' + _iso + '</span>' 797 + '<div style="font-size:9px;color:#94a3b8;margin-top:1px">
797' + _e(r.nelTempoPropostoMotivo || '') + ' \u2014 il tecnico conferma o corregge</div></td>'; 798 } 799 return '<td style="padding:7px 10px;white-space:nowrap;color:#b45309">\u2014 da indicare</td>'; 800 } 801 802 // v20.373.231 â COLONNA STATO + EFFICACIA nella tabella azioni. Lo stato dell'azione e, se 803 // e' chiusa ma il rischio non e' sceso, il segnale di REINDIRIZZAMENTO (via _efficacia). 804 function _statoCell(r) { 805 var St = win.TecAcamStato; 806 var st = (St && St.statoDi) ? St.statoDi(r) : ((r && r.stato) || 'Proposta'); 807 var col = (St && St.meta) ? St.meta(st).col : '#475569'; 808 return '<td data-acam-stato="1" style="padding:7px 10px;white-space:nowrap;line-height:1.35">' 809 + '<span style="font-weight:800;color:' + col + '">' + _e(st) + '</span>' 810 + _efficacia(r) + '</td>'; 811 } 812 813 function htmlAzioni(d, opts) { 814 opts = opts || {}; 815 var righe = opts.righe || A.righeAzioni(d); 816 if (!righe.length) { 817 return '<div data-acam-blocco="azioni" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;padding:15px 17px">' 818 + '<div style="font-size:13.5px;font-weight:800;color:' + NAVY + '">Nessuna azione nel programma</div>' 819 + '<div style="font-size:11.5px;color:' + GRIGIO + ';margin-top:3px">Non è di per sé un difetto: può non essercene bisogno. ' 820 + 'Lo diventa quando ci sono rilievi aperti che nessuna misura chiude.</div></div>'; 821 }
822 var h = '<div data-acam-blocco="azioni" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;overflow:hidden">' 823 + '<div style="padding:13px 16px;border-top:3px solid #4338ca;border-bottom:1px solid ' + BORDO + '">' 824 + '<div style="font-size:9.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:' + GRIGIO + '">' 825 + 'art. 28 c.2 lett. c e d D.Lgs. 81/08</div>' 826 + '<div style="font-size:14.5px;font-weight:800;color:' + NAVY + ';margin-top:3px">Le azioni, con i cinque dati che la legge chiede</div>' 827 + '<div style="font-size:11px;color:' + GRIGIO + ';margin-top:2px">Cosa · Nel tempo · Come (gradino della gerarchia) · Chi · Con quale idoneità (competenze <b>e poteri</b>)</div></div>' 828 + '<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:11px">' 829 + '<tr style="background:#f8fafc">' 830 + ['', 'Cosa', 'Causa', 'Nel tempo', 'Come', 'Chi', 'Poteri', 'Stato'].map(function (t) { 831 return '<th style="text-align:left;padding:7px 10px;font-size:9px;font-weight:900;text-transform:uppercase;letter-spacing:.06em;color:' + GRIGIO + '">' + t + '</th>'; 832 }).join('') + '</tr>'; 833 righe.forEach(function (r) { 834 var manca = !r.nelTempo || !r.chi; 835 var pot = r.potereDiSpesa === true ? '<span style="color:#16a34a;font-weight:800">sì</span>' 836 : r.potereDiSpesa === false ? '<span style="color:#dc2626;font-weight:800">no</span>' 837 : (r.oneroso ? '<span style="color:#b45309;font-weight:800">non dichiarato</span>' 838 : '<span style="color:#cbd5e1">â</span>'); 839 var _res = r.assegnazione; 840 var chiHtml = (_res && _res.esito === 'trovata') 841 ? _assegnazione(r) 842 : ('<span style="color:' + (r.chi ? '#1e293b' : '#b45309') + '">' + _e(r.chi || '\u2014 da indicare') + '</span>' + _assegnazione(r)); 843 h += '<tr style="border-top:1px solid #f1f5f9' + (manca ? ';background:#fffbeb' : '') + '">' 844 + '<td style="padding:7px 10px;font-weight:900;color:' + GRIGIO + ';white-space:nowrap">' + _e(r.codice) + '</td>' 845 + '<td style="padding:7px 10px;color:#1e293b;line-height:1.4">' + _e(r.cosa) + '</td>' 846 + _causaCell(r) 847 + _tempoCell(r) 848 + '<td style="padding:7px 10px;color:' + (r.comeRango && r.comeRango >= 4 ? '#b45309' : '#1e293b') + '">' 849 + _e(r.come || 'â non classificato') + '</td>' 850 + '<td style="padding:7px 10px">' + chiHtml + '</td>' 851 + '<td style="padding:7px 10px;white-space:nowrap">' + pot + '</td>' 852 + _statoCell(r) + '</tr>'; 853 }); 854 return h + '</table></div></div>'; 855 } 856 857 /** 858 * IL REGISTRO DEGLI OBBLIGHI, A SCHERMO. E' la cosa che va vista per prima: prima di sapere 859 * quanto bene stiamo lavorando (gli indicatori), il tecnico deve sapere CHE COSA C'E' DA FARE. 860 * Prima questa tabella non esisteva perche' non esisteva l'elenco: l'ACAM partiva dalle azioni. 861 */ 862 /** 863 * ââ v20.373.140 (onda 3) â CHI CE L'HA IN CARICO, E SE QUELLA PERSONA ESISTE ââââââââââââ 864 * Il responsabile era una stringa e nessuno diceva se corrispondesse a qualcuno. Qui la 865 * colonna dice tre cose diverse con tre colori diversi: assegnata a una persona vera · 866 * assegnata a un nome che l'azienda non conosce · ambigua fra piu' persone. E se l'azione 867 * costa e chi la porta non puo' spendere, lo scrive: e' la lett. d) resa verificabile. 868 */ 869 function _assegnazione(o) { 870 var a = o && o.assegnazione; 871 var avv = _arr(o && o.avvisiAssegnazione); 872 if (!a && !avv.length) return ''; 873 var h = ''; 874 if (a && a.esito === 'trovata' && a.persona) { 875 h += '<div style="font-size:10px;color:#166534;margin-top:3px">✓ in carico a <b>' + _e(a.persona.nome) 876 + '</b> <span style="color:#94a3b8">(' + _e(a.persona.ruolo) + ')</span></div>'; 877 } else if (a && a.esito === 'non_censita') { 878 h += '<div style="font-size:10px;color:#991b1b;margin-top:3px">⚠ «' + _e(a.testo) 879 + '» non risulta in azienda</div>'; 880 } else if (a && a.esito === 'ambigua') { 881 h += '<div style="font-size:10px;color:#b45309;margin-top:3px">⚠
881 «' + _e(a.testo) 882 + '» corrisponde a più persone: scegli tu</div>'; 883 } 884 avv.filter(function (x) { return /potere/.test(x.id); }).slice(0, 1).forEach(function (x) { 885 h += '<div style="font-size:9.5px;color:#9a3412;margin-top:2px;line-height:1.4">⚠ ' 886 + _e(String(x.testo).split('.')[0]) + '.</div>'; 887 }); 888 avv.filter(function (x) { return /^ruolo_/.test(x.id); }).slice(0, 1).forEach(function (x) { 889 var lbl = x.id === 'ruolo_rspp' ? 'ruolo errato: l\u2019RSPP propone, non attua (artt. 32-33)' 890 : x.id === 'ruolo_rls' ? 'ruolo errato: l\u2019RLS \u00e8 consultato, non responsabile (artt. 47-50)' : 'ruolo errato'; 891 h += '<div data-acam-ruolo-errore="1" style="font-size:9.5px;color:#991b1b;font-weight:700;margin-top:2px;line-height:1.4">⚠ ' + lbl + '</div>'; 892 }); 893 return h; 894 } 895 896 /** 897 * ââ v20.373.141 (onda 5) â «NON POSSO AVERE DISALLINEAMENTO TRA AZIONI CHIUSE E RISCHI» ââ 898 * Qui si vede. Un'azione chiusa con esito efficace su un rischio rimasto dov'era non e' un 899 * successo: e' la cosa piu' importante che questa tabella possa dire, e la dice in rosso 900 * accanto allo stato «Chiuso» â perche' e' esattamente li' che il disallineamento si annida. 901 */ 902 function _efficacia(o) { 903 var f = o && o.efficaciaSulRischio; 904 if (!f || !f.esito || f.esito === 'non_pertinente' || f.esito === 'nessuna_chiusa' || f.esito === 'raggiunto') return ''; 905 var COL = { non_sceso: '#dc2626', sceso_parzialmente: '#b45309', atteso_non_dichiarato: '#7c3aed' }; 906 var ETI = { non_sceso: 'chiusa, rischio fermo', sceso_parzialmente: 'chiusa, atteso non raggiunto', 907 atteso_non_dichiarato: 'chiusa senza un atteso' }; 908 return '<div style="font-size:9.5px;font-weight:800;color:' + (COL[f.esito] || '#b45309') + ';margin-top:3px">' 909 + '⚠ ' + _e(ETI[f.esito] || f.esito) 910 + (f.ora != null && f.atteso != null ? (' <span style="font-weight:600">(' + f.ora + ' contro ' + f.atteso + ' atteso)</span>') : '') 911 + '</div>'; 912 } 913 914 /** 915 * ââ CHE COSA FARE, E DOVE STA GIA' SCRITTO ââââââââââââââââââââââââââââââââââââââââââââââââ 916 * 917 * Tre casi, e nessuno lascia la casella vuota: 918 * · COPERTO â l'obbligo ha gia' una o piu' ACAM. Si NOMINANO, con il loro stato: e' il 919 * legame che il committente ha chiesto («se quel che deve accadere e' una ACAM, perche' 920 * non mi leghi quel dovuto a quella ACAM corrispondente?»). Erano nella riga, in `azioni`, 921 * e questa tabella non le stampava. 922 * · SCOPERTO CON PROPOSTA â il catalogo sa che cosa serve: si mostra, marcata come PROPOSTA 923 * (non e' ancora una decisione di nessuno) e con il pulsante per portarla nel programma. 924 * · SCOPERTO SENZA PROPOSTA â il catalogo non propone nulla per questo pericolo. Si DICE. 925 * Una casella vuota accanto a «dovuto» lascia il tecnico a indovinare, ed e' esattamente 926 * cio' di cui si e' lamentato. 927 */ 928 function _cosaFare(o) { 929 var az = _arr(o.azioni); 930 if (az.length) { 931 return az.map(function (a) { 932 var t = _s(a.descrizione || a.azione || a.misura) || '(azione senza testo)'; 933 var st = _s(a.stato) || 'aperta'; 934 var chiusa = /^(chiusa|chiuso|attuata|efficace|assolta|assolto)$/i.test(st); 935 return '<div style="margin-bottom:3px">' 936 + '<span style="font-size:8.5px;font-weight:900;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:' 937 + (chiusa ? '#16a34a' : '#2563eb') + ';border-radius:4px;padding:1px 5px" data-acam-copre="1">ACAM</span> ' 938 + '<span style="color:#1e293b">' + _e(t) + '</span>' 939 + '<span style="font-size:9.5px;color:' + GRIGIO + '"> â ' + _e(st) + '</span></div>'; 940 }).join(''); 941 } 942 var prop = _s(o.azioneProposta); 943 if (prop) { 944 return '<div><span style="font-size:8.5px;font-weight:900;text-transform:uppercase;letter-spacing:.04em;color:#fff;' 945 + 'background:#7c3aed;border-radius:4px;
945padding:1px 5px" data-acam-copre="proposta">Proposta</span> ' 946 + '<span style="color:#1e293b">' + _e(prop) + '</span>' 947 + (_s(o.azioneNorma) ? ('<div style="font-size:9px;color:#94a3b8;margin-top:1px">' + _e(o.azioneNorma) + '</div>') : '') 948 + '<div style="font-size:9.5px;color:' + GRIGIO + ';margin-top:2px">Non è ancora una misura del programma: ' 949 + 'va accolta, modificata o rifiutata.</div></div>'; 950 } 951 return '<div data-acam-copre="0" style="color:#b45309;font-size:10.5px;line-height:1.45">Il catalogo non propone una misura ' 952 + 'per questo caso: la misura va scritta dal tecnico. Finché manca, lâobbligo resta scoperto.</div>'; 953 } 954 955 /** 956 * LA PORTA. «Non mi porti nel campo in cui devo fare qualcosa.» Non se ne inventa una nuova: 957 * si usa quella che il prodotto ha gia' â lo Stato dei luoghi per i luoghi, la linguetta 958 * proprietaria per il resto â e quando per quell'entita' una porta non esiste, lo si DICE 959 * invece di offrire un pulsante che non porta da nessuna parte. 960 */ 961 function _portaEntita(o) { 962 var t = _s(o.entitaTipo); 963 var atto = ''; 964 if (t === 'luogo' && _s(o.luogoId)) { 965 atto = 'window._tecStatoLuoghi && window._tecStatoLuoghi.apri(' + JSON.stringify(_s(o.luogoId)) 966 + ',\'\',' + JSON.stringify(_s(o.punto)) + ')'; 967 } else if (t === 'mansione') { 968 atto = 'window._tecDashboardRischi && window._tecDashboardRischi.apri({tab:20,titolo:' 969 + JSON.stringify(_s(o.entitaNome)) + ',norma:' + JSON.stringify(_s(o.norma)) + '})'; 970 } else if (t === 'attrezzo') { 971 atto = 'window._tecDashboardRischi && window._tecDashboardRischi.apri({tab:19,titolo:' 972 + JSON.stringify(_s(o.entitaNome)) + ',norma:' + JSON.stringify(_s(o.norma)) + '})'; 973 } else if (t === 'ditta') { 974 atto = 'window._tecDashboardRischi && window._tecDashboardRischi.apri({tab:15,titolo:' 975 + JSON.stringify(_s(o.entitaNome)) + ',norma:' + JSON.stringify(_s(o.norma)) + '})'; 976 } 977 if (!atto) {
978 return '<div data-acam-porta="0" style="font-size:9.5px;color:#94a3b8;font-style:italic;margin-top:3px">' 979 + 'nessuna schermata propria</div>'; 980 } 981 return '<div style="margin-top:4px"><button type="button" data-acam-porta="1" onclick="' + _e(atto) + '" ' 982 + 'title="Apri la schermata dove si lavora su questa voce" ' 983 + 'style="cursor:pointer;border:1px solid #cbd5e1;background:#fff;color:#1d4ed8;border-radius:6px;' 984 + 'font-size:10px;font-weight:800;padding:2px 8px;font-family:inherit">Vai →</button></div>'; 985 } 986 987 988 // v20.373.284 â portata a livello di modulo: la usano sia il Registro sia la vista «Il giro». 989 // Una funzione annidata dentro una vista non e' raggiungibile dalle altre, e duplicarla 990 // avrebbe prodotto due pulsanti che col tempo fanno cose diverse. 991 /** 992 * LA PROPOSTA DI DEFAULT DEL CLUSTER. Regola d'oro, la stessa dei Luoghi: il pulsante 993 * dichiara che cosa fa, SU QUANTE righe, e che cosa NON accade se non lo premi. E quando 994 * non c'e' nulla da proporre, non si nasconde: si dice perche'. 995 */ 996 function _propostaCluster(lista, nomeCluster) { 997 lista = _arr(lista); 998 var conProposta = lista.filter(function (o) { return !_arr(o.azioni).length && _s(o.azioneProposta); }); 999 var senzaNulla = lista.filter(function (o) { return !_arr(o.azioni).length && !_s(o.azioneProposta); }); 1000 var coperti = lista.filter(function (o) { return _arr(o.azioni).length; }); 1001 var idx = conProposta.map(function (o) { return _ULT_RIGHE.indexOf(o); }).filter(function (k) { return k >= 0; });
1002 var h2 = '<div data-acam-proposta-cluster="1" style="display:flex;gap:9px;flex-wrap:wrap;padding:9px 13px;background:#fbfcfe;border-bottom:1px solid ' + BORDO + '">'; 1003 if (idx.length) { 1004 h2 += '<div style="flex:1 1 320px;border:1px solid #86efac;background:#f0fdf4;border-radius:10px;padding:9px 11px">' 1005 + '<button type="button" data-acam-batch="1" onclick="window._tecAcamPiano.portaProposteCluster(' + _e(JSON.stringify(idx)) + ')" ' 1006 + 'title="Porta nel Programma di Miglioramento le misure che il catalogo propone per questo gruppo" ' 1007 + 'style="width:100%;cursor:pointer;border:none;background:none;color:#15803d;font-weight:800;font-size:11.5px;padding:0;text-align:left;font-family:inherit">' 1008 + '✓ Porta nel Programma le ' + idx.length + ' misure proposte per ' + _e(nomeCluster) + '</button>' 1009 + '<div style="font-size:9.5px;color:#166534;line-height:1.45;margin-top:5px">' 1010 + 'Crea in un colpo solo ' + idx.length + ' azioni del Programma (stato «Proposta»: nessuna è ancora una decisione presa), ' 1011 + 'ciascuna legata allâobbligo che la genera â così la riga passa da «Scoperto» a «In carico». ' 1012 + '<b>Se non lo premi</b>: questi ' + idx.length + ' obblighi restano <u>dovuti e senza azione</u>, e il DVR dichiara un obbligo ' 1013 + 'senza dire con quale misura viene assolto â art. 28 c.2 lett. c.</div></div>'; 1014 } else if (lista.length && !senzaNulla.length) { 1015 h2 += '<div style="flex:1 1 320px;border:1px solid #bbf7d0;background:#f0fdf4;border-radius:10px;padding:9px 11px;' 1016 + 'font-size:10.5px;color:#166534;line-height:1.45"><b>✓ Nulla da proporre qui</b>: ' 1017 + 'tutti i ' + lista.length + ' obblighi di questo gruppo hanno già la loro azione nel Programma.</div>'; 1018 } 1019 if (senzaNulla.length) { 1020 h2 += '<div style="flex:1 1 280px;border:1px solid #fde68a;background:#fffbeb;border-radius:10px;padding:9px 11px;' 1021 + 'font-size:10.5px;color:#92400e;line-height:1.45"><b>□ ' + senzaNulla.length + ' obblighi che nessun pulsante può chiudere</b>: ' 1022 + 'per questi il catalogo non propone una misura, e inventarne una sarebbe peggio che tacere. ' 1023 + 'Vanno scritti dal tecnico, uno per uno, dalla colonna «Che cosa fare».</div>'; 1024 } 1025 if (coperti.length && idx.length) { 1026 h2 += '<div style="flex:1 1 200px;border:1px solid #c7d2fe;background:#eef2ff;border-radius:10px;padding:9px 11px;' 1027 + 'font-size:10.5px;color:#3730a3;line-height:1.45"><b>' + coperti.length + '</b> già presi in carico: ' 1028 + 'il pulsante non li tocca.</div>'; 1029 } 1030 return h2 + '</div>'; 1031 } 1032 1033 function htmlObblighi(reg) { 1034 if (!reg || !reg.disponibile) return ''; 1035 var BANDA = { non_tollerabile: '#dc2626', tollerabile_se_ridotto: '#ea580c', 1036 da_programmare: '#ca8a04', sorvegliato: '#16a34a' }; 1037 /* BL-4 â etichette da TecAcamStato.META; senza azione â «Senza azione». */
1038 var h = '<div data-acam-blocco="obblighi" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;overflow:hidden">' 1039 + '<div style="padding:13px 16px;border-top:3px solid ' + (reg.dovutiScoperti ? '#dc2626' : '#16a34a') 1040 + ';border-bottom:1px solid ' + BORDO + '">' 1041 + '<div style="font-size:9.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:' + GRIGIO + '">' 1042 + _e(reg.norma) + '</div>' 1043 + '<div style="font-size:14.5px;font-weight:800;color:' + NAVY + ';margin-top:3px">Che cosa câè da trattare</div>' 1044 + '<div style="font-size:11px;color:' + GRIGIO + ';margin-top:2px">' 1045 + reg.totale + ' obblighi di trattamento · <b>' + reg.dovuti + ' dovuti</b>' 1046 + (reg.dovutiScoperti ? ' · <b style="color:#dc2626">' + reg.dovutiScoperti + ' dovuti e ancora senza azione</b>' : ' · tutti presi in carico') 1047 + (reg.azioniAutonome ? ' · ' + reg.azioniAutonome + ' azioni di iniziativa autonoma' : '') 1048 + '</div>' 1049 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:4px">' 1050 + 'Mondo di questa azienda: ' 1051 + Object.keys(reg.perEntita || {}).map(function (k) { 1052 return '<b>' + reg.perEntita[k].totale + '</b> su ' + _e(k); 1053 }).join(' · ') 1054 + (reg.personeCensite ? (' · <b>' + reg.personeCensite + '</b> persone censite') : '') 1055 + (reg.avvisiAssegnazione ? (' · <b style="color:#b45309">' + reg.avvisiAssegnazione + ' rilievi sullâassegnazione</b>') : '') 1056 + '</div>' 1057 + ((reg.chiuseSenzaEffetto || reg.chiuseEffettoParziale || reg.chiuseSenzaAtteso) 1058 ? ('<div style="margin-top:7px;padding:8px 11px;background:#fef2f2;border:1px solid #fecaca;border-radius:9px;' 1059 + 'font-size:11px;color:#991b1b;line-height:1.5">' 1060 + (reg.chiuseSenzaEffetto ? ('<b>' + reg.chiuseSenzaEffetto + ' azioni risultano chiuse con esito efficace e il rischio collegato è rimasto dovâera.</b> ') : '') 1061 + (reg.chiuseEffettoParziale ? (reg.chiuseEffettoParziale + ' sono scese ma non fino al livello che avevano dichiarato. ') : '') 1062 + (reg.chiuseSenzaAtteso ? (reg.chiuseSenzaAtteso + ' erano state chiuse senza dichiarare a che livello il rischio dovesse arrivare. ') : '') 1063 + 'O la misura non ha inciso, o P e D non sono stati rivalutati dopo lâattuazione: ' 1064 + 'in entrambi i casi il programma dichiara un risultato che i numeri non confermano.</div>') 1065 : ''); 1066 _arr(reg.sorgentiMute).forEach(function (m) { 1067 h += '<div style="font-size:10.5px;color:#b45309;font-style:italic;margin-top:5px">⚠ La sorgente «' 1068 + _e(m.id) + '» non si è potuta leggere (' + _e(m.perche) + '): questo elenco è incompleto e non so di quanto.</div>'; 1069 }); 1070 // ââ v20.373.150 â LA LEGENDA, PERCHE' UN COLORE SENZA LEGENDA E' UN INDOVINELLO ââââââ 1071 // Segnalazione del committente: «mostra le cose da fare, con colori diversi (e senza una 1072 // legenda che ne spiega il significato)». Le quattro bande hanno un significato preciso â 1073 // decidono ENTRO QUANDO l'azione e' dovuta e CHI puo' accettare il rischio residuo â e 1074 // lasciarle senza nome costringeva a dedurlo dal rosso. Un colore che va interpretato non 1075 // e' un'informazione, e' un test di memoria.
1076 h += '<div data-acam-legenda="bande" style="display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:0 16px 10px">' 1077 + '<span style="font-size:9px;font-weight:900;letter-spacing:.07em;text-transform:uppercase;color:' + GRIGIO + '">Bande</span>'; 1078 var _SOG = (win._tecAcamObblighi && win._tecAcamObblighi.SOGLIE) || []; 1079 _arr(_SOG).forEach(function (sg) { 1080 h += '<span title="' + _e(sg.perche || '') + '" style="display:inline-flex;align-items:center;gap:5px;font-size:10.5px;color:' 1081 + GRIGIO + ';cursor:help">' 1082 + '<span style="width:10px;height:10px;border-radius:3px;background:' + (BANDA[sg.id] || '#64748b') + ';display:inline-block"></span>' 1083 + '<b style="color:#1e293b">' + _e(sg.label) + '</b> ' + _e(sg.termine) + '</span>'; 1084 }); 1085 h += '<span style="display:inline-flex;align-items:center;gap:5px;font-size:10.5px;color:' + GRIGIO + '">' 1086 + '<span style="width:10px;height:10px;border-radius:3px;background:#7c3aed;display:inline-block"></span>' 1087 + '<b style="color:#1e293b">Adempimento</b> dovuto per legge o non conformità rilevata</span>'; 1088 h += '</div>'; 1089 h += '</div>'; 1090 1091 // ââ v20.373.283 â LE SOTTO-CARTELLE, I CLUSTER E LA PROPOSTA DI DEFAULT ââââââââââââââ 1092 // Segnalazione del committente sulla .282: «la tabella Programmazione e' un elenco 1093 // telefonico, occorre trasformarla come hai fatto con le tabelle Luoghi e Mansioni». E 1094 // aveva indicato lui stesso il modello: «nella tabella Luoghi, sotto-cartella "Conformita' 1095 // All. IV" e' il modello migliore: la complessita' e' gestita tramite sotto-cartelle, 1096 // ciascuna suddivisa in cluster, ciascun cluster con le proposte di default». 1097 // 1098 // Qui la gerarchia non e' inventata: e' quella che i dati hanno gia'. 1099 // · SOTTO-CARTELLA = la NATURA dell'obbligo â adempimento dovuto per legge, non 1100 // conformita' rilevata, rischio oltre soglia. Sono tre mestieri diversi e tre modi 1101 // diversi di chiudere: tenerli in un elenco unico era il difetto. 1102 // · CLUSTER = l'ENTITA' su cui l'obbligo grava â questo luogo, questa mansione, questo 1103 // attrezzo. E' l'unita' di lavoro reale: quando si va sul posto, si chiudono insieme. 1104 // · PROPOSTA DI DEFAULT = il gemello di «Dichiara conformi i N requisiti non esaminati». 1105 // Dice che cosa fa, su quante righe, e â la meta' che conta â che cosa NON accade se 1106 // non lo premi. 1107 var SORG = [ 1108 { id: 'obbligo_legge', tit: 'Adempimenti dovuti per legge', 1109 sot: 'non nascono da un difetto: nascono dallâesistenza stessa dellâattività . Formazione, sorveglianza, verifiche periodiche.', 1110 col: '#7c3aed' }, 1111 { id: 'non_conformita', tit: 'Non conformità rilevate', 1112 sot: 'qualcuno è andato a guardare e ha trovato uno scostamento. Finché non è chiuso, la valutazione indica un problema e non la sua misura.', 1113 col: '#dc2626' }, 1114 { id: 'rischio', tit: 'Rischi oltre soglia da ridurre', 1115 sot: 'il residuo calcolato sta sopra la soglia che obbliga ad agire: la banda dice entro quando e chi può accettarlo.', 1116 col: '#ea580c' } 1117 ]; 1118 var TIPO_ET = { luogo: 'ð¢ Luogo', mansione: 'ð· Mansione', attrezzo: 'ð§ Attrezzo', 1119 ditta: 'ðï¸ Ditta', sede: 'ð¢ Sede' }; 1120 1121 // l'indice serve al pulsante: la riga non ha un id proprio, e passare l'oggetto intero 1122 // dentro un onclick significherebbe serializzare mezzo progetto in un attributo HTML. 1123 var _tutte = _arr(reg.righe); 1124 _ULT_RIGHE = _tutte; 1125 1126 function _tabella(lista) { 1127 var t = '<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:11px">' 1128 + '<tr style="background:#f8fafc">' 1129 + ['Da dove', 'Che cosa manca', 'Che cosa fare', 'Dove / chi', 'Entro', 'Chi può accettarlo', 'Stato'].map(function (t2) { 1130 return '<th style="text-align:left;padding:7px 10px;font-size:9px;font-weight:900;text-transform:uppercase;letter-spacing:.06em;color:' + GRIGIO + '">' + t2 + '</th>'; 1131 }).join('') + '</tr>'; 1132 lista.forEach(function (o) { 1133 var StT = win.TecAcamStato; 1134 var st = (!o.stato) 1135 ? { c: '#dc2626', t: 'Senza azione' } 1136 : (StT && StT.meta ? { c: StT.meta(o.stato).col, t: StT.normalizza(o.stato) } : { c: '#64748b', t: String(o.stato) });
1137 var col = o.soglia ? (BANDA[o.soglia.id] || '#64748b') : '#7c3aed'; 1138 var dovuto = (o.soglia && o.soglia.obbliga) || o.sorgente === 'obbligo_legge' || o.sorgente === 'non_conformita'; 1139 t += '<tr style="border-top:1px solid #f1f5f9' + (!o.stato && dovuto ? ';background:#fff7f7' : '') + '">' 1140 + '<td style="padding:7px 10px;white-space:nowrap">' 1141 + '<span style="font-size:8.5px;font-weight:900;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:' 1142 + col + ';border-radius:4px;padding:2px 6px">' + _e(dovuto ? 'dovuto' : 'proposto') + '</span>' 1143 + '<div style="font-size:9px;color:' + GRIGIO + ';margin-top:2px">' + _e(o.soglia ? o.soglia.label : (o.sorgente === 'obbligo_legge' ? 'obbligo di legge' : 'non conformità ')) + '</div></td>' 1144 + '<td style="padding:7px 10px;color:#1e293b;line-height:1.4">' + _e(o.titolo) 1145 + (o.norma ? '<div style="font-size:9px;color:#94a3b8;margin-top:2px">' + _e(o.norma) + '</div>' : '') + '</td>' 1146 // ââ CHE COSA FARE, E IL LEGAME CON L'ACAM ââââââââââââââââââââââââââââââââââââ 1147 // «Quando dici "dovuto" non mi dici cosa devo fare⦠e se quel che deve accadere e' 1148 // una ACAM, perche' non mi leghi quel dovuto a quella ACAM?» Le due risposte erano 1149 // gia' nella riga â `azioneProposta` e `azioni` â e questa tabella le buttava via. 1150 + '<td data-acam-cosafare="1" style="padding:7px 10px;line-height:1.45;
1150min-width:200px">' + _cosaFare(o) + '</td>' 1151 + '<td style="padding:7px 10px;font-size:10.5px;color:' + GRIGIO + '">' + _e(o.entitaTipo) 1152 + '<br><b style="color:#1e293b">' + _e(o.entitaNome || 'â') + '</b>' + _portaEntita(o) + '</td>' 1153 + '<td style="padding:7px 10px;font-size:10.5px;color:#1e293b">' + _e(o.soglia ? o.soglia.termine : (o.termineDovuto || 'da programmare')) + '</td>' 1154 + '<td style="padding:7px 10px;font-size:10px;color:' + GRIGIO + ';line-height:1.4">' 1155 + _e(o.soglia ? o.soglia.accetta : 'â') + _assegnazione(o) + '</td>' 1156 + '<td style="padding:7px 10px"><span style="font-size:9.5px;font-weight:800;color:' + st.c + ';white-space:nowrap">' + st.t + '</span>' 1157 + _efficacia(o) + '</td>' 1158 + '</tr>'; 1159 }); 1160 return t + '</table></div>'; 1161 } 1162 1163 1164 SORG.forEach(function (sg) { 1165 var righeS = _tutte.filter(function (o) { return _s(o.sorgente) === sg.id; }); 1166 if (!righeS.length) return; 1167 var scopertiS = righeS.filter(function (o) { return !o.stato; }).length; 1168 1169 // i cluster: una entita' = un cluster. L'ordine mette davanti chi ha piu' scoperti, 1170 // perche' e' li' che il lavoro serve. 1171 var byK = {}, ordine = []; 1172 righeS.forEach(function (o) { 1173 var k = _s(o.entitaTipo) + '|' + (_s(o.entitaNome) || 'â'); 1174 if (!byK[k]) { byK[k] = []; ordine.push(k); } 1175 byK[k].push(o); 1176 }); 1177 ordine.sort(function (a, b) { 1178 var sa = byK[a].filter(function (o) { return !o.stato; }).length; 1179 var sb = byK[b].filter(function (o) { return !o.stato; }).length; 1180 return sb - sa || byK[b].length - byK[a].length; 1181 }); 1182 1183 h += '<details data-acam-cartella="' + sg.id + '"' + (scopertiS ? ' open' : '') 1184 + ' style="border:1px solid ' + BORDO + ';border-left:4px solid ' + sg.col + ';border-radius:12px;background:#fff;overflow:hidden;margin-top:11px">' 1185 + '<summary style="cursor:pointer;padding:11px 15px;list-style:none">' 1186 + '<div style="display:flex;align-items:baseline;gap:9px;flex-wrap:wrap">' 1187 + '<span style="font-size:13.5px;font-weight:800;color:' + NAVY + '">' + _e(sg.tit) + '</span>' 1188 + '<span style="font-size:11px;font-weight:800;color:' + (scopertiS ? '#dc2626' : '#16a34a') + '">' 1189 + righeS.length + ' obblighi · ' + (scopertiS ? (scopertiS + ' ancora senza azione') : 'tutti presi in carico') + '</span>' 1190 + '<span style="font-size:10.5px;color:' + GRIGIO + '">â ' + ordine.length + ' ' + (ordine.length === 1 ? 'gruppo' : 'gruppi') + ' di lavoro · apri/chiudi</span>' 1191 + '</div>' 1192 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:3px;line-height:1.45">' + _e(sg.sot) + '</div>' 1193 + '</summary>'; 1194 1195 ordine.forEach(function (k) { 1196 var lista = byK[k]; 1197 var tipo = k.split('|')[0], nome = k.split('|').slice(1).join('|'); 1198 var sc = lista.filter(function (o) { return !o.stato; }).length;
1199 h += '<div data-acam-cluster="1" style="border-top:1px solid ' + BORDO + '">' 1200 + '<div style="padding:9px 15px;background:#f8fafc;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap">' 1201 + '<span style="font-size:9px;font-weight:900;letter-spacing:.06em;text-transform:uppercase;color:' + GRIGIO + '">' 1202 + _e(TIPO_ET[tipo] || tipo || 'altro') + '</span>' 1203 + '<b style="font-size:12.5px;color:' + NAVY + '">' + _e(nome || 'â') + '</b>' 1204 + '<span style="font-size:10.5px;color:' + (sc ? '#dc2626' : '#16a34a') + ';font-weight:800">' 1205 + lista.length + ' obblighi · ' + (sc ? (sc + ' scoperti') : 'nessuno scoperto') + '</span>' 1206 + '</div>' 1207 + _propostaCluster(lista, nome || 'questo gruppo') 1208 + _tabella(lista) 1209 + '</div>'; 1210 }); 1211 1212 h += '</details>'; 1213 }); 1214 1215 // le righe che nessuna sotto-cartella ha raccolto non spariscono: sparire e' il difetto 1216 // che questo file combatte. Si dichiarano, con la loro sorgente in chiaro. 1217 var _resto = _tutte.filter(function (o) { 1218 return !SORG.some(function (sg) { return _s(o.sorgente) === sg.id; }); 1219 }); 1220 if (_resto.length) { 1221 h += '<details data-acam-cartella="altro" open style="border:1px solid ' + BORDO + ';border-radius:12px;background:#fff;overflow:hidden;margin-top:11px">' 1222 + '<summary style="cursor:pointer;padding:11px 15px;list-style:none">' 1223 + '<span style="font-size:13.5px;font-weight:800;color:' + NAVY + '">Obblighi di altra provenienza</span> ' 1224 + '<span style="font-size:11px;color:' + GRIGIO + '">' + _resto.length + ' righe la cui sorgente non rientra nelle tre note: ' 1225 + 'restano visibili perché nasconderle sarebbe peggio</span></summary>' 1226 + _propostaCluster(_resto, 'altra provenienza') + _tabella(_resto) + '</details>'; 1227 } 1228 1229 return h + '</div>'; 1230 } 1231 1232 /** 1233 * ââ LA PROPOSTA DI DEFAULT, QUANDO SI PREME ââââââââââââââââââââââââââââââââââââââââââââââ 1234 * Crea le azioni del Programma per gli obblighi indicati. Tre regole, tutte e tre nate da 1235 * difetti gia' visti su questo prodotto: 1236 * · IDEMPOTENTE â l'azione porta la chiave dell'obbligo (`_obbligoKey`): premere due volte 1237 * non duplica nulla. 1238 * · NON DECIDE â nasce in stato «Proposta». Il pulsante mette la misura sul tavolo, non la 1239 * firma al posto del tecnico. 1240 * · NON TOCCA CIO' CHE ESISTE â un obbligo gia' coperto viene saltato e contato a parte. 1241 */ 1242 var _ULT_RIGHE = []; 1243 function portaProposteCluster(idx) { 1244 var lista = []; 1245 try { lista = (typeof idx === 'string') ? JSON.parse(idx) : idx; } catch (_eJ) { lista = []; } 1246 if (!Array.isArray(lista) || !lista.length) return; 1247 var d = null; 1248 try { d = (typeof win.D === 'function') ? win.D() : (win.d || null); } catch (_eD) { d = win.d || null; } 1249 if (!d) { if (win.showToast) win.showToast('Progetto non disponibile', 'warn'); return; } 1250 if (!Array.isArray(d.azioniMiglioramento)) d.azioniMiglioramento = []; 1251 var esistenti = {}; 1252 d.azioniMiglioramento.forEach(function (a) { if (a && a._obbligoKey) esistenti[_s(a._obbligoKey)] = 1; }); 1253 var K = win._tecAcamObblighi && win._tecAcamObblighi.chiaveObbligo; 1254 var creati = 0, saltati = 0; 1255 lista.forEach(function (i) { 1256 var o = _ULT_RIGHE[i]; 1257 if (!o) return; 1258 var key = _s(o._chiave) || (K ? _s(K(o)) : ''); 1259 if (!key || esistenti[key]) { saltati++; return; } 1260 var R = _num(o.residuo) || _num(o.rischioIntrinseco); 1261 d.azioniMiglioramento.push({ 1262 rischioCod: _s(o.rischioCod), 1263 rischioNome: _s(o.titolo), 1264 luogoId: _s(o.luogoId), mansioneId: _s(o.mansioneId), attrezzoId: _s(o.attrezzoId), 1265 entitaTipo: _s(o.entitaTipo), entitaNome: _s(o.entitaNome), 1266 tipo: o.sorgente === 'obbligo_legge' ? 'organizzativa' : 'tecnica', 1267 descrizione: _s(o.azioneProposta), 1268 norma: _s(o.azioneNorma) || _s(o.norma), 1269 responsabile: (win._tecAzioniDaResiduo && win._tecAzioniDaResiduo.RESPONSABILE_DEFAULT) || 'Datore di Lavoro', 1270 rResiduoPre: R || null, 1271 rAtteso: R ? Math.max(1, Math.round(R * 0.6)) : null, 1272 priorita: (o.soglia && o.soglia.id === 'non_tollerabile') ? 'Alta' : (R >= 9 ? 'Alta' : (R >= 5 ? 'Media' : 'Bassa')), 1273 scadenza: '', 1274 stato: 'Proposta', 1275 fonte: 'Obbligo di trattamento â ' + _s(o.sorgente).replace(/_/g, ' ') 1276 + (_s(o.entitaNome) ? (' su ' + _s(o.entitaTipo) + ' «' + _s(o.entitaNome) + '»') : ''), 1277 _daObbligo: true, 1278 _obbligoKey: key 1279 }); 1280 esistenti[key] = 1; creati++; 1281 }); 1282 try { 1283 if (typeof win.setD === 'function') win.setD('azioniMiglioramento', JSON.parse(JSON.stringify(d.azioniMiglioramento))); 1284 else if (typeof win.saveAll === 'function') win.saveAll(); 1285 } catch (_eS) { if (win.tecLog) win.tecLog.catch('acam:portaProposteCluster', _eS, { noThrow: true }); } 1286 if (win.showToast) { 1287 win.showToast(creati + ' azioni create nel Programma (stato «Proposta»)' 1288 + (saltati ? (' · ' + saltati + ' saltate: già presenti') : '') 1289 + ' â assegna responsabile e scadenza per renderle esigibili', creati ? 'success' : 'info'); 1290 } 1291 try { if (typeof win.renderTab === 'function') win.renderTab(); } catch (_eR) {} 1292 } 1293 1294 /** 1295 * ââ v20.373.150 â UNA TAB SOLA, NON QUATTRO MONDI ââââââââââââââââââââââââââââââââââââ 1296 * Segnalazione del committente sulla .149: «4 blocchi, invece di essere integrati sembrano 1297 * 4 mondi a parte. Un disastro». Era vero, e la causa non era grafica: i quattro blocchi 1298 * rispondevano a DUE domande diverse messe una sopra l'altra senza dirlo. 1299 * - «che cosa c'è da fare» â registro obblighi · carenze del programma · azioni 1300 * - «come sto lavorando» â i nove indicatori e le tre corsie 1301 * La seconda domanda è legittima ma non è quella che si porta in tab: si guarda quando si 1302 * vuole guardare. Da qui: una sola testata che dichiara i tre passi con i loro numeri e ci 1303 * porta sopra, i tre passi in fila numerati (la numerazione qui è vera: il passo 2 esiste 1304 * solo per gli obblighi del passo 1, il passo 3 è ciò che li chiude), e gli indicatori in 1305 * fondo, chiusi, dietro un titolo che dice a che cosa servono. 1306 */ 1307 function _passo(n, titolo, sottotitolo, ancora) { 1308 return '<div id="' + ancora + '" style="display:flex;align-items:baseline;gap:9px;margin:2px 0 -5px 2px">' 1309 + '<span style="flex:none;width:20px;height:20px;border-radius:50%;background:' + NAVY + ';color:#fff;' 1310 + 'font-size:10.5px;font-weight:900;display:inline-flex;align-items:center;justify-content:center">' + n + '</span>' 1311 + '<span style="font-size:11px;font-weight:900;letter-spacing:.06em;text-transform:uppercase;color:' + NAVY + '">' 1312 + _e(titolo) + '</span>' 1313 + '<span style="font-size:10.5px;color:' + GRIGIO + ';font-style:italic">' + _e(sottotitolo) + '</span></div>'; 1314 } 1315
1316 function _tappa(n, ancora, eti, cifra, colore) { 1317 return '<button type="button" onclick="var _x=document.getElementById(\'' + ancora + '\');if(_x)_x.scrollIntoView({behavior:\'smooth\',block:\'start\'})" ' 1318 + 'style="cursor:pointer;text-align:left;flex:1 1 190px;border:1px solid ' + BORDO + ';border-left:4px solid ' + colore + ';' 1319 + 'background:#fbfcfe;border-radius:9px;padding:8px 12px;font-family:inherit">' 1320 + '<div style="display:flex;align-items:baseline;gap:7px">' 1321 + '<span style="font-size:9px;font-weight:900;color:' + GRIGIO + '">' + n + '</span>' 1322 + '<span style="font-size:21px;font-weight:900;line-height:1;color:' + colore + '">' + _e(cifra) + '</span></div>' 1323 + '<div style="font-size:11px;font-weight:800;color:' + NAVY + ';margin-top:3px;line-height:1.35">' + _e(eti) + '</div></button>'; 1324 } 1325 1326 1327 /* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1328 * v20.373.284 â IL PASSO 1 IN CINQUE VISTE 1329 * 1330 * ââ LA SEGNALAZIONE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1331 * Beniamino Macchiarola, sulla .283: «il tecnico guarda quelle righe e, all'interno di 1332 * ciascun cluster, sembrano tutte uguali: non c'e' indicazione di gravita. Inoltre non 1333 * capisco a quale rischio ciascuna di quelle azioni sia riconducibile⦠sarebbe utile avere 1334 * un pulsante che mi colleghi alla tabella del rischio⦠sarebbe utile che sia indicata 1335 * l'azione, con il responsabile, la tempistica desiderata e quella reale (cosi' monitoriamo 1336 * anche l'efficacia della pianificazione)». E prima ancora: «proviamo a spezzare l'elefante 1337 * in tanti pezzi, sara' piu' facile mangiarlo». 1338 * 1339 * ââ PERCHE' CINQUE VISTE E NON UNA TABELLA PIU' LARGA ââââââââââââââââââââââââââââââââââââ 1340 * La tabella del Registro risponde a sette domande per ogni riga â da dove nasce, che cosa 1341 * manca, che cosa fare, dove, entro quando, chi puo' accettarlo, a che punto e' â cioe' circa 1342 * diciotto fatti per riga. Su ventinove obblighi fanno cinquecento fatti a schermo. Non e' 1343 * una tabella sbagliata: e' la vista ISPETTIVA, quella che deve reggere un controllo, messa 1344 * come vista predefinita. Ma chi apre questa tab il martedi' mattina non fa un'ispezione: fa 1345 * UNA cosa, e ogni cosa vuole campi diversi. 1346 * 1 Oggi â «che cosa faccio adesso?» ordinata per scadenza e gravita' 1347 * 2 Il giro â «vado sul posto: cosa chiudo li'?» raggruppata per entita' 1348 * 3 Avanzamento â «dove si e' fermato il lavoro?» quattro colonne di stato 1349 * 4 Scadenze â «previsto contro reale» quattro bande di legge 1350 * 5 Registro â «regge un controllo?» la tabella di prima, intera 1351 * 1352 * ââ COME SONO FATTE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1353 * Un solo dato â `reg.righe` â e cinque proiezioni. Nessuna vista ricalcola nulla e nessuna 1354 * possiede righe che le altre non hanno: e' la stessa regola SSOT del Q1, applicata alla 1355 * vista invece che al motore. Tutte e cinque vengono disegnate insieme e si alternano 1356 * nascondendo un contenitore: cosi' cambiare vista non ridisegna la tab, non perde lo stato 1357 * aperto delle cartelle del Registro e non costa una seconda lettura dei motori. 1358 * ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1359 1360 /** Legge una data italiana (gg/mm/aaaa) o ISO. null se non e' una data: non si indovina. */ 1361 function _dataIt(v) { 1362 var s = _s(v); if (!s) return null; 1363 var m = s.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{2,4})$/); 1364 if (m) { var a = +m[3]; if (a < 100) a += 2000; return new Date(a, +m[2] - 1, +m[1]); } 1365 m = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})/); 1366 if (m) return new Date(+m[1], +m[2] - 1, +m[3]); 1367 return null; 1368 } 1369 function _oggiD() { var x = new Date(); return new Date(x.getFullYear(), x.getMonth(), x.getDate()); } 1370 function _ggTra(a, b) { return Math.round((a - b) / 864e5); } 1371 1372 /** 1373 * LO SCOSTAMENTO â previsto contro reale, sulla singola riga. 1374 * «Cosi' monitoriamo anche l'efficacia della pianificazione.» E' l'indicatore «puntualita'» 1375 * di tec-programma-misura, che oggi si calcola sull'intero programma e non si vede su nessuna 1376 * riga. Qui non si inventa nulla: si mettono le due date una accanto all'altra e si dice la 1377 * differenza. Quando la scadenza e' solo PROPOSTA dal catalogo e nessuno l'ha presa, lo si 1378 * DICHIARA invece di misurare un ritardo contro una data che non esiste â sarebbe un 1379 * indicatore inventato, e questo file esiste per non produrne. 1380 */ 1381 function _scost(o) { 1382 var pi = (o && o.piano) || {}; 1383 var pv = _dataIt(pi.scadenza), rl = _dataIt(pi.dataAttuazione); 1384 if (!pv) { 1385 if (_num(pi.giorniProposti)) { 1386 return { cls: 'prop', txt: _num(pi.giorniProposti) + ' gg proposti â data non decisa', 1387 col: '#64748b', bg: '#f1f5f9', bd: '#e2e8f0' }; 1388 } 1389 return { cls: 'none', txt: 'da pianificare', col: '#64748b', bg: '#f1f5f9', bd: '#e2e8f0' }; 1390 } 1391 if (rl) { 1392 var d = _ggTra(rl, pv); 1393 return d > 0 1394 ? { cls: 'late', txt: 'chiusa con ' + d + ' gg di ritardo', col: '#b91c1c', bg: '#fef2f2', bd: '#fecaca' }
1395 : { cls: 'ok', txt: (d === 0 ? 'chiusa in data' : ('chiusa ' + (-d) + ' gg prima')), col: '#15803d', bg: '#f0fdf4', bd: '#bbf7d0' }; 1396 } 1397 var r = _ggTra(pv, _oggiD()); 1398 if (r < 0) return { cls: 'late', txt: 'scaduta da ' + (-r) + ' gg', col: '#b91c1c', bg: '#fef2f2', bd: '#fecaca' }; 1399 if (r <= 45) return { cls: 'soon', txt: 'mancano ' + r + ' gg', col: '#a16207', bg: '#fffbeb', bd: '#fde68a' }; 1400 return { cls: 'far', txt: 'mancano ' + r + ' gg', col: '#475569', bg: '#f8fafc', bd: '#e2e8f0' }; 1401 } 1402 1403 /** 1404 * LA GRAVITA', CON LA SUA SCALA. 1405 * Non esiste UNA gravita' per tutti e tre i tipi di obbligo, e fingere che esista sarebbe un 1406 * numero inventato: un residuo P x D e una sanzione penale non sono lo stesso numero. Ogni 1407 * peso porta quindi il nome della scala da cui viene, e chi legge sa che cosa sta 1408 * confrontando. Il valore arriva gia' calcolato dal registro: qui non si ricalcola, si 1409 * traduce in colore e in ordine. 1410 */ 1411 var _RANGO = { critico: 4, alto: 3, medio: 2, basso: 1 }; 1412 function _gravOf(o) { 1413 var lab = _s(o && o.gravita) || 'â'; 1414 var col = '#' + (_s(o && o.gravitaColore) || '64748B'); 1415 var peso = _num(o && o.residuo) || _num(o && o.rischioIntrinseco); 1416 return { 1417 lab: lab, col: col, peso: peso, rango: _RANGO[lab.toLowerCase()] || 0, 1418 scala: _s(o && o.gravitaScala) || 'scala non dichiarata', 1419 cifra: peso ? String(peso) : 'â' 1420 }; 1421 } 1422 /** Stessa tinta del peso, quasi bianca: un fondo che si accorda senza urlare. */ 1423 /** 1424 * v20.373.284 â IL TITOLO E' UN TITOLO, NON UN PARAGRAFO. 1425 * Su un adempimento di legge `titolo` e `azioneProposta` sono LO STESSO TESTO â la KB scrive 1426 * l'obbligo come frase d'azione â e una riga a due piani lo stampava due volte di seguito, 1427 * per intero: quattrocento caratteri ripetuti. Qui si taglia, si mette il testo completo nel 1428 * title (niente si perde: il taglio e' visivo) e piu' sotto si smette di ripetere. 1429 */ 1430 function _tronca(v, n) { 1431 var t = _s(v); 1432 return (t.length <= n) ? t : (t.slice(0, n - 1).replace(/[\s,;:.]+$/, '') + 'â¦'); 1433 } 1434 function _stessoTesto(a, b) { 1435 function k(x) { return _s(x).toLowerCase().replace(/[\s.,;:]+/g, ' ').trim(); } 1436 return !!k(a) && k(a) === k(b); 1437 } 1438 1439 function _tenue(hex) { 1440 try { 1441 var h = String(hex).replace('#', ''); 1442 return 'rgba(' + parseInt(h.slice(0, 2), 16) + ',' + parseInt(h.slice(2, 4), 16) + ',' + parseInt(h.slice(4, 6), 16) + ',.08)'; 1443 } catch (_e) { return '#f8fafc'; } 1444 } 1445 1446 /** 1447 * LA PORTA VERSO LA TABELLA DEL RISCHIO. 1448 * «Sarebbe utile avere un pulsante che mi colleghi alla tabella del rischio (luoghi, 1449 * mansioni, attrezzi, trasversale).» Le prime tre le sapeva gia' fare `_portaEntita`; la 1450 * quarta no. Un rischio TRASVERSALE d'impresa â stress, differenze di genere, gravidanza â 1451 * non si valuta sulla mansione: si valuta una volta sola, nella tab dei Rischi. Mandare li' 1452 * il tecnico sulla mansione sarebbe mandarlo nel posto sbagliato con un pulsante che 1453 * funziona, che e' peggio di un pulsante rotto. 1454 * La classe non la decidiamo qui: si legge dal CATALOGO del Motore, che ne e' il proprietario. 1455 */ 1456 function _eTrasversale(cod) { 1457 if (!_s(cod)) return false; 1458 try { 1459 var CAT = win._tecMotore && win._tecMotore.CATALOGO_PERICOLI; 1460 if (!CAT) return false; 1461 for (var k in CAT) { 1462 if (CAT[k] && _s(CAT[k].cod).toUpperCase() === _s(cod).toUpperCase()) { 1463 return /trasversal/i.test(_s(CAT[k].categoria)); 1464 } 1465 } 1466 } catch (_e) {} 1467 return false; 1468 } 1469 function _dest(o) { 1470 var t = _s(o.entitaTipo); 1471 if (_eTrasversale(o.rischioCod)) { 1472 return { lab: 'Rischi trasversali', atto: 'window._tecVaiTab && window._tecVaiTab(5)', 1473 tit: 'Questo rischio è trasversale dâimpresa: si valuta una volta sola, nella tab dei Rischi' }; 1474 } 1475 if (t === 'luogo' && _s(o.luogoId)) { 1476 return { lab: 'Luoghi', 1477 atto: 'window._tecStatoLuoghi && window._tecStatoLuoghi.apri(' + JSON.stringify(_s(o.luogoId)) + ',\'\',' + JSON.stringify(_s(o.punto)) + ')', 1478 tit: 'Apre la scheda del luogo, sul requisito ' + (_s(o.punto) || 'interessato') }; 1479 } 1480 var TAB = { mansione: [20, 'Mansioni'], attrezzo: [19, 'Attrezzi'], ditta: [15, 'Ditte'], luogo: [3, 'Luoghi'] }; 1481 var x = TAB[t]; 1482 if (!x) return null; 1483 return { lab: x[1], 1484 atto: 'window._tecDashboardRischi && window._tecDashboardRischi.apri({tab:' + x[0] + ',titolo:' 1485 + JSON.stringify(_s(o.entitaNome)) + ',norma:' + JSON.stringify(_s(o.norma)) + '})', 1486 tit: 'Apre la tabella in cui questo rischio è stato valutato: è lì che si cambiano P e D' }; 1487 } 1488 function _btnVai(o, largo) { 1489 var D = _dest(o);
1490 if (!D) return '<span data-acam-porta="0" style="font-size:9.5px;color:#94a3b8;font-style:italic">nessuna schermata propria</span>'; 1491 return '<button type="button" data-acam-porta="1" onclick="event.stopPropagation();' + _e(D.atto) + '" title="' + _e(D.tit) + '" ' 1492 + 'style="cursor:pointer;border:1px solid ' + BORDO + ';background:#fff;color:#1d4ed8;border-radius:7px;' 1493 + 'font-family:inherit;font-size:11px;font-weight:750;padding:5px 9px;white-space:nowrap' 1494 + (largo ? ';width:100%' : '') + '">→ ' + _e(D.lab) + '</button>'; 1495 } 1496 1497 /* BL-4 â UI stato da TecAcamStato.META; chiave '' = senza azione collegata. */ 1498 function _statoUi(k) { 1499 if (!k) return { l: 'Senza azione', c: '#c2410c', b: '#fff3ec' }; 1500 var St = win.TecAcamStato; 1501 if (St && St.meta) { 1502 var m = St.meta(k); 1503 return { l: St.normalizza(k), c: m.col, b: m.bg }; 1504 } 1505 return { l: String(k), c: '#64748b', b: '#f8fafc' }; 1506 } 1507 var _SORG_UI = { 1508 rischio: { l: 'Rischio valutato', c: '#c2410c', b: '#fff3ec' }, 1509 non_conformita: { l: 'Non conformità ', c: '#a16207', b: '#fffbeb' }, 1510 obbligo_legge: { l: 'Adempimento', c: '#6d28d9', b: '#f5f2ff' } 1511 }; 1512 var _TIPO_ET = { luogo: 'Luogo', mansione: 'Mansione', attrezzo: 'Attrezzo', ditta: 'Ditta' }; 1513 1514 /** 1515 * LA RIGA. Tre piani, e la gravita' a sinistra perche' si legga senza leggere: 1516 * 1 · codice del rischio · di quale rischio si tratta · su quale entita' 1517 * 2 · l'azione â o la dichiarazione che il catalogo non ne propone 1518 * 3 · responsabile · prevista · reale · scostamento 1519 * `piani:false` toglie i piani 2 e 3 dove non servono (il Giro, dove conta l'inventario). 1520 */ 1521 function _rigaObbligo(o, i, opz) { 1522 opz = opz || {}; 1523 var G = _gravOf(o), S = _scost(o), St = _statoUi(o.stato); 1524 var pi = o.piano || {}; 1525 var h = '<button type="button" data-acam-riga="1" onclick="window._tecAcamDettaglio&&window._tecAcamDettaglio(' + i + ')" ' 1526 + 'style="display:grid;grid-template-columns:6px 62px 1fr auto;align-items:stretch;width:100%;text-align:left;' 1527 + 'font-family:inherit;background:#fff;border:0;border-top:1px solid #f1f5f9;padding:0;cursor:pointer">' 1528 + '<span style="background:' + G.col + '"></span>' 1529 + '<span style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:9px 4px;' 1530 + 'background:' + _tenue(G.col) + ';border-right:1px solid #f1f5f9">' 1531 + '<span style="font-size:19px;font-weight:800;line-height:1;color:' + G.col + '">' + G.cifra + '</span>' 1532 + '<span style="font-size:8px;font-weight:900;letter-spacing:.05em;text-transform:uppercase;margin-top:3px;color:' + G.col + '">' + _e(G.lab) + '</span>' 1533 + '<span style="font-size:7.5px;color:' + GRIGIO + ';margin-top:1px">' + _e(G.scala.split(' (')[0]) + '</span>' 1534 + '</span>' 1535 + '<span style="padding:9px 12px;min-width:0">' 1536 + '<span style="display:flex;align-items:baseline;gap:8px;flex-wrap:wrap">' 1537 + (_s(o.rischioCod) 1538 ? '<span style="font-size:10.5px;font-weight:800;padding:1px 5px;border-radius:4px;background:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe">' + _e(o.rischioCod) + '</span>' 1539 : '<span title="Un adempimento di legge non discende da un rischio valutato" style="font-size:10.5px;font-weight:800;padding:1px 5px;border-radius:4px;background:#f5f2ff;color:#6d28d9;border:1px solid #ddd6fe">LEGGE</span>') 1540 + '<span title="' + _e(o.titolo) + '" style="font-size:13.2px;font-weight:650;color:#1e293b">' + _e(_tronca(o.titolo, 96)) + '</span>' 1541 + '<span style="font-size:11.5px;color:' + GRIGIO + '">' + _e(_TIPO_ET[o.entitaTipo] || o.entitaTipo) + ' · ' + _e(o.entitaNome || 'â') + '</span>' 1542 + '</span>'; 1543 if (opz.piani !== false) { 1544 h += '<span data-acam-cosafare="1" style="display:flex;gap:6px;margin-top:5px;font-size:11.8px;color:#334155;line-height:1.45">' 1545 + '<span style="flex:none;color:' + GRIGIO + '">⚒</span><span>' 1546 + (_arr(o.azioni).length 1547 ? (_arr(o.azioni).map(function (a) { return _e(_tronca(_s(a.descrizione || a.azione || a.misura) || '(azione senza testo)', 150)); }).join(' · ')
1548 + ' <span data-acam-copre="1" style="font-size:8.5px;font-weight:900;text-transform:uppercase;color:#fff;background:#2563eb;border-radius:4px;padding:1px 5px">ACAM</span>') 1549 : (_s(o.azioneProposta) 1550 ? ((_stessoTesto(o.azioneProposta, o.titolo) 1551 ? '<span style="color:#64748b">lâadempimento stesso è la misura: non câè altro da progettare, va eseguito</span>' 1552 : _e(_tronca(o.azioneProposta, 150))) 1553 + ' <span data-acam-copre="proposta" style="font-size:8.5px;font-weight:900;text-transform:uppercase;color:#fff;background:#7c3aed;border-radius:4px;padding:1px 5px">Proposta</span>') 1554 : '<span data-acam-copre="0" style="color:#b45309">il catalogo non propone la misura: va scritta dal tecnico</span>')) 1555 + '</span></span>' 1556 + '<span style="display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:5px;font-size:11px;color:' + GRIGIO + '">' 1557 + '<span>Resp. <b style="color:#334155">' + _e(_s(pi.responsabile) || 'da assegnare') + '</b></span><span>·</span>' 1558 + '<span>prevista <b style="color:#334155">' + _e(_s(pi.scadenza) || 'â') + '</b></span><span>·</span>' 1559 + '<span>reale <b style="color:#334155">' + _e(_s(pi.dataAttuazione) || 'â') + '</b></span>' 1560 + '<span style="font-size:10.5px;font-weight:700;padding:2px 8px;border-radius:999px;color:' + S.col + ';background:' + S.bg + ';border:1px solid ' + S.bd + '">' + _e(S.txt) + '</span>' 1561 + '</span>'; 1562 } else { 1563 h += '<span data-acam-cosafare="1" style="display:block;margin-top:3px;font-size:10.5px;color:' + GRIGIO + '">' 1564 + _e(St.l) + ' · ' + _e(_s(pi.responsabile) || 'responsabile da assegnare') 1565 + (_s(pi.scadenza) ? (' · prevista ' + _e(pi.scadenza)) : '') + '</span>'; 1566 } 1567 h += '</span>' 1568 + '<span style="display:flex;flex-direction:column;gap:6px;align-items:flex-end;justify-content:center;padding:9px 12px 9px 6px">' 1569 + '<span style="font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:5px;color:' + St.c + ';background:' + St.b + '">' + St.l + '</span>' 1570 + _btnVai(o) 1571 + '</span></button>'; 1572 return h; 1573 } 1574 1575 function _cornice(l) { 1576 return '<div style="border:1px solid ' + BORDO + ';border-radius:10px;overflow:hidden;background:#fff">' + l + '</div>'; 1577 } 1578 1579 /** L'istogramma delle gravita': quattro barre che dicono di che cosa e' fatto l'insieme. */ 1580 function _istogramma(righe) { 1581 var K = [['critico', '#b91c1c'], ['alto', '#c2410c'], ['medio', '#a16207'], ['basso', '#15803d']]; 1582 var n = K.map(function (k) { return _arr(righe).filter(function (o) { return _s(o.gravita).toLowerCase() === k[0]; }).length; }); 1583 var mx = Math.max.apply(null, n.concat([1]));
1584 return '<div data-acam-istogramma="1" title="Di che cosa è fatto questo insieme, per gravità " ' 1585 + 'style="flex:none;display:flex;gap:5px;align-items:flex-end;border:1px solid ' + BORDO + ';background:#fbfcfe;border-radius:9px;padding:8px 11px">' 1586 + K.map(function (k, i) { 1587 return '<div style="display:flex;flex-direction:column;align-items:center;gap:4px;min-width:34px">' 1588 + '<div style="font-size:11px;font-weight:700;color:' + k[1] + '">' + n[i] + '</div>' 1589 + '<div style="width:100%;border-radius:3px 3px 0 0;height:' + Math.round(5 + n[i] / mx * 28) + 'px;background:' + k[1] + ';opacity:' + (n[i] ? 1 : 0.2) + '"></div>' 1590 + '<div style="font-size:8.5px;font-weight:900;letter-spacing:.05em;text-transform:uppercase;color:' + GRIGIO + '">' + k[0].slice(0, 3) + '</div></div>'; 1591 }).join('') + '</div>'; 1592 } 1593 function _testataVista(tit, sotto, righe) { 1594 return '<div style="display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap;margin-bottom:13px">' 1595 + '<div style="flex:1;min-width:250px"><div style="font-size:14.5px;font-weight:800;color:' + NAVY + '">' + tit + '</div>' 1596 + '<div style="font-size:12px;color:' + GRIGIO + ';margin-top:3px;line-height:1.5;max-width:76ch">' + sotto + '</div></div>' 1597 + _istogramma(righe) + '</div>'; 1598 } 1599 1600 /* ââ 1 · OGGI âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1601 * L'ordine e' quello del mestiere, non quello dell'archivio: prima chi e' gia' scaduto, poi 1602 * chi scade presto, e a parita' di tempo il piu' grave. E' l'unica vista che mette insieme le 1603 * tre sorgenti senza distinguerle: alle otto del martedi' non importa da dove nasce 1604 * l'obbligo, importa quale sia il prossimo. */ 1605 function _vOggi(righe) { 1606 var TETTO = 12; 1607 var ord = { late: 0, soon: 1, prop: 2, far: 3, none: 4, ok: 5 }; 1608 var aperti = _arr(righe).filter(function (o) { return o.stato !== 'chiuso'; }).slice().sort(function (a, b) { 1609 var sa = _scost(a), sb = _scost(b); 1610 return (ord[sa.cls] - ord[sb.cls]) || (_gravOf(b).rango - _gravOf(a).rango) || (_gravOf(b).peso - _gravOf(a).peso); 1611 }); 1612 if (!aperti.length) { 1613 return _testataVista('Nessun obbligo aperto', 1614 'Tutti gli obblighi del registro sono chiusi. Non è un traguardo definitivo: la valutazione è dinamica e il registro si riapre al primo dato nuovo.', righe); 1615 } 1616 var vis = aperti.slice(0, TETTO); 1617 return _testataVista('Le ' + vis.length + ' che pesano di più, in ordine di scadenza e gravità ', 1618 'Prima ciò che è già scaduto, poi ciò che scade presto; a parità di tempo, il più grave. La colonna di sinistra è il peso: ' 1619 + 'sotto il numero c\'è la <b>scala</b> da cui viene, perché un residuo PÃD e una sanzione penale non sono lo stesso numero.', aperti) 1620 + _cornice(vis.map(function (o) { return _rigaObbligo(o, _ULT_RIGHE.indexOf(o)); }).join('')) 1621 + (aperti.length > TETTO 1622 ? ('<div style="font-size:11.5px;color:' + GRIGIO + ';margin-top:9px">Altri <b>' + (aperti.length - TETTO) 1623 + '</b> obblighi aperti oltre questi: non sono nascosti, sono in coda â e per intero nella vista <b>Registro</b>.</div>') 1624 : ''); 1625 } 1626 1627 /* ââ 2 · IL GIRO ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1628 * Come si lavora davvero: quando vai in Archivio chiudi tutto quel che riguarda l'Archivio, 1629 * non «tutti gli scivolamenti dell'azienda». Qui la riga si accorcia â l'entita' e' nella 1630 * testata e ripeterla su ogni riga era rumore â e il pulsante «Vai» sta nella testata una 1631 * volta sola. I riquadri sono ordinati per il caso peggiore che contengono. */ 1632 function _vGiro(righe) { 1633 var by = {}, ord = []; 1634 _arr(righe).forEach(function (o) { 1635 var k = _s(o.entitaTipo) + '|' + (_s(o.entitaNome) || 'â'); 1636 if (!by[k]) { by[k] = []; ord.push(k); } 1637 by[k].push(o); 1638 }); 1639 function peggiore(l) { 1640 var StG = win.TecAcamStato; 1641 return Math.max.apply(null, l.map(function (o) { 1642 return (StG && StG.chiusa({ stato: o.stato })) ? 0 : _gravOf(o).rango; 1643 }).concat([0])); 1644 } 1645 ord.sort(function (a, b) { return peggiore(by[b]) - peggiore(by[a]) || by[b].length - by[a].length; }); 1646 return _testataVista('Un riquadro per posto â ordinati per la gravità peggiore che contengono', 1647 'L\'unità di lavoro reale: quando si va sul posto si chiude tutto quel che c\'è lì. Dentro ogni riquadro le righe sono ordinate per peso, ' 1648 + 'e la proposta di default agisce sulle sole righe di quel posto.', righe) 1649 + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:13px">' 1650 + ord.map(function (k) { 1651 var l = by[k].slice().sort(function (a, b) { return _gravOf(b).rango - _gravOf(a).rango || _gravOf(b).peso - _gravOf(a).peso; }); 1652 var sc = l.filter(function (o) { return !o.stato; }).length; 1653 var StG2 = win.TecAcamStato; 1654 var w = l.filter(function (o) { return !(StG2 && StG2.chiusa({ stato: o.stato })); })[0]; 1655 var G = w ? _gravOf(w) : null; 1656 var nome = k.split('|').slice(1).join('|');
1657 return '<div data-acam-cluster="1" style="border:1px solid ' + BORDO + ';border-radius:11px;background:#fff;overflow:hidden;display:flex;flex-direction:column">' 1658 + '<div style="padding:10px 12px;background:#f8fafc;border-bottom:1px solid ' + BORDO + ';display:flex;gap:10px;align-items:flex-start">' 1659 + '<div style="flex:1;min-width:0"><div style="font-size:13px;font-weight:750;color:' + NAVY + '">' + _e(nome) + '</div>' 1660 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:2px">' + _e(_TIPO_ET[k.split('|')[0]] || k.split('|')[0]) + ' · ' + l.length + ' obblighi · ' 1661 + (sc ? ('<b style="color:#c2410c">' + sc + ' scoperti</b>') : 'nessuno scoperto') 1662 + (G ? (' · peggiore: <b style="color:' + G.col + '">' + _e(G.lab) + '</b>') : '') + '</div></div>' 1663 + '<div style="flex:none;max-width:152px">' + _btnVai(l[0], true) + '</div>' 1664 + '</div>' 1665 + '<div style="flex:1">' + l.map(function (o) { return _rigaObbligo(o, _ULT_RIGHE.indexOf(o), { piani: false }); }).join('') + '</div>' 1666 + _propostaCluster(l, nome || 'questo gruppo') 1667 + '</div>'; 1668 }).join('') + '</div>'; 1669 } 1670 1671 /* ââ 3 · AVANZAMENTO ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1672 * La domanda non e' «che cosa manca» ma «dove si e' fermato il lavoro». Una colonna «In 1673 * carico» piena di bordi rossi che non si svuota e' un'informazione che nessun elenco 1674 * ordinato per urgenza restituisce. Il bordo sinistro di ogni scheda e' la gravita', la 1675 * pastiglia e' lo scostamento fra data prevista e data reale. */ 1676 function _vAvanz(righe) { 1677 var StA = win.TecAcamStato; 1678 var C = [''].concat((StA && StA.STATI) ? StA.STATI.slice() : []); 1679 return _testataVista('Il Programma come flusso, non come elenco', 1680 'Le stesse righe disposte per stato. Una colonna che non si svuota, o che si svuota in ritardo, si vede da qui e da nessun\'altra parte.', righe) 1681 + '<div style="display:grid;grid-template-columns:repeat(' + Math.min(C.length, 5) + ',minmax(196px,1fr));gap:11px;overflow-x:auto">' 1682 + C.map(function (k) { 1683 var l = _arr(righe).filter(function (o) { return k ? o.stato === k : !o.stato; }) 1684 .sort(function (a, b) { return _gravOf(b).rango - _gravOf(a).rango; }); 1685 if (!l.length && k) return ''; 1686 var St = _statoUi(k);
1687 return '<div data-acam-colonna="' + k + '" style="background:#f8fafc;border:1px solid ' + BORDO + ';border-radius:11px;padding:10px;min-width:196px">' 1688 + '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:9px;font-size:11px;font-weight:900;' 1689 + 'letter-spacing:.05em;text-transform:uppercase;color:' + St.c + '">' + St.l + ' <span style="font-size:13px">' + l.length + '</span></div>' 1690 + (l.length ? l.map(function (o) { 1691 var G = _gravOf(o), S = _scost(o); 1692 return '<button type="button" data-acam-riga="1" onclick="window._tecAcamDettaglio&&window._tecAcamDettaglio(' + _ULT_RIGHE.indexOf(o) + ')" ' 1693 + 'style="display:block;width:100%;text-align:left;font-family:inherit;background:#fff;border:1px solid #f1f5f9;' 1694 + 'border-left:3px solid ' + G.col + ';border-radius:8px;padding:8px 10px;margin-bottom:7px;cursor:pointer">' 1695 + '<span style="display:block;font-size:12.2px;font-weight:650;color:#1e293b;line-height:1.35">' 1696 + (_s(o.rischioCod) ? ('<span style="font-size:10px;font-weight:800;color:#1d4ed8">' + _e(o.rischioCod) + '</span> ') : '') 1697 + _e(_tronca(o.titolo, 72)) + '</span>' 1698 + '<span style="display:flex;justify-content:space-between;gap:6px;align-items:center;margin-top:4px;font-size:10.5px;color:' + GRIGIO + '">' 1699 + '<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + _e(o.entitaNome || 'â') + '</span>' 1700 + '<span style="flex:none;font-weight:700;padding:1px 7px;border-radius:999px;color:' + S.col + ';background:' + S.bg + ';border:1px solid ' + S.bd + '">' + _e(S.txt) + '</span>' 1701 + '</span></button>'; 1702 }).join('') 1703 : ('<div style="font-size:11.5px;color:' + GRIGIO + ';font-style:italic;padding:8px 2px;line-height:1.45">Nessun obbligo in questo stato.</div>')) 1704 + '</div>'; 1705 }).join('') + '</div>'; 1706 } 1707 1708 /* ââ 4 · SCADENZE âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1709 * L'unica vista in cui «chi puo' accettare il rischio residuo» conta davvero: e' la banda a 1710 * deciderlo, ed e' la riga piu' pesante del DVR sul piano della responsabilita'. Sta nel 1711 * sottotitolo della banda â una volta â invece che ripetuta in ogni riga. */ 1712 function _vScad(righe) { 1713 var aperti = _arr(righe).filter(function (o) { return o.stato !== 'chiuso'; }); 1714 // ââ LA BANDA SI CHIEDE AL REGISTRO, NON SI INDOVINA DALL'ETICHETTA ââââââââââââââââââââ 1715 // La prima stesura ordinava le bande con una regex sul TESTO del termine («urgente», 1716 // «senza indugio»). Il cricchetto delle regole duplicate l'ha bocciata, e aveva ragione: 1717 // e' la classe di difetti in cui una regex sul nome decide al posto della struttura che 1718 // lo dichiara, e basta riformulare un termine perche' l'ordine di legge si scomponga in 1719 // silenzio. La struttura c'e': `soglia.rango` (1 = non tollerabile ⦠4 = sorvegliato) e la 1720 // `sorgente`. L'etichetta resta solo cio' che si legge a schermo. 1721 function _chiaveBanda(o) { 1722 if (_s(o.sorgente) === 'obbligo_legge') return 'L'; // gia' dovuto: nessuna soglia 1723 if (o.soglia && _num(o.soglia.rango)) return 'S' + _num(o.soglia.rango); 1724 if (_s(o.sorgente) === 'non_conformita') return 'N'; // il termine lo fissa il DL 1725 return 'Z'; 1726 } 1727 // L'ordine di legge: cio' che e' gia' dovuto, poi le bande del residuo dalla piu' stretta, 1728 // poi cio' il cui termine e' ancora da concordare. 1729 var ORDINE = { L: 0, S1: 1, S2: 2, N: 3, S3: 4, S4: 5, Z: 9 }; 1730 var by = {}, ord = []; 1731 aperti.forEach(function (o) { 1732 var k = _chiaveBanda(o); 1733 if (!by[k]) { by[k] = []; ord.push(k); } 1734 by[k].push(o); 1735 }); 1736 ord.sort(function (a, b) { return (ORDINE[a] == null ? 9 : ORDINE[a]) - (ORDINE[b] == null ? 9 : ORDINE[b]); }); 1737 var late = aperti.filter(function (o) { return _scost(o).cls === 'late'; }).length; 1738 return _testataVista('Quattro bande di legge â e in ognuna, previsto contro reale', 1739 'Chi può accettare il rischio residuo lo decide la banda: sta scritto qui una volta, invece che ripetuto su ogni riga. ' 1740 + (late ? ('In questo progetto <b style="color:#b91c1c">' + late + ' obblighi aperti sono già oltre la data prevista</b>: è la misura di quanto la pianificazione sta reggendo.') 1741 : 'Nessun obbligo aperto è oltre la data prevista.'), aperti) 1742 + '<div style="position:relative;padding-left:22px">' 1743 + '<div style="position:absolute;
1743left:6px;top:8px;bottom:8px;width:2px;background:' + BORDO + '"></div>' 1744 + ord.map(function (k) { 1745 var l = by[k].slice().sort(function (a, b) { return _gravOf(b).rango - _gravOf(a).rango; }); 1746 var acc = _s(l[0].soglia ? l[0].soglia.accetta : '') || 'lo fissa il Datore di Lavoro alla presa dâatto'; 1747 var eti = _s(l[0].soglia ? l[0].soglia.termine : (l[0].termineDovuto || 'da programmare')); 1748 return '<div data-acam-banda="' + k + '" style="position:relative;margin-bottom:16px">' 1749 + '<div style="position:absolute;left:-20px;top:6px;width:10px;height:10px;border-radius:50%;background:#fff;border:2.5px solid ' + NAVY + '"></div>' 1750 + '<div style="font-size:12.5px;font-weight:750;color:' + NAVY + '">' + _e(eti) + ' <span style="font-size:11.5px;font-weight:600;color:' + GRIGIO + '">· ' + l.length + '</span></div>' 1751 + '<div style="font-size:11px;color:' + GRIGIO + ';margin:2px 0 8px;line-height:1.45">Chi può accettarlo: <b>' + _e(acc) + '</b></div>' 1752 + _cornice(l.map(function (o) { return _rigaObbligo(o, _ULT_RIGHE.indexOf(o)); }).join('')) 1753 + '</div>'; 1754 }).join('') + '</div>'; 1755 } 1756 1757 /* ââ LA BARRA DELLE VISTE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1758 * Cinque linguette, ciascuna con la DOMANDA a cui risponde: e' l'etichetta che fa scegliere, 1759 * non il nome. */ 1760 var VISTE = [ 1761 { id: 'oggi', n: '1', t: 'Oggi', q: 'Che cosa faccio adesso?' }, 1762 { id: 'giro', n: '2', t: 'Il giro', q: 'Vado sul posto: cosa chiudo lì?' }, 1763 { id: 'avanz', n: '3', t: 'Avanzamento', q: 'Dove si è fermato il lavoro?' }, 1764 { id: 'scad', n: '4', t: 'Scadenze', q: 'Previsto contro reale' }, 1765 { id: 'reg', n: '5', t: 'Registro', q: 'Regge un controllo?' } 1766 ]; 1767 var VISTA_DEFAULT = 'oggi'; 1768 1769 function htmlPasso1(reg) { 1770 if (!reg || !reg.disponibile) return ''; 1771 var righe = _arr(reg.righe); 1772 _ULT_RIGHE = righe; 1773 var att = _vistaCorrente(); 1774 var CORPO = { oggi: _vOggi, giro: _vGiro, avanz: _vAvanz, scad: _vScad }; 1775
1776 var h = '<div data-acam-blocco="passo1" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;overflow:hidden">'; 1777 h += '<div style="display:flex;border-bottom:1px solid ' + BORDO + ';overflow-x:auto">'; 1778 VISTE.forEach(function (v) { 1779 var on = (v.id === att); 1780 h += '<button type="button" data-acam-vista-btn="' + v.id + '" onclick="window._tecAcamVista&&window._tecAcamVista(\'' + v.id + '\')" ' 1781 + 'style="flex:1 0 auto;min-width:150px;text-align:left;font-family:inherit;cursor:pointer;border:0;' 1782 + 'border-bottom:2.5px solid ' + (on ? NAVY : 'transparent') + ';background:' + (on ? '#f4f8fd' : '#fff') + ';padding:11px 15px 9px">' 1783 + '<span style="display:block;font-size:9.5px;font-weight:800;letter-spacing:.08em;color:' + (on ? '#2E5FA1' : '#94a3b8') + '">VISTA ' + v.n + '</span>' 1784 + '<span style="display:block;font-size:13px;font-weight:750;margin-top:2px;color:' + (on ? NAVY : '#475569') + '">' + _e(v.t) + '</span>' 1785 + '<span style="display:block;font-size:10.5px;margin-top:1px;color:' + GRIGIO + '">' + _e(v.q) + '</span>' 1786 + '</button>'; 1787 }); 1788 h += '</div>'; 1789 1790 VISTE.forEach(function (v) { 1791 var on = (v.id === att);
1792 h += '<div data-acam-vista="' + v.id + '" style="padding:16px 16px 18px' + (on ? '' : ';display:none') + '">'; 1793 try { 1794 h += (v.id === 'reg') 1795 ? ('<div style="font-size:14.5px;font-weight:800;color:' + NAVY + '">La vista completa â sette colonne, tutte le sotto-cartelle</div>' 1796 + '<div style="font-size:12px;color:' + GRIGIO + ';margin:3px 0 13px;line-height:1.5;max-width:76ch">' 1797 + 'à la vista che porti a un controllo e che allega il DVR. Non è cambiata: cambia solo che non è più la prima che vedi.</div>' 1798 + htmlObblighi(reg)) 1799 : CORPO[v.id](righe); 1800 } catch (_eV) { 1801 if (win.tecLog) win.tecLog.catch('acam:vista:' + v.id, _eV, { noThrow: true }); 1802 h += '<div style="font-size:11.5px;color:#b45309">Questa vista non si è potuta disegnare (' 1803 + _e(_s(_eV && _eV.message)) + '): le altre restano leggibili, e il Registro ha comunque tutte le righe.</div>'; 1804 } 1805 h += '</div>'; 1806 }); 1807 return h + '</div>'; 1808 } 1809 1810 /* ââ LO STATO DELLA VISTA: per progetto, e senza sporcare il progetto ââââââââââââââââââ 1811 * «La vista scelta si ricorda per progetto.» Si ricorda in localStorage: scriverla dentro `d` 1812 * marcherebbe il progetto come modificato ogni volta che si cambia linguetta, e una 1813 * preferenza di lettura non e' una modifica al DVR. */ 1814 function _chiaveVista() { 1815 var pid = ''; 1816 try { pid = _s(win.currentProjectId) || _s(win.CP && win.CP() && win.CP().id); } catch (_e) {} 1817 return 'tecdvr:acam:vista:' + (pid || 'default'); 1818 } 1819 function _vistaCorrente() { 1820 if (_s(win._acamVista)) return win._acamVista; 1821 try { 1822 var v = win.localStorage && win.localStorage.getItem(_chiaveVista()); 1823 if (v && VISTE.some(function (x) { return x.id === v; })) { win._acamVista = v; return v; } 1824 } catch (_e) {} 1825 return VISTA_DEFAULT; 1826 } 1827 win._tecAcamVista = function (k) { 1828 if (!VISTE.some(function (x) { return x.id === k; })) return; 1829 win._acamVista = k; 1830 try { if (win.localStorage) win.localStorage.setItem(_chiaveVista(), k); } catch (_e) {} 1831 // si alternano i contenitori: nessun ridisegno, nessuno stato perso 1832 try { 1833 var i, els = document.querySelectorAll('[data-acam-vista]'); 1834 for (i = 0; i < els.length; i++) els[i].style.display = (els[i].getAttribute('data-acam-vista') === k) ? '' : 'none'; 1835 var bs = document.querySelectorAll('[data-acam-vista-btn]'); 1836 for (i = 0; i < bs.length; i++) { 1837 var on = bs[i].getAttribute('data-acam-vista-btn') === k; 1838 bs[i].style.borderBottomColor = on ? NAVY : 'transparent'; 1839 bs[i].style.background = on ? '#f4f8fd' : '#fff'; 1840 } 1841 } catch (_e2) {} 1842 }; 1843 1844 /* ââ IL DETTAGLIO, DI LATO âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1845 * E' il patto che rende possibile la poverta' di colonne nelle quattro viste operative: 1846 * niente e' nascosto, e' solo SU RICHIESTA. Qui stanno tutti e sette i campi del Registro, 1847 * piu' la spiegazione della gravita' e il previsto-contro-reale per esteso. */ 1848 win._tecAcamDettaglio = function (i) { 1849 var o = _ULT_RIGHE[i]; if (!o) return; 1850 var G = _gravOf(o), S = _scost(o), St = _statoUi(o.stato); 1851 var Sg = _SORG_UI[_s(o.sorgente)] || { l: _s(o.sorgente), c: GRIGIO, b: '#f8fafc' }; 1852 var pi = o.piano || {}; 1853 var id = 'tecAcamDrawer'; 1854 var old = document.getElementById(id); if (old && old.parentNode) old.parentNode.removeChild(old); 1855 function rg(k, v) { 1856 return '<div style="display:grid;grid-template-columns:118px 1fr;gap:10px;align-items:baseline;margin-bottom:9px">' 1857 + '<div style="font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:' + GRIGIO + '">' + k + '</div>' 1858 + '<div style="font-size:12.3px;color:#334155;line-height:1.5">' + v + '</div></div>'; 1859 } 1860 var chiudi = 'var m=document.getElementById(\'' + id + '\');if(m&&m.parentNode)m.parentNode.removeChild(m)'; 1861 var fonteTxt = _s(pi.fonte) === 'azione' ? 'quella dellâazione assunta' 1862 : _s(pi.fonte) === 'decisa' ? 'una scadenza decisa dal tecnico' 1863 : _s(pi.fonte) === 'proposta' ? 'solo una proposta del catalogo, non ancora presa da nessuno' 1864 : 'assente';
1865 var box = document.createElement('div'); 1866 box.id = id; 1867 box.style.cssText = 'position:fixed;inset:0;z-index:9998;display:flex;justify-content:flex-end'; 1868 box.innerHTML = 1869 '<div onclick="' + chiudi + '" style="position:absolute;inset:0;background:rgba(15,23,42,.45)"></div>' 1870 + '<div style="position:relative;width:min(460px,94vw);height:100%;background:#fff;border-left:1px solid ' + BORDO + ';' 1871 + 'box-shadow:-16px 0 44px -22px rgba(15,23,42,.5);display:flex;flex-direction:column;font-family:inherit">' 1872 + '<div style="padding:15px 18px;border-bottom:1px solid ' + BORDO + ';display:flex;gap:12px;align-items:flex-start">' 1873 + '<div style="flex:1;font-size:14px;font-weight:800;color:' + NAVY + ';line-height:1.35">' + _e(o.titolo) + '</div>' 1874 + '<button type="button" onclick="' + chiudi + '" style="cursor:pointer;border:1px solid ' + BORDO + ';background:#fff;color:' + GRIGIO + ';border-radius:7px;width:28px;height:28px;font:inherit;font-size:15px">×</button>' 1875 + '</div>' 1876 + '<div style="padding:16px 18px;overflow:auto;flex:1">' 1877 + '<div style="display:flex;gap:7px;flex-wrap:wrap;margin-bottom:15px">' 1878 + '<span style="font-size:9.5px;font-weight:800;text-transform:uppercase;padding:2px 8px;border-radius:5px;color:' + G.col + ';background:' + _tenue(G.col) + '">' + _e(G.lab) + (G.peso ? (' · ' + G.peso) : '') + '</span>' 1879 + '<span style="font-size:9.5px;font-weight:800;text-transform:uppercase;padding:2px 8px;border-radius:5px;color:' + Sg.c + ';background:' + Sg.b + '">' + _e(Sg.l) + '</span>' 1880 + '<span style="font-size:9.5px;font-weight:800;text-transform:uppercase;padding:2px 8px;border-radius:5px;color:' + St.c + ';background:' + St.b + '">' + St.l + '</span>' 1881 + '</div>' 1882 + rg('Rischio', _s(o.rischioCod) 1883 ? ('<b>' + _e(o.rischioCod) + '</b>' + (_s(o.rischioNome) ? (' â ' + _e(o.rischioNome)) : '')) 1884 : '<i>nessuno: è un adempimento di legge, non discende da un P à D</i>') 1885 + rg('Gravità ', '<b style="color:' + G.col + '">' + _e(G.lab) + '</b>' 1886 + (G.peso ? (' â ' + _num(o.P) + ' à ' + _num(o.D) + ' = ' + G.peso) : '') 1887 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:2px">scala: ' + _e(G.scala) + '</div>') 1888 + rg('Dove / chi', _e(_TIPO_ET[o.entitaTipo] || o.entitaTipo) + ' â <b>' + _e(o.entitaNome || 'â') + '</b>') 1889 + rg('Norma', _e(o.norma)) 1890 + rg('Entro', _e(_s(o.soglia ? o.soglia.termine : (o.termineDovuto || 'da programmare')))) 1891 + rg('Chi accetta', _e(_s(o.soglia ? o.soglia.accetta : '') || 'lo fissa il Datore di Lavoro alla presa dâatto')) 1892 + '<div style="margin-top:16px;padding-top:14px;border-top:1px solid #f1f5f9">' 1893 + '<div style="font-size:10px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:' + GRIGIO + ';margin-bottom:8px">Lâazione</div>' 1894 + _cosaFare(o) 1895 + '<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-top:11px">' 1896 + '<div style="border:1px solid ' + BORDO + ';border-radius:9px;padding:8px 10px;background:#fbfcfe">' 1897 + '<div style="font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:' + GRIGIO + '">Responsabile</div>' 1898 + '<div style="font-size:12px;font-weight:650;color:#1e293b;margin-top:3px">' + _e(_s(pi.responsabile) || 'da assegnare') + '</div></div>' 1899 + '<div style="border:1px solid ' + BORDO + ';border-radius:9px;padding:8px 10px;background:#fbfcfe">' 1900 + '<div style="font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:' + GRIGIO + '">Prevista</div>' 1901 + '<div style="font-size:12px;font-weight:650;color:#1e293b;margin-top:3px">' + _e(_s(pi.scadenza) || 'â') + '</div></div>' 1902 + '<div style="border:1px solid ' + BORDO + ';border-radius:9px;padding:8px 10px;background:#fbfcfe">' 1903 + '<div style="font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:' + GRIGIO + '">Reale</div>' 1904 + '<div style="font-size:12px;font-weight:650;color:#1e293b;margin-top:3px">' + _e(_s(pi.dataAttuazione) || 'â') + '</div></div>' 1905 + '</div>' 1906 + '<div style="margin-top:9px"><span style="font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;color:' + S.col + ';background:' + S.bg + ';border:1px solid ' + S.bd + '">' + _e(S.txt) + '</span>' 1907 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:6px;line-height:1.45">Lo scostamento fra prevista e reale è lâindicatore <b>puntualità </b> del programma, portato sulla singola riga. ' 1908 + 'La data qui è <b>' + _e(fonteTxt) + '</b>.</div></div>' 1909 + '</div>' 1910 + '<div style="margin-top:16px;padding-top:14px;border-top:1px solid #f1f5f9">' 1911 + '<div style="font-size:10px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:' + GRIGIO + ';margin-bottom:8px">Da qui si va</div>' 1912 + _btnVai(o) 1913 + '<div style="font-size:10.5px;color:' + GRIGIO + ';margin-top:8px;line-height:1.45">Apre la tabella in cui questo rischio è stato valutato: è lì che si cambiano P e D, non qui.</div>' 1914 + '</div>' 1915 + '</div></div>'; 1916 document.body.appendChild(box); 1917 }; 1918 1919 function html(d, opts) { 1920 opts = opts || {}; 1921 var v = opts.vista || A.vista(d); 1922 var reg = v.obblighi || {}; 1923 var nAzioni = v.totale || 0;
1924 var nScoperti = reg.disponibile ? (reg.dovutiScoperti || 0) : null; 1925 var nCarenze = (v.allarmi || []).length; 1926 1927 var h = '<div style="display:grid;gap:14px">'; 1928 1929 // ââ LA TESTATA: una sola, e dice dove si va ââââââââââââââââââââââââââââââââââââââââ 1930 h += '<div data-acam-testata="1" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;padding:14px 17px">' 1931 + '<div style="font-size:9.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:' + GRIGIO + '">' 1932 + _e(v.norma) + '</div>' 1933 + '<div style="font-size:16px;font-weight:900;color:' + NAVY + ';margin-top:3px">Che cosa câè da fare, e con che cosa lo chiudiamo</div>' 1934 + '<div style="font-size:11.5px;color:' + GRIGIO + ';margin-top:3px;line-height:1.5">' 1935 + 'Tre passi, uno dentro lâaltro: gli obblighi che questa azienda ha, che cosa manca al programma ' 1936 + 'per reggerli, e le azioni che li chiudono. Gli indicatori di come stiamo lavorando stanno in fondo, ' 1937 + 'chiusi: rispondono a unâaltra domanda.</div>' 1938 + '<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:11px">' 1939 + (reg.disponibile 1940 ? _tappa('1', 'acam-passo-1', (nScoperti ? 'obblighi dovuti e ancora senza azione' : 'obblighi dovuti, tutti presi in carico'), 1941 (nScoperti || reg.dovuti || 0), (nScoperti ? '#dc2626' : '#16a34a')) 1942 : '') 1943 + _tappa('2', 'acam-passo-2', (nCarenze ? 'cose che il programma non regge a un controllo' : 'il programma non ha carenze aperte'), 1944 (nCarenze || 'â'), (nCarenze ? '#ea580c' : '#16a34a')) 1945 + _tappa('3', 'acam-passo-3', (nAzioni ? 'azioni in programma' : 'nessuna azione nel programma'), 1946 (nAzioni || 'â'), (nAzioni ? '#4338ca' : '#94a3b8')) 1947 + '</div></div>'; 1948 1949 // ââ PASSO 1 â gli obblighi âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1950 if (reg.disponibile) { 1951 h += _passo('1', 'Gli obblighi', 'che cosa la legge e i dati censiti chiedono a questa azienda', 'acam-passo-1'); 1952 // v20.373.284 â cinque viste sullo stesso registro; la quinta E' htmlObblighi, intatta. 1953 h += htmlPasso1(reg); 1954 } 1955 1956 // ââ PASSO 2 â che cosa manca al programma ââââââââââââââââââââââââââââââââââââââââââ 1957 h += _passo('2', 'Le carenze del programma', 'perché quegli obblighi, così come sono scritti, non reggono', 'acam-passo-2'); 1958 h += htmlAllarmi(v); 1959 1960 // ââ PASSO 3 â le azioni ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1961 h += _passo('3', 'Le azioni', 'che cosa li chiude, con i cinque dati che lâart. 28 c.2 lett. c e d chiede', 'acam-passo-3'); 1962 h += htmlAzioni(d, opts); 1963 1964 // ââ IN FONDO, CHIUSO: come stiamo lavorando ââââââââââââââââââââââââââââââââââââââââ 1965 h += '<details data-acam-blocco="indicatori" style="border:1px solid ' + BORDO + ';border-radius:14px;background:#fff;overflow:hidden">' 1966 + '<summary style="cursor:pointer;padding:13px 16px;list-style:none">' 1967 + '<div style="font-size:13.5px;font-weight:800;color:' + NAVY + '">Come stiamo lavorando su queste azioni ' 1968 + '<span style="font-size:11px;font-weight:700;color:' + GRIGIO + '">â apri</span></div>' 1969 + '<div style="font-size:11px;color:' + GRIGIO + ';margin-top:2px;line-height:1.45">' 1970 + v.kpi.length + ' indicatori · <b style="color:' + (v.nSotto ? '#dc2626' : '#16a34a') + '">' 1971 + v.nCentrati + ' bersagli centrati</b>' 1972 + (v.nNonMisurabili ? ' · ' + v.nNonMisurabili + ' non ancora misurabili' : '') 1973 + ' · e il rigore richiesto a ciascuna corsia. Non è che cosa câè da fare: è come lo stiamo facendo.</div>' 1974 + '</summary>' 1975 + '<div style="padding:0 16px 15px;display:grid;gap:12px">' 1976 + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:11px">'; 1977 v.kpi.forEach(function (k) { h += _cardKpi(k); }); 1978 h += '</div>' + htmlCorsie(v) + '</div></details>'; 1979 1980 return h + '</div>'; 1981 } 1982 1983 A.html = html; 1984 A.htmlObblighi = htmlObblighi; 1985 A.htmlPasso1 = htmlPasso1; 1986 A.VISTE = VISTE; 1987 A.portaProposteCluster = portaProposteCluster; 1988 A.htmlAllarmi = htmlAllarmi; 1989 A.htmlAzioni = htmlAzioni; 1990})(typeof window !== 'undefined' ? window : this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.