# Export modifiche anagrafiche utente

## Scopo

Ogni volta che l'anagrafica di un utente viene **modificata**, il sistema genera
un file di testo `.txt` che descrive *cosa* e' cambiato, lo salva nello storage
del progetto e lo invia via FTP allo **stesso server FTP** da cui un altro modulo
recupera l'Excel. Il ricevente puo' cosi' allineare i propri dati alle modifiche
effettuate nel gestionale / PWA.

## Quando scatta

Il modulo e' implementato come **Observer Eloquent** sul modello `User`:

- Classe: `app/Observers/UserObserver.php`
- Registrazione: `app/Providers/AppServiceProvider.php` (metodo `boot()`),
  tramite `User::observe(UserObserver::class)`.

Essendo un Observer sull'evento `updated`, scatta **indipendentemente da chi**
effettua la modifica:

- l'utente stesso dalla PWA,
- un amministratore dal backend.

Non scatta in fase di creazione o cancellazione, ne' se nessun campo **anagrafico**
viene modificato (es. solo il login o la password). In tal caso non viene prodotto
alcun file.

## Campi tracciati

Vengono tracciati solo i campi **anagrafici**. I campi tecnici
(`password`, `remember_token`, `*_at` tecnici, `riga_excel`, `type`, `stato`,
`last_login_at`, ecc.) sono esclusi.

Elenco dei campi monitorati:

```
username, name, surname, titolo,
email, email01, email02, email03, pec01, pec02,
phone, cellulare02, cellulare03, telefono01, telefono02, tel_casa_01, tel_casa_02,
indirizzo, cap, citta, prov, nazione,
codice_fiscale, luogo_nascita, data_nascita,
partita_iva, codice_univoco, iban
```

I valori **precedenti** sono letti da `$user->getOriginal()`, i valori **nuovi**
da `$user->getChanges()`.

## Formato del file

Il file e' un testo UTF-8 con terminatore di riga `\n` e due sezioni:

1. **Intestazione** con i dati identificativi dell'utente, l'autore della
   modifica e il timestamp.
2. **Sezione leggibile** (`--- CAMPI MODIFICATI (leggibile) ---`): una riga per
   campo nel formato `Etichetta [NomeCampoExcel]: <vecchio> => <nuovo>`.
   I valori vuoti/null sono resi come `(vuoto)`.
3. **Sezione parsabile** (`--- DATI PARSABILI (NomeCampoExcel|DA|A) ---`): una riga
   per campo nel formato `NomeCampoExcel|valore_da|valore_a`, pensata per
   l'elaborazione automatica lato ricevente. I caratteri `|`, `\r` e `\n`
   eventualmente presenti nei valori vengono sostituiti da uno spazio per non
   rompere il parsing.

### Nomi dei campi: NomeCampoExcel (foglio Mappatura/Anagrafica)

I campi NON sono identificati dai nomi delle colonne del DB, ma dai **nomi-colonna
del file Excel di transfer** (così il ricevente puo' ri-allineare/ri-importare i
dati). I nomi corrispondono alle intestazioni del foglio **Anagrafica** (colonne
`User_*`), coerenti con il foglio **Mappatura Campi**.

La mappa `campo DB -> NomeCampoExcel` e' definita in
`app/Observers/UserObserver.php` (proprieta' `$excelNames`):

| Campo DB        | NomeCampoExcel    |
|-----------------|-------------------|
| username        | User_Name         |
| name            | User_Nome         |
| surname         | User_Cognome      |
| titolo          | User_Titolo       |
| email           | User_Mail01       |
| email01         | User_Mail01       |
| email02         | User_Mail02       |
| email03         | User_Mail03       |
| pec01           | User_Pec01        |
| pec02           | User_Pec02        |
| phone           | User_Cellulare01  |
| cellulare02     | User_Cellulare02  |
| cellulare03     | User_Cellulare03  |
| telefono01      | User_Tel_Uff_01   |
| telefono02      | User_Tel_Uff_02   |
| tel_casa_01     | User_TelCasa01    |
| tel_casa_02     | User_TelCasa02    |
| indirizzo       | User_Indirizzo    |
| cap             | User_CAP          |
| citta           | User_Città        |
| prov            | Prov.             |
| nazione         | User_Paese        |
| codice_fiscale  | CodiceFiscale     |
| luogo_nascita   | Luogo di Nascita  |
| data_nascita    | Data di Nascita   |
| partita_iva     | Partita Iva       |
| codice_univoco  | Codice Univoco    |
| iban            | IBAN              |

> Nota: la tabella sopra vale per **proprietari/conduttori** (foglio *Anagrafica*).
> Per gli utenti **amministratore** le colonne canoniche sono quelle del foglio
> *Amministratori* (`admin_*`); se servira' tracciarli con quei nomi, aggiungere
> una variante di `$excelNames` selezionata in base a `User->type`.

L'autore della modifica e' `auth()->user()` quando presente
(`Nome Cognome (ruolo: <type>, id: <id>)`), altrimenti la stringa `sistema`
(es. modifiche da comando/seed/job senza utente autenticato).

### Esempio completo

```
==================================================
MODIFICA ANAGRAFICA UTENTE
==================================================
Utente ID  : 42
Nominativo : Mario Rossi
Email      : mario.rossi@example.com
Modificato da : Anna Bianchi (ruolo: admin, id: 1)
Data/ora   : 2026-06-25 13:07:42

--- CAMPI MODIFICATI (leggibile) ---
Email 1 [User_Mail01]: mario.rossi@old.it => mario.rossi@example.com
Telefono [User_Cellulare01]: (vuoto) => 0123456789
Citta [User_Città]: Roma => Milano

--- DATI PARSABILI (NomeCampoExcel|DA|A) ---
User_Mail01|mario.rossi@old.it|mario.rossi@example.com
User_Cellulare01||0123456789
User_Città|Roma|Milano
==================================================
```

## Naming dei file

```
anagrafica_user{ID}_YYYYmmdd_His.txt
```

Esempio: `anagrafica_user42_20260625_130742.txt`

- `{ID}` = chiave primaria dell'utente modificato;
- `YYYYmmdd_His` = data e ora (`now()`) della modifica.

## Dove vengono salvati

1. **Storage del progetto** (sempre):
   `storage/app/anagrafica-changes/` tramite `Storage::disk('local')`.

2. **FTP** (disk `ftp_studio`, cartella `outgoing/`):
   `Storage::disk('ftp_studio')->put('outgoing/<file>', ...)`.

L'invio FTP e' racchiuso in `try/catch` con logging: se l'FTP non risponde,
l'errore viene loggato ma **non** interrompe il salvataggio dell'utente.

## Configurazione FTP (.env)

Il disk `ftp_studio` e' definito in `config/filesystems.php` ed e' condiviso con
il modulo di import dell'Excel. Variabili di ambiente:

```dotenv
FTP_HOST=ftp.studio.example.com
FTP_USERNAME=utente
FTP_PASSWORD=segreta
FTP_PORT=21
FTP_ROOT=/                # cartella radice sul server FTP
FTP_PASSIVE=true          # opzionale (default true)
FTP_SSL=false             # opzionale (default false)
FTP_TIMEOUT=30            # opzionale (secondi, default 30)
```

## Comportamento di fallback (simulazione locale)

Quando `FTP_HOST` **non e' valorizzato** (ambiente di sviluppo o FTP non ancora
configurato), il modulo non tenta la connessione FTP ma scrive il file nella
**simulazione locale**:

```
storage/app/ftp-sim/outgoing/
```

La cartella `storage/app/ftp-sim/` e' la stessa usata dal modulo di import, che
utilizza `storage/app/ftp-sim/incoming/` e `storage/app/ftp-sim/processed/`.
In questo modo i due moduli condividono la stessa convenzione di simulazione.
