'date', 'valore_acquisto' => 'decimal:2', 'rendita_catastale' => 'decimal:2', 'superficie_catastale' => 'decimal:2', 'superficie_commerciale' => 'decimal:2', 'millesimi_proprieta' => 'decimal:6', 'millesimi_riscaldamento' => 'decimal:6', 'millesimi_ascensore' => 'decimal:6', 'millesimi_scale' => 'decimal:6', 'millesimi_pulizie' => 'decimal:6', 'millesimi_acqua' => 'decimal:6', 'millesimi_custom_1' => 'decimal:6', 'millesimi_custom_2' => 'decimal:6', 'millesimi_custom_3' => 'decimal:6', 'attiva' => 'boolean', // Legacy casts 'superficie' => 'decimal:2', 'vani' => 'decimal:2', // Advanced casts 'superficie_calpestabile' => 'decimal:2', 'superficie_balconi' => 'decimal:2', 'superficie_terrazzi' => 'decimal:2', 'numero_vani' => 'integer', 'numero_bagni' => 'integer', 'numero_balconi' => 'integer', 'anno_costruzione' => 'integer', 'anno_ristrutturazione' => 'integer', 'necessita_lavori' => 'boolean', 'calcolo_automatico_millesimi' => 'boolean', 'notifiche_subentri' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /** * Relazione con Stabile */ public function stabile() { return $this->belongsTo(Stabile::class, 'stabile_id', 'id'); } /** * Relazione con il tipo di utilizzo */ public function tipoUtilizzo() { return $this->belongsTo(TipoUtilizzo::class); } /** * Relazione con i diritti reali */ public function dirittiReali() { return $this->hasMany(DirittoReale::class); } /** * Relazione con i contratti di locazione */ public function contrattiLocazione() { return $this->hasMany(ContrattoLocazione::class); } /** * Relazione con le ripartizioni spese inquilini */ public function ripartizioniSpese() { return $this->hasMany(RipartizioneSpeseInquilini::class); } /** * Relazione con Tickets (legacy) */ public function tickets() { return $this->hasMany(Ticket::class, 'unita_immobiliare_id', 'id'); } /** * Relazione con Proprietà (legacy) */ public function proprieta() { return $this->hasMany(Proprieta::class, 'unita_immobiliare_id', 'id'); } /** * Relazione con DettaglioRipartizioneSpese */ public function dettagliRipartizioneSpese() { return $this->hasMany(DettaglioRipartizioneSpese::class); } /** * Relazione con Rate */ public function rate() { return $this->hasMany(Rata::class); } /** * Rate attive (emesse o parzialmente pagate) */ public function rateAttive() { return $this->hasMany(Rata::class)->whereIn('stato', ['emessa', 'parzialmente_pagata', 'scaduta']); } /** * Rate scadute */ public function rateScadute() { return $this->hasMany(Rata::class)->scadute(); } // === NUOVE RELAZIONI AVANZATE === /** * Relazione con i subentri */ public function subentri() { return $this->hasMany(SubentroUnita::class, 'unita_immobiliare_id', 'id')->orderBy('data_subentro', 'desc'); } /** * Ultimo subentro */ public function ultimoSubentro() { return $this->hasOne(SubentroUnita::class, 'unita_immobiliare_id', 'id')->latest('data_subentro'); } /** * Relazione con la composizione dell'unità */ public function composizione() { return $this->hasMany(ComposizioneUnita::class, 'unita_immobiliare_id', 'id'); } /** * Relazione con le ripartizioni spese specifiche */ public function ripartizioniSpeseSpecifiche() { return $this->hasMany(RipartizioneSpese::class, 'unita_immobiliare_id', 'id'); } /** * Relazione con la struttura fisica dettagliata */ public function strutturaFisica() { return $this->belongsTo(StrutturaFisicaDettaglio::class, 'struttura_fisica_id', 'id'); } /** * Relazione con l'utente che ha creato il record */ public function createdBy() { return $this->belongsTo(User::class, 'created_by', 'id'); } /** * Relazione con l'utente che ha aggiornato il record */ public function updatedBy() { return $this->belongsTo(User::class, 'updated_by', 'id'); } /** * Accessor per il nome completo dell'unità */ public function getNomeCompletoAttribute() { $nome = ''; if ($this->palazzina) { $nome .= "Palazzina {$this->palazzina} - "; } if ($this->scala) { $nome .= "Scala {$this->scala} - "; } if ($this->piano) { $nome .= "Piano {$this->piano} - "; } $numeroInterno = $this->numero_interno ?: $this->interno; $nome .= "Interno {$numeroInterno}"; return $nome; } /** * Accessor per i dati catastali */ public function getDatiCatastaliAttribute() { return [ 'foglio' => $this->foglio, 'particella' => $this->particella, 'subalterno' => $this->subalterno, 'categoria' => $this->categoria ?: $this->categoria_catastale, 'classe' => $this->classe, 'consistenza' => $this->consistenza, 'rendita' => $this->rendita_catastale ]; } /** * Accessor per tutti i millesimi */ public function getMillesimiAttribute() { return [ 'proprieta' => $this->millesimi_proprieta, 'riscaldamento' => $this->millesimi_riscaldamento, 'ascensore' => $this->millesimi_ascensore, 'scale' => $this->millesimi_scale, 'acqua' => $this->millesimi_acqua, 'custom_1' => $this->millesimi_custom_1, 'custom_2' => $this->millesimi_custom_2, 'custom_3' => $this->millesimi_custom_3 ]; } /** * Metodo per ottenere i proprietari attuali */ public function getProprietariAttuali() { return $this->dirittiReali() ->where('tipo_diritto', 'proprieta') ->whereNull('data_fine') ->with('anagraficaCondominiale') ->get(); } /** * Metodo per ottenere gli inquilini attuali */ public function getInquiliniAttuali() { return $this->contrattiLocazione() ->where('stato', 'attivo') ->whereDate('data_inizio', '<=', now()) ->where(function ($query) { $query->whereNull('data_fine') ->orWhereDate('data_fine', '>=', now()); }) ->with('anagraficaCondominiale') ->get(); } /** * Metodo per verificare se l'unità è in locazione */ public function isInLocazione() { return $this->contrattiLocazione() ->where('stato', 'attivo') ->whereDate('data_inizio', '<=', now()) ->where(function ($query) { $query->whereNull('data_fine') ->orWhereDate('data_fine', '>=', now()); }) ->exists(); } /** * Metodo per ottenere la ripartizione spese attuale */ public function getRipartizioneSpese() { return $this->ripartizioniSpese() ->whereDate('data_inizio', '<=', now()) ->where(function ($query) { $query->whereNull('data_fine') ->orWhereDate('data_fine', '>=', now()); }) ->first(); } /** * Accessor per identificazione completa dell'unità (legacy) */ public function getIdentificazioneCompiletaAttribute() { $parts = []; if ($this->fabbricato) $parts[] = 'Fabb. ' . $this->fabbricato; if ($this->scala) $parts[] = 'Scala ' . $this->scala; if ($this->piano) $parts[] = 'Piano ' . $this->piano; if ($this->interno || $this->numero_interno) { $interno = $this->numero_interno ?: $this->interno; $parts[] = 'Int. ' . $interno; } return implode(', ', $parts) ?: 'N/A'; } /** * Accessor per l'indirizzo completo */ public function getIndirizzoCompletoAttribute() { return $this->indirizzo ?: $this->stabile->indirizzo_completo; } // === METODI AVANZATI === /** * Ottieni il proprietario attuale */ public function proprietarioAttuale() { return $this->soggetti() ->wherePivot('tipo_diritto', 'proprietà') ->wherePivot('data_fine', null) ->first(); } /** * Calcola automaticamente i millesimi */ public function calcolaMillesimiAutomatici(): array { if (!$this->calcolo_automatico_millesimi) { return []; } $totaleStabile = $this->stabile->unita()->sum('superficie_commerciale'); if ($totaleStabile <= 0) { return []; } $coefficiente = $this->superficie_commerciale / $totaleStabile * 1000; return [ 'millesimi_proprieta' => round($coefficiente, 4), 'millesimi_riscaldamento' => $this->calcolaMillesimiRiscaldamento(), 'millesimi_ascensore' => $this->calcolaMillesimiAscensore(), 'millesimi_scale' => $this->calcolaMillesimiScale(), 'millesimi_pulizie' => round($coefficiente, 4), // Stesso di proprietà di default ]; } private function calcolaMillesimiRiscaldamento(): float { // Calcolo basato su superficie + coefficienti piano/esposizione $base = $this->superficie_commerciale ?: 0; $coefficientePiano = $this->getCoefficientePiano(); $coefficienteEsposizione = $this->getCoefficieneEsposizione(); $totaleRiscaldamento = $this->stabile->getTotaleMillesimiRiscaldamento(); if ($totaleRiscaldamento <= 0) return 0; return round($base * $coefficientePiano * $coefficienteEsposizione / $totaleRiscaldamento * 1000, 4); } private function calcolaMillesimiAscensore(): float { if ($this->piano <= 0) { return 0; // Piano terra e seminterrati non pagano ascensore } $coefficientePiano = max(1, $this->piano * 0.15 + 0.85); // Crescente per piano $totaleAscensore = $this->stabile->getTotaleMillesimiAscensore(); if ($totaleAscensore <= 0) return 0; return round(($this->superficie_commerciale ?: 0) * $coefficientePiano / $totaleAscensore * 1000, 4); } private function calcolaMillesimiScale(): float { $base = $this->superficie_commerciale ?: 0; $totaleScale = $this->stabile->getTotaleMillesimiScale(); if ($totaleScale <= 0) return 0; if ($this->piano <= 0) { return round($base * 0.5 / $totaleScale * 1000, 4); } return round($base / $totaleScale * 1000, 4); } private function getCoefficientePiano(): float { // Coefficiente per piano (piano terra = 1.0, aumenta con l'altezza) return max(0.8, 1.0 + ($this->piano * 0.1)); } private function getCoefficieneEsposizione(): float { // Placeholder per coefficiente esposizione - implementare logica specifica return 1.0; } /** * Badge color per stato conservazione */ public function getStatoBadgeColor(): string { return match($this->stato_conservazione) { 'ottimo' => 'success', 'buono' => 'info', 'discreto' => 'warning', 'cattivo' => 'danger', default => 'secondary' }; } /** * Genera un subentro automatico */ public function generaSubentroAutomatico(Soggetto $nuovoSoggetto, array $datiSubentro): SubentroUnita { $proprietarioAttuale = $this->proprietarioAttuale(); return $this->subentri()->create([ 'soggetto_precedente_id' => $proprietarioAttuale?->id, 'soggetto_nuovo_id' => $nuovoSoggetto->id, 'data_subentro' => $datiSubentro['data_subentro'], 'tipo_subentro' => $datiSubentro['tipo_subentro'], 'quota_nuova' => $datiSubentro['quota'] ?? 1.0000, 'numero_atto' => $datiSubentro['numero_atto'] ?? null, 'data_atto' => $datiSubentro['data_atto'] ?? null, 'notaio' => $datiSubentro['notaio'] ?? null, 'prezzo_vendita' => $datiSubentro['prezzo_vendita'] ?? null, 'created_by' => auth()->id() ]); } // === SCOPES AVANZATI === /** * Scope per unità con millesimi del tipo specificato */ public function scopeConMillesimi($query, string $tipo = 'proprieta') { return $query->where("millesimi_{$tipo}", '>', 0); } /** * Scope per unità del piano specificato */ public function scopeDelPiano($query, int $piano) { return $query->where('piano', $piano); } /** * Scope per unità con superficie minima */ public function scopeConSuperficieMinima($query, float $minima) { return $query->where('superficie_commerciale', '>=', $minima); } /** * Scope per unità che necessitano lavori */ public function scopeConLavoriNecessari($query) { return $query->where('necessita_lavori', true); } }