# Esportazioni per i fondi: report presenze nei tracciati attesi da fondi e Regioni

Status: draft (16/09/2026, deciso dall'utente: «se conosciamo i formati per i vari fondi, prevediamo già gli export»)

## Context

Nessun fondo interprofessionale espone un'API a software terzi (`docs/fondi/integrazioni-fondi.md`): il canale è
sempre un file esportato dalla piattaforma FAD, caricato a mano sul portale o conservato per l'ispezione. Forma.Temp
ha la sua API (`specs/formatemp-ftweb.md`); per tutti gli altri EasyForma deve produrre il file giusto al primo
colpo, dai log di ingresso e uscita di BigBlueButton già in `attendances`/`meeting_events`. Il concorrente Jforma
vende ogni exporter a 80 €/anno: qui sono inclusi nel piano. I formati implementati in questo giro sono quelli
pubblici e verificati alla fonte (§8 del documento): baseline ANPAL 4/2020, Fondimpresa, FonARCom, Veneto, Lazio
Modello B, Lombardia prospetto di riconciliazione. Gli altri (Fonservizi, Fondir, Puglia con IP, template FPF e
GE.CO. dietro login) vengono dopo.

## Requirements

- **Codici esterni per aula.** Su `classrooms`: `funder` (enum: `formatemp`, `fondimpresa`, `fonarcom`,
  `fonservizi`, `fondir`, `forte`, `regione_veneto`, `regione_lazio`, `regione_lombardia`, `altro`, nullable) e
  `external_codes` json con chiavi note per fondo (Fondimpresa: `id_piano`, `id_azione`; FonARCom: `id_piano`,
  `progetto`, `edizione`; Veneto: `codice_progetto` nel formato `nnnn-nnnn-nnnn-aaaa`, `codice_piattaforma`;
  Lazio: `codice_progetto`, `id_operazione`; Lombardia: `id_corso`, `id_sezione`). Su `meetings`: `external_lesson_code`
  (Veneto «codice lezione A39», altrove opzionale) e `topic` (argomento, per il Modello B). Form aula e form
  lezione mostrano solo i campi del fondo scelto. Colonne aggiunte nei file `create_*` atomici esistenti.
- **Modello interno delle sessioni** (`App\Services\Exports\AttendanceSessions`): dalle presenze di una lezione
  produce righe `{meeting, participant|teacher, role (1 allievo, 2 docente, 3 tutor, 4 ispettore), nickname,
joined_at, left_at, minutes}` una per intervallo di connessione, con fusione degli intervalli sovrapposti e delle
  interruzioni sotto `exports.gap_merge_minutes` (default 0: le Regioni vogliono le interruzioni vere), più le
  aggregazioni per partecipante (primo ingresso, ultima uscita, minuti totali). Tutte le esportazioni derivano da
  qui; parametri (tolleranza, arrotondamento, unità di durata) in `config/exports.php`.
- **Esportatori** (`App\Services\Exports\Formats\*`, interfaccia `AttendanceExporter { key(), label(), fileName(),
build(Classroom, ?period): ExportFile }`), registro in `ExportRegistry`; ogni esportatore dichiara i codici esterni
  che richiede e rifiuta con errore leggibile se mancano:
    1. `anpal` — baseline circolare ANPAL 4/2020: XLSX e PDF con titolo azione + ID, data, orario sessione, docente,
       discente, orario accesso, orario uscita; una riga per discente per sessione.
    2. `fondimpresa` — XLSX con le 9 colonne di §8.2 (codice alfanumerico sessione = `meeting.uuid`, titolo azione con
       ID FPF, titolo sessione, data, orario programmato, docente, discente, accesso, uscita) più PDF pronto per la
       firma (la firma qualificata resta esterna, vedi Out of scope).
    3. `fonarcom` — XLSX con le 11 colonne di §8.3 (titolo corso, ID piano, progetto, edizione, docente/tutor, discente,
       codice fiscale, email o cellulare, inizio, fine, durata in ore e minuti `HH:MM`); l'email esportata è quella del
       partecipante e il form partecipante spiega che deve coincidere col Fon03.
    4. `veneto` — XLSX con le 11 colonne dell'Allegato A al DDR 480/2020 nell'ordine A–K, intestazioni identiche al
       template in `docs/fondi/tracciati/`, **una riga per sessione di collegamento**, `Durata` in **minuti**, date come
       celle data con formato `dd/mm/yyyy`, ore come celle ora `hh:mm:ss`, celle vuote per i facoltativi, `Ruolo`
       numerico, `Codice_progetto` validato sulla maschera; periodo = mese solare.
    5. `lazio` — PDF «Modello B» giornaliero: partecipante, ora di connessione, ora di disconnessione, orario della
       lezione, argomento, spazio firma del docente; un foglio per lezione.
    6. `lombardia_riconciliazione` — XLSX utenza in piattaforma ↔ nome, cognome, codice fiscale, ruolo.
       Generazione con `phpoffice/phpspreadsheet` (XLSX) e `barryvdh/laravel-dompdf` (PDF); nessun dato in memoria
       oltre l'aula richiesta; nomi file `easyforma-{fondo}-{codice aula}-{periodo}.xlsx|pdf`.
- **Endpoint** `GET classrooms/{classroom}/exports/{format}?from=&to=` (download sincrono, streaming; tenancy da
  binding; 422 se i codici richiesti mancano, con l'elenco dei campi da compilare e link al form aula) e
  `GET classrooms/{classroom}/exports` (Inertia: elenco formati disponibili per il fondo dell'aula, periodo
  suggerito = mese corrente, requisiti mancanti evidenziati). Ogni download registrato in `export_logs`
  (company, classroom, format, period, user, created_at) per l'audit.
- **Scheda «dati per l'ispezione»** (`GET classrooms/{classroom}/exports/inspection.pdf`): piattaforma, indirizzo,
  modalità di accesso ospite, contatto assistenza, elenco lezioni con codice riunione BBB; richiesta da ANPAL,
  FonARCom, Lombardia, Lazio, Puglia.
- **Vetrina e docs**: sezione «Esporta nel formato del tuo fondo» con l'elenco dei formati e la nota onesta che il
  caricamento sul portale resta manuale (tranne Forma.Temp); riga in tabella prezzi «Export per fondi e Regioni:
  inclusi»; `docs/ARCHITECTURE.md` sezione Esportazioni; `docs/marketing-copy.md` aggiornato.

## Acceptance criteria

1. Aula Veneto con `codice_progetto = 1234-5678-9012-2026`, `codice_piattaforma = 7`, lezione con
   `external_lesson_code = 4411` e un discente connesso 09:00:00–09:50:00 e 10:00:00–10:47:00, docente 08:58:00–10:50:00:
   `GET classrooms/{id}/exports/veneto?from=2026-10-01&to=2026-10-31` restituisce un XLSX con intestazioni A–K
   identiche al template e 3 righe dati: due per il discente (`Durata` 50 e 47, `Ruolo` 1, `Ora_inizio`/`Ora_fine`
   come celle ora `hh:mm:ss`) e una per il docente (`Durata` 112, `Ruolo` 2), `Data_riunione` cella data
   `dd/mm/yyyy`, `Codice_progetto` = `1234-5678-9012-2026`, `Codice_lezione` = 4411. Con `codice_progetto` mancante
   la risposta è 422 e il messaggio nomina «codice progetto».
2. Stessa lezione: `exports/fonarcom` produce 11 colonne nell'ordine di §8.3 con una riga per discente, `Durata`
   `01:37`, email del partecipante, codice fiscale; `exports/anpal` produce XLSX e (con `?pdf=1`) un PDF con le 7
   colonne; `exports/fondimpresa` produce 9 colonne con il codice sessione uguale allo `uuid` della lezione.
3. `exports/lazio` restituisce un PDF con un foglio per lezione, intestazione «Registro formativo e delle presenze
   online», le 6 colonne del Modello B e l'argomento della lezione.
4. `GET classrooms/{id}/exports` (Inertia `classrooms/exports`) espone `formats[]` con `{key, label, available,
missing[]}`; per un'aula senza fondo `available = false` su tutti tranne `anpal`; ogni download crea una riga in
   `export_logs`; un utente di un'altra company riceve 404.
5. `GET classrooms/{id}/exports/inspection.pdf` contiene il nome della piattaforma, l'URL dell'app, la sezione
   «Accesso per le verifiche» e l'elenco delle lezioni con il codice riunione.
6. `composer test`, `npm run types:check`, `npm run check`, `npm run build` passano; nessuna riga di `export_logs`
   o di log contiene codici fiscali.

## Out of scope

Firma digitale qualificata e marca temporale (integrazione con un trust provider, giro successivo), cattura
dell'indirizzo IP e export Puglia, PDF sui modelli cartacei degli enti (Fondimpresa 7/8/10/12, Piemonte), archivio
mensile firmato (conservazione), template FPF e GE.CO. dietro login, cooperazione applicativa Lombardia, Fonservizi
e Fondir (varianti minori da aggiungere dopo il primo feedback dei clienti).
