netgescon-day0/app/Models/Stabile.php

619 lines
19 KiB
PHP
Executable File

<?php
namespace App\Models;
use App\Models\RubricaUniversale;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Carbon;
class Stabile extends Model
{
use HasFactory;
protected $table = 'stabili';
// Primary key di default ('id')
protected $fillable = [
'amministratore_id',
'codice_stabile',
'denominazione',
'codice_fiscale',
'cod_fisc_amministratore',
'indirizzo',
'citta',
'cap',
'provincia',
'note',
'old_id',
'stato',
// Dati catastali
'codice_comune_catasto',
'foglio',
'foglio_catasto',
'particella',
'particella_catasto',
'mappale',
'subalterno',
'sezione',
'categoria',
'categoria_catastale',
'classe',
'consistenza',
'rendita_catastale',
'superficie_catastale',
// Dati amministrativi e fiscali aggiuntivi
'codice_destinatario_sdi',
'pec_amministratore',
'pec_condominio',
'configurazione_default_unita',
'modalita_rateizzazione',
'inizio_esercizio',
'fine_esercizio',
// Dati bancari principali
'iban_principale',
'banca_principale',
'filiale',
'iban_secondario',
'banca_secondaria',
'filiale_secondaria',
// Info amministratore e mandato
'amministratore_nome',
'amministratore_email',
'data_nomina',
'scadenza_mandato',
// Configurazioni strutturali e caratteristiche
'struttura_fisica_json',
'numero_palazzine',
'numero_scale_per_palazzina',
'numero_piani',
'piano_seminterrato',
'piano_sottotetto',
'presenza_ascensore',
'cortile_giardino',
'superficie_cortile',
'riscaldamento_centralizzato',
'acqua_centralizzata',
'gas_centralizzato',
'servizio_portineria',
'orari_portineria',
'videocitofono',
'antenna_tv_centralizzata',
'internet_condominiale',
'palazzine_data',
'locali_servizio',
// Configurazioni economiche e rate
'fondo_riserva_minimo',
'importo_rata_standard',
'frequenza_rate',
'giorno_scadenza_rate',
'iban_condominio',
'numero_rate_ordinarie',
'mesi_rate_ordinarie',
'numero_rate_straordinarie',
'mesi_rate_straordinarie',
'configurazione_avanzata',
'ultima_generazione_unita',
'rate_ordinarie_mesi',
'rate_riscaldamento_mesi',
'descrizione_rate',
// Indicatori vari
'millesimi_generali_calcolati',
'millesimi_riscaldamento_separati',
'millesimi_acqua_separati',
'millesimi_ascensore_separati',
// Altri campi legacy
'anno_costruzione',
'numero_unita',
'superficie_totale',
'tipo_riscaldamento',
'tipo_acqua',
'numero_ascensori',
'presenza_giardino',
'presenza_piscina',
'presenza_garage',
'numero_garage',
'codice_interno',
'registro_anagrafe',
'documenti_path',
'attivo',
'rubrica_id',
];
protected $casts = [
'created_at' => '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 && ! empty($data['codice_univoco'])) {
$existing = RubricaUniversale::where('codice_univoco', $data['codice_univoco'])->first();
if ($existing) {
$rubrica = $existing;
$this->update(['rubrica_id' => $rubrica->id]);
}
}
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();
}
}