# 🏢 MODULO STABILI AVANZATO - Specifiche Implementazione > **🎯 Primo modulo prioritario del piano implementazione** > Basato su: brainstorming/01-stabili/ANALISI-STABILI.md > Aggiornato: 14 Luglio 2025 ## 📋 **OVERVIEW** Evoluzione del CRUD Stabili esistente con funzionalità innovative: - ✅ **Base CRUD** già implementato (StabileController, Model, Views) - 🔄 **Struttura fisica automatica** (palazzine, scale, piani) - 🔑 **Gestione chiavi con QR Code** (archivio, tipologie, tracking) - 🏠 **Auto-generazione unità immobiliari** da struttura - 💰 **Fondi condominiali gerarchici** (ordinario, riserva, specifici) ## 🏗️ **ARCHITETTURA DATABASE** ### **Tabelle Esistenti da Estendere** #### **stabili** (ESTENSIONE) ```sql -- Campi già esistenti: denominazione, codice_fiscale, indirizzo, etc. -- NUOVI CAMPI DA AGGIUNGERE: ALTER TABLE stabili ADD COLUMN struttura_fisica_json JSON; ALTER TABLE stabili ADD COLUMN numero_palazzine INT DEFAULT 1; ALTER TABLE stabili ADD COLUMN numero_scale_per_palazzina INT DEFAULT 1; ALTER TABLE stabili ADD COLUMN numero_piani INT DEFAULT 3; ALTER TABLE stabili ADD COLUMN piano_seminterrato BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN piano_sottotetto BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN presenza_ascensore BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN cortile_giardino BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN superficie_cortile DECIMAL(8,2) NULL; ALTER TABLE stabili ADD COLUMN riscaldamento_centralizzato BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN acqua_centralizzata BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN gas_centralizzato BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN servizio_portineria BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN orari_portineria VARCHAR(255) NULL; ALTER TABLE stabili ADD COLUMN videocitofono BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN antenna_tv_centralizzata BOOLEAN DEFAULT FALSE; ALTER TABLE stabili ADD COLUMN internet_condominiale BOOLEAN DEFAULT FALSE; ``` ### **Nuove Tabelle da Creare** #### **1. chiavi_stabili** ```sql CREATE TABLE chiavi_stabili ( id BIGINT PRIMARY KEY AUTO_INCREMENT, stabile_id BIGINT NOT NULL, codice_chiave VARCHAR(50) UNIQUE NOT NULL, qr_code_data TEXT NOT NULL, tipologia ENUM('portone_principale', 'porte_secondarie', 'locali_tecnici', 'spazi_comuni', 'servizi', 'emergenza') NOT NULL, descrizione VARCHAR(255) NOT NULL, ubicazione VARCHAR(255), numero_duplicati INT DEFAULT 1, stato ENUM('attiva', 'smarrita', 'sostituita', 'fuori_uso') DEFAULT 'attiva', assegnata_a VARCHAR(255) NULL, -- Chi ce l'ha attualmente data_assegnazione TIMESTAMP NULL, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (stabile_id) REFERENCES stabili(id_stabile) ON DELETE CASCADE, INDEX idx_stabile_tipologia (stabile_id, tipologia), INDEX idx_qr_code (qr_code_data(100)) ); ``` #### **2. movimenti_chiavi** ```sql CREATE TABLE movimenti_chiavi ( id BIGINT PRIMARY KEY AUTO_INCREMENT, chiave_id BIGINT NOT NULL, tipo_movimento ENUM('assegnazione', 'riconsegna', 'smarrimento', 'sostituzione') NOT NULL, data_movimento TIMESTAMP DEFAULT CURRENT_TIMESTAMP, assegnata_da VARCHAR(255), -- Chi ha fatto l'assegnazione assegnata_a VARCHAR(255), -- A chi è stata assegnata motivo VARCHAR(255), note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (chiave_id) REFERENCES chiavi_stabili(id) ON DELETE CASCADE, INDEX idx_chiave_data (chiave_id, data_movimento) ); ``` #### **3. fondi_condominiali** ```sql CREATE TABLE fondi_condominiali ( id BIGINT PRIMARY KEY AUTO_INCREMENT, stabile_id BIGINT NOT NULL, tipo_fondo ENUM('ordinario', 'riserva', 'ascensore', 'riscaldamento', 'facciata_tetto', 'verde_giardini', 'sicurezza', 'innovazione', 'investimenti', 'personalizzato') NOT NULL, denominazione VARCHAR(255) NOT NULL, descrizione TEXT, saldo_attuale DECIMAL(12,2) DEFAULT 0.00, saldo_minimo DECIMAL(12,2) DEFAULT 0.00, percentuale_accantonamento DECIMAL(5,2) DEFAULT 0.00, attivo BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (stabile_id) REFERENCES stabili(id_stabile) ON DELETE CASCADE, INDEX idx_stabile_tipo (stabile_id, tipo_fondo) ); ``` #### **4. struttura_fisica_dettaglio** ```sql CREATE TABLE struttura_fisica_dettaglio ( id BIGINT PRIMARY KEY AUTO_INCREMENT, stabile_id BIGINT NOT NULL, palazzina VARCHAR(10) NOT NULL, -- A, B, C o 1, 2, 3 scala VARCHAR(10), -- 1, 2, 3 o A, B, C piano INT NOT NULL, -- -2, -1, 0, 1, 2, 3... (0=piano terra) numero_interni INT DEFAULT 1, presenza_ascensore BOOLEAN DEFAULT FALSE, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (stabile_id) REFERENCES stabili(id_stabile) ON DELETE CASCADE, INDEX idx_stabile_struttura (stabile_id, palazzina, scala, piano) ); ``` ## 🎯 **FUNZIONALITÀ DA IMPLEMENTARE** ### **1. Gestione Struttura Fisica Automatica** #### **StruttureFisicaController** ```php class StruttureFisicaController extends Controller { public function generateUnitaImmobiliari(Request $request, Stabile $stabile) { // Calcola numero totale unità dalla struttura fisica $struttura = $this->calcolaStrutturaFisica($stabile); // Genera preview unità immobiliari $preview = $this->generaPreviewUnita($struttura); return view('admin.stabili.genera-unita', compact('stabile', 'preview')); } private function calcolaStrutturaFisica(Stabile $stabile) { $totalePalazzine = $stabile->numero_palazzine; $scalePerPalazzina = $stabile->numero_scale_per_palazzina; $numeroPiani = $stabile->numero_piani; // ... logica calcolo } } ``` #### **Service: UnitaImmobiliareGenerator** ```php class UnitaImmobiliareGenerator { public function generateFromStruttura(Stabile $stabile, array $config = []) { $unita = []; for ($palazzina = 1; $palazzina <= $stabile->numero_palazzine; $palazzina++) { for ($scala = 1; $scala <= $stabile->numero_scale_per_palazzina; $scala++) { for ($piano = -1; $piano <= $stabile->numero_piani; $piano++) { // Genera unità per questo piano $unitaPiano = $this->generaUnitaPiano($stabile, $palazzina, $scala, $piano); $unita = array_merge($unita, $unitaPiano); } } } return $unita; } } ``` ### **2. Gestione Chiavi con QR Code** #### **ChiaveController** ```php class ChiaveController extends Controller { public function index(Stabile $stabile) { $chiavi = ChiaveStabile::where('stabile_id', $stabile->id_stabile) ->with('movimenti') ->paginate(20); return view('admin.stabili.chiavi.index', compact('stabile', 'chiavi')); } public function create(Stabile $stabile) { $tipologie = ChiaveStabile::TIPOLOGIE; return view('admin.stabili.chiavi.create', compact('stabile', 'tipologie')); } public function store(Request $request, Stabile $stabile) { $validated = $request->validate([ 'tipologia' => 'required|in:portone_principale,porte_secondarie,locali_tecnici,spazi_comuni,servizi,emergenza', 'descrizione' => 'required|string|max:255', 'ubicazione' => 'nullable|string|max:255', 'numero_duplicati' => 'integer|min:1|max:50' ]); $chiave = new ChiaveStabile(); $chiave->stabile_id = $stabile->id_stabile; $chiave->codice_chiave = $this->generateCodiceChiave($stabile); $chiave->qr_code_data = $this->generateQRCode($chiave); $chiave->fill($validated); $chiave->save(); return redirect()->route('admin.stabili.chiavi.index', $stabile) ->with('success', 'Chiave creata con successo'); } private function generateQRCode(ChiaveStabile $chiave) { $data = [ 'stabile_id' => $chiave->stabile_id, 'chiave_id' => $chiave->id, 'codice' => $chiave->codice_chiave, 'tipologia' => $chiave->tipologia, 'timestamp' => now()->timestamp ]; return json_encode($data); } } ``` #### **Model: ChiaveStabile** ```php class ChiaveStabile extends Model { protected $table = 'chiavi_stabili'; const TIPOLOGIE = [ 'portone_principale' => 'Portone Principale', 'porte_secondarie' => 'Porte Secondarie', 'locali_tecnici' => 'Locali Tecnici', 'spazi_comuni' => 'Spazi Comuni', 'servizi' => 'Servizi', 'emergenza' => 'Emergenza' ]; protected $fillable = [ 'stabile_id', 'codice_chiave', 'qr_code_data', 'tipologia', 'descrizione', 'ubicazione', 'numero_duplicati', 'stato', 'assegnata_a', 'data_assegnazione', 'note' ]; public function stabile() { return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile'); } public function movimenti() { return $this->hasMany(MovimentoChiave::class, 'chiave_id'); } public function getQRCodeImageAttribute() { return QrCode::size(200)->generate($this->qr_code_data); } } ``` ### **3. Fondi Condominiali Gerarchici** #### **FondoCondominiale Model** ```php class FondoCondominiale extends Model { protected $table = 'fondi_condominiali'; const TIPI_FONDO = [ 'ordinario' => 'Fondo Ordinario', 'riserva' => 'Fondo di Riserva', 'ascensore' => 'Fondo Ascensore', 'riscaldamento' => 'Fondo Riscaldamento', 'facciata_tetto' => 'Fondo Facciata/Tetto', 'verde_giardini' => 'Fondo Verde/Giardini', 'sicurezza' => 'Fondo Sicurezza', 'innovazione' => 'Fondo Innovazione', 'investimenti' => 'Fondo Investimenti', 'personalizzato' => 'Fondo Personalizzato' ]; protected $fillable = [ 'stabile_id', 'tipo_fondo', 'denominazione', 'descrizione', 'saldo_attuale', 'saldo_minimo', 'percentuale_accantonamento', 'attivo' ]; public function stabile() { return $this->belongsTo(Stabile::class, 'stabile_id', 'id_stabile'); } public function movimenti() { return $this->hasMany(MovimentoContabile::class, 'fondo_id'); } } ``` ## 🎨 **INTERFACCIA UTENTE** ### **Views da Creare/Modificare** #### **admin/stabili/show.blade.php** (ESTENSIONE) ```html
@include('admin.stabili.partials.struttura-fisica')
@include('admin.stabili.partials.chiavi')
@include('admin.stabili.partials.fondi')
``` #### **admin/stabili/partials/struttura-fisica.blade.php** ```html
Struttura Fisica Stabile
Palazzine: {{ $stabile->numero_palazzine }}
Scale per Palazzina: {{ $stabile->numero_scale_per_palazzina }}
Piani: {{ $stabile->numero_piani }}
Ascensore: {{ $stabile->presenza_ascensore ? 'Sì' : 'No' }}
@include('admin.stabili.partials.struttura-visual')
``` #### **admin/stabili/chiavi/index.blade.php** ```html @extends('layouts.app-universal') @section('content')

Gestione Chiavi - {{ $stabile->denominazione }}

Nuova Chiave
@foreach($chiavi as $chiave) @endforeach
Codice Tipologia Descrizione Stato Assegnata a QR Code Azioni
{{ $chiave->codice_chiave }} {{ ChiaveStabile::TIPOLOGIE[$chiave->tipologia] }} {{ $chiave->descrizione }} {{ ucfirst($chiave->stato) }} {{ $chiave->assegnata_a ?? '-' }}
{{ $chiavi->links() }}
@endsection ``` ## 🚀 **IMPLEMENTAZIONE STEP-BY-STEP** ### **Step 1: Database** (1 giorno) - [ ] Creare migration per estensione tabella `stabili` - [ ] Creare migration per tabella `chiavi_stabili` - [ ] Creare migration per tabella `movimenti_chiavi` - [ ] Creare migration per tabella `fondi_condominiali` - [ ] Creare migration per tabella `struttura_fisica_dettaglio` ### **Step 2: Models** (0.5 giorni) - [ ] Estendere model `Stabile` con nuovi campi - [ ] Creare model `ChiaveStabile` - [ ] Creare model `MovimentoChiave` - [ ] Creare model `FondoCondominiale` - [ ] Creare model `StruttureFisicaDettaglio` ### **Step 3: Controllers** (2 giorni) - [ ] Estendere `StabileController` con nuovi metodi - [ ] Creare `ChiaveController` completo - [ ] Creare `FondoCondominiale Controller` - [ ] Creare `StruttureFisicaController` ### **Step 4: Views** (2 giorni) - [ ] Estendere form create/edit stabili - [ ] Creare views gestione chiavi - [ ] Creare views gestione fondi - [ ] Creare componenti visualizzazione struttura ### **Step 5: Features Avanzate** (2 giorni) - [ ] Implementare generazione QR Code - [ ] Sistema auto-generazione unità immobiliari - [ ] Dashboard fondi con grafici - [ ] API mobile per scanner QR ## 📊 **TESTING** ### **Unit Tests** - [ ] Test generazione codici chiavi univoci - [ ] Test calcolo struttura fisica - [ ] Test auto-generazione unità - [ ] Test gestione fondi ### **Integration Tests** - [ ] Test workflow completo creazione stabile - [ ] Test import/export configurazioni - [ ] Test API QR Code scanner --- **Questo modulo sarà la base per tutti gli altri. Una volta completato, avremo il pattern per implementare rapidamente gli altri moduli!** 🚀