++ 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

@@ -525,18 +525,22 @@ class XmlExchange extends Component
$utentiDaCollegare = [];
foreach (($xml->users->user ?? []) as $userNode) {
$email = trim((string) ($userNode->email ?? ''));
$username = trim((string) ($userNode->username ?? ''));
$legacyId = (string) ($userNode['legacy_id'] ?? '');
// Il sorvegliante di gruppo non ha email: si riconosce dal nome utente.
if ($email === '' && $username === '') {
$this->pushImportIssue('utente', $legacyId, 'Email e nome utente mancanti');
// Nei file del 19-20 ottobre 2026 c'erano le utenze disattivate
// degli ex sorveglianti: ora, sciolto il gruppo, l'utenza non
// esiste più. Le loro assegnazioni passano a chi importa.
if (trim((string) ($userNode->disattivato_at ?? '')) !== '') {
continue;
}
$user = User::query()->firstOrNew($email !== '' ? ['email' => $email] : ['username' => $username]);
$user->name = (string) ($userNode->name ?? ($email !== '' ? $email : $username));
$user->username = $username !== '' ? $username : null;
if ($email === '') {
$this->pushImportIssue('utente', $legacyId, 'Email mancante');
continue;
}
$user = User::query()->firstOrNew(['email' => $email]);
$user->name = (string) ($userNode->name ?? $email);
if (! $user->exists) {
// password is NOT NULL; the real hash is written raw below.
$user->password = Str::password(24);
@@ -597,10 +601,7 @@ class XmlExchange extends Component
// Utenza di un sorvegliante: si collega al proclamatore dopo che
// i proclamatori sono stati importati (vedi i gruppi di servizio).
$utentiDaCollegare[$user->id] = [
'proclamatore' => (string) ($userNode->legacy_proclamatore_id ?? ''),
'disattivato_at' => $this->normalizeDateTimeForDb((string) ($userNode->disattivato_at ?? '')),
];
$utentiDaCollegare[$user->id] = (string) ($userNode->legacy_proclamatore_id ?? '');
$stats['utenti_importati']++;
if ($legacyId !== '') {
@@ -681,10 +682,9 @@ class XmlExchange extends Component
DB::table('proclamatori')->where('id', $idProclamatore)->update(['gruppo_servizio_id' => $gruppoMap[$legacyGruppo]]);
}
}
foreach ($utentiDaCollegare as $idUtente => $collegamento) {
foreach ($utentiDaCollegare as $idUtente => $legacyProclamatore) {
DB::table('users')->where('id', $idUtente)->update([
'proclamatore_id' => $proclamatoreMap[$collegamento['proclamatore']] ?? null,
'disattivato_at' => $collegamento['disattivato_at'],
'proclamatore_id' => $proclamatoreMap[$legacyProclamatore] ?? null,
]);
}
@@ -869,15 +869,13 @@ class XmlExchange extends Component
'legacy_id' => (string) $u->id,
'name' => (string) $u->name,
'email' => (string) $u->email,
'username' => (string) $u->username,
'password' => (string) $u->getAuthPassword(),
'email_verified_at' => optional($u->email_verified_at)->toIso8601String() ?? '',
// remember_token no: all'import si azzera comunque, e nel file
// sarebbe solo una credenziale in più per chi lo tiene.
'roles' => $u->roles->pluck('name')->all(),
// Sorvegliante di gruppo: il proclamatore collegato e se l'utenza è disattivata.
// Sorvegliante di gruppo: il proclamatore collegato.
'proclamatore_id' => $u->proclamatore_id ? (string) $u->proclamatore_id : '',
'disattivato_at' => optional($u->disattivato_at)->toIso8601String() ?? '',
'created_at' => optional($u->created_at)->toIso8601String() ?? '',
'updated_at' => optional($u->updated_at)->toIso8601String() ?? '',
])->all(),
@@ -1285,11 +1283,9 @@ class XmlExchange extends Component
}
$this->addXmlText($node, 'name', (string) ($user['name'] ?? ''));
$this->addXmlText($node, 'email', (string) ($user['email'] ?? ''));
$this->addXmlText($node, 'username', (string) ($user['username'] ?? ''));
$this->addXmlText($node, 'password', (string) ($user['password'] ?? ''));
$node->addChild('email_verified_at', (string) ($user['email_verified_at'] ?? ''));
$node->addChild('legacy_proclamatore_id', (string) ($user['proclamatore_id'] ?? ''));
$node->addChild('disattivato_at', (string) ($user['disattivato_at'] ?? ''));
$node->addChild('created_at', (string) ($user['created_at'] ?? ''));
$node->addChild('updated_at', (string) ($user['updated_at'] ?? ''));
$rolesNode = $node->addChild('roles');