📋 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
20 KiB
20 KiB
🏢 MODULO STABILE - Specifiche Complete
📋 OVERVIEW
Il Modulo Stabile è il modulo fondamentale di NetGesCon che gestisce l'anagrafica completa degli stabili/condomini e rappresenta il punto di partenza per tutti gli altri moduli del sistema.
🎯 OBIETTIVI DEL MODULO
✅ Funzionalità Core
- Anagrafica Stabile Completa: Tutti i dati identificativi e tecnici
- Codici Univoci: Sistema di identificazione a 8 caratteri
- Multi-Gestione: Un amministratore può gestire più stabili
- Configurazioni Personalizzate: Settings specifici per tipologia stabile
- Storico Modifiche: Audit trail completo delle variazioni
🔗 Integrazione Sistema
- Base per tutti i moduli: Ogni funzionalità parte dal concetto di stabile
- Permessi Granulari: Controllo accessi per stabile
- Dashboard Dedicata: Vista riassuntiva per ogni stabile
- API Complete: Endpoint REST per integrazione
💾 STRUTTURA DATABASE
📊 Tabella: stabili
CREATE TABLE stabili (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
codice_stabile VARCHAR(8) UNIQUE NOT NULL COMMENT 'Codice univoco 8 caratteri',
denominazione VARCHAR(255) NOT NULL,
-- Dati Legali/Fiscali
codice_fiscale VARCHAR(16) UNIQUE,
partita_iva VARCHAR(11),
natura_giuridica ENUM('condominio', 'cooperativa', 'societa', 'altro') DEFAULT 'condominio',
-- Indirizzo Completo
indirizzo VARCHAR(255) NOT NULL,
numero_civico VARCHAR(10),
cap VARCHAR(5) NOT NULL,
citta VARCHAR(100) NOT NULL,
provincia CHAR(2) NOT NULL,
regione VARCHAR(50) NOT NULL,
nazione VARCHAR(50) DEFAULT 'Italia',
-- Dati Tecnici Edificio
anno_costruzione YEAR,
numero_piani TINYINT UNSIGNED,
numero_unita SMALLINT UNSIGNED NOT NULL DEFAULT 0,
superficie_totale DECIMAL(10,2),
tipologia_edificio ENUM('residenziale', 'commerciale', 'misto', 'industriale') DEFAULT 'residenziale',
-- Dati Catastali
foglio VARCHAR(10),
particella VARCHAR(10),
subalterno VARCHAR(10),
categoria_catastale VARCHAR(5),
classe_energetica ENUM('A4', 'A3', 'A2', 'A1', 'B', 'C', 'D', 'E', 'F', 'G'),
-- Gestione Amministrativa
user_id BIGINT UNSIGNED NOT NULL COMMENT 'Amministratore responsabile',
data_inizio_gestione DATE NOT NULL,
data_fine_gestione DATE NULL,
stato ENUM('attivo', 'sospeso', 'chiuso') DEFAULT 'attivo',
-- Configurazioni
configurazioni JSON COMMENT 'Settings specifici stabile',
-- Timestamps e Audit
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by BIGINT UNSIGNED,
updated_by BIGINT UNSIGNED,
-- Indici
INDEX idx_codice_stabile (codice_stabile),
INDEX idx_user_id (user_id),
INDEX idx_stato (stato),
INDEX idx_citta_provincia (citta, provincia),
-- Foreign Keys
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
📊 Tabella: stabili_configurazioni
CREATE TABLE stabili_configurazioni (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
stabile_id BIGINT UNSIGNED NOT NULL,
-- Configurazioni Contabili
piano_conti_personalizzato BOOLEAN DEFAULT FALSE,
gestione_anticipi BOOLEAN DEFAULT TRUE,
calcolo_interessi_mora BOOLEAN DEFAULT TRUE,
tasso_interesse_mora DECIMAL(5,2) DEFAULT 0.50,
-- Configurazioni Millesimali
numero_tabelle_millesimali TINYINT DEFAULT 1,
aggiornamento_automatico_millesimi BOOLEAN DEFAULT FALSE,
validazione_matematica_millesimi BOOLEAN DEFAULT TRUE,
-- Configurazioni Comunicazioni
email_amministratore VARCHAR(255),
telefono_amministratore VARCHAR(20),
pec_stabile VARCHAR(255),
sito_web VARCHAR(255),
-- Configurazioni Fatturazione
formato_fattura ENUM('standard', 'semplificata', 'elettronica') DEFAULT 'standard',
numerazione_fatture VARCHAR(50) DEFAULT 'YYYY/NNNN',
iva_default DECIMAL(5,2) DEFAULT 22.00,
-- Configurazioni Backup
backup_automatico BOOLEAN DEFAULT TRUE,
frequenza_backup ENUM('giornaliero', 'settimanale', 'mensile') DEFAULT 'settimanale',
retention_backup SMALLINT DEFAULT 12 COMMENT 'Mesi',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
UNIQUE KEY uk_stabile_configurazioni (stabile_id)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
🔧 CONTROLLER LARAVEL
📝 StabileController
<?php
namespace App\Http\Controllers;
use App\Models\Stabile;
use App\Http\Requests\StabileRequest;
use App\Services\StabileService;
use Illuminate\Http\Request;
class StabileController extends Controller
{
protected $stabileService;
public function __construct(StabileService $stabileService)
{
$this->stabileService = $stabileService;
$this->middleware('auth');
$this->middleware('permission:stabili.view')->only(['index', 'show']);
$this->middleware('permission:stabili.create')->only(['create', 'store']);
$this->middleware('permission:stabili.edit')->only(['edit', 'update']);
$this->middleware('permission:stabili.delete')->only(['destroy']);
}
/**
* Lista stabili con filtri e ricerca
*/
public function index(Request $request)
{
$stabili = $this->stabileService->getFilteredStabili($request->all());
return view('stabili.index', compact('stabili'));
}
/**
* Form creazione nuovo stabile
*/
public function create()
{
return view('stabili.create');
}
/**
* Salvataggio nuovo stabile
*/
public function store(StabileRequest $request)
{
try {
$stabile = $this->stabileService->createStabile($request->validated());
return redirect()->route('stabili.show', $stabile)
->with('success', 'Stabile creato con successo!');
} catch (\Exception $e) {
return back()->withInput()
->with('error', 'Errore durante la creazione: ' . $e->getMessage());
}
}
/**
* Visualizzazione dettaglio stabile
*/
public function show(Stabile $stabile)
{
$this->authorize('view', $stabile);
$stabile->load(['unita', 'configurazioni', 'documenti']);
$statistiche = $this->stabileService->getStatistiche($stabile);
return view('stabili.show', compact('stabile', 'statistiche'));
}
/**
* Form modifica stabile
*/
public function edit(Stabile $stabile)
{
$this->authorize('update', $stabile);
return view('stabili.edit', compact('stabile'));
}
/**
* Aggiornamento stabile
*/
public function update(StabileRequest $request, Stabile $stabile)
{
$this->authorize('update', $stabile);
try {
$this->stabileService->updateStabile($stabile, $request->validated());
return redirect()->route('stabili.show', $stabile)
->with('success', 'Stabile aggiornato con successo!');
} catch (\Exception $e) {
return back()->withInput()
->with('error', 'Errore durante l\'aggiornamento: ' . $e->getMessage());
}
}
/**
* Eliminazione stabile (soft delete)
*/
public function destroy(Stabile $stabile)
{
$this->authorize('delete', $stabile);
try {
$this->stabileService->deleteStabile($stabile);
return redirect()->route('stabili.index')
->with('success', 'Stabile eliminato con successo!');
} catch (\Exception $e) {
return back()->with('error', 'Errore durante l\'eliminazione: ' . $e->getMessage());
}
}
/**
* Dashboard stabile con statistiche
*/
public function dashboard(Stabile $stabile)
{
$this->authorize('view', $stabile);
$dashboard = $this->stabileService->getDashboardData($stabile);
return view('stabili.dashboard', compact('stabile', 'dashboard'));
}
}
🎨 INTERFACCE UTENTE
📋 Lista Stabili (stabili/index.blade.php)
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h3>🏢 Gestione Stabili</h3>
@can('stabili.create')
<a href="{{ route('stabili.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Nuovo Stabile
</a>
@endcan
</div>
<div class="card-body">
<!-- Filtri di Ricerca -->
<form method="GET" class="mb-4">
<div class="row">
<div class="col-md-3">
<input type="text" name="search" class="form-control"
placeholder="Cerca per denominazione..."
value="{{ request('search') }}">
</div>
<div class="col-md-2">
<select name="stato" class="form-control">
<option value="">Tutti gli stati</option>
<option value="attivo" {{ request('stato') == 'attivo' ? 'selected' : '' }}>Attivo</option>
<option value="sospeso" {{ request('stato') == 'sospeso' ? 'selected' : '' }}>Sospeso</option>
<option value="chiuso" {{ request('stato') == 'chiuso' ? 'selected' : '' }}>Chiuso</option>
</select>
</div>
<div class="col-md-2">
<select name="provincia" class="form-control">
<option value="">Tutte le province</option>
@foreach($province as $prov)
<option value="{{ $prov }}" {{ request('provincia') == $prov ? 'selected' : '' }}>
{{ $prov }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-outline-primary">
<i class="fas fa-search"></i> Cerca
</button>
</div>
</div>
</form>
<!-- Tabella Stabili -->
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Codice</th>
<th>Denominazione</th>
<th>Indirizzo</th>
<th>Unità</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@forelse($stabili as $stabile)
<tr>
<td>
<span class="badge badge-secondary">{{ $stabile->codice_stabile }}</span>
</td>
<td>
<strong>{{ $stabile->denominazione }}</strong>
@if($stabile->tipologia_edificio)
<br><small class="text-muted">{{ ucfirst($stabile->tipologia_edificio) }}</small>
@endif
</td>
<td>
{{ $stabile->indirizzo }} {{ $stabile->numero_civico }}<br>
<small class="text-muted">{{ $stabile->cap }} {{ $stabile->citta }} ({{ $stabile->provincia }})</small>
</td>
<td>
<span class="badge badge-info">{{ $stabile->numero_unita }} unità</span>
</td>
<td>
@switch($stabile->stato)
@case('attivo')
<span class="badge badge-success">Attivo</span>
@break
@case('sospeso')
<span class="badge badge-warning">Sospeso</span>
@break
@case('chiuso')
<span class="badge badge-danger">Chiuso</span>
@break
@endswitch
</td>
<td>
<div class="btn-group" role="group">
@can('view', $stabile)
<a href="{{ route('stabili.show', $stabile) }}"
class="btn btn-sm btn-info" title="Visualizza">
<i class="fas fa-eye"></i>
</a>
@endcan
@can('update', $stabile)
<a href="{{ route('stabili.edit', $stabile) }}"
class="btn btn-sm btn-warning" title="Modifica">
<i class="fas fa-edit"></i>
</a>
@endcan
@can('delete', $stabile)
<form method="POST" action="{{ route('stabili.destroy', $stabile) }}"
class="d-inline" onsubmit="return confirm('Sei sicuro?')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger" title="Elimina">
<i class="fas fa-trash"></i>
</button>
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center">
<em>Nessuno stabile trovato</em>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Paginazione -->
{{ $stabili->links() }}
</div>
</div>
</div>
</div>
</div>
@endsection
🔄 WORKFLOW OPERATIVO
📝 Processo Creazione Stabile
- Accesso Form: Admin accede al form di creazione
- Validazione Input: Controllo dati obbligatori e formati
- Generazione Codice: Sistema genera codice univoco 8 caratteri
- Salvataggio Database: Insert in tabella
stabilicon audit - Configurazioni Default: Creazione configurazioni iniziali
- Notifica: Conferma successo e redirect a dettaglio
📊 Dashboard Stabile
- Dati Anagrafici: Visualizzazione completa info stabile
- Statistiche: Numero unità, superfici, millesimi totali
- Azioni Rapide: Link diretti a funzioni principali
- Stato Sistema: Indicatori salute dati e configurazioni
- Attività Recenti: Log ultime modifiche e operazioni
🔒 SICUREZZA E PERMESSI
👥 Livelli Accesso
- Super Admin: Accesso totale a tutti gli stabili
- Amministratore: Accesso solo ai propri stabili
- Collaboratore: Accesso limitato in base alle assegnazioni
- Consulente: Solo visualizzazione dati autorizzati
🛡️ Policy Laravel
class StabilePolicy
{
public function view(User $user, Stabile $stabile)
{
return $user->hasPermissionTo('stabili.view') &&
($user->hasRole('super-admin') || $user->id === $stabile->user_id);
}
public function create(User $user)
{
return $user->hasPermissionTo('stabili.create');
}
public function update(User $user, Stabile $stabile)
{
return $user->hasPermissionTo('stabili.edit') &&
($user->hasRole('super-admin') || $user->id === $stabile->user_id);
}
public function delete(User $user, Stabile $stabile)
{
return $user->hasPermissionTo('stabili.delete') &&
$user->hasRole('super-admin');
}
}
✅ TESTING E VALIDAZIONE
🧪 Test Unitari
- Validazione creazione stabile
- Verifica unicità codice stabile
- Test configurazioni default
- Controllo permessi accesso
🔍 Test Integrazione
- Workflow completo CRUD
- Collegamento con altri moduli
- Test performance query
- Validazione audit trail
📈 METRICHE E KPI
📊 Indicatori Performance
- Tempo risposta liste stabili (< 200ms)
- Accuratezza dati anagrafici (100%)
- Percentuale uptime sistema (99.9%)
- Soddisfazione utenti (> 4.5/5)
📋 Reportistica
- Report mensile stabili gestiti
- Statistiche utilizzo funzionalità
- Analisi errori e problematiche
- Trend crescita database
🚀 PROSSIMI SVILUPPI
🔄 Fase 2 - Miglioramenti
- Import massivo da file Excel/CSV
- API REST complete per integrazioni
- Backup automatico differenziale
- Notificazioni push importanti
🎯 Fase 3 - Avanzate
- Dashboard analytics avanzate
- Machine learning per previsioni
- Integrazione con servizi esterni
- Mobile app dedicata
Documento tecnico Modulo Stabile | Versione 1.0 | Luglio 2025