Il gruppo ha un sorvegliante (proclamatore con utenza: email e password temporanea, senza conferma dell'email) e dei membri; l'amministratore mette in blocco dei territori nel pacchetto del gruppo. Il pacchetto non e' un'assegnazione e non tocca le statistiche; quando il sorvegliante da' un territorio a un membro nasce un'assegnazione normale con gruppo_servizio_id. Al rientro il territorio torna in reparto. Sciogliere il gruppo libera pacchetto e membri e disattiva l'utenza. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
115 lines
3.3 KiB
PHP
115 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Spatie\Activitylog\Traits\LogsActivity;
|
|
use Spatie\Activitylog\LogOptions;
|
|
|
|
class Proclamatore extends Model
|
|
{
|
|
use SoftDeletes, LogsActivity;
|
|
|
|
protected $table = 'proclamatori';
|
|
|
|
// Niente email né telefono (tolti il 19 ottobre 2026): il link della
|
|
// piantina lo manda chi assegna, copiandolo o con "Condividi", e il
|
|
// destinatario lo sceglie dai propri contatti.
|
|
protected $fillable = ['nome', 'cognome', 'attivo', 'gruppo_servizio_id'];
|
|
|
|
/** Il gruppo di servizio di cui è membro (al massimo uno). */
|
|
public function gruppo()
|
|
{
|
|
return $this->belongsTo(GruppoServizio::class, 'gruppo_servizio_id');
|
|
}
|
|
|
|
/** L'utenza, se è sorvegliante di un gruppo. */
|
|
public function utente()
|
|
{
|
|
return $this->hasOne(User::class, 'proclamatore_id');
|
|
}
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'nome' => 'encrypted',
|
|
'cognome' => 'encrypted',
|
|
'attivo' => 'boolean',
|
|
'gruppo_servizio_id' => 'integer',
|
|
];
|
|
}
|
|
|
|
public function getActivitylogOptions(): LogOptions
|
|
{
|
|
return LogOptions::defaults()
|
|
->logOnly(['attivo']) // Do NOT log nome/cognome in audit (encrypted, GDPR)
|
|
->logOnlyDirty()
|
|
->dontSubmitEmptyLogs();
|
|
}
|
|
|
|
/**
|
|
* Full name (decrypted, only visible in PHP/UI).
|
|
*/
|
|
public function getNomeCompletoAttribute(): string
|
|
{
|
|
return trim($this->cognome . ' ' . $this->nome);
|
|
}
|
|
|
|
/**
|
|
* L'opzione per il campo di scelta con ricerca (<x-scelta>): si cerca per
|
|
* cognome e nome. Nome e cognome sono cifrati nel database: la ricerca
|
|
* avviene sui valori già decifrati, nella pagina, mai in SQL.
|
|
*/
|
|
public function opzioneScelta(?string $motivo = null): array
|
|
{
|
|
return array_filter([
|
|
'valore' => (string) $this->id,
|
|
'etichetta' => $this->nome_completo,
|
|
'chiavi' => [(string) $this->cognome, (string) $this->nome],
|
|
'motivo' => $motivo,
|
|
], fn ($v) => $v !== null);
|
|
}
|
|
|
|
/**
|
|
* Le opzioni di un elenco di proclamatori, in ordine di cognome e nome.
|
|
* L'ordine si fa qui: in SQL ordinerebbe i testi cifrati.
|
|
*/
|
|
public static function opzioniScelta(iterable $proclamatori): array
|
|
{
|
|
return collect($proclamatori)
|
|
->sortBy(fn (self $p) => mb_strtolower($p->cognome . ' ' . $p->nome))
|
|
->map(fn (self $p) => $p->opzioneScelta($p->attivo ? null : 'non attivo'))
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
public function assegnazioni()
|
|
{
|
|
return $this->hasMany(Assegnazione::class, 'proclamatore_id');
|
|
}
|
|
|
|
public function assegnazioniAperte()
|
|
{
|
|
return $this->hasMany(Assegnazione::class, 'proclamatore_id')
|
|
->whereNull('returned_at');
|
|
}
|
|
|
|
public function scopeAttivi($query)
|
|
{
|
|
return $query->where('attivo', true);
|
|
}
|
|
|
|
/**
|
|
* Anonymize this proclamatore (GDPR right to be forgotten).
|
|
*/
|
|
public function anonimizza(): void
|
|
{
|
|
$this->nome = 'Anonimo';
|
|
$this->cognome = 'Proclamatore #' . $this->id;
|
|
$this->attivo = false;
|
|
$this->save();
|
|
$this->delete(); // soft delete
|
|
}
|
|
}
|