Dashboard Condivisa con il Cliente in GLPI

Come creare e condividere dashboard di assistenza con i clienti usando Metabase o Grafana su GLPI, con filtro per entità (incluse le sotto-entità), calcolo corretto dello SLA nel database e accesso a prova di manomissione.

La trasparenza genera fiducia, ma una dashboard mal isolata diventa un incidente di protezione dei dati. Condividere gli indicatori di assistenza con il cliente riduce i solleciti via e-mail e comunica professionalità. La sfida tecnica non è disegnare il grafico: è garantire che ogni cliente veda solo i propri ticket. Ecco come farlo con GLPI + Metabase o Grafana, con il filtro per entità bloccato nel punto giusto.

Prima di pubblicare: tre decisioni che definiscono tutto

Nella gestione di ambienti GLPI multicliente, tre scelte decidono se la dashboard sarà un valore aggiunto o un problema di sicurezza:

  • Dove bloccare il filtro per entità: nel link, nella query o nell'embed firmato. Solo l'ultimo resiste alla manomissione dell'URL.
  • Quale data usare per il periodo: creazione (date), risoluzione (solvedate) o chiusura (closedate). Mescolare le tre distorce ogni indicatore.
  • Da dove leggere i dati: il database primario o una replica in sola lettura. Metabase e Grafana lanciano query pesanti e competono con GLPI in produzione.

Passo 1 - un utente di database in sola lettura

Non puntare mai lo strumento di BI all'utente di GLPI. Crea un utente dedicato con SELECT e nient'altro. In MariaDB:

-- Utente in sola lettura per la BI, limitato alla rete interna
CREATE USER 'bi_ro'@'10.20.%' IDENTIFIED BY '...password_forte...';
GRANT SELECT ON glpi.* TO 'bi_ro'@'10.20.%';

-- Il limite di tempo per query evita che una dashboard blocchi il database (MariaDB)
SET GLOBAL max_statement_time = 15;   -- secondi

FLUSH PRIVILEGES;

Se possibile, punta la BI a una replica di lettura invece che al database primario. Così un filtro dimenticato in un grafico non trattiene le transazioni di GLPI in produzione; abbiamo visto un singolo pannello con un JOIN scritto male degradare il tempo di risposta dell'intero help desk.

Passo 2 - isolare per entità senza esporre le sotto-entità

Ogni cliente è un'entità in GLPI, e l'istinto è filtrare per entities_id = X. Qui si nasconde la trappola più comune: se il cliente ha filiali o reparti come sotto-entità, quel filtro cattura solo l'entità radice e i numeri risultano più bassi della realtà. Bisogna includere tutto il sottoalbero. In MariaDB 10.2.2+ si risolve con una CTE ricorsiva:

-- Tutte le entità del cliente (radice + discendenti)
WITH RECURSIVE albero_cliente AS (
    SELECT id FROM glpi_entities WHERE id = 4        -- entità radice del cliente
    UNION ALL
    SELECT e.id
    FROM glpi_entities e
    JOIN albero_cliente a ON e.entities_id = a.id
)
SELECT
    COUNT(*)                       AS totale,
    SUM(t.status IN (1,2,3,4))     AS aperti,
    SUM(t.status IN (5,6))         AS chiusi
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM albero_cliente)
  AND t.is_deleted = 0;

Nota il is_deleted = 0: GLPI usa la cancellazione logica e, senza quel filtro, i ticket nel cestino entrano nel conteggio. È la seconda correzione più frequente che applichiamo alle dashboard ereditate da un altro team.

Passo 3 - lo SLA senza gonfiare la percentuale

Il tasso di rispetto dello SLA sembra banale: risolto entro la scadenza, conteggiato. Il dettaglio ingannevole è nel campo time_to_resolve (la scadenza dello SLA salvata sul ticket): è NULL quando nessuno SLA è associato al ticket. Se confronti solvedate <= time_to_resolve senza gestire il NULL, quei ticket spariscono dal denominatore e la percentuale sale artificialmente. Rendi esplicita la popolazione:

WITH RECURSIVE albero_cliente AS (
    SELECT id FROM glpi_entities WHERE id = 4
    UNION ALL
    SELECT e.id FROM glpi_entities e
    JOIN albero_cliente a ON e.entities_id = a.id
)
SELECT
    COUNT(*)                                                          AS con_sla,
    SUM(t.solvedate <= t.time_to_resolve)                          AS nei_tempi,
    ROUND(100 * SUM(t.solvedate <= t.time_to_resolve) / COUNT(*), 1) AS pct_sla
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM albero_cliente)
  AND t.is_deleted = 0
  AND t.time_to_resolve IS NOT NULL      -- solo ticket con SLA
  AND t.solvedate IS NOT NULL            -- già risolti
  AND t.date >= '2026-06-01'
  AND t.date <  '2026-07-01';

Quali indicatori hanno senso per il cliente

Non tutto ciò che il team interno monitora interessa - o deve essere esposto - al cliente. Un insieme snello funziona meglio:

  • Operativo: ticket aperti per stato e priorità, aperti vs. chiusi nel periodo, tempo medio di risposta e di risoluzione.
  • SLA: percentuale entro la scadenza e ticket a rischio (vicini alla scadenza di time_to_resolve).
  • Soddisfazione: voto medio del sondaggio (glpi_ticketsatisfactions) e andamento mensile.
  • Contratto: ore consumate vs. contrattate e saldo residuo, quando c'è un monte ore.

Dove bloccare il filtro: matrice decisionale

Il punto più delicato è impedire che il cliente cambi il parametro nell'URL e veda i dati di un altro. Ogni metodo applica il filtro in un punto diverso:

MetodoDove viene applicato il filtroRischio di fugaSforzoQuando usarlo
Metabase - Link pubblicoParametro modificabile nell'URLAltoBassoSolo demo interna, mai con dati del cliente
Metabase - Embed firmato (JWT)Parametro bloccato lato server (token)BassoMedioOpzione consigliata per i clienti
Grafana - Accesso anonimoEspone l'intera organizzazioneAltoBassoDa evitare
Grafana - Public Dashboards (9.1+)Una dashboard per link, senza variabile modificabileMedioBassoCliente singolo, senza sotto-entità sensibili
Grafana - organizzazione per clienteIsolato per org + proxyBassoAltoMolti clienti, isolamento forte

La trappola che ci è costata di più (nella gestione quotidiana)

In un ambiente con decine di entità, la dashboard di un cliente mostrava circa la metà dei ticket indicati dal report manuale. La causa non era la query di conteggio: era il link pubblico di Metabase. Il filtro per entità era un parametro della dashboard, e chiunque avesse il link poteva modificare il valore nell'URL e navigare in altre entità. Siamo passati a un embed firmato (JWT), con entities_id iniettato nel token e il parametro contrassegnato come locked. Il cliente ha perso la possibilità di toccare il filtro e ha ottenuto un numero che finalmente coincideva con il contratto. La lezione che portiamo in ogni progetto: la sicurezza della dashboard non vive nella query, vive in dove viene applicato il filtro.

Checklist di pubblicazione

  1. Utente di database in sola lettura, preferibilmente puntato a una replica.
  2. Filtro per entità che include il sottoalbero (CTE ricorsiva o elenco di ID).
  3. is_deleted = 0 in ogni query su glpi_tickets.
  4. Periodo coerente: scegli una data (creazione, risoluzione o chiusura) e usa la stessa su ogni scheda.
  5. Filtro bloccato lato server (embed firmato), mai solo nell'URL.
  6. Prova il link con un utente esterno prima di inviarlo al cliente.

Ti servono dashboard cliente con isolamento per entità a prova di manomissione? NexTool implementa e mantiene ambienti GLPI con BI integrata (Metabase e Grafana) e governance dei dati allineata alla normativa sulla protezione dei dati. Scopri il servizio di supporto e manutenzione, oppure consulta il confronto Metabase vs Grafana e i KPI essenziali per il service desk.


Questo contenuto è stato prodotto con l'ausilio dell'intelligenza artificiale e revisionato dal team di Nextool Solutions.

Domande Frequenti

Filtrare solo per entities_id = X cattura soltanto l'entità radice. Per includere filiali e reparti, costruisci il sottoalbero con una CTE ricorsiva su glpi_entities (MariaDB 10.2.2+) e usa entities_id IN (SELECT id FROM albero_cliente). In alternativa, genera l'elenco di ID nell'applicazione e passalo come parametro.

Perché time_to_resolve è NULL quando il ticket non ha uno SLA associato. Confrontando solvedate <= time_to_resolve senza filtrare questi casi, i ticket senza SLA escono dal denominatore e gonfiano la percentuale. Restringi la popolazione con time_to_resolve IS NOT NULL.

Non per dati sensibili. Con il link pubblico il filtro per entità sta nell'URL e può essere modificato, esponendo i dati di altri clienti. Usa un embed firmato (JWT) con il parametro contrassegnato come locked e applicato lato server. Il link pubblico va bene solo per le demo interne.

Preferisci una replica in sola lettura. Se non c'è una replica, crea almeno un utente con GRANT SELECT e imposta max_statement_time affinché una query pesante della dashboard non trattenga le transazioni di GLPI in produzione.

Dipende dall'indicatore. Per il volume in entrata usa date (creazione). Per la produttività e lo SLA usa solvedate (risoluzione) o closedate (chiusura). L'errore comune è mescolarle: contare i risolti per data di creazione sottostima il risultato del mese.

Confronta solvedate con time_to_resolve sul ticket: SUM(solvedate <= time_to_resolve) sul totale con SLA. Filtra is_deleted = 0, time_to_resolve IS NOT NULL e solvedate IS NOT NULL affinché il denominatore sia corretto.

Hai bisogno di aiuto?