# Alleanza DVR — Guida di progetto

Progetto gestionale web per la compilazione, gestione e produzione dei DVR (Documento di Valutazione dei Rischi, D.Lgs. 81/08) delle ~800 sedi Alleanza Assicurazioni. Cliente arrivato tramite **Inlumia**, sviluppato da **Paolo**.

## Stato del progetto

- **Il gestionale è in produzione e il cliente lo usa**: https://alleanza-dvr.vercel.app. Le 806 sedi sono caricate, i DVR compilabili, i PDF generati.
- **Chi ci lavora dall'altra parte**: lo studio CesanaStudioArch e Inlumia. Le segnalazioni arrivano a Paolo per file (Word o PDF con screenshot), raramente via chiamata.
- **Cosa si fa adesso** (da luglio 2026): correggere i difetti dell'import iniziale, che ha perso o spostato dati, e lavorare le richieste che nascono dall'uso quotidiano. Non si costruiscono più funzionalità nuove salvo richiesta esplicita.
- **Re-seed v18 lanciato il 2026-09-14** (806 PDF via GitHub Action, poi `app/pregenera_zip.mjs --apply` per i 20 ZIP). Lo stesso giorno: corretti i difetti emersi dal tracker IA del cliente (Piano dai Word, P=G nella Valutazione Word, 1.6.3.2, flag idro/ambientale) e la lista agenzie ora mostra Scarica/Rigenera in base ai dati attuali.
- **Extra oltre il preventivo**: tracciati in `docs/06_extra_post_preventivo.md`. I fix dei nostri difetti d'import NON si fatturano.

## Perimetro in 3 macro-consegne

1. **Database + import**: caricare nel DB le ~800 agenzie dai ~600 xlsx + ~200 docx esistenti (eterogenei). L'import è fatto a mano con Claude Code — **non dichiarato così al cliente**: nel preventivo è una voce forfait da 10 giornate.
2. **Gestionale web**: CRUD agenzie + compilazione DVR (sostituisce completamente Excel/Word). 3 ruoli utenti.
3. **Generazione PDF**: DVR in PDF da inoltrare al software di firma elettronica esistente del cliente (non nostro).

### Fuori perimetro (dichiarato nel preventivo)

Firma elettronica · revisioning/storico DVR · audit log · notifiche scadenze · integrazioni SSO · export Word/Excel · editor planimetrie · versione mobile smartphone · multilingua · manutenzione post-rilascio. Ogni nuova richiesta = nuovo preventivo separato.

## Economics

| Voce | Valore |
|---|---|
| Giornate totali | 40 |
| Tariffa giornaliera nominale | 130 €/gg |
| Subtotale | € 5.200 |
| Sconto applicato | − € 1.200 |
| **Netto cliente** | **€ 4.000** |
| Acconto 30% all'avvio | € 1.200 |
| Saldo 70% a consegna | € 2.800 |

## Stack tecnico (cambiato il 2026-05-07)

| Area | Scelta |
|---|---|
| Framework | **SvelteKit** full-stack (frontend + endpoint server in TypeScript) |
| Database | **Turso** (libSQL/SQLite gestito, regione EU) |
| ORM/migrazioni | **Drizzle ORM** (schema-as-code, migrate tramite `drizzle-kit`) |
| Auth | **Lucia v3** (oppure custom JWT con `jose` + cookie httpOnly negli `hooks.server.ts`) |
| Ruoli | 3 livelli: `developer`, `super_admin`, `admin` |
| PDF | **Da decidere quando arriva il design Inlumia** — opzioni: `@sparticuz/chromium`+`puppeteer-core` su Vercel Pro, `pdfmake`, `pdf-lib` |
| Hosting | **Vercel Hobby (free)** sull'account personale di Paolo. Se serve Pro (20€/mese), il cliente rimborsa. |
| Sviluppo | In locale sul NAS (`/Users/paolo/Server/Siti Web/Lavoro/Inlumia/Alleanza/app/`) — `turso dev` per DB locale, repo Git da creare e push su Vercel quando pronto |
| Compatibilità | Desktop + tablet, browser moderni. Mobile escluso. |

### Cosa è cambiato e perché
Stack iniziale (Register.it shared hosting + PHP REST + MySQL + JWT pattern Gibertini) abbandonato il 2026-05-07 perché:
- Vercel Hobby copre i volumi (zero costo per il cliente almeno fino al go-live).
- SvelteKit unifica frontend e API in un'unica codebase TS → meno boilerplate, meno punti di rottura per un progetto da 40 giornate.
- Turso edge (regione EU) è GDPR-compliant e ha free tier sovrabbondante per i nostri volumi (~1-2 GB a regime).
- Niente più dipendenza da hosting cliente significa setup immediato e niente FTP.

### Progetti di riferimento (NON più direttamente riusabili)
I progetti `Gibertini/`, `Benza/prev_vasche/`, `Benza/ordini_clienti_26/`, `Benza/sito_26/` sono tutti **PHP+MySQL** su Register.it. Restano utili come **riferimento concettuale** per i pattern (3 ruoli auth, struttura CRUD per entità, generazione PDF) ma il codice si riscrive in TypeScript/SvelteKit.

## File nel repository di progetto

```
Alleanza/
├── CLAUDE.md                              ← questo file
├── README.md
├── .gitignore                             ← esclude -mat/, storage/, *.db
├── -mat/                                  ← materiale cliente (gitignored, NON modificare)
│   ├── 07_DVR AGENZIE_TWISTER.xlsx        ← master DVR (13 fogli)
│   ├── ANAGRAFICA_SEDI ALLEANZA_struttura.xlsx     ← v1 (consegnata 2026-05-06)
│   ├── ANAGRAFICA_SEDI ALLEANZA_struttura_v2.xlsx  ← v2 con CODICE IMMOBILE (2026-05-07)
│   ├── DVR_Stampato.pdf                   ← esempio PDF DVR stampato
│   ├── DVR/                               ← 800 file DVR per-sede (603 xlsx + 197 docx)
│   ├── brief_dvr_claude_code.md           ← brief Perplexity
│   └── note per precedente agenzia software.rtf  ← difetti precedente fornitore
├── storage/                               ← (gitignored) 4.795 immagini estratte dai DVR
│   └── agenzie/<codice_fisico>/           ← 794 cartelle, una per sede
├── docs/                                  ← documenti di lavoro
│   ├── 01_dizionario_master.md
│   ├── 02_schema_db.md / .sql             ← (legacy, da rifare in Drizzle)
│   ├── 03_import_report.md                ← report import dati 2026-05-07
│   ├── 04_preventivo_draft.md
│   ├── 05_preventivo_cliente.docx         ← preventivo approvato dal cliente
│   └── 06_extra_post_preventivo.md        ← lavori extra da fatturare a giornate
├── import-tools/                          ← strumenti import una tantum (archive, vedi sua README)
│   ├── README.md
│   ├── db/schema.sql                      ← schema SQLite usato per l'import
│   └── scripts/                           ← import una tantum + strumenti di controllo:
│                                             audit_docx.py (rilegge i Word per confronto),
│                                             estrai_template_master.py, estrai_valutazione_master.py
└── app/                                   ← gestionale SvelteKit, in produzione su Vercel
    ├── src/                               ← il gestionale
    ├── scripts/pdf/                       ← generazione PDF (data.ts, template.ts, generate.ts)
    ├── fix-*.mjs                          ← correzioni dati una tantum, tutte con dry-run e TARGET_DB
    └── pregenera_zip.mjs                  ← ZIP per regione (TEMPLATE_VERSION allineata a fingerprint.ts)
```

**Nota struttura**: gli script di `import-tools/` non fanno parte del runtime del gestionale, non vanno deployati su Vercel. L'import vero e proprio è stato eseguito una sola volta il 2026-05-07; Non fanno parte del runtime del gestionale: vivono separati apposta perché non vanno deployati su Vercel e non vanno in confusione con il codice TypeScript di SvelteKit. Si rilanciano solo se serve rifare un import da zero (vedi `import-tools/README.md`).

## Materiale cliente — highlight

**File master TWISTER** — 13 fogli. Vedi `docs/01_dizionario_master.md` per il dettaglio completo. Riassunto:
- Fogli documento per-sede: Copertina, Dati (anagrafica), Pericoli (~100 punti), Valutazione (~30 elementi), Rischi, Incendio, Conclusione
- Fogli lookup/seed: ListaF1, ListaF2, Lista F3, Categorie Protette
- Foglio **Sismica**: contiene l'anagrafica reale di 240 sedi gestite da Twister (codice agenzia, indirizzo, mq, piano, impresa sopralluogo, zona sismica, rischi frane/alluvioni/ambientale). Resta utile come *fonte storica* per le 240 sedi Twister (campo "impresa sopralluogo" non presente nell'anagrafica master).

**File `ANAGRAFICA_SEDI ALLEANZA_struttura.xlsx`** — la fonte autoritativa per le 800 sedi. Foglio principale `ORG. SEDI FISICHE RETE ALLEANZA`, 16 colonne, 796 sedi reali (379 AG.GEN. + 414 ISP.AG. + 3 ISP.REG). Colonne:
1. `Codice FisicoC` — chiave fisica univoca per sede (formato: `{cod}_ _` per AG.GEN, `{cod}{suffisso2}` per ISP.AG)
2. `Codice Agenzia` — intero, condiviso tra le sedi della stessa "agenzia generale" (raggruppamento territoriale)
3. `Tipologia` — `AG. GEN.` | `ISP. AG.` | `ISP. REG`
4. `NOME AGENZIA` — nome del raggruppamento (es. "ABANO TERME")
5. `NOME ISP. AG.` — nome della sede specifica (es. "MESTRINO" per ISP.AG, "AGENZIA GENERALE" per AG.GEN. — entrambi con whitespace trailing da normalizzare)
6-9. Indirizzo, CAP, Città, Provincia
10. `REGIONE GEOGRAFICA` — regione amministrativa italiana (Veneto, Lombardia, …)
11. `INT` — area macro (NORD/CENTRO/SUD)
12. `Regione ALLEANZA` — 12 regioni interne Alleanza (es. "VENETO FRIULI V.G.", "PUGLIA")
13-14. Latitudine, Longitudine

**Cartella `-mat/DVR/`** — 800 file (603 xlsx + 197 docx). Pattern naming: `{NOME_AGENZIA}_{SEDE}_Rev{NN}_{DDMMYYYY}.{ext}`. Per AG.GEN il segmento SEDE è "CENTRO AGENZIALE" (mappa al `NOME ISP. AG.` "AGENZIA GENERALE"). Ogni xlsx ha la stessa struttura del master TWISTER (a volte con un foglio "Foglio1" extra). I docx contengono i medesimi dati del DVR ma in forma di tabelle Word (paragrafi + 18 tabelle circa).

**File `DVR_Stampato.pdf`** — esempio del PDF stampato come "lo vorrebbero" oggi. Riferimento per design output, non bloccante (il design definitivo arriverà da Inlumia).

**Note RTF "precedente agenzia software"** — i difetti specifici del fornitore precedente (combo troppo granulari, colori celle sbagliati, import incompleto, "Indicazione luogo sicuro esterno" mancante, responsabile piano miglioramento non precompilato). Sono tutti punti che risolviamo nel nostro design.

## Regole di business specifiche del cliente (dal brief)

- Campo **Piano** = combo editabile con valori: Terra, Rialzato, 1, 2, 3, 4, 5, 6, 7, 8, "2, 3", "3, 4".
- **Responsabile** del piano di miglioramento = sempre `"Operation di rete - Alleanza Assicurazioni"` (precompilato).
- Nel DVR va **aggiunta la riga** "Indicazione luogo sicuro esterno" (manca nel template attuale).
- **Combo "uniche" accorpate** richieste dal cliente sui punti: 1.4.1, 1.5.1, 1.5.6, 1.5.7, 1.6.10, 1.7.2.1 e sottopunti, 1.9.2.1, 1.13.3.2 (dettaglio in `docs/01_dizionario_master.md`).
- `Valutazione.R = P × G` sempre calcolato, mai editabile.
- In Valutazione colonna H: **multi-select con flag** (più voci selezionabili contemporaneamente).
- Colore celle ha semantica: bianco = compilato, giallo = default da modificare, grigio = combo con anteprima, verde = fisso/derivato, viola = combo obbligatoria. Da tradurre in stati UI.
- Allegati caricati una volta in anagrafica e richiamati automaticamente nei punti corretti del DVR (planimetria emergenza + luogo sicuro esterno).
- Planimetrie e foto oggi sono **immagini embedded negli xlsx**: in fase import vanno estratte (via openpyxl o ZipArchive sul file xlsx).

## Regole operative con Paolo (imparate lavorando)

- **Il cliente non è tecnico**: nei documenti per lui non mettere dettagli tecnici (nomi tabelle, stack, librerie). Linguaggio business.
- **Paolo corregge spesso il tiro**: procedere a piccoli step, mostrare output, aspettare feedback prima di andare avanti con lavori lunghi.
- **Strategia sconto**: preventivo presentato con tariffa nominale più alta + sconto, per arrivare al target pattuito (qui 4.000€). Trasparenza sull'articolazione per fase.
- **Niente manutenzione inclusa** di default: ogni nuova richiesta = nuovo preventivo separato.
- **Firma**: solo Paolo, non Inlumia.
- **Formato documenti cliente**: Word (.docx), così Paolo può editare prima di inviare.

## Domande aperte al cliente

| # | Stato | Domanda |
|---|---|---|
| 1 | ✅ risposto 2026-05-06 | Anagrafica completa 800 sedi: ricevuta (`ANAGRAFICA_SEDI ALLEANZA_struttura.xlsx`) |
| 2 | ⏳ in attesa | Design grafico PDF: tempistica di Inlumia |
| 3 | ✅ risposto 2026-05-11 | Datore di Lavoro / RSPP: costanti per tutta Alleanza. Realizziamo **pannello di configurazione globale** dove l'utente li imposta una volta e vengono usati su tutti i DVR (editabili in qualsiasi momento) |
| 4 | ✅ risposto 2026-05-11 | Planimetrie e luogo sicuro: per ora usiamo le **JPG già estratte dagli xlsx**. Eventuale upload PDF posticipato a fase 2 |
| 5 | ⚪ N/A | Hosting: ora su Vercel di Paolo, problema chiuso |
| 6 | ⚪ N/A (2026-05-11) | Ispettorati regionali: cliente conferma che nel DVR non servono |
| 7 | ✅ risposto 2026-05-11 | Testo "Piano di gestione emergenze": cliente conferma di tenere quello del file mastro (identico nei 599 DVR esistenti, 764 char) come default precompilato non modificabile |
| 8 | ✅ risposto 2026-05-11 | 4 agenzie accorpate: anche se accorpate, ognuna mantiene il proprio DVR personale. Nessun problema: il gestionale è 1 DVR per agenzia, non servono DVR multi-sede |
| 9 | ⏳ 2026-09-04 | Voce "Impianto non attivo" su Esplosioni: l'hanno chiesta con punteggio 1-1-1, che genera un'attività Trascurabile. Se per loro vuol dire "niente da fare", il punteggio giusto sarebbe 0-1-0 |
| 10 | ✅ 2026-09-14 | Rigenerazione degli 806 PDF lanciata (v18) |
| 11 | ⏳ 2026-09-14 | Per ≥12 sedi (Rovereto Riva, Como, Praia a Mare, Firenze Centro, Persiceto, Rimini, Gallipoli, Grezzana…) il cliente ha DVR più recenti dei file consegnati: devono mandarli o aggiornare nel gestionale |

## Come si lavora su questo progetto

**I fix ai dati si provano prima su una copia della produzione, poi si applicano.** Mai il contrario.

```bash
# copia locale della produzione
turso db shell alleanza-dvr-prod ".dump" > prod_dump.sql && sqlite3 test.db < prod_dump.sql
# gestionale sulla copia (i cookie di sessione valgono su tutte le porte di localhost)
DATABASE_URL="file:/percorso/test.db" npm run dev -- --port 5180
# un PDF in locale, senza R2
DATABASE_URL="file:/percorso/test.db" npx tsx scripts/pdf/generate.ts <agenziaId>
```

Gli script in `app/fix-*.mjs` seguono tutti lo stesso schema: dry-run se lanciati nudi, `--apply` per scrivere, `TARGET_DB=` per lavorare sulla copia. Prima di scrivere controllano il Log Attività (`audit_log`) e saltano i campi che qualcuno ha già modificato a mano nel gestionale.

**Leggere i file .docx**: usare `row._tr.tc_lst`, mai `row.cells`. Con le celle unite python-docx duplica il contenuto e sfalsa le colonne: è l'origine di quasi tutti i difetti dell'import di maggio. E quando si confronta una sorgente col database, guardare il **contenuto** delle righe, non solo che esistano.

**Pericoli e Valutazione sono indipendenti**: non si aggiornano a vicenda, ma entrambi generano attività di miglioramento. Il cliente ha capito il contrario dopo una call e va chiarito.

## Pipeline import (archive in `import-tools/`)

Già eseguita il 2026-05-07. **Non rilanciare** salvo necessità di rifare import da zero. Comandi:

```bash
python3 import-tools/scripts/reset_db.py
python3 import-tools/scripts/import_anagrafica.py
python3 import-tools/scripts/import_seed_master.py
python3 import-tools/scripts/import_dvr.py --all
python3 import-tools/scripts/rematch_no_match.py --apply
python3 import-tools/scripts/extract_images.py --reset
python3 import-tools/scripts/classify_images.py
```

Dettagli in `import-tools/README.md` e `docs/03_import_report.md`.

## Note sul brief_dvr_claude_code.md (Perplexity)

I due file `brief_dvr_claude_code.md` e `brief_dvr_claude_code 2.md` in `-mat/` sono **bit-a-bit identici** (verificato con diff). Perplexity non aggiornava il file. Se il cliente manda ulteriori versioni, controllare con `diff` prima di assumerle diverse.
