'Bianco (finitura predefinita su interno ed esterno)', 'uniforme' => 'Uniforme (stessa finitura su interno ed esterno)', 'bicolore' => 'Bicolore (finiture diverse)', ]; public const SUPPLEMENTI = [ 'percentuale' => 'Percentuale sul prezzo base', 'fisso' => 'Importo fisso (€)', 'metro_lineare' => 'Al metro lineare (€/ml)', ]; protected static array $immagini = ['icona']; protected $fillable = [ 'sistema_id', 'modello_id', 'gruppo', 'valore', 'etichetta', 'icona', 'alternativa', 'supplemento_tipo', 'supplemento', 'ml_per_larghezza', 'ml_per_altezza', 'ml_aggiunta_mm', 'parametri', 'ordine', 'attivo', ]; protected $casts = [ 'supplemento' => 'float', 'ml_per_larghezza' => 'float', 'ml_per_altezza' => 'float', 'ml_aggiunta_mm' => 'float', 'parametri' => 'array', 'ordine' => 'integer', 'attivo' => 'boolean', ]; public function sistema(): BelongsTo { return $this->belongsTo(Sistema::class); } public function modello(): BelongsTo { return $this->belongsTo(Modello::class); } public function definizione(): BelongsTo { return $this->belongsTo(GruppoOpzioni::class, 'gruppo', 'codice'); } public function scopeDelSistema(Builder $query): Builder { return $query->whereNull('modello_id'); } /** * Opzioni attive valide per il modello, per gruppo: quelle del modello dove ne ha, * altrimenti quelle del sistema. * * @return Collection> */ public static function perModello(Modello $modello): Collection { $righe = static::query() ->where('sistema_id', $modello->sistema_id) ->where(fn (Builder $q) => $q->whereNull('modello_id')->orWhere('modello_id', $modello->getKey())) ->orderBy('ordine') ->orderBy('id') ->get(); // Basta una riga del modello (anche disattiva) per sostituire il gruppo. $personalizzati = $righe->whereNotNull('modello_id')->pluck('gruppo')->unique(); return $righe ->filter(fn (Opzione $o) => $o->attivo && ($o->modello_id !== null) === $personalizzati->contains($o->gruppo)) ->groupBy('gruppo'); } public function descriviSupplemento(): string { if (! $this->supplemento_tipo || ! $this->supplemento) { return '—'; } $valore = number_format($this->supplemento, 2, ',', '.'); return match ($this->supplemento_tipo) { 'percentuale' => "{$valore}%", 'metro_lineare' => "€ {$valore}/ml × ".$this->formulaMetri(), default => "€ {$valore}", }; } /** Metri lineari per un serramento L x H (mm), senza arrotondamenti. */ public function metriLineari(float $larghezza, float $altezza): float { return max(0, $larghezza * $this->ml_per_larghezza + $altezza * $this->ml_per_altezza + $this->ml_aggiunta_mm) / 1000; } /** Importo del supplemento sul prezzo base, per un serramento L x H (mm). */ public function importo(float $base, float $larghezza, float $altezza): float { return match ($this->supplemento_tipo) { 'percentuale' => $base * $this->supplemento / 100, 'metro_lineare' => $this->supplemento * $this->metriLineari($larghezza, $altezza), 'fisso' => $this->supplemento, default => 0.0, }; } /** Es. "(H x 2 + 200 mm)". */ public function formulaMetri(): string { $termini = array_filter([ $this->ml_per_larghezza ? 'L'.($this->ml_per_larghezza != 1 ? ' × '.(float) $this->ml_per_larghezza : '') : null, $this->ml_per_altezza ? 'H'.($this->ml_per_altezza != 1 ? ' × '.(float) $this->ml_per_altezza : '') : null, $this->ml_aggiunta_mm ? (float) $this->ml_aggiunta_mm.' mm' : null, ]); return '('.(implode(' + ', $termini) ?: '0').')'; } }