'datetime', 'updated_at' => 'datetime', 'anno_costruzione' => 'integer', 'numero_piani' => 'integer', 'numero_unita' => 'integer', 'superficie_totale' => 'decimal:2', 'numero_ascensori' => 'integer', 'numero_garage' => 'integer', 'numero_rate_ordinarie' => 'integer', 'numero_rate_straordinarie' => 'integer', 'presenza_ascensore' => 'boolean', 'presenza_giardino' => 'boolean', 'presenza_piscina' => 'boolean', 'presenza_garage' => 'boolean', 'registro_anagrafe' => 'boolean', 'attivo' => 'boolean', 'mesi_rate_ordinarie' => 'array', 'mesi_rate_straordinarie' => 'array', 'configurazione_default_unita' => 'array', 'struttura_fisica_json' => 'array', 'palazzine_data' => 'array', 'locali_servizio' => 'array', 'configurazione_avanzata' => 'array', 'rate_ordinarie_mesi' => 'array', 'rate_riscaldamento_mesi' => 'array', 'rendita_catastale' => 'decimal:2', 'superficie_catastale' => 'decimal:2', 'fondo_riserva_minimo' => 'decimal:2', 'importo_rata_standard' => 'decimal:2', 'inizio_esercizio' => 'date', 'fine_esercizio' => 'date', 'data_nomina' => 'date', 'scadenza_mandato' => 'date', 'ultima_generazione_unita' => 'datetime', ]; public function hasOperationalHeating(): bool { if ((bool) ($this->riscaldamento_centralizzato ?? false)) { return true; } if ((bool) ($this->millesimi_riscaldamento_separati ?? false)) { return true; } $rateRiscaldamento = $this->rate_riscaldamento_mesi ?? []; if (is_string($rateRiscaldamento)) { $rateRiscaldamento = json_decode($rateRiscaldamento, true); } if (is_array($rateRiscaldamento) && $rateRiscaldamento !== []) { return true; } return (bool) data_get($this->getOperationalConfiguration(), 'mostra_riscaldamento', false); } public function getOperationalConfiguration(): array { return is_array($this->configurazione_avanzata ?? null) ? $this->configurazione_avanzata : []; } public function getTakeoverDate(): ?Carbon { $raw = data_get($this->getOperationalConfiguration(), 'amministrazione.presa_in_carico_dal'); if (is_string($raw) && trim($raw) !== '') { try { return Carbon::parse($raw); } catch (\Throwable) { // ignore invalid stored values } } return $this->data_nomina instanceof Carbon ? $this->data_nomina : null; } public function isOperationalYearVisible(?int $year): bool { if (! is_int($year) || $year <= 0) { return true; } $takeoverDate = $this->getTakeoverDate(); if (! $takeoverDate) { return true; } return $year >= (int) $takeoverDate->year; } public function getNominaVerbaleDocumentoId(): ?int { $id = data_get($this->getOperationalConfiguration(), 'amministrazione.verbale_nomina_documento_id'); return is_numeric($id) && (int) $id > 0 ? (int) $id : null; } /** * Relazione con Amministratore */ public function amministratore() { return $this->belongsTo(Amministratore::class, 'amministratore_id', 'id'); } public function amministratoreTransfers(): HasMany { return $this->hasMany(StabileAmministratoreTransfer::class, 'stabile_id', 'id'); } public function latestAmministratoreTransfer(): HasOne { return $this->hasOne(StabileAmministratoreTransfer::class, 'stabile_id', 'id')->latestOfMany(); } /** * Rubrica universale collegata (opzionale) */ public function rubrica() { return $this->belongsTo(RubricaUniversale::class, 'rubrica_id', 'id'); } /** * Relazione con UnitaImmobiliari */ public function unitaImmobiliari() { return $this->hasMany(UnitaImmobiliare::class, 'stabile_id', 'id'); } /** * Relazione con Tickets */ public function tickets() { return $this->hasMany(Ticket::class, 'stabile_id', 'id'); } public function insurancePolicies() { return $this->hasMany(InsurancePolicy::class, 'stabile_id', 'id') ->orderByDesc('renewal_at') ->orderByDesc('expires_at') ->orderByDesc('id'); } /** * Relazione con RipartizioneSpese */ public function ripartizioniSpese() { return $this->hasMany(RipartizioneSpese::class); } /** * Relazione con VociSpesa */ public function vociSpesa() { return $this->hasMany(VoceSpesa::class, 'stabile_id', 'id')->orderBy('ordinamento')->orderBy('descrizione'); } /** * Relazione con TabelleMillesimali */ public function tabelleMillesimali() { return $this->hasMany(TabellaMillesimale::class, 'stabile_id', 'id')->orderBy('ordinamento')->orderBy('nome_tabella'); } /** * Relazione con Gestioni Contabili (per CRUD nella vista dello stabile) */ public function gestioniContabili() { return $this->hasMany(GestioneContabile::class, 'stabile_id', 'id') ->orderByDesc('anno_gestione') ->orderBy('tipo_gestione') ->orderBy('numero_straordinaria'); } /** * Relazione con Contratti Hub */ public function contrattiHub() { return $this->hasMany(StabileContratto::class, 'stabile_id', 'id') ->orderByDesc('decorrenza_dal') ->orderByDesc('id'); } /** * Relazione con PianoRateizzazione */ public function pianiRateizzazione() { return $this->hasMany(PianoRateizzazione::class); } /** * Scope per stabili attivi */ public function scopeAttivi($query) { return $query->where('attivo', true); } /** * Scope per stabili per amministratore */ public function scopeByAmministratore($query, $amministratoreId) { return $query->where('amministratore_id', $amministratoreId); } /** * Accessor per il nome completo dell'indirizzo */ public function getIndirizzoCompletoAttribute() { return $this->indirizzo . ', ' . $this->cap . ' ' . $this->citta . ($this->provincia ? ' (' . $this->provincia . ')' : ''); } /** * Accessor per i dati catastali */ public function getDatiCatastaliAttribute() { $codiceComune = $this->codice_comune_catasto ?? $this->codice_catastale_comune; $foglio = $this->foglio ?? $this->foglio_catasto; $particella = $this->particella ?? $this->particella_catasto ?? $this->mappale; $categoria = $this->categoria_catastale ?? $this->categoria; return [ 'codice_comune' => $codiceComune, 'foglio' => $foglio, 'particella' => $particella, 'subalterno' => $this->subalterno, 'sezione' => $this->sezione, 'categoria' => $categoria, 'classe' => $this->classe, 'consistenza' => $this->consistenza, 'rendita' => $this->rendita_catastale, 'superficie' => $this->superficie_catastale, ]; } /** * Accessor per i dati SDI */ public function getDatiSdiAttribute() { return [ 'codice_destinatario' => $this->codice_destinatario_sdi, 'pec_amministratore' => $this->pec_amministratore, 'pec_condominio' => $this->pec_condominio, ]; } /** * Accessor per la configurazione rate ordinarie */ public function getConfigurazioneRateOrdinarieAttribute() { return [ 'numero_rate' => $this->numero_rate_ordinarie, 'mesi' => $this->mesi_rate_ordinarie, ]; } /** * Accessor per la configurazione rate straordinarie */ public function getConfigurazioneRateStraordinarieAttribute() { return [ 'numero_rate' => $this->numero_rate_straordinarie, 'mesi' => $this->mesi_rate_straordinarie, ]; } /** * Accessor per le caratteristiche dello stabile */ public function getCaratteristicheAttribute() { return [ 'anno_costruzione' => $this->anno_costruzione, 'numero_piani' => $this->numero_piani, 'numero_unita' => $this->numero_unita, 'superficie_totale' => $this->superficie_totale, 'tipo_riscaldamento' => $this->tipo_riscaldamento, 'tipo_acqua' => $this->tipo_acqua, 'presenza_ascensore' => $this->presenza_ascensore, 'numero_ascensori' => $this->numero_ascensori, 'presenza_giardino' => $this->presenza_giardino, 'presenza_piscina' => $this->presenza_piscina, 'presenza_garage' => $this->presenza_garage, 'numero_garage' => $this->numero_garage, ]; } /** * Metodo per ottenere tutte le anagrafiche associate */ public function getAnagraficheAssociate() { return AnagraficaCondominiale::whereHas('dirittiReali.unitaImmobiliare', function ($query) { $query->where('stabile_id', $this->id); })->orWhereHas('contrattiLocazione.unitaImmobiliare', function ($query) { $query->where('stabile_id', $this->id); })->distinct()->get(); } /** * Metodo per ottenere i proprietari dello stabile */ public function getProprietari() { return AnagraficaCondominiale::whereHas('dirittiReali', function ($query) { $query->where('tipo_diritto', 'proprieta') ->whereNull('data_fine') ->whereHas('unitaImmobiliare', function ($q) { $q->where('stabile_id', $this->id); }); })->distinct()->get(); } /** * Metodo per ottenere gli inquilini dello stabile */ public function getInquilini() { return AnagraficaCondominiale::whereHas('contrattiLocazione', function ($query) { $query->where('stato', 'attivo') ->whereDate('data_inizio', '<=', now()) ->where(function ($q) { $q->whereNull('data_fine') ->orWhereDate('data_fine', '>=', now()); }) ->whereHas('unitaImmobiliare', function ($q) { $q->where('stabile_id', $this->id); }); })->distinct()->get(); } /** * Metodo per calcolare il totale millesimi */ public function getTotaleMillesimi($tipo = 'proprieta') { $campo = 'millesimi_' . $tipo; return $this->unitaImmobiliari()->where('attiva', true)->sum($campo); } /** * Metodo per verificare la coerenza dei millesimi */ public function verificaMillesimi($tipo = 'proprieta') { $totale = $this->getTotaleMillesimi($tipo); return abs($totale - 1000) < 0.001; // Tolleranza per errori di arrotondamento } /** * Metodo per ottenere le statistiche dello stabile */ public function getStatistiche() { $unita = $this->unitaImmobiliari()->where('attiva', true); return [ 'totale_unita' => $unita->count(), 'unita_in_locazione' => $unita->whereHas('contrattiLocazione', function ($query) { $query->where('stato', 'attivo') ->whereDate('data_inizio', '<=', now()) ->where(function ($q) { $q->whereNull('data_fine') ->orWhereDate('data_fine', '>=', now()); }); })->count(), 'superficie_totale' => $unita->sum('superficie_commerciale'), 'totale_millesimi_proprieta' => $this->getTotaleMillesimi('proprieta'), 'totale_proprietari' => $this->getProprietari()->count(), 'totale_inquilini' => $this->getInquilini()->count(), ]; } /** * Relazione con dati bancari */ public function datiBancari() { return $this->hasMany(DatiBancari::class, 'stabile_id'); } /** * Relazione con documenti collegati */ public function documentiCollegati() { return $this->hasMany(DocumentoCollegato::class, 'stabile_id'); } /** * Relazione con Palazzine */ public function palazzine() { return $this->hasMany(Palazzina::class, 'stabile_id'); } /** * Relazione con Palazzine attive */ public function palazzineAttive() { return $this->palazzine()->where('attiva', true); } /** * Relazione con Gestioni Condominiali */ public function gestioniCondominiali() { return $this->hasMany(\App\Models\GestioneCondominiale::class, 'stabile_id'); } /** * Relazione con Gestioni Condominiali attive */ public function gestioniAttive() { return $this->gestioniCondominiali()->where('stato', 'ATTIVA'); } /** * Sincronizza o crea il contatto dello stabile nella Rubrica Universale. */ public function syncRubricaContatto(): RubricaUniversale { $rubrica = $this->rubrica; $data = [ 'amministratore_id' => $this->amministratore_id, 'codice_univoco' => substr('ST-' . $this->codice_stabile, 0, 8), 'categoria' => 'condomino', // Usiamo condomino o altra categoria gestita 'tipo_contatto' => 'persona_giuridica', 'ragione_sociale' => $this->denominazione, 'cognome' => null, 'nome' => null, 'codice_fiscale' => $this->codice_fiscale, 'indirizzo' => $this->indirizzo, 'citta' => $this->citta, 'cap' => $this->cap, 'provincia' => $this->provincia, 'stato' => 'attivo', ]; if ($rubrica) { $rubrica->update($data); } else { $rubrica = RubricaUniversale::create($data); $this->update(['rubrica_id' => $rubrica->id]); } return $rubrica; } /** * Collaboratori assegnati allo stabile (tramite pivot) */ public function collaboratori() { return $this->belongsToMany(User::class, 'collaboratore_stabile', 'stabile_id', 'user_id') ->withPivot(['ruolo', 'permessi']) ->withTimestamps(); } }