Notifica di compleanno: auguri al festeggiato, avviso agli altri (DD-047)

Ogni giorno, dalle 8:00 (Europe/Rome), un job pg_cron orario manda a chi compie
gli anni «Buon compleanno, <Nome>! 🎂» e a tutti gli altri membri attivi,
allenatori compresi, «Oggi è il compleanno di <Nome>» (con più compleanni lo
stesso giorno un solo messaggio a testa, nomi in ordine alfabetico). Solo
titolo, senza corpo né età. Canali: push, centro notifiche ed email.

- migration m31: tipi `compleanno` e `compleanno_auguri`, registro
  `compleanni_notificati` (una sola volta per destinatario e giorno, anche se la
  notifica viene eliminata), funzione `genera_avvisi_compleanno()` e job
  `avvisi-compleanno`, trigger push esteso ai nuovi tipi. Non ancora applicata
  in produzione.
- 29 febbraio non gestito: chi è nato quel giorno è avvisato solo negli anni
  bisestili (limite noto, documentato).
- test: integrazione `notifiche-compleanno` (ora e ora legale, destinatari,
  più compleanni, registro, coda push/email, 29 febbraio, job pianificato);
  unit sui nuovi tipi e sul corpo vuoto del service worker.
- documentazione: DD-047, catalogo e testi delle notifiche, DATABASE, CHANGELOG,
  ROADMAP, PROJECT_STATE.

Chiude la issue #11.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-06 10:18:22 +02:00
1 parent 7347fdba6e
commit 985fc2fcc1
15 files changed
+706 -43

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
# DD-047 — Notifica di compleanno
**Data:** 6 ottobre 2026
**Stato:** Accettata (migration `m31`, non ancora applicata in produzione; nasce dalla issue #11 «notifica»)
**Contesto**
I compleanni si vedono solo aprendo il Calendario, dove `compleanniEventi()` li calcola dalla data di
nascita pubblica (`giocatori_squadra.nascita`, DD-031). Nessun avviso arriva ad app chiusa. La issue #11
chiede una notifica del tipo «oggi è il compleanno di _tizio_».
**Decisione**
1. **Una notifica vera, automatica, su tutti e tre i canali** (DD-038): push, centro notifiche in-app ed
email. Nasce nel database come le altre notifiche automatiche (DD-040): un job `pg_cron` scrive in
`notifiche_utente`, il worker `mailer/` manda push ed email.
2. **Quando:** una volta al giorno, **dalle 8:00 (fuso `Europe/Rome`)**. Il job gira ogni ora e agisce solo
dalle 8 in poi; un registro ricorda a chi l'avviso del giorno è già stato generato, quindi un giro
mancato si recupera all'ora dopo e non si genera mai due volte.
3. **Chi compie gli anni** è chi ha `nascita` con lo stesso giorno e mese di oggi tra i membri **attivi**
della squadra (`giocatori_squadra.attivo`), allenatori compresi. Senza data di nascita o non attivo:
nessun avviso.
4. **Destinatari: tutti i membri attivi**, allenatori compresi (il compleanno è un fatto della squadra,
non una notifica di gioco: DD-039 non si applica). Due messaggi diversi, entrambi **senza l'età**:
- **il festeggiato** riceve **solo gli auguri**: titolo `Buon compleanno, <Nome>! 🎂`, **senza corpo**;
- **tutti gli altri** ricevono **solo l'avviso**: titolo `Oggi è il compleanno di <Nome>`, **senza corpo**.
5. **Più compleanni lo stesso giorno: un solo messaggio per destinatario.** Chi non compie gli anni riceve
un unico avviso con tutti i nomi: titolo `Oggi è il compleanno di <A> e <B>` (con tre o più: `A, B e C`,
in ordine alfabetico per nome), sempre senza corpo. Ogni festeggiato riceve **solo i propri auguri**,
senza i nomi degli altri.
6. **Registro.** Nuova tabella `compleanni_notificati (giocatore_id, giorno)`: una riga per destinatario e
giorno. Il registro, e non `notifiche_utente`, impedisce il doppione anche se il giocatore elimina la
notifica (stessa logica di DD-037). Il registro non è legato a un evento: i compleanni non sono
`eventi_app`.
7. **Tipi** (`CHECK` di `notifiche_utente`): `compleanno` (l'avviso agli altri) e `compleanno_auguri` (gli
auguri). Nessun pulsante manuale: la notifica parte solo dal job. Il trigger delle push accoda
entrambi; quello delle email li lascia passare, quindi arrivano anche per email.
8. **29 febbraio: per ora non gestito.** Chi è nato il 29 febbraio viene avvisato solo negli anni bisestili,
perché il confronto è tra giorno e mese. Negli altri anni non parte nulla (né il 28 febbraio né il 1°
marzo). È un limite noto, accettato: oggi non interessa nessuno in squadra.
**Alternative scartate**
- **Card in Home, senza push né migration** → più semplice, ma non avvisa chi non apre l'app: non è ciò che
chiede la issue.
- **Un messaggio per festeggiato anche a chi lo riceve da altri** → con due compleanni lo stesso giorno
sarebbero due notifiche e due mail al giorno per lo stesso destinatario.
- **Registro per `(destinatario, festeggiato, giorno)`** → serve solo a chi vuole due avvisi separati, che
abbiamo scartato; la chiave `(destinatario, giorno)` basta.
- **Allenatori esclusi** → scartata su indicazione esplicita: «tutti».
- **Gestire il 29 febbraio il 28 febbraio** → rimandato (punto 8).
**Conseguenze**
- Ogni giorno con un compleanno ogni membro attivo riceve una notifica e, se ha account, email e le email
accese, una mail. Con molti compleanni nello stesso periodo il tetto giornaliero di invio del worker
(`mailer/`) può farsi sentire: il compleanno non ha priorità sulle altre mail.
- Chi inserisce la propria data di nascita **il giorno stesso** del compleanno, dopo le 8 e dopo che gli
altri sono già stati avvisati, non genera un secondo avviso (il registro ha già il giorno per i
destinatari). Chi la inserisce prima delle 8 viene incluso.
- Un nuovo membro che si aggiunge dopo l'avviso della giornata non riceve quello del giorno.
- Il compleanno resta anche in Calendario, invariato.
**Riesame**
Se le mail di compleanno risultano troppe, spegnerle e tenere solo push e centro notifiche (come il turno
palloni, DD-040). Se in squadra arriva qualcuno nato il 29 febbraio, gestire quel caso (punto 8).
+1
View File
@@ -86,6 +86,7 @@ Una decisione sostituita non si cancella: si cambia il suo **Stato** e si rimand
| [DD-038](DD-038.md) | Tre canali per ogni notifica |
| [DD-039](DD-039.md) | Niente promemoria all'allenatore |
| [DD-040](DD-040.md) | Palloni e solleciti automatici |
| [DD-047](DD-047.md) | Notifica di compleanno |
| [DD-046](DD-046.md) | Avviso certificati mancanti |
| [DD-045](DD-045.md) | Sollecito presenze: un solo avviso a 24 ore |
| [DD-044](DD-044.md) | Via la foto tessera dal profilo |