# DD-050 — Avviso a admin e allenatori se una presenza cambia a meno di 6 ore dall'evento **Data:** 8 ottobre 2026 **Stato:** Implementata per admin e allenatori (migration `m32`, solo admin, applicata in produzione il 9 ottobre 2026; `m33`, anche allenatori) **Codice:** `supabase/migrations/20261009120000_m32_presenza_modificata.sql`, `supabase/migrations/20261009150000_m33_presenza_modificata_allenatori.sql` **Contesto** Un giocatore può cambiare la risposta a un evento fino all'ultimo minuto. Chi organizza (admin e allenatore) se ne accorge solo riaprendo l'evento: un «assente» detto due ore prima di una partita può far saltare la formazione o il numero minimo per l'allenamento. Oggi nessuna notifica avvisa chi gestisce gli eventi di una risposta (`notifiche.md`, «Cosa non genera nessuna notifica»). **Decisione** 1. **Cosa fa scattare l'avviso.** Una riga di `risposte_presenze` che cambia — nuova, modificata o ritirata — quando mancano **da 0 a 6 ore** all'inizio di una **partita o un allenamento** (non agli altri tipi di evento). Scatta per **qualsiasi cambio di stato**, da qualunque valore a qualunque altro (presente, assente, forse, in ritardo, infortunato) e anche per la prima risposta e per il ritiro; non scatta solo se lo stato resta quello di prima (nessun cambio reale). Vale per qualunque giocatore, anche se la modifica la fa un admin per conto suo. 2. **Chi la riceve:** gli **admin e gli allenatori attivi** (chi ha il ruolo `admin` o `allenatore` in `user_roles` e uno slot attivo in `giocatori_squadra`), **tranne la persona a cui appartiene la presenza** (se un admin cambia la propria, non avvisa se stesso). Chi ha il ruolo ma nessuno slot collegato non può ricevere notifiche, perché `notifiche_utente` è per slot. 3. **Come funziona.** Un trigger `AFTER INSERT OR UPDATE OF stato OR DELETE` su `risposte_presenze` chiama una funzione `SECURITY DEFINER` (`avvisa_presenza_modificata()`, migration `m32`). La funzione: 1. ignora il caso in cui lo stato prima e dopo è lo stesso; 2. legge l'evento e continua solo se `tipo` è `partita` o `allenamento` e `inizio_evento(data, ora) - now()` sta tra 0 e 6 ore (stessa `inizio_evento()` di M26: fuso `Europe/Rome`, ora illeggibile = nessun avviso); 3. inserisce una riga in `notifiche_utente` per ogni destinatario, con il tipo nuovo `presenza_modificata`. Dal database in poi il resto è già fatto: il trigger di M22/M26 accoda l'**email**, il trigger delle push (esteso con il tipo nuovo) accoda la **push**, la riga è il **centro notifiche in-app** (DD-038). Nessuna route dell'app e nessun codice lato client oltre al nuovo valore di `TipoNotifica`. 4. **Testo.** Titolo `Presenza modificata: `; corpo su righe: `Evento: `, `Data: , ore `, `Da: `, `A: `. Gli stati sono «presente», «assente», «forse», «in ritardo», «infortunato», «nessuna risposta» (prima di una risposta nuova, o dopo un ritiro). Non c'è un link all'evento: il pulsante «Apri CrAPP» della mail e il tocco sulla push aprono l'app. 5. **Una notifica per modifica, senza raggruppare.** `evento_id` resta NULL (come i messaggi `admin`), così la `UNIQUE (giocatore_id, evento_id, tipo)` non si applica e due modifiche dello stesso evento non si sovrascrivono. Contro: se la stessa persona cambia idea cinque volte, ognuno dei destinatari riceve cinque notifiche e cinque email. 6. **Nessuna configurazione.** Né una soglia modificabile (6 ore è scritto nella funzione), né un interruttore per ruolo: l'email e la push seguono le preferenze già esistenti di ciascuno (`preferenze_utente`, DD-036). **Fase di prova (conclusa)** La `m32` (9 ottobre 2026) l'ha attivata per i soli admin, per vedere in pratica quante notifiche genera. Dopo la prova la `m33` (stesso giorno) ha aggiunto `'allenatore'` alla lista dei ruoli in `avvisa_presenza_modificata()`: ora vale la regola del punto 2. **Alternative scartate** - **Una route dell'app invocata dal client dopo il salvataggio** (come `sollecita-presenze`) → il client scrive su `risposte_presenze` direttamente con RLS: una route in più sarebbe aggirabile e dipenderebbe dal fatto che l'app sia ancora aperta al momento del salvataggio. Il trigger copre ogni scrittura. - **Raggruppare in una sola notifica per evento** (upsert con `evento_id`) → perde il dettaglio di chi ha cambiato cosa, e l'upsert non rifa partire la push (il trigger delle push è solo `INSERT`). - **Notificare anche chi ha il ruolo ma è il giocatore stesso** → inutile: sa già cosa ha fatto. **Conseguenze** - Nuova migration `m32` (CHECK dei tipi, tre funzioni, due trigger); `docs/DATABASE.md` e il catalogo in `notifiche.md` si aggiornano con essa. Applicata in produzione il 9 ottobre 2026. - L'allenatore, che prima non riceveva nulla sulle presenze, riceve questa notifica: aggiunta a [allenatore.md](../modules/allenatore.md#notifiche). - Rumore possibile a ridosso dell'evento (punto 5). Se in pratica dà fastidio, il primo ritocco è raggruppare per evento, non togliere l'avviso. **Riesame** Dopo qualche settimana d'uso: se arrivano troppe notifiche per lo stesso giocatore, raggruppare; se 6 ore sono troppe o poche, spostare la soglia (una costante nella funzione).