From 5fb42b05c7bbc6fcc5dcc6682a0b7bbc8afa4ada Mon Sep 17 00:00:00 2001 From: Francesco Picone Date: Tue, 20 Oct 2026 17:48:18 +0200 Subject: [PATCH] ++ HANDOFF e README allineati all'ultima versione delle funzionalita' HANDOFF: stato attuale vero (produzione dal 19/10, cosa c'e' solo su dev e come rilasciarlo), sezione dei gruppi di servizio descritta com'e' oggi con la storia delle decisioni in coda, lavori recenti in ordine, cronologia di luglio-agosto in "Da dove veniamo", ambienti con autodeploy che non parte e test sul server prima del push, copertura dei test (577 + 13), frasi superate su WhatsApp/email/contatti corrette. README: impostazioni reali della pagina, due liste della Home, soglia di priorita' in giorni e ordine di priorita', deploy a mano, struttura del progetto. Co-Authored-By: Claude Opus 5.5 --- HANDOFF.md | 624 ++++++++++++++++++++++++++++------------------------- README.md | 70 +++--- 2 files changed, 378 insertions(+), 316 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index ba49d8a..b56332a 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -4,236 +4,59 @@ Stato del progetto per chi ci mette mano dopo. Per installazione, deploy e uso quotidiano vedi [README.md](README.md); qui c'è il contesto che il README non può dare: perché le cose sono come sono, e cosa resta aperto. -Ultimo aggiornamento: **20 ottobre 2026** (gruppi di servizio, solo su dev) — vedi [Lavori in corso](#lavori-in-corso--12-ottobre-2026-riprendere-da-qui). +Ultimo aggiornamento: **20 ottobre 2026** — si riprende da [Stato attuale](#stato-attuale). --- ## Stato attuale -L'applicazione è **pronta per il deploy in produzione su Dokploy**. Lo stack è -stato verificato end-to-end: build dell'immagine, primo deploy su volumi vuoti, -redeploy, login, tutte le pagine, code e scheduler. +**Produzione** (`main` → compose `termanager2-core-production`) in esercizio +su Dokploy dal 19 ottobre 2026. Ultimo rilascio: `d3314a9` (proclamatori senza +contatti, il link si copia o si condivide). Backup fatti prima dei rilasci in +`/etc/dokploy/termanager2-backups/` sul server. + +**Demo** (`dev` → compose `termanager2-core-demo`, +`demo-termanager.pyconetwork.it`): avanti di una ventina di commit, tutti +provati con la suite sul server e pubblicati sulla demo. **Non ancora in +produzione** — l'utente ha chiesto di aspettare il suo ok: + +- [gruppi di servizio](#gruppi-di-servizio-19-20-ottobre-2026-solo-su-dev): + sorvegliante con email fittizia, pacchetto di territori, "Il mio gruppo", + scioglimento; migrazioni `2026_10_20_000010` … `000040`; +- priorità e filtri nel pacchetto, con la regola di priorità della Home in un + metodo solo (`Territorio::ordinaPerPriorita()`); +- pulsante **Rientro** nell'elenco dei territori assegnati; +- storico assegnazioni in una tabella sola, con le colonne in linea; +- *Assegna* aperta da "Assegna →" della Home senza l'elenco dei territori + assegnati. + +**Per portarli in produzione**: backup del database di produzione, `main` +avanti fino a `dev` (fast-forward), deploy (l'autodeploy non parte: vedi +[Dove gira davvero](#dove-gira-davvero)), poi controllo di migrazioni e +conteggi. In produzione non ci sono gruppi: le quattro migrazioni non hanno +dati da convertire. | Aspetto | Stato | |-----------------------------|-------| -| Deploy Dokploy (Compose) | ✅ verificato | +| Deploy Dokploy (Compose) | ✅ produzione e demo | +| Autodeploy al push | ⚠️ configurato, ma dal 19/10 il push non fa partire il deploy: si avvia a mano (`compose.deploy`) | | Primo avvio a zero-config | ✅ migrazioni + seed + admin automatici | | Redeploy senza interventi | ✅ ~25 secondi | | Sviluppo locale | ✅ stesso compose, profilo `dev` | -| Backup / ripristino / azzeramento | ✅ verificati end-to-end in locale | +| Backup / ripristino / azzeramento | ✅ verificati end-to-end | | Ripristino da browser in linea | ⚠️ **limitato a 100 MB da Cloudflare** — vedi [Cosa resta aperto](#cosa-resta-aperto) | -| Test automatici | ⚠️ **impalcatura pronta, 294 test** — copertura ancora parziale | +| Test automatici | ✅ **577 test PHP + 13 JavaScript, verdi** (20/10/2026); copertura ancora parziale | +| Prove nel browser | ⚠️ le funzioni del 19-20 ottobre sono verificate dai test e sul server; nel browser le ha usate l'utente sulla demo | -**Punto in cui ci si è fermati (30/07/2026).** Su `develop` è pubblicato tutto: -backup completo `.zip`, ripristino, azzeramento, cambio password forzato, barre -di avanzamento reali e console di log (commit `f909791`, deploy verificato -confrontando l'impronta degli asset). `MAX_UPLOAD_MB=2048` è impostato **solo** -su develop; la produzione non è stata toccata e resta sul suo deploy -precedente, senza quella variabile. - -**Il 31/07 è nata la sotto-app "Piantine"** (nuova voce nel menu -Amministrazione): KML dell'area città, editor dei poligoni su OpenStreetMap, -immagini HD dal container `map-renderer` e PDF sul modulo S-12 con storico -versioni. Architettura e agganci sono descritti in [Decisioni -architetturali](#le-piantine-sono-una-sotto-app-renderer-esterno-agganci-minimi). -Al primo deploy il compose costruisce anche l'immagine del renderer; non -servono variabili nuove in Dokploy. - -**Il 28/08 la sotto-app è stata rifatta e l'interfaccia ripassata.** Le piantine -si sono staccate dai territori (una piantina vive anche scollegata, il territorio -si crea dall'editor), il disegno è passato su una pagina a schermo intero, la -cartolina S-12 è stata reimpaginata (confini sulla prima pagina, piantina al 98% -sulla seconda) e la rigenerazione massiva è stata rimossa. In più il sistema -visivo è stato ripassato su tutta l'applicazione, non solo qui: vedi [Il sistema -visivo sta nei token](#il-sistema-visivo-sta-nei-token-non-nelle-viste). - -Nello stesso giro è emerso e corretto un guasto d'ambiente che bloccava **tutti** -gli upload in sviluppo: vedi [`HTTP_HOST` a nginx deve conservare la -porta](#http_host-a-nginx-deve-conservare-la-porta). - -Il 30/07 sono state fatte due cose. - -**La suite di test, che prima non esisteva**: 79 test eseguibili con un comando, -senza dipendere da altri servizi. Sono **verdi** ma coprono ancora una frazione -della logica di business — vedi *Cosa resta aperto*. Non fanno parte -dell'immagine di produzione e non girano su un deploy: sono uno strumento -locale. - -**La pagina "Backup e import" è stata riorganizzata**: cinque zone numerate con -un indice che ci salta sopra evidenziandole, un solo sistema di pulsanti (con -gli stati di focus, che prima non c'erano), e **una sola console** per tutte le -operazioni, agganciata in fondo alla finestra, al posto dei cinque riquadri di -log sparsi. L'azzeramento è diventato un componente a sé per poter stare in -fondo alla pagina. Tutto verificato lato server dai test; **il comportamento nel -browser — apertura automatica della console, scorrimento, lampeggio delle zone — -non è stato provato**, perché richiede un browser: è la prima cosa da guardare -aprendo la pagina. - -Il ripristino dall'interfaccia resta impraticabile per pacchetti grandi finché -non si decide come aggirare Cloudflare: è la prima voce di *Cosa resta aperto* -e l'unica che richiede una decisione, non del lavoro. +La storia precedente (luglio-agosto: backup e ripristino, sotto-app Piantine, +sistema visivo, prima suite di test) è in [Da dove veniamo](#da-dove-veniamo). --- -## Lavori in corso — 12 ottobre 2026 (riprendere da qui) +## Lavori recenti — ottobre 2026 -Commit su `dev`, **nessuno pubblicato** (il push fa partire il deploy sulla demo): -- `d433c93` — Modulo S-13 sul modulo ufficiale (regole in - `app/Services/Registro/ModuloS13.php`) e blocco 1 della revisione - (integrità dati). -- `056d95c` — Editor piantine: bozza automatica, un solo "Pubblica" con - anteprima, stati Bozza / Pubblicata / Modifiche da pubblicare. Come funziona: - [Le piantine sono una sotto-app](#le-piantine-sono-una-sotto-app-renderer-esterno-agganci-minimi). -- `426a952` — blocchi 2 e 3 della revisione (sotto); le ultime due righe - della tabella sono nel commit successivo. - -Produzione: al 12/10/2026 non esiste ancora (il sito gira in Docker sul PC di -sviluppo, più la demo da `dev`). Nessuna correzione una tantum da fare. - -### Posta, contatti dei proclamatori, conferma email (12-13 ottobre 2026) - -Richieste dell'utente, con le sue scelte: - -- **Server di posta dall'app** (Impostazioni → Server di posta, rotta - `posta.edit`, `Settings\PostaElettronica`): host, porta, cifratura - (automatica / SSL / nessuna), utente, password cifrata, mittente; pulsante - "Invia email di prova". Se compilato prende il posto del `.env` - (`ConfigurazionePosta::applica()`, agganciata alla creazione del mail - manager e a ogni job della coda). Non si esporta nell'XML e l'import non la - cancella (`Setting::CAMPI_POSTA`). Nel `.env.example` `MAIL_SCHEME=tls` era - un valore non valido per Laravel 11: ora vuoto. -- **Proclamatori: email e telefono facoltativi**, cifrati come nome e cognome, - nell'export/import XML, cancellati dall'anonimizzazione. Il numero apre - WhatsApp (``, `Proclamatore::whatsappUrl()`: senza prefisso si - considera italiano). -- **Dopo l'assegnazione** — *rivisto il 19 ottobre 2026, vedi "Invio del link - dopo l'assegnazione" qui sotto*. Il riquadro in sessione - (`Assegna::DA_INVIARE`) non esiste più. -- **Conferma email obbligatoria** per tutti gli utenti, anche quelli già - esistenti (la migrazione `2026_10_13_000010` azzera `email_verified_at`). - L'admin crea l'utente con la password come prima; parte l'email di conferma - (in italiano, `VerifyEmail::toMailUsing`); il link funziona anche senza - login. Cambiare email (profilo o admin) la rende da confermare e rimanda il - link. In Utenti: stato "da confermare" e "Reinvia conferma". - Raggiungibili senza conferma: profilo, cambio password, pagina di conferma - e — per l'amministratore — il server di posta. - Emergenza: `php artisan utenti:conferma-email `. Il primo admin - creato da riga di comando nasce confermato. -- Nei test gli utenti nascono confermati (`tests/TestCase.php`, - `$confermaUtentiCreati`). - -Provato nel browser in locale con mailpit come server configurato dall'app: -email di prova e di conferma arrivate, link aperto senza login, account -sbloccato; riquadro dopo l'assegnazione con WhatsApp e scelta. Configurazione -e dati di prova rimossi dopo. - -### Gruppi di servizio (19 ottobre 2026, solo su dev) - -Richiesta dell'utente: un **gruppo di servizio** con un sorvegliante che, -entrato con una sua utenza, distribuisce ai membri un pacchetto di territori -e non vede nient'altro. Scelte dell'utente: - -- Il gruppo ha **membri** (proclamatori); un proclamatore sta in **un solo** - gruppo; il sorvegliante è anche membro e assegna solo ai membri, a sé - compreso. -- Il **pacchetto non è un'assegnazione**: "la media non deve essere alterata". - I territori del pacchetto sono "In gruppo …" (stato `in_gruppo`), fuori da - "Da assegnare" e dalla pagina *Assegna* (`Territorio::scopeInReparto` - esclude `gruppo_servizio_id`). -- Quando il sorvegliante dà un territorio nasce un'**assegnazione normale**, - tracciata con `assegnazioni.gruppo_servizio_id` ("via gruppo …" nel - registro e nella scheda del territorio). -- Al **rientro** il territorio esce dal pacchetto e torna in reparto - (hook `updated` in `Assegnazione`, vale da qualunque pagina). -- Il sorvegliante: assegna, manda il link (copia/condividi), registra i - rientri, apre la piantina, rimanda in reparto i non dati. Gruppi, membri e - pacchetti li gestisce **solo l'amministratore**. -- Utenza: password temporanea da cambiare al primo accesso, **senza - conferma dell'email** (`User::hasVerifiedEmail()` è vero per il - sorvegliante). *Dal 20 ottobre l'email è fittizia, creata dall'app — vedi - sotto.* -- **Togliere il ruolo = sciogliere il gruppo** (`GruppiServizio::sciogli`): - non dati in reparto, membri liberi. I territori già dati restano ai membri. - Si riparte con un gruppo nuovo. *Dal 20 ottobre gruppo e utenza del - sorvegliante si eliminano — vedi sotto.* - -Decisioni successive dell'utente (20 ottobre 2026): -- **Sorvegliante non più attivo ⇒ gruppo sciolto** e territori non dati - rilasciati, come "Togli il ruolo". La regola sta nel modello - (`Proclamatore::booted`, eventi `updated` su `attivo` e `deleted`), così - vale dall'elenco, dalla modifica, dal cestino e dall'anonimizzazione; il - motivo finisce nel registro (`gruppo_sciolto`, proprietà `motivo`). - L'elenco chiede conferma prima di disattivare o cestinare un sorvegliante; - la modifica lo avvisa sotto la spunta "Attivo". Il cestino e - l'anonimizzazione erano prima bloccati per il sorvegliante: li ho allineati - alla stessa regola. Un membro non attivo resta membro (non riceve - territori); nel cestino esce dal gruppo. -- **Dettagli nel profilo**: la scheda del proclamatore (`ProclamatoreShow`) - mostra il gruppo che guida con i numeri (`GruppoServizio::riepilogo()`), - l'utenza (email solo all'amministratore, password temporanea o no, - ultimo accesso dal registro), oppure il gruppo di cui è membro. Il - *Profilo* del sorvegliante mostra il suo gruppo con gli stessi numeri e - "Torna al mio gruppo". -- **Il gruppo sciolto non resta nello storico**: `sciogli()` lo elimina - (colonna `sciolto_at` tolta dalla migrazione `2026_10_20_000020`, che - elimina anche i gruppi già sciolti). I territori solo nel pacchetto tornano - in reparto (conferma esplicita dell'utente); quelli già dati restano ai - membri come assegnazioni qualsiasi, che conservano il nome del gruppo - (`assegnazioni.gruppo_nome`, "via gruppo …" in registro e scheda - territorio): **confermato dall'utente il 20 ottobre 2026**. Dopo lo - scioglimento si torna all'elenco dei gruppi; nella scheda del proclamatore - non c'è più "In passato sorvegliante di". -- **Email fittizia, niente nome utente** (seconda decisione del 20 ottobre, - che sostituisce il nome utente ricavato dal nome del gruppo fatto qualche - ora prima): creando il gruppo l'email non si chiede, - `GruppiServizio::emailFittizia()` la ricava da iniziale del nome e cognome, - minuscoli, senza accenti, spazi né apostrofi, sul dominio - `GruppiServizio::DOMINIO_EMAIL` = `termanager.it` (Francesco Fontana → - `ffontana@termanager.it`, D'Angelo → `fdangelo@…`, numero in coda se già - presa) — la stessa convenzione degli altri utenti. Il login è di nuovo solo - per email. Il profilo del sorvegliante mostra l'email ma non la fa - cambiare. Il nome del gruppo è unico. -- **Sciolto il gruppo, l'utenza del sorvegliante si elimina** ("torna - proclamatore normale, non tenere traccia"): `User::eliminaConservandoStorico()` - — la stessa eliminazione della pagina *Utenti*, ora in un metodo solo — passa - le assegnazioni che aveva fatto o fatto rientrare a chi scioglie (o al primo - amministratore) e lascia le sue voci nel registro a nome "Sorvegliante del - gruppo …", senza email. Niente più utenze disattivate: via - `users.disattivato_at` e i controlli in `LoginController` e - `LimitaSorvegliante`; una sessione aperta resta senza utente. - `Proclamatore::anonimizza()` non tocca più utenze: se era sorvegliante, il - salvataggio scioglie il gruppo e l'utenza sparisce. -- Migrazione `2026_10_20_000030_sorveglianti_con_email_fittizia`: elimina le - utenze già disattivate (assegnazioni al primo amministratore, registro a - nome "Ex sorvegliante di gruppo"), dà l'email fittizia a chi non l'aveva, - toglie `users.username` e `users.disattivato_at`, rende di nuovo - obbligatoria `users.email`. XML: niente più `` né - ``; da un file vecchio non si importano le utenze - disattivate né i gruppi con ``. -- **Primo accesso del sorvegliante**: la pagina del cambio password obbligato - diceva sempre "Ripristino da backup eseguito" (era l'unico motivo - previsto). Ora `users.password_temporanea` (migrazione `000040`, che la - accende per i sorveglianti che non hanno ancora cambiato la password) - distingue la password provvisoria data dall'amministratore — creazione del - gruppo, "Nuova password temporanea" — e la pagina dice "Password - temporanea". Si spegne a password cambiata (pagina obbligatoria o - profilo); il ripristino e l'import XML la spengono per tutti, perché lì il - motivo è il ripristino. -- **Bug: ogni clic del sorvegliante finiva in 403.** In Livewire 3.7 la - rotta delle azioni si chiama `default.livewire.update`, e - `LimitaSorvegliante` lasciava passare solo `livewire.*`: ora `*livewire.*`. - I test non lo vedevano perché `Livewire::test()` non passa dai middleware - delle rotte; ora un test chiama la rotta vera. -- **Priorità e filtri** (richiesta dell'utente): la regola della Home ("Da - assegnare": prioritari — spunta o giacenza oltre `giorni_giacenza_prioritari` - — poi giacenza, poi numero) è ora `Territorio::ordinaPerPriorita()`, usata - dalla Home, dall'aggiunta al pacchetto (`GruppoShow`: ricerca, zona, - tipologia, solo prioritari, "Seleziona tutti quelli mostrati") e da "Il mio - gruppo" (prima i da dare in ordine di priorità, poi i già dati dal più - vecchio; filtri stato, zona, tipologia, solo prioritari, nell'indirizzo). - Gli attributi calcolati si chiamano `giacenza_giorni` e `in_priorita` - (erano `home_giorni_giacenza` e `home_is_prioritario`, anche nel PDF). -- "Il mio gruppo" mostra sotto il titolo nome e cognome del sorvegliante. +Dal più recente. Le sezioni del 12-19 ottobre sono in produzione; quelle del +20 ottobre e i gruppi di servizio solo sulla demo. ### Assegna aperta dalla Home: senza l'elenco dei territori assegnati (20 ottobre 2026) @@ -271,51 +94,124 @@ Anche le righe senza PDF lo hanno. L'elenco ha un tetto di peso per riga starci l'indirizzo è relativo (`route(..., false)`, `data-ritorno` lo gestisce) e il pulsante sta sulla stessa riga del `
`, senza a capo né commenti dentro il ciclo (i commenti Blade spariscono, gli spazi no). -- **Anche i membri si segnalano**: `Proclamatore::gruppo_etichetta` - ("Gruppo Zisa" / "Sorvegliante del gruppo Zisa") come badge nell'elenco - (più il filtro *In un gruppo di servizio*, `filtroStato=gruppo`) e in testa - alla scheda; nelle opzioni di `` come `dettaglio` (Assegna, - Registro), non come `motivo`: assegnare direttamente a un membro resta - possibile. In "Il mio gruppo" la nota non c'è (`opzioniScelta(..., - conGruppo: false)`). -- L'utenza del sorvegliante porta nome ed email del proclamatore: nel registro - delle attività `User::tapActivity()` li sostituisce con "proclamatore #id", - come vale per tutti i proclamatori. Restano invece in chiaro nella tabella - `users`, come per ogni utente. -Dove sta: -- Migrazione `2026_10_20_000010_gruppi_di_servizio`: tabella - `gruppi_servizio`, colonne `gruppo_servizio_id` su proclamatori, territori e - assegnazioni, `users.proclamatore_id` (unico) e `users.disattivato_at` - (tolto dalla `000030`), permesso `gruppo.gestisci` e ruolo `sorvegliante`. Il seeder dà - all'amministratore tutti i permessi **tranne** `gruppo.gestisci`. -- Regole in `App\Services\GruppiServizio` (crea, aggiungiMembri, togliMembro, - aggiungiAlPacchetto, rimandaInReparto, assegna, sciogli); i componenti - `Gruppi\GruppiIndex`, `Gruppi\GruppoShow` (amministratore) e - `Gruppi\MioGruppo` (sorvegliante) le chiamano soltanto. -- `LimitaSorvegliante` (gruppo `web`): chi ha **solo** il ruolo sorvegliante - raggiunge `mio-gruppo*`, `assegnazioni.rientra` (solo per il suo gruppo, - controllo in `Rientra::autorizza`), profilo e cambio password; la Home lo - rimanda al gruppo; il resto è 403. -- L'invio del link è diventato il trait `Concerns\InviaLink` + il partial - `assegnazioni/_invio-link`, usati da *Assegna* e da *Il mio gruppo*. -- Il ruolo sorvegliante non compare nella pagina *Utenti* e le utenze legate a - un proclamatore lì non si modificano né si eliminano: passano dal gruppo. -- La voce di menu "Il mio gruppo" guarda il **ruolo**, non `@can`: - l'amministratore passa ogni `@can` (`Gate::before`). -- XML: si esportano e reimportano gruppi, membri, pacchetto, provenienza delle - assegnazioni (id e nome del gruppo) e legame utenza ↔ - proclamatore. -- Informativa privacy aggiornata (utenza del sorvegliante, gruppo tra i dati - del proclamatore). -- L'azzeramento dell'applicazione (*Zona pericolosa*) cancella anche i gruppi. -- Test: `tests/Feature/GruppiServizioTest.php`. Eseguiti sul server **prima** - del push, su una copia della checkout di dev con i commit portati da un - `git bundle` (la checkout di Dokploy è shallow: il bundle deve partire dal - commit che ha, `git log -1` lì dentro). +### Gruppi di servizio (19-20 ottobre 2026, solo su dev) -Non provato nel browser. **In produzione non c'è**: l'utente ha chiesto -(20 ottobre 2026) di non portarlo ancora in produzione; si aspetta il suo ok. +Richiesta dell'utente: un **gruppo di servizio** con un sorvegliante che, +entrato con una sua utenza, distribuisce ai membri un pacchetto di territori +e non vede nient'altro. Le decisioni sono cambiate più volte in due giorni: +qui sotto **com'è oggi**, in fondo la storia. + +**Come funziona** + +- **Chi**: un gruppo ha un nome (unico), un sorvegliante (proclamatore + attivo) e dei membri. Un proclamatore sta in **un solo** gruppo; il + sorvegliante è anche membro e assegna solo ai membri, a sé compreso. + Gruppi, membri e pacchetti li gestisce **solo l'amministratore** (menu + *Gruppi di servizio*). +- **Utenza del sorvegliante**: nasce con il gruppo. Email **fittizia** + (`GruppiServizio::emailFittizia()`: iniziale del nome e cognome, minuscoli, + senza accenti, spazi né apostrofi, su `GruppiServizio::DOMINIO_EMAIL` = + `termanager.it` — Francesco Fontana → `ffontana@termanager.it`, numero in + coda se già presa), la stessa convenzione degli altri utenti. Password + temporanea, mostrata una volta: al primo accesso la pagina del cambio dice + "Password temporanea" (`users.password_temporanea`, che il ripristino e + l'import XML spengono, perché lì il motivo è il ripristino). Nessuna + conferma dell'email (`User::hasVerifiedEmail()` vero per il sorvegliante), + e dal profilo l'email non si cambia. Ruolo `sorvegliante`, con il solo + permesso `gruppo.gestisci`; non compare nella pagina *Utenti*, dove le + utenze legate a un proclamatore non si modificano né si eliminano. + "Nuova password temporanea" dalla pagina del gruppo. +- **Cosa vede**: `LimitaSorvegliante` (gruppo `web`) lascia a chi ha **solo** + quel ruolo `mio-gruppo*`, `assegnazioni.rientra` (solo per il suo gruppo: + `Rientra::autorizza`), profilo, cambio password e le azioni Livewire — + `*livewire.*`, perché in Livewire 3.7 la rotta si chiama + `default.livewire.update` (con `livewire.*` ogni clic era un 403, e + `Livewire::test()` non passa dai middleware: un test chiama la rotta vera). + La Home lo rimanda al gruppo, il resto è 403. La voce di menu "Il mio + gruppo" guarda il **ruolo**, non `@can`: l'amministratore passa ogni `@can` + (`Gate::before`). +- **Pacchetto**: **non è un'assegnazione** ("la media non deve essere + alterata"): i territori sono "In gruppo …" (stato `in_gruppo`), fuori da + "Da assegnare" e dalla pagina *Assegna* (`Territorio::scopeInReparto` + esclude `gruppo_servizio_id`). Si aggiungono in blocco dai territori in + reparto in ordine di priorità (`Territorio::ordinaPerPriorita()`, la regola + della Home), con ricerca, zona, tipologia, "solo prioritari" e "Seleziona + tutti quelli mostrati" (`GruppoShow`). +- **"Il mio gruppo"** (`MioGruppo`): sotto il titolo nome e cognome del + sorvegliante; prima i territori da dare in ordine di priorità, poi i già + dati dal più vecchio; filtri stato, zona, tipologia, "solo prioritari" + (nell'indirizzo). Il sorvegliante assegna, manda il link (copia o + condividi: trait `Concerns\InviaLink` + partial `assegnazioni/_invio-link`, + gli stessi di *Assegna*), registra i rientri, apre la piantina, rimanda in + reparto quelli non dati. +- **Assegnazione dal pacchetto**: un'**assegnazione normale**, con + `gruppo_servizio_id` e `gruppo_nome` ("via gruppo …" in registro e schede). + `GruppiServizio::assegna()` rilegge territorio e membro e ricontrolla il + pacchetto sotto lock. Al **rientro** il territorio esce dal pacchetto e + torna in reparto (hook `updated` in `Assegnazione`, da qualunque pagina). +- **Scioglimento**: "Togli il ruolo e sciogli il gruppo", oppure il + sorvegliante non è più attivo — reso non attivo, nel cestino o anonimizzato + (`Proclamatore::booted`, eventi `updated` su `attivo` e `deleted`; l'elenco + chiede conferma, la modifica avvisa sotto "Attivo"). `GruppiServizio::sciogli()` + **elimina il gruppo**: i territori solo nel pacchetto tornano in reparto; + quelli già dati restano ai membri e l'assegnazione ne conserva il nome + (confermato dall'utente); i membri restano senza gruppo; l'**utenza del + sorvegliante si elimina** con `User::eliminaConservandoStorico()` — la + stessa eliminazione della pagina *Utenti* — che passa le sue assegnazioni a + chi scioglie (o al primo amministratore) e lascia le sue voci nel registro + a nome "Sorvegliante del gruppo …". Il motivo va nel registro + (`gruppo_sciolto`, `motivo`). Un membro non attivo resta nel gruppo ma non + riceve territori; nel cestino ne esce. +- **Segnalazioni**: `Proclamatore::gruppo_etichetta` ("Gruppo Zisa" / + "Sorvegliante del gruppo Zisa") come badge nell'elenco dei proclamatori + (filtro *In un gruppo di servizio*, `filtroStato=gruppo`) e in testa alla + scheda; nelle opzioni di `` come `dettaglio` (Assegna, Registro), + non come `motivo`: assegnare direttamente a un membro resta possibile. La + scheda del proclamatore mostra il gruppo che guida + (`GruppoServizio::riepilogo()`, email, password temporanea o no, ultimo + accesso) o quello di cui è membro; il *Profilo* del sorvegliante il suo + gruppo. +- **Dati personali**: nel registro delle attività `User::tapActivity()` + sostituisce nome ed email dell'utenza del sorvegliante con + "proclamatore #id", come per tutti i proclamatori; nella tabella `users` + restano in chiaro, come per ogni utente. Informativa privacy aggiornata. +- **Backup**: l'XML porta gruppi, membri, pacchetto, provenienza delle + assegnazioni (id e nome del gruppo) e il legame utenza ↔ proclamatore; da + un file del 19-20 ottobre non si importano i gruppi con `` né + le utenze con ``. L'azzeramento (*Zona pericolosa*) + cancella anche i gruppi. + +**Dove sta** + +- Migrazioni: `2026_10_20_000010_gruppi_di_servizio` (tabella, colonne + `gruppo_servizio_id` su proclamatori, territori e assegnazioni, + `users.proclamatore_id` unico, permesso e ruolo; il seeder dà + all'amministratore tutti i permessi **tranne** `gruppo.gestisci`), + `000020` (`assegnazioni.gruppo_nome`, via `sciolto_at` e i gruppi sciolti), + `000030` (via `users.username` e `users.disattivato_at` e le utenze + disattivate, email fittizia a chi non l'aveva, email di nuovo obbligatoria), + `000040` (`users.password_temporanea`). +- Regole in `App\Services\GruppiServizio`; i componenti `Gruppi\GruppiIndex`, + `Gruppi\GruppoShow` (amministratore) e `Gruppi\MioGruppo` (sorvegliante) le + chiamano soltanto. +- Test: `tests/Feature/GruppiServizioTest.php`. + +**Com'è cambiato** (decisioni dell'utente, in ordine) + +1. 19/10 — gruppo con membri; il pacchetto non conta nelle statistiche; + utenza con email e password, senza conferma; togliere il ruolo scioglie il + gruppo, che restava archiviato (`sciolto_at`) con l'utenza disattivata. +2. 20/10 — sorvegliante non più attivo ⇒ gruppo sciolto; dettagli nella + scheda e nel profilo; anche i membri segnalati. +3. 20/10 — il gruppo sciolto non resta nello storico (si elimina); traccia + "via gruppo" sulle assegnazioni confermata. Per qualche ora il sorvegliante + è entrato con un nome utente ricavato dal nome del gruppo. +4. 20/10 — al posto del nome utente l'email fittizia iniziale+cognome; sciolto + il gruppo l'utenza si elimina ("torna proclamatore normale, non tenere + traccia"). +5. 20/10 — "Password temporanea" al primo accesso; il 403 sulle azioni; + priorità e filtri; nome del sorvegliante sotto il titolo. ### Proclamatori senza contatti; il link si copia o si condivide (19 ottobre 2026) @@ -416,15 +312,17 @@ Proclamatore Semplificato" che non esistono). non è HTTPS), i ruoli con i permessi veri (`Privacy::PERMESSI` li spiega) e quanti dati sono conservati (contati in SQL: i campi cifrati vuoti restano NULL, non serve decifrare). `Privacy::REVISIONE` è la data del testo. -- Aggiunte: email/telefono cifrati, link alla piantina, invio via WhatsApp - (parte dal telefono di chi assegna) ed email (server di posta configurato), - mappe OpenStreetMap, conferma email, blocco dopo troppi tentativi. +- Aggiunte: link alla piantina, invio dal telefono di chi assegna, mappe + OpenStreetMap, conferma email, blocco dopo troppi tentativi. (Email e + telefono dei proclamatori, che il primo testo citava, sono stati tolti la + sera stessa; dal 20 ottobre il testo parla anche del gruppo di servizio e + dell'utenza del sorvegliante.) - Titolare e base giuridica sono rimasti com'erano: sono valutazioni legali, da far rivedere a chi ne ha la competenza. - **Aperto — backup in chiaro**: il pacchetto di backup usa l'export XML, che - legge i proclamatori dal modello e quindi esporta nome, cognome, email e - telefono **decifrati**. L'informativa ora lo dice; valutare se cifrare - l'archivio (per esempio con una password scelta al download). + legge i proclamatori dal modello e quindi esporta nome e cognome + **decifrati**. L'informativa lo dice; valutare se cifrare l'archivio (per + esempio con una password scelta al download). - `PrivacyTest` verifica che i valori cambino con le impostazioni, i ruoli, i conteggi senza nomi e l'accesso solo amministratore. @@ -518,14 +416,14 @@ all'**elenco filtrato**, non alla scheda (scelta esplicita). Richieste dell'utente: - **Dopo l'assegnazione non si va avanti** finché non si è deciso del link: si - apre una finestra che blocca la pagina (`Assegna::apriInvio`) con i canali + apre una finestra che blocca la pagina (oggi trait `Concerns\InviaLink`, + usato anche da "Il mio gruppo") con i canali. *Superato la sera stessa: + WhatsApp ed email sono stati tolti, restano Copia link e Condividi… — vedi + "Proclamatori senza contatti".* Com'era il primo giorno: - *Copia link*: sempre; - *WhatsApp*: solo se il proclamatore ha un numero utilizzabile; - *Email*: solo se ha un indirizzo **e** la posta funziona - (`ConfigurazionePosta::funzionante`). La manda l'app con - `Notification::route('mail', …)->notifyNow(new LinkTerritorio)`: **mai in - coda**, perché il job salverebbe in chiaro su Redis/`failed_jobs` - l'indirizzo che nel database è cifrato. + (`ConfigurazionePosta::funzionante`), inviata subito e mai in coda. Poi "Hai inviato il link?": *Sì* vuole un canale usato, *No* è sempre possibile. Solo dopo la risposta si ricarica la pagina. @@ -537,17 +435,62 @@ Richieste dell'utente: "Non inviato / Link inviato" dell'elenco. - Colonne nuove (migrazione `2026_10_19_000010`): `link_da_inviare` (le assegnazioni già esistenti restano false: niente avvisi a posteriori su - link di cui non si sa niente) e `link_canale` (`copia`/`whatsapp`/`email`, - `Assegnazione::CANALI_LINK`). Entrambe nell'XML. + link di cui non si sa niente) e `link_canale` (oggi `copia`/`condividi`, + `Assegnazione::CANALI_ATTIVI`; `whatsapp`/`email` restano in + `CANALI_LINK` solo per le assegnazioni già inviate così). Entrambe nell'XML. - Nel log `link_inviato` / `link_non_inviato` con canale e `proclamatore_id`: - mai nome, numero o email. Email e telefono restano solo, cifrati, nel - proclamatore. + mai nomi. - **"Nuovo territorio"** (elenco territori) apre il designer delle piantine con `?nuovo=1` (`PiantineEditor::$creaTerritorio`): la mappa parte già in disegno e la prima piantina disegnata si apre con "nuovo territorio" scelto. L'editor è solo dell'amministratore: chi ha `territori.manage` ma non `settings.manage` resta sul form `territori.create`, che esiste ancora. +### Posta, contatti dei proclamatori, conferma email (12-13 ottobre 2026) + +Richieste dell'utente, con le sue scelte: + +- **Server di posta dall'app** (Impostazioni → Server di posta, rotta + `posta.edit`, `Settings\PostaElettronica`): host, porta, cifratura + (automatica / SSL / nessuna), utente, password cifrata, mittente; pulsante + "Invia email di prova". Se compilato prende il posto del `.env` + (`ConfigurazionePosta::applica()`, agganciata alla creazione del mail + manager e a ogni job della coda). Non si esporta nell'XML e l'import non la + cancella (`Setting::CAMPI_POSTA`). Nel `.env.example` `MAIL_SCHEME=tls` era + un valore non valido per Laravel 11: ora vuoto. +- ~~Proclamatori: email e telefono facoltativi~~ — *tolti il 19 ottobre 2026, + vedi "Proclamatori senza contatti" qui sotto*. +- **Dopo l'assegnazione** — *rivisto il 19 ottobre 2026, vedi "Invio del link + dopo l'assegnazione" qui sotto*. Il riquadro in sessione + (`Assegna::DA_INVIARE`) non esiste più. +- **Conferma email obbligatoria** per tutti gli utenti (tranne il sorvegliante + di gruppo), anche quelli già esistenti (la migrazione `2026_10_13_000010` + azzera `email_verified_at`). L'admin crea l'utente con la password come + prima; parte l'email di conferma (in italiano, `VerifyEmail::toMailUsing`); + il link funziona anche senza login. Cambiare email (profilo o admin) la + rende da confermare e rimanda il link. In Utenti: stato "da confermare" e + "Reinvia conferma". Raggiungibili senza conferma: profilo, cambio password, + pagina di conferma e — per l'amministratore — il server di posta. + Emergenza: `php artisan utenti:conferma-email `. Il primo admin + creato da riga di comando nasce confermato. +- Nei test gli utenti nascono confermati (`tests/TestCase.php`, + `$confermaUtentiCreati`). + +Provato nel browser in locale con mailpit come server configurato dall'app: +email di prova e di conferma arrivate, link aperto senza login, account +sbloccato. Configurazione e dati di prova rimossi dopo. + +### Modulo S-13, editor piantine, revisione (12 ottobre 2026) + +In produzione dal 19 ottobre 2026 (erano su `dev` dal 12): +- `d433c93` — Modulo S-13 sul modulo ufficiale (regole in + `app/Services/Registro/ModuloS13.php`) e blocco 1 della revisione + (integrità dati). +- `056d95c` — Editor piantine: bozza automatica, un solo "Pubblica" con + anteprima, stati Bozza / Pubblicata / Modifiche da pubblicare. Come funziona: + [Le piantine sono una sotto-app](#le-piantine-sono-una-sotto-app-renderer-esterno-agganci-minimi). +- `426a952` — blocchi 2 e 3 della revisione (sotto). + ### Revisione profonda: blocchi 2 e 3 chiusi | Problema | Correzione | @@ -602,20 +545,92 @@ a ogni deploy: lento, dipendente dalla rete, e capace di fallire in produzione. Da qui la ristrutturazione descritta sotto. +### Luglio-agosto 2026 + +**Punto in cui ci si è fermati (30/07/2026).** Su `develop` è pubblicato tutto: +backup completo `.zip`, ripristino, azzeramento, cambio password forzato, barre +di avanzamento reali e console di log (commit `f909791`, deploy verificato +confrontando l'impronta degli asset). `MAX_UPLOAD_MB=2048` è impostato **solo** +su develop; la produzione non è stata toccata e resta sul suo deploy +precedente, senza quella variabile. + +**Il 31/07 è nata la sotto-app "Piantine"** (nuova voce nel menu +Amministrazione): KML dell'area città, editor dei poligoni su OpenStreetMap, +immagini HD dal container `map-renderer` e PDF sul modulo S-12 con storico +versioni. Architettura e agganci sono descritti in [Decisioni +architetturali](#le-piantine-sono-una-sotto-app-renderer-esterno-agganci-minimi). +Al primo deploy il compose costruisce anche l'immagine del renderer; non +servono variabili nuove in Dokploy. + +**Il 28/08 la sotto-app è stata rifatta e l'interfaccia ripassata.** Le piantine +si sono staccate dai territori (una piantina vive anche scollegata, il territorio +si crea dall'editor), il disegno è passato su una pagina a schermo intero, la +cartolina S-12 è stata reimpaginata (confini sulla prima pagina, piantina al 98% +sulla seconda) e la rigenerazione massiva è stata rimossa. In più il sistema +visivo è stato ripassato su tutta l'applicazione, non solo qui: vedi [Il sistema +visivo sta nei token](#il-sistema-visivo-sta-nei-token-non-nelle-viste). + +Nello stesso giro è emerso e corretto un guasto d'ambiente che bloccava **tutti** +gli upload in sviluppo: vedi [`HTTP_HOST` a nginx deve conservare la +porta](#http_host-a-nginx-deve-conservare-la-porta). + +Il 30/07 sono state fatte due cose. + +**La suite di test, che prima non esisteva**: 79 test eseguibili con un comando, +senza dipendere da altri servizi. Sono **verdi** ma coprono ancora una frazione +della logica di business — vedi *Cosa resta aperto*. Non fanno parte +dell'immagine di produzione e non girano su un deploy: sono uno strumento +locale. + +**La pagina "Backup e import" è stata riorganizzata**: cinque zone numerate con +un indice che ci salta sopra evidenziandole, un solo sistema di pulsanti (con +gli stati di focus, che prima non c'erano), e **una sola console** per tutte le +operazioni, agganciata in fondo alla finestra, al posto dei cinque riquadri di +log sparsi. L'azzeramento è diventato un componente a sé per poter stare in +fondo alla pagina. Tutto verificato lato server dai test; **il comportamento nel +browser — apertura automatica della console, scorrimento, lampeggio delle zone — +non è stato provato**, perché richiede un browser: è la prima cosa da guardare +aprendo la pagina. + +Il ripristino dall'interfaccia resta impraticabile per pacchetti grandi finché +non si decide come aggirare Cloudflare: è la prima voce di *Cosa resta aperto* +e l'unica che richiede una decisione, non del lavoro. + --- ## Dove gira davvero Due ambienti su Dokploy, stesso progetto `TerManager2`, entrambi alimentati dal -repository Gitea `francesco/termanager2` con **autoDeploy attivo**. +repository Gitea `francesco/termanager2` su `repository.pyconetwork.it` (il +vecchio `git.pyconetwork.it` non è più la sorgente). -| Ambiente | Branch | Dominio | Compose (`appName`) | -|--------------|--------|-------------------------------------|----------------------------| -| `production` | `main` | `termanager2.pyconetwork.it` | `termanager2-core-vizcxr` | -| `develop` | `dev` | `demo-termanager.pyconetwork.it` | `termanager2-core-vbekis` | +| Ambiente | Branch | Dominio | Compose (nome / `appName` / `composeId`) | +|--------------|--------|-------------------------------------|------------------------------------------| +| `production` | `main` | `termanager2.pyconetwork.it` | `termanager2-core-production` / `termanager2-core-vizcxr` / `vh_NtgaPyfQintCbcB3p6` | +| `develop` | `dev` | `demo-termanager.pyconetwork.it` | `termanager2-core-demo` / `termanager2-core-vbekis` / `20DoOKciTzmhlPWdcqcxz` | Conseguenze pratiche, tutte imparate sul campo: +- **L'autodeploy non parte.** È attivo in Dokploy, ma dal 19 ottobre 2026 + nessun push ha avviato un deploy (il webhook di Gitea è da verificare): + dopo un push si aspettano un paio di minuti e, se in `deployment` non + compare niente di nuovo, si avvia a mano (`compose.deploy` via API, o + *Deploy* nell'interfaccia). Controllare prima di avviare, per non fare + deploy doppi. + +- **Test sul server, prima del push.** In locale (Windows) non ci sono PHP, + Docker né Node: la suite gira sul server Dokploy con una *schedule* di tipo + `dokploy-server` usa e getta, che costruisce lo stage `test` del Dockerfile + e lancia anche `node --test tests/js/`. Per provare un commit non ancora + pubblicato (il push su `dev` lo pubblicherebbe): `git bundle create x + ..dev`, bundle in base64 dentro lo script, `git + clone` della checkout di Dokploy in `/tmp` e `git fetch` del bundle. La + checkout di Dokploy (`/etc/dokploy/compose//code`) è **shallow** e + può essere indietro rispetto a `origin/dev` (i commit di sola + documentazione non la aggiornano): il bundle parte dal suo `git log -1`. La + schedule gira dentro il container di Dokploy: `docker run -v /tmp/...` non + vede quei file, si passano con `tar | docker run -i`. + - **Il branch decide l'ambiente.** Pubblicare su `dev` non tocca la produzione, che segue `main`. È la separazione su cui contare quando si vuole provare qualcosa senza rischi. @@ -1590,7 +1605,9 @@ blocchi che impedivano di scrivere il primo test sono stati rimossi: stage docker compose --profile test run --build --rm test ``` -**Coperto oggi — 294 test, tutti verdi:** +**Coperto oggi — 577 test PHP e 13 JavaScript, tutti verdi (20/10/2026).** +Senza Docker in locale si fanno girare sul server: vedi [Dove gira +davvero](#dove-gira-davvero). I file storici, con cosa proteggono: | File | Cosa protegge | |------|---------------| @@ -1607,6 +1624,21 @@ docker compose --profile test run --build --rm test | `tests/Unit/PiantinaRendererTest.php` | Il client del renderer: formato, qualita' e lato massimo davvero spediti, JPEG salvato con l'estensione giusta, il file di un formato precedente rimosso invece che lasciato orfano, errori e risposte non-immagine trasformati in messaggi leggibili, geometria corrotta bloccata prima della chiamata | | `tests/Feature/HomeMediaDurataTest.php` | La "media percorrenza congregazione": unità di misura, esclusione delle assegnazioni aperte, dei rientri nello stesso giorno, delle date impossibili ereditate dalla vecchia applicazione e degli altri anni di servizio | +Arrivati dopo (ottobre 2026), uno per funzione: `GruppiServizioTest` (gruppi +di servizio, compresa la rotta vera delle azioni Livewire del sorvegliante, +priorità e filtri), `InvioLinkTest` (invio del link, pulsante Rientro, Assegna +dalla Home), `StoricoAssegnazioniTest`, `SceltaRicercaTest` e +`tests/js/scelta.test.mjs` (scelta con ricerca), `RitornoTest`, +`AssegnaPrestazioniTest` (peso della pagina Assegna e delle righe +dell'elenco), `AssegnazioneSovrapposizioneTest`, `CampagnaCalcoliTest`, +`CambioPasswordTest`, `ConfermaEmailTest`, `PostaElettronicaTest`, +`PrivacyTest`, `LogSenzaNomiTest`, `AvvisiTest`, `CodaTest`, +`UltimoAmministratoreTest`, i test di import (`XmlExchangeImportTest`, +`ImportXmlFileOrfaniTest`, `ImportPdfTerritoriTest`, `PostoUnicoImportTest`, +`RipristinoBackupTest`) e dei file (`TerritorioFileTest`, +`TerritoriFileOrfaniTest`, `PubblicazionePiantinaTest`, +`AzzeramentoPiantineTest`, `PiantineAreaTest`). + Il primo giro ha già trovato tre difetti reali, tutti corretti: il warning di `ZipArchive::addFile()` che copriva il messaggio d'errore vero, il `GREATEST()` in migrazione, e `@disabled` dentro un tag di componente (vedi *Bug corretti*). @@ -1621,22 +1653,25 @@ nel browser, e nella suite non c'è un browser. Quella parte va provata a mano. entry, dimensione della singola entry, totale decompresso. Sono tre `if` che nessun test tocca, e il caso in cui contano non si presenta mai per sbaglio. - `Jobs/RestoreBackupArchive` e `Jobs/ResetApplication` **nel loro insieme**: - l'ordine staging → database → file, il `failed()` che libera il posto, la - sopravvivenza dell'amministratore che ha chiesto l'azzeramento, il - `password_change_required` acceso su tutte le righe. È la logica il cui - errore si scopre solo quando è troppo tardi. -- `Livewire/Settings/XmlExchange`: il **round-trip** export → import senza - perdite, che è il presidio contro il tipo di bug già visto con `legacy_id`. + in parte coperti (`RipristinoBackupTest`, `ZonaPericolosaTest`, + `AzzeramentoPiantineTest`, l'azzeramento in `GruppiServizioTest`); restano + da verificare l'ordine staging → database → file e il `failed()` che libera + il posto. È la logica il cui errore si scopre solo quando è troppo tardi. +- `Livewire/Settings/XmlExchange`: il round-trip export → import è coperto per + le parti nuove (gruppi, invio del link) e da `XmlExchangeImportTest`, ma + non campo per campo su tutto il dataset. - Le regole di business: anno teocratico (`AnnoTeocratico::perData` a cavallo - del 1º settembre), `Territorio::stato` e la soglia `giorni_per_smarrito`, - priorità automatica, percentuale di percorrenza delle campagne. + del 1º settembre), `Territorio::stato` e la soglia `giorni_per_smarrito`. + La priorità (`Territorio::ordinaPerPriorita`) e la percorrenza delle + campagne ora hanno i loro test. - Il permesso come contratto: che un `assistente` **non** possa modificare o eliminare un'assegnazione, una campagna, un territorio o un proclamatore. È scritto in tre posti (rotta, componente, vista) e un test di richiesta lo verificherebbe tutto insieme. -Attenzione a non scrivere test su ciò che SQLite non sa eseguire: la dashboard -(`Home`) e l'ordinamento di `Registro::render()` usano SQL specifico di MySQL. +Attenzione a non scrivere test su ciò che SQLite non sa eseguire: resta solo +l'ordinamento di `Registro::render()`, che usa SQL specifico di MySQL (la Home +non più: vedi *La suite di test ha un'immagine sua*). ### 2. Race su doppia assegnazione — chiusa il 09/10/2026 @@ -1703,6 +1738,18 @@ e `territorio_pdf_versioni`, con il numero del territorio per ricollegarle) e la cartella `piantine/` dell'area KML. Un archivio di formato 1 si ripristina lo stesso: piantine e storico restano vuoti, l'area in uso non si tocca. +### 8. L'autodeploy non parte + +Dal 19 ottobre 2026 nessun push su `dev` o `main` ha avviato un deploy, pur +con autoDeploy attivo in Dokploy. Da verificare il webhook del repository in +Gitea (`repository.pyconetwork.it`). Intanto ogni deploy si avvia a mano: +vedi [Dove gira davvero](#dove-gira-davvero). + +### 9. Gruppi di servizio e lavori del 20 ottobre non ancora in produzione + +Sono sulla demo e l'utente li sta provando lì; per il rilascio serve il suo +ok. Come portarli in produzione: [Stato attuale](#stato-attuale). + --- ## Trappole da conoscere @@ -1797,7 +1844,8 @@ cp .env.example .env # SESSION_SECURE_COOKIE=false, e le tre password docker network create dokploy-network -# 0. La suite passa (non serve nient'altro acceso, ~15 s) +# 0. La suite passa (non serve nient'altro acceso). Senza Docker in locale: +# sul server Dokploy, vedi "Dove gira davvero" docker compose --profile test run --build --rm test # 1. L'immagine builda diff --git a/README.md b/README.md index f12c739..47bdf96 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # TerManager2 Applicazione web per la gestione dell'assegnazione e del rientro dei territori -(cartoline) di congregazione, con statistiche di percorrenza, campagne speciali -e registro storico completo. +(cartoline) di congregazione, con statistiche di percorrenza, campagne speciali, +gruppi di servizio (un sorvegliante distribuisce ai membri un pacchetto di +territori) e registro storico completo. I dati personali dei proclamatori sono **cifrati nel database**, e ogni operazione è tracciata in un **audit log** consultabile. @@ -410,24 +411,27 @@ Redis, quindi puntarli su un driver diverso interrompe l'elaborazione dei job. Configurabili dall'interfaccia (**Impostazioni**, permesso `settings.manage`), non da variabili d'ambiente: -| Impostazione | Descrizione | -|--------------------------------|------------------------------------------------------| -| Nome congregazione | Mostrato nell'intestazione e nei PDF | -| Logo | Immagine nell'intestazione | -| URL pubblico base | Base dei link PDF condivisibili | -| Giorni giacenza "da assegnare" | Soglia oltre cui un territorio è da assegnare | -| Mesi giacenza "prioritari" | Soglia oltre cui un territorio diventa prioritario | -| Giorni per "smarrito" | Soglia oltre cui un'assegnazione è da rientrare | -| Elementi nelle liste della home| Quanti record mostrare nelle tre liste rapide | -| Validità link PDF (mesi) | Scadenza dei link pubblici alle cartoline | -| Download PDF visibile | Mostra o nasconde il pulsante di download | -| Retention audit log (giorni) | Dopo quanti giorni i log vengono cancellati | +| Impostazione | Descrizione | +|------------------------------------|------------------------------------------------------| +| Nome congregazione | Mostrato nell'intestazione, nei PDF e nell'informativa | +| Soglia da assegnare (giorni) | Dopo quanti giorni dal rientro un territorio è "da assegnare" | +| Soglia priorità (giorni) | Dopo quanti giorni dal rientro un territorio diventa prioritario | +| Soglia rientro (giorni) | Oltre questa un'assegnazione è "da rientrare"; è anche la durata del link alla piantina | +| Limite liste Home | Quanti territori mostrare nelle due liste della Home | +| "Scarica PDF" nel viewer | Mostra o nasconde il pulsante di download a chi apre il link | +| Conservazione audit (giorni) | Dopo quanti giorni le voci del registro delle attività si cancellano | + +Il server di posta ha una pagina sua (*Impostazioni → Server di posta*). Il +logo nell'intestazione, se c'è, arriva da un backup: la pagina non lo carica. --- ## Aggiornamenti -Push su `main` → **Deploy** su Dokploy. Ad ogni avvio il container: +Push su `main` → **Deploy** su Dokploy. Se l'autodeploy non parte entro un +paio di minuti (succede: vedi HANDOFF, *Dove gira davvero*), il deploy si +avvia a mano dall'interfaccia o con `compose.deploy`. Ad ogni avvio il +container: 1. ricrea le directory di storage e risolve `APP_KEY` 2. attende il database @@ -687,9 +691,11 @@ docker compose --profile dev up -d --build - Anno teocratico corrente (creato automaticamente, settembre → agosto) - Media mensile di percorrenza e durata media delle assegnazioni - Campagna attiva con barra di progresso -- Tre liste rapide con azione diretta: **da assegnare**, **prioritari**, - **da rientrare**; quando l'elenco è troncato, **"Vedi tutti"** è un pulsante - in testata che apre l'elenco territori già filtrato +- Due liste rapide con azione diretta: **da assegnare** (prima i prioritari, + con il badge ★, poi quelli fermi da più tempo) e **da rientrare**; quando + l'elenco è troncato, **"Vedi tutti"** è un pulsante in testata che apre + l'elenco territori già filtrato. "Assegna →" apre la pagina *Assegna* con + il territorio scelto e senza l'elenco dei territori assegnati ### Territori - CRUD completo con upload PDF e miniatura generata automaticamente @@ -949,10 +955,16 @@ Calcolato dinamicamente, non memorizzato: | `inattivo` | Contrassegnato come non attivo | ### Priorità -Un territorio è prioritario se lo è per **flag manuale** o perché non viene -assegnato da più mesi della soglia configurata. La soglia automatica **vince -sempre** sul flag: l'amministratore può aggiungere priorità, non toglierla -quando la soglia è scattata. +Un territorio è prioritario se lo è per **flag manuale** o perché è fermo +(dall'ultimo rientro, o dalla creazione se mai assegnato) da più giorni della +soglia configurata. La soglia automatica **vince sempre** sul flag: +l'amministratore può aggiungere priorità, non toglierla quando la soglia è +scattata. Assegnare il territorio azzera il flag. + +Dove si sceglie un territorio da dare — "Da assegnare" nella Home, l'aggiunta +al pacchetto di un gruppo, "Il mio gruppo" — l'ordine è lo stesso: prima i +prioritari, poi quelli fermi da più tempo, poi il numero +(`Territorio::ordinaPerPriorita()`). ### Percentuale di percorrenza delle campagne - **Denominatore**: tutti i territori attivi @@ -1000,14 +1012,16 @@ sempre. ``` TerManager2/ ├── app/ -│ ├── Console/Commands/ # audit:cleanup, backup:cleanup, app:create-initial-admin +│ ├── Console/Commands/ # audit:cleanup, backup:cleanup, app:create-initial-admin, +│ │ # utenti:conferma-email, territori:file-orfani │ ├── Http/Controllers/ # PDF pubblici, import, backup, login, cambio password -│ ├── Http/Middleware/ # RequirePasswordChange +│ ├── Http/Middleware/ # RequirePasswordChange, LimitaSorvegliante │ ├── Jobs/ # Import PDF, backup, ripristino, azzeramento (in coda) -│ ├── Livewire/ # Tutti i componenti di interfaccia -│ ├── Models/ # 9 modelli Eloquent +│ ├── Livewire/ # Tutti i componenti di interfaccia (Gruppi/: gruppi di servizio) +│ ├── Models/ # 12 modelli Eloquent │ ├── Providers/ # AppServiceProvider -│ └── Services/ # Miniature PDF, stato import, archivio backup +│ └── Services/ # Miniature PDF, import, archivio backup, posta, +│ # piantine, modulo S-13, gruppi di servizio ├── bootstrap/app.php # Middleware, proxy e host fidati ├── config/ # Configurazione Laravel ├── database/ @@ -1029,7 +1043,7 @@ TerManager2/ │ ├── components/btn.blade.php # L'unico pulsante dell'applicazione │ └── livewire/ # Una vista per componente ├── routes/web.php # Rotte con i relativi permessi -├── tests/ # Suite PHPUnit (profilo compose "test") +├── tests/ # Suite PHPUnit (profilo compose "test") e tests/js (Node) ├── phpunit.xml # Configurazione e ambiente dei test ├── docker-compose.yml # Unico, produzione e sviluppo ├── .env.example # Tutta la configurazione