Se gestisci un piano Fondimpresa con Moodle e finisci sempre a esportare in Excel e sistemare colonne a mano, non è un problema tuo: è che Moodle non parla il linguaggio dei fondi interprofessionali nativamente. In questo articolo vediamo cosa chiedono i fondi, dove Moodle si ferma, e come automatizzare la rendicontazione con Reportbuilder o un plugin custom.
Se lavori in un ente accreditato o in una direzione formazione aziendale che eroga piani su Fondimpresa, Forma.Temp o Fondirigenti, conosci il momento: fine del piano formativo, 2.000 ore erogate, 180 partecipanti — e la rendicontazione da consegnare entro giovedì.
Moodle tiene tutto: completamenti, ore connesse, valutazioni. Ma il tracciato XML che Fondimpresa richiede non lo genera nessuno.
Ci siamo passati. Questo articolo spiega cosa serve e come strutturare la soluzione.
Cosa chiedono Fondimpresa e i fondi interprofessionali
Ogni fondo interprofessionale italiano ha le proprie specifiche tecniche per la rendicontazione delle attività formative. Il tracciato standard di Fondimpresa (versione corrente sul portale ufficiale) richiede, per ogni partecipante e per ogni modulo formativo:
- Codice fiscale partecipante
- Ore di partecipazione effettiva (non ore erogate: le ore realmente frequentate)
- Codice modulo formativo
- Data di completamento
- Eventuale risultato della valutazione finale (se il piano lo prevede)
Forma.Temp ha una struttura analoga ma usa un formato CSV con separatori e codifiche particolari. Fondirigenti richiede un XML con namespace specifici.
Il problema comune: questi dati esistono in Moodle, ma sono distribuiti su tabelle diverse — mdl_user, mdl_course, mdl_course_completions, mdl_logstore_standard_log — e nessuna vista predefinita li aggrega nel formato richiesto.
Perché l'export manuale fallisce sopra i 1.000 utenti
Sotto i 200 partecipanti, un export CSV + Excel può essere gestibile, a patto di avere una persona dedicata che sa cosa sta facendo. Sopra quella soglia, iniziano i problemi:
Errori di mappatura. Le colonne CSV di Moodle non corrispondono one-to-one ai campi del tracciato. Ogni export richiede un'operazione di trasformazione manuale che introduce errori.
Ore errate. Moodle traccia il tempo di connessione alla sessione, non il tempo di apprendimento effettivo. Un partecipante che tiene la finestra aperta 3 ore ma non interagisce appare come "3 ore frequentate". I fondi interprofessionali non accettano questo.
Nessuna validazione. Se un codice fiscale è sbagliato nel profilo Moodle, lo scopri quando il fondo rigetta il tracciato — non prima.
Nessuna traccia di chi ha fatto cosa. In caso di audit, ricostruire l'iter di generazione del tracciato da un file Excel è impossibile.
Reportbuilder vs plugin custom: quando basta il primo
Il modulo Reportbuilder introdotto in Moodle 4.1 è significativamente più potente dei vecchi report custom. Permette di costruire query su 30+ entità del database Moodle con join multipli, filtri, aggregazioni e export in CSV/Excel/PDF.
Per la rendicontazione ai fondi, Reportbuilder può essere sufficiente se:
- Il tuo fondo accetta un CSV con campi standard (nome, cognome, CF, ore, completamento)
- Sei disposto a fare un passaggio manuale di validazione prima dell'invio
- Il volume di partecipanti è inferiore ai 500 per piano
La limitazione principale di Reportbuilder per questo use case: non produce XML strutturato, non valida i codici fiscali contro un pattern, non carica automaticamente il file sul portale del fondo.
Un plugin custom è necessario quando:
- Il fondo richiede un XML con struttura specifica (namespace, XSD validation)
- Vuoi la validazione automatica dei CF prima dell'export
- Hai più fondi con formati diversi e non vuoi gestirne la differenza a mano
- Vuoi automatizzare l'invio (upload FTP/SFTP al portale del fondo)
- Devi tenere un log di ogni rendicontazione generata per audit
Esempio: query per estrarre completamenti con ore effettive
Questo è il tipo di query che costruiamo come base per la rendicontazione. La logica centrale: unire i completamenti corsi (mdl_course_completions) con il tempo effettivo dal logstore (mdl_logstore_standard_log), aggregando per utente e per corso.
SELECT
u.id AS user_id,
u.lastname AS cognome,
u.firstname AS nome,
u.idnumber AS codice_fiscale,
c.shortname AS codice_corso,
cc.timecompleted AS data_completamento,
ROUND(
SUM(CASE WHEN l.action = 'viewed' THEN 1 ELSE 0 END) / 60.0,
2
) AS ore_effettive
FROM
mdl_course_completions cc
JOIN mdl_user u ON u.id = cc.userid
JOIN mdl_course c ON c.id = cc.course
LEFT JOIN mdl_logstore_standard_log l
ON l.userid = cc.userid
AND l.courseid = cc.course
AND l.timecreated BETWEEN cc.timestarted AND cc.timecompleted
WHERE
cc.timecompleted IS NOT NULL
AND cc.course IN (/* lista ID corsi del piano */)
GROUP BY
u.id, u.lastname, u.firstname, u.idnumber,
c.shortname, cc.timecompleted
ORDER BY
u.lastname, u.firstname;Il risultato di questa query è la base per costruire il tracciato. Le ore "effettive" qui sono una stima basata sugli eventi viewed — un punto di partenza, non una misura precisa. Per una misura più accurata serve tracciare eventi heartbeat personalizzati, ma questa complessità dipende dal fondo e dal tipo di rendicontazione richiesta.
Come strutturiamo un plugin local per la rendicontazione
Per le integrazioni più complete, sviluppiamo un local plugin Moodle — il tipo di plugin pensato esattamente per questo: funzionalità aziendali che non appartengono al core e non devono essere nei plugin di terze parti.
La struttura minima:
// local/fondimpresa_export/version.php
<?php
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2026050800;
$plugin->requires = 2023042400; // Moodle 4.2+
$plugin->component = 'local_fondimpresa_export';
$plugin->maturity = MATURITY_STABLE;
$plugin->release = '1.0.0';Il file lib.php contiene la logica principale: query dei dati, validazione, generazione del tracciato e (opzionalmente) upload automatico.
// local/fondimpresa_export/lib.php
<?php
defined('MOODLE_INTERNAL') || die();
function local_fondimpresa_export_generate_tracciato(
array $course_ids,
string $fondo = 'fondimpresa'
): string {
global $DB;
// 1. Recupero completamenti con ore
$records = $DB->get_records_sql("
SELECT u.idnumber AS cf, u.lastname, u.firstname,
c.shortname, cc.timecompleted,
COALESCE(ore.minuti, 0) / 60.0 AS ore_effettive
FROM {course_completions} cc
JOIN {user} u ON u.id = cc.userid
JOIN {course} c ON c.id = cc.course
LEFT JOIN (
SELECT userid, courseid, COUNT(*) AS minuti
FROM {logstore_standard_log}
WHERE action = 'viewed'
GROUP BY userid, courseid
) ore ON ore.userid = cc.userid AND ore.courseid = cc.course
WHERE cc.timecompleted IS NOT NULL
AND cc.course " . $DB->get_in_or_equal($course_ids),
$course_ids
);
// 2. Validazione CF
foreach ($records as $r) {
if (!preg_match('/^[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]$/i', $r->cf)) {
// Log errore e skip — non blocca l'intero export
debugging("CF non valido per utente {$r->lastname}: {$r->cf}", DEBUG_DEVELOPER);
}
}
// 3. Generazione output (qui CSV — per XML estendi con XMLWriter)
$adapter = local_fondimpresa_export_get_adapter($fondo);
return $adapter->render($records);
}La funzione local_fondimpresa_export_get_adapter() istanzia l'adapter corretto per il fondo (pattern Strategy), così aggiungere Forma.Temp richiede solo un nuovo file adapter_formatemp.php senza toccare la logica centrale.
Validazione prima dell'invio: i controlli che salvano
I tracciati rigettati dai fondi hanno quasi sempre le stesse cause:
- Codice fiscale con caratteri errati (spazi, minuscole, caratteri accentati)
- Ore dichiarate superiori alla durata del corso (Moodle non valida questo)
- Data completamento fuori dal periodo del piano (completamenti registrati dopo la scadenza)
- Partecipanti senza profilazione completa (campo
idnumbervuoto)
Un plugin ben fatto esegue questi controlli prima di generare il file e produce un report di anomalie — così il responsabile formazione corregge i dati in Moodle prima dell'invio, non dopo il rigetto.
Integrazione con il sistema di reportistica esistente
Una nota operativa: se la tua organizzazione usa già un gestionale HR (Zucchetti, TeamSystem, SAP) che alimenta Moodle via CSV o API, il flusso ideale è:
- Il gestionale HR sincronizza utenti su Moodle con CF corretto
- Moodle traccia le attività formative
- Il plugin genera il tracciato dal dato Moodle arricchito
Questo elimina la doppia gestione dell'anagrafica. Lo abbiamo implementato per organizzazioni da 50 a 150.000 utenti — la logica è la stessa, cambia la scala.
Per approfondire le possibilità di sviluppo custom su Moodle, leggi il nostro articolo su plugin Moodle vs sviluppo custom — dove spieghiamo quando vale la pena sviluppare qualcosa di specifico e come valutare il TCO.
Conclusione: tre punti da portare via
-
L'export manuale non scala. Sopra i 500 partecipanti per piano, il costo del lavoro manuale supera rapidamente il costo di un plugin custom.
-
Reportbuilder è un punto di partenza, non la soluzione finale. Ottimo per report interni; limitato per rendicontazione ai fondi che richiede XML strutturato o upload automatico.
-
La qualità del dato in Moodle è il vero collo di bottiglia. Anche il miglior plugin non può generare un tracciato corretto se i codici fiscali nel database sono sbagliati. Prima di automatizzare la rendicontazione, vale la pena fare un audit dei profili utente.
Hai un piano formativo da rendicontare e Moodle non genera i tracciati che ti servono? Scrivici una riga: in 24 ore ti diciamo se è risolvibile e in quanto tempo.
Domande frequenti
Articoli correlati
Plugin Moodle vs sviluppo custom: come scegliere
Quando usare un plugin esistente, quando fare un fork, quando svilupparne uno da zero. Framework decisionale con calcolo TCO su 3 anni e tipologie di plugin Moodle.
Normativa & ComplianceMoodle e sicurezza sul lavoro: Accordo Stato-Regioni 2025
Cosa cambia con l'Accordo Stato-Regioni del 17/04/2025 per la FAD obbligatoria su D.Lgs 81/08, e cosa serve adeguare in Moodle: identificazione, tracciamento, attestati.
Architettura & ScalabilitàMoodle multi-tenant senza Workplace: guida pratica
Come gestire più aziende su un unico Moodle senza acquistare Workplace. Cohorts, plugin open source e soluzioni custom a confronto: quando conviene ognuno.