📋 Commit iniziale con: - ✅ Documentazione unificata in docs/ - ✅ Codice Laravel in netgescon-laravel/ - ✅ Script automazione in scripts/ - ✅ Configurazione sync rsync - ✅ Struttura organizzata e pulita 🔄 Versione: 2025.07.19-1644 🎯 Sistema pronto per Git distribuito
19 KiB
19 KiB
🏢 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)
-- 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
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
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
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
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
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
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
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
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
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)
<!-- Aggiungere nuovi tab -->
<div class="nav nav-pills" id="stabile-tabs">
<!-- Tab esistenti -->
<a class="nav-link" data-bs-toggle="pill" href="#struttura-fisica">Struttura Fisica</a>
<a class="nav-link" data-bs-toggle="pill" href="#chiavi">Gestione Chiavi</a>
<a class="nav-link" data-bs-toggle="pill" href="#fondi">Fondi Condominiali</a>
</div>
<div class="tab-content">
<!-- Tab Struttura Fisica -->
<div class="tab-pane" id="struttura-fisica">
@include('admin.stabili.partials.struttura-fisica')
</div>
<!-- Tab Chiavi -->
<div class="tab-pane" id="chiavi">
@include('admin.stabili.partials.chiavi')
</div>
<!-- Tab Fondi -->
<div class="tab-pane" id="fondi">
@include('admin.stabili.partials.fondi')
</div>
</div>
admin/stabili/partials/struttura-fisica.blade.php
<div class="card">
<div class="card-header">
<h5>Struttura Fisica Stabile</h5>
<button class="btn btn-primary" onclick="generateUnita()">
<i class="fas fa-magic"></i> Genera Unità Immobiliari
</button>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<strong>Palazzine:</strong> {{ $stabile->numero_palazzine }}
</div>
<div class="col-md-3">
<strong>Scale per Palazzina:</strong> {{ $stabile->numero_scale_per_palazzina }}
</div>
<div class="col-md-3">
<strong>Piani:</strong> {{ $stabile->numero_piani }}
</div>
<div class="col-md-3">
<strong>Ascensore:</strong>
<span class="badge bg-{{ $stabile->presenza_ascensore ? 'success' : 'secondary' }}">
{{ $stabile->presenza_ascensore ? 'Sì' : 'No' }}
</span>
</div>
</div>
<!-- Visualizzazione grafica struttura -->
<div id="struttura-visual" class="mt-4">
@include('admin.stabili.partials.struttura-visual')
</div>
</div>
</div>
admin/stabili/chiavi/index.blade.php
@extends('layouts.app-universal')
@section('content')
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between">
<h4>Gestione Chiavi - {{ $stabile->denominazione }}</h4>
<a href="{{ route('admin.stabili.chiavi.create', $stabile) }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Nuova Chiave
</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Codice</th>
<th>Tipologia</th>
<th>Descrizione</th>
<th>Stato</th>
<th>Assegnata a</th>
<th>QR Code</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@foreach($chiavi as $chiave)
<tr>
<td>{{ $chiave->codice_chiave }}</td>
<td>
<span class="badge bg-info">
{{ ChiaveStabile::TIPOLOGIE[$chiave->tipologia] }}
</span>
</td>
<td>{{ $chiave->descrizione }}</td>
<td>
<span class="badge bg-{{ $chiave->stato == 'attiva' ? 'success' : 'warning' }}">
{{ ucfirst($chiave->stato) }}
</span>
</td>
<td>{{ $chiave->assegnata_a ?? '-' }}</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="showQRCode('{{ $chiave->id }}')">
<i class="fas fa-qrcode"></i>
</button>
</td>
<td>
<div class="btn-group">
<a href="{{ route('admin.stabili.chiavi.show', [$stabile, $chiave]) }}" class="btn btn-sm btn-info">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('admin.stabili.chiavi.edit', [$stabile, $chiave]) }}" class="btn btn-sm btn-warning">
<i class="fas fa-edit"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{ $chiavi->links() }}
</div>
</div>
</div>
</div>
</div>
<!-- Modal QR Code -->
<div class="modal fade" id="qrCodeModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">QR Code Chiave</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body text-center">
<div id="qrCodeContainer"></div>
</div>
</div>
</div>
</div>
@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
Stabilecon nuovi campi - Creare model
ChiaveStabile - Creare model
MovimentoChiave - Creare model
FondoCondominiale - Creare model
StruttureFisicaDettaglio
Step 3: Controllers (2 giorni)
- Estendere
StabileControllercon nuovi metodi - Creare
ChiaveControllercompleto - 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! 🚀