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:
@@ -60,8 +60,8 @@ Entry point is `src/js/starter.js`, loaded from `src/index.html`. Flow on load (
|
|||||||
`flipView` (180° GSAP-animated turn between front/back view), `addMarker` (in-scene hotspot with a video
|
`flipView` (180° GSAP-animated turn between front/back view), `addMarker` (in-scene hotspot with a video
|
||||||
popup).
|
popup).
|
||||||
6. **`PriceCalculator`** (`src/js/price_calculator.js`) is a stateless-ish module (single module-level
|
6. **`PriceCalculator`** (`src/js/price_calculator.js`) is a stateless-ish module (single module-level
|
||||||
`listini` cache set via `init`) that looks up a base price from a width/height grid (`listino.json`,
|
`listini` cache set via `init`) that looks up a base price from a width/height grid in **mm** (served by
|
||||||
rounded up to the nearest 10cm) and layers on percentage/flat supplements for finish, glass, hinges, handle
|
the backoffice API, using the first grid size ≥ the requested one) and layers on percentage/flat supplements for finish, glass, hinges, handle
|
||||||
height, and actuator type, then adds 22% VAT.
|
height, and actuator type, then adds 22% VAT.
|
||||||
|
|
||||||
### Data conventions
|
### Data conventions
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Resources;
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
use App\Filament\Resources\ModelloResource\Pages;
|
use App\Filament\Resources\ModelloResource\Pages;
|
||||||
|
use App\Filament\Resources\ModelloResource\RelationManagers\ListinoRelationManager;
|
||||||
use App\Models\Modello;
|
use App\Models\Modello;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
@@ -126,6 +127,13 @@ class ModelloResource extends Resource
|
|||||||
->badge()
|
->badge()
|
||||||
->formatStateUsing(fn (?string $state) => Modello::CATEGORIE[$state] ?? $state)
|
->formatStateUsing(fn (?string $state) => Modello::CATEGORIE[$state] ?? $state)
|
||||||
->placeholder('—'),
|
->placeholder('—'),
|
||||||
|
Tables\Columns\TextColumn::make('listino_count')
|
||||||
|
->label('Listino')
|
||||||
|
->counts('listino')
|
||||||
|
->formatStateUsing(fn (int $state) => $state ? "{$state} prezzi" : 'Nessuno')
|
||||||
|
->color(fn (int $state) => $state ? null : 'gray')
|
||||||
|
->description(fn (Modello $record) => $record->listino_aggiornato_at?->format('d/m/Y'))
|
||||||
|
->sortable(),
|
||||||
Tables\Columns\ToggleColumn::make('attivo')
|
Tables\Columns\ToggleColumn::make('attivo')
|
||||||
->label('Attivo')
|
->label('Attivo')
|
||||||
->disabled(fn (Modello $record) => ! $record->attivo && (! $record->cartella || ! $record->file_modello)),
|
->disabled(fn (Modello $record) => ! $record->attivo && (! $record->cartella || ! $record->file_modello)),
|
||||||
@@ -146,6 +154,13 @@ class ModelloResource extends Resource
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ListinoRelationManager::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
+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';
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -32,7 +32,7 @@ class ItemsRelationManager extends RelationManager
|
|||||||
Tables\Columns\TextColumn::make('dimensioni')
|
Tables\Columns\TextColumn::make('dimensioni')
|
||||||
->label('Dimensioni')
|
->label('Dimensioni')
|
||||||
->state(fn (Model $record) => $record->larghezza && $record->altezza
|
->state(fn (Model $record) => $record->larghezza && $record->altezza
|
||||||
? "{$record->larghezza} × {$record->altezza} cm" : '—'),
|
? "{$record->larghezza} × {$record->altezza} mm" : '—'),
|
||||||
Tables\Columns\TextColumn::make('quantita')->label('Q.tà'),
|
Tables\Columns\TextColumn::make('quantita')->label('Q.tà'),
|
||||||
Tables\Columns\TextColumn::make('prezzo_unitario')->label('Prezzo un.')->money('EUR'),
|
Tables\Columns\TextColumn::make('prezzo_unitario')->label('Prezzo un.')->money('EUR'),
|
||||||
Tables\Columns\TextColumn::make('prezzo_totale')->label('Totale')->money('EUR'),
|
Tables\Columns\TextColumn::make('prezzo_totale')->label('Totale')->money('EUR'),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources;
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
|
use App\Filament\Resources\SistemaResource\Actions\ImportaListiniAction;
|
||||||
use App\Filament\Resources\SistemaResource\Pages;
|
use App\Filament\Resources\SistemaResource\Pages;
|
||||||
use App\Models\Sistema;
|
use App\Models\Sistema;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
@@ -77,6 +78,7 @@ class SistemaResource extends Resource
|
|||||||
->url(fn (Sistema $record) => ModelloResource::getUrl('index', [
|
->url(fn (Sistema $record) => ModelloResource::getUrl('index', [
|
||||||
'tableFilters' => ['sistema' => ['value' => $record->getKey()]],
|
'tableFilters' => ['sistema' => ['value' => $record->getKey()]],
|
||||||
])),
|
])),
|
||||||
|
ImportaListiniAction::make(),
|
||||||
Tables\Actions\EditAction::make(),
|
Tables\Actions\EditAction::make(),
|
||||||
Tables\Actions\DeleteAction::make()
|
Tables\Actions\DeleteAction::make()
|
||||||
->modalDescription('Verrà eliminato definitivamente il sistema insieme a tutti i suoi modelli.'),
|
->modalDescription('Verrà eliminato definitivamente il sistema insieme a tutti i suoi modelli.'),
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\SistemaResource\Actions;
|
||||||
|
|
||||||
|
use App\Models\Sistema;
|
||||||
|
use App\Support\Listini\ListinoExcelParser;
|
||||||
|
use App\Support\Listini\ListinoImporter;
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Get;
|
||||||
|
use Filament\Forms\Set;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Support\Exceptions\Halt;
|
||||||
|
use Filament\Tables\Actions\Action;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Importa i listini di un sistema da Excel in due passi: caricamento del file e
|
||||||
|
* revisione delle associazioni tabella del file → modello prima di salvare.
|
||||||
|
*/
|
||||||
|
class ImportaListiniAction
|
||||||
|
{
|
||||||
|
public static function make(): Action
|
||||||
|
{
|
||||||
|
return Action::make('importaListini')
|
||||||
|
->label('Importa listini')
|
||||||
|
->icon('heroicon-o-arrow-up-tray')
|
||||||
|
->color('gray')
|
||||||
|
->modalHeading(fn (Sistema $record) => "Importa listini {$record->nome}")
|
||||||
|
->modalWidth('3xl')
|
||||||
|
->modalSubmitActionLabel('Importa listini')
|
||||||
|
->steps(fn (Sistema $record) => [
|
||||||
|
Forms\Components\Wizard\Step::make('File')
|
||||||
|
->description('Carica il file Excel')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\FileUpload::make('file')
|
||||||
|
->label('File listini')
|
||||||
|
->helperText('File .xls o .xlsx: ogni tabella ha nella prima colonna il codice dell\'infisso, nella prima riga le larghezze e nella prima colonna delle righe successive le altezze (in mm).')
|
||||||
|
->required()
|
||||||
|
->storeFiles(false)
|
||||||
|
->maxSize(10240),
|
||||||
|
])
|
||||||
|
->afterValidation(function (Get $get, Set $set) use ($record) {
|
||||||
|
$blocchi = self::leggi($get('file'));
|
||||||
|
|
||||||
|
$associazioni = ListinoImporter::suggerisciAssociazioni($record, array_column($blocchi, 'codice'));
|
||||||
|
|
||||||
|
$set('associazioni', collect($blocchi)->mapWithKeys(fn (array $b) => [(string) Str::uuid() => [
|
||||||
|
'codice' => $b['codice'],
|
||||||
|
'prezzi' => count($b['prezzi']),
|
||||||
|
'modello_id' => $associazioni[$b['codice']],
|
||||||
|
]])->all());
|
||||||
|
}),
|
||||||
|
Forms\Components\Wizard\Step::make('Associazioni')
|
||||||
|
->description('Verifica i modelli associati')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Placeholder::make('istruzioni')
|
||||||
|
->hiddenLabel()
|
||||||
|
->content('Per ogni tabella trovata nel file scegli il modello a cui assegnare il listino. Le tabelle senza modello non vengono importate; il listino dei modelli associati viene sostituito.'),
|
||||||
|
// Intestazione unica: le righe sotto sono senza etichette, in forma di tabella.
|
||||||
|
Forms\Components\Grid::make(5)
|
||||||
|
->extraAttributes(['class' => 'px-4 text-sm font-medium text-gray-950 dark:text-white'])
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Placeholder::make('h_codice')->hiddenLabel()->content('Codice nel file')->columnSpan(2),
|
||||||
|
Forms\Components\Placeholder::make('h_prezzi')->hiddenLabel()->content('Prezzi')->columnSpan(1),
|
||||||
|
Forms\Components\Placeholder::make('h_modello')->hiddenLabel()->content('Modello')->columnSpan(2),
|
||||||
|
]),
|
||||||
|
Forms\Components\Repeater::make('associazioni')
|
||||||
|
->hiddenLabel()
|
||||||
|
->addable(false)
|
||||||
|
->deletable(false)
|
||||||
|
->reorderable(false)
|
||||||
|
->columns(5)
|
||||||
|
->extraAttributes(['class' => '-mt-4'])
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Hidden::make('codice'),
|
||||||
|
Forms\Components\Placeholder::make('codice_visibile')
|
||||||
|
->hiddenLabel()
|
||||||
|
->content(fn (Get $get) => $get('codice'))
|
||||||
|
->extraAttributes(['class' => 'font-mono break-all'])
|
||||||
|
->columnSpan(2),
|
||||||
|
Forms\Components\Placeholder::make('prezzi')
|
||||||
|
->hiddenLabel()
|
||||||
|
->content(fn (Get $get) => $get('prezzi'))
|
||||||
|
->columnSpan(1),
|
||||||
|
Forms\Components\Select::make('modello_id')
|
||||||
|
->hiddenLabel()
|
||||||
|
->placeholder('— Non importare —')
|
||||||
|
->options(fn () => $record->modelli()->orderBy('ordine')->pluck('nome', 'id'))
|
||||||
|
->searchable()
|
||||||
|
->distinct()
|
||||||
|
->columnSpan(2),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->action(function (array $data, Sistema $record) {
|
||||||
|
$blocchi = self::leggi($data['file']);
|
||||||
|
$associazioni = collect($data['associazioni'] ?? [])
|
||||||
|
->mapWithKeys(fn (array $a) => [$a['codice'] => $a['modello_id'] ?? null])
|
||||||
|
->all();
|
||||||
|
|
||||||
|
$aggiornati = ListinoImporter::importa($record, $blocchi, $associazioni);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Listini importati')
|
||||||
|
->body("Aggiornati i listini di {$aggiornati} modelli.")
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<array{codice: string, prezzi: list<array>}>
|
||||||
|
*/
|
||||||
|
private static function leggi(mixed $file): array
|
||||||
|
{
|
||||||
|
if (is_array($file)) {
|
||||||
|
$file = reset($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$blocchi = $file instanceof TemporaryUploadedFile ? ListinoExcelParser::parse($file->getRealPath()) : [];
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
$blocchi = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $blocchi) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Nessun listino trovato')
|
||||||
|
->body('Il file non è un Excel valido o non contiene tabelle nel formato atteso.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
throw new Halt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $blocchi;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,26 @@ class CatalogoController extends Controller
|
|||||||
return response()->json($this->sistema($sistema));
|
return response()->json($this->sistema($sistema));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/sistemi/{codice}/modelli/{modello}/listino
|
||||||
|
* Griglia prezzi del modello (misure in mm), ordinata per altezza e larghezza.
|
||||||
|
*/
|
||||||
|
public function listino(string $codice, string $modello): JsonResponse
|
||||||
|
{
|
||||||
|
$modello = Modello::attivi()
|
||||||
|
->where('codice', $modello)
|
||||||
|
->whereHas('sistema', fn ($q) => $q->where('codice', $codice)->where('attivo', true))
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$prezzi = $modello->listino()
|
||||||
|
->orderBy('altezza')
|
||||||
|
->orderBy('larghezza')
|
||||||
|
->get(['altezza', 'larghezza', 'prezzo'])
|
||||||
|
->map(fn ($p) => ['altezza' => $p->altezza, 'larghezza' => $p->larghezza, 'prezzo' => $p->prezzo]);
|
||||||
|
|
||||||
|
return response()->json($prezzi);
|
||||||
|
}
|
||||||
|
|
||||||
private function sistema(Sistema $sistema): array
|
private function sistema(Sistema $sistema): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class ListinoPrezzo extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'listino_prezzi';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'modello_id', 'larghezza', 'altezza', 'prezzo',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'larghezza' => 'integer',
|
||||||
|
'altezza' => 'integer',
|
||||||
|
'prezzo' => 'float',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function modello(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Modello::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function booted(): void
|
||||||
|
{
|
||||||
|
// Le modifiche manuali dal pannello aggiornano la data del listino del modello.
|
||||||
|
$tocca = fn (ListinoPrezzo $prezzo) => $prezzo->modello?->update(['listino_aggiornato_at' => now()]);
|
||||||
|
|
||||||
|
static::saved($tocca);
|
||||||
|
static::deleted($tocca);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class Modello extends Model
|
class Modello extends Model
|
||||||
@@ -36,12 +37,14 @@ class Modello extends Model
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'sistema_id', 'codice', 'nome', 'descrizione', 'icona', 'ordine', 'attivo',
|
'sistema_id', 'codice', 'nome', 'descrizione', 'icona', 'ordine', 'attivo',
|
||||||
'cartella', 'file_modello', 'categoria', 'cartella_texture', 'moduli',
|
'cartella', 'file_modello', 'categoria', 'cartella_texture', 'moduli',
|
||||||
|
'listino_aggiornato_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'ordine' => 'integer',
|
'ordine' => 'integer',
|
||||||
'attivo' => 'boolean',
|
'attivo' => 'boolean',
|
||||||
'moduli' => 'array',
|
'moduli' => 'array',
|
||||||
|
'listino_aggiornato_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function sistema(): BelongsTo
|
public function sistema(): BelongsTo
|
||||||
@@ -49,6 +52,11 @@ class Modello extends Model
|
|||||||
return $this->belongsTo(Sistema::class);
|
return $this->belongsTo(Sistema::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function listino(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(ListinoPrezzo::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeAttivi(Builder $query): Builder
|
public function scopeAttivi(Builder $query): Builder
|
||||||
{
|
{
|
||||||
return $query->where('attivo', true)->orderBy('ordine')->orderBy('id');
|
return $query->where('attivo', true)->orderBy('ordine')->orderBy('id');
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class ConfigurazioneFormatter
|
|||||||
{
|
{
|
||||||
/** Etichette italiane per le chiavi note. */
|
/** Etichette italiane per le chiavi note. */
|
||||||
private const LABELS = [
|
private const LABELS = [
|
||||||
'width' => 'Larghezza (cm)',
|
'width' => 'Larghezza (mm)',
|
||||||
'height' => 'Altezza (cm)',
|
'height' => 'Altezza (mm)',
|
||||||
'tipo_finitura' => 'Tipo finitura',
|
'tipo_finitura' => 'Tipo finitura',
|
||||||
'finitura_interna' => 'Finitura interna',
|
'finitura_interna' => 'Finitura interna',
|
||||||
'finitura_esterna' => 'Finitura esterna',
|
'finitura_esterna' => 'Finitura esterna',
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Support\Listini;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legge il file Excel dei listini (primo foglio). Il file contiene più tabelle una
|
||||||
|
* sotto l'altra, ciascuna nella forma (misure in mm, come salvate a sistema):
|
||||||
|
*
|
||||||
|
* codice_infisso | 400 | 500 | ... ← larghezze in mm
|
||||||
|
* 400 | p | p | ... ← altezza in mm + prezzi
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* Se la riga del titolo non contiene larghezze, l'intestazione è sulla riga successiva
|
||||||
|
* (es. "CASSONETTO PVC" seguito da "mm | 500 | 600 ...").
|
||||||
|
*/
|
||||||
|
class ListinoExcelParser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return list<array{codice: string, prezzi: list<array{larghezza: int, altezza: int, prezzo: float}>}>
|
||||||
|
*/
|
||||||
|
public static function parse(string $path): array
|
||||||
|
{
|
||||||
|
$reader = IOFactory::createReader(IOFactory::identify($path));
|
||||||
|
$reader->setReadDataOnly(true);
|
||||||
|
|
||||||
|
$righe = $reader->load($path)->getSheet(0)->toArray(null, true, false, false);
|
||||||
|
|
||||||
|
return self::parseRighe($righe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<list<mixed>> $righe
|
||||||
|
*/
|
||||||
|
public static function parseRighe(array $righe): array
|
||||||
|
{
|
||||||
|
$blocchi = [];
|
||||||
|
$n = count($righe);
|
||||||
|
|
||||||
|
for ($r = 0; $r < $n; $r++) {
|
||||||
|
$titolo = $righe[$r][0] ?? null;
|
||||||
|
if (! is_string($titolo) || trim($titolo) === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$intestazione = $r;
|
||||||
|
$larghezze = self::larghezze($righe[$r]);
|
||||||
|
if (! $larghezze && isset($righe[$r + 1])) {
|
||||||
|
$intestazione = $r + 1;
|
||||||
|
$larghezze = self::larghezze($righe[$r + 1]);
|
||||||
|
}
|
||||||
|
if (! $larghezze) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prezzi = [];
|
||||||
|
for ($rr = $intestazione + 1; $rr < $n; $rr++) {
|
||||||
|
$altezza = $righe[$rr][0] ?? null;
|
||||||
|
if (! is_numeric($altezza)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($larghezze as $colonna => $larghezza) {
|
||||||
|
$prezzo = $righe[$rr][$colonna] ?? null;
|
||||||
|
if (! is_numeric($prezzo)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prezzi[] = [
|
||||||
|
'larghezza' => (int) round($larghezza),
|
||||||
|
'altezza' => (int) round((float) $altezza),
|
||||||
|
'prezzo' => round((float) $prezzo, 2),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prezzi) {
|
||||||
|
$blocchi[] = ['codice' => trim($titolo), 'prezzi' => $prezzi];
|
||||||
|
}
|
||||||
|
|
||||||
|
$r = $rr - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $blocchi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Larghezze consecutive a partire dalla colonna B, indicizzate per colonna.
|
||||||
|
*
|
||||||
|
* @return array<int, float>
|
||||||
|
*/
|
||||||
|
private static function larghezze(array $riga): array
|
||||||
|
{
|
||||||
|
$larghezze = [];
|
||||||
|
for ($c = 1; $c < count($riga) && is_numeric($riga[$c]); $c++) {
|
||||||
|
$larghezze[$c] = (float) $riga[$c];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $larghezze;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Support\Listini;
|
||||||
|
|
||||||
|
use App\Models\Modello;
|
||||||
|
use App\Models\Sistema;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class ListinoImporter
|
||||||
|
{
|
||||||
|
// Codici del file con refusi noti rispetto al codice interno del modello.
|
||||||
|
private const ALIAS = [
|
||||||
|
'finestra_wasistas' => 'finestra_vasistas',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associazione proposta codice-del-file → modello del sistema (null se nessun
|
||||||
|
* modello corrisponde): per codice interno, poi per nome o descrizione.
|
||||||
|
*
|
||||||
|
* @param list<string> $codici
|
||||||
|
* @return array<string, int|null>
|
||||||
|
*/
|
||||||
|
public static function suggerisciAssociazioni(Sistema $sistema, array $codici): array
|
||||||
|
{
|
||||||
|
$modelli = $sistema->modelli()->get();
|
||||||
|
$associazioni = [];
|
||||||
|
|
||||||
|
foreach ($codici as $codice) {
|
||||||
|
$associazioni[$codice] = self::trovaModello($modelli, $codice)?->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $associazioni;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function trovaModello(Collection $modelli, string $codice): ?Modello
|
||||||
|
{
|
||||||
|
$slug = Str::slug($codice, '_');
|
||||||
|
$slug = self::ALIAS[$slug] ?? $slug;
|
||||||
|
|
||||||
|
return $modelli->first(fn (Modello $m) => $m->codice === $slug)
|
||||||
|
?? $modelli->first(fn (Modello $m) => Str::slug($m->nome, '_') === $slug)
|
||||||
|
?? $modelli->first(fn (Modello $m) => $m->descrizione && Str::slug($m->descrizione, '_') === $slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sostituisce il listino di ogni modello associato con i prezzi del rispettivo blocco.
|
||||||
|
*
|
||||||
|
* @param list<array{codice: string, prezzi: list<array>}> $blocchi
|
||||||
|
* @param array<string, int|null> $associazioni codice del file → id modello
|
||||||
|
* @return int numero di modelli aggiornati
|
||||||
|
*/
|
||||||
|
public static function importa(Sistema $sistema, array $blocchi, array $associazioni): int
|
||||||
|
{
|
||||||
|
$prezziPerCodice = collect($blocchi)->keyBy('codice')->map(fn (array $b) => $b['prezzi']);
|
||||||
|
$modelli = $sistema->modelli()->get()->keyBy('id');
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($associazioni, $prezziPerCodice, $modelli) {
|
||||||
|
$aggiornati = 0;
|
||||||
|
$ora = now();
|
||||||
|
|
||||||
|
foreach ($associazioni as $codice => $modelloId) {
|
||||||
|
$modello = $modelloId ? $modelli->get($modelloId) : null;
|
||||||
|
if (! $modello || ! $prezziPerCodice->has($codice)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$modello->listino()->delete();
|
||||||
|
// Una stessa misura ripetuta nel blocco: vale l'ultima.
|
||||||
|
$righe = collect($prezziPerCodice[$codice])
|
||||||
|
->keyBy(fn (array $p) => $p['larghezza'].'x'.$p['altezza'])
|
||||||
|
->map(fn (array $p) => $p + ['modello_id' => $modello->id, 'created_at' => $ora, 'updated_at' => $ora])
|
||||||
|
->values();
|
||||||
|
foreach ($righe->chunk(500) as $chunk) {
|
||||||
|
$modello->listino()->insert($chunk->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
$modello->update(['listino_aggiornato_at' => $ora]);
|
||||||
|
$aggiornati++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aggiornati;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -127,7 +127,7 @@ onMounted(loadCarrello)
|
|||||||
<p class="item-title">{{ item.nome_modello || 'Prodotto' }}</p>
|
<p class="item-title">{{ item.nome_modello || 'Prodotto' }}</p>
|
||||||
<p class="item-sub">
|
<p class="item-sub">
|
||||||
<span v-if="item.sistema">{{ item.sistema }}</span>
|
<span v-if="item.sistema">{{ item.sistema }}</span>
|
||||||
<span v-if="item.larghezza && item.altezza"> · {{ item.larghezza }}×{{ item.altezza }} cm</span>
|
<span v-if="item.larghezza && item.altezza"> · {{ item.larghezza }}×{{ item.altezza }} mm</span>
|
||||||
</p>
|
</p>
|
||||||
<div class="conf">
|
<div class="conf">
|
||||||
<div v-for="row in item.configurazione" :key="row.label">
|
<div v-for="row in item.configurazione" :key="row.label">
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"filament/filament": "^3.2",
|
"filament/filament": "^3.2",
|
||||||
"laravel/framework": "^13.8",
|
"laravel/framework": "^13.8",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.0",
|
||||||
"laravel/tinker": "^3.0"
|
"laravel/tinker": "^3.0",
|
||||||
|
"phpoffice/phpspreadsheet": "^5.10"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
|||||||
Generated
+373
-1
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "5fb04fda27e55e3061efd06c8eed80c5",
|
"content-hash": "e1a742903ad0f0633f16a259629241b9",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "anourvalar/eloquent-serialize",
|
"name": "anourvalar/eloquent-serialize",
|
||||||
@@ -426,6 +426,82 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-02-09T16:56:22+00:00"
|
"time": "2024-02-09T16:56:22+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "composer/pcre",
|
||||||
|
"version": "3.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/composer/pcre.git",
|
||||||
|
"reference": "d5a341b3fb61f3001970940afb1d332968a183ed"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed",
|
||||||
|
"reference": "d5a341b3fb61f3001970940afb1d332968a183ed",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan": "<2.2.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^2",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2",
|
||||||
|
"phpstan/phpstan-strict-rules": "^2",
|
||||||
|
"phpunit/phpunit": "^9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Composer\\Pcre\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "http://seld.be"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
|
||||||
|
"keywords": [
|
||||||
|
"PCRE",
|
||||||
|
"preg",
|
||||||
|
"regex",
|
||||||
|
"regular expression"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/composer/pcre/issues",
|
||||||
|
"source": "https://github.com/composer/pcre/tree/3.4.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://packagist.com",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/composer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-06-07T11:47:49+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "danharrin/date-format-converter",
|
"name": "danharrin/date-format-converter",
|
||||||
"version": "v0.3.1",
|
"version": "v0.3.1",
|
||||||
@@ -3463,6 +3539,191 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-06-27T03:14:20+00:00"
|
"time": "2026-06-27T03:14:20+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "maennchen/zipstream-php",
|
||||||
|
"version": "3.2.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/maennchen/ZipStream-PHP.git",
|
||||||
|
"reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
|
||||||
|
"reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-zlib": "*",
|
||||||
|
"php-64bit": "^8.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"brianium/paratest": "^7.7",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.86",
|
||||||
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"mikey179/vfsstream": "^1.6",
|
||||||
|
"php-coveralls/php-coveralls": "^2.5",
|
||||||
|
"phpunit/phpunit": "^12.0",
|
||||||
|
"vimeo/psalm": "^6.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"guzzlehttp/psr7": "^2.4",
|
||||||
|
"psr/http-message": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ZipStream\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Paul Duncan",
|
||||||
|
"email": "pabs@pablotron.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jonatan Männchen",
|
||||||
|
"email": "jonatan@maennchen.ch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jesse Donat",
|
||||||
|
"email": "donatj@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "András Kolesár",
|
||||||
|
"email": "kolesar@kolesar.hu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
|
||||||
|
"keywords": [
|
||||||
|
"stream",
|
||||||
|
"zip"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
|
||||||
|
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/maennchen",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-04-11T18:38:28+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "markbaker/complex",
|
||||||
|
"version": "3.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||||
|
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||||
|
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Complex\\": "classes/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"email": "mark@lange.demon.co.uk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Class for working with complex numbers",
|
||||||
|
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||||
|
"keywords": [
|
||||||
|
"complex",
|
||||||
|
"mathematics"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||||
|
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"time": "2022-12-06T16:21:08+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "markbaker/matrix",
|
||||||
|
"version": "3.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||||
|
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
|
||||||
|
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpdocumentor/phpdocumentor": "2.*",
|
||||||
|
"phploc/phploc": "^4.0",
|
||||||
|
"phpmd/phpmd": "2.*",
|
||||||
|
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||||
|
"sebastian/phpcpd": "^4.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Matrix\\": "classes/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"email": "mark@demon-angel.eu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Class for working with matrices",
|
||||||
|
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||||
|
"keywords": [
|
||||||
|
"mathematics",
|
||||||
|
"matrix",
|
||||||
|
"vector"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||||
|
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
|
||||||
|
},
|
||||||
|
"time": "2022-12-02T22:17:43+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "masterminds/html5",
|
"name": "masterminds/html5",
|
||||||
"version": "2.10.1",
|
"version": "2.10.1",
|
||||||
@@ -4133,6 +4394,117 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-09-03T16:03:54+00:00"
|
"time": "2025-09-03T16:03:54+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoffice/phpspreadsheet",
|
||||||
|
"version": "5.10.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||||
|
"reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/eb18727acf6b1f4cc67145a52ab04f9fd4c28d53",
|
||||||
|
"reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"composer/pcre": "^1||^2||^3",
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"ext-libxml": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-xml": "*",
|
||||||
|
"ext-xmlreader": "*",
|
||||||
|
"ext-xmlwriter": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"ext-zlib": "*",
|
||||||
|
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||||
|
"markbaker/complex": "^3.0",
|
||||||
|
"markbaker/matrix": "^3.0",
|
||||||
|
"php": "^8.2",
|
||||||
|
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||||
|
"dompdf/dompdf": "^2.0 || ^3.0",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.2",
|
||||||
|
"mitoteam/jpgraph": "^10.5",
|
||||||
|
"mpdf/mpdf": "^8.1.1",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpstan/phpstan": "^1.1 || ^2.0",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
|
||||||
|
"phpunit/phpunit": "^10.5 || ^11.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
|
"tecnickcom/tcpdf": ">=6.8.0 <7.0.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||||
|
"ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()",
|
||||||
|
"ext-openssl": "Handline Agile-encrypted Xlsx spreadsheets",
|
||||||
|
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||||
|
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||||
|
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Maarten Balliauw",
|
||||||
|
"homepage": "https://blog.maartenballiauw.be"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"homepage": "https://markbakeruk.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Franck Lefevre",
|
||||||
|
"homepage": "https://rootslabs.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Erik Tilt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Adrien Crivelli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Owen Leibman"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||||
|
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||||
|
"keywords": [
|
||||||
|
"OpenXML",
|
||||||
|
"excel",
|
||||||
|
"gnumeric",
|
||||||
|
"ods",
|
||||||
|
"php",
|
||||||
|
"spreadsheet",
|
||||||
|
"xls",
|
||||||
|
"xlsx"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||||
|
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.10.0"
|
||||||
|
},
|
||||||
|
"time": "2026-09-17T03:54:50+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpoption/phpoption",
|
"name": "phpoption/phpoption",
|
||||||
"version": "1.9.5",
|
"version": "1.9.5",
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Griglia prezzi del modello: un prezzo per ogni coppia larghezza × altezza.
|
||||||
|
Schema::create('listino_prezzi', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('modello_id')->constrained('modelli')->cascadeOnDelete();
|
||||||
|
$table->decimal('larghezza', 8, 2);
|
||||||
|
$table->decimal('altezza', 8, 2);
|
||||||
|
$table->decimal('prezzo', 10, 2);
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['modello_id', 'larghezza', 'altezza']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('modelli', function (Blueprint $table) {
|
||||||
|
$table->timestamp('listino_aggiornato_at')->nullable()->after('moduli');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('modelli', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('listino_aggiornato_at');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::dropIfExists('listino_prezzi');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Le misure passano da centimetri a millimetri: listini dei modelli e misure dei
|
||||||
|
* prodotti già nei preventivi (colonne e payload del configuratore).
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$this->converti(10);
|
||||||
|
|
||||||
|
Schema::table('listino_prezzi', function (Blueprint $table) {
|
||||||
|
$table->unsignedInteger('larghezza')->change();
|
||||||
|
$table->unsignedInteger('altezza')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('listino_prezzi', function (Blueprint $table) {
|
||||||
|
$table->decimal('larghezza', 8, 2)->change();
|
||||||
|
$table->decimal('altezza', 8, 2)->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->converti(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function converti(float $fattore): void
|
||||||
|
{
|
||||||
|
// In due passaggi (prima negativi, poi positivi): MySQL verifica l'indice univoco
|
||||||
|
// riga per riga, e un valore già convertito (30 → 300) collide con uno non ancora
|
||||||
|
// convertito. I negativi non collidono con nessun valore originale.
|
||||||
|
DB::table('listino_prezzi')->update([
|
||||||
|
'larghezza' => DB::raw("-ROUND(larghezza * {$fattore}, 2)"),
|
||||||
|
'altezza' => DB::raw("-ROUND(altezza * {$fattore}, 2)"),
|
||||||
|
]);
|
||||||
|
DB::table('listino_prezzi')->update([
|
||||||
|
'larghezza' => DB::raw('-larghezza'),
|
||||||
|
'altezza' => DB::raw('-altezza'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::table('preventivo_items')->orderBy('id')->chunkById(200, function ($items) use ($fattore) {
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$payload = json_decode($item->payload ?? 'null', true);
|
||||||
|
foreach (['width', 'height'] as $chiave) {
|
||||||
|
if (is_array($payload) && is_numeric($payload[$chiave] ?? null)) {
|
||||||
|
$payload[$chiave] = (string) round((float) $payload[$chiave] * $fattore, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('preventivo_items')->where('id', $item->id)->update([
|
||||||
|
'larghezza' => $item->larghezza === null ? null : (int) round($item->larghezza * $fattore),
|
||||||
|
'altezza' => $item->altezza === null ? null : (int) round($item->altezza * $fattore),
|
||||||
|
'payload' => $payload === null ? $item->payload : json_encode($payload),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@ Route::get('config', fn () => response()->json([
|
|||||||
// Catalogo prodotti (sistemi e modelli con icone) per il configuratore.
|
// Catalogo prodotti (sistemi e modelli con icone) per il configuratore.
|
||||||
Route::get('sistemi', [CatalogoController::class, 'sistemi']);
|
Route::get('sistemi', [CatalogoController::class, 'sistemi']);
|
||||||
Route::get('sistemi/{codice}', [CatalogoController::class, 'sistemaPerCodice']);
|
Route::get('sistemi/{codice}', [CatalogoController::class, 'sistemaPerCodice']);
|
||||||
|
Route::get('sistemi/{codice}/modelli/{modello}/listino', [CatalogoController::class, 'listino']);
|
||||||
|
|
||||||
Route::prefix('auth')->group(function () {
|
Route::prefix('auth')->group(function () {
|
||||||
Route::post('login', [AuthController::class, 'login']);
|
Route::post('login', [AuthController::class, 'login']);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Modello;
|
||||||
|
use App\Models\Preventivo;
|
||||||
|
use App\Support\ConfigurazioneFormatter;
|
||||||
|
use Database\Seeders\CatalogoSeeder;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ConversioneMisureMmTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
private const MIGRAZIONE = 'database/migrations/2026_10_19_150000_converti_misure_in_mm.php';
|
||||||
|
|
||||||
|
public function test_la_migrazione_porta_listini_e_preventivi_esistenti_in_mm(): void
|
||||||
|
{
|
||||||
|
Storage::fake('public');
|
||||||
|
$this->seed(CatalogoSeeder::class);
|
||||||
|
|
||||||
|
// Riporta lo schema e i dati allo stato precedente (in cm).
|
||||||
|
Artisan::call('migrate:rollback', ['--path' => self::MIGRAZIONE]);
|
||||||
|
|
||||||
|
$modello = Modello::where('codice', 'finestra_1_anta')->firstOrFail();
|
||||||
|
DB::table('listino_prezzi')->insert(['modello_id' => $modello->id, 'larghezza' => 145, 'altezza' => 40, 'prezzo' => 367.2]);
|
||||||
|
$preventivo = Preventivo::create(['token' => Str::random(40), 'stato' => 'bozza']);
|
||||||
|
$item = $preventivo->items()->create([
|
||||||
|
'nome_modello' => 'Finestra 1 anta',
|
||||||
|
'larghezza' => 80,
|
||||||
|
'altezza' => 120,
|
||||||
|
'payload' => ['width' => '80.00', 'height' => '120.5', 'maniglia' => 'Bianca'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Artisan::call('migrate', ['--path' => self::MIGRAZIONE]);
|
||||||
|
|
||||||
|
$this->assertSame([1450, 400], [(int) $modello->listino()->value('larghezza'), (int) $modello->listino()->value('altezza')]);
|
||||||
|
|
||||||
|
$item->refresh();
|
||||||
|
$this->assertSame([800, 1200], [$item->larghezza, $item->altezza]);
|
||||||
|
$this->assertSame(['width' => '800', 'height' => '1205', 'maniglia' => 'Bianca'], $item->payload);
|
||||||
|
|
||||||
|
$righe = collect(ConfigurazioneFormatter::toRows($item->payload))->pluck('value', 'label');
|
||||||
|
$this->assertSame('800', $righe['Larghezza (mm)']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Filament\Resources\ModelloResource\Pages\EditModello;
|
||||||
|
use App\Filament\Resources\ModelloResource\RelationManagers\ListinoRelationManager;
|
||||||
|
use App\Filament\Resources\SistemaResource\Pages\ListSistemi;
|
||||||
|
use App\Models\ListinoPrezzo;
|
||||||
|
use App\Models\Modello;
|
||||||
|
use App\Models\Sistema;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Support\Listini\ListinoExcelParser;
|
||||||
|
use App\Support\Listini\ListinoImporter;
|
||||||
|
use Database\Seeders\CatalogoSeeder;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ImportazioneListiniTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
private Sistema $pvc;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Storage::fake('public');
|
||||||
|
$this->seed(CatalogoSeeder::class);
|
||||||
|
$this->pvc = Sistema::where('codice', 'pvc')->firstOrFail();
|
||||||
|
$this->actingAs(User::factory()->create());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Due tabelle nel formato del listino, separate da una riga vuota. */
|
||||||
|
private function righe(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[null, null, null],
|
||||||
|
['finestra_1_anta', 400, 500],
|
||||||
|
[400, 367.2, 381.244],
|
||||||
|
[500, 381.24, null],
|
||||||
|
[null, null, null],
|
||||||
|
['PORTA 1 ANTA, CON SERRATURA', 500, 600],
|
||||||
|
[1800, 796.5, 820],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fileExcel(): UploadedFile
|
||||||
|
{
|
||||||
|
$spreadsheet = new Spreadsheet;
|
||||||
|
$spreadsheet->getActiveSheet()->fromArray($this->righe(), null, 'A1', true);
|
||||||
|
$path = tempnam(sys_get_temp_dir(), 'listino');
|
||||||
|
(new Xlsx($spreadsheet))->save($path);
|
||||||
|
|
||||||
|
return UploadedFile::fake()->createWithContent('listino.xlsx', file_get_contents($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_il_parser_legge_le_tabelle_in_mm(): void
|
||||||
|
{
|
||||||
|
$blocchi = ListinoExcelParser::parseRighe($this->righe());
|
||||||
|
|
||||||
|
$this->assertSame(['finestra_1_anta', 'PORTA 1 ANTA, CON SERRATURA'], array_column($blocchi, 'codice'));
|
||||||
|
$this->assertSame([
|
||||||
|
['larghezza' => 400, 'altezza' => 400, 'prezzo' => 367.2],
|
||||||
|
['larghezza' => 500, 'altezza' => 400, 'prezzo' => 381.24],
|
||||||
|
['larghezza' => 400, 'altezza' => 500, 'prezzo' => 381.24],
|
||||||
|
], $blocchi[0]['prezzi']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_il_parser_gestisce_l_intestazione_sulla_riga_successiva(): void
|
||||||
|
{
|
||||||
|
$blocchi = ListinoExcelParser::parseRighe([
|
||||||
|
['CASSONETTO PVC', null, null],
|
||||||
|
['mm', 500, 600],
|
||||||
|
[300, 94.14, 104.59],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame('CASSONETTO PVC', $blocchi[0]['codice']);
|
||||||
|
$this->assertCount(2, $blocchi[0]['prezzi']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_associazione_proposta_per_codice_alias_e_descrizione(): void
|
||||||
|
{
|
||||||
|
$associazioni = ListinoImporter::suggerisciAssociazioni($this->pvc, [
|
||||||
|
'finestra_1_anta', 'finestra_wasistas', 'BILICO ORIZZONTALE', 'ALZANTE 2ANTE_MANO SX',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$id = fn (string $codice) => Modello::where('codice', $codice)->value('id');
|
||||||
|
$this->assertSame([
|
||||||
|
'finestra_1_anta' => $id('finestra_1_anta'),
|
||||||
|
'finestra_wasistas' => $id('finestra_vasistas'),
|
||||||
|
'BILICO ORIZZONTALE' => $id('bilico'),
|
||||||
|
'ALZANTE 2ANTE_MANO SX' => null,
|
||||||
|
], $associazioni);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_l_azione_importa_i_listini_secondo_le_associazioni_scelte(): void
|
||||||
|
{
|
||||||
|
$finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail();
|
||||||
|
$porta = Modello::where('codice', 'porta_1_anta')->firstOrFail();
|
||||||
|
$bilico = Modello::where('codice', 'bilico')->firstOrFail();
|
||||||
|
// Listino preesistente di un modello non coinvolto: deve restare com'è.
|
||||||
|
ListinoPrezzo::create(['modello_id' => $bilico->id, 'larghezza' => 1000, 'altezza' => 1000, 'prezzo' => 1]);
|
||||||
|
ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 9990, 'altezza' => 9990, 'prezzo' => 1]);
|
||||||
|
|
||||||
|
Livewire::test(ListSistemi::class)
|
||||||
|
->callTableAction('importaListini', $this->pvc, data: [
|
||||||
|
'file' => $this->fileExcel(),
|
||||||
|
'associazioni' => [
|
||||||
|
'a' => ['codice' => 'finestra_1_anta', 'modello_id' => $finestra->id],
|
||||||
|
// Associazione corretta a mano dall'admin.
|
||||||
|
'b' => ['codice' => 'PORTA 1 ANTA, CON SERRATURA', 'modello_id' => $porta->id],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->assertHasNoTableActionErrors()
|
||||||
|
->assertNotified('Listini importati');
|
||||||
|
|
||||||
|
$this->assertSame(3, $finestra->listino()->count());
|
||||||
|
$this->assertFalse($finestra->listino()->where('larghezza', 9990)->exists());
|
||||||
|
$this->assertEquals(820, $porta->listino()->where('larghezza', 600)->value('prezzo'));
|
||||||
|
$this->assertSame(1, $bilico->listino()->count());
|
||||||
|
$this->assertNotNull($finestra->fresh()->listino_aggiornato_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_due_tabelle_non_possono_andare_sullo_stesso_modello(): void
|
||||||
|
{
|
||||||
|
$finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail();
|
||||||
|
|
||||||
|
Livewire::test(ListSistemi::class)
|
||||||
|
->callTableAction('importaListini', $this->pvc, data: [
|
||||||
|
'file' => $this->fileExcel(),
|
||||||
|
'associazioni' => [
|
||||||
|
'a' => ['codice' => 'finestra_1_anta', 'modello_id' => $finestra->id],
|
||||||
|
'b' => ['codice' => 'PORTA 1 ANTA, CON SERRATURA', 'modello_id' => $finestra->id],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->assertHasTableActionErrors();
|
||||||
|
|
||||||
|
$this->assertSame(0, $finestra->listino()->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_il_listino_e_modificabile_dal_modello(): void
|
||||||
|
{
|
||||||
|
$finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail();
|
||||||
|
$prezzo = ListinoPrezzo::create(['modello_id' => $finestra->id, 'larghezza' => 400, 'altezza' => 400, 'prezzo' => 100]);
|
||||||
|
|
||||||
|
Livewire::test(ListinoRelationManager::class, ['ownerRecord' => $finestra, 'pageClass' => EditModello::class])
|
||||||
|
->assertCanSeeTableRecords([$prezzo])
|
||||||
|
->callTableAction('create', data: ['larghezza' => 500, 'altezza' => 400, 'prezzo' => 120])
|
||||||
|
->assertHasNoTableActionErrors()
|
||||||
|
->callTableAction('create', data: ['larghezza' => 400, 'altezza' => 400, 'prezzo' => 1])
|
||||||
|
->assertHasTableActionErrors(['larghezza' => 'unique']);
|
||||||
|
|
||||||
|
$this->assertSame(2, $finestra->listino()->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_api_listino_restituisce_la_griglia_ordinata(): void
|
||||||
|
{
|
||||||
|
$finestra = Modello::where('codice', 'finestra_1_anta')->firstOrFail();
|
||||||
|
ListinoImporter::importa($this->pvc, ListinoExcelParser::parseRighe($this->righe()), ['finestra_1_anta' => $finestra->id]);
|
||||||
|
|
||||||
|
$this->getJson('/api/sistemi/pvc/modelli/finestra_1_anta/listino')
|
||||||
|
->assertOk()
|
||||||
|
->assertExactJson([
|
||||||
|
['altezza' => 400, 'larghezza' => 400, 'prezzo' => 367.2],
|
||||||
|
['altezza' => 400, 'larghezza' => 500, 'prezzo' => 381.24],
|
||||||
|
['altezza' => 500, 'larghezza' => 400, 'prezzo' => 381.24],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Modello disattivo (senza asset 3D): non esposto.
|
||||||
|
$this->getJson('/api/sistemi/pvc/modelli/scorrevole_sf_3_ante/listino')->assertNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-6 col-6">
|
<div class="col-sm-6 col-6">
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
<h6>Larghezza (cm)</h6>
|
<h6>Larghezza (mm)</h6>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="number" step=".01" class="form-control form-control-sm" id="larghezza"
|
<input type="number" step="1" min="0" class="form-control form-control-sm" id="larghezza"
|
||||||
placeholder="Larghezza" aria-label="Larghezza">
|
placeholder="Larghezza" aria-label="Larghezza">
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">min/max <span class="larghezza-max"></span></div>
|
<div class="hint">min/max <span class="larghezza-max"></span></div>
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-6">
|
<div class="col-sm-6 col-6">
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
<h6>Altezza (cm)</h6>
|
<h6>Altezza (mm)</h6>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="number" step=".01" class="form-control form-control-sm" id="altezza"
|
<input type="number" step="1" min="0" class="form-control form-control-sm" id="altezza"
|
||||||
placeholder="Altezza" aria-label="Larghezza">
|
placeholder="Altezza" aria-label="Larghezza">
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">min/max <span class="altezza-max"></span></div>
|
<div class="hint">min/max <span class="altezza-max"></span></div>
|
||||||
|
|||||||
@@ -65,9 +65,9 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-6 col-6">
|
<div class="col-sm-6 col-6">
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
<h6>Larghezza (cm)</h6>
|
<h6>Larghezza (mm)</h6>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="number" step=".01" class="form-control form-control-sm" id="larghezza"
|
<input type="number" step="1" min="0" class="form-control form-control-sm" id="larghezza"
|
||||||
placeholder="Larghezza" aria-label="Larghezza">
|
placeholder="Larghezza" aria-label="Larghezza">
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">min/max <span class="larghezza-max"></span></div>
|
<div class="hint">min/max <span class="larghezza-max"></span></div>
|
||||||
@@ -75,9 +75,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-6">
|
<div class="col-sm-6 col-6">
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
<h6>Altezza (cm)</h6>
|
<h6>Altezza (mm)</h6>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="number" step=".01" class="form-control form-control-sm" id="altezza"
|
<input type="number" step="1" min="0" class="form-control form-control-sm" id="altezza"
|
||||||
placeholder="Altezza" aria-label="Larghezza">
|
placeholder="Altezza" aria-label="Larghezza">
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">min/max <span class="altezza-max"></span></div>
|
<div class="hint">min/max <span class="altezza-max"></span></div>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 60, "altezza_maniglia": "19 cm" },
|
{ "altezza": 600, "altezza_maniglia": "190 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "28,3 cm" },
|
{ "altezza": 700, "altezza_maniglia": "283 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "28,3 cm" },
|
{ "altezza": 800, "altezza_maniglia": "283 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "43,3 cm" },
|
{ "altezza": 900, "altezza_maniglia": "433 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "43,3 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "433 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "102 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 100, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "100,7 cm" }
|
{ "altezza": 2800, "altezza_maniglia": "1007 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "58,3 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "583 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "102 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2500, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 60, "altezza_maniglia": "13,4 cm" },
|
{ "altezza": 600, "altezza_maniglia": "134 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "13,4 cm" },
|
{ "altezza": 700, "altezza_maniglia": "134 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "34,2 cm" },
|
{ "altezza": 800, "altezza_maniglia": "342 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "39,2 cm" },
|
{ "altezza": 900, "altezza_maniglia": "392 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "44,2 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "442 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "49,2 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "492 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "54,2 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "542 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "59,2 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "592 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "64,2 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "642 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "69,2 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "692 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "74,2 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "742 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "79,2 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "792 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "84,2 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "842 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "89,2 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "892 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "94,2 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "942 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "99,2 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "992 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "104,2 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1042 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "109,2 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1092 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "114,2 cm" }
|
{ "altezza": 2400, "altezza_maniglia": "1142 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 50, "altezza_maniglia": "19,8 cm" },
|
{ "altezza": 500, "altezza_maniglia": "198 mm" },
|
||||||
{ "altezza": 60, "altezza_maniglia": "19,8 cm" },
|
{ "altezza": 600, "altezza_maniglia": "198 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "31,8 cm" },
|
{ "altezza": 700, "altezza_maniglia": "318 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "31,8 cm" },
|
{ "altezza": 800, "altezza_maniglia": "318 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "41,8 cm" },
|
{ "altezza": 900, "altezza_maniglia": "418 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "41,8 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "418 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "51,8 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "518 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "51,8 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "518 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "61,8 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "618 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "61,8 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "618 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "71,8 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "718 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "71,8 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "718 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "71,8 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "718 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "71,8 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "718 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "71,8 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "718 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "101 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1010 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "101 cm" }
|
{ "altezza": 2600, "altezza_maniglia": "1010 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "26,4 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "264 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "31,4 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "314 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "36,4 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "364 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "41,4 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "414 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "46,4 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "464 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "51,4 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "514 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "56,4 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "564 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "61,4 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "614 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "66,4 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "664 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "71,4 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "714 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "76,4 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "764 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "81,4 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "814 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "86,4 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "864 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "91,4 cm" }
|
{ "altezza": 2800, "altezza_maniglia": "914 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "72,7 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "727 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "77,7 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "777 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "87,7 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "877 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "92,7 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "927 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "97,7 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "977 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "102,7 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1027 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "107,7 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1077 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "112,7 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1127 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "112,7 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1127 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "117,7 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1177 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "122,7 cm" }
|
{ "altezza": 2500, "altezza_maniglia": "1227 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 210, "altezza_maniglia": "105 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1050 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "105 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1050 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "105 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1050 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "105 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1050 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "105 cm" }
|
{ "altezza": 2500, "altezza_maniglia": "1050 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 100, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "40,8 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "408 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2800, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 290, "altezza_maniglia": "100,8 cm" },
|
{ "altezza": 2900, "altezza_maniglia": "1008 mm" },
|
||||||
{ "altezza": 300, "altezza_maniglia": "100,8 cm" }
|
{ "altezza": 3000, "altezza_maniglia": "1008 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 50, "altezza_maniglia": "13,8 cm" },
|
{ "altezza": 500, "altezza_maniglia": "138 mm" },
|
||||||
{ "altezza": 60, "altezza_maniglia": "13,8 cm" },
|
{ "altezza": 600, "altezza_maniglia": "138 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "18,8 cm" },
|
{ "altezza": 700, "altezza_maniglia": "188 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "28,1 cm" },
|
{ "altezza": 800, "altezza_maniglia": "281 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "28,1 cm" },
|
{ "altezza": 900, "altezza_maniglia": "281 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "43,1 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "431 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "43,1 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "431 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "53,1 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "531 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "53,1 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "531 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "101,8 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1018 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "101,8 cm" }
|
{ "altezza": 2100, "altezza_maniglia": "1018 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 100, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "100,7 cm" }
|
{ "altezza": 2800, "altezza_maniglia": "1007 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "58,1 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "581 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "108,1 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1081 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "108,1 cm" }
|
{ "altezza": 2500, "altezza_maniglia": "1081 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2500, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "40,5 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "405 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "40,5 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "405 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "40,5 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "405 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2800, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 290, "altezza_maniglia": "100,5 cm" },
|
{ "altezza": 2900, "altezza_maniglia": "1005 mm" },
|
||||||
{ "altezza": 300, "altezza_maniglia": "100,5 cm" }
|
{ "altezza": 3000, "altezza_maniglia": "1005 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 60, "altezza_maniglia": "14 cm" },
|
{ "altezza": 600, "altezza_maniglia": "140 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "19 cm" },
|
{ "altezza": 700, "altezza_maniglia": "190 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "28,3 cm" },
|
{ "altezza": 800, "altezza_maniglia": "283 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "28,3 cm" },
|
{ "altezza": 900, "altezza_maniglia": "283 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "43,3 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "433 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "43,3 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "433 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2600, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 100, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "40,7 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "407 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2600, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 270, "altezza_maniglia": "100,7 cm" },
|
{ "altezza": 2700, "altezza_maniglia": "1007 mm" },
|
||||||
{ "altezza": 280, "altezza_maniglia": "100,7 cm" }
|
{ "altezza": 2800, "altezza_maniglia": "1007 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 150, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "53,3 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "533 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 250, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2500, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 260, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2600, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 200, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "102 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1020 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "102 cm" }
|
{ "altezza": 2400, "altezza_maniglia": "1020 mm" }
|
||||||
]
|
]
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{ "altezza": 60, "altezza_maniglia": "13,4 cm" },
|
{ "altezza": 600, "altezza_maniglia": "134 mm" },
|
||||||
{ "altezza": 70, "altezza_maniglia": "13,4 cm" },
|
{ "altezza": 700, "altezza_maniglia": "134 mm" },
|
||||||
{ "altezza": 80, "altezza_maniglia": "34,2 cm" },
|
{ "altezza": 800, "altezza_maniglia": "342 mm" },
|
||||||
{ "altezza": 90, "altezza_maniglia": "39,2 cm" },
|
{ "altezza": 900, "altezza_maniglia": "392 mm" },
|
||||||
{ "altezza": 100, "altezza_maniglia": "44,2 cm" },
|
{ "altezza": 1000, "altezza_maniglia": "442 mm" },
|
||||||
{ "altezza": 110, "altezza_maniglia": "49,2 cm" },
|
{ "altezza": 1100, "altezza_maniglia": "492 mm" },
|
||||||
{ "altezza": 120, "altezza_maniglia": "54,2 cm" },
|
{ "altezza": 1200, "altezza_maniglia": "542 mm" },
|
||||||
{ "altezza": 130, "altezza_maniglia": "59,2 cm" },
|
{ "altezza": 1300, "altezza_maniglia": "592 mm" },
|
||||||
{ "altezza": 140, "altezza_maniglia": "64,2 cm" },
|
{ "altezza": 1400, "altezza_maniglia": "642 mm" },
|
||||||
{ "altezza": 150, "altezza_maniglia": "69,2 cm" },
|
{ "altezza": 1500, "altezza_maniglia": "692 mm" },
|
||||||
{ "altezza": 160, "altezza_maniglia": "74,2 cm" },
|
{ "altezza": 1600, "altezza_maniglia": "742 mm" },
|
||||||
{ "altezza": 170, "altezza_maniglia": "79,2 cm" },
|
{ "altezza": 1700, "altezza_maniglia": "792 mm" },
|
||||||
{ "altezza": 180, "altezza_maniglia": "84,2 cm" },
|
{ "altezza": 1800, "altezza_maniglia": "842 mm" },
|
||||||
{ "altezza": 190, "altezza_maniglia": "89,2 cm" },
|
{ "altezza": 1900, "altezza_maniglia": "892 mm" },
|
||||||
{ "altezza": 200, "altezza_maniglia": "94,2 cm" },
|
{ "altezza": 2000, "altezza_maniglia": "942 mm" },
|
||||||
{ "altezza": 210, "altezza_maniglia": "99,2 cm" },
|
{ "altezza": 2100, "altezza_maniglia": "992 mm" },
|
||||||
{ "altezza": 220, "altezza_maniglia": "104,2 cm" },
|
{ "altezza": 2200, "altezza_maniglia": "1042 mm" },
|
||||||
{ "altezza": 230, "altezza_maniglia": "109,2 cm" },
|
{ "altezza": 2300, "altezza_maniglia": "1092 mm" },
|
||||||
{ "altezza": 240, "altezza_maniglia": "114,2 cm" }
|
{ "altezza": 2400, "altezza_maniglia": "1142 mm" }
|
||||||
]
|
]
|
||||||
@@ -112,19 +112,18 @@ const Configurator = {
|
|||||||
|
|
||||||
let val_maniglia = $(`input[name=maniglia]:checked`).val();
|
let val_maniglia = $(`input[name=maniglia]:checked`).val();
|
||||||
let finitura_maniglia = shared_data.data_maniglie.find(m => m.id == val_maniglia);
|
let finitura_maniglia = shared_data.data_maniglie.find(m => m.id == val_maniglia);
|
||||||
|
// Prima fascia della tabella con altezza (mm) >= a quella del serramento.
|
||||||
let altezza = parseFloat(config.configurazione.height);
|
let altezza = parseFloat(config.configurazione.height);
|
||||||
let mod_altezza = altezza % 10;
|
let altezza_maniglia = [...shared_data.altezza_maniglia]
|
||||||
|
.sort((a, b) => a.altezza - b.altezza)
|
||||||
if(mod_altezza > 0) altezza = (altezza - mod_altezza) + 10;
|
.find((m) => m.altezza >= altezza);
|
||||||
|
|
||||||
let altezza_maniglia = shared_data.altezza_maniglia.find((m) => m.altezza == altezza);
|
|
||||||
|
|
||||||
if(altezza_maniglia != null){
|
if(altezza_maniglia != null){
|
||||||
config.configurazione.altezza_maniglia = altezza_maniglia.altezza_maniglia;
|
config.configurazione.altezza_maniglia = altezza_maniglia.altezza_maniglia;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(altezza_maniglia == null && shared_data.altezza_maniglia.length > 0){
|
if(altezza_maniglia == null && shared_data.altezza_maniglia.length > 0){
|
||||||
config.configurazione.altezza_maniglia = shared_data.altezza_maniglia[0];
|
config.configurazione.altezza_maniglia = shared_data.altezza_maniglia[0].altezza_maniglia;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(finitura_maniglia != null)
|
if(finitura_maniglia != null)
|
||||||
|
|||||||
@@ -103,8 +103,10 @@ const MenuBuilder = {
|
|||||||
const minLarghezza = Math.min(...listino.map(item => item.larghezza));
|
const minLarghezza = Math.min(...listino.map(item => item.larghezza));
|
||||||
const minAltezza = Math.min(...listino.map(item => item.altezza));
|
const minAltezza = Math.min(...listino.map(item => item.altezza));
|
||||||
$('.titolo').html(`${params.nome}`);
|
$('.titolo').html(`${params.nome}`);
|
||||||
$('.larghezza-max').html(`${minLarghezza}-${maxLarghezza}cm`);
|
$('.larghezza-max').html(`${minLarghezza}-${maxLarghezza} mm`);
|
||||||
$('.altezza-max').html(`${minAltezza}-${maxAltezza}cm`);
|
$('.altezza-max').html(`${minAltezza}-${maxAltezza} mm`);
|
||||||
|
$('#larghezza').attr({ min: minLarghezza, max: maxLarghezza });
|
||||||
|
$('#altezza').attr({ min: minAltezza, max: maxAltezza });
|
||||||
|
|
||||||
window.sistema = data.sistema;
|
window.sistema = data.sistema;
|
||||||
|
|
||||||
@@ -210,8 +212,8 @@ const MenuBuilder = {
|
|||||||
*/
|
*/
|
||||||
let size = PriceCalculator.getMinSize();
|
let size = PriceCalculator.getMinSize();
|
||||||
|
|
||||||
$('#larghezza').val(size.larghezza + '.00');
|
$('#larghezza').val(size.larghezza);
|
||||||
$('#altezza').val(size.altezza + '.00');
|
$('#altezza').val(size.altezza);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-13
@@ -25,24 +25,27 @@ const PriceCalculator = {
|
|||||||
return firstSize;
|
return firstSize;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
// Misure in mm: entro min/max del listino si usa la prima misura >= a quella
|
||||||
|
// richiesta (le griglie non sono sempre a passo regolare, es. traslanti da 1450 mm).
|
||||||
getGridPrice: (width, height) => {
|
getGridPrice: (width, height) => {
|
||||||
let altezza = parseFloat(height);
|
const larghezza = PriceCalculator.misuraListino('larghezza', width);
|
||||||
let larghezza = parseFloat(width);
|
const altezza = PriceCalculator.misuraListino('altezza', height);
|
||||||
|
if (larghezza == null || altezza == null) return -1;
|
||||||
|
|
||||||
|
const misure = listini.misure.find(el => el.altezza == altezza && el.larghezza == larghezza);
|
||||||
|
|
||||||
let mod_altezza = altezza % 10;
|
|
||||||
let mod_larghezza = larghezza % 10;
|
|
||||||
|
|
||||||
if(mod_altezza > 0) altezza = (altezza - mod_altezza) + 10;
|
|
||||||
if(mod_larghezza > 0) larghezza = (larghezza - mod_larghezza) + 10;
|
|
||||||
|
|
||||||
let misure = listini.misure.find((el) => {
|
|
||||||
if(el.altezza == altezza && el.larghezza == larghezza)
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(misure == null) return -1;
|
if(misure == null) return -1;
|
||||||
return misure.prezzo;
|
return misure.prezzo;
|
||||||
},
|
},
|
||||||
|
misuraListino: (campo, valore) => {
|
||||||
|
const v = parseFloat(valore);
|
||||||
|
if (isNaN(v)) return null;
|
||||||
|
|
||||||
|
const misure = [...new Set(listini.misure.map(el => el[campo]))].sort((a, b) => a - b);
|
||||||
|
// Min/max del listino vincolanti: fuori range la misura non è disponibile.
|
||||||
|
if (!misure.length || v < misure[0]) return null;
|
||||||
|
return misure.find(m => m >= v) ?? null;
|
||||||
|
},
|
||||||
getConfigurationPrice: (data) => {
|
getConfigurationPrice: (data) => {
|
||||||
|
|
||||||
let prezzo_finale = data.prezzo_base;
|
let prezzo_finale = data.prezzo_base;
|
||||||
|
|||||||
+5
-1
@@ -173,7 +173,11 @@ const start = async () => {
|
|||||||
const data_maniglie = modello.moduli.includes('maniglia') ? (await (await fetch(`${modello_folder}/${modello.cartella}/maniglia.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
const data_maniglie = modello.moduli.includes('maniglia') ? (await (await fetch(`${modello_folder}/${modello.cartella}/maniglia.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
||||||
const data_avvolgibile = modello.moduli.includes('avvolgibile') ? (await (await fetch(`${modello_folder}/${modello.cartella}/avvolgibile.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
const data_avvolgibile = modello.moduli.includes('avvolgibile') ? (await (await fetch(`${modello_folder}/${modello.cartella}/avvolgibile.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
||||||
const data_vetri = modello.moduli.includes('vetro') ? (await (await fetch(`${modello_folder}/${modello.cartella}/vetro.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
const data_vetri = modello.moduli.includes('vetro') ? (await (await fetch(`${modello_folder}/${modello.cartella}/vetro.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
|
||||||
const listino = modello.moduli.includes('listino') ? await (await fetch(`${modello_folder}/${modello.cartella}/listino.json?rnd=${Math.random()}`)).json() : [];
|
const listino = modello.moduli.includes('listino')
|
||||||
|
? await (await fetch(`${PREVENTIVI_API_BASE}/sistemi/${encodeURIComponent(params.sistema)}/modelli/${encodeURIComponent(modello.id)}/listino`, { headers: { Accept: 'application/json' } })).json()
|
||||||
|
: [];
|
||||||
|
if (modello.moduli.includes('listino') && !listino.length)
|
||||||
|
throw new Error(`Nessun listino caricato per il modello ${modello.id}`);
|
||||||
const altezza_maniglia = modello.moduli.includes('altezza_maniglia') ? await (await fetch(`${data_folder}/altezza_maniglia_${modello.categoria}.json?rnd=${Math.random()}`)).json() : [];
|
const altezza_maniglia = modello.moduli.includes('altezza_maniglia') ? await (await fetch(`${data_folder}/altezza_maniglia_${modello.categoria}.json?rnd=${Math.random()}`)).json() : [];
|
||||||
|
|
||||||
let priceCalculator = await MenuBuilder.build(data, 'left-container', modello.cartella_texture || modello.cartella, {
|
let priceCalculator = await MenuBuilder.build(data, 'left-container', modello.cartella_texture || modello.cartella, {
|
||||||
|
|||||||
Reference in New Issue
Block a user