'decimal:2', 'saldo_minimo' => 'decimal:2', 'percentuale_accantonamento' => 'decimal:2', 'attivo' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime' ]; /** * Tipi fondo disponibili */ public const TIPI_FONDO = [ 'ordinario' => 'Fondo Ordinario', 'riserva' => 'Fondo di Riserva', 'ascensore' => 'Fondo Ascensore', 'riscaldamento' => 'Fondo Riscaldamento', 'facciata_tetto' => 'Fondo Facciata/Tetto', 'verde_giardini' => 'Fondo Verde/Giardini', 'sicurezza' => 'Fondo Sicurezza/Videosorveglianza', 'innovazione' => 'Fondo Innovazione Tecnologica', 'investimenti' => 'Fondo Investimenti Spazi Comuni', 'personalizzato' => 'Fondo Personalizzato' ]; /** * Priorità fondi (per ordinamento) */ public const PRIORITA_FONDI = [ 'ordinario' => 1, 'riserva' => 2, 'ascensore' => 3, 'riscaldamento' => 4, 'facciata_tetto' => 5, 'verde_giardini' => 6, 'sicurezza' => 7, 'innovazione' => 8, 'investimenti' => 9, 'personalizzato' => 10 ]; /** * Relazione con Stabile */ public function stabile() { return $this->belongsTo(Stabile::class, 'stabile_id'); } /** * Relazione con MovimentiContabili */ public function movimenti() { return $this->hasMany(MovimentoContabile::class, 'fondo_id'); } /** * Accessor per tipo fondo descrizione */ public function getTipoFondoDescrizioneAttribute() { return self::TIPI_FONDO[$this->tipo_fondo] ?? $this->tipo_fondo; } /** * Accessor per priorità ordinamento */ public function getPrioritaAttribute() { return self::PRIORITA_FONDI[$this->tipo_fondo] ?? 999; } /** * Accessor per badge class tipo fondo */ public function getTipoFondoBadgeClassAttribute() { return match($this->tipo_fondo) { 'ordinario' => 'bg-primary', 'riserva' => 'bg-success', 'ascensore', 'riscaldamento', 'facciata_tetto' => 'bg-warning', 'verde_giardini', 'sicurezza' => 'bg-info', 'innovazione', 'investimenti' => 'bg-purple', 'personalizzato' => 'bg-secondary', default => 'bg-secondary' }; } /** * Accessor per stato saldo (critico, normale, ottimale) */ public function getStatoSaldoAttribute() { if ($this->saldo_attuale < $this->saldo_minimo) { return 'critico'; } elseif ($this->saldo_attuale < ($this->saldo_minimo * 1.5)) { return 'normale'; } else { return 'ottimale'; } } /** * Accessor per stato saldo badge class */ public function getStatoSaldoBadgeClassAttribute() { return match($this->stato_saldo) { 'critico' => 'bg-danger', 'normale' => 'bg-warning', 'ottimale' => 'bg-success', default => 'bg-secondary' }; } /** * Scope per fondi attivi */ public function scopeAttivi($query) { return $query->where('attivo', true); } /** * Scope per tipo fondo */ public function scopePerTipo($query, $tipo) { return $query->where('tipo_fondo', $tipo); } /** * Scope ordinati per priorità */ public function scopeOrdinatiPerPriorita($query) { return $query->orderByRaw("FIELD(tipo_fondo, 'ordinario', 'riserva', 'ascensore', 'riscaldamento', 'facciata_tetto', 'verde_giardini', 'sicurezza', 'innovazione', 'investimenti', 'personalizzato')"); } /** * Calcola l'accantonamento necessario per raggiungere il saldo minimo */ public function calcolaAccantonamentoNecessario() { $differenza = $this->saldo_minimo - $this->saldo_attuale; return max(0, $differenza); } /** * Aggiorna il saldo del fondo */ public function aggiornaSaldo($importo, $descrizione = null) { $this->saldo_attuale += $importo; $this->save(); // Registra il movimento se richiesto if ($descrizione) { $this->movimenti()->create([ 'descrizione' => $descrizione, 'importo' => $importo, 'data_movimento' => now() ]); } return $this; } /** * Verifica se il fondo è sotto la soglia minima */ public function isSaldoCritico() { return $this->saldo_attuale < $this->saldo_minimo; } /** * Boot method per eventi model */ protected static function boot() { parent::boot(); static::creating(function ($fondo) { // Se non specificata, genera denominazione automatica if (empty($fondo->denominazione)) { $fondo->denominazione = self::TIPI_FONDO[$fondo->tipo_fondo] ?? 'Fondo Personalizzato'; } }); } }