Document generation a scala: PDF, Word e PowerPoint dinamici da pipeline ecommerce
- automation
- documents
- ecommerce
In quasi tutti i progetti ecommerce o B2B che vediamo, prima o poi arriva la stessa richiesta: “Possiamo generare il contratto/la bolla/il report automaticamente?”. La risposta tecnica è banale — sì, si può. La risposta utile è un’altra: con quale architettura, perché altrimenti ti ritrovi con sei script diversi che generano sei tipi di documento, ognuno con il proprio bug e nessuna fonte di verità condivisa sul branding.
In questo articolo racconto come strutturiamo una pipeline di document generation che gestisce sei tipi diversi di documento — payout statement, contratti, bolle, condition report con testo AI, snapshot di tecnical docs e brochure commerciali — partendo da un unico template engine. L’esempio viene da un progetto nel settore della resale di mobili, ma il pattern si applica a qualunque pipeline ecommerce o B2B.
Perché serve un template engine, non sei script
L’errore più comune è iniziare con uno script Puppeteer per il primo PDF, poi uno script wkhtmltopdf per il secondo perché serviva una feature diversa, poi una libreria per il PowerPoint perché “ci servono le slide per le presentazioni vendita”. Dopo sei mesi hai sei toolchain, ognuna con dipendenze sue, e ogni cambio di logo richiede sei pull request.
L’approccio che funziona è un template engine unico che produce output diversi a partire dalla stessa fonte di dati. Nel nostro stack: una Edge Function Supabase per ogni tipo di documento, ma tutte chiamano lo stesso layer di rendering. Il layer di rendering riceve { template_id, data, output_format } e ritorna il binario. I template sono HTML+CSS versionati nel repo, con partial condivisi per header, footer, blocchi prezzo, tabelle.
Sembra ovvio detto così. Non lo è quando hai la pressione di shippare la prima feature di settimana prossima.
I sei tipi di documento
Nel progetto reference che racconto, la pipeline genera:
1. Settlement PDF — statement di payout per il venditore. Riepilogo vendite del periodo, commissioni, tasse, totale liquidato. Generato on-demand quando il venditore apre il proprio portale, o automaticamente al passaggio di stato eligible → paid.
2. Agreement PDF — contratto di conto vendita firmato in fase di onboarding. Dati venditore + clausole + listino commissioni applicato. Il template ha una sezione clausole che cambia per giurisdizione (IT/UK/DE) mantenendo lo stesso layout.
3. Collection PDF — istruzioni di ritiro per il logistic partner. Indirizzo, contatto, foto dei pezzi, note di accesso. Generato quando lo stato del listing passa a ready_for_collection.
4. Delivery Note — bolla di consegna. Dati cliente, dettaglio item, condizioni di consegna. Generato al passaggio in shipped.
5. Condition Report — report di QC con foto + testo descrittivo generato da Gemini. Questo è il caso più interessante: il flusso passa per AI (descrizione condizioni dalle foto + note del QC), poi un “voice gate” verifica che il testo rispetti il tone of voice prima di iniettarlo nel template PDF.
6. Tech Docs Snapshot — documentazione tecnica admin. Snapshot di configurazioni, schema database, lista di edge functions. Generato on-demand dall’admin per audit interni. Output sia PDF sia Markdown.
A questa lista aggiungiamo, per altri progetti, brochure commerciali in formato PPTX o DOCX — utili quando il team commerciale vuole personalizzare le slide dopo la generazione.
Il pattern: data layer → template engine → format adapter
L’architettura che ha retto bene è in tre strati:
Data layer. Una vista SQL o una RPC che, dato l’ID dell’entità (settlement, ordine, listing), ritorna un JSON denormalizzato con tutti i dati necessari al documento. È l’unica fonte di verità: se la fattura mostra un totale, lo prende da qui. Vantaggio: se cambia una regola di calcolo, cambia in un posto solo.
Template engine. Riceve { template_id, data } e ritorna HTML pre-renderizzato. Usiamo template Handlebars con partial condivisi. I template vivono nel repo, versionati come il codice. Ogni cambio di template è un PR con preview automatica.
Format adapter. Trasforma l’HTML in PDF (Puppeteer + Chromium headless), DOCX o PPTX (libreria pandoc o convertitori dedicati). Lo strato adapter è l’unico che cambia per formato; il template e i dati restano gli stessi.
Un dettaglio che fa la differenza: il rendering PDF gira in una Edge Function dedicata con un pool di browser headless preinizializzati, perché lo spin-up di Chromium a freddo costa 2-3 secondi. Con il pool, la P95 di generazione di un settlement PDF resta sotto 800ms.
AI nel template: il caso Condition Report
Il Condition Report è l’unico documento dove l’AI scrive testo che finisce in un PDF firmato dal team QC. È anche il caso più rischioso: un testo off-brand o sbagliato si stampa con la stessa autorevolezza del resto. La pipeline è:
- Input: foto del pezzo + note strutturate del QC operator (graffi, integrità materiali, condizione superficie).
- Gemini riceve un prompt con few-shot examples di descrizioni accettabili e genera una bozza.
- Voice gate: una funzione di eval verifica banned words, tone score (su un set di criteri brand), e regressione contro un test set di output noti.
- Solo se il voice gate passa, il testo entra nel template. Altrimenti torna in coda per review manuale.
Il voice gate è la cosa che permette di dormire la notte. Senza, un singolo prompt drift può iniettare testo sbagliato in 200 documenti prima che qualcuno se ne accorga.
Tech docs snapshot: documenti generati dal codice
L’ultimo tipo merita una nota a parte. Per progetti complessi — quando hai 100+ edge functions, RLS policy multiple, cron job schedulati — la documentazione tecnica scritta a mano è obsoleta nel momento in cui la pubblichi. Lo snapshot funziona così: una Edge Function legge metadata da Postgres (pg_cron.job, pg_proc, pg_policy), introspetta le edge functions deployate, e genera un PDF/Markdown che è la fotografia esatta del sistema.
Lo usiamo per audit di sicurezza e per onboarding di nuovi sviluppatori. Il fatto che sia rigenerato a comando — e quindi sempre allineato — toglie la categoria “doc obsolete” dal backlog.
La metrica realistica
Su un progetto a volume medio (circa 1.500 documenti/mese tra payout, bolle e contratti), questa architettura tiene questi numeri:
- Tempo di generazione PDF P95: 650-800ms
- Tempo di refactor dopo cambio di logo/brand: una PR su un partial, propagata a tutti i sei tipi.
- Costo infra per documenti generati: nell’ordine di pochi centesimi di euro al mese per i volumi indicati.
Niente di magico. La cosa che fa la differenza non è la performance — è il fatto che dopo sei mesi continui ad aggiungere nuovi tipi di documento senza moltiplicare debito tecnico.
Quando partire da qui
Se stai per generare il tuo secondo tipo di PDF dinamico, è il momento di fermarsi e pensare al template engine condiviso. Se sei già a quattro script diversi, il refactor è doloroso ma paga in tre mesi. Se generi un solo documento e non ne prevedi altri, va benissimo Puppeteer inline — non sovra-ingegnerizzare.
La domanda da farsi non è “che libreria uso”, ma “tra sei mesi quanti tipi di documento avrò, e quanto mi costa cambiare il logo?”. La risposta a quella domanda ti dice se ti serve l’architettura sopra o no.