seed(CatalogoSeeder::class); $this->pvc = Sistema::where('codice', 'pvc')->firstOrFail(); $this->actingAs(User::factory()->create(['is_superadmin' => true])); } /** Due tabelle nel formato del listino, separate da una riga vuota. */ private function righe(): array { return [ [null, null, null], ['finestra_1_anta', 400, 500], [400, 367.2, 381.244], [500, 381.24, null], [null, null, null], ['PORTA 1 ANTA, CON SERRATURA', 500, 600], [1800, 796.5, 820], ]; } private function fileExcel(): UploadedFile { $spreadsheet = new Spreadsheet; $spreadsheet->getActiveSheet()->fromArray($this->righe(), null, 'A1', true); $path = tempnam(sys_get_temp_dir(), 'listino'); (new Xlsx($spreadsheet))->save($path); return UploadedFile::fake()->createWithContent('listino.xlsx', file_get_contents($path)); } public function test_il_parser_legge_le_tabelle_in_mm(): void { $blocchi = ListinoExcelParser::parseRighe($this->righe()); $this->assertSame(['finestra_1_anta', 'PORTA 1 ANTA, CON SERRATURA'], array_column($blocchi, 'codice')); $this->assertSame([ ['larghezza' => 400, 'altezza' => 400, 'prezzo' => 367.2], ['larghezza' => 500, 'altezza' => 400, 'prezzo' => 381.24], ['larghezza' => 400, 'altezza' => 500, 'prezzo' => 381.24], ], $blocchi[0]['prezzi']); } public function test_il_parser_gestisce_l_intestazione_sulla_riga_successiva(): void { $blocchi = ListinoExcelParser::parseRighe([ ['CASSONETTO PVC', null, null], ['mm', 500, 600], [300, 94.14, 104.59], ]); $this->assertSame('CASSONETTO PVC', $blocchi[0]['codice']); $this->assertCount(2, $blocchi[0]['prezzi']); } public function test_associazione_proposta_per_codice_alias_e_descrizione(): void { $associazioni = ListinoImporter::suggerisciAssociazioni($this->pvc, [ 'finestra_1_anta', 'finestra_wasistas', 'BILICO ORIZZONTALE', 'ALZANTE 2ANTE_MANO SX', ]); $id = fn (string $codice) => Modello::where('codice', $codice)->value('id'); $this->assertSame([ 'finestra_1_anta' => $id('finestra_1_anta'), 'finestra_wasistas' => $id('finestra_vasistas'), 'BILICO ORIZZONTALE' => $id('bilico'), 'ALZANTE 2ANTE_MANO SX' => null, ], $associazioni); } public function test_l_azione_importa_i_listini_secondo_le_associazioni_scelte(): void { $finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail(); $porta = Modello::where('codice', 'porta_1_anta')->firstOrFail(); $bilico = Modello::where('codice', 'bilico')->firstOrFail(); // Listino preesistente di un modello non coinvolto: deve restare com'รจ. ListinoPrezzo::create(['modello_id' => $bilico->id, 'larghezza' => 1000, 'altezza' => 1000, 'prezzo' => 1]); ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 9990, 'altezza' => 9990, 'prezzo' => 1]); Livewire::test(ListSistemi::class) ->callTableAction('importaListini', $this->pvc, data: [ 'file' => $this->fileExcel(), 'associazioni' => [ 'a' => ['codice' => 'finestra_1_anta', 'modello_id' => $finestra->id], // Associazione corretta a mano dall'admin. 'b' => ['codice' => 'PORTA 1 ANTA, CON SERRATURA', 'modello_id' => $porta->id], ], ]) ->assertHasNoTableActionErrors() ->assertNotified('Listini importati'); $this->assertSame(3, $finestra->listino()->count()); $this->assertFalse($finestra->listino()->where('larghezza', 9990)->exists()); $this->assertEquals(820, $porta->listino()->where('larghezza', 600)->value('prezzo')); $this->assertSame(1, $bilico->listino()->count()); $this->assertNotNull($finestra->fresh()->listino_aggiornato_at); } /** Listino con un prezzo da Excel e due aggiunti a mano (uno su una misura presente nel file). */ private function finestraConPrezziPersonalizzati(): Modello { $finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail(); ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 400, 'altezza' => 400, 'prezzo' => 1]); ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 500, 'altezza' => 400, 'prezzo' => 999, 'personalizzato' => true]); ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 2000, 'altezza' => 2000, 'prezzo' => 888, 'personalizzato' => true]); return $finestra; } private function importaFinestra(Modello $finestra, ?bool $sovrascriviTutto): void { $data = [ 'file' => $this->fileExcel(), 'associazioni' => ['a' => ['codice' => 'finestra_1_anta', 'modello_id' => $finestra->id]], ]; if ($sovrascriviTutto !== null) { $data['sovrascrivi_tutto'] = $sovrascriviTutto; } Livewire::test(ListSistemi::class) ->callTableAction('importaListini', $this->pvc, data: $data) ->assertHasNoTableActionErrors(); } public function test_di_default_l_excel_sovrascrive_anche_i_prezzi_personalizzati(): void { $finestra = $this->finestraConPrezziPersonalizzati(); Livewire::test(ListSistemi::class) ->mountTableAction('importaListini', $this->pvc) ->assertTableActionDataSet(['sovrascrivi_tutto' => true]); $this->importaFinestra($finestra, null); $this->assertSame(3, $finestra->listino()->count()); $this->assertFalse($finestra->listino()->where('personalizzato', true)->exists()); $this->assertEquals(381.24, $finestra->listino()->where('larghezza', 500)->where('altezza', 400)->value('prezzo')); } public function test_si_possono_preservare_i_prezzi_personalizzati(): void { $finestra = $this->finestraConPrezziPersonalizzati(); $this->importaFinestra($finestra, false); // 3 misure dal file (una sostituita dal prezzo personalizzato) + 1 personalizzata fuori file. $this->assertSame(4, $finestra->listino()->count()); $this->assertEquals(367.2, $finestra->listino()->where('larghezza', 400)->where('altezza', 400)->value('prezzo')); $this->assertEquals(999, $finestra->listino()->where('larghezza', 500)->where('altezza', 400)->value('prezzo')); $this->assertEquals(888, $finestra->listino()->where('larghezza', 2000)->value('prezzo')); $this->assertSame(2, $finestra->listino()->where('personalizzato', true)->count()); } public function test_due_tabelle_non_possono_andare_sullo_stesso_modello(): void { $finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail(); Livewire::test(ListSistemi::class) ->callTableAction('importaListini', $this->pvc, data: [ 'file' => $this->fileExcel(), 'associazioni' => [ 'a' => ['codice' => 'finestra_1_anta', 'modello_id' => $finestra->id], 'b' => ['codice' => 'PORTA 1 ANTA, CON SERRATURA', 'modello_id' => $finestra->id], ], ]) ->assertHasTableActionErrors(); $this->assertSame(0, $finestra->listino()->count()); } public function test_il_listino_e_modificabile_dal_modello(): void { $finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail(); $prezzo = ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 400, 'altezza' => 400, 'prezzo' => 100]); Livewire::test(ListinoRelationManager::class, ['ownerRecord' => $finestra, 'pageClass' => EditModello::class]) ->assertCanSeeTableRecords([$prezzo]) ->callTableAction('create', data: ['larghezza' => 500, 'altezza' => 400, 'prezzo' => 120]) ->assertHasNoTableActionErrors() ->callTableAction('create', data: ['larghezza' => 400, 'altezza' => 400, 'prezzo' => 1]) ->assertHasTableActionErrors(['larghezza' => 'unique']); $this->assertSame(2, $finestra->listino()->count()); $this->assertTrue($finestra->listino()->where('larghezza', 500)->value('personalizzato')); $this->assertFalse($prezzo->fresh()->personalizzato); } public function test_api_listino_restituisce_la_griglia_ordinata(): void { $finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail(); ListinoImporter::importa($this->pvc, ListinoExcelParser::parseRighe($this->righe()), ['finestra_1_anta' => $finestra->id]); $this->conModelli3d(); $this->getJson('/api/sistemi/pvc/modelli/finestra_1_anta/listino') ->assertOk() ->assertExactJson([ ['altezza' => 400, 'larghezza' => 400, 'prezzo' => 367.2], ['altezza' => 400, 'larghezza' => 500, 'prezzo' => 381.24], ['altezza' => 500, 'larghezza' => 400, 'prezzo' => 381.24], ]); // Modello disattivo (senza asset 3D): non esposto. $this->getJson('/api/sistemi/pvc/modelli/scorrevole_hst_4_ante/listino')->assertNotFound(); } }