++ gruppi di servizio: sorvegliante con utenza limitata, pacchetto di territori, assegnazione ai membri
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>
This commit is contained in:
@@ -2,10 +2,9 @@
|
||||
|
||||
namespace App\Livewire\Assegnazioni;
|
||||
|
||||
use App\Livewire\Concerns\InviaLink;
|
||||
use App\Livewire\Concerns\RicordaRitorno;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\On;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Assegnazione;
|
||||
@@ -15,25 +14,7 @@ use Illuminate\Support\Facades\DB;
|
||||
class Assegna extends Component
|
||||
{
|
||||
use RicordaRitorno;
|
||||
|
||||
// ─── Invio del link ─────────────────────────────────────────
|
||||
//
|
||||
// Dopo l'assegnazione non si torna subito alla pagina: si sceglie il
|
||||
// canale (copia link o Condividi: il destinatario lo sceglie chi usa
|
||||
// l'app dai suoi contatti, i proclamatori non hanno numero né email),
|
||||
// si dice se il link è partito, e solo allora si va avanti. Se la
|
||||
// procedura si interrompe, l'assegnazione resta con link_da_inviare e lo
|
||||
// si segnala dove compare.
|
||||
|
||||
/** L'assegnazione di cui si sta mandando il link. */
|
||||
#[Locked]
|
||||
public ?int $invioId = null;
|
||||
|
||||
/** Il canale usato in questo invio (Assegnazione::CANALI_LINK). */
|
||||
#[Locked]
|
||||
public ?string $canaleUsato = null;
|
||||
|
||||
public string $erroreInvio = '';
|
||||
use InviaLink;
|
||||
|
||||
public ?int $territorio_id = null;
|
||||
public ?int $proclamatore_id = null;
|
||||
@@ -83,6 +64,12 @@ class Assegna extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
// Nel pacchetto di un gruppo di servizio: lo assegna il sorvegliante.
|
||||
if ($territorio->gruppo_servizio_id) {
|
||||
$this->addError('territorio_id', "Il territorio {$territorio->numero} è nel pacchetto del gruppo {$territorio->gruppo?->nome}: lo assegna il sorvegliante, oppure va prima tolto dal pacchetto.");
|
||||
return;
|
||||
}
|
||||
|
||||
$proclamatore = Proclamatore::findOrFail($this->proclamatore_id);
|
||||
if (!$proclamatore->attivo) {
|
||||
$this->addError('proclamatore_id', "Il proclamatore {$proclamatore->nome_completo} è inattivo.");
|
||||
@@ -140,91 +127,16 @@ class Assegna extends Component
|
||||
$this->apriInvio($assegnazione->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apre l'invio del link: subito dopo l'assegnazione, o da un'assegnazione
|
||||
* rimasta in sospeso ("Invia link" nell'elenco, che è un componente a sé
|
||||
* e lo chiede con l'evento apri-invio).
|
||||
*/
|
||||
#[On('apri-invio')]
|
||||
public function apriInvio(int $assegnazioneId): void
|
||||
// ─── Invio del link (trait InviaLink) ──────────────────────
|
||||
|
||||
protected function puoInviare(Assegnazione $assegnazione): bool
|
||||
{
|
||||
$this->authorize('territori.assign');
|
||||
|
||||
$assegnazione = Assegnazione::aperte()->find($assegnazioneId);
|
||||
if (! $assegnazione) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invioId = $assegnazione->id;
|
||||
$this->canaleUsato = null;
|
||||
$this->erroreInvio = '';
|
||||
return auth()->user()->can('territori.assign');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copia link o Condividi: l'invio vero lo fa chi usa l'app (il
|
||||
* destinatario lo sceglie lui dai suoi contatti), qui si annota il canale.
|
||||
*/
|
||||
public function canale(string $canale): void
|
||||
protected function paginaDopoInvio(): string
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione || ! in_array($canale, Assegnazione::CANALI_ATTIVI, true) || ! $assegnazione->shortPdfUrl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->canaleUsato = $canale;
|
||||
$this->erroreInvio = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* La risposta a "Hai inviato il link?": la si registra e solo adesso si
|
||||
* va avanti. "Sì" vuole un canale usato; "No" lascia il link da inviare.
|
||||
*/
|
||||
public function concludiInvio(bool $inviato)
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione) {
|
||||
$this->invioId = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($inviato && $this->canaleUsato === null) {
|
||||
$this->erroreInvio = 'Scegli prima come mandare il link.';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$assegnazione->registraInvioLink($inviato, $this->canaleUsato);
|
||||
|
||||
// Nel log il canale e l'id del proclamatore, mai nome, numero o email.
|
||||
activity()->causedBy(auth()->user())
|
||||
->performedOn($assegnazione)
|
||||
->withProperties([
|
||||
'territorio' => $assegnazione->territorio?->numero,
|
||||
'proclamatore_id' => $assegnazione->proclamatore_id,
|
||||
'canale' => $inviato ? $this->canaleUsato : null,
|
||||
])
|
||||
->log($inviato ? 'link_inviato' : 'link_non_inviato');
|
||||
|
||||
$numero = $assegnazione->territorio?->numero;
|
||||
$nome = $assegnazione->proclamatore?->nome_completo;
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
|
||||
session()->flash('success', $inviato
|
||||
? "Territorio {$numero} assegnato a {$nome}: link inviato."
|
||||
: "Territorio {$numero} assegnato a {$nome}. Il link è ancora da inviare: lo trovi segnalato nell'elenco.");
|
||||
return $this->redirect($this->destinazione(route('assegnazioni.assegna')), navigate: true);
|
||||
}
|
||||
|
||||
private function assegnazioneInInvio(): ?Assegnazione
|
||||
{
|
||||
if (! $this->invioId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->authorize('territori.assign');
|
||||
|
||||
return Assegnazione::with(['territorio', 'proclamatore'])->find($this->invioId);
|
||||
return $this->destinazione(route('assegnazioni.assegna'));
|
||||
}
|
||||
|
||||
public function render()
|
||||
@@ -233,30 +145,27 @@ class Assegna extends Component
|
||||
// i territori già assegnati, che non si possono scegliere: cercandone
|
||||
// uno si legge a chi è, invece di un "nessun risultato". La miniatura
|
||||
// serve all'anteprima, che si aggiorna nel browser.
|
||||
// Quelli nel pacchetto di un gruppo di servizio li assegna il
|
||||
// sorvegliante: qui si trovano ma non si scelgono.
|
||||
$territori = Territorio::attivi()
|
||||
->with(['zona', 'tipologia', 'assegnazioneCorrente.proclamatore'])
|
||||
->with(['zona', 'tipologia', 'assegnazioneCorrente.proclamatore', 'gruppo'])
|
||||
->get()
|
||||
->sortBy('numero', SORT_NATURAL);
|
||||
|
||||
$opzioniTerritori = $territori
|
||||
->map(fn (Territorio $t) => $t->opzioneScelta(
|
||||
$t->assegnazioneCorrente ? 'già assegnato a ' . ($t->assegnazioneCorrente->proclamatore?->nome_completo ?? '—') : null
|
||||
) + array_filter(['miniatura' => $t->miniaturaUrl()]))
|
||||
->map(fn (Territorio $t) => $t->opzioneScelta(match (true) {
|
||||
(bool) $t->assegnazioneCorrente => 'già assegnato a ' . ($t->assegnazioneCorrente->proclamatore?->nome_completo ?? '—'),
|
||||
(bool) $t->gruppo_servizio_id => 'nel pacchetto del gruppo ' . ($t->gruppo?->nome ?? ''),
|
||||
default => null,
|
||||
}) + array_filter(['miniatura' => $t->miniaturaUrl()]))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$opzioniProclamatori = Proclamatore::opzioniScelta(Proclamatore::attivi()->get());
|
||||
|
||||
// L'elenco dei territori assegnati sta in ElencoAssegnati.
|
||||
// L'invio in corso; se nel frattempo l'assegnazione è rientrata o
|
||||
// sparita non c'è più niente da mandare.
|
||||
$invio = $this->invioId ? Assegnazione::aperte()->with(['territorio', 'proclamatore'])->find($this->invioId) : null;
|
||||
if ($this->invioId && ! $invio) {
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
}
|
||||
|
||||
return view('livewire.assegnazioni.assegna', [
|
||||
'invio' => $invio,
|
||||
'invio' => $this->invioCorrente(),
|
||||
'opzioniTerritori' => $opzioniTerritori,
|
||||
'opzioniProclamatori' => $opzioniProclamatori,
|
||||
'anteprimaIniziale' => $this->territorio_id ? $territori->firstWhere('id', $this->territorio_id)?->miniaturaUrl() : null,
|
||||
|
||||
@@ -27,6 +27,7 @@ class Rientra extends Component
|
||||
|
||||
public function mount(Assegnazione $assegnazione)
|
||||
{
|
||||
$this->autorizza($assegnazione);
|
||||
$this->assegnazione = $assegnazione->load(['territorio', 'proclamatore']);
|
||||
|
||||
if ($assegnazione->returned_at) {
|
||||
@@ -69,8 +70,28 @@ class Rientra extends Component
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Chi ha territori.return registra qualsiasi rientro. Il sorvegliante di
|
||||
* gruppo (gruppo.gestisci) solo quelli del suo gruppo: assegnazioni nate
|
||||
* dal gruppo o territori del suo pacchetto.
|
||||
*/
|
||||
private function autorizza(Assegnazione $assegnazione): void
|
||||
{
|
||||
$utente = auth()->user();
|
||||
if ($utente->can('territori.return')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gruppo = $utente->can('gruppo.gestisci') ? $utente->gruppoSorvegliato() : null;
|
||||
abort_unless(
|
||||
$gruppo && ($assegnazione->gruppo_servizio_id === $gruppo->id || $assegnazione->territorio?->gruppo_servizio_id === $gruppo->id),
|
||||
403
|
||||
);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->autorizza($this->assegnazione);
|
||||
$this->validate();
|
||||
|
||||
// mount() rejects an already-returned assignment, but the component
|
||||
@@ -112,7 +133,18 @@ class Rientra extends Component
|
||||
}
|
||||
|
||||
session()->flash('success', "Territorio {$territorio->numero} rientrato da {$proclamatore->nome_completo} dopo {$this->assegnazione->giorni} giorni.");
|
||||
return $this->redirect($this->destinazione(route('territori.show', $territorio)), navigate: true);
|
||||
return $this->redirect($this->destinazione($this->paginaPredefinita()), navigate: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dove si torna senza una pagina di partenza: la scheda del territorio,
|
||||
* o "Il mio gruppo" per il sorvegliante, che la scheda non la vede.
|
||||
*/
|
||||
public function paginaPredefinita(): string
|
||||
{
|
||||
return auth()->user()->can('territori.assign')
|
||||
? route('territori.show', $this->assegnazione->territorio_id)
|
||||
: route('mio-gruppo');
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
132
app/Livewire/Concerns/InviaLink.php
Normal file
132
app/Livewire/Concerns/InviaLink.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Concerns;
|
||||
|
||||
use App\Models\Assegnazione;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\On;
|
||||
|
||||
/**
|
||||
* Invio del link della piantina dopo un'assegnazione, condiviso dalla pagina
|
||||
* Assegna e da "Il mio gruppo" del sorvegliante. La vista è il partial
|
||||
* livewire/assegnazioni/_invio-link.
|
||||
*
|
||||
* Dopo l'assegnazione non si va avanti: si sceglie il canale (copia link o
|
||||
* Condividi: il destinatario lo sceglie chi usa l'app dai suoi contatti, i
|
||||
* proclamatori non hanno numero né email), si dice se il link è partito, e
|
||||
* solo allora si va avanti. Se la procedura si interrompe, l'assegnazione
|
||||
* resta con link_da_inviare e lo si segnala dove compare.
|
||||
*
|
||||
* Chi usa il trait dice chi può mandare il link di un'assegnazione
|
||||
* (puoInviare) e dove si va dopo (paginaDopoInvio).
|
||||
*/
|
||||
trait InviaLink
|
||||
{
|
||||
/** L'assegnazione di cui si sta mandando il link. */
|
||||
#[Locked]
|
||||
public ?int $invioId = null;
|
||||
|
||||
/** Il canale usato in questo invio (Assegnazione::CANALI_LINK). */
|
||||
#[Locked]
|
||||
public ?string $canaleUsato = null;
|
||||
|
||||
public string $erroreInvio = '';
|
||||
|
||||
abstract protected function puoInviare(Assegnazione $assegnazione): bool;
|
||||
|
||||
abstract protected function paginaDopoInvio(): string;
|
||||
|
||||
/**
|
||||
* Apre l'invio: subito dopo l'assegnazione, o da un'assegnazione rimasta
|
||||
* in sospeso ("Invia link" nell'elenco, che lo chiede con apri-invio).
|
||||
*/
|
||||
#[On('apri-invio')]
|
||||
public function apriInvio(int $assegnazioneId): void
|
||||
{
|
||||
$assegnazione = Assegnazione::aperte()->find($assegnazioneId);
|
||||
if (! $assegnazione) {
|
||||
return;
|
||||
}
|
||||
abort_unless($this->puoInviare($assegnazione), 403);
|
||||
|
||||
$this->invioId = $assegnazione->id;
|
||||
$this->canaleUsato = null;
|
||||
$this->erroreInvio = '';
|
||||
}
|
||||
|
||||
/** Copia link o Condividi: l'invio vero lo fa chi usa l'app, qui si annota il canale. */
|
||||
public function canale(string $canale): void
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione || ! in_array($canale, Assegnazione::CANALI_ATTIVI, true) || ! $assegnazione->shortPdfUrl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->canaleUsato = $canale;
|
||||
$this->erroreInvio = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* La risposta a "Hai inviato il link?": la si registra e solo adesso si
|
||||
* va avanti. "Sì" vuole un canale usato; "No" lascia il link da inviare.
|
||||
*/
|
||||
public function concludiInvio(bool $inviato)
|
||||
{
|
||||
$assegnazione = $this->assegnazioneInInvio();
|
||||
if (! $assegnazione) {
|
||||
$this->invioId = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($inviato && $this->canaleUsato === null) {
|
||||
$this->erroreInvio = 'Scegli prima come mandare il link.';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$assegnazione->registraInvioLink($inviato, $this->canaleUsato);
|
||||
|
||||
// Nel log il canale e l'id del proclamatore, mai il nome.
|
||||
activity()->causedBy(auth()->user())
|
||||
->performedOn($assegnazione)
|
||||
->withProperties([
|
||||
'territorio' => $assegnazione->territorio?->numero,
|
||||
'proclamatore_id' => $assegnazione->proclamatore_id,
|
||||
'canale' => $inviato ? $this->canaleUsato : null,
|
||||
])
|
||||
->log($inviato ? 'link_inviato' : 'link_non_inviato');
|
||||
|
||||
$numero = $assegnazione->territorio?->numero;
|
||||
$nome = $assegnazione->proclamatore?->nome_completo;
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
|
||||
session()->flash('success', $inviato
|
||||
? "Territorio {$numero} assegnato a {$nome}: link inviato."
|
||||
: "Territorio {$numero} assegnato a {$nome}. Il link è ancora da inviare: lo trovi segnalato nell'elenco.");
|
||||
return $this->redirect($this->paginaDopoInvio(), navigate: true);
|
||||
}
|
||||
|
||||
/** L'invio in corso, per la vista; null se l'assegnazione nel frattempo è rientrata o sparita. */
|
||||
protected function invioCorrente(): ?Assegnazione
|
||||
{
|
||||
$invio = $this->invioId ? Assegnazione::aperte()->with(['territorio', 'proclamatore'])->find($this->invioId) : null;
|
||||
if ($this->invioId && ! $invio) {
|
||||
$this->reset(['invioId', 'canaleUsato', 'erroreInvio']);
|
||||
}
|
||||
|
||||
return $invio;
|
||||
}
|
||||
|
||||
private function assegnazioneInInvio(): ?Assegnazione
|
||||
{
|
||||
if (! $this->invioId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assegnazione = Assegnazione::with(['territorio', 'proclamatore'])->find($this->invioId);
|
||||
abort_unless($assegnazione && $this->puoInviare($assegnazione), 403);
|
||||
|
||||
return $assegnazione;
|
||||
}
|
||||
}
|
||||
92
app/Livewire/Gruppi/GruppiIndex.php
Normal file
92
app/Livewire/Gruppi/GruppiIndex.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Gruppi;
|
||||
|
||||
use App\Livewire\Concerns\Avvisa;
|
||||
use App\Models\GruppoServizio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Services\GruppiServizio;
|
||||
use InvalidArgumentException;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Gruppi di servizio (solo amministratore): elenco e creazione. Creando un
|
||||
* gruppo si sceglie il sorvegliante, che riceve un'utenza con l'email
|
||||
* indicata e una password temporanea, mostrata qui una volta sola.
|
||||
* Membri e pacchetto si gestiscono nella pagina del gruppo (GruppoShow).
|
||||
*/
|
||||
class GruppiIndex extends Component
|
||||
{
|
||||
use Avvisa;
|
||||
|
||||
public string $nome = '';
|
||||
|
||||
public ?int $sorveglianteId = null;
|
||||
|
||||
public string $email = '';
|
||||
|
||||
/** Gruppo appena creato e password temporanea del sorvegliante, da consegnare. */
|
||||
#[Locked]
|
||||
public ?int $creatoId = null;
|
||||
|
||||
#[Locked]
|
||||
public ?string $passwordTemporanea = null;
|
||||
|
||||
public function crea(): void
|
||||
{
|
||||
$this->authorize('settings.manage');
|
||||
|
||||
$this->validate([
|
||||
'nome' => 'required|string|max:100',
|
||||
'sorveglianteId' => 'required|integer|exists:proclamatori,id',
|
||||
'email' => 'required|email|max:255',
|
||||
], [
|
||||
'sorveglianteId.required' => 'Scegli il sorvegliante.',
|
||||
'email.required' => "Serve l'email con cui il sorvegliante entrerà.",
|
||||
]);
|
||||
|
||||
try {
|
||||
[$gruppo, $password] = app(GruppiServizio::class)->crea(
|
||||
$this->nome,
|
||||
Proclamatore::findOrFail($this->sorveglianteId),
|
||||
trim($this->email),
|
||||
);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->addError('sorveglianteId', $e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reset(['nome', 'sorveglianteId', 'email']);
|
||||
$this->creatoId = $gruppo->id;
|
||||
$this->passwordTemporanea = $password;
|
||||
}
|
||||
|
||||
public function chiudiPassword(): void
|
||||
{
|
||||
$this->reset(['creatoId', 'passwordTemporanea']);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$gruppi = GruppoServizio::with('sorvegliante')
|
||||
->withCount(['membri', 'territori'])
|
||||
->orderByRaw('sciolto_at IS NOT NULL')
|
||||
->orderBy('nome')
|
||||
->get();
|
||||
|
||||
// Possono diventare sorveglianti i proclamatori attivi senza gruppo e
|
||||
// senza già un'utenza.
|
||||
$candidati = Proclamatore::attivi()
|
||||
->whereNull('gruppo_servizio_id')
|
||||
->whereDoesntHave('utente')
|
||||
->get();
|
||||
|
||||
return view('livewire.gruppi.gruppi-index', [
|
||||
'gruppi' => $gruppi,
|
||||
'opzioniSorveglianti' => Proclamatore::opzioniScelta($candidati),
|
||||
'creato' => $this->creatoId ? GruppoServizio::with('sorvegliante.utente')->find($this->creatoId) : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
139
app/Livewire/Gruppi/GruppoShow.php
Normal file
139
app/Livewire/Gruppi/GruppoShow.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Gruppi;
|
||||
|
||||
use App\Livewire\Concerns\Avvisa;
|
||||
use App\Livewire\Concerns\RicordaRitorno;
|
||||
use App\Models\GruppoServizio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\User;
|
||||
use App\Services\GruppiServizio;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Un gruppo di servizio, lato amministratore: membri (aggiungere/togliere),
|
||||
* pacchetto (aggiungere in blocco, rimandare in reparto quelli non dati),
|
||||
* nuova password temporanea per il sorvegliante, scioglimento.
|
||||
*/
|
||||
class GruppoShow extends Component
|
||||
{
|
||||
use Avvisa;
|
||||
use RicordaRitorno;
|
||||
|
||||
public GruppoServizio $gruppo;
|
||||
|
||||
/** @var int[] proclamatori da aggiungere */
|
||||
public array $nuoviMembri = [];
|
||||
|
||||
/** @var int[] territori da aggiungere al pacchetto */
|
||||
public array $nuoviTerritori = [];
|
||||
|
||||
#[Locked]
|
||||
public ?string $passwordTemporanea = null;
|
||||
|
||||
public function mount(GruppoServizio $gruppo): void
|
||||
{
|
||||
$this->gruppo = $gruppo;
|
||||
}
|
||||
|
||||
private function servizio(): GruppiServizio
|
||||
{
|
||||
$this->authorize('settings.manage');
|
||||
|
||||
return app(GruppiServizio::class);
|
||||
}
|
||||
|
||||
private function esegui(callable $azione, string $ok): void
|
||||
{
|
||||
try {
|
||||
$azione();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->avvisaErrore($e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->gruppo->refresh();
|
||||
$this->avvisa($ok);
|
||||
}
|
||||
|
||||
public function aggiungiMembri(): void
|
||||
{
|
||||
$ids = array_map('intval', $this->nuoviMembri);
|
||||
$this->esegui(fn () => $this->servizio()->aggiungiMembri($this->gruppo, $ids), count($ids) . ' membri aggiunti.');
|
||||
$this->nuoviMembri = [];
|
||||
}
|
||||
|
||||
public function togliMembro(int $proclamatoreId): void
|
||||
{
|
||||
$membro = Proclamatore::findOrFail($proclamatoreId);
|
||||
$this->esegui(fn () => $this->servizio()->togliMembro($this->gruppo, $membro), "{$membro->nome_completo} tolto dal gruppo.");
|
||||
}
|
||||
|
||||
public function aggiungiTerritori(): void
|
||||
{
|
||||
$ids = array_map('intval', $this->nuoviTerritori);
|
||||
$this->esegui(fn () => $this->servizio()->aggiungiAlPacchetto($this->gruppo, $ids), count($ids) . ' territori aggiunti al pacchetto.');
|
||||
$this->nuoviTerritori = [];
|
||||
}
|
||||
|
||||
public function rimanda(int $territorioId): void
|
||||
{
|
||||
$territorio = Territorio::findOrFail($territorioId);
|
||||
$this->esegui(fn () => $this->servizio()->rimandaInReparto($this->gruppo, $territorio), "Territorio {$territorio->numero} rimandato in reparto.");
|
||||
}
|
||||
|
||||
/** Il sorvegliante ha perso la password: una nuova temporanea, da cambiare al primo accesso. */
|
||||
public function nuovaPassword(): void
|
||||
{
|
||||
$this->servizio();
|
||||
$utente = User::where('proclamatore_id', $this->gruppo->sorvegliante_id)->first();
|
||||
if (! $utente || $this->gruppo->sciolto_at) {
|
||||
$this->avvisaErrore('Il gruppo non ha un sorvegliante con utenza attiva.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$password = Str::password(12, symbols: false);
|
||||
$utente->forceFill(['password' => $password, 'password_change_required' => true])->save();
|
||||
activity()->causedBy(auth()->user())->performedOn($utente)->log('password_reimpostata');
|
||||
|
||||
$this->passwordTemporanea = $password;
|
||||
}
|
||||
|
||||
/** Toglie il ruolo di sorvegliante: il gruppo si scioglie. */
|
||||
public function sciogli(): void
|
||||
{
|
||||
$this->esegui(fn () => $this->servizio()->sciogli($this->gruppo), "Gruppo {$this->gruppo->nome} sciolto.");
|
||||
$this->passwordTemporanea = null;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->gruppo->load(['sorvegliante.utente']);
|
||||
|
||||
$membri = $this->gruppo->membri()->get()->sortBy(fn ($p) => mb_strtolower($p->cognome . ' ' . $p->nome))->values();
|
||||
$territori = $this->gruppo->territori()->with(['zona', 'assegnazioneCorrente.proclamatore'])->get()->sortBy('numero', SORT_NATURAL)->values();
|
||||
|
||||
$attivo = ! $this->gruppo->sciolto_at;
|
||||
|
||||
return view('livewire.gruppi.gruppo-show', [
|
||||
'attivo' => $attivo,
|
||||
'membri' => $membri,
|
||||
'territori' => $territori,
|
||||
// Si possono aggiungere i proclamatori attivi senza gruppo...
|
||||
'candidatiMembri' => $attivo
|
||||
? Proclamatore::attivi()->whereNull('gruppo_servizio_id')->get()->sortBy(fn ($p) => mb_strtolower($p->cognome . ' ' . $p->nome))->values()
|
||||
: collect(),
|
||||
// ...e i territori in reparto (attivi, non assegnati, fuori da ogni pacchetto).
|
||||
'candidatiTerritori' => $attivo
|
||||
? Territorio::inReparto()->with('zona')->get()->sortBy('numero', SORT_NATURAL)->values()
|
||||
: collect(),
|
||||
'assegnazioniDelGruppo' => $this->gruppo->assegnazioni()->count(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
191
app/Livewire/Gruppi/MioGruppo.php
Normal file
191
app/Livewire/Gruppi/MioGruppo.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Gruppi;
|
||||
|
||||
use App\Livewire\Concerns\Avvisa;
|
||||
use App\Livewire\Concerns\InviaLink;
|
||||
use App\Models\Assegnazione;
|
||||
use App\Models\GruppoServizio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Territorio;
|
||||
use App\Services\GruppiServizio;
|
||||
use Carbon\Carbon;
|
||||
use InvalidArgumentException;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* "Il mio gruppo": la sola pagina del sorvegliante di gruppo. Vede il
|
||||
* pacchetto del suo gruppo e i membri; dà i territori ai membri (o a sé),
|
||||
* manda il link, registra i rientri (pagina Rientra), apre la piantina e
|
||||
* rimanda in reparto quelli non ancora dati. Le regole stanno in
|
||||
* App\Services\GruppiServizio; qui ogni azione ricontrolla che il territorio
|
||||
* o l'assegnazione siano del suo gruppo.
|
||||
*/
|
||||
class MioGruppo extends Component
|
||||
{
|
||||
use Avvisa;
|
||||
use InviaLink;
|
||||
|
||||
/** Il territorio del pacchetto che si sta assegnando. */
|
||||
#[Locked]
|
||||
public ?int $territorioDaAssegnare = null;
|
||||
|
||||
public ?int $membroId = null;
|
||||
|
||||
public string $dataAssegnazione = '';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->dataAssegnazione = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
private function gruppo(): ?GruppoServizio
|
||||
{
|
||||
return auth()->user()->gruppoSorvegliato();
|
||||
}
|
||||
|
||||
private function gruppoOFallisci(): GruppoServizio
|
||||
{
|
||||
$gruppo = $this->gruppo();
|
||||
abort_unless($gruppo, 403, 'Non sei il sorvegliante di un gruppo di servizio attivo.');
|
||||
|
||||
return $gruppo;
|
||||
}
|
||||
|
||||
private function territorioDelPacchetto(int $id): Territorio
|
||||
{
|
||||
$gruppo = $this->gruppoOFallisci();
|
||||
$territorio = Territorio::find($id);
|
||||
abort_unless($territorio && $territorio->gruppo_servizio_id === $gruppo->id, 403);
|
||||
|
||||
return $territorio;
|
||||
}
|
||||
|
||||
// ─── Assegnare un territorio del pacchetto ──────────────────
|
||||
|
||||
public function apriAssegnazione(int $territorioId): void
|
||||
{
|
||||
$this->territorioDelPacchetto($territorioId);
|
||||
$this->territorioDaAssegnare = $territorioId;
|
||||
$this->membroId = null;
|
||||
$this->dataAssegnazione = now()->format('Y-m-d');
|
||||
$this->resetValidation();
|
||||
}
|
||||
|
||||
public function annullaAssegnazione(): void
|
||||
{
|
||||
$this->reset(['territorioDaAssegnare', 'membroId']);
|
||||
$this->resetValidation();
|
||||
}
|
||||
|
||||
public function assegna(): void
|
||||
{
|
||||
$this->validate([
|
||||
'membroId' => 'required|integer',
|
||||
'dataAssegnazione' => 'required|date|before_or_equal:today',
|
||||
], [
|
||||
'membroId.required' => 'Scegli a chi dare il territorio.',
|
||||
'dataAssegnazione.before_or_equal' => 'La data non può essere nel futuro.',
|
||||
]);
|
||||
|
||||
$gruppo = $this->gruppoOFallisci();
|
||||
$territorio = $this->territorioDelPacchetto((int) $this->territorioDaAssegnare);
|
||||
$membro = Proclamatore::find($this->membroId);
|
||||
|
||||
if (! $membro) {
|
||||
$this->addError('membroId', 'Scegli un membro del gruppo.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$assegnazione = app(GruppiServizio::class)->assegna($gruppo, $territorio, $membro, Carbon::parse($this->dataAssegnazione));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->addError('membroId', $e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reset(['territorioDaAssegnare', 'membroId']);
|
||||
|
||||
// Come dalla pagina Assegna: si apre l'invio del link.
|
||||
$this->apriInvio($assegnazione->id);
|
||||
}
|
||||
|
||||
// ─── Rimandare in reparto ───────────────────────────────────
|
||||
|
||||
public function rimanda(int $territorioId): void
|
||||
{
|
||||
$territorio = $this->territorioDelPacchetto($territorioId);
|
||||
|
||||
try {
|
||||
app(GruppiServizio::class)->rimandaInReparto($this->gruppoOFallisci(), $territorio);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->avvisaErrore($e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->avvisa("Territorio {$territorio->numero} rimandato in reparto.");
|
||||
}
|
||||
|
||||
// ─── Link già inviato / condiviso dall'elenco ───────────────
|
||||
|
||||
private function assegnazioneDelGruppo(int $id): Assegnazione
|
||||
{
|
||||
$assegnazione = Assegnazione::aperte()->with('territorio')->find($id);
|
||||
abort_unless($assegnazione && $this->puoInviare($assegnazione), 403);
|
||||
|
||||
return $assegnazione;
|
||||
}
|
||||
|
||||
public function toggleLinkSent(int $assegnazioneId): void
|
||||
{
|
||||
$assegnazione = $this->assegnazioneDelGruppo($assegnazioneId);
|
||||
$assegnazione->registraInvioLink(! $assegnazione->link_sent, $assegnazione->link_canale);
|
||||
}
|
||||
|
||||
/** "Condividi" dall'elenco andato a buon fine (elencoCercabile.condividi). */
|
||||
public function segnaCondiviso(int $assegnazioneId): void
|
||||
{
|
||||
$this->assegnazioneDelGruppo($assegnazioneId)->registraInvioLink(true, 'condividi');
|
||||
}
|
||||
|
||||
// ─── Invio del link (trait InviaLink) ───────────────────────
|
||||
|
||||
protected function puoInviare(Assegnazione $assegnazione): bool
|
||||
{
|
||||
$gruppo = $this->gruppo();
|
||||
|
||||
return $gruppo !== null
|
||||
&& ($assegnazione->gruppo_servizio_id === $gruppo->id
|
||||
|| $assegnazione->territorio?->gruppo_servizio_id === $gruppo->id);
|
||||
}
|
||||
|
||||
protected function paginaDopoInvio(): string
|
||||
{
|
||||
return route('mio-gruppo');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$gruppo = $this->gruppo();
|
||||
|
||||
$territori = $gruppo
|
||||
? $gruppo->territori()->with(['zona', 'assegnazioneCorrente.proclamatore'])->get()->sortBy('numero', SORT_NATURAL)->values()
|
||||
: collect();
|
||||
|
||||
$membri = $gruppo
|
||||
? $gruppo->membri()->where('attivo', true)->get()->sortBy(fn ($p) => mb_strtolower($p->cognome . ' ' . $p->nome))->values()
|
||||
: collect();
|
||||
|
||||
return view('livewire.gruppi.mio-gruppo', [
|
||||
'gruppo' => $gruppo,
|
||||
'territori' => $territori,
|
||||
'membri' => $membri,
|
||||
'opzioniMembri' => Proclamatore::opzioniScelta($membri),
|
||||
'invio' => $this->invioCorrente(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class Privacy extends Component
|
||||
public const PERMESSI = [
|
||||
'settings.manage' => 'impostazioni, utenti, backup e import',
|
||||
'proclamatori.manage' => 'anagrafica dei proclamatori',
|
||||
'gruppo.gestisci' => 'solo il proprio gruppo di servizio: i territori del pacchetto e i membri del gruppo',
|
||||
'territori.manage' => 'gestione dei territori e delle piantine',
|
||||
'campagne.manage' => 'campagne',
|
||||
'registro.view' => 'consultazione del registro delle assegnazioni',
|
||||
|
||||
@@ -41,7 +41,8 @@ class Profile extends Component
|
||||
|
||||
// L'indirizzo nuovo va confermato (User::booted lo segna da
|
||||
// confermare): finché non apre il link, l'account resta bloccato.
|
||||
if ($emailCambiata) {
|
||||
// Il sorvegliante di gruppo non conferma l'email (User::hasVerifiedEmail).
|
||||
if ($emailCambiata && ! $user->isSorvegliante()) {
|
||||
$user->inviaConfermaEmail()
|
||||
? session()->flash('success', "Profilo aggiornato. Conferma il nuovo indirizzo: ti abbiamo mandato un'email a {$user->email}.")
|
||||
: session()->flash('error', 'Profilo aggiornato, ma l\'email di conferma non è partita: richiedila qui sotto.');
|
||||
|
||||
@@ -285,7 +285,7 @@ class Registro extends Component
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Assegnazione::with(['territorio.zona', 'territorio.assegnazioneCorrente', 'proclamatore', 'annoTeocratico', 'campagna']);
|
||||
$query = Assegnazione::with(['territorio.zona', 'territorio.assegnazioneCorrente', 'proclamatore', 'annoTeocratico', 'campagna', 'gruppo']);
|
||||
|
||||
if ($this->filtroAnno) {
|
||||
$query->where('anno_teocratico_id', $this->filtroAnno);
|
||||
|
||||
@@ -34,7 +34,10 @@ class UsersIndex extends Component
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
// "sorvegliante" si dà solo creando un gruppo di servizio, con
|
||||
// l'utenza collegata al proclamatore: da qui non si assegna.
|
||||
$this->availableRoles = Role::query()
|
||||
->where('name', '!=', 'sorvegliante')
|
||||
->orderBy('name')
|
||||
->pluck('name')
|
||||
->all();
|
||||
@@ -111,9 +114,28 @@ class UsersIndex extends Component
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Le utenze dei sorveglianti di gruppo (attive o disattivate) si gestiscono
|
||||
* da Gruppi di servizio: cambiarle o cancellarle da qui lascerebbe un
|
||||
* gruppo senza sorvegliante, o un sorvegliante con un ruolo diverso.
|
||||
*/
|
||||
private function gestitaDaiGruppi(User $user): bool
|
||||
{
|
||||
if ($user->proclamatore_id === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->avvisaErrore("L'utenza di {$user->name} è di un sorvegliante di gruppo: si gestisce da Gruppi di servizio.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function startEdit(int $userId): void
|
||||
{
|
||||
$user = User::query()->with('roles')->findOrFail($userId);
|
||||
if ($this->gestitaDaiGruppi($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->editingUserId = $user->id;
|
||||
$this->editName = $user->name;
|
||||
@@ -143,6 +165,9 @@ class UsersIndex extends Component
|
||||
|
||||
$validated = $this->validate($this->editRules());
|
||||
$user = User::query()->findOrFail($this->editingUserId);
|
||||
if ($this->gestitaDaiGruppi($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Togliere il ruolo di amministratore a sé stessi, o all'ultimo
|
||||
// rimasto, lascia l'installazione senza nessuno che possa gestire
|
||||
@@ -193,6 +218,9 @@ class UsersIndex extends Component
|
||||
{
|
||||
$currentUser = auth()->user();
|
||||
$user = User::query()->with('roles')->findOrFail($userId);
|
||||
if ($this->gestitaDaiGruppi($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $currentUser || $currentUser->id === $user->id) {
|
||||
$this->avvisaErrore('Non puoi cancellare il tuo utente.');
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Jobs\ImportTerritoryPdfFolder;
|
||||
use App\Models\AnnoTeocratico;
|
||||
use App\Models\Assegnazione;
|
||||
use App\Models\Campagna;
|
||||
use App\Models\GruppoServizio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Territorio;
|
||||
@@ -469,6 +470,7 @@ class XmlExchange extends Component
|
||||
try {
|
||||
DB::transaction(function () use ($xml, $actorId, $attore, $attoreEraAmministratore, &$stats) {
|
||||
Assegnazione::query()->delete();
|
||||
GruppoServizio::query()->delete();
|
||||
Campagna::query()->delete();
|
||||
Territorio::query()->withTrashed()->forceDelete();
|
||||
Proclamatore::query()->withTrashed()->forceDelete();
|
||||
@@ -520,6 +522,7 @@ class XmlExchange extends Component
|
||||
// accounts in the file are (re)created. $userMap remaps the
|
||||
// created_by/returned_by FKs on the assignments below.
|
||||
$userMap = [];
|
||||
$utentiDaCollegare = [];
|
||||
foreach (($xml->users->user ?? []) as $userNode) {
|
||||
$email = trim((string) ($userNode->email ?? ''));
|
||||
$legacyId = (string) ($userNode['legacy_id'] ?? '');
|
||||
@@ -589,6 +592,13 @@ class XmlExchange extends Component
|
||||
|
||||
DB::table('users')->where('id', $user->id)->update($meta);
|
||||
|
||||
// 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 ?? '')),
|
||||
];
|
||||
|
||||
$stats['utenti_importati']++;
|
||||
if ($legacyId !== '') {
|
||||
$userMap[$legacyId] = $user->id;
|
||||
@@ -624,6 +634,7 @@ class XmlExchange extends Component
|
||||
}
|
||||
|
||||
$proclamatoreMap = [];
|
||||
$gruppiDeiProclamatori = [];
|
||||
foreach (($xml->proclamatori->proclamatore ?? []) as $proclamatoreNode) {
|
||||
$p = Proclamatore::create([
|
||||
'nome' => (string) $proclamatoreNode->nome,
|
||||
@@ -634,14 +645,42 @@ class XmlExchange extends Component
|
||||
]);
|
||||
$this->applyRowMeta('proclamatori', $p->id, $proclamatoreNode, true);
|
||||
$stats['proclamatori_importati']++;
|
||||
$gruppiDeiProclamatori[$p->id] = (string) ($proclamatoreNode->legacy_gruppo_id ?? '');
|
||||
$legacyId = (string) ($proclamatoreNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$proclamatoreMap[$legacyId] = $p->id;
|
||||
}
|
||||
}
|
||||
|
||||
$annoMap = [];
|
||||
foreach (($xml->anni_teocratici->anno ?? []) as $annoNode) {
|
||||
// Gruppi di servizio, dopo i proclamatori: il sorvegliante è uno di
|
||||
// loro. Poi i collegamenti che li aspettavano (membri, utenze).
|
||||
$gruppoMap = [];
|
||||
foreach (($xml->gruppi_servizio->gruppo ?? []) as $gruppoNode) {
|
||||
$legacySorvegliante = (string) ($gruppoNode->legacy_sorvegliante_id ?? '');
|
||||
$g = GruppoServizio::create([
|
||||
'nome' => (string) $gruppoNode->nome,
|
||||
'sorvegliante_id' => $proclamatoreMap[$legacySorvegliante] ?? null,
|
||||
'sciolto_at' => $this->normalizeDateTimeForDb((string) ($gruppoNode->sciolto_at ?? '')),
|
||||
]);
|
||||
$this->applyRowMeta('gruppi_servizio', $g->id, $gruppoNode);
|
||||
$legacyId = (string) ($gruppoNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$gruppoMap[$legacyId] = $g->id;
|
||||
}
|
||||
}
|
||||
foreach ($gruppiDeiProclamatori as $idProclamatore => $legacyGruppo) {
|
||||
if (isset($gruppoMap[$legacyGruppo])) {
|
||||
DB::table('proclamatori')->where('id', $idProclamatore)->update(['gruppo_servizio_id' => $gruppoMap[$legacyGruppo]]);
|
||||
}
|
||||
}
|
||||
foreach ($utentiDaCollegare as $idUtente => $collegamento) {
|
||||
DB::table('users')->where('id', $idUtente)->update([
|
||||
'proclamatore_id' => $proclamatoreMap[$collegamento['proclamatore']] ?? null,
|
||||
'disattivato_at' => $collegamento['disattivato_at'],
|
||||
]);
|
||||
}
|
||||
|
||||
$annoMap = []; foreach (($xml->anni_teocratici->anno ?? []) as $annoNode) {
|
||||
$label = (string) $annoNode->label;
|
||||
$anno = AnnoTeocratico::create([
|
||||
'label' => $label,
|
||||
@@ -706,6 +745,7 @@ class XmlExchange extends Component
|
||||
'note' => (string) ($territorioNode->note ?? ''),
|
||||
'attivo' => ((int) ($territorioNode->attivo ?? 1)) === 1,
|
||||
'prioritario' => ((int) ($territorioNode->prioritario ?? 0)) === 1,
|
||||
'gruppo_servizio_id' => $gruppoMap[(string) ($territorioNode->legacy_gruppo_id ?? '')] ?? null,
|
||||
'pdf_path' => $pdfPath !== '' ? $pdfPath : null,
|
||||
'thumbnail_path' => $thumbnailPath !== '' ? $thumbnailPath : null,
|
||||
]);
|
||||
@@ -768,6 +808,7 @@ class XmlExchange extends Component
|
||||
'campaign_id' => $legacyCampagna !== '' && isset($campagnaMap[$legacyCampagna]) ? $campagnaMap[$legacyCampagna] : null,
|
||||
'link_sent' => ((int) ($assegnazioneNode->link_sent ?? 0)) === 1,
|
||||
'link_da_inviare' => ((int) ($assegnazioneNode->link_da_inviare ?? 0)) === 1,
|
||||
'gruppo_servizio_id' => $gruppoMap[(string) ($assegnazioneNode->legacy_gruppo_id ?? '')] ?? null,
|
||||
'link_canale' => in_array((string) ($assegnazioneNode->link_canale ?? ''), array_keys(Assegnazione::CANALI_LINK), true) ? (string) $assegnazioneNode->link_canale : null,
|
||||
'pdf_access_code' => $pdfAccessCode !== '' ? $pdfAccessCode : null,
|
||||
'note' => (string) ($assegnazioneNode->note ?? ''),
|
||||
@@ -824,16 +865,20 @@ class XmlExchange extends Component
|
||||
// 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.
|
||||
'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(),
|
||||
'zones' => $this->righeEsportate(Zona::query()->orderBy('id'), ['id', 'nome', 'attivo', 'created_at', 'updated_at']),
|
||||
'tipologie' => $this->righeEsportate(Tipologia::query()->orderBy('id'), ['id', 'nome', 'attivo', 'created_at', 'updated_at']),
|
||||
'proclamatori' => $this->righeEsportate(Proclamatore::query()->withTrashed()->orderBy('id'), ['id', 'nome', 'cognome', 'attivo', 'deleted_at', 'created_at', 'updated_at']),
|
||||
'territori' => $this->righeEsportate(Territorio::query()->withTrashed()->orderBy('id'), ['id', 'numero', 'zona_id', 'tipologia_id', 'confini', 'note', 'attivo', 'prioritario', 'pdf_path', 'thumbnail_path', 'deleted_at', 'created_at', 'updated_at']),
|
||||
'proclamatori' => $this->righeEsportate(Proclamatore::query()->withTrashed()->orderBy('id'), ['id', 'nome', 'cognome', 'attivo', 'gruppo_servizio_id', 'deleted_at', 'created_at', 'updated_at']),
|
||||
'gruppi_servizio' => $this->righeEsportate(GruppoServizio::query()->orderBy('id'), ['id', 'nome', 'sorvegliante_id', 'sciolto_at', 'created_at', 'updated_at']),
|
||||
'territori' => $this->righeEsportate(Territorio::query()->withTrashed()->orderBy('id'), ['id', 'numero', 'zona_id', 'tipologia_id', 'confini', 'note', 'attivo', 'prioritario', 'gruppo_servizio_id', 'pdf_path', 'thumbnail_path', 'deleted_at', 'created_at', 'updated_at']),
|
||||
'anni_teocratici' => $this->righeEsportate(AnnoTeocratico::query()->orderBy('id'), ['id', 'label', 'start_date', 'end_date', 'created_at', 'updated_at']),
|
||||
'campagne' => $this->righeEsportate(Campagna::query()->orderBy('id'), ['id', 'descrizione', 'start_date', 'end_date', 'created_at', 'updated_at']),
|
||||
'assegnazioni' => $this->righeEsportate(Assegnazione::query()->orderBy('id'), ['id', 'territorio_id', 'proclamatore_id', 'anno_teocratico_id', 'campaign_id', 'assigned_at', 'returned_at', 'counted_in_campaign', 'note', 'created_by', 'returned_by', 'link_sent', 'link_da_inviare', 'link_canale', 'pdf_access_code', 'created_at', 'updated_at']),
|
||||
'assegnazioni' => $this->righeEsportate(Assegnazione::query()->orderBy('id'), ['id', 'territorio_id', 'proclamatore_id', 'anno_teocratico_id', 'campaign_id', 'assigned_at', 'returned_at', 'counted_in_campaign', 'note', 'created_by', 'returned_by', 'link_sent', 'link_da_inviare', 'link_canale', 'gruppo_servizio_id', 'pdf_access_code', 'created_at', 'updated_at']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1232,6 +1277,8 @@ class XmlExchange extends Component
|
||||
$this->addXmlText($node, 'email', (string) ($user['email'] ?? ''));
|
||||
$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');
|
||||
@@ -1265,6 +1312,7 @@ class XmlExchange extends Component
|
||||
$this->addXmlText($node, 'nome', (string) ($proclamatore['nome'] ?? ''));
|
||||
$this->addXmlText($node, 'cognome', (string) ($proclamatore['cognome'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($proclamatore['attivo'] ?? 1)));
|
||||
$node->addChild('legacy_gruppo_id', (string) ($proclamatore['gruppo_servizio_id'] ?? ''));
|
||||
$node->addChild('deleted_at', (string) ($proclamatore['deleted_at'] ?? ''));
|
||||
$this->addTimestampNodes($node, $proclamatore);
|
||||
}
|
||||
@@ -1280,6 +1328,7 @@ class XmlExchange extends Component
|
||||
$this->addXmlText($node, 'note', (string) ($territorio['note'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($territorio['attivo'] ?? 1)));
|
||||
$node->addChild('prioritario', (string) ((int) ($territorio['prioritario'] ?? 0)));
|
||||
$node->addChild('legacy_gruppo_id', (string) ($territorio['gruppo_servizio_id'] ?? ''));
|
||||
$this->addXmlText($node, 'pdf_path', (string) ($territorio['pdf_path'] ?? ''));
|
||||
$this->addXmlText($node, 'thumbnail_path', (string) ($territorio['thumbnail_path'] ?? ''));
|
||||
$node->addChild('deleted_at', (string) ($territorio['deleted_at'] ?? ''));
|
||||
@@ -1296,6 +1345,17 @@ class XmlExchange extends Component
|
||||
$this->addTimestampNodes($node, $anno);
|
||||
}
|
||||
|
||||
// Gruppi di servizio: il sorvegliante è un proclamatore (legacy id).
|
||||
$gruppiNode = $xml->addChild('gruppi_servizio');
|
||||
foreach (($dataset['gruppi_servizio'] ?? []) as $gruppo) {
|
||||
$node = $gruppiNode->addChild('gruppo');
|
||||
$this->addLegacyIdAttribute($node, $gruppo);
|
||||
$this->addXmlText($node, 'nome', (string) ($gruppo['nome'] ?? ''));
|
||||
$node->addChild('legacy_sorvegliante_id', (string) ($gruppo['sorvegliante_id'] ?? ''));
|
||||
$node->addChild('sciolto_at', (string) ($gruppo['sciolto_at'] ?? ''));
|
||||
$this->addTimestampNodes($node, $gruppo);
|
||||
}
|
||||
|
||||
$campagneNode = $xml->addChild('campagne');
|
||||
foreach ($dataset['campagne'] as $campagna) {
|
||||
$node = $campagneNode->addChild('campagna');
|
||||
@@ -1321,6 +1381,7 @@ class XmlExchange extends Component
|
||||
$node->addChild('counted_in_campaign', (string) ((int) ($assegnazione['counted_in_campaign'] ?? 0)));
|
||||
$node->addChild('link_sent', (string) ((int) ($assegnazione['link_sent'] ?? 0)));
|
||||
$node->addChild('link_da_inviare', (string) ((int) ($assegnazione['link_da_inviare'] ?? 0)));
|
||||
$node->addChild('legacy_gruppo_id', (string) ($assegnazione['gruppo_servizio_id'] ?? ''));
|
||||
$this->addXmlText($node, 'link_canale', (string) ($assegnazione['link_canale'] ?? ''));
|
||||
$this->addXmlText($node, 'pdf_access_code', (string) ($assegnazione['pdf_access_code'] ?? ''));
|
||||
$this->addXmlText($node, 'note', (string) ($assegnazione['note'] ?? ''));
|
||||
|
||||
@@ -155,6 +155,7 @@ class TerritorioIndex extends Component
|
||||
'tipologia',
|
||||
'assegnazioneCorrente.proclamatore',
|
||||
'ultimaAssegnazione',
|
||||
'gruppo',
|
||||
]);
|
||||
|
||||
if ($this->search) {
|
||||
@@ -179,6 +180,7 @@ class TerritorioIndex extends Component
|
||||
if ($this->filterStato) {
|
||||
match ($this->filterStato) {
|
||||
'in_reparto' => $query->inReparto(),
|
||||
'in_gruppo' => $query->inGruppo(),
|
||||
'assegnato' => $query->assegnato(),
|
||||
'da_rientrare' => $query->daRientrare(),
|
||||
'inattivo' => $query->where('attivo', false),
|
||||
|
||||
@@ -21,7 +21,7 @@ class TerritorioShow extends Component
|
||||
public function render()
|
||||
{
|
||||
$assegnazioni = Assegnazione::where('territorio_id', $this->territorio->id)
|
||||
->with(['proclamatore', 'annoTeocratico', 'campagna', 'creatoDa', 'rientratoDa'])
|
||||
->with(['proclamatore', 'annoTeocratico', 'campagna', 'creatoDa', 'rientratoDa', 'gruppo'])
|
||||
->orderByDesc('assigned_at')
|
||||
->get()
|
||||
->groupBy(fn($a) => $a->annoTeocratico->label);
|
||||
|
||||
Reference in New Issue
Block a user