'token-test-123', 'stato' => 'bozza', 'totale' => 0, ]); $preventivo->items()->create([ 'sistema' => 'pvc', 'type' => 'finestra_1_anta', 'nome_modello' => 'Finestra 1 anta', 'larghezza' => 100, 'altezza' => 100, 'quantita' => 1, 'prezzo_unitario' => 500, 'prezzo_totale' => 500, 'payload' => ['nome' => 'Finestra 1 anta'], ]); $preventivo->ricalcolaTotale(); return $preventivo; } private function datiConferma(): array { return [ 'nome' => 'Mario', 'cognome' => 'Rossi', 'ragione_sociale' => null, 'email' => 'mario.rossi@example.com', 'telefono' => '3331234567', 'indirizzo' => 'Via Roma 1', 'cap' => '20100', 'citta' => 'Milano', 'provincia' => 'MI', 'note' => null, 'trasporto' => 'campania_entro_150km', ]; } public function test_conferma_da_cliente_loggato_non_crea_un_nuovo_cliente(): void { Mockery::mock('alias:App\Support\PreventivoPdf') ->shouldReceive('genera') ->andReturn('preventivi/preventivo-1.pdf'); Mail::fake(); $cliente = Cliente::create([ 'username' => 'user1234', 'password' => 'Segreta123!', 'nome' => 'Mario', 'cognome' => 'Bianchi', 'email' => 'mario.bianchi@example.com', 'telefono' => '3330000000', ]); $token = $cliente->createToken('configuratore', ['*'])->plainTextToken; $preventivo = $this->creaCarrelloBozza(); $dati = $this->datiConferma(); $dati['cognome'] = 'Rossi'; // diverso dal dato salvato: l'anagrafica deve aggiornarsi $response = $this->postJson("/api/carrello/{$preventivo->token}/conferma", $dati, [ 'Authorization' => "Bearer {$token}", ]); $response->assertOk(); // Nessun nuovo utente creato: in database c'è ancora solo il cliente originale. $this->assertSame(1, Cliente::count()); $preventivo->refresh(); $this->assertSame($cliente->id, $preventivo->cliente_id); $this->assertSame('inviato', $preventivo->stato); $this->assertNotNull($preventivo->numero); // L'anagrafica del cliente loggato è aggiornata con i dati confermati nel form. $this->assertSame('Rossi', $cliente->fresh()->cognome); $this->assertSame('3331234567', $cliente->fresh()->telefono); Mail::assertSent(PreventivoCliente::class); } public function test_conferma_da_guest_crea_il_cliente(): void { Mockery::mock('alias:App\Support\PreventivoPdf') ->shouldReceive('genera') ->andReturn('preventivi/preventivo-1.pdf'); Mail::fake(); $this->assertSame(0, Cliente::count()); $preventivo = $this->creaCarrelloBozza(); $response = $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma()); $response->assertOk(); $this->assertSame(1, Cliente::count()); $preventivo->refresh(); $this->assertNotNull($preventivo->cliente_id); $this->assertSame('mario.rossi@example.com', $preventivo->cliente->email); } /** Carrello da 1000 € e cliente loggato; ritorna [preventivo, header Authorization]. */ private function carrelloDaMilleConCliente(array $attributiCliente = []): array { Mockery::mock('alias:App\Support\PreventivoPdf') ->shouldReceive('genera') ->andReturn('preventivi/preventivo-1.pdf'); Mail::fake(); $cliente = Cliente::create($attributiCliente + ['username' => 'user1234', 'password' => 'Segreta123!']); $token = $cliente->createToken('configuratore', ['*'])->plainTextToken; $preventivo = $this->creaCarrelloBozza(); $preventivo->items()->first()->update(['prezzo_unitario' => 1000, 'prezzo_totale' => 1000]); $preventivo->ricalcolaTotale(); return [$preventivo, ['Authorization' => "Bearer {$token}"]]; } public function test_regola_base_sconti_a_cascata(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); $this->getJson('/api/auth/me', $auth) ->assertJsonPath('sconti.max_1', 50) ->assertJsonPath('sconti.max_2', 10); // Schema: 1000 -50% = 500, -10% extra sul già scontato = 450. $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 50, 'sconto_2' => 10], $auth) ->assertOk(); $preventivo->refresh(); $this->assertEquals(1000, $preventivo->subtotale); $this->assertEquals(450 + 100, $preventivo->totale); $this->assertSame(['sconto_1' => 500.0, 'sconto_2' => 50.0, 'imponibile' => 450.0], $preventivo->importiSconto()); } public function test_sconto_oltre_il_massimo_e_rifiutato(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 60], $auth) ->assertStatus(422) ->assertJsonValidationErrors('sconto_1'); $this->assertSame('bozza', $preventivo->fresh()->stato); } public function test_massimi_personalizzati_per_cliente(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(['sconto_max_1' => 30, 'sconto_max_2' => 0]); $this->getJson('/api/auth/me', $auth) ->assertJsonPath('sconti.max_1', 30) ->assertJsonPath('sconti.max_2', 0); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 30, 'sconto_2' => 5], $auth) ->assertStatus(422) ->assertJsonValidationErrors('sconto_2'); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 30], $auth) ->assertOk(); $this->assertEquals(700 + 100, $preventivo->fresh()->totale); } public function test_regola_base_modificata_dal_backoffice(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); Impostazione::imposta('sconti.max_1', 20); Impostazione::imposta('sconti.max_2', 5); $this->getJson('/api/auth/me', $auth) ->assertJsonPath('sconti.max_1', 20) ->assertJsonPath('sconti.max_2', 5); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 25], $auth) ->assertStatus(422) ->assertJsonValidationErrors('sconto_1'); } public function test_sconti_disabilitati_dall_amministratore(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(['sconti_abilitati' => false]); $this->getJson('/api/auth/me', $auth)->assertJsonPath('sconti.max_1', 0); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 10], $auth) ->assertStatus(422) ->assertJsonValidationErrors('sconto_1'); } public function test_guest_non_puo_applicare_sconti(): void { Mockery::mock('alias:App\Support\PreventivoPdf') ->shouldReceive('genera') ->andReturn('preventivi/preventivo-1.pdf'); Mail::fake(); $preventivo = $this->creaCarrelloBozza(); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma() + ['sconto_1' => 10]) ->assertStatus(422) ->assertJsonValidationErrors('sconto_1'); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma())->assertOk(); $this->assertEquals(500 + 100, $preventivo->fresh()->totale); // + trasporto entro 150 km, 1 pz } public function test_trasporto_cadauno_dopo_gli_sconti(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); // 10 finestre: il contributo si paga 10 volte e non entra negli sconti. $preventivo->items()->first()->update(['quantita' => 10]); $dati = ['trasporto' => 'campania_oltre_150km', 'sconto_1' => 50, 'sconto_2' => 10] + $this->datiConferma(); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $dati, $auth)->assertOk(); $preventivo->refresh(); $this->assertEquals(1500, $preventivo->trasporto_importo); $this->assertEquals(450 + 1500, $preventivo->totale); $this->assertFalse($preventivo->trasportoDaQuantificare()); } public function test_oltre_20_pezzi_trasporto_da_quantificare_senza_scelta(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); $preventivo->items()->first()->update(['quantita' => 21]); // Nessuna scelta richiesta (e quella inviata è ignorata): il trasporto è da quantificare. $dati = $this->datiConferma(); unset($dati['trasporto']); $this->postJson("/api/carrello/{$preventivo->token}/conferma", $dati, $auth)->assertOk(); $preventivo->refresh()->load('items', 'cliente'); $this->assertSame(Preventivo::TRASPORTO_OLTRE_MAX, $preventivo->trasporto); $this->assertNull($preventivo->trasporto_importo); $this->assertEquals(1000, $preventivo->totale); $html = view('pdf.preventivo', ['preventivo' => $preventivo])->render(); $this->assertStringContainsString('Contributo per il trasporto da quantificare', $html); $this->assertStringContainsString('supera i 20 pezzi', $html); } public function test_trasporto_altro_da_quantificare(): void { [$preventivo, $auth] = $this->carrelloDaMilleConCliente(); $this->postJson("/api/carrello/{$preventivo->token}/conferma", ['trasporto' => 'altro'] + $this->datiConferma(), $auth)->assertOk(); $preventivo->refresh(); $this->assertNull($preventivo->trasporto_importo); $this->assertTrue($preventivo->trasportoDaQuantificare()); $this->assertEquals(1000, $preventivo->totale); $html = view('pdf.preventivo', ['preventivo' => $preventivo->load('items', 'cliente')])->render(); $this->assertStringContainsString(config('preventivi.trasporto.nota_da_quantificare'), $html); } public function test_trasporto_obbligatorio_e_valido(): void { $preventivo = $this->creaCarrelloBozza(); $this->postJson("/api/carrello/{$preventivo->token}/conferma", ['trasporto' => null] + $this->datiConferma()) ->assertStatus(422)->assertJsonValidationErrors('trasporto'); $this->postJson("/api/carrello/{$preventivo->token}/conferma", ['trasporto' => 'luna'] + $this->datiConferma()) ->assertStatus(422)->assertJsonValidationErrors('trasporto'); $this->getJson('/api/config')->assertJsonPath('trasporto.0.valore', 'campania_entro_150km') ->assertJsonPath('trasporto.2.importo', null); } public function test_conferma_con_token_non_valido_tratta_l_utente_come_guest(): void { Mockery::mock('alias:App\Support\PreventivoPdf') ->shouldReceive('genera') ->andReturn('preventivi/preventivo-1.pdf'); Mail::fake(); $preventivo = $this->creaCarrelloBozza(); $response = $this->postJson("/api/carrello/{$preventivo->token}/conferma", $this->datiConferma(), [ 'Authorization' => 'Bearer token-non-esistente', ]); $response->assertOk(); $this->assertSame(1, Cliente::count()); $this->assertNotNull($preventivo->fresh()->cliente_id); } }