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
@@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* Scontistica sul preventivo: il cliente applica, prima dell'invio, un 1° sconto sull'imponibile e
* un 2° sconto extra sul totale già scontato, entro i massimi decisi dall'amministratore.
* Massimi null = regola base (config preventivi.sconti); sconti_abilitati = false li disattiva.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('clienti', function (Blueprint $table) {
$table->boolean('sconti_abilitati')->default(true)->after('password');
$table->decimal('sconto_max_1', 5, 2)->nullable()->after('sconti_abilitati');
$table->decimal('sconto_max_2', 5, 2)->nullable()->after('sconto_max_1');
});
Schema::table('preventivi', function (Blueprint $table) {
$table->decimal('subtotale', 10, 2)->default(0)->after('stato');
$table->decimal('sconto_1', 5, 2)->default(0)->after('subtotale');
$table->decimal('sconto_2', 5, 2)->default(0)->after('sconto_1');
});
// Preventivi esistenti: nessuno sconto, il subtotale coincide con il totale.
DB::table('preventivi')->update(['subtotale' => DB::raw('totale')]);
}
public function down(): void
{
Schema::table('clienti', function (Blueprint $table) {
$table->dropColumn(['sconti_abilitati', 'sconto_max_1', 'sconto_max_2']);
});
Schema::table('preventivi', function (Blueprint $table) {
$table->dropColumn(['subtotale', 'sconto_1', 'sconto_2']);
});
}
};
@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Contributo trasporto scelto al checkout (chiave di config preventivi.trasporto.zone): importo
* per pezzo x pezzi, aggiunto dopo gli sconti. Importo null = da quantificare (zona "Altro").
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('preventivi', function (Blueprint $table) {
$table->string('trasporto', 50)->nullable()->after('sconto_2');
$table->decimal('trasporto_importo', 10, 2)->nullable()->after('trasporto');
});
}
public function down(): void
{
Schema::table('preventivi', function (Blueprint $table) {
$table->dropColumn(['trasporto', 'trasporto_importo']);
});
}
};
@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Impostazioni globali modificabili dal backoffice (chiave => valore), es. la regola base di
* scontistica. Una chiave assente ricade sul valore in config/preventivi.php.
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('impostazioni', function (Blueprint $table) {
$table->id();
$table->string('chiave')->unique();
$table->json('valore')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('impostazioni');
}
};