diff --git a/app/Livewire/Registro.php b/app/Livewire/Registro.php index fa9b15d..304bfda 100644 --- a/app/Livewire/Registro.php +++ b/app/Livewire/Registro.php @@ -250,7 +250,7 @@ class Registro extends Component { $vuota = [ 'numero' => '', - 'riporto' => ['nome' => '', 'data' => ''], + 'riporto' => '', 'assegnazioni' => $this->completaCelleS13([]), ]; @@ -285,28 +285,29 @@ class Registro extends Component ->get() ->groupBy('territorio_id'); - $rientriPrecedenti = $this->ultimiRientriPrimaDi( - $assegnazioni->keys()->all(), - $anno - ); - return $assegnazioni - ->map(function ($gruppo, $territorioId) use ($rientriPrecedenti) { + ->map(function ($gruppo) { $gruppo = $gruppo->values(); - // I quattro gruppi "Assegnato a" ospitano le ultime quattro - // assegnazioni, in ordine cronologico da sinistra. - $ultime = $gruppo->slice(-self::S13_ASSEGNAZIONI_PER_RIGA)->values(); + // Il ciclo di registrazione è lungo quanto i gruppi "Assegnato + // a": riempito il quarto si ricomincia da capo, come sul modulo + // cartaceo si aprirebbe una riga nuova per quel territorio. + // Cinque assegnazioni non mostrano quindi le ultime quattro, ma + // solo la quinta, in prima colonna. + $inizioCiclo = intdiv($gruppo->count() - 1, self::S13_ASSEGNAZIONI_PER_RIGA) + * self::S13_ASSEGNAZIONI_PER_RIGA; - $celle = $ultime->map(fn (Assegnazione $a) => [ - 'nome' => $a->proclamatore?->nome_completo ?? '', - 'assegnato_il' => $a->assigned_at?->format('d/m/Y') ?? '', - 'restituito_il' => $a->returned_at?->format('d/m/Y') ?? '', - ])->all(); + $celle = $gruppo + ->slice($inizioCiclo, self::S13_ASSEGNAZIONI_PER_RIGA) + ->map(fn (Assegnazione $a) => [ + 'nome' => $a->proclamatore?->nome_completo ?? '', + 'assegnato_il' => $a->assigned_at?->format('d/m/Y') ?? '', + 'restituito_il' => $a->returned_at?->format('d/m/Y') ?? '', + ])->all(); return [ 'numero' => $gruppo->first()->territorio?->numero ?? '', - 'riporto' => $this->riportoS13($gruppo, $rientriPrecedenti->get($territorioId)), + 'riporto' => $this->riportoS13($gruppo, $inizioCiclo), 'assegnazioni' => $this->completaCelleS13($celle), ]; }) @@ -315,64 +316,26 @@ class Registro extends Component } /** - * Contenuto della colonna "Restituito l'ultima volta il", che funziona da - * quinta casella della riga. + * Contenuto della colonna "Restituito l'ultima volta il": la data di + * rientro dell'ultima assegnazione *prima* che il ciclo ricominciasse, + * cioè quella che occupava il quarto gruppo del giro precedente. * - * Quando le assegnazioni dell'anno superano le quattro che i gruppi - * "Assegnato a" possono ospitare, la colonna accoglie quella - * immediatamente precedente alle quattro mostrate — nome e data di - * rientro — così la riga copre cinque passaggi invece di quattro. In quel - * caso il riporto dell'anno precedente non ha più senso: la continuità - * con il passato ce l'ha già l'assegnazione in colonna. + * È il riporto che il modulo cartaceo chiede quando si apre una riga nuova + * per un territorio già registrato: la riga mostra da dove si riparte, + * senza il nome di chi lo aveva (la colonna ha solo la data). * - * Con quattro o meno assegnazioni nell'anno la colonna torna al suo uso - * da modulo: l'ultima data di rientro dell'anno di servizio precedente, - * senza nome. + * Finché il primo ciclo non è esaurito — quattro assegnazioni o meno + * nell'anno — non c'è niente da riportare e la colonna resta vuota. * * @param \Illuminate\Support\Collection $gruppo - * @return array{nome: string, data: string} */ - private function riportoS13(\Illuminate\Support\Collection $gruppo, ?string $rientroAnnoPrecedente): array + private function riportoS13(\Illuminate\Support\Collection $gruppo, int $inizioCiclo): string { - $eccedenti = $gruppo->count() - self::S13_ASSEGNAZIONI_PER_RIGA; - - if ($eccedenti > 0) { - $precedente = $gruppo->get($eccedenti - 1); - - return [ - 'nome' => $precedente->proclamatore?->nome_completo ?? '', - 'data' => $precedente->returned_at?->format('d/m/Y') ?? '', - ]; + if ($inizioCiclo === 0) { + return ''; } - return [ - 'nome' => '', - 'data' => $rientroAnnoPrecedente - ? \Carbon\Carbon::parse($rientroAnnoPrecedente)->format('d/m/Y') - : '', - ]; - } - - /** - * Ultima data di rientro di ogni territorio *precedente* all'inizio - * dell'anno di servizio: è il dato che la colonna "Restituito l'ultima - * volta il" del modulo chiede di riportare quando si apre un foglio nuovo. - * - * @param array $territorioIds - * @return \Illuminate\Support\Collection - */ - private function ultimiRientriPrimaDi(array $territorioIds, AnnoTeocratico $anno): \Illuminate\Support\Collection - { - if ($territorioIds === []) { - return collect(); - } - - return Assegnazione::whereIn('territorio_id', $territorioIds) - ->whereNotNull('returned_at') - ->whereDate('returned_at', '<', $anno->start_date) - ->groupBy('territorio_id') - ->selectRaw('territorio_id, MAX(returned_at) as ultimo_rientro') - ->pluck('ultimo_rientro', 'territorio_id'); + return $gruppo->get($inizioCiclo - 1)?->returned_at?->format('d/m/Y') ?? ''; } /** diff --git a/resources/views/pdf/registro-s13.blade.php b/resources/views/pdf/registro-s13.blade.php index 4c83403..317253b 100644 --- a/resources/views/pdf/registro-s13.blade.php +++ b/resources/views/pdf/registro-s13.blade.php @@ -30,31 +30,19 @@ * l'ellissi rende visibile l'accorciamento, mentre un nome che deborda * corromperebbe la griglia in silenzio. * - * Le due celle hanno vincoli diversi. - * - * Le larghezze qui sotto sono misurate sul PDF renderizzato, non dedotte - * dalle percentuali: al netto di padding e bordi le celle risultano - * 35.9mm e 22.2mm, e queste costanti tengono un margine su quei valori. - * Se cambiano le percentuali delle colonne o i margini di pagina, vanno - * rimisurate. + * La larghezza qui sotto è misurata sul PDF renderizzato, non dedotta + * dalle percentuali: al netto di padding e bordi la cella risulta 35.9mm, + * e la costante tiene un margine su quel valore. Se cambiano le + * percentuali delle colonne o i margini di pagina, va rimisurata. * * "Assegnato a" è nowrap su una riga sola: basta il corpo che ci sta, e - * oltre il minimo si tronca. - * - * Il riporto sta in una colonna stretta alta due righe. Lì il - * nome NON viene mandato a capo da dompdf: lo spezziamo noi in due righe - * bilanciate e le teniamo nowrap. Lasciar decidere al wrapper significa - * non sapere quante righe verranno fuori — e una terza riga non è un - * problema estetico, sfonda l'altezza del blocco e manda l'ultimo - * territorio su una pagina nuova, raddoppiando i fogli. Spezzando a mano - * l'altezza della cella è esatta per costruzione. + * oltre il minimo si tronca. La colonna del riporto ospita solo una data, + * quindi non ha bisogno di adattamenti. */ $MM_PER_CARATTERE_PER_PUNTO = 0.20; $CORPO_MIN_PT = 4.5; - $LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm - $LARGHEZZA_RIPORTO_MM = 20.5; // colonna misurata 22.2mm - $CORPO_RIPORTO_MAX_PT = 6.0; + $LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm $corpoPer = static function (string $testo, float $larghezzaMm, float $corpoMaxPt) use ($MM_PER_CARATTERE_PER_PUNTO, $CORPO_MIN_PT): string { $len = mb_strlen($testo); @@ -74,46 +62,6 @@ return mb_strlen($testo) > $max ? mb_substr($testo, 0, $max - 1) . '…' : $testo; }; - /** - * Spezza il nome in due righe scegliendo, fra tutti i punti fra una parola - * e l'altra, quello che rende più corta la riga più lunga: è la riga più - * lunga a decidere il corpo, quindi bilanciare significa poter scrivere - * più grande. - * - * @return array{righe: array, corpo: string} - */ - $adattaRiporto = static function (string $testo) use ($corpoPer, $troncaPer, $LARGHEZZA_RIPORTO_MM, $CORPO_RIPORTO_MAX_PT): array { - $parole = preg_split('/\s+/', trim($testo), -1, PREG_SPLIT_NO_EMPTY); - - $righe = [$testo]; - - if (count($parole) > 1) { - $migliore = null; - - for ($i = 1; $i < count($parole); $i++) { - $prima = implode(' ', array_slice($parole, 0, $i)); - $dopo = implode(' ', array_slice($parole, $i)); - $peggio = max(mb_strlen($prima), mb_strlen($dopo)); - - if ($migliore === null || $peggio < $migliore['peggio']) { - $migliore = ['righe' => [$prima, $dopo], 'peggio' => $peggio]; - } - } - - $righe = $migliore['righe']; - } - - // Una riga più larga della colonna deborderebbe: si accorcia. - $righe = array_map(fn (string $r) => $troncaPer($r, $LARGHEZZA_RIPORTO_MM), $righe); - - $piuLunga = array_reduce($righe, fn (?string $a, string $b) => mb_strlen($b) > mb_strlen((string) $a) ? $b : $a); - - return [ - 'righe' => $righe, - 'corpo' => $corpoPer((string) $piuLunga, $LARGHEZZA_RIPORTO_MM, $CORPO_RIPORTO_MAX_PT), - ]; - }; - $ultimaPagina = $pagine->count() - 1; @endphp @@ -172,9 +120,9 @@ border: 1.6pt solid #000; /* Obbligatorio, non cosmetico: in layout automatico le larghezze del sono solo un suggerimento e una cella con del - testo lungo (la colonna del riporto, quando ospita un nome) si - allarga schiacciando le altre. I corpi dei nomi sono calcolati - su larghezze fisse, quindi a quel punto il testo deborda. */ + testo lungo (un nome di proclamatore) si allarga schiacciando le + altre. I corpi dei nomi sono calcolati su larghezze fisse, + quindi a quel punto il testo deborda. */ table-layout: fixed; } td, th { @@ -194,21 +142,18 @@ th.sotto { font-size: 6.4pt; } /* Corpo */ - /* 9.4mm a blocco. Il vincolo non sono i nomi (8pt = 2.8mm) ma la cella - del riporto, che a cavallo delle due righe ospita due righe di nome - a 6pt più la data: ~7.2mm. */ + /* 9.4mm a blocco: 21 blocchi devono stare in un foglio come sul modulo + cartaceo. Alzando queste altezze l'ultimo territorio scivola su una + pagina nuova e ogni foglio ne occupa due. */ tr.riga-nome td { height: 5.6mm; } tr.riga-date td { height: 3.8mm; } td.numero { font-size: 9.5pt; font-weight: bold; } td.data { font-size: 7.5pt; } - /* La colonna del riporto è alta due righe: quando ospita - un'assegnazione ci stanno il nome (che qui può andare a capo, c'è - spazio in verticale) e sotto la data di rientro. */ - td.riporto { padding: 0 0.5mm; } - .riporto-nome { line-height: 1.05; white-space: nowrap; } - .riporto-data { font-size: 7pt; line-height: 1.1; } + /* La colonna del riporto è alta due righe e ospita solo la data di + rientro con cui si chiudeva il ciclo precedente. */ + td.riporto { font-size: 7.5pt; padding: 0 0.5mm; } td.nome { line-height: 1.1; padding: 0 0.6mm; white-space: nowrap; } .nota { font-size: 7.4pt; margin-top: 1.6mm; } @@ -251,17 +196,9 @@ @foreach($righe as $riga) - @php($riporto = $adattaRiporto($riga['riporto']['nome'])) {{ $riga['numero'] }} - - @if($riga['riporto']['nome'] !== '') -
- @foreach($riporto['righe'] as $r)
{{ $r }}
@endforeach -
- @endif -
{{ $riga['riporto']['data'] }}
- + {{ $riga['riporto'] }} @foreach($riga['assegnazioni'] as $a) @php($nome = $troncaPer($a['nome'], $LARGHEZZA_NOME_MM)) {{ $nome }}