Modulo S-13 - Il PDF scrive sopra il modulo ufficiale (FPDI, matrice convertita in PDF 1.4) invece di ricostruirlo in HTML: 20 righe per foglio come l'originale. - Una riga per ogni territorio attivo; riporto dell'ultimo rientro prima dell'anno; assegnazioni a cavallo d'anno su entrambi i fogli; scelta per data e non per anno registrato; nomi come "Cognome I.". - Logica spostata in App\Services\Registro\ModuloS13. Integrità dei dati - Export/import XML (e quindi backup/ripristino): le date non slittano più di un giorno; i file vecchi in UTC tornano al giorno giusto. - Import XML: rifiuta file estranei o incompleti senza toccare i dati, errore con riga e motivo, territori senza zona importati con il loro storico, caratteri di controllo rimossi in export, chi importa conserva password e ruolo di amministratore. - Assegnazioni: assegnazioneCorrente corretta (filtro dentro ofMany), periodi sovrapposti rifiutati in Assegna e Registro sotto lock, anno di servizio ricavato dalla data, niente date future nel Registro. - Utenti: non si declassa sé stessi né l'ultimo amministratore. - Entrypoint: non sovrascrive più la copia persistente di APP_KEY con un valore diverso; serve APP_KEY_ROTATE=1 per un cambio voluto. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
212 lines
7.7 KiB
PHP
212 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Livewire\Assegnazioni\Assegna;
|
|
use App\Livewire\Registro;
|
|
use App\Models\AnnoTeocratico;
|
|
use App\Models\Assegnazione;
|
|
use App\Models\Proclamatore;
|
|
use App\Models\Territorio;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Spatie\Permission\Models\Permission;
|
|
use Spatie\Permission\Models\Role;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Un territorio è fuori al massimo una volta: nessuna doppia assegnazione
|
|
* aperta, nessuna uscita retrodatata a prima dell'ultimo rientro, nessun
|
|
* periodo accavallato dalle correzioni nel Registro.
|
|
*/
|
|
class AssegnazioneSovrapposizioneTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private User $admin;
|
|
private Territorio $territorio;
|
|
private Proclamatore $proclamatore;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
Carbon::setTestNow('2026-10-09 10:00:00');
|
|
|
|
foreach (['settings.manage', 'territori.assign', 'territori.return', 'registro.view'] as $permesso) {
|
|
Permission::create(['name' => $permesso, 'guard_name' => 'web']);
|
|
}
|
|
Role::create(['name' => 'amministratore', 'guard_name' => 'web'])->givePermissionTo(Permission::all());
|
|
|
|
$this->admin = User::create(['name' => 'Admin', 'email' => 'admin@example.test', 'password' => 'password']);
|
|
$this->admin->assignRole('amministratore');
|
|
$this->actingAs($this->admin);
|
|
|
|
$this->territorio = Territorio::create(['numero' => '5', 'attivo' => true]);
|
|
$this->proclamatore = Proclamatore::create(['nome' => 'Mario', 'cognome' => 'Rossi', 'attivo' => true]);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
Carbon::setTestNow();
|
|
parent::tearDown();
|
|
}
|
|
|
|
// ─── L'assegnazione in corso ────────────────────────────────
|
|
|
|
/**
|
|
* Il caso che rendeva il territorio "libero" mentre era fuori: una chiusa
|
|
* con data più recente dell'aperta.
|
|
*/
|
|
public function test_l_assegnazione_in_corso_si_trova_anche_con_una_chiusa_piu_recente(): void
|
|
{
|
|
$aperta = $this->assegna('2026-08-20');
|
|
$this->assegna('2026-09-01', '2026-09-15');
|
|
|
|
$this->assertTrue($this->territorio->fresh()->assegnazioneCorrente?->is($aperta));
|
|
}
|
|
|
|
// ─── Il controllo di sovrapposizione ────────────────────────
|
|
|
|
public function test_rientro_e_nuova_uscita_nello_stesso_giorno_non_si_sovrappongono(): void
|
|
{
|
|
$this->assegna('2026-09-01', '2026-09-20');
|
|
|
|
$this->assertNull(Assegnazione::sovrappostaA($this->territorio->id, Carbon::parse('2026-09-20')));
|
|
}
|
|
|
|
public function test_un_uscita_prima_dell_ultimo_rientro_si_sovrappone(): void
|
|
{
|
|
$chiusa = $this->assegna('2026-09-01', '2026-09-20');
|
|
|
|
$this->assertTrue(Assegnazione::sovrappostaA($this->territorio->id, Carbon::parse('2026-09-10'))?->is($chiusa));
|
|
}
|
|
|
|
public function test_con_un_assegnazione_aperta_ogni_nuova_uscita_si_sovrappone(): void
|
|
{
|
|
$aperta = $this->assegna('2026-09-01');
|
|
|
|
$this->assertTrue(Assegnazione::sovrappostaA($this->territorio->id, Carbon::parse('2026-10-01'))?->is($aperta));
|
|
}
|
|
|
|
public function test_un_periodo_chiuso_inserito_prima_di_tutti_non_si_sovrappone(): void
|
|
{
|
|
$this->assegna('2026-09-01');
|
|
|
|
$this->assertNull(Assegnazione::sovrappostaA($this->territorio->id, Carbon::parse('2026-06-01'), Carbon::parse('2026-07-01')));
|
|
}
|
|
|
|
// ─── Pagina Assegna ─────────────────────────────────────────
|
|
|
|
public function test_assegna_rifiuta_un_uscita_retrodatata_a_prima_dell_ultimo_rientro(): void
|
|
{
|
|
$this->assegna('2026-09-01', '2026-09-20');
|
|
|
|
Livewire::test(Assegna::class, ['territorioId' => $this->territorio->id])
|
|
->set('proclamatore_id', $this->proclamatore->id)
|
|
->set('assigned_at', '2026-09-10')
|
|
->call('save')
|
|
->assertHasErrors('assigned_at');
|
|
|
|
$this->assertSame(1, Assegnazione::count());
|
|
}
|
|
|
|
public function test_assegna_accetta_l_uscita_dal_giorno_del_rientro(): void
|
|
{
|
|
$this->assegna('2026-09-01', '2026-09-20');
|
|
|
|
Livewire::test(Assegna::class, ['territorioId' => $this->territorio->id])
|
|
->set('proclamatore_id', $this->proclamatore->id)
|
|
->set('assigned_at', '2026-09-20')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$this->assertSame(2, Assegnazione::count());
|
|
}
|
|
|
|
// ─── Registro (correzioni dell'amministratore) ──────────────
|
|
|
|
public function test_il_registro_ricava_l_anno_di_servizio_dalla_data(): void
|
|
{
|
|
Livewire::test(Registro::class)
|
|
->call('openCreate')
|
|
->set('form_territorio_id', (string) $this->territorio->id)
|
|
->set('form_proclamatore_id', (string) $this->proclamatore->id)
|
|
->set('form_assigned_at', '2025-08-10')
|
|
->set('form_returned_at', '2025-09-05')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$this->assertSame('2024-2025', Assegnazione::sole()->annoTeocratico->label);
|
|
}
|
|
|
|
public function test_il_registro_non_crea_una_seconda_assegnazione_aperta(): void
|
|
{
|
|
$this->assegna('2026-09-01');
|
|
|
|
Livewire::test(Registro::class)
|
|
->call('openCreate')
|
|
->set('form_territorio_id', (string) $this->territorio->id)
|
|
->set('form_proclamatore_id', (string) $this->proclamatore->id)
|
|
->set('form_assigned_at', '2026-10-01')
|
|
->call('save')
|
|
->assertHasErrors('form_assigned_at');
|
|
|
|
$this->assertSame(1, Assegnazione::count());
|
|
}
|
|
|
|
public function test_il_registro_non_riapre_un_assegnazione_chiusa_se_il_territorio_e_fuori(): void
|
|
{
|
|
$chiusa = $this->assegna('2026-08-01', '2026-08-20');
|
|
$this->assegna('2026-09-01');
|
|
|
|
Livewire::test(Registro::class)
|
|
->call('openEdit', $chiusa->id)
|
|
->set('form_returned_at', '')
|
|
->call('save')
|
|
->assertHasErrors('form_assigned_at');
|
|
|
|
$this->assertNotNull($chiusa->fresh()->returned_at);
|
|
}
|
|
|
|
public function test_il_registro_rifiuta_le_date_future(): void
|
|
{
|
|
Livewire::test(Registro::class)
|
|
->call('openCreate')
|
|
->set('form_territorio_id', (string) $this->territorio->id)
|
|
->set('form_proclamatore_id', (string) $this->proclamatore->id)
|
|
->set('form_assigned_at', '2026-11-01')
|
|
->call('save')
|
|
->assertHasErrors('form_assigned_at');
|
|
}
|
|
|
|
public function test_il_registro_permette_di_correggere_la_stessa_assegnazione(): void
|
|
{
|
|
$aperta = $this->assegna('2026-09-01');
|
|
|
|
Livewire::test(Registro::class)
|
|
->call('openEdit', $aperta->id)
|
|
->set('form_assigned_at', '2026-09-03')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$this->assertSame('2026-09-03', $aperta->fresh()->assigned_at->toDateString());
|
|
}
|
|
|
|
// ─── helper ─────────────────────────────────────────────────
|
|
|
|
private function assegna(string $uscita, ?string $rientro = null): Assegnazione
|
|
{
|
|
return Assegnazione::create([
|
|
'territorio_id' => $this->territorio->id,
|
|
'proclamatore_id' => $this->proclamatore->id,
|
|
'anno_teocratico_id' => AnnoTeocratico::perData(Carbon::parse($uscita))->id,
|
|
'assigned_at' => $uscita,
|
|
'returned_at' => $rientro,
|
|
'created_by' => $this->admin->id,
|
|
]);
|
|
}
|
|
}
|