feat: Implement price list management and import functionality
- Added ListinoRelationManager for managing price lists associated with models. - Introduced ImportaListiniAction for importing price lists from Excel files. - Created ListinoPrezzo model to represent price entries in the database. - Developed ListinoExcelParser for parsing Excel files containing price lists. - Implemented ListinoImporter to handle the import logic and associations. - Added migration for creating listino_prezzi table and converting measures to mm. - Updated frontend logic to handle new price list features and validations. - Enhanced tests for price list import and conversion functionalities.
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\ModelloResource\RelationManagers;
|
||||
|
||||
use App\Models\ListinoPrezzo;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Validation\Rules\Unique;
|
||||
|
||||
class ListinoRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'listino';
|
||||
|
||||
protected static ?string $title = 'Listino';
|
||||
|
||||
protected static ?string $modelLabel = 'prezzo';
|
||||
|
||||
protected static ?string $pluralModelLabel = 'prezzi';
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return $form->columns(3)->schema([
|
||||
Forms\Components\TextInput::make('larghezza')
|
||||
->label('Larghezza (mm)')
|
||||
->integer()
|
||||
->minValue(1)
|
||||
->required()
|
||||
->unique(
|
||||
ignoreRecord: true,
|
||||
modifyRuleUsing: fn (Unique $rule, Forms\Get $get) => $rule
|
||||
->where('modello_id', $this->getOwnerRecord()->getKey())
|
||||
->where('altezza', $get('altezza')),
|
||||
)
|
||||
->validationMessages(['unique' => 'Esiste già un prezzo per questa misura.']),
|
||||
Forms\Components\TextInput::make('altezza')
|
||||
->label('Altezza (mm)')
|
||||
->integer()
|
||||
->minValue(1)
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('prezzo')
|
||||
->label('Prezzo')
|
||||
->numeric()
|
||||
->minValue(0)
|
||||
->prefix('€')
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$misure = fn (string $colonna) => fn () => ListinoPrezzo::query()
|
||||
->where('modello_id', $this->getOwnerRecord()->getKey())
|
||||
->orderBy($colonna)
|
||||
->distinct()
|
||||
->pluck($colonna)
|
||||
->mapWithKeys(fn ($v) => [$v => $this->mm($v)])
|
||||
->all();
|
||||
|
||||
return $table
|
||||
->description(fn () => ($data = $this->getOwnerRecord()->listino_aggiornato_at)
|
||||
? 'Ultimo aggiornamento: '.$data->format('d/m/Y H:i').'. Il configuratore usa la prima misura del listino uguale o superiore a quella richiesta.'
|
||||
: 'Nessun listino caricato: importalo dalla lista Sistemi o aggiungi i prezzi a mano.')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query->orderBy('altezza')->orderBy('larghezza'))
|
||||
->defaultPaginationPageOption(25)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('altezza')
|
||||
->label('Altezza')
|
||||
->formatStateUsing(fn ($state) => $this->mm($state))
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('larghezza')
|
||||
->label('Larghezza')
|
||||
->formatStateUsing(fn ($state) => $this->mm($state))
|
||||
->sortable(),
|
||||
Tables\Columns\TextInputColumn::make('prezzo')
|
||||
->label('Prezzo (€)')
|
||||
->type('number')
|
||||
->step('0.01')
|
||||
->rules(['required', 'numeric', 'min:0'])
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('altezza')->label('Altezza')->options($misure('altezza')),
|
||||
Tables\Filters\SelectFilter::make('larghezza')->label('Larghezza')->options($misure('larghezza')),
|
||||
])
|
||||
->headerActions([
|
||||
Tables\Actions\CreateAction::make(),
|
||||
Tables\Actions\Action::make('svuota')
|
||||
->label('Svuota listino')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->modalDescription('Verranno eliminati tutti i prezzi del modello. Il configuratore non potrà calcolare il prezzo finché non sarà caricato un nuovo listino.')
|
||||
->visible(fn () => $this->getOwnerRecord()->listino()->exists())
|
||||
->action(function () {
|
||||
$modello = $this->getOwnerRecord();
|
||||
$modello->listino()->delete();
|
||||
$modello->update(['listino_aggiornato_at' => null]);
|
||||
|
||||
Notification::make()->title('Listino svuotato')->success()->send();
|
||||
}),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function isReadOnly(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function mm(mixed $valore): string
|
||||
{
|
||||
return number_format((int) $valore, 0, ',', '.').' mm';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user