1507 lines
43 KiB
Markdown
Executable File
1507 lines
43 KiB
Markdown
Executable File
# 6. SISTEMA MULTI-RUOLO - GUIDA COMPLETA
|
||
|
||
## 📋 **INDICE CAPITOLO**
|
||
- [6.1 Architettura Ruoli e Permessi](#61-architettura-ruoli-e-permessi)
|
||
- [6.2 Configurazione Spatie Permission](#62-configurazione-spatie-permission)
|
||
- [6.3 Modelli e Relazioni](#63-modelli-e-relazioni)
|
||
- [6.4 Seeder Ruoli e Permessi](#64-seeder-ruoli-e-permessi)
|
||
- [6.5 Middleware di Protezione](#65-middleware-di-protezione)
|
||
- [6.6 Controllers per Ruoli](#66-controllers-per-ruoli)
|
||
- [6.7 Dashboard Condizionali](#67-dashboard-condizionali)
|
||
- [6.8 Gestione Utenti](#68-gestione-utenti)
|
||
- [6.9 Esempi Pratici](#69-esempi-pratici)
|
||
|
||
---
|
||
|
||
## 6.1 Architettura Ruoli e Permessi
|
||
|
||
### Gerarchia Ruoli NetGescon
|
||
```
|
||
SuperAdmin
|
||
├── Accesso completo sistema
|
||
├── Gestione utenti e ruoli
|
||
├── Configurazione globale
|
||
├── Archivi base (comuni)
|
||
└── Monitoring e logs
|
||
|
||
Admin
|
||
├── Gestione completa stabili assegnati
|
||
├── CRUD completo tutti i dati
|
||
├── Contabilità e documenti
|
||
├── Comunicazioni condomini
|
||
└── Backup e reports
|
||
|
||
Amministratore
|
||
├── Gestione stabili assegnati
|
||
├── CRUD limitato (no delete critici)
|
||
├── Contabilità base
|
||
├── Documenti e comunicazioni
|
||
└── Reports stabili
|
||
|
||
Condomino
|
||
├── Visualizzazione dati personali
|
||
├── Documenti unità di proprietà
|
||
├── Comunicazioni con amministratore
|
||
├── Consultazione movimenti
|
||
└── Modifica profilo personale
|
||
```
|
||
|
||
### Matrice Permessi Dettagliata
|
||
```
|
||
FUNZIONALITÀ | SuperAdmin | Admin | Amministratore | Condomino
|
||
-----------------------|------------|-------|----------------|----------
|
||
Gestione Utenti | ✅ | ❌ | ❌ | ❌
|
||
Comuni Italiani | ✅ | ❌ | ❌ | ❌
|
||
Stabili (CRUD) | ✅ | ✅ | ✅ | ❌
|
||
Unità Immobiliari | ✅ | ✅ | ✅ | 👁️
|
||
Soggetti (CRUD) | ✅ | ✅ | ✅ | ❌
|
||
Documenti (Upload) | ✅ | ✅ | ✅ | ❌
|
||
Documenti (Download) | ✅ | ✅ | ✅ | ✅
|
||
Contabilità (CRUD) | ✅ | ✅ | ⚠️ | 👁️
|
||
Tickets (Gestione) | ✅ | ✅ | ✅ | 📝
|
||
Backup Sistema | ✅ | ✅ | ❌ | ❌
|
||
Reports Avanzati | ✅ | ✅ | ⚠️ | ❌
|
||
```
|
||
|
||
*Legenda: ✅ = Completo, ⚠️ = Limitato, 👁️ = Solo visualizzazione, 📝 = Creazione/Risposta, ❌ = Negato*
|
||
|
||
---
|
||
|
||
## 6.2 Configurazione Spatie Permission
|
||
|
||
### Installazione Package
|
||
```bash
|
||
composer require spatie/laravel-permission
|
||
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
|
||
php artisan migrate
|
||
```
|
||
|
||
### Configurazione
|
||
**File**: `config/permission.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
return [
|
||
'models' => [
|
||
'permission' => Spatie\Permission\Models\Permission::class,
|
||
'role' => Spatie\Permission\Models\Role::class,
|
||
],
|
||
|
||
'table_names' => [
|
||
'roles' => 'roles',
|
||
'permissions' => 'permissions',
|
||
'model_has_permissions' => 'model_has_permissions',
|
||
'model_has_roles' => 'model_has_roles',
|
||
'role_has_permissions' => 'role_has_permissions',
|
||
],
|
||
|
||
'column_names' => [
|
||
'role_pivot_key' => null,
|
||
'permission_pivot_key' => null,
|
||
'model_morph_key' => 'model_id',
|
||
'team_foreign_key' => 'team_id',
|
||
],
|
||
|
||
'register_permission_check_method' => true,
|
||
'teams' => false,
|
||
'use_passport_client_credentials' => false,
|
||
'display_permission_in_exception' => false,
|
||
'display_role_in_exception' => false,
|
||
'enable_wildcard_permission' => false,
|
||
'cache' => [
|
||
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||
'key' => 'spatie.permission.cache',
|
||
'store' => 'default',
|
||
],
|
||
];
|
||
```
|
||
|
||
### Registrazione Middleware
|
||
**File**: `app/Http/Kernel.php`
|
||
|
||
```php
|
||
protected $routeMiddleware = [
|
||
// ...existing middleware...
|
||
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
|
||
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
|
||
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
|
||
];
|
||
```
|
||
|
||
---
|
||
|
||
## 6.3 Modelli e Relazioni
|
||
|
||
### Modello User Esteso
|
||
**File**: `app/Models/User.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Laravel\Sanctum\HasApiTokens;
|
||
use Spatie\Permission\Traits\HasRoles;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use HasApiTokens, HasRoles, Notifiable;
|
||
|
||
protected $fillable = [
|
||
'name',
|
||
'email',
|
||
'password',
|
||
'is_active',
|
||
'last_login_at',
|
||
'profile_photo_path',
|
||
'phone',
|
||
'address',
|
||
];
|
||
|
||
protected $hidden = [
|
||
'password',
|
||
'remember_token',
|
||
];
|
||
|
||
protected $casts = [
|
||
'email_verified_at' => 'datetime',
|
||
'last_login_at' => 'datetime',
|
||
'is_active' => 'boolean',
|
||
];
|
||
|
||
// Relazioni
|
||
public function stabiliAmministrati()
|
||
{
|
||
return $this->hasMany(Stabile::class, 'amministratore_id');
|
||
}
|
||
|
||
public function unitaImmobiliari()
|
||
{
|
||
return $this->belongsToMany(UnitaImmobiliare::class, 'diritti_reali')
|
||
->withPivot('tipo_diritto', 'quota_proprieta', 'data_inizio', 'data_fine')
|
||
->withTimestamps();
|
||
}
|
||
|
||
public function tickets()
|
||
{
|
||
return $this->hasMany(Ticket::class);
|
||
}
|
||
|
||
// Helper methods
|
||
public function isSuperAdmin(): bool
|
||
{
|
||
return $this->hasRole('super-admin');
|
||
}
|
||
|
||
public function isAdmin(): bool
|
||
{
|
||
return $this->hasRole('admin');
|
||
}
|
||
|
||
public function isAmministratore(): bool
|
||
{
|
||
return $this->hasRole('amministratore');
|
||
}
|
||
|
||
public function isCondomino(): bool
|
||
{
|
||
return $this->hasRole('condomino');
|
||
}
|
||
|
||
public function canManageStabile(Stabile $stabile): bool
|
||
{
|
||
if ($this->isSuperAdmin()) {
|
||
return true;
|
||
}
|
||
|
||
if ($this->isAdmin() || $this->isAmministratore()) {
|
||
return $this->stabiliAmministrati()->where('id', $stabile->id)->exists();
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public function canAccessUnita(UnitaImmobiliare $unita): bool
|
||
{
|
||
if ($this->isSuperAdmin()) {
|
||
return true;
|
||
}
|
||
|
||
if ($this->isAdmin() || $this->isAmministratore()) {
|
||
return $this->canManageStabile($unita->stabile);
|
||
}
|
||
|
||
if ($this->isCondomino()) {
|
||
return $this->unitaImmobiliari()->where('id', $unita->id)->exists();
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public function getDisplayRoleName(): string
|
||
{
|
||
$roleNames = [
|
||
'super-admin' => 'Super Amministratore',
|
||
'admin' => 'Amministratore',
|
||
'amministratore' => 'Amministratore Condominio',
|
||
'condomino' => 'Condomino',
|
||
];
|
||
|
||
$role = $this->getRoleNames()->first();
|
||
return $roleNames[$role] ?? 'Utente';
|
||
}
|
||
|
||
public function getRoleColor(): string
|
||
{
|
||
$roleColors = [
|
||
'super-admin' => 'danger',
|
||
'admin' => 'primary',
|
||
'amministratore' => 'warning',
|
||
'condomino' => 'success',
|
||
];
|
||
|
||
$role = $this->getRoleNames()->first();
|
||
return $roleColors[$role] ?? 'secondary';
|
||
}
|
||
}
|
||
```
|
||
|
||
### Modello Stabile con Controlli Accesso
|
||
**File**: `app/Models/Stabile.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||
|
||
class Stabile extends Model
|
||
{
|
||
protected $fillable = [
|
||
'denominazione',
|
||
'indirizzo',
|
||
'comune_id',
|
||
'cap',
|
||
'codice_fiscale',
|
||
'partita_iva',
|
||
'amministratore_id',
|
||
'is_active',
|
||
'note',
|
||
];
|
||
|
||
protected $casts = [
|
||
'is_active' => 'boolean',
|
||
];
|
||
|
||
// Relazioni
|
||
public function amministratore(): BelongsTo
|
||
{
|
||
return $this->belongsTo(User::class, 'amministratore_id');
|
||
}
|
||
|
||
public function comune(): BelongsTo
|
||
{
|
||
return $this->belongsTo(ComuneItaliano::class, 'comune_id');
|
||
}
|
||
|
||
public function unitaImmobiliari(): HasMany
|
||
{
|
||
return $this->hasMany(UnitaImmobiliare::class);
|
||
}
|
||
|
||
public function documenti(): HasMany
|
||
{
|
||
return $this->hasMany(Documento::class);
|
||
}
|
||
|
||
public function tickets(): HasMany
|
||
{
|
||
return $this->hasMany(Ticket::class);
|
||
}
|
||
|
||
public function movimentiBancari(): HasMany
|
||
{
|
||
return $this->hasMany(MovimentoBancario::class);
|
||
}
|
||
|
||
// Scope per filtri di accesso
|
||
public function scopeAccessibleBy($query, User $user)
|
||
{
|
||
if ($user->isSuperAdmin()) {
|
||
return $query;
|
||
}
|
||
|
||
if ($user->isAdmin() || $user->isAmministratore()) {
|
||
return $query->where('amministratore_id', $user->id);
|
||
}
|
||
|
||
if ($user->isCondomino()) {
|
||
return $query->whereHas('unitaImmobiliari.soggetti', function($q) use ($user) {
|
||
$q->where('users.id', $user->id);
|
||
});
|
||
}
|
||
|
||
return $query->whereRaw('1 = 0'); // Nessun accesso
|
||
}
|
||
|
||
// Helper methods
|
||
public function getIndirizzoCompleto(): string
|
||
{
|
||
$indirizzo = $this->indirizzo;
|
||
|
||
if ($this->comune) {
|
||
$indirizzo .= ', ' . $this->comune->denominazione;
|
||
}
|
||
|
||
if ($this->cap) {
|
||
$indirizzo .= ' (' . $this->cap . ')';
|
||
}
|
||
|
||
return $indirizzo;
|
||
}
|
||
|
||
public function getTotalUnita(): int
|
||
{
|
||
return $this->unitaImmobiliari()->count();
|
||
}
|
||
|
||
public function getTotalMillesimi(): float
|
||
{
|
||
return $this->unitaImmobiliari()->sum('millesimi_proprieta');
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.4 Seeder Ruoli e Permessi
|
||
|
||
### Seeder Completo
|
||
**File**: `database/seeders/RolesAndPermissionsSeeder.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace Database\Seeders;
|
||
|
||
use Illuminate\Database\Seeder;
|
||
use Spatie\Permission\Models\Role;
|
||
use Spatie\Permission\Models\Permission;
|
||
|
||
class RolesAndPermissionsSeeder extends Seeder
|
||
{
|
||
public function run(): void
|
||
{
|
||
// Reset cached roles and permissions
|
||
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
||
|
||
// Definizione permessi organizzati per area
|
||
$permissions = [
|
||
// Gestione Utenti
|
||
'users.view',
|
||
'users.create',
|
||
'users.edit',
|
||
'users.delete',
|
||
'users.assign-roles',
|
||
'users.toggle-status',
|
||
|
||
// Gestione Stabili
|
||
'stabili.view',
|
||
'stabili.create',
|
||
'stabili.edit',
|
||
'stabili.delete',
|
||
'stabili.assign-admin',
|
||
|
||
// Gestione Unità Immobiliari
|
||
'unita.view',
|
||
'unita.create',
|
||
'unita.edit',
|
||
'unita.delete',
|
||
'unita.assign-soggetti',
|
||
|
||
// Gestione Soggetti
|
||
'soggetti.view',
|
||
'soggetti.create',
|
||
'soggetti.edit',
|
||
'soggetti.delete',
|
||
'soggetti.merge',
|
||
|
||
// Gestione Documenti
|
||
'documenti.view',
|
||
'documenti.upload',
|
||
'documenti.download',
|
||
'documenti.delete',
|
||
'documenti.share',
|
||
|
||
// Gestione Contabilità
|
||
'contabilita.view',
|
||
'contabilita.create',
|
||
'contabilita.edit',
|
||
'contabilita.delete',
|
||
'contabilita.reports',
|
||
'contabilita.export',
|
||
|
||
// Gestione Tickets
|
||
'tickets.view',
|
||
'tickets.create',
|
||
'tickets.edit',
|
||
'tickets.delete',
|
||
'tickets.assign',
|
||
'tickets.close',
|
||
|
||
// Configurazione Sistema
|
||
'system.config',
|
||
'system.backup',
|
||
'system.logs',
|
||
'system.maintenance',
|
||
|
||
// Archivi Base
|
||
'comuni.view',
|
||
'comuni.edit',
|
||
'comuni.import',
|
||
];
|
||
|
||
// Crea tutti i permessi
|
||
foreach ($permissions as $permission) {
|
||
Permission::create(['name' => $permission]);
|
||
}
|
||
|
||
// Creazione ruoli
|
||
$superAdmin = Role::create(['name' => 'super-admin']);
|
||
$admin = Role::create(['name' => 'admin']);
|
||
$amministratore = Role::create(['name' => 'amministratore']);
|
||
$condomino = Role::create(['name' => 'condomino']);
|
||
|
||
// SuperAdmin: tutti i permessi
|
||
$superAdmin->givePermissionTo(Permission::all());
|
||
|
||
// Admin: tutti tranne configurazione sistema
|
||
$admin->givePermissionTo([
|
||
'stabili.view', 'stabili.create', 'stabili.edit', 'stabili.delete',
|
||
'unita.view', 'unita.create', 'unita.edit', 'unita.delete', 'unita.assign-soggetti',
|
||
'soggetti.view', 'soggetti.create', 'soggetti.edit', 'soggetti.delete', 'soggetti.merge',
|
||
'documenti.view', 'documenti.upload', 'documenti.download', 'documenti.delete', 'documenti.share',
|
||
'contabilita.view', 'contabilita.create', 'contabilita.edit', 'contabilita.delete', 'contabilita.reports', 'contabilita.export',
|
||
'tickets.view', 'tickets.create', 'tickets.edit', 'tickets.delete', 'tickets.assign', 'tickets.close',
|
||
'system.backup', 'system.logs',
|
||
]);
|
||
|
||
// Amministratore: gestione limitata
|
||
$amministratore->givePermissionTo([
|
||
'stabili.view', 'stabili.edit',
|
||
'unita.view', 'unita.create', 'unita.edit', 'unita.assign-soggetti',
|
||
'soggetti.view', 'soggetti.create', 'soggetti.edit',
|
||
'documenti.view', 'documenti.upload', 'documenti.download', 'documenti.share',
|
||
'contabilita.view', 'contabilita.create', 'contabilita.edit', 'contabilita.reports',
|
||
'tickets.view', 'tickets.create', 'tickets.edit', 'tickets.assign', 'tickets.close',
|
||
]);
|
||
|
||
// Condomino: solo visualizzazione e interazione limitata
|
||
$condomino->givePermissionTo([
|
||
'unita.view',
|
||
'documenti.view', 'documenti.download',
|
||
'contabilita.view',
|
||
'tickets.view', 'tickets.create',
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Seeder Utenti Base
|
||
**File**: `database/seeders/UserSeeder.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace Database\Seeders;
|
||
|
||
use Illuminate\Database\Seeder;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use App\Models\User;
|
||
|
||
class UserSeeder extends Seeder
|
||
{
|
||
public function run(): void
|
||
{
|
||
// SuperAdmin
|
||
$superAdmin = User::create([
|
||
'name' => 'Super Amministratore',
|
||
'email' => 'superadmin@netgescon.it',
|
||
'password' => Hash::make('password'),
|
||
'is_active' => true,
|
||
'email_verified_at' => now(),
|
||
]);
|
||
$superAdmin->assignRole('super-admin');
|
||
|
||
// Admin
|
||
$admin = User::create([
|
||
'name' => 'Amministratore Principale',
|
||
'email' => 'admin@netgescon.it',
|
||
'password' => Hash::make('password'),
|
||
'is_active' => true,
|
||
'email_verified_at' => now(),
|
||
]);
|
||
$admin->assignRole('admin');
|
||
|
||
// Amministratore Condominio
|
||
$amministratore = User::create([
|
||
'name' => 'Mario Rossi',
|
||
'email' => 'mario.rossi@netgescon.it',
|
||
'password' => Hash::make('password'),
|
||
'is_active' => true,
|
||
'email_verified_at' => now(),
|
||
]);
|
||
$amministratore->assignRole('amministratore');
|
||
|
||
// Condomino
|
||
$condomino = User::create([
|
||
'name' => 'Luigi Bianchi',
|
||
'email' => 'luigi.bianchi@netgescon.it',
|
||
'password' => Hash::make('password'),
|
||
'is_active' => true,
|
||
'email_verified_at' => now(),
|
||
]);
|
||
$condomino->assignRole('condomino');
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.5 Middleware di Protezione
|
||
|
||
### Middleware Personalizzato
|
||
**File**: `app/Http/Middleware/RoleAccessMiddleware.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Middleware;
|
||
|
||
use Closure;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class RoleAccessMiddleware
|
||
{
|
||
public function handle(Request $request, Closure $next, ...$roles)
|
||
{
|
||
if (!Auth::check()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$user = Auth::user();
|
||
|
||
// Verifica se utente è attivo
|
||
if (!$user->is_active) {
|
||
Auth::logout();
|
||
return redirect()->route('login')
|
||
->with('error', 'Account disattivato. Contatta l\'amministratore.');
|
||
}
|
||
|
||
// Verifica ruoli
|
||
if (!$user->hasAnyRole($roles)) {
|
||
abort(403, 'Accesso non autorizzato per questo ruolo.');
|
||
}
|
||
|
||
// Aggiorna ultimo login
|
||
$user->update(['last_login_at' => now()]);
|
||
|
||
return $next($request);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Middleware Permessi Specifici
|
||
**File**: `app/Http/Middleware/PermissionMiddleware.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Middleware;
|
||
|
||
use Closure;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class PermissionMiddleware
|
||
{
|
||
public function handle(Request $request, Closure $next, $permission)
|
||
{
|
||
if (!Auth::check()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$user = Auth::user();
|
||
|
||
if (!$user->can($permission)) {
|
||
if ($request->ajax()) {
|
||
return response()->json([
|
||
'error' => 'Permesso negato',
|
||
'message' => "Non hai i permessi per: {$permission}"
|
||
], 403);
|
||
}
|
||
|
||
abort(403, "Permesso negato: {$permission}");
|
||
}
|
||
|
||
return $next($request);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.6 Controllers per Ruoli
|
||
|
||
### Controller SuperAdmin
|
||
**File**: `app/Http/Controllers/SuperAdmin/SuperAdminDashboardController.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Controllers\SuperAdmin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\User;
|
||
use App\Models\Stabile;
|
||
use App\Models\ComuneItaliano;
|
||
use App\Models\Ticket;
|
||
|
||
class SuperAdminDashboardController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware(['auth', 'role:super-admin']);
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$stats = [
|
||
'users_total' => User::count(),
|
||
'users_active' => User::where('is_active', true)->count(),
|
||
'stabili_total' => Stabile::count(),
|
||
'stabili_active' => Stabile::where('is_active', true)->count(),
|
||
'comuni_loaded' => ComuneItaliano::count(),
|
||
'tickets_open' => Ticket::where('stato', 'aperto')->count(),
|
||
'users_by_role' => User::join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id')
|
||
->join('roles', 'model_has_roles.role_id', '=', 'roles.id')
|
||
->selectRaw('roles.name, COUNT(*) as count')
|
||
->groupBy('roles.name')
|
||
->pluck('count', 'name')
|
||
->toArray(),
|
||
];
|
||
|
||
return view('superadmin.dashboard', [
|
||
'pageTitle' => 'Dashboard Super Amministratore',
|
||
'stats' => $stats,
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Controller Admin
|
||
**File**: `app/Http/Controllers/Admin/AdminDashboardController.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\Stabile;
|
||
use App\Models\UnitaImmobiliare;
|
||
use App\Models\Documento;
|
||
use App\Models\Ticket;
|
||
use App\Models\MovimentoBancario;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class AdminDashboardController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware(['auth', 'role:admin|amministratore']);
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$user = Auth::user();
|
||
$stabiliIds = $user->stabiliAmministrati()->pluck('id');
|
||
|
||
$stats = [
|
||
'stabili_count' => $stabiliIds->count(),
|
||
'stabili_attivi' => $user->stabiliAmministrati()->where('is_active', true)->count(),
|
||
'unita_total' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)->count(),
|
||
'documenti_count' => Documento::whereIn('stabile_id', $stabiliIds)->count(),
|
||
'documenti_mese' => Documento::whereIn('stabile_id', $stabiliIds)
|
||
->whereMonth('created_at', now()->month)
|
||
->count(),
|
||
'tickets_aperti' => Ticket::whereIn('stabile_id', $stabiliIds)
|
||
->where('stato', 'aperto')->count(),
|
||
'tickets_totali' => Ticket::whereIn('stabile_id', $stabiliIds)->count(),
|
||
'saldo_totale' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)
|
||
->sum('importo'),
|
||
'condomini_count' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)
|
||
->has('soggetti')->count(),
|
||
'proprietari_count' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)
|
||
->whereHas('soggetti', function($q) {
|
||
$q->where('tipo_diritto', 'proprietario');
|
||
})->count(),
|
||
'movimenti_count' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)->count(),
|
||
];
|
||
|
||
return view('admin.dashboard', [
|
||
'pageTitle' => 'Dashboard Amministrazione',
|
||
'stats' => $stats,
|
||
'user' => $user,
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Controller Condomino
|
||
**File**: `app/Http/Controllers/Condomino/CondominoDashboardController.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Controllers\Condomino;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\UnitaImmobiliare;
|
||
use App\Models\Documento;
|
||
use App\Models\Ticket;
|
||
use App\Models\MovimentoBancario;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class CondominoDashboardController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware(['auth', 'role:condomino']);
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$user = Auth::user();
|
||
$unitaIds = $user->unitaImmobiliari()->pluck('unita_immobiliari.id');
|
||
$stabiliIds = $user->unitaImmobiliari()->pluck('stabile_id');
|
||
|
||
$stats = [
|
||
'unita_count' => $unitaIds->count(),
|
||
'documenti_count' => Documento::whereIn('stabile_id', $stabiliIds)->count(),
|
||
'tickets_aperti' => Ticket::where('user_id', $user->id)
|
||
->where('stato', 'aperto')->count(),
|
||
'tickets_totali' => Ticket::where('user_id', $user->id)->count(),
|
||
'movimenti_mese' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)
|
||
->whereMonth('data_movimento', now()->month)
|
||
->count(),
|
||
];
|
||
|
||
$unita = $user->unitaImmobiliari()
|
||
->with(['stabile', 'stabile.comune'])
|
||
->get();
|
||
|
||
return view('condomino.dashboard', [
|
||
'pageTitle' => 'Area Condomino',
|
||
'stats' => $stats,
|
||
'unita' => $unita,
|
||
'user' => $user,
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.7 Dashboard Condizionali
|
||
|
||
### Router Principale
|
||
**File**: `app/Http/Controllers/DashboardController.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class DashboardController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware('auth');
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$user = Auth::user();
|
||
|
||
// Redirect in base al ruolo
|
||
if ($user->hasRole('super-admin')) {
|
||
return redirect()->route('superadmin.dashboard');
|
||
}
|
||
|
||
if ($user->hasAnyRole(['admin', 'amministratore'])) {
|
||
return redirect()->route('admin.dashboard');
|
||
}
|
||
|
||
if ($user->hasRole('condomino')) {
|
||
return redirect()->route('condomino.dashboard');
|
||
}
|
||
|
||
// Fallback per utenti senza ruolo
|
||
return view('dashboard.no-role', [
|
||
'pageTitle' => 'Accesso Negato',
|
||
'message' => 'Nessun ruolo assegnato. Contatta l\'amministratore.',
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.8 Gestione Utenti
|
||
|
||
### Form Assegnazione Ruoli
|
||
**File**: `resources/views/superadmin/users/edit-roles.blade.php`
|
||
|
||
```blade
|
||
<form action="{{ route('superadmin.users.update-roles', $user) }}" method="POST">
|
||
@csrf
|
||
@method('PUT')
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h5>Gestione Ruoli: {{ $user->name }}</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
@foreach($roles as $role)
|
||
<div class="col-md-6 mb-3">
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox"
|
||
name="roles[]" value="{{ $role->name }}"
|
||
id="role_{{ $role->id }}"
|
||
{{ $user->hasRole($role->name) ? 'checked' : '' }}>
|
||
<label class="form-check-label" for="role_{{ $role->id }}">
|
||
<strong>{{ $role->name }}</strong>
|
||
<small class="text-muted d-block">
|
||
{{ $roleDescriptions[$role->name] ?? '' }}
|
||
</small>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
<div class="card-footer">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-save me-2"></i>Salva Ruoli
|
||
</button>
|
||
<a href="{{ route('superadmin.users.index') }}" class="btn btn-secondary">
|
||
<i class="fas fa-times me-2"></i>Annulla
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
```
|
||
|
||
### Controller Gestione Ruoli
|
||
**File**: `app/Http/Controllers/SuperAdmin/UserRoleController.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Http\Controllers\SuperAdmin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\User;
|
||
use Illuminate\Http\Request;
|
||
use Spatie\Permission\Models\Role;
|
||
|
||
class UserRoleController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware(['auth', 'role:super-admin']);
|
||
}
|
||
|
||
public function editRoles(User $user)
|
||
{
|
||
$roles = Role::all();
|
||
$roleDescriptions = [
|
||
'super-admin' => 'Accesso completo al sistema',
|
||
'admin' => 'Gestione completa stabili assegnati',
|
||
'amministratore' => 'Gestione limitata stabili assegnati',
|
||
'condomino' => 'Visualizzazione dati personali',
|
||
];
|
||
|
||
return view('superadmin.users.edit-roles', compact('user', 'roles', 'roleDescriptions'));
|
||
}
|
||
|
||
public function updateRoles(Request $request, User $user)
|
||
{
|
||
$request->validate([
|
||
'roles' => 'array',
|
||
'roles.*' => 'exists:roles,name',
|
||
]);
|
||
|
||
$user->syncRoles($request->get('roles', []));
|
||
|
||
return redirect()->route('superadmin.users.index')
|
||
->with('success', "Ruoli aggiornati per {$user->name}");
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6.9 Esempi Pratici
|
||
|
||
### Verifica Permessi in Blade
|
||
```blade
|
||
@can('stabili.create')
|
||
<a href="{{ route('admin.stabili.create') }}" class="btn btn-primary">
|
||
<i class="fas fa-plus me-2"></i>Nuovo Stabile
|
||
</a>
|
||
@endcan
|
||
|
||
@role('super-admin')
|
||
<div class="alert alert-info">
|
||
<i class="fas fa-crown me-2"></i>
|
||
Modalità Super Amministratore attiva
|
||
</div>
|
||
@endrole
|
||
|
||
@hasrole('admin|amministratore')
|
||
<div class="card">
|
||
<div class="card-header">Pannello Gestione</div>
|
||
<!-- Contenuto per admin -->
|
||
</div>
|
||
@endhasrole
|
||
```
|
||
|
||
### Controllo Accesso in Controller
|
||
```php
|
||
public function show(Stabile $stabile)
|
||
{
|
||
$user = Auth::user();
|
||
|
||
// Verifica accesso al singolo stabile
|
||
if (!$user->canManageStabile($stabile)) {
|
||
abort(403, 'Non puoi accedere a questo stabile');
|
||
}
|
||
|
||
return view('admin.stabili.show', compact('stabile'));
|
||
}
|
||
```
|
||
|
||
### Policy per Modelli
|
||
**File**: `app/Policies/StabilePolicy.php`
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Policies;
|
||
|
||
use App\Models\Stabile;
|
||
use App\Models\User;
|
||
|
||
class StabilePolicy
|
||
{
|
||
public function viewAny(User $user): bool
|
||
{
|
||
return $user->can('stabili.view');
|
||
}
|
||
|
||
public function view(User $user, Stabile $stabile): bool
|
||
{
|
||
return $user->can('stabili.view') && $user->canManageStabile($stabile);
|
||
}
|
||
|
||
public function create(User $user): bool
|
||
{
|
||
return $user->can('stabili.create');
|
||
}
|
||
|
||
public function update(User $user, Stabile $stabile): bool
|
||
{
|
||
return $user->can('stabili.edit') && $user->canManageStabile($stabile);
|
||
}
|
||
|
||
public function delete(User $user, Stabile $stabile): bool
|
||
{
|
||
return $user->can('stabili.delete') && $user->canManageStabile($stabile);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 6.10 CREAZIONE MODULI NETGESCON - GUIDA COMPLETA 🚀
|
||
|
||
### 📋 PROBLEMI COMUNI E SOLUZIONI DEFINITIVE
|
||
|
||
#### 🔥 **ERRORI CRITICI DA EVITARE**
|
||
|
||
1. **Layout NON NetGescon**
|
||
```php
|
||
// ❌ SBAGLIATO
|
||
@extends('modules.layouts.base')
|
||
|
||
// ✅ CORRETTO
|
||
@extends('admin.layouts.netgescon')
|
||
```
|
||
|
||
2. **Database Columns Inesistenti**
|
||
```php
|
||
// ❌ SBAGLIATO - Campo 'codice' non esiste
|
||
$piano_conti = DB::table('piano_conti')->select('codice', 'descrizione')->get();
|
||
|
||
// ✅ CORRETTO - Campi reali della tabella
|
||
$piano_conti = DB::table('piano_conti')->select('mastro', 'conto', 'sottoconto', 'descrizione')->get();
|
||
```
|
||
|
||
3. **Route Conflicts**
|
||
```php
|
||
// ❌ SBAGLIATO - Conflitti con routes esistenti
|
||
Route::resource('contabilita', ContabilitaController::class);
|
||
|
||
// ✅ CORRETTO - Prefix specifico per moduli
|
||
Route::prefix('admin')->name('admin.')->group(function () {
|
||
Route::resource('contabilita', ContabilitaController::class)->names([
|
||
'index' => 'contabilita.index',
|
||
'create' => 'contabilita.create',
|
||
'store' => 'contabilita.store',
|
||
'show' => 'contabilita.show',
|
||
'edit' => 'contabilita.edit',
|
||
'update' => 'contabilita.update',
|
||
'destroy' => 'contabilita.destroy'
|
||
]);
|
||
});
|
||
```
|
||
|
||
### 🏗️ **STRUTTURA MODULO STANDARD**
|
||
|
||
```
|
||
app/Modules/[NomeModulo]/
|
||
├── Controllers/
|
||
│ └── [NomeModulo]Controller.php
|
||
├── Models/
|
||
│ └── [NomeModulo].php
|
||
├── Routes/
|
||
│ └── web.php
|
||
└── Views/
|
||
├── index.blade.php
|
||
├── create.blade.php
|
||
├── edit.blade.php
|
||
└── show.blade.php
|
||
|
||
resources/views/modules/[nomemodulo]/
|
||
├── index.blade.php
|
||
├── create.blade.php
|
||
├── edit.blade.php
|
||
└── show.blade.php
|
||
```
|
||
|
||
### 🎨 **TEMPLATE BASE NETGESCON**
|
||
|
||
#### Index.blade.php Standard
|
||
```php
|
||
@extends('admin.layouts.netgescon')
|
||
|
||
@section('title', '[Nome Modulo]')
|
||
|
||
@section('content')
|
||
<div class="content-wrapper">
|
||
<div class="content-header">
|
||
<div class="container-fluid">
|
||
<div class="row mb-2">
|
||
<div class="col-sm-6">
|
||
<h1 class="m-0">[Nome Modulo]</h1>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<ol class="breadcrumb float-sm-right">
|
||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Home</a></li>
|
||
<li class="breadcrumb-item active">[Nome Modulo]</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="content">
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Elenco [Nome Modulo]</h3>
|
||
<div class="card-tools">
|
||
<a href="{{ route('admin.[nomemodulo].create') }}" class="btn btn-primary btn-sm">
|
||
<i class="fas fa-plus"></i> Nuovo
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<!-- Contenuto tabella/lista -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
@endsection
|
||
```
|
||
|
||
#### Create.blade.php Standard
|
||
```php
|
||
@extends('admin.layouts.netgescon')
|
||
|
||
@section('title', 'Nuovo [Nome Modulo]')
|
||
|
||
@section('content')
|
||
<div class="content-wrapper">
|
||
<div class="content-header">
|
||
<div class="container-fluid">
|
||
<div class="row mb-2">
|
||
<div class="col-sm-6">
|
||
<h1 class="m-0">Nuovo [Nome Modulo]</h1>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<ol class="breadcrumb float-sm-right">
|
||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Home</a></li>
|
||
<li class="breadcrumb-item"><a href="{{ route('admin.[nomemodulo].index') }}">[Nome Modulo]</a></li>
|
||
<li class="breadcrumb-item active">Nuovo</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="content">
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Nuovo [Nome Modulo]</h3>
|
||
</div>
|
||
<form action="{{ route('admin.[nomemodulo].store') }}" method="POST">
|
||
@csrf
|
||
<div class="card-body">
|
||
<!-- Form fields -->
|
||
</div>
|
||
<div class="card-footer">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-save"></i> Salva
|
||
</button>
|
||
<a href="{{ route('admin.[nomemodulo].index') }}" class="btn btn-secondary">
|
||
<i class="fas fa-arrow-left"></i> Annulla
|
||
</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
@endsection
|
||
```
|
||
|
||
### <20>️ **CONTROLLER STANDARD**
|
||
|
||
```php
|
||
<?php
|
||
|
||
namespace App\Modules\[NomeModulo]\Controllers;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
class [NomeModulo]Controller extends Controller
|
||
{
|
||
public function index()
|
||
{
|
||
// Verifica credenziali database corrette
|
||
// netgescon_user / NetGescon2024!
|
||
|
||
$items = DB::table('[nome_tabella]')->get();
|
||
|
||
return view('modules.[nomemodulo].index', compact('items'));
|
||
}
|
||
|
||
public function create()
|
||
{
|
||
// Carica dati necessari per il form
|
||
// ATTENZIONE: Usa SEMPRE i nomi colonne reali del DB
|
||
|
||
return view('modules.[nomemodulo].create');
|
||
}
|
||
|
||
public function store(Request $request)
|
||
{
|
||
$request->validate([
|
||
// Validazioni
|
||
]);
|
||
|
||
DB::table('[nome_tabella]')->insert([
|
||
// Campi corretti del database
|
||
'created_at' => now(),
|
||
'updated_at' => now()
|
||
]);
|
||
|
||
return redirect()->route('admin.[nomemodulo].index')
|
||
->with('success', '[Nome Modulo] creato con successo!');
|
||
}
|
||
|
||
public function show($id)
|
||
{
|
||
$item = DB::table('[nome_tabella]')->where('id', $id)->first();
|
||
|
||
if (!$item) {
|
||
abort(404);
|
||
}
|
||
|
||
return view('modules.[nomemodulo].show', compact('item'));
|
||
}
|
||
|
||
public function edit($id)
|
||
{
|
||
$item = DB::table('[nome_tabella]')->where('id', $id)->first();
|
||
|
||
if (!$item) {
|
||
abort(404);
|
||
}
|
||
|
||
return view('modules.[nomemodulo].edit', compact('item'));
|
||
}
|
||
|
||
public function update(Request $request, $id)
|
||
{
|
||
$request->validate([
|
||
// Validazioni
|
||
]);
|
||
|
||
DB::table('[nome_tabella]')
|
||
->where('id', $id)
|
||
->update([
|
||
// Campi da aggiornare
|
||
'updated_at' => now()
|
||
]);
|
||
|
||
return redirect()->route('admin.[nomemodulo].index')
|
||
->with('success', '[Nome Modulo] aggiornato con successo!');
|
||
}
|
||
|
||
public function destroy($id)
|
||
{
|
||
DB::table('[nome_tabella]')->where('id', $id)->delete();
|
||
|
||
return redirect()->route('admin.[nomemodulo].index')
|
||
->with('success', '[Nome Modulo] eliminato con successo!');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 🗄️ **STRUTTURA DATABASE STANDARD**
|
||
|
||
#### Credenziali Database NetGescon
|
||
```env
|
||
DB_CONNECTION=mysql
|
||
DB_HOST=127.0.0.1
|
||
DB_PORT=3306
|
||
DB_DATABASE=netgescon
|
||
DB_USERNAME=netgescon_user
|
||
DB_PASSWORD=NetGescon2024!
|
||
```
|
||
|
||
#### Convenzioni Naming Database
|
||
```sql
|
||
-- ✅ CORRETTO - Nomi italiani senza accenti
|
||
CREATE TABLE piano_conti (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
mastro VARCHAR(10) NOT NULL,
|
||
conto VARCHAR(10) NOT NULL,
|
||
sottoconto VARCHAR(10) NOT NULL,
|
||
descrizione VARCHAR(255) NOT NULL,
|
||
tipo ENUM('DARE', 'AVERE') NOT NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Esempio tabella contabilita
|
||
CREATE TABLE registrazioni_contabili (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
data_registrazione DATE NOT NULL,
|
||
numero_documento VARCHAR(50),
|
||
descrizione TEXT NOT NULL,
|
||
totale_dare DECIMAL(10,2) DEFAULT 0,
|
||
totale_avere DECIMAL(10,2) DEFAULT 0,
|
||
stabile_id INT NOT NULL,
|
||
gestione_id INT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (stabile_id) REFERENCES stabili(id),
|
||
FOREIGN KEY (gestione_id) REFERENCES gestioni(id)
|
||
);
|
||
|
||
-- Tabella movimenti contabili
|
||
CREATE TABLE movimenti_contabili (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
registrazione_id INT NOT NULL,
|
||
piano_conti_id INT NOT NULL,
|
||
dare DECIMAL(10,2) DEFAULT 0,
|
||
avere DECIMAL(10,2) DEFAULT 0,
|
||
descrizione VARCHAR(255),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (registrazione_id) REFERENCES registrazioni_contabili(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (piano_conti_id) REFERENCES piano_conti(id)
|
||
);
|
||
```
|
||
|
||
### 🔧 **ROUTES CONFIGURATION**
|
||
|
||
#### File: routes/web.php - Sezione Moduli
|
||
```php
|
||
// ===== MODULI AMMINISTRATIVI =====
|
||
Route::prefix('admin')->name('admin.')->middleware(['auth', 'role:Admin|SuperAdmin'])->group(function () {
|
||
|
||
// Contabilità Module
|
||
Route::resource('contabilita', App\Modules\Contabilita\Controllers\ContabilitaController::class)->names([
|
||
'index' => 'contabilita.index',
|
||
'create' => 'contabilita.create',
|
||
'store' => 'contabilita.store',
|
||
'show' => 'contabilita.show',
|
||
'edit' => 'contabilita.edit',
|
||
'update' => 'contabilita.update',
|
||
'destroy' => 'contabilita.destroy'
|
||
]);
|
||
|
||
// Altri moduli...
|
||
|
||
});
|
||
```
|
||
|
||
### 🎨 **SUB-MENU STYLE COME STABILI**
|
||
|
||
Per implementare il sub-menu come nella pagina stabili, aggiungi questa sezione dopo il content-header:
|
||
|
||
```php
|
||
<!-- Sub-menu tabs come nella pagina stabili -->
|
||
<div class="content-header-tabs">
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<ul class="nav nav-tabs" id="content-tabs" role="tablist">
|
||
<li class="nav-item">
|
||
<a class="nav-link active" href="{{ route('admin.[modulo].index') }}">
|
||
<i class="fas fa-list"></i> Elenco
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="{{ route('admin.[modulo].create') }}">
|
||
<i class="fas fa-plus"></i> Nuovo
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="{{ route('admin.[modulo].reports') }}">
|
||
<i class="fas fa-chart-bar"></i> Reports
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="{{ route('admin.[modulo].config') }}">
|
||
<i class="fas fa-cog"></i> Configurazione
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
### <20>📝 **CHECKLIST MODULO COMPLETO**
|
||
|
||
#### Pre-Sviluppo
|
||
- [ ] Verificare struttura database esistente
|
||
- [ ] Controllare credenziali database (netgescon_user/NetGescon2024!)
|
||
- [ ] Verificare nomi tabelle e colonne reali
|
||
- [ ] Pianificare routes senza conflitti
|
||
|
||
#### Durante Sviluppo
|
||
- [ ] Controller con namespace corretto
|
||
- [ ] Routes con prefix 'admin' e name 'admin.'
|
||
- [ ] Views che estendono 'admin.layouts.netgescon'
|
||
- [ ] Query database con colonne esistenti
|
||
- [ ] Validazioni form corrette
|
||
- [ ] Breadcrumb navigation funzionante
|
||
|
||
#### Post-Sviluppo
|
||
- [ ] Test tutte le routes (index, create, store, show, edit, update, destroy)
|
||
- [ ] Verifica layout NetGescon caricato correttamente
|
||
- [ ] Test permessi e autorizzazioni
|
||
- [ ] Validazione form funzionante
|
||
- [ ] Test CRUD completo
|
||
- [ ] Verifica responsive design
|
||
|
||
### 🚨 **PROBLEMI RISOLTI - CASO CONTABILITÀ**
|
||
|
||
1. **Layout Wrong**: Cambiato da `modules.layouts.base` a `admin.layouts.netgescon`
|
||
2. **Database Columns**: Corretto `codice` inesistente con `mastro`, `conto`, `sottoconto`
|
||
3. **Route Conflicts**: Aggiunto prefix `admin` e namespace specifico
|
||
4. **Credentials**: Usato `netgescon_user/NetGescon2024!` invece di default
|
||
5. **Display Format**: Piano conti ora mostra `mastro.conto.sottoconto - descrizione`
|
||
|
||
### 🎯 **TEMPLATE RAPIDO NUOVO MODULO**
|
||
|
||
Usa questo comando per creare rapidamente la struttura:
|
||
|
||
```bash
|
||
# Crea directories
|
||
mkdir -p app/Modules/[NomeModulo]/Controllers
|
||
mkdir -p app/Modules/[NomeModulo]/Models
|
||
mkdir -p resources/views/modules/[nomemodulo]
|
||
|
||
# Copia template files dal modulo Contabilità funzionante
|
||
cp -r app/Modules/Contabilita/* app/Modules/[NomeModulo]/
|
||
cp -r resources/views/modules/contabilita/* resources/views/modules/[nomemodulo]/
|
||
|
||
# Sostituisci tutti i riferimenti
|
||
find app/Modules/[NomeModulo] -type f -name "*.php" -exec sed -i 's/Contabilita/[NomeModulo]/g' {} \;
|
||
find resources/views/modules/[nomemodulo] -type f -name "*.php" -exec sed -i 's/contabilita/[nomemodulo]/g' {} \;
|
||
```
|
||
|
||
---
|
||
|
||
**📝 COMPLETATO: Capitolo 6 - Sistema Multi-Ruolo + Creazione Moduli**
|
||
|
||
Questo capitolo fornisce una guida completa per:
|
||
- ✅ Configurare architettura ruoli e permessi
|
||
- ✅ Implementare Spatie Permission
|
||
- ✅ Creare modelli con controlli accesso
|
||
- ✅ Configurare seeder ruoli e permessi
|
||
- ✅ Implementare middleware protezione
|
||
- ✅ Creare controllers per ogni ruolo
|
||
- ✅ Gestire dashboard condizionali
|
||
- ✅ Amministrare utenti e ruoli
|
||
- ✅ Esempi pratici implementazione
|
||
- ✅ **GUIDA COMPLETA CREAZIONE MODULI NETGESCON**
|
||
- ✅ **RISOLUZIONE ERRORI COMUNI**
|
||
- ✅ **TEMPLATE STANDARD E CHECKLIST**
|
||
|
||
**🔄 Prossimo capitolo: Database e Strutture Avanzate**
|