netgescon-master/netgescon-laravel/app/Models/UnitaImmobiliare.php

555 lines
15 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class UnitaImmobiliare extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'unita_immobiliari';
protected $fillable = [
'stabile_id',
'tipo_utilizzo_id',
'numero_interno',
'palazzina',
'piano',
'scala',
'codice_interno',
'codice_catastale',
'foglio',
'particella',
'subalterno',
'categoria',
'classe',
'consistenza',
'rendita_catastale',
'superficie_catastale',
'superficie_commerciale',
'millesimi_proprieta',
'millesimi_riscaldamento',
'millesimi_ascensore',
'millesimi_scale',
'millesimi_pulizie',
'millesimi_acqua',
'millesimi_custom_1',
'millesimi_custom_2',
'millesimi_custom_3',
'nome_custom_1',
'nome_custom_2',
'nome_custom_3',
'stato',
'data_acquisto',
'valore_acquisto',
'note',
'documenti_path',
'attiva',
// Campi legacy per compatibilità
'interno',
'fabbricato',
'categoria_catastale',
'superficie',
'vani',
'indirizzo',
// Nuovi campi avanzati
'superficie_calpestabile',
'superficie_balconi',
'superficie_terrazzi',
'numero_vani',
'numero_bagni',
'numero_balconi',
'classe_energetica',
'anno_costruzione',
'anno_ristrutturazione',
'stato_conservazione',
'necessita_lavori',
'note_tecniche',
'struttura_fisica_id',
'calcolo_automatico_millesimi',
'notifiche_subentri',
'created_by',
'updated_by'
];
protected $casts = [
'data_acquisto' => '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);
}
}