utente = User::create([ 'name' => 'Amministratore', 'email' => 'admin@example.test', 'password' => 'password', ]); $this->proclamatore = Proclamatore::create([ 'nome' => 'Mario', 'cognome' => 'Rossi', 'attivo' => true, ]); $this->anno = AnnoTeocratico::create([ 'label' => '2025-2026', 'start_date' => '2025-09-01', 'end_date' => '2026-08-31', ]); } // ─── Il primo ciclo ───────────────────────────────────────── public function test_una_sola_assegnazione_riempie_la_prima_colonna_e_lascia_le_altre_in_bianco(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-10', '2025-10-15'); $riga = $this->righe()->sole(); $this->assertSame('1', $riga['numero']); $this->assertSame('', $riga['riporto']); $this->assertCount(4, $riga['assegnazioni']); $this->assertSame( ['nome' => 'Rossi M.', 'assegnato_il' => '10/09/2025', 'restituito_il' => '15/10/2025'], $riga['assegnazioni'][0] ); // Le colonne libere restano stampate: sul modulo cartaceo si compilano // a mano. foreach ([1, 2, 3] as $indice) { $this->assertSame( ['nome' => '', 'assegnato_il' => '', 'restituito_il' => ''], $riga['assegnazioni'][$indice] ); } } public function test_il_nome_e_cognome_piu_iniziale_del_nome(): void { $proclamatore = Proclamatore::create([ 'nome' => 'élena Maria', 'cognome' => "Dell'Acqua Bianchi", 'attivo' => true, ]); $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-10')->update(['proclamatore_id' => $proclamatore->id]); $this->assertSame("Dell'Acqua Bianchi É.", $this->righe()->sole()['assegnazioni'][0]['nome']); } public function test_un_assegnazione_ancora_aperta_ha_la_data_di_rientro_vuota(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-10'); $riga = $this->righe()->sole(); $this->assertSame('10/09/2025', $riga['assegnazioni'][0]['assegnato_il']); $this->assertSame('', $riga['assegnazioni'][0]['restituito_il']); } public function test_tre_assegnazioni_occupano_le_prime_tre_colonne(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-01', '2025-09-20'); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $riga = $this->righe()->sole(); $this->assertSame( ['01/09/2025', '01/10/2025', '01/11/2025', ''], array_column($riga['assegnazioni'], 'assegnato_il') ); $this->assertSame('', $riga['riporto']); } public function test_quattro_assegnazioni_riempiono_la_riga_senza_riporto(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-01', '2025-09-20'); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $this->assegna($territorio, '2025-12-01', '2025-12-20'); $riga = $this->righe()->sole(); $this->assertSame( ['01/09/2025', '01/10/2025', '01/11/2025', '01/12/2025'], array_column($riga['assegnazioni'], 'assegnato_il') ); // Finche' il primo ciclo non e' esaurito non c'e' niente da riportare. $this->assertSame('', $riga['riporto']); } // ─── Il ciclo che ricomincia ──────────────────────────────── /** * Il caso che da' il nome alla regola: riempito il quarto gruppo si * ricomincia da capo, come sul modulo cartaceo si aprirebbe una riga nuova. * Le prime quattro assegnazioni escono dal modulo e restano solo nel * riporto. */ public function test_la_quinta_assegnazione_apre_un_ciclo_nuovo_e_lascia_il_riporto(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-01', '2025-09-20'); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $this->assegna($territorio, '2025-12-01', '2025-12-24'); $this->assegna($territorio, '2026-01-05', '2026-01-25'); $riga = $this->righe()->sole(); $this->assertSame( ['05/01/2026', '', '', ''], array_column($riga['assegnazioni'], 'assegnato_il') ); // La colonna riporta la data di rientro della quarta, cioe' quella che // chiudeva il giro precedente — senza il nome di chi lo aveva. $this->assertSame('24/12/2025', $riga['riporto']); } public function test_otto_assegnazioni_mostrano_il_secondo_ciclo_per_intero(): void { $territorio = $this->territorio('1'); $date = [ ['2025-09-01', '2025-09-20'], ['2025-10-01', '2025-10-20'], ['2025-11-01', '2025-11-20'], ['2025-12-01', '2025-12-24'], ['2026-01-05', '2026-01-25'], ['2026-02-05', '2026-02-25'], ['2026-03-05', '2026-03-25'], ['2026-04-05', '2026-04-25'], ]; foreach ($date as [$da, $a]) { $this->assegna($territorio, $da, $a); } $riga = $this->righe()->sole(); $this->assertSame( ['05/01/2026', '05/02/2026', '05/03/2026', '05/04/2026'], array_column($riga['assegnazioni'], 'assegnato_il') ); $this->assertSame('24/12/2025', $riga['riporto']); } public function test_alla_nona_assegnazione_il_riporto_e_quello_dell_ottava(): void { $territorio = $this->territorio('1'); $date = [ ['2025-09-01', '2025-09-20'], ['2025-10-01', '2025-10-20'], ['2025-11-01', '2025-11-20'], ['2025-12-01', '2025-12-24'], ['2026-01-05', '2026-01-25'], ['2026-02-05', '2026-02-25'], ['2026-03-05', '2026-03-25'], ['2026-04-05', '2026-04-28'], ['2026-05-05', null], ]; foreach ($date as [$da, $a]) { $this->assegna($territorio, $da, $a); } $riga = $this->righe()->sole(); $this->assertSame( ['05/05/2026', '', '', ''], array_column($riga['assegnazioni'], 'assegnato_il') ); $this->assertSame('28/04/2026', $riga['riporto']); } /** * Il riporto legge la data di rientro dell'ultima assegnazione del giro * precedente: se quella e' ancora aperta la colonna resta vuota invece di * inventarsi una data. */ public function test_il_riporto_resta_vuoto_se_l_ultima_del_giro_precedente_non_e_rientrata(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-01', '2025-09-20'); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $this->assegna($territorio, '2025-12-01', null); $this->assegna($territorio, '2026-01-05', '2026-01-25'); $riga = $this->righe()->sole(); $this->assertSame('05/01/2026', $riga['assegnazioni'][0]['assegnato_il']); $this->assertSame('', $riga['riporto']); } // ─── Cosa entra nel modulo ────────────────────────────────── public function test_i_territori_disattivati_restano_fuori_dal_modulo(): void { $attivo = $this->territorio('1'); $disattivato = $this->territorio('2', attivo: false); $this->assegna($attivo, '2025-09-01'); $this->assegna($disattivato, '2025-09-01'); $this->assertSame(['1'], $this->righe()->pluck('numero')->all()); } public function test_i_territori_nel_cestino_restano_fuori_dal_modulo(): void { $vivo = $this->territorio('1'); $cestinato = $this->territorio('2'); $this->assegna($vivo, '2025-09-01'); $this->assegna($cestinato, '2025-09-01'); $cestinato->delete(); $this->assertSame(['1'], $this->righe()->pluck('numero')->all()); } public function test_le_assegnazioni_chiuse_di_un_altro_anno_di_servizio_non_compaiono(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-09-01', '2025-09-20'); $this->assegna($territorio, '2025-01-10', '2025-02-10', $this->annoPrecedente()); $riga = $this->righe()->sole(); $this->assertSame( ['01/09/2025', '', '', ''], array_column($riga['assegnazioni'], 'assegnato_il') ); } /** * Il registro serve a vedere anche i territori fermi: una riga vuota con * la data dell'ultimo rientro dice da quanto non escono. */ public function test_un_territorio_senza_assegnazioni_nell_anno_ha_comunque_la_sua_riga(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-03-01', '2025-04-12', $this->annoPrecedente()); $riga = $this->righe()->sole(); $this->assertSame('1', $riga['numero']); $this->assertSame('12/04/2025', $riga['riporto']); $this->assertSame(['', '', '', ''], array_column($riga['assegnazioni'], 'nome')); } public function test_un_territorio_mai_assegnato_ha_la_riga_vuota(): void { $this->territorio('1'); $riga = $this->righe()->sole(); $this->assertSame('', $riga['riporto']); $this->assertSame(['', '', '', ''], array_column($riga['assegnazioni'], 'assegnato_il')); } // ─── Il riporto a inizio anno ─────────────────────────────── /** * La nota del modulo: "quando si inizia un nuovo foglio, registrare in * questa colonna l'ultima data in cui e' stato restituito ogni territorio". */ public function test_nel_primo_ciclo_il_riporto_e_l_ultimo_rientro_prima_dell_anno(): void { $precedente = $this->annoPrecedente(); $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-01-10', '2025-02-10', $precedente); $this->assegna($territorio, '2025-05-02', '2025-06-30', $precedente); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $riga = $this->righe()->sole(); $this->assertSame('30/06/2025', $riga['riporto']); $this->assertSame('01/10/2025', $riga['assegnazioni'][0]['assegnato_il']); } // ─── A cavallo d'anno ─────────────────────────────────────── /** * Uscito ad agosto, rientrato a ottobre: sul foglio nuovo occupa il primo * gruppo con entrambe le date, e il riporto e' il rientro precedente. */ public function test_l_assegnazione_a_cavallo_apre_il_foglio_nuovo_con_entrambe_le_date(): void { $precedente = $this->annoPrecedente(); $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-05-02', '2025-06-30', $precedente); $this->assegna($territorio, '2025-08-10', '2025-10-05', $precedente); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $riga = $this->righe()->sole(); $this->assertSame('30/06/2025', $riga['riporto']); $this->assertSame( ['nome' => 'Rossi M.', 'assegnato_il' => '10/08/2025', 'restituito_il' => '05/10/2025'], $riga['assegnazioni'][0] ); $this->assertSame('01/11/2025', $riga['assegnazioni'][1]['assegnato_il']); } public function test_sul_foglio_vecchio_l_assegnazione_a_cavallo_resta_senza_rientro(): void { $precedente = $this->annoPrecedente(); $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-08-10', '2025-10-05', $precedente); $riga = $this->righe($precedente)->sole(); $this->assertSame('10/08/2025', $riga['assegnazioni'][0]['assegnato_il']); $this->assertSame('', $riga['assegnazioni'][0]['restituito_il']); } public function test_un_assegnazione_ancora_fuori_da_un_anno_precedente_compare_sul_foglio_nuovo(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-07-01', null, $this->annoPrecedente()); $riga = $this->righe()->sole(); $this->assertSame('01/07/2025', $riga['assegnazioni'][0]['assegnato_il']); $this->assertSame('', $riga['assegnazioni'][0]['restituito_il']); } /** L'assegnazione a cavallo conta nel ciclo: con altre quattro nell'anno, la riga ricomincia. */ public function test_l_assegnazione_a_cavallo_conta_nel_ciclo_da_quattro(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-08-10', '2025-09-15', $this->annoPrecedente()); $this->assegna($territorio, '2025-10-01', '2025-10-20'); $this->assegna($territorio, '2025-11-01', '2025-11-20'); $this->assegna($territorio, '2025-12-01', '2025-12-24'); $this->assegna($territorio, '2026-01-05'); $riga = $this->righe()->sole(); $this->assertSame('24/12/2025', $riga['riporto']); $this->assertSame(['05/01/2026', '', '', ''], array_column($riga['assegnazioni'], 'assegnato_il')); } /** * Il foglio sceglie per data, non per anno_teocratico_id: una data * corretta a mano nel Registro o un import legacy possono lasciare l'anno * registrato incoerente con il giorno di uscita. */ public function test_conta_la_data_di_uscita_non_l_anno_registrato(): void { $precedente = $this->annoPrecedente(); $territorio = $this->territorio('1'); $this->assegna($territorio, '2025-10-01', '2025-10-20', $precedente); $this->assertSame('01/10/2025', $this->righe()->sole()['assegnazioni'][0]['assegnato_il']); $this->assertSame('', $this->righe($precedente)->sole()['assegnazioni'][0]['assegnato_il']); } public function test_l_ultimo_giorno_dell_anno_appartiene_al_foglio(): void { $territorio = $this->territorio('1'); $this->assegna($territorio, '2026-08-31', '2026-08-31'); $riga = $this->righe()->sole(); $this->assertSame('31/08/2026', $riga['assegnazioni'][0]['assegnato_il']); $this->assertSame('31/08/2026', $riga['assegnazioni'][0]['restituito_il']); } // ─── Impaginazione e PDF ──────────────────────────────────── public function test_ogni_foglio_ha_venti_righe_come_il_modulo(): void { foreach (range(1, 21) as $numero) { $this->territorio((string) $numero); } $pagine = (new ModuloS13())->pagine($this->righe()); $this->assertCount(2, $pagine); $this->assertCount(20, $pagine[0]); $this->assertCount(20, $pagine[1]); $this->assertSame('21', $pagine[1][0]['numero']); $this->assertSame('', $pagine[1][1]['numero']); } public function test_il_pdf_e_il_modulo_ufficiale_con_una_pagina_per_foglio(): void { foreach (range(1, 21) as $numero) { $this->assegna($this->territorio((string) $numero), '2025-09-01'); } $pdf = (new ModuloS13())->genera($this->anno); $this->assertStringStartsWith('%PDF-', $pdf); $this->assertSame(2, preg_match_all('#/Type /Page\b(?!s)#', $pdf)); } /** * I numeri di territorio sono stringhe ("1", "1 A", "10"): l'ordinamento e' * naturale, non alfabetico, altrimenti il 10 finirebbe fra l'1 e il 2. */ public function test_le_righe_sono_in_ordine_naturale_di_numero(): void { foreach (['10', '2', '1 A', '1'] as $numero) { $this->assegna($this->territorio($numero), '2025-09-01'); } $this->assertSame(['1', '1 A', '2', '10'], $this->righe()->pluck('numero')->all()); } // ─── helper ───────────────────────────────────────────────── private function territorio(string $numero, bool $attivo = true): Territorio { return Territorio::create([ 'numero' => $numero, 'attivo' => $attivo, ]); } private function assegna( Territorio $territorio, string $assegnatoIl, ?string $rientratoIl = null, ?AnnoTeocratico $anno = null ): Assegnazione { return Assegnazione::create([ 'territorio_id' => $territorio->id, 'proclamatore_id' => $this->proclamatore->id, 'anno_teocratico_id' => ($anno ?? $this->anno)->id, 'assigned_at' => $assegnatoIl, 'returned_at' => $rientratoIl, 'created_by' => $this->utente->id, ]); } /** * @return Collection */ private function righe(?AnnoTeocratico $anno = null): Collection { return (new ModuloS13())->righe($anno ?? $this->anno); } private function annoPrecedente(): AnnoTeocratico { return AnnoTeocratico::create([ 'label' => '2024-2025', 'start_date' => '2024-09-01', 'end_date' => '2025-08-31', ]); } }