++ gruppi: email fittizia iniziale+cognome per il sorvegliante; sciolto il gruppo l'utenza si elimina

- L'utenza del sorvegliante ha un'email fittizia creata dall'app:
  iniziale del nome e cognome @termanager.it (ffontana@termanager.it),
  come gli altri utenti. Via il nome utente: login di nuovo per email.
- Sciolto il gruppo l'utenza si elimina, il sorvegliante torna un
  proclamatore come gli altri: assegnazioni a chi scioglie, registro a nome
  "Sorvegliante del gruppo ...". Via le utenze disattivate.
- User::eliminaConservandoStorico(): l'eliminazione della pagina Utenti in
  un metodo solo, usato anche dallo scioglimento.
- Migrazione 000030: elimina le utenze gia' disattivate, toglie
  users.username e users.disattivato_at, email di nuovo obbligatoria.
- XML, 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 12:09:15 +02:00
parent 69250ecb8c
commit ec2e7bd7e9
26 changed files with 347 additions and 311 deletions

View File

@@ -175,22 +175,9 @@ class Proclamatore extends Model
$this->cognome = 'Proclamatore #' . $this->id;
$this->attivo = false;
$this->gruppo_servizio_id = null;
// Se era sorvegliante, il salvataggio (non più attivo) scioglie il
// gruppo ed elimina la sua utenza: booted().
$this->save();
$this->delete(); // soft delete
// L'utenza di un ex sorvegliante (già disattivata: il sorvegliante di
// un gruppo attivo non si anonimizza) porta nome ed email in chiaro.
$utente = $this->utente()->first();
if ($utente) {
$utente->syncRoles([]);
$utente->forceFill([
'name' => 'Anonimo',
'email' => null,
'username' => "anonimo.{$utente->id}",
'password' => \Illuminate\Support\Str::random(40),
'remember_token' => null,
'disattivato_at' => $utente->disattivato_at ?? now(),
])->save();
}
}
}

View File

@@ -9,6 +9,8 @@ use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Models\Activity;
use Illuminate\Support\Facades\DB;
/**
* MustVerifyEmail: l'account funziona solo con l'indirizzo confermato
@@ -22,11 +24,9 @@ class User extends Authenticatable implements MustVerifyEmail
protected $fillable = [
'name',
'email',
'username',
'password',
'password_change_required',
'proclamatore_id',
'disattivato_at',
];
protected $hidden = [
@@ -40,7 +40,6 @@ class User extends Authenticatable implements MustVerifyEmail
'email_verified_at' => 'datetime',
'password' => 'hashed',
'password_change_required' => 'boolean',
'disattivato_at' => 'datetime',
'proclamatore_id' => 'integer',
];
}
@@ -60,7 +59,7 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->hasRole('sorvegliante');
}
/** Il gruppo di cui è sorvegliante, se c'è (sciolto, il gruppo non esiste più). */
/** Il gruppo di cui è sorvegliante (sciolto il gruppo, l'utenza non esiste più). */
public function gruppoSorvegliato(): ?GruppoServizio
{
return $this->proclamatore_id
@@ -69,18 +68,37 @@ class User extends Authenticatable implements MustVerifyEmail
}
/**
* 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.
* Elimina l'utenza senza perdere lo storico: le assegnazioni che ha fatto
* o fatto rientrare passano a $subentra (la colonna non può restare
* vuota), e nel registro delle attività le sue voci si tengono, con
* scritto chi era ($nome, $email) al posto del collegamento all'utenza.
*/
public function getIdentificativoAttribute(): string
public function eliminaConservandoStorico(self $subentra, string $nome, ?string $email): void
{
return (string) ($this->email ?? $this->username ?? $this->name);
}
DB::transaction(function () use ($subentra, $nome, $email) {
$eliminatoIl = now()->toDateTimeString();
/** Utenza disattivata (gruppo sciolto): non può più entrare. */
public function isDisattivato(): bool
{
return $this->disattivato_at !== null;
Activity::query()
->where('causer_type', self::class)
->where('causer_id', $this->id)
->chunkById(200, function ($attivita) use ($nome, $email, $eliminatoIl) {
foreach ($attivita as $voce) {
$proprieta = $voce->properties?->toArray() ?? [];
$proprieta['causer_name'] = $nome;
$proprieta['causer_email'] = $email;
$proprieta['causer_deleted_at'] = $eliminatoIl;
$voce->properties = $proprieta;
$voce->save();
}
});
Assegnazione::query()->where('created_by', $this->id)->update(['created_by' => $subentra->id]);
Assegnazione::query()->where('returned_by', $this->id)->update(['returned_by' => $subentra->id]);
$this->syncRoles([]);
$this->delete();
});
}
/**