Files
kreios/backoffice/app/Filament/Resources/PreventivoResource.php
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

205 lines
9.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\PreventivoResource\Pages;
use App\Filament\Resources\PreventivoResource\RelationManagers\ItemsRelationManager;
use App\Models\Cliente;
use App\Models\Preventivo;
use App\Support\PreventivoPdf;
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;
use Illuminate\Support\Facades\Storage;
class PreventivoResource extends Resource
{
protected static ?string $model = Preventivo::class;
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static ?string $navigationLabel = 'Preventivi';
protected static ?string $modelLabel = 'preventivo';
protected static ?string $pluralModelLabel = 'preventivi';
protected static ?int $navigationSort = 1;
public static function getNavigationBadge(): ?string
{
return (string) static::getModel()::where('stato', 'inviato')->count();
}
public static function form(Form $form): Form
{
return $form->schema([
Forms\Components\Select::make('stato')
->label('Stato')
->options(Preventivo::STATI)
->required(),
Forms\Components\Select::make('cliente_id')
->label('Cliente')
->relationship('cliente', 'nome')
->getOptionLabelFromRecordUsing(function (Cliente $record): string {
$nome = trim("{$record->nome} {$record->cognome}");
$parti = array_filter([$record->ragione_sociale, $nome]);
return $parti ? implode(' — ', $parti) : (string) $record->username;
})
->searchable(['ragione_sociale', 'nome', 'cognome'])
->preload(),
]);
}
public static function table(Table $table): Table
{
return $table
->defaultSort('created_at', 'desc')
->columns([
Tables\Columns\TextColumn::make('numero')
->label('N.')
->sortable()
->placeholder('—'),
Tables\Columns\TextColumn::make('cliente.ragione_sociale')
->label('Ragione sociale')
->searchable()
->toggleable()
->placeholder('—'),
Tables\Columns\TextColumn::make('cliente.nome_completo')
->label('Cliente')
->searchable(['nome', 'cognome'])
->placeholder('—'),
Tables\Columns\TextColumn::make('cliente.email')
->label('Email')
->searchable()
->toggleable()
->placeholder('—'),
Tables\Columns\TextColumn::make('items_count')
->label('Prodotti')
->counts('items'),
Tables\Columns\TextColumn::make('totale')
->label('Totale')
->money('EUR')
->sortable(),
Tables\Columns\TextColumn::make('stato')
->label('Stato')
->badge()
->formatStateUsing(fn (string $state) => Preventivo::STATI[$state] ?? $state)
->color(fn (string $state) => match ($state) {
'bozza' => 'gray',
'inviato' => 'warning',
'in_lavorazione' => 'info',
'chiuso' => 'success',
default => 'gray',
}),
Tables\Columns\TextColumn::make('created_at')
->label('Data')
->dateTime('d/m/Y H:i')
->sortable(),
])
->filters([
Tables\Filters\SelectFilter::make('stato')
->label('Stato')
->options(Preventivo::STATI),
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\Action::make('pdf')
->label('PDF')
->icon('heroicon-o-arrow-down-tray')
->visible(fn (Preventivo $record) => $record->stato !== 'bozza')
->action(function (Preventivo $record) {
if (! $record->pdf_path || ! Storage::disk('local')->exists($record->pdf_path)) {
$record->pdf_path = PreventivoPdf::genera($record);
$record->save();
}
return Storage::disk('local')->download(
$record->pdf_path,
'preventivo-'.$record->numero.'.pdf'
);
}),
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make()
->successRedirectUrl(static::getUrl('index')),
]),
]);
}
public static function infolist(Infolist $infolist): Infolist
{
return $infolist->schema([
Infolists\Components\Section::make('Preventivo')
->columns(3)
->schema([
Infolists\Components\TextEntry::make('numero')->label('Numero')->placeholder('—'),
Infolists\Components\TextEntry::make('stato')
->label('Stato')
->badge()
->formatStateUsing(fn (string $state) => Preventivo::STATI[$state] ?? $state),
Infolists\Components\TextEntry::make('subtotale')
->label('Totale parziale')
->money('EUR')
->visible(fn (Preventivo $record) => $record->haSconti()),
Infolists\Components\TextEntry::make('sconti')
->label('Sconti applicati')
->visible(fn (Preventivo $record) => $record->haSconti())
->state(function (Preventivo $record): string {
$importi = $record->importiSconto();
$euro = fn (float $v) => '€ '.number_format($v, 2, ',', '.');
return sprintf('1° sconto %s%% (−%s) · extra %s%% (−%s)',
(float) $record->sconto_1, $euro($importi['sconto_1']),
(float) $record->sconto_2, $euro($importi['sconto_2']));
}),
Infolists\Components\TextEntry::make('trasporto_etichetta')
->label('Trasporto')
->visible(fn (Preventivo $record) => $record->trasporto !== null)
->formatStateUsing(fn (string $state, Preventivo $record) => $record->trasportoDaQuantificare()
? "{$state} — da quantificare"
: $state.' — € '.number_format($record->trasporto_importo, 2, ',', '.')),
Infolists\Components\TextEntry::make('totale')->label('Totale (IVA esclusa)')->money('EUR'),
Infolists\Components\TextEntry::make('confirmed_at')->label('Confermato il')->dateTime('d/m/Y H:i')->placeholder('—'),
Infolists\Components\TextEntry::make('created_at')->label('Creato il')->dateTime('d/m/Y H:i'),
]),
Infolists\Components\Section::make('Cliente')
->columns(3)
->visible(fn (Preventivo $record) => $record->cliente !== null)
->schema([
Infolists\Components\TextEntry::make('cliente.nominativo')->label('Nominativo'),
Infolists\Components\TextEntry::make('cliente.email')->label('Email')->copyable(),
Infolists\Components\TextEntry::make('cliente.telefono')->label('Telefono')->placeholder('—'),
Infolists\Components\TextEntry::make('cliente.indirizzo')->label('Indirizzo')->placeholder('—'),
Infolists\Components\TextEntry::make('cliente.cap')->label('CAP')->placeholder('—'),
Infolists\Components\TextEntry::make('cliente.citta')->label('Città')->placeholder('—'),
Infolists\Components\TextEntry::make('cliente.provincia')->label('Provincia')->placeholder('—'),
Infolists\Components\TextEntry::make('cliente.note')->label('Note')->columnSpanFull()->placeholder('—'),
]),
]);
}
public static function getRelations(): array
{
return [
ItemsRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPreventivos::route('/'),
'view' => Pages\ViewPreventivo::route('/{record}'),
'edit' => Pages\EditPreventivo::route('/{record}/edit'),
];
}
}