++ fix
This commit is contained in:
@@ -250,7 +250,7 @@ class Registro extends Component
|
|||||||
{
|
{
|
||||||
$vuota = [
|
$vuota = [
|
||||||
'numero' => '',
|
'numero' => '',
|
||||||
'riporto' => ['nome' => '', 'data' => ''],
|
'riporto' => '',
|
||||||
'assegnazioni' => $this->completaCelleS13([]),
|
'assegnazioni' => $this->completaCelleS13([]),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -285,28 +285,29 @@ class Registro extends Component
|
|||||||
->get()
|
->get()
|
||||||
->groupBy('territorio_id');
|
->groupBy('territorio_id');
|
||||||
|
|
||||||
$rientriPrecedenti = $this->ultimiRientriPrimaDi(
|
|
||||||
$assegnazioni->keys()->all(),
|
|
||||||
$anno
|
|
||||||
);
|
|
||||||
|
|
||||||
return $assegnazioni
|
return $assegnazioni
|
||||||
->map(function ($gruppo, $territorioId) use ($rientriPrecedenti) {
|
->map(function ($gruppo) {
|
||||||
$gruppo = $gruppo->values();
|
$gruppo = $gruppo->values();
|
||||||
|
|
||||||
// I quattro gruppi "Assegnato a" ospitano le ultime quattro
|
// Il ciclo di registrazione è lungo quanto i gruppi "Assegnato
|
||||||
// assegnazioni, in ordine cronologico da sinistra.
|
// a": riempito il quarto si ricomincia da capo, come sul modulo
|
||||||
$ultime = $gruppo->slice(-self::S13_ASSEGNAZIONI_PER_RIGA)->values();
|
// 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) => [
|
$celle = $gruppo
|
||||||
'nome' => $a->proclamatore?->nome_completo ?? '',
|
->slice($inizioCiclo, self::S13_ASSEGNAZIONI_PER_RIGA)
|
||||||
'assegnato_il' => $a->assigned_at?->format('d/m/Y') ?? '',
|
->map(fn (Assegnazione $a) => [
|
||||||
'restituito_il' => $a->returned_at?->format('d/m/Y') ?? '',
|
'nome' => $a->proclamatore?->nome_completo ?? '',
|
||||||
])->all();
|
'assegnato_il' => $a->assigned_at?->format('d/m/Y') ?? '',
|
||||||
|
'restituito_il' => $a->returned_at?->format('d/m/Y') ?? '',
|
||||||
|
])->all();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'numero' => $gruppo->first()->territorio?->numero ?? '',
|
'numero' => $gruppo->first()->territorio?->numero ?? '',
|
||||||
'riporto' => $this->riportoS13($gruppo, $rientriPrecedenti->get($territorioId)),
|
'riporto' => $this->riportoS13($gruppo, $inizioCiclo),
|
||||||
'assegnazioni' => $this->completaCelleS13($celle),
|
'assegnazioni' => $this->completaCelleS13($celle),
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
@@ -315,64 +316,26 @@ class Registro extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contenuto della colonna "Restituito l'ultima volta il", che funziona da
|
* Contenuto della colonna "Restituito l'ultima volta il": la data di
|
||||||
* quinta casella della riga.
|
* 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
|
* È il riporto che il modulo cartaceo chiede quando si apre una riga nuova
|
||||||
* "Assegnato a" possono ospitare, la colonna accoglie quella
|
* per un territorio già registrato: la riga mostra da dove si riparte,
|
||||||
* immediatamente precedente alle quattro mostrate — nome e data di
|
* senza il nome di chi lo aveva (la colonna ha solo la data).
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Con quattro o meno assegnazioni nell'anno la colonna torna al suo uso
|
* Finché il primo ciclo non è esaurito — quattro assegnazioni o meno
|
||||||
* da modulo: l'ultima data di rientro dell'anno di servizio precedente,
|
* nell'anno — non c'è niente da riportare e la colonna resta vuota.
|
||||||
* senza nome.
|
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Support\Collection<int, Assegnazione> $gruppo
|
* @param \Illuminate\Support\Collection<int, Assegnazione> $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 ($inizioCiclo === 0) {
|
||||||
|
return '';
|
||||||
if ($eccedenti > 0) {
|
|
||||||
$precedente = $gruppo->get($eccedenti - 1);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'nome' => $precedente->proclamatore?->nome_completo ?? '',
|
|
||||||
'data' => $precedente->returned_at?->format('d/m/Y') ?? '',
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return $gruppo->get($inizioCiclo - 1)?->returned_at?->format('d/m/Y') ?? '';
|
||||||
'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<int, int> $territorioIds
|
|
||||||
* @return \Illuminate\Support\Collection<int, string>
|
|
||||||
*/
|
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,31 +30,19 @@
|
|||||||
* l'ellissi rende visibile l'accorciamento, mentre un nome che deborda
|
* l'ellissi rende visibile l'accorciamento, mentre un nome che deborda
|
||||||
* corromperebbe la griglia in silenzio.
|
* corromperebbe la griglia in silenzio.
|
||||||
*
|
*
|
||||||
* Le due celle hanno vincoli diversi.
|
* La larghezza qui sotto è misurata sul PDF renderizzato, non dedotta
|
||||||
*
|
* dalle percentuali: al netto di padding e bordi la cella risulta 35.9mm,
|
||||||
* Le larghezze qui sotto sono misurate sul PDF renderizzato, non dedotte
|
* e la costante tiene un margine su quel valore. Se cambiano le
|
||||||
* dalle percentuali: al netto di padding e bordi le celle risultano
|
* percentuali delle colonne o i margini di pagina, va rimisurata.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* "Assegnato a" è nowrap su una riga sola: basta il corpo che ci sta, e
|
* "Assegnato a" è nowrap su una riga sola: basta il corpo che ci sta, e
|
||||||
* oltre il minimo si tronca.
|
* oltre il minimo si tronca. La colonna del riporto ospita solo una data,
|
||||||
*
|
* quindi non ha bisogno di adattamenti.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
$MM_PER_CARATTERE_PER_PUNTO = 0.20;
|
$MM_PER_CARATTERE_PER_PUNTO = 0.20;
|
||||||
$CORPO_MIN_PT = 4.5;
|
$CORPO_MIN_PT = 4.5;
|
||||||
|
|
||||||
$LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm
|
$LARGHEZZA_NOME_MM = 34.0; // cella misurata 35.9mm
|
||||||
$LARGHEZZA_RIPORTO_MM = 20.5; // colonna misurata 22.2mm
|
|
||||||
$CORPO_RIPORTO_MAX_PT = 6.0;
|
|
||||||
|
|
||||||
$corpoPer = static function (string $testo, float $larghezzaMm, float $corpoMaxPt) use ($MM_PER_CARATTERE_PER_PUNTO, $CORPO_MIN_PT): string {
|
$corpoPer = static function (string $testo, float $larghezzaMm, float $corpoMaxPt) use ($MM_PER_CARATTERE_PER_PUNTO, $CORPO_MIN_PT): string {
|
||||||
$len = mb_strlen($testo);
|
$len = mb_strlen($testo);
|
||||||
@@ -74,46 +62,6 @@
|
|||||||
return mb_strlen($testo) > $max ? mb_substr($testo, 0, $max - 1) . '…' : $testo;
|
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<int, string>, 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;
|
$ultimaPagina = $pagine->count() - 1;
|
||||||
@endphp
|
@endphp
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -172,9 +120,9 @@
|
|||||||
border: 1.6pt solid #000;
|
border: 1.6pt solid #000;
|
||||||
/* Obbligatorio, non cosmetico: in layout automatico le larghezze
|
/* Obbligatorio, non cosmetico: in layout automatico le larghezze
|
||||||
del <colgroup> sono solo un suggerimento e una cella con del
|
del <colgroup> sono solo un suggerimento e una cella con del
|
||||||
testo lungo (la colonna del riporto, quando ospita un nome) si
|
testo lungo (un nome di proclamatore) si allarga schiacciando le
|
||||||
allarga schiacciando le altre. I corpi dei nomi sono calcolati
|
altre. I corpi dei nomi sono calcolati su larghezze fisse,
|
||||||
su larghezze fisse, quindi a quel punto il testo deborda. */
|
quindi a quel punto il testo deborda. */
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
td, th {
|
td, th {
|
||||||
@@ -194,21 +142,18 @@
|
|||||||
th.sotto { font-size: 6.4pt; }
|
th.sotto { font-size: 6.4pt; }
|
||||||
|
|
||||||
/* Corpo */
|
/* Corpo */
|
||||||
/* 9.4mm a blocco. Il vincolo non sono i nomi (8pt = 2.8mm) ma la cella
|
/* 9.4mm a blocco: 21 blocchi devono stare in un foglio come sul modulo
|
||||||
del riporto, che a cavallo delle due righe ospita due righe di nome
|
cartaceo. Alzando queste altezze l'ultimo territorio scivola su una
|
||||||
a 6pt più la data: ~7.2mm. */
|
pagina nuova e ogni foglio ne occupa due. */
|
||||||
tr.riga-nome td { height: 5.6mm; }
|
tr.riga-nome td { height: 5.6mm; }
|
||||||
tr.riga-date td { height: 3.8mm; }
|
tr.riga-date td { height: 3.8mm; }
|
||||||
|
|
||||||
td.numero { font-size: 9.5pt; font-weight: bold; }
|
td.numero { font-size: 9.5pt; font-weight: bold; }
|
||||||
td.data { font-size: 7.5pt; }
|
td.data { font-size: 7.5pt; }
|
||||||
|
|
||||||
/* La colonna del riporto è alta due righe: quando ospita
|
/* La colonna del riporto è alta due righe e ospita solo la data di
|
||||||
un'assegnazione ci stanno il nome (che qui può andare a capo, c'è
|
rientro con cui si chiudeva il ciclo precedente. */
|
||||||
spazio in verticale) e sotto la data di rientro. */
|
td.riporto { font-size: 7.5pt; padding: 0 0.5mm; }
|
||||||
td.riporto { padding: 0 0.5mm; }
|
|
||||||
.riporto-nome { line-height: 1.05; white-space: nowrap; }
|
|
||||||
.riporto-data { font-size: 7pt; line-height: 1.1; }
|
|
||||||
td.nome { line-height: 1.1; padding: 0 0.6mm; white-space: nowrap; }
|
td.nome { line-height: 1.1; padding: 0 0.6mm; white-space: nowrap; }
|
||||||
|
|
||||||
.nota { font-size: 7.4pt; margin-top: 1.6mm; }
|
.nota { font-size: 7.4pt; margin-top: 1.6mm; }
|
||||||
@@ -251,17 +196,9 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach($righe as $riga)
|
@foreach($righe as $riga)
|
||||||
@php($riporto = $adattaRiporto($riga['riporto']['nome']))
|
|
||||||
<tr class="riga-nome">
|
<tr class="riga-nome">
|
||||||
<td class="numero" rowspan="2">{{ $riga['numero'] }}</td>
|
<td class="numero" rowspan="2">{{ $riga['numero'] }}</td>
|
||||||
<td class="riporto" rowspan="2">
|
<td class="riporto" rowspan="2">{{ $riga['riporto'] }}</td>
|
||||||
@if($riga['riporto']['nome'] !== '')
|
|
||||||
<div class="riporto-nome" style="font-size:{{ $riporto['corpo'] }}">
|
|
||||||
@foreach($riporto['righe'] as $r)<div>{{ $r }}</div>@endforeach
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<div class="riporto-data">{{ $riga['riporto']['data'] }}</div>
|
|
||||||
</td>
|
|
||||||
@foreach($riga['assegnazioni'] as $a)
|
@foreach($riga['assegnazioni'] as $a)
|
||||||
@php($nome = $troncaPer($a['nome'], $LARGHEZZA_NOME_MM))
|
@php($nome = $troncaPer($a['nome'], $LARGHEZZA_NOME_MM))
|
||||||
<td class="nome" colspan="2" style="font-size:{{ $corpoPer($nome, $LARGHEZZA_NOME_MM, 8) }}">{{ $nome }}</td>
|
<td class="nome" colspan="2" style="font-size:{{ $corpoPer($nome, $LARGHEZZA_NOME_MM, 8) }}">{{ $nome }}</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user