++ 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:
@@ -22,20 +22,24 @@ use InvalidArgumentException;
|
||||
class GruppiServizio
|
||||
{
|
||||
/**
|
||||
* Crea il gruppo e l'utenza del sorvegliante: ruolo "sorvegliante",
|
||||
* password temporanea da cambiare al primo accesso, nessuna conferma
|
||||
* dell'email. Restituisce il gruppo e la password temporanea, da mostrare
|
||||
* una volta sola all'amministratore.
|
||||
* Crea il gruppo e l'utenza del sorvegliante: nome utente ricavato dal
|
||||
* nome del gruppo (niente email), ruolo "sorvegliante", password
|
||||
* temporanea da cambiare al primo accesso. Restituisce il gruppo e la
|
||||
* password temporanea, da mostrare una volta sola all'amministratore
|
||||
* insieme al nome utente ($gruppo->sorvegliante->utente->username).
|
||||
*
|
||||
* @param int[] $membri id dei proclamatori, oltre al sorvegliante
|
||||
* @return array{0: GruppoServizio, 1: string}
|
||||
*/
|
||||
public function crea(string $nome, Proclamatore $sorvegliante, string $email, array $membri = []): array
|
||||
public function crea(string $nome, Proclamatore $sorvegliante, array $membri = []): array
|
||||
{
|
||||
$nome = trim($nome);
|
||||
if ($nome === '') {
|
||||
throw new InvalidArgumentException('Il nome del gruppo è obbligatorio.');
|
||||
}
|
||||
if (GruppoServizio::where('nome', $nome)->exists()) {
|
||||
throw new InvalidArgumentException("Esiste già un gruppo «{$nome}».");
|
||||
}
|
||||
if (! $sorvegliante->attivo || $sorvegliante->trashed()) {
|
||||
throw new InvalidArgumentException('Il sorvegliante deve essere un proclamatore attivo.');
|
||||
}
|
||||
@@ -48,13 +52,10 @@ class GruppiServizio
|
||||
if ($esistente && ! $esistente->isDisattivato()) {
|
||||
throw new InvalidArgumentException("{$sorvegliante->nome_completo} ha già un'utenza.");
|
||||
}
|
||||
if (User::where('email', $email)->when($esistente, fn ($q) => $q->whereKeyNot($esistente->id))->exists()) {
|
||||
throw new InvalidArgumentException("L'email {$email} è già usata da un altro utente.");
|
||||
}
|
||||
|
||||
$password = Str::password(12, symbols: false);
|
||||
|
||||
$gruppo = DB::transaction(function () use ($nome, $sorvegliante, $email, $membri, $password, $esistente) {
|
||||
$gruppo = DB::transaction(function () use ($nome, $sorvegliante, $membri, $password, $esistente) {
|
||||
$gruppo = GruppoServizio::create(['nome' => $nome, 'sorvegliante_id' => $sorvegliante->id]);
|
||||
$sorvegliante->update(['gruppo_servizio_id' => $gruppo->id]);
|
||||
$this->aggiungiMembri($gruppo, $membri);
|
||||
@@ -62,7 +63,7 @@ class GruppiServizio
|
||||
$utente = $esistente ?? new User(['proclamatore_id' => $sorvegliante->id]);
|
||||
$utente->forceFill([
|
||||
'name' => $sorvegliante->nome_completo,
|
||||
'email' => $email,
|
||||
'username' => self::nomeUtente($nome),
|
||||
'password' => $password,
|
||||
'password_change_required' => true,
|
||||
'disattivato_at' => null,
|
||||
@@ -79,6 +80,23 @@ class GruppiServizio
|
||||
return [$gruppo, $password];
|
||||
}
|
||||
|
||||
/**
|
||||
* Il nome utente dal nome del gruppo: minuscole, senza accenti, parole
|
||||
* unite da un punto ("Gruppo Noce" → "gruppo.noce"); se è già preso, un
|
||||
* numero in coda ("zisa2"). Mai una chiocciola: al login distingue il
|
||||
* nome utente dall'email.
|
||||
*/
|
||||
public static function nomeUtente(string $nomeGruppo): string
|
||||
{
|
||||
$base = Str::limit(Str::slug($nomeGruppo, '.') ?: 'gruppo', 50, '');
|
||||
$nome = $base;
|
||||
for ($n = 2; User::where('username', $nome)->exists(); $n++) {
|
||||
$nome = $base . $n;
|
||||
}
|
||||
|
||||
return $nome;
|
||||
}
|
||||
|
||||
/** @param int[] $ids */
|
||||
public function aggiungiMembri(GruppoServizio $gruppo, array $ids): void
|
||||
{
|
||||
@@ -205,6 +223,8 @@ class GruppiServizio
|
||||
'assigned_at' => $data,
|
||||
'created_by' => auth()->id(),
|
||||
'gruppo_servizio_id' => $gruppo->id,
|
||||
// Il nome resta anche quando il gruppo, sciolto, non c'è più.
|
||||
'gruppo_nome' => $gruppo->nome,
|
||||
'link_da_inviare' => (bool) $territorio->pdf_path,
|
||||
]);
|
||||
});
|
||||
@@ -225,52 +245,58 @@ class GruppiServizio
|
||||
}
|
||||
|
||||
/**
|
||||
* Scioglie il gruppo (si toglie il ruolo di sorvegliante): i territori del
|
||||
* pacchetto non ancora dati tornano in reparto, i membri restano senza
|
||||
* gruppo, l'utenza del sorvegliante si disattiva. Le assegnazioni già
|
||||
* fatte restano com'erano, con il loro riferimento al gruppo, che resta
|
||||
* nello storico come sciolto.
|
||||
* Scioglie il gruppo (si toglie il ruolo di sorvegliante, o il
|
||||
* sorvegliante non è più attivo) e lo elimina: non resta nello storico.
|
||||
* I territori del pacchetto non ancora dati tornano in reparto; quelli
|
||||
* già dati restano ai membri come assegnazioni qualsiasi, che conservano
|
||||
* solo il nome del gruppo (gruppo_nome). I membri restano senza gruppo.
|
||||
* L'utenza del sorvegliante si disattiva e libera il nome utente; resta
|
||||
* perché è l'autrice delle assegnazioni che ha fatto.
|
||||
*/
|
||||
public function sciogli(GruppoServizio $gruppo, ?string $motivo = null): void
|
||||
{
|
||||
if ($gruppo->sciolto_at) {
|
||||
if (! GruppoServizio::whereKey($gruppo->id)->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($gruppo, $motivo) {
|
||||
// Esce dal pacchetto solo quello non ancora dato: un territorio
|
||||
// assegnato resta del suo gruppo finché non rientra (Assegnazione
|
||||
// lo libera al rientro), così non può essere ridato due volte.
|
||||
$nonDati = Territorio::withTrashed()
|
||||
$liberati = Territorio::withTrashed()
|
||||
->where('gruppo_servizio_id', $gruppo->id)
|
||||
->whereDoesntHave('assegnazioni', fn ($q) => $q->whereNull('returned_at'));
|
||||
$liberati = (clone $nonDati)->pluck('numero');
|
||||
$nonDati->update(['gruppo_servizio_id' => null]);
|
||||
|
||||
->whereDoesntHave('assegnazioni', fn ($q) => $q->whereNull('returned_at'))
|
||||
->pluck('numero');
|
||||
Territorio::withTrashed()->where('gruppo_servizio_id', $gruppo->id)->update(['gruppo_servizio_id' => null]);
|
||||
Proclamatore::withTrashed()->where('gruppo_servizio_id', $gruppo->id)->update(['gruppo_servizio_id' => null]);
|
||||
Assegnazione::where('gruppo_servizio_id', $gruppo->id)->update(['gruppo_servizio_id' => null]);
|
||||
|
||||
$utente = User::where('proclamatore_id', $gruppo->sorvegliante_id)->first();
|
||||
if ($utente) {
|
||||
$utente->removeRole('sorvegliante');
|
||||
$utente->forceFill(['disattivato_at' => now(), 'remember_token' => null])->save();
|
||||
// Il nome utente del gruppo si libera; al suo posto uno che
|
||||
// identifica l'utenza (backup XML) e con cui non si entra.
|
||||
$utente->forceFill([
|
||||
'disattivato_at' => now(),
|
||||
'remember_token' => null,
|
||||
'username' => "ex.sorvegliante.{$utente->id}",
|
||||
])->save();
|
||||
}
|
||||
|
||||
$gruppo->update(['sciolto_at' => now()]);
|
||||
|
||||
activity()->causedBy(auth()->user())->performedOn($gruppo)
|
||||
->withProperties(array_filter([
|
||||
'gruppo' => $gruppo->nome,
|
||||
'territori_liberati' => $liberati->all(),
|
||||
'utente_disattivato' => $utente?->id,
|
||||
'motivo' => $motivo,
|
||||
], fn ($v) => $v !== null))
|
||||
->log('gruppo_sciolto');
|
||||
|
||||
$gruppo->delete();
|
||||
});
|
||||
}
|
||||
|
||||
private function attivo(GruppoServizio $gruppo): void
|
||||
{
|
||||
if ($gruppo->sciolto_at) {
|
||||
throw new InvalidArgumentException('Il gruppo è stato sciolto: creane uno nuovo.');
|
||||
if (! $gruppo->exists || ! GruppoServizio::whereKey($gruppo->id)->exists()) {
|
||||
throw new InvalidArgumentException('Il gruppo non esiste più: è stato sciolto.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user