authorize('richieste.gestisci'); Richiesta::findOrFail($id)->scarta(auth()->user()); $this->avvisa('Richiesta scartata.'); } /** * Le richieste aperte, con quello che serve a confermarle: per un * territorio nuovo i proclamatori dal nome simile a quello scritto, per * una riconsegna l'assegnazione aperta di quel territorio. */ private function richiesteAperte(): \Illuminate\Support\Collection { if (! auth()->user()->can('richieste.gestisci')) { return collect(); } $aperte = Richiesta::aperte()->orderBy('created_at')->get(); if ($aperte->isEmpty()) { return collect(); } $servizio = app(RichiesteServizio::class); $proclamatori = Proclamatore::attivi()->get(); return $aperte->map(fn (Richiesta $r) => [ 'richiesta' => $r, 'simili' => $r->tipo === Richiesta::NUOVO ? $servizio->nomiSimili($r->nome, $proclamatori) : collect(), 'assegnazione' => $r->tipo === Richiesta::RICONSEGNA ? $servizio->assegnazioneAperta($r->numero_territorio) : null, ]); } public function downloadPdfDaAssegnare() { $this->authorize('territori.assign'); $settings = Setting::instance(); $territori = Territorio::ordinaPerPriorita( Territorio::inReparto()->with('zona', 'tipologia', 'ultimaAssegnazione')->get() ); $pdf = Pdf::loadView('pdf.territori-lista', [ 'titolo' => 'Territori da Assegnare', 'congregazione' => $settings->congregazione_nome ?? 'TerManager2', 'data' => now()->format('d/m/Y'), 'territori' => $territori, 'tipo' => 'assegnare', ]); return response()->streamDownload( fn () => print($pdf->output()), 'territori-da-assegnare-' . now()->format('Y-m-d') . '.pdf' ); } public function downloadPdfDaRientrare() { $this->authorize('territori.return'); $settings = Setting::instance(); $territori = Territorio::daRientrare() ->with(['zona', 'assegnazioneCorrente.proclamatore']) ->get(); $pdf = Pdf::loadView('pdf.territori-lista', [ 'titolo' => 'Territori da Rientrare', 'congregazione' => $settings->congregazione_nome ?? 'TerManager2', 'data' => now()->format('d/m/Y'), 'territori' => $territori, 'tipo' => 'rientrare', ]); return response()->streamDownload( fn () => print($pdf->output()), 'territori-da-rientrare-' . now()->format('Y-m-d') . '.pdf' ); } public function render() { $settings = Setting::instance(); $homeLimit = max(1, (int) ($settings->home_limit_list ?? 10)); $annoCorrente = AnnoTeocratico::corrente(); $campagnaAttiva = Campagna::attiva(); // Territory counts $totTerritoriAttivi = Territorio::where('attivo', true)->count(); $totAssegnati = Territorio::assegnato()->count(); $totInReparto = Territorio::inReparto()->count(); // Coverage: returned territories per current theocratic year $territoriPercorsi = 0; if ($annoCorrente) { $territoriPercorsi = Assegnazione::where('anno_teocratico_id', $annoCorrente->id) ->whereNotNull('returned_at') ->distinct('territorio_id') ->count('territorio_id'); } // Monthly average (territories/month this year) $mediaPercorrenzaMensile = 0; if ($annoCorrente && $annoCorrente->mesi_trascorsi > 0) { $mediaPercorrenzaMensile = round($territoriPercorsi / $annoCorrente->mesi_trascorsi, 1); } // Average assignment duration in months (current year only, matches old app "Media Percorrenza Congregazione") // // Il calcolo sta in PHP e non in SQL. La versione precedente usava // DATEDIFF() e YEAR(), che sono funzioni MySQL: rendevano questa pagina // — la piu' visitata dell'applicazione — l'unica non verificabile dalla // suite di test, che gira su SQLite. Il costo e' due colonne di date per // le assegnazioni chiuse di un anno di servizio: qualche centinaio di // righe, non un tavolo intero. // // I due filtri sono quelli di prima: le date assurde arrivate dalla // conversione del vecchio database, e le durate non positive, che // altrimenti abbasserebbero la media senza significare niente. $avgGiorni = null; if ($annoCorrente) { $durate = Assegnazione::where('anno_teocratico_id', $annoCorrente->id) ->whereNotNull('returned_at') ->get(['assigned_at', 'returned_at']) ->filter(fn (Assegnazione $a) => $a->assigned_at?->year >= 1900) ->map(fn (Assegnazione $a) => $a->assigned_at->diffInDays($a->returned_at, false)) ->filter(fn (float $giorni) => $giorni > 0); $avgGiorni = $durate->isEmpty() ? null : $durate->avg(); } $mediaDurataPercorrenzaMesi = $avgGiorni ? round($avgGiorni / 30.44, 1) : 0; // Campaign stats $campagnaStats = null; if ($campagnaAttiva) { $campagnaStats = [ 'descrizione' => $campagnaAttiva->descrizione, 'percentuale' => $campagnaAttiva->percentuale_percorrenza, 'fine' => $campagnaAttiva->end_date->format('d/m/Y'), ]; } // Quick lists: la logica di priorità sta in Territorio::ordinaPerPriorita // (la usano anche il pacchetto dei gruppi di servizio e "Il mio gruppo"). $territoriDaAssegnare = Territorio::ordinaPerPriorita( Territorio::inReparto()->with('zona', 'tipologia', 'ultimaAssegnazione')->get() )->take($homeLimit)->values(); $daRientrare = Territorio::daRientrare() ->with(['zona', 'assegnazioneCorrente.proclamatore']) ->take($homeLimit) ->get(); return view('livewire.home', [ 'congregazione' => $settings->congregazione_nome ?? 'TerManager2', 'annoCorrente' => $annoCorrente, 'totTerritoriAttivi' => $totTerritoriAttivi, 'totAssegnati' => $totAssegnati, 'totInReparto' => $totInReparto, 'territoriPercorsi' => $territoriPercorsi, 'mediaPercorrenzaMensile' => $mediaPercorrenzaMensile, 'mediaDurataPercorrenzaMesi' => $mediaDurataPercorrenzaMesi, 'campagnaStats' => $campagnaStats, 'homeLimit' => $homeLimit, 'territoriDaAssegnare' => $territoriDaAssegnare, 'daRientrare' => $daRientrare, 'richieste' => $this->richiesteAperte(), ]); } }