Files
kreios/backoffice/tests/Feature/ConfermaPreventivoTest.php
T
carmine.savino 8bef57b64b feat: implement discount and transport features in the quote system
- Added maximum discount settings configurable by the administrator.
- Implemented discount calculations in the quote confirmation process.
- Introduced transport options with associated costs, including handling for exceeding maximum piece limits.
- Updated frontend to display discounts and transport information in the quote summary.
- Created new database migrations for storing discount and transport settings.
- Developed a new Filament page for managing global discount settings.
- Enhanced email templates to include discount and transport details.
- Added tests to ensure proper functionality of discounts and transport calculations.
2026-10-20 17:45:50 +02:00

314 lines
12 KiB
PHP

<?php
namespace Tests\Feature;
use App\Mail\PreventivoCliente;
use App\Models\Cliente;
use App\Models\Impostazione;
use App\Models\Preventivo;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Mockery;
use Tests\TestCase;
class ConfermaPreventivoTest extends TestCase
{
use RefreshDatabase;
/** Crea un preventivo in bozza con una riga, pronto per la conferma. */
private function creaCarrelloBozza(): Preventivo
{
$preventivo = Preventivo::create([
'token' => '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);
}
}