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.
This commit is contained in:
2026-10-20 17:45:50 +02:00
parent 06617a985a
commit 8bef57b64b
27 changed files with 875 additions and 26 deletions
@@ -18,6 +18,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class CarrelloController extends Controller
{
@@ -142,6 +143,15 @@ class CarrelloController extends Controller
'note' => ['nullable', 'string', 'max:2000'],
]);
// Oltre il massimo di pezzi il trasporto non si sceglie: è da quantificare.
$trasporto = Preventivo::oltreMaxPezzi((int) $preventivo->items->sum('quantita'))
? Preventivo::TRASPORTO_OLTRE_MAX
: $request->validate([
'trasporto' => ['required', 'string', Rule::in(array_keys(config('preventivi.trasporto.zone')))],
], [
'trasporto.required' => 'Seleziona il trasporto.',
])['trasporto'];
// Se la richiesta arriva da un cliente loggato nel configuratore (token Sanctum
// valido), il preventivo va agganciato al suo record esistente — aggiornando
// l'anagrafica con i dati confermati nel form — e NON a un nuovo cliente.
@@ -149,7 +159,17 @@ class CarrelloController extends Controller
// per i guest (o token assenti/scaduti/non validi) restituisce null.
$clienteLoggato = Auth::guard('sanctum')->user();
DB::transaction(function () use ($preventivo, $dati, $clienteLoggato) {
// Sconti solo per i clienti loggati, entro i massimi decisi dall'amministratore.
$massimi = $clienteLoggato?->scontiMassimi() ?? ['max_1' => 0, 'max_2' => 0];
$sconti = $request->validate([
'sconto_1' => ['nullable', 'numeric', 'min:0', 'max:'.$massimi['max_1']],
'sconto_2' => ['nullable', 'numeric', 'min:0', 'max:'.$massimi['max_2']],
], [
'sconto_1.max' => 'Il 1° sconto non può superare il :max%.',
'sconto_2.max' => 'Lo sconto extra non può superare il :max%.',
]);
DB::transaction(function () use ($preventivo, $dati, $clienteLoggato, $sconti, $trasporto) {
$cliente = $clienteLoggato;
if ($cliente) {
@@ -162,7 +182,10 @@ class CarrelloController extends Controller
$preventivo->stato = 'inviato';
$preventivo->numero = Preventivo::prossimoNumero();
$preventivo->confirmed_at = now();
$preventivo->save();
$preventivo->sconto_1 = round((float) ($sconti['sconto_1'] ?? 0), 2);
$preventivo->sconto_2 = round((float) ($sconti['sconto_2'] ?? 0), 2);
$preventivo->trasporto = $trasporto;
$preventivo->ricalcolaTotale();
});
$preventivo->refresh()->load('cliente', 'items');