# Log Attività (audit log) + campo Azienda utente — design

Data: 2026-07-21. Richiesta cliente post-call, approvata da Inlumia (voce E dei lavori extra, 1 giornata). Era tra le esclusioni del preventivo originale ("audit log").

## Decisioni approvate da Paolo

1. **Dettaglio valore: solo quale campo.** Il log registra *quale* campo è stato modificato (etichetta leggibile), non il valore vecchio né il nuovo. È il minimo indispensabile chiesto dal cliente (colonne: data, ora, azienda, utente, sede, campo modificato).
2. **Campo Azienda: testo libero** sulla scheda utente. Nessuna lista gestita.
3. **Cosa loggare: solo modifiche ai DVR** (ogni campo/tendina/checkbox dei DVR + allegati). Niente login/logout.

## Scope

Loggiamo ogni scrittura di contenuto DVR nei 9 punti di scrittura mappati:
- Form actions in `agenzie/[id]/dvr/modifica/+page.server.ts`: `saveInquadramento`, `saveRischioIncendio`, `risolviAttivita`.
- Endpoint PATCH/POST/DELETE `api/dvr/**`: `pericoli/[id]`, `valutazione/[id]`, `valutazione-misura/[id]` (PATCH+DELETE), `valutazione-misura` (POST), `incendio/[id]`, `piano/[id]` (oggi orfano, coperto per il futuro).
- Allegati in `agenzie/[id]/+page.server.ts`: `upload`, `delete`, `replace`.

Le attività di miglioramento sono derivate/auto-generate: NON si loggano come voci a sé. La loro causa (modifica a pericolo/valutazione) è già loggata. La **risoluzione manuale** (`risolviAttivita`) è un gesto utente e si logga.

Fuori scope: valori prima/dopo, vista diff, login, retention/pulizia automatica.

## Schema

Nuova colonna su `utenti`: `azienda TEXT` (nullable).

Nuova tabella `audit_log`:

| Colonna | Tipo | Note |
|---|---|---|
| id | INTEGER PK autoincrement | |
| user_id | INTEGER FK utenti(id) ON DELETE SET NULL | chi ha fatto la modifica |
| utente | TEXT | snapshot (nome o email) per sopravvivere alla cancellazione utente |
| azienda | TEXT | snapshot dell'azienda dell'utente al momento |
| agenzia_id | INTEGER FK agenzie(id) ON DELETE SET NULL | sede master del DVR |
| entita | TEXT NOT NULL | area DVR: inquadramento / pericoli / valutazione / incendio / allegati / attivita |
| campo | TEXT NOT NULL | etichetta leggibile del campo modificato |
| created_at | TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP | |

Indici: `(created_at)`, `(user_id)`, `(agenzia_id)`.

Migrazione: `app/migrations/2026-07-21-audit-log-e-azienda.sql` (idempotente). Applicata a dev via db:push/sqlite; su prod la lancia Paolo (client libsql da `.env.pdf.local`).

## Helper centralizzato

`app/src/lib/server/dvr-log.ts`:
```
logModificaDvr({ user, agenziaId, entita, campo }): Promise<void>
```
- `user` = `locals.user` (id, nome/email, azienda dalla sessione o riletta).
- `agenziaId` = sede master del DVR (risolta via riga → dvr_id → dvr.agenzia_id).
- `entita` + `campo` = area + etichetta leggibile.
- Non deve MAI far fallire la scrittura DVR: errori del log si inghiottono (try/catch, best-effort).

Etichette leggibili per area:
- pericoli: `rif` + `requisito` da `lookup_pericoli_punti` (via `punto_id` della riga) + quale colonna (Uffici/Pertinenze/Verifica/Note).
- valutazione / valutazione-misura: nome elemento da `lookup_valutazione_elementi` + campo (Probabilità/Gravità/Situazione/Misure).
- incendio: nome documento da `lookup_documenti_incendio` + campo (Stato/Note).
- inquadramento: mappa statica campo→label ("Descrizione immobile", "Piano emergenze", ecc.).
- allegati: tipo allegato leggibile + azione (caricato/sostituito/eliminato).
- attivita (risoluzione): "Attività risolta" + eventuale origine.

Serve mettere l'azienda dell'utente nella sessione (`SessionUser.azienda`) così il log la ha senza query extra: aggiungere `azienda` a `auth.ts` (SessionUser + createSession + readSession) e in `login/+page.server.ts`.

## Schermata Log Attività

Route `app/src/routes/report/log-attivita/`:
- `+page.server.ts`: `canSee` = developer|super_admin|admin (tutti); load da `$lib/server/log-attivita.ts`.
- `+page.svelte`: tabella (data/ora, azienda, utente, sede con link, campo), ricerca + filtri (azienda/utente/sede), ordinata dal più recente. Read-only.
- `csv/+server.ts`: export CSV (stesso pattern di incongruenze, `;` + BOM).
- `$lib/server/log-attivita.ts`: query `audit_log` join `agenzie` (nome sede), ordinata `created_at DESC`, con LIMIT ragionevole (es. 1000) + eventuale filtro.

Voce menu "Log Attività" in `+layout.svelte`, fuori da qualsiasi `{#if}` (visibile a tutti), accanto a "Report incongruenze".

## Gestione Azienda utente

In `utenti/+page.svelte` + `+page.server.ts`: campo Azienda nel form create, colonna in tabella, e action `changeAzienda` con edit inline (modello `changeRuolo`).

## Verifica

svelte-check 0 errori; prova funzionale: fare una modifica a un DVR in locale e controllare che compaia una riga nel Log; export CSV.
