- 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.
203 lines
11 KiB
PHP
203 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources;
|
|
|
|
use App\Filament\Resources\ClienteResource\Pages;
|
|
use App\Models\Cliente;
|
|
use App\Models\Impostazione;
|
|
use App\Support\ClienteCredenziali;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Infolists;
|
|
use Filament\Infolists\Infolist;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
|
|
class ClienteResource extends Resource
|
|
{
|
|
protected static ?string $model = Cliente::class;
|
|
|
|
protected static ?string $navigationIcon = 'heroicon-o-users';
|
|
|
|
protected static ?string $navigationLabel = 'Clienti';
|
|
|
|
protected static ?string $modelLabel = 'cliente';
|
|
|
|
protected static ?string $pluralModelLabel = 'clienti';
|
|
|
|
protected static ?int $navigationSort = 2;
|
|
|
|
public static function form(Form $form): Form
|
|
{
|
|
return $form->schema([
|
|
Forms\Components\Section::make('Accesso')
|
|
->description('Credenziali per l\'accesso del cliente. Generate automaticamente, ma modificabili.')
|
|
->columns(2)
|
|
->schema([
|
|
Forms\Components\TextInput::make('username')
|
|
->label('Nome utente')
|
|
->required()
|
|
->maxLength(50)
|
|
->unique(ignoreRecord: true)
|
|
->default(fn (string $operation) => $operation === 'create' ? ClienteCredenziali::generaUsername() : null)
|
|
->suffixAction(
|
|
Forms\Components\Actions\Action::make('rigeneraUsername')
|
|
->icon('heroicon-m-arrow-path')
|
|
->tooltip('Genera un nuovo nome utente')
|
|
->action(fn (Forms\Set $set) => $set('username', ClienteCredenziali::generaUsername()))
|
|
),
|
|
Forms\Components\TextInput::make('password')
|
|
->label('Password')
|
|
->password()
|
|
->revealable()
|
|
->required(fn (string $operation) => $operation === 'create')
|
|
->dehydrated(fn (?string $state) => filled($state))
|
|
->default(fn (string $operation) => $operation === 'create' ? ClienteCredenziali::generaPassword() : null)
|
|
->suffixAction(
|
|
Forms\Components\Actions\Action::make('rigeneraPassword')
|
|
->icon('heroicon-m-arrow-path')
|
|
->tooltip('Genera una nuova password')
|
|
->action(fn (Forms\Set $set) => $set('password', ClienteCredenziali::generaPassword()))
|
|
)
|
|
->helperText(fn (string $operation) => $operation === 'create'
|
|
? 'Generata automaticamente. Ricordati di copiarla dopo il salvataggio: non sarà più leggibile.'
|
|
: 'Lascia vuoto per mantenere la password attuale, oppure genera/inserisci una nuova password.'),
|
|
]),
|
|
Forms\Components\Section::make('Scontistica')
|
|
->description(fn () => sprintf(
|
|
'Sconti che il cliente può applicare al preventivo prima dell\'invio. Lascia vuoto per la regola base (1° sconto fino al %s%%, sconto extra fino al %s%% sul totale già scontato).',
|
|
static::percentuale(Impostazione::scontiBase()['max_1']),
|
|
static::percentuale(Impostazione::scontiBase()['max_2']),
|
|
))
|
|
->columns(2)
|
|
->schema([
|
|
Forms\Components\Toggle::make('sconti_abilitati')
|
|
->label('Può applicare sconti')
|
|
->default(true)
|
|
->live()
|
|
->columnSpanFull(),
|
|
Forms\Components\TextInput::make('sconto_max_1')
|
|
->label('1° sconto massimo')
|
|
->numeric()
|
|
->minValue(0)
|
|
->maxValue(100)
|
|
->suffix('%')
|
|
->placeholder(fn () => static::percentuale(Impostazione::scontiBase()['max_1']).' (regola base)')
|
|
->visible(fn (Forms\Get $get) => (bool) $get('sconti_abilitati')),
|
|
Forms\Components\TextInput::make('sconto_max_2')
|
|
->label('Sconto extra massimo')
|
|
->numeric()
|
|
->minValue(0)
|
|
->maxValue(100)
|
|
->suffix('%')
|
|
->placeholder(fn () => static::percentuale(Impostazione::scontiBase()['max_2']).' (regola base)')
|
|
->visible(fn (Forms\Get $get) => (bool) $get('sconti_abilitati')),
|
|
]),
|
|
Forms\Components\Section::make('Anagrafica')
|
|
->description('Campi facoltativi.')
|
|
->columns(2)
|
|
->schema([
|
|
Forms\Components\TextInput::make('nome')->label('Nome'),
|
|
Forms\Components\TextInput::make('cognome')->label('Cognome'),
|
|
Forms\Components\TextInput::make('ragione_sociale')->label('Ragione sociale'),
|
|
Forms\Components\TextInput::make('email')->label('Email')->email(),
|
|
Forms\Components\TextInput::make('telefono')->label('Telefono')->tel(),
|
|
Forms\Components\TextInput::make('indirizzo')->label('Indirizzo'),
|
|
Forms\Components\TextInput::make('cap')->label('CAP')->maxLength(10),
|
|
Forms\Components\TextInput::make('citta')->label('Città'),
|
|
Forms\Components\TextInput::make('provincia')->label('Provincia')->maxLength(5),
|
|
Forms\Components\Textarea::make('note')->label('Note')->columnSpanFull(),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->defaultSort('created_at', 'desc')
|
|
->columns([
|
|
Tables\Columns\TextColumn::make('nominativo')->label('Nominativo')->searchable(['nome', 'cognome', 'ragione_sociale']),
|
|
Tables\Columns\TextColumn::make('username')->label('Nome utente')->searchable()->placeholder('—'),
|
|
Tables\Columns\TextColumn::make('email')->label('Email')->searchable()->copyable()->placeholder('—'),
|
|
Tables\Columns\TextColumn::make('telefono')->label('Telefono')->placeholder('—'),
|
|
Tables\Columns\TextColumn::make('citta')->label('Città')->placeholder('—'),
|
|
Tables\Columns\TextColumn::make('preventivi_count')->label('Preventivi')->counts('preventivi'),
|
|
Tables\Columns\TextColumn::make('created_at')->label('Registrato')->dateTime('d/m/Y')->sortable(),
|
|
])
|
|
->actions([
|
|
Tables\Actions\ViewAction::make(),
|
|
Tables\Actions\EditAction::make(),
|
|
Tables\Actions\DeleteAction::make()
|
|
->modalDescription('Verrà eliminato definitivamente il cliente insieme a tutti i suoi preventivi e ai file associati (PDF, immagini)')
|
|
->successRedirectUrl(static::getUrl('index')),
|
|
])
|
|
->bulkActions([
|
|
Tables\Actions\BulkActionGroup::make([
|
|
Tables\Actions\DeleteBulkAction::make()
|
|
->modalDescription('I clienti selezionati verranno eliminati definitivamente insieme a tutti i loro preventivi e ai file associati (PDF, immagini)')
|
|
->successRedirectUrl(static::getUrl('index')),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function infolist(Infolist $infolist): Infolist
|
|
{
|
|
return $infolist->schema([
|
|
Infolists\Components\View::make('filament.infolists.cliente-password-reveal')
|
|
->visible(fn () => filled(session('cliente_password_plain')))
|
|
->viewData(fn () => [
|
|
'username' => session('cliente_username_plain'),
|
|
'password' => session('cliente_password_plain'),
|
|
]),
|
|
Infolists\Components\Section::make('Accesso')
|
|
->columns(2)
|
|
->schema([
|
|
Infolists\Components\TextEntry::make('username')->label('Nome utente')->copyable()->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('created_at')->label('Cliente dal')->dateTime('d/m/Y H:i'),
|
|
Infolists\Components\TextEntry::make('scontistica')
|
|
->label('Scontistica')
|
|
->state(function (Cliente $record): string {
|
|
if (! $record->sconti_abilitati) {
|
|
return 'Disabilitata';
|
|
}
|
|
$massimi = $record->scontiMassimi();
|
|
$base = $record->sconto_max_1 === null && $record->sconto_max_2 === null ? ' (regola base)' : '';
|
|
|
|
return sprintf('Fino al %s%% + extra fino al %s%%%s',
|
|
static::percentuale($massimi['max_1']), static::percentuale($massimi['max_2']), $base);
|
|
})
|
|
->columnSpanFull(),
|
|
]),
|
|
Infolists\Components\Section::make('Anagrafica')
|
|
->columns(3)
|
|
->schema([
|
|
Infolists\Components\TextEntry::make('nominativo')->label('Nominativo')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('email')->label('Email')->copyable()->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('telefono')->label('Telefono')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('indirizzo')->label('Indirizzo')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('cap')->label('CAP')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('citta')->label('Città')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('provincia')->label('Provincia')->placeholder('—'),
|
|
Infolists\Components\TextEntry::make('note')->label('Note')->columnSpanFull()->placeholder('—'),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
/** 50.00 -> "50", 12.50 -> "12,5" */
|
|
private static function percentuale(mixed $valore): string
|
|
{
|
|
return rtrim(rtrim(number_format((float) $valore, 2, ',', ''), '0'), ',');
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\ListClientes::route('/'),
|
|
'create' => Pages\CreateCliente::route('/create'),
|
|
'view' => Pages\ViewCliente::route('/{record}'),
|
|
'edit' => Pages\EditCliente::route('/{record}/edit'),
|
|
];
|
|
}
|
|
}
|