MAJOR IMPLEMENTATION COMPLETED: ✅ Modern database structure with Laravel best practices ✅ Complete Eloquent relationships (Amministratore→Stabili→Movements) ✅ 8-character alphanumeric codes system (ADM, ANA, MOV, ALL prefixes) ✅ Multi-database architecture for administrators ✅ Complete property management (anagrafica_condominiale, diritti_reali, contratti) ✅ Distribution system for multi-server deployment ✅ Universal responsive UI with permission-based sidebar NEW MODELS & MIGRATIONS: - AnagraficaCondominiale: Complete person/entity management - ContattoAnagrafica: Multi-contact system with usage flags - DirittoReale: Property rights with quotas and percentages - ContrattoLocazione: Rental contracts with landlord/tenant - TipoUtilizzo: Property usage types (residential, commercial, etc.) - Enhanced Stabile: Cadastral data, SDI, rate configuration - Enhanced UnitaImmobiliare: Modern structure with backward compatibility SERVICES & CONTROLLERS: - DistributionService: Multi-server deployment and migration - FileManagerController: Administrator folder management - DistributionController: API for server-to-server communication - MultiDatabaseService: Dynamic database connections READY FOR PRODUCTION: ✅ Database schema: Complete and tested ✅ Models relationships: All working and verified ✅ Code generation: Automatic 8-char codes implemented ✅ Testing: Successful data creation confirmed ✅ Documentation: Complete internal technical docs NEXT PHASE: Millésimal tables, expense categories, cost distribution engine
301 lines
7.8 KiB
PHP
301 lines
7.8 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_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'
|
|
];
|
|
|
|
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_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',
|
|
'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');
|
|
}
|
|
|
|
/**
|
|
* Scope per unità attive
|
|
*/
|
|
public function scopeAttive($query)
|
|
{
|
|
return $query->where('attiva', true);
|
|
}
|
|
|
|
/**
|
|
* Scope per tipo di utilizzo
|
|
*/
|
|
public function scopeByTipoUtilizzo($query, $tipoUtilizzo)
|
|
{
|
|
return $query->whereHas('tipoUtilizzo', function ($q) use ($tipoUtilizzo) {
|
|
$q->where('codice', $tipoUtilizzo);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Scope per stabile
|
|
*/
|
|
public function scopeByStabile($query, $stabileId)
|
|
{
|
|
return $query->where('stabile_id', $stabileId);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
} |