832 lines
28 KiB
Markdown
832 lines
28 KiB
Markdown
# 🏠 TABELLA UNITA IMMOBILIARI
|
|
## Appartamenti, locali e box collegati agli stabili
|
|
|
|
---
|
|
|
|
## 🔑 **CONNESSIONE DATABASE**
|
|
|
|
### **Credenziali NetGescon**:
|
|
```bash
|
|
# Connessione MySQL
|
|
mysql -u netgescon_user -p netgescon
|
|
# Password: NetGescon2024!
|
|
|
|
# Test connessione Laravel
|
|
cd /var/www/netgescon
|
|
php artisan tinker
|
|
DB::connection()->getPdo();
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 **CARATTERISTICHE TABELLA**
|
|
|
|
### **Scopo**:
|
|
Tabella per tutti gli appartamenti, locali, box e cantine appartenenti agli stabili
|
|
|
|
### **Principio**:
|
|
**Collegamento via stabile** - ogni unità appartiene a uno stabile → isolamento automatico per amministratore
|
|
|
|
---
|
|
|
|
## 🗄️ **STRUTTURA TABELLA**
|
|
|
|
```sql
|
|
CREATE TABLE unita_immobiliari (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
stabile_id BIGINT UNSIGNED NOT NULL, -- FK verso stabili
|
|
|
|
-- Identificazione unità
|
|
interno VARCHAR(20) NOT NULL, -- A1, B12, Box 5, ecc
|
|
piano INT NULL, -- Piano (0=terra, -1=seminterrato)
|
|
scala VARCHAR(10) NULL, -- A, B, C
|
|
denominazione VARCHAR(255) NULL, -- Appartamento al 2° piano
|
|
codice_interno VARCHAR(50) NULL, -- Codice amministratore
|
|
|
|
-- Tipologia unità
|
|
tipo_unita ENUM('appartamento', 'locale_commerciale', 'box', 'cantina', 'mansarda', 'soffitta', 'altro') DEFAULT 'appartamento',
|
|
destinazione_uso ENUM('abitazione', 'commerciale', 'ufficio', 'deposito', 'altro') DEFAULT 'abitazione',
|
|
|
|
-- Dati catastali
|
|
foglio VARCHAR(20) NULL,
|
|
particella VARCHAR(20) NULL,
|
|
subalterno VARCHAR(20) NOT NULL, -- Obbligatorio per unità
|
|
categoria_catastale VARCHAR(10) NULL, -- A/2, A/3, C/1, ecc
|
|
classe VARCHAR(10) NULL,
|
|
consistenza VARCHAR(50) NULL, -- Vani, mq, mc
|
|
superficie_commerciale DECIMAL(8, 2) NULL, -- mq
|
|
superficie_calpestabile DECIMAL(8, 2) NULL, -- mq
|
|
rendita_catastale DECIMAL(8, 2) NULL, -- Euro
|
|
|
|
-- Caratteristiche fisiche
|
|
numero_vani INT NULL,
|
|
numero_bagni INT NULL,
|
|
numero_balconi INT NULL,
|
|
has_terrazzo BOOLEAN DEFAULT FALSE,
|
|
has_giardino BOOLEAN DEFAULT FALSE,
|
|
has_cantina BOOLEAN DEFAULT FALSE,
|
|
has_box BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Millesimi
|
|
millesimi_generali DECIMAL(8, 4) DEFAULT 0.0000, -- /1000
|
|
millesimi_riscaldamento DECIMAL(8, 4) DEFAULT 0.0000,
|
|
millesimi_ascensore DECIMAL(8, 4) DEFAULT 0.0000,
|
|
millesimi_scala DECIMAL(8, 4) DEFAULT 0.0000,
|
|
millesimi_personalizzati JSON NULL, -- Altri tipi millesimi
|
|
|
|
-- Stato occupazione
|
|
stato_occupazione ENUM('libera', 'occupata_proprietario', 'occupata_inquilino', 'sfitto', 'vendita') DEFAULT 'libera',
|
|
data_ultimo_cambio_stato DATE NULL,
|
|
|
|
-- Configurazioni
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
note TEXT NULL,
|
|
configurazioni JSON NULL, -- Config specifiche unità
|
|
|
|
-- Audit
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
-- Foreign Keys
|
|
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
|
|
|
|
-- Indici
|
|
INDEX idx_stabile_id (stabile_id),
|
|
INDEX idx_interno (interno),
|
|
INDEX idx_piano (piano),
|
|
INDEX idx_tipo_unita (tipo_unita),
|
|
INDEX idx_categoria_catastale (categoria_catastale),
|
|
INDEX idx_stato_occupazione (stato_occupazione),
|
|
INDEX idx_active (is_active),
|
|
INDEX idx_subalterno (subalterno),
|
|
|
|
-- Indici composti
|
|
INDEX idx_stabile_interno (stabile_id, interno),
|
|
INDEX idx_stabile_piano (stabile_id, piano),
|
|
INDEX idx_catastale (foglio, particella, subalterno),
|
|
|
|
-- Vincolo unicità: interno univoco per stabile
|
|
UNIQUE KEY unique_stabile_interno (stabile_id, interno),
|
|
-- Vincolo unicità: subalterno univoco per stabile
|
|
UNIQUE KEY unique_stabile_subalterno (stabile_id, subalterno)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ **MIGRATION LARAVEL**
|
|
|
|
### **File**: `database/migrations/2024_08_05_000030_create_unita_immobiliari_table.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
return new class extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
Schema::create('unita_immobiliari', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->foreignId('stabile_id')
|
|
->constrained('stabili')
|
|
->onDelete('cascade');
|
|
|
|
// Identificazione unità
|
|
$table->string('interno', 20);
|
|
$table->integer('piano')->nullable();
|
|
$table->string('scala', 10)->nullable();
|
|
$table->string('denominazione')->nullable();
|
|
$table->string('codice_interno', 50)->nullable();
|
|
|
|
// Tipologia unità
|
|
$table->enum('tipo_unita', [
|
|
'appartamento', 'locale_commerciale', 'box',
|
|
'cantina', 'mansarda', 'soffitta', 'altro'
|
|
])->default('appartamento');
|
|
$table->enum('destinazione_uso', [
|
|
'abitazione', 'commerciale', 'ufficio', 'deposito', 'altro'
|
|
])->default('abitazione');
|
|
|
|
// Dati catastali
|
|
$table->string('foglio', 20)->nullable();
|
|
$table->string('particella', 20)->nullable();
|
|
$table->string('subalterno', 20);
|
|
$table->string('categoria_catastale', 10)->nullable();
|
|
$table->string('classe', 10)->nullable();
|
|
$table->string('consistenza', 50)->nullable();
|
|
$table->decimal('superficie_commerciale', 8, 2)->nullable();
|
|
$table->decimal('superficie_calpestabile', 8, 2)->nullable();
|
|
$table->decimal('rendita_catastale', 8, 2)->nullable();
|
|
|
|
// Caratteristiche fisiche
|
|
$table->integer('numero_vani')->nullable();
|
|
$table->integer('numero_bagni')->nullable();
|
|
$table->integer('numero_balconi')->nullable();
|
|
$table->boolean('has_terrazzo')->default(false);
|
|
$table->boolean('has_giardino')->default(false);
|
|
$table->boolean('has_cantina')->default(false);
|
|
$table->boolean('has_box')->default(false);
|
|
|
|
// Millesimi
|
|
$table->decimal('millesimi_generali', 8, 4)->default(0.0000);
|
|
$table->decimal('millesimi_riscaldamento', 8, 4)->default(0.0000);
|
|
$table->decimal('millesimi_ascensore', 8, 4)->default(0.0000);
|
|
$table->decimal('millesimi_scala', 8, 4)->default(0.0000);
|
|
$table->json('millesimi_personalizzati')->nullable();
|
|
|
|
// Stato occupazione
|
|
$table->enum('stato_occupazione', [
|
|
'libera', 'occupata_proprietario', 'occupata_inquilino', 'sfitto', 'vendita'
|
|
])->default('libera');
|
|
$table->date('data_ultimo_cambio_stato')->nullable();
|
|
|
|
// Configurazioni
|
|
$table->boolean('is_active')->default(true);
|
|
$table->text('note')->nullable();
|
|
$table->json('configurazioni')->nullable();
|
|
|
|
$table->timestamps();
|
|
|
|
// Indici
|
|
$table->index(['stabile_id']);
|
|
$table->index(['interno']);
|
|
$table->index(['piano']);
|
|
$table->index(['tipo_unita']);
|
|
$table->index(['categoria_catastale']);
|
|
$table->index(['stato_occupazione']);
|
|
$table->index(['is_active']);
|
|
$table->index(['subalterno']);
|
|
|
|
// Indici composti
|
|
$table->index(['stabile_id', 'interno'], 'idx_stabile_interno');
|
|
$table->index(['stabile_id', 'piano'], 'idx_stabile_piano');
|
|
$table->index(['foglio', 'particella', 'subalterno'], 'idx_catastale');
|
|
|
|
// Vincoli unicità
|
|
$table->unique(['stabile_id', 'interno'], 'unique_stabile_interno');
|
|
$table->unique(['stabile_id', 'subalterno'], 'unique_stabile_subalterno');
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('unita_immobiliari');
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **MODEL ELOQUENT**
|
|
|
|
### **File**: `app/Models/UnitaImmobiliare.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class UnitaImmobiliare extends Model
|
|
{
|
|
protected $fillable = [
|
|
'stabile_id',
|
|
'interno',
|
|
'piano',
|
|
'scala',
|
|
'denominazione',
|
|
'codice_interno',
|
|
'tipo_unita',
|
|
'destinazione_uso',
|
|
'foglio',
|
|
'particella',
|
|
'subalterno',
|
|
'categoria_catastale',
|
|
'classe',
|
|
'consistenza',
|
|
'superficie_commerciale',
|
|
'superficie_calpestabile',
|
|
'rendita_catastale',
|
|
'numero_vani',
|
|
'numero_bagni',
|
|
'numero_balconi',
|
|
'has_terrazzo',
|
|
'has_giardino',
|
|
'has_cantina',
|
|
'has_box',
|
|
'millesimi_generali',
|
|
'millesimi_riscaldamento',
|
|
'millesimi_ascensore',
|
|
'millesimi_scala',
|
|
'millesimi_personalizzati',
|
|
'stato_occupazione',
|
|
'data_ultimo_cambio_stato',
|
|
'is_active',
|
|
'note',
|
|
'configurazioni',
|
|
];
|
|
|
|
protected $casts = [
|
|
'piano' => 'integer',
|
|
'superficie_commerciale' => 'decimal:2',
|
|
'superficie_calpestabile' => 'decimal:2',
|
|
'rendita_catastale' => 'decimal:2',
|
|
'numero_vani' => 'integer',
|
|
'numero_bagni' => 'integer',
|
|
'numero_balconi' => 'integer',
|
|
'has_terrazzo' => 'boolean',
|
|
'has_giardino' => 'boolean',
|
|
'has_cantina' => 'boolean',
|
|
'has_box' => 'boolean',
|
|
'millesimi_generali' => 'decimal:4',
|
|
'millesimi_riscaldamento' => 'decimal:4',
|
|
'millesimi_ascensore' => 'decimal:4',
|
|
'millesimi_scala' => 'decimal:4',
|
|
'millesimi_personalizzati' => 'array',
|
|
'data_ultimo_cambio_stato' => 'date',
|
|
'is_active' => 'boolean',
|
|
'configurazioni' => 'array',
|
|
];
|
|
|
|
// Relazioni
|
|
public function stabile(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Stabile::class);
|
|
}
|
|
|
|
public function soggetti(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(Soggetto::class, 'unita_soggetti')
|
|
->withPivot([
|
|
'tipo_rapporto',
|
|
'quota_proprieta',
|
|
'data_inizio',
|
|
'data_fine',
|
|
'is_active'
|
|
])
|
|
->withTimestamps();
|
|
}
|
|
|
|
public function proprietari(): BelongsToMany
|
|
{
|
|
return $this->soggetti()->wherePivot('tipo_rapporto', 'proprietario');
|
|
}
|
|
|
|
public function inquilini(): BelongsToMany
|
|
{
|
|
return $this->soggetti()->wherePivot('tipo_rapporto', 'inquilino');
|
|
}
|
|
|
|
public function contabilitaRegistrazioni(): HasMany
|
|
{
|
|
return $this->hasMany(ContabilitaRegistrazione::class);
|
|
}
|
|
|
|
public function documenti(): HasMany
|
|
{
|
|
return $this->hasMany(Documento::class);
|
|
}
|
|
|
|
// Scope automatico per amministratore tramite stabile
|
|
protected static function booted(): void
|
|
{
|
|
static::addGlobalScope('amministratore', function (Builder $builder) {
|
|
$user = auth()->user();
|
|
if ($user && $user->amministratore_id) {
|
|
$builder->whereHas('stabile', function ($query) use ($user) {
|
|
$query->where('amministratore_id', $user->amministratore_id);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Scope
|
|
public function scopeActive(Builder $query): Builder
|
|
{
|
|
return $query->where('is_active', true);
|
|
}
|
|
|
|
public function scopeByTipo(Builder $query, string $tipo): Builder
|
|
{
|
|
return $query->where('tipo_unita', $tipo);
|
|
}
|
|
|
|
public function scopeByPiano(Builder $query, int $piano): Builder
|
|
{
|
|
return $query->where('piano', $piano);
|
|
}
|
|
|
|
public function scopeByStato(Builder $query, string $stato): Builder
|
|
{
|
|
return $query->where('stato_occupazione', $stato);
|
|
}
|
|
|
|
public function scopeLibere(Builder $query): Builder
|
|
{
|
|
return $query->where('stato_occupazione', 'libera');
|
|
}
|
|
|
|
public function scopeOccupate(Builder $query): Builder
|
|
{
|
|
return $query->whereIn('stato_occupazione', ['occupata_proprietario', 'occupata_inquilino']);
|
|
}
|
|
|
|
public function scopeByMillesimi(Builder $query, string $tipo = 'generali'): Builder
|
|
{
|
|
$campo = "millesimi_{$tipo}";
|
|
return $query->where($campo, '>', 0);
|
|
}
|
|
|
|
// Accessors
|
|
public function getDenominazioneComputedAttribute(): string
|
|
{
|
|
return $this->denominazione ?: "{$this->tipo_unita} {$this->interno}";
|
|
}
|
|
|
|
public function getIndirizzoCompletoAttribute(): string
|
|
{
|
|
$parts = array_filter([
|
|
$this->stabile->denominazione,
|
|
$this->scala ? "Scala {$this->scala}" : null,
|
|
"Interno {$this->interno}",
|
|
$this->piano !== null ? "Piano {$this->piano}" : null,
|
|
]);
|
|
|
|
return implode(', ', $parts);
|
|
}
|
|
|
|
public function getIdentificativoCatastaleAttribute(): string
|
|
{
|
|
$parts = array_filter([
|
|
$this->foglio ? "F.{$this->foglio}" : null,
|
|
$this->particella ? "P.{$this->particella}" : null,
|
|
$this->subalterno ? "Sub.{$this->subalterno}" : null,
|
|
]);
|
|
|
|
return implode(' ', $parts);
|
|
}
|
|
|
|
public function getSuperficieTotaleAttribute(): ?float
|
|
{
|
|
return $this->superficie_commerciale ?: $this->superficie_calpestabile;
|
|
}
|
|
|
|
public function getIsAppartamentoAttribute(): bool
|
|
{
|
|
return $this->tipo_unita === 'appartamento';
|
|
}
|
|
|
|
public function getIsLocaleCommercialeAttribute(): bool
|
|
{
|
|
return $this->tipo_unita === 'locale_commerciale';
|
|
}
|
|
|
|
public function getIsBoxAttribute(): bool
|
|
{
|
|
return $this->tipo_unita === 'box';
|
|
}
|
|
|
|
public function getHasProprietarioAttribute(): bool
|
|
{
|
|
return $this->proprietari()->exists();
|
|
}
|
|
|
|
public function getHasInquilinoAttribute(): bool
|
|
{
|
|
return $this->inquilini()->exists();
|
|
}
|
|
|
|
public function getStatoColorAttribute(): string
|
|
{
|
|
return match($this->stato_occupazione) {
|
|
'libera' => 'gray',
|
|
'occupata_proprietario' => 'green',
|
|
'occupata_inquilino' => 'blue',
|
|
'sfitto' => 'yellow',
|
|
'vendita' => 'red',
|
|
default => 'gray'
|
|
};
|
|
}
|
|
|
|
// Helper Methods
|
|
public function calcolaMillesimiAutomatici(): void
|
|
{
|
|
if (!$this->superficie_totale) {
|
|
return;
|
|
}
|
|
|
|
$stabileSuperficieTotale = $this->stabile->unitaImmobiliari()
|
|
->whereNotNull('superficie_commerciale')
|
|
->sum('superficie_commerciale');
|
|
|
|
if ($stabileSuperficieTotale > 0) {
|
|
$millesimi = ($this->superficie_totale / $stabileSuperficieTotale) * 1000;
|
|
|
|
$this->update([
|
|
'millesimi_generali' => round($millesimi, 4)
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function aggiornaStatoOccupazione(string $nuovoStato): bool
|
|
{
|
|
if ($this->stato_occupazione === $nuovoStato) {
|
|
return false;
|
|
}
|
|
|
|
$this->update([
|
|
'stato_occupazione' => $nuovoStato,
|
|
'data_ultimo_cambio_stato' => now()->toDateString()
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function getProprietarioAttuale(): ?Soggetto
|
|
{
|
|
return $this->proprietari()
|
|
->wherePivot('is_active', true)
|
|
->wherePivot('data_fine', null)
|
|
->first();
|
|
}
|
|
|
|
public function getInquilinoAttuale(): ?Soggetto
|
|
{
|
|
return $this->inquilini()
|
|
->wherePivot('is_active', true)
|
|
->wherePivot('data_fine', null)
|
|
->first();
|
|
}
|
|
|
|
public function associaSoggetto(Soggetto $soggetto, string $tipoRapporto, array $datiAggiuntivi = []): void
|
|
{
|
|
$datiPivot = array_merge([
|
|
'tipo_rapporto' => $tipoRapporto,
|
|
'quota_proprieta' => $datiAggiuntivi['quota_proprieta'] ?? 100.00,
|
|
'data_inizio' => $datiAggiuntivi['data_inizio'] ?? now()->toDateString(),
|
|
'data_fine' => $datiAggiuntivi['data_fine'] ?? null,
|
|
'is_active' => $datiAggiuntivi['is_active'] ?? true,
|
|
], $datiAggiuntivi);
|
|
|
|
$this->soggetti()->attach($soggetto->id, $datiPivot);
|
|
|
|
// Aggiorna stato occupazione automaticamente
|
|
if ($tipoRapporto === 'proprietario') {
|
|
$this->aggiornaStatoOccupazione('occupata_proprietario');
|
|
} elseif ($tipoRapporto === 'inquilino') {
|
|
$this->aggiornaStatoOccupazione('occupata_inquilino');
|
|
}
|
|
}
|
|
|
|
public function rimuoviSoggetto(Soggetto $soggetto): void
|
|
{
|
|
$this->soggetti()->updateExistingPivot($soggetto->id, [
|
|
'is_active' => false,
|
|
'data_fine' => now()->toDateString()
|
|
]);
|
|
|
|
// Controlla se rimangono soggetti attivi
|
|
$hasProprietarioAttivo = $this->proprietari()
|
|
->wherePivot('is_active', true)
|
|
->exists();
|
|
|
|
$hasInquilinoAttivo = $this->inquilini()
|
|
->wherePivot('is_active', true)
|
|
->exists();
|
|
|
|
if (!$hasProprietarioAttivo && !$hasInquilinoAttivo) {
|
|
$this->aggiornaStatoOccupazione('libera');
|
|
}
|
|
}
|
|
|
|
public function canDelete(): bool
|
|
{
|
|
return $this->soggetti()->count() === 0 &&
|
|
$this->contabilitaRegistrazioni()->count() === 0;
|
|
}
|
|
|
|
// Observer Events
|
|
protected static function boot(): void
|
|
{
|
|
parent::boot();
|
|
|
|
static::creating(function ($unita) {
|
|
// Genera denominazione automatica se non specificata
|
|
if (!$unita->denominazione) {
|
|
$unita->denominazione = ucfirst($unita->tipo_unita) . " {$unita->interno}";
|
|
}
|
|
});
|
|
|
|
static::saved(function ($unita) {
|
|
// Aggiorna conteggio unità dello stabile
|
|
$unita->stabile->updateNumeroUnita();
|
|
|
|
// Ricalcola millesimi se superficie cambiata
|
|
if ($unita->wasChanged('superficie_commerciale')) {
|
|
$unita->calcolaMillesimiAutomatici();
|
|
}
|
|
});
|
|
|
|
static::deleted(function ($unita) {
|
|
// Aggiorna conteggio unità dello stabile
|
|
$unita->stabile->updateNumeroUnita();
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🌱 **SEEDER DATI DEMO**
|
|
|
|
### **File**: `database/seeders/UnitaImmobiliariSeeder.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use Illuminate\Database\Seeder;
|
|
use App\Models\UnitaImmobiliare;
|
|
use App\Models\Stabile;
|
|
|
|
class UnitaImmobiliariSeeder extends Seeder
|
|
{
|
|
public function run(): void
|
|
{
|
|
$stabili = Stabile::all();
|
|
|
|
foreach ($stabili as $stabile) {
|
|
$this->createUnitaPerStabile($stabile);
|
|
}
|
|
|
|
$totalUnita = UnitaImmobiliare::count();
|
|
$this->command->info("✅ Create {$totalUnita} unità immobiliari per tutti gli stabili");
|
|
|
|
// Mostra riassunto per amministratore
|
|
$stabili->groupBy('amministratore_id')->each(function ($stabiliGroup) {
|
|
$admin = $stabiliGroup->first()->amministratore;
|
|
$unitaCount = UnitaImmobiliare::whereIn('stabile_id', $stabiliGroup->pluck('id'))->count();
|
|
$this->command->info("Admin {$admin->codice_univoco}: {$unitaCount} unità immobiliari");
|
|
});
|
|
}
|
|
|
|
private function createUnitaPerStabile(Stabile $stabile): void
|
|
{
|
|
$tipoEdificio = $stabile->tipo_edificio;
|
|
$numeroPiani = $stabile->numero_piani ?? 3;
|
|
|
|
if ($tipoEdificio === 'condominio') {
|
|
$this->createUnitaCondominio($stabile, $numeroPiani);
|
|
} elseif ($tipoEdificio === 'villetta') {
|
|
$this->createUnitaVilletta($stabile);
|
|
} elseif ($tipoEdificio === 'complesso') {
|
|
$this->createUnitaComplesso($stabile, $numeroPiani);
|
|
}
|
|
}
|
|
|
|
private function createUnitaCondominio(Stabile $stabile, int $numeroPiani): void
|
|
{
|
|
$subCount = 1;
|
|
|
|
// Appartamenti per piano
|
|
for ($piano = 1; $piano <= $numeroPiani; $piano++) {
|
|
$unitaPerPiano = $piano === 1 ? 3 : 4; // Piano terra meno unità
|
|
|
|
for ($unita = 1; $unita <= $unitaPerPiano; $unita++) {
|
|
$interno = "A{$piano}{$unita}";
|
|
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile->id,
|
|
'interno' => $interno,
|
|
'piano' => $piano,
|
|
'scala' => 'A',
|
|
'denominazione' => "Appartamento piano {$piano}",
|
|
'tipo_unita' => 'appartamento',
|
|
'destinazione_uso' => 'abitazione',
|
|
'foglio' => $stabile->foglio,
|
|
'particella' => $stabile->particella,
|
|
'subalterno' => str_pad($subCount++, 3, '0', STR_PAD_LEFT),
|
|
'categoria_catastale' => $piano <= 2 ? 'A/2' : 'A/3',
|
|
'classe' => '3',
|
|
'consistenza' => rand(4, 6) . ' vani',
|
|
'superficie_commerciale' => rand(60, 120),
|
|
'superficie_calpestabile' => rand(55, 100),
|
|
'rendita_catastale' => rand(300, 800),
|
|
'numero_vani' => rand(3, 5),
|
|
'numero_bagni' => rand(1, 2),
|
|
'numero_balconi' => rand(1, 3),
|
|
'has_terrazzo' => $piano === $numeroPiani && rand(0, 1),
|
|
'has_cantina' => rand(0, 1),
|
|
'millesimi_generali' => rand(80, 120),
|
|
'millesimi_riscaldamento' => $stabile->riscaldamento_centralizzato ? rand(80, 120) : 0,
|
|
'millesimi_ascensore' => $stabile->ascensore && $piano > 1 ? rand(80, 120) : 0,
|
|
'stato_occupazione' => ['occupata_proprietario', 'occupata_inquilino', 'libera'][rand(0, 2)],
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Box/garage al piano seminterrato
|
|
if (rand(0, 1)) {
|
|
for ($box = 1; $box <= 8; $box++) {
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile->id,
|
|
'interno' => "Box{$box}",
|
|
'piano' => -1,
|
|
'denominazione' => "Box auto n. {$box}",
|
|
'tipo_unita' => 'box',
|
|
'destinazione_uso' => 'deposito',
|
|
'foglio' => $stabile->foglio,
|
|
'particella' => $stabile->particella,
|
|
'subalterno' => str_pad($subCount++, 3, '0', STR_PAD_LEFT),
|
|
'categoria_catastale' => 'C/6',
|
|
'superficie_commerciale' => rand(15, 25),
|
|
'superficie_calpestabile' => rand(15, 25),
|
|
'rendita_catastale' => rand(50, 150),
|
|
'millesimi_generali' => rand(5, 15),
|
|
'stato_occupazione' => ['occupata_proprietario', 'libera'][rand(0, 1)],
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function createUnitaVilletta(Stabile $stabile): void
|
|
{
|
|
// Villetta = un'unica unità
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile->id,
|
|
'interno' => 'VILLA',
|
|
'piano' => 0,
|
|
'denominazione' => 'Villa unifamiliare',
|
|
'tipo_unita' => 'appartamento',
|
|
'destinazione_uso' => 'abitazione',
|
|
'foglio' => $stabile->foglio,
|
|
'particella' => $stabile->particella,
|
|
'subalterno' => '001',
|
|
'categoria_catastale' => 'A/7',
|
|
'classe' => '4',
|
|
'consistenza' => '8 vani',
|
|
'superficie_commerciale' => rand(150, 250),
|
|
'superficie_calpestabile' => rand(130, 220),
|
|
'rendita_catastale' => rand(800, 1500),
|
|
'numero_vani' => rand(6, 8),
|
|
'numero_bagni' => rand(2, 3),
|
|
'numero_balconi' => rand(2, 4),
|
|
'has_terrazzo' => true,
|
|
'has_giardino' => true,
|
|
'has_cantina' => true,
|
|
'millesimi_generali' => 1000, // Unica unità = 1000/1000
|
|
'stato_occupazione' => 'occupata_proprietario',
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
|
|
private function createUnitaComplesso(Stabile $stabile, int $numeroPiani): void
|
|
{
|
|
$subCount = 1;
|
|
$scale = ['A', 'B', 'C'];
|
|
|
|
foreach ($scale as $scala) {
|
|
for ($piano = 1; $piano <= $numeroPiani; $piano++) {
|
|
for ($unita = 1; $unita <= 2; $unita++) {
|
|
$interno = "{$scala}{$piano}{$unita}";
|
|
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile->id,
|
|
'interno' => $interno,
|
|
'piano' => $piano,
|
|
'scala' => $scala,
|
|
'denominazione' => "Appartamento scala {$scala} piano {$piano}",
|
|
'tipo_unita' => 'appartamento',
|
|
'destinazione_uso' => 'abitazione',
|
|
'foglio' => $stabile->foglio,
|
|
'particella' => $stabile->particella,
|
|
'subalterno' => str_pad($subCount++, 3, '0', STR_PAD_LEFT),
|
|
'categoria_catastale' => 'A/2',
|
|
'classe' => '3',
|
|
'superficie_commerciale' => rand(70, 130),
|
|
'superficie_calpestabile' => rand(65, 115),
|
|
'rendita_catastale' => rand(400, 900),
|
|
'numero_vani' => rand(3, 5),
|
|
'numero_bagni' => rand(1, 2),
|
|
'numero_balconi' => rand(1, 2),
|
|
'has_terrazzo' => $piano === $numeroPiani && rand(0, 1),
|
|
'millesimi_generali' => rand(30, 50),
|
|
'millesimi_riscaldamento' => $stabile->riscaldamento_centralizzato ? rand(30, 50) : 0,
|
|
'millesimi_ascensore' => $stabile->ascensore && $piano > 1 ? rand(30, 50) : 0,
|
|
'millesimi_scala' => rand(15, 25),
|
|
'stato_occupazione' => ['occupata_proprietario', 'occupata_inquilino', 'libera'][rand(0, 2)],
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Locali commerciali al piano terra
|
|
for ($locale = 1; $locale <= 3; $locale++) {
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile->id,
|
|
'interno' => "LC{$locale}",
|
|
'piano' => 0,
|
|
'denominazione' => "Locale commerciale n. {$locale}",
|
|
'tipo_unita' => 'locale_commerciale',
|
|
'destinazione_uso' => 'commerciale',
|
|
'foglio' => $stabile->foglio,
|
|
'particella' => $stabile->particella,
|
|
'subalterno' => str_pad($subCount++, 3, '0', STR_PAD_LEFT),
|
|
'categoria_catastale' => 'C/1',
|
|
'superficie_commerciale' => rand(40, 80),
|
|
'superficie_calpestabile' => rand(35, 70),
|
|
'rendita_catastale' => rand(200, 500),
|
|
'numero_bagni' => 1,
|
|
'millesimi_generali' => rand(20, 40),
|
|
'stato_occupazione' => ['occupata_inquilino', 'sfitto'][rand(0, 1)],
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 **UTILIZZO PATTERN**
|
|
|
|
### **Query Base**:
|
|
```php
|
|
// Unità di uno stabile
|
|
$unita = UnitaImmobiliare::where('stabile_id', $stabile->id)
|
|
->orderBy('piano')
|
|
->orderBy('interno')
|
|
->get();
|
|
|
|
// Appartamenti occupati
|
|
$occupati = UnitaImmobiliare::occupate()
|
|
->byTipo('appartamento')
|
|
->with(['proprietari', 'inquilini'])
|
|
->get();
|
|
|
|
// Unità libere per affitto
|
|
$libere = UnitaImmobiliare::libere()
|
|
->with('stabile')
|
|
->get();
|
|
```
|
|
|
|
---
|
|
|
|
**🎯 TABELLA UNITÀ IMMOBILIARI COMPLETATA**
|
|
**Con dati catastali, millesimi e collegamenti per soggetti**
|
|
|
|
**📖 Prossimo: `soggetti.md` per proprietari e inquilini collegati alle unità**
|