++ gruppi: il gruppo sciolto si elimina; il sorvegliante entra con un nome utente dal nome del gruppo

- Sciolto, il gruppo non resta nello storico: si elimina (via sciolto_at;
  la migrazione elimina anche quelli gia' sciolti). I territori solo nel
  pacchetto tornano in reparto; quelli gia' dati restano ai membri, e
  l'assegnazione conserva il nome del gruppo (assegnazioni.gruppo_nome).
- Creando il gruppo non si chiede l'email: users.username dal nome del
  gruppo ("Gruppo Zisa" -> gruppo.zisa), users.email facoltativa. Login con
  "Email o nome utente". Nome del gruppo unico.
- Sciolto il gruppo il nome utente si libera (l'utenza disattivata prende
  ex.sorvegliante.<id>). XML: nome utente e gruppo_nome.
- Profilo, testata, cambio password e Utenti mostrano il nome utente dove
  manca l'email. Informativa privacy, README, HANDOFF e test aggiornati.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Francesco Picone
2026-10-20 11:45:00 +02:00
parent a93539242a
commit aaffeb7de7
29 changed files with 408 additions and 199 deletions

View File

@@ -26,6 +26,7 @@ class Assegnazione extends Model
'created_by',
'returned_by',
'gruppo_servizio_id',
'gruppo_nome',
];
/** Il gruppo di servizio da cui è nata (data dal sorvegliante a un membro). */

View File

@@ -17,9 +17,9 @@ use Spatie\Activitylog\Traits\LogsActivity;
* gruppo_servizio_id per dire da dove viene. Al rientro il territorio torna in
* reparto ed esce dal pacchetto.
*
* Un gruppo sciolto (sciolto_at) non ha più membri né pacchetto, ma resta: le
* assegnazioni nate da lui continuano a citarlo. Logica in
* App\Services\GruppiServizio.
* Sciolto, il gruppo si elimina: non resta nello storico (decisione del 20
* ottobre 2026). Le assegnazioni già date dal sorvegliante ne conservano solo
* il nome (assegnazioni.gruppo_nome). Logica in App\Services\GruppiServizio.
*/
class GruppoServizio extends Model
{
@@ -27,17 +27,17 @@ class GruppoServizio extends Model
protected $table = 'gruppi_servizio';
protected $fillable = ['nome', 'sorvegliante_id', 'sciolto_at'];
protected $fillable = ['nome', 'sorvegliante_id'];
protected function casts(): array
{
return ['sciolto_at' => 'datetime', 'sorvegliante_id' => 'integer'];
return ['sorvegliante_id' => 'integer'];
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnly(['nome', 'sorvegliante_id', 'sciolto_at'])
->logOnly(['nome', 'sorvegliante_id'])
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
@@ -63,16 +63,6 @@ class GruppoServizio extends Model
return $this->hasMany(Assegnazione::class, 'gruppo_servizio_id');
}
public function getScioltoAttribute(): bool
{
return $this->sciolto_at !== null;
}
public function scopeAttivi($query)
{
return $query->whereNull('sciolto_at');
}
/**
* I numeri del gruppo per la scheda del proclamatore e il profilo del
* sorvegliante: solo conteggi in SQL, nessun nome da decifrare.

View File

@@ -52,7 +52,7 @@ class Proclamatore extends Model
/** Il gruppo attivo di cui è sorvegliante, se c'è. */
public function gruppoSorvegliato(): ?GruppoServizio
{
return GruppoServizio::attivi()->where('sorvegliante_id', $this->id)->first();
return GruppoServizio::where('sorvegliante_id', $this->id)->first();
}
private function lasciaIlGruppo(string $motivo): void
@@ -185,7 +185,8 @@ class Proclamatore extends Model
$utente->syncRoles([]);
$utente->forceFill([
'name' => 'Anonimo',
'email' => "utente-{$utente->id}@anonimo.invalid",
'email' => null,
'username' => "anonimo.{$utente->id}",
'password' => \Illuminate\Support\Str::random(40),
'remember_token' => null,
'disattivato_at' => $utente->disattivato_at ?? now(),

View File

@@ -22,6 +22,7 @@ class User extends Authenticatable implements MustVerifyEmail
protected $fillable = [
'name',
'email',
'username',
'password',
'password_change_required',
'proclamatore_id',
@@ -59,14 +60,23 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->hasRole('sorvegliante');
}
/** Il gruppo (attivo) di cui è sorvegliante, se c'è. */
/** Il gruppo di cui è sorvegliante, se c'è (sciolto, il gruppo non esiste più). */
public function gruppoSorvegliato(): ?GruppoServizio
{
return $this->proclamatore_id
? GruppoServizio::attivi()->where('sorvegliante_id', $this->proclamatore_id)->first()
? GruppoServizio::where('sorvegliante_id', $this->proclamatore_id)->first()
: null;
}
/**
* Con cosa entra: l'email, o il nome utente per il sorvegliante di
* gruppo, che l'email non ce l'ha. Da mostrare dove prima c'era l'email.
*/
public function getIdentificativoAttribute(): string
{
return (string) ($this->email ?? $this->username ?? $this->name);
}
/** Utenza disattivata (gruppo sciolto): non può più entrare. */
public function isDisattivato(): bool
{