📋 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
1773 lines
56 KiB
Markdown
1773 lines
56 KiB
Markdown
# 4. DATABASE E STRUTTURE - GUIDA COMPLETA
|
|
|
|
## 📋 **INDICE CAPITOLO**
|
|
- [4.1 Schema Database Completo](#41-schema-database-completo)
|
|
- [4.2 Migrazioni Laravel](#42-migrazioni-laravel)
|
|
- [4.3 Relazioni e Vincoli](#43-relazioni-e-vincoli)
|
|
- [4.4 Modelli Eloquent](#44-modelli-eloquent)
|
|
- [4.5 Seeder e Dati Base](#45-seeder-e-dati-base)
|
|
- [4.6 Gestione Conflitti Migrazioni](#46-gestione-conflitti-migrazioni)
|
|
- [4.7 Triggers e Codici Univoci](#47-triggers-e-codici-univoci)
|
|
- [4.8 Troubleshooting Database](#48-troubleshooting-database)
|
|
- [4.9 Backup e Ripristino](#49-backup-e-ripristino)
|
|
- [4.10 Sincronizzazione Multi-Macchina](#410-sincronizzazione-multi-macchina)
|
|
|
|
---
|
|
|
|
## 4.1 Schema Database Completo
|
|
|
|
### Tabelle Sistema Utenti
|
|
```sql
|
|
-- Tabella utenti base
|
|
CREATE TABLE users (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
email_verified_at TIMESTAMP NULL,
|
|
password VARCHAR(255) NOT NULL,
|
|
remember_token VARCHAR(100) NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
last_login_at TIMESTAMP NULL,
|
|
profile_photo_path VARCHAR(2048) NULL,
|
|
phone VARCHAR(20) NULL,
|
|
address TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
INDEX idx_email (email),
|
|
INDEX idx_active (is_active),
|
|
INDEX idx_last_login (last_login_at)
|
|
);
|
|
|
|
-- Tabelle Spatie Permission
|
|
CREATE TABLE roles (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
guard_name VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
UNIQUE KEY unique_role_guard (name, guard_name)
|
|
);
|
|
|
|
CREATE TABLE permissions (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
guard_name VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
UNIQUE KEY unique_permission_guard (name, guard_name)
|
|
);
|
|
|
|
CREATE TABLE model_has_roles (
|
|
role_id BIGINT UNSIGNED NOT NULL,
|
|
model_type VARCHAR(255) NOT NULL,
|
|
model_id BIGINT UNSIGNED NOT NULL,
|
|
PRIMARY KEY (role_id, model_id, model_type),
|
|
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
|
|
INDEX idx_model_type (model_type),
|
|
INDEX idx_model_id (model_id)
|
|
);
|
|
|
|
CREATE TABLE model_has_permissions (
|
|
permission_id BIGINT UNSIGNED NOT NULL,
|
|
model_type VARCHAR(255) NOT NULL,
|
|
model_id BIGINT UNSIGNED NOT NULL,
|
|
PRIMARY KEY (permission_id, model_id, model_type),
|
|
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE,
|
|
INDEX idx_model_type (model_type),
|
|
INDEX idx_model_id (model_id)
|
|
);
|
|
|
|
CREATE TABLE role_has_permissions (
|
|
permission_id BIGINT UNSIGNED NOT NULL,
|
|
role_id BIGINT UNSIGNED NOT NULL,
|
|
PRIMARY KEY (permission_id, role_id),
|
|
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
### Tabelle Anagrafica Base
|
|
```sql
|
|
-- Comuni italiani
|
|
CREATE TABLE comuni_italiani (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
codice_catastale VARCHAR(4) NOT NULL UNIQUE,
|
|
denominazione VARCHAR(255) NOT NULL,
|
|
denominazione_tedesca VARCHAR(255) NULL,
|
|
denominazione_altra VARCHAR(255) NULL,
|
|
codice_provincia VARCHAR(2) NOT NULL,
|
|
provincia VARCHAR(255) NOT NULL,
|
|
regione VARCHAR(255) NOT NULL,
|
|
cap VARCHAR(5) NULL,
|
|
prefisso VARCHAR(10) NULL,
|
|
email_pec VARCHAR(255) NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
INDEX idx_codice_catastale (codice_catastale),
|
|
INDEX idx_provincia (codice_provincia),
|
|
INDEX idx_denominazione (denominazione),
|
|
INDEX idx_cap (cap)
|
|
);
|
|
|
|
-- Stabili
|
|
CREATE TABLE stabili (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
denominazione VARCHAR(255) NOT NULL,
|
|
indirizzo VARCHAR(255) NOT NULL,
|
|
comune_id BIGINT UNSIGNED NULL,
|
|
cap VARCHAR(10) NULL,
|
|
codice_fiscale VARCHAR(16) NULL,
|
|
partita_iva VARCHAR(11) NULL,
|
|
amministratore_id BIGINT UNSIGNED NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (comune_id) REFERENCES comuni_italiani(id) ON DELETE SET NULL,
|
|
FOREIGN KEY (amministratore_id) REFERENCES users(id) ON DELETE SET NULL,
|
|
|
|
INDEX idx_denominazione (denominazione),
|
|
INDEX idx_amministratore (amministratore_id),
|
|
INDEX idx_comune (comune_id),
|
|
INDEX idx_active (is_active),
|
|
INDEX idx_codice_fiscale (codice_fiscale)
|
|
);
|
|
|
|
-- Soggetti (persone fisiche e giuridiche)
|
|
CREATE TABLE soggetti (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
tipo_soggetto ENUM('persona_fisica', 'persona_giuridica') NOT NULL,
|
|
nome VARCHAR(255) NULL,
|
|
cognome VARCHAR(255) NULL,
|
|
ragione_sociale VARCHAR(255) NULL,
|
|
codice_fiscale VARCHAR(16) NULL,
|
|
partita_iva VARCHAR(11) NULL,
|
|
data_nascita DATE NULL,
|
|
luogo_nascita VARCHAR(255) NULL,
|
|
indirizzo VARCHAR(255) NULL,
|
|
comune_id BIGINT UNSIGNED NULL,
|
|
cap VARCHAR(10) NULL,
|
|
telefono VARCHAR(20) NULL,
|
|
email VARCHAR(255) NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (comune_id) REFERENCES comuni_italiani(id) ON DELETE SET NULL,
|
|
|
|
INDEX idx_codice_fiscale (codice_fiscale),
|
|
INDEX idx_partita_iva (partita_iva),
|
|
INDEX idx_tipo_soggetto (tipo_soggetto),
|
|
INDEX idx_cognome_nome (cognome, nome),
|
|
INDEX idx_ragione_sociale (ragione_sociale),
|
|
INDEX idx_active (is_active)
|
|
);
|
|
|
|
-- Unità immobiliari
|
|
CREATE TABLE unita_immobiliari (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
stabile_id BIGINT UNSIGNED NOT NULL,
|
|
numero VARCHAR(50) NOT NULL,
|
|
piano VARCHAR(20) NULL,
|
|
scala VARCHAR(20) NULL,
|
|
interno VARCHAR(20) NULL,
|
|
tipo ENUM('appartamento', 'negozio', 'garage', 'cantina', 'altro') NOT NULL DEFAULT 'appartamento',
|
|
categoria_catastale VARCHAR(10) NULL,
|
|
classe_catastale VARCHAR(10) NULL,
|
|
consistenza VARCHAR(20) NULL,
|
|
superficie_catastale DECIMAL(8,2) NULL,
|
|
superficie_commerciale DECIMAL(8,2) NULL,
|
|
rendita_catastale DECIMAL(10,2) NULL,
|
|
millesimi_proprieta DECIMAL(8,4) NULL,
|
|
millesimi_riscaldamento DECIMAL(8,4) NULL,
|
|
millesimi_acqua DECIMAL(8,4) NULL,
|
|
millesimi_ascensore DECIMAL(8,4) NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_stabile (stabile_id),
|
|
INDEX idx_numero (numero),
|
|
INDEX idx_tipo (tipo),
|
|
INDEX idx_active (is_active),
|
|
UNIQUE KEY unique_stabile_numero (stabile_id, numero)
|
|
);
|
|
```
|
|
|
|
### Tabelle Relazioni
|
|
```sql
|
|
-- Diritti reali (collega soggetti a unità immobiliari)
|
|
CREATE TABLE diritti_reali (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
soggetto_id BIGINT UNSIGNED NOT NULL,
|
|
unita_immobiliare_id BIGINT UNSIGNED NOT NULL,
|
|
tipo_diritto ENUM('proprietario', 'nudo_proprietario', 'usufruttuario', 'inquilino', 'comodatario') NOT NULL,
|
|
quota_proprieta DECIMAL(8,4) NULL,
|
|
data_inizio DATE NULL,
|
|
data_fine DATE NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (soggetto_id) REFERENCES soggetti(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_soggetto (soggetto_id),
|
|
INDEX idx_unita (unita_immobiliare_id),
|
|
INDEX idx_tipo_diritto (tipo_diritto),
|
|
INDEX idx_active (is_active),
|
|
INDEX idx_data_inizio (data_inizio),
|
|
INDEX idx_data_fine (data_fine)
|
|
);
|
|
|
|
-- Collegamento users a unità immobiliari (per condomini)
|
|
CREATE TABLE user_unita_immobiliari (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
user_id BIGINT UNSIGNED NOT NULL,
|
|
unita_immobiliare_id BIGINT UNSIGNED NOT NULL,
|
|
tipo_accesso ENUM('proprietario', 'inquilino', 'amministratore') NOT NULL,
|
|
data_inizio DATE NULL,
|
|
data_fine DATE NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_user (user_id),
|
|
INDEX idx_unita (unita_immobiliare_id),
|
|
INDEX idx_tipo_accesso (tipo_accesso),
|
|
INDEX idx_active (is_active),
|
|
UNIQUE KEY unique_user_unita (user_id, unita_immobiliare_id)
|
|
);
|
|
```
|
|
|
|
### Tabelle Gestione Documenti
|
|
```sql
|
|
-- Documenti
|
|
CREATE TABLE documenti (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
stabile_id BIGINT UNSIGNED NOT NULL,
|
|
unita_immobiliare_id BIGINT UNSIGNED NULL,
|
|
soggetto_id BIGINT UNSIGNED NULL,
|
|
user_id BIGINT UNSIGNED NOT NULL,
|
|
titolo VARCHAR(255) NOT NULL,
|
|
descrizione TEXT NULL,
|
|
tipo_documento VARCHAR(100) NULL,
|
|
categoria ENUM('generale', 'amministrativo', 'contabile', 'tecnico', 'legale') NOT NULL DEFAULT 'generale',
|
|
nome_file VARCHAR(255) NOT NULL,
|
|
path_file VARCHAR(500) NOT NULL,
|
|
dimensione_file BIGINT UNSIGNED NULL,
|
|
mime_type VARCHAR(100) NULL,
|
|
is_public BOOLEAN DEFAULT FALSE,
|
|
data_documento DATE NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (soggetto_id) REFERENCES soggetti(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_stabile (stabile_id),
|
|
INDEX idx_unita (unita_immobiliare_id),
|
|
INDEX idx_soggetto (soggetto_id),
|
|
INDEX idx_user (user_id),
|
|
INDEX idx_categoria (categoria),
|
|
INDEX idx_public (is_public),
|
|
INDEX idx_data_documento (data_documento),
|
|
INDEX idx_created_at (created_at)
|
|
);
|
|
```
|
|
|
|
### Tabelle Contabilità
|
|
```sql
|
|
-- Movimenti bancari
|
|
CREATE TABLE movimenti_bancari (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
stabile_id BIGINT UNSIGNED NOT NULL,
|
|
data_movimento DATE NOT NULL,
|
|
data_valuta DATE NULL,
|
|
descrizione TEXT NOT NULL,
|
|
importo DECIMAL(12,2) NOT NULL,
|
|
tipo_movimento ENUM('entrata', 'uscita') NOT NULL,
|
|
categoria VARCHAR(100) NULL,
|
|
sottocategoria VARCHAR(100) NULL,
|
|
causale VARCHAR(255) NULL,
|
|
documento_riferimento VARCHAR(255) NULL,
|
|
conto_corrente VARCHAR(100) NULL,
|
|
is_riconciliato BOOLEAN DEFAULT FALSE,
|
|
user_id BIGINT UNSIGNED NOT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_stabile (stabile_id),
|
|
INDEX idx_data_movimento (data_movimento),
|
|
INDEX idx_tipo_movimento (tipo_movimento),
|
|
INDEX idx_categoria (categoria),
|
|
INDEX idx_riconciliato (is_riconciliato),
|
|
INDEX idx_importo (importo)
|
|
);
|
|
|
|
-- Ripartizioni spese
|
|
CREATE TABLE ripartizioni_spese (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
movimento_bancario_id BIGINT UNSIGNED NOT NULL,
|
|
unita_immobiliare_id BIGINT UNSIGNED NOT NULL,
|
|
importo_ripartito DECIMAL(12,2) NOT NULL,
|
|
quota_millesimi DECIMAL(8,4) NULL,
|
|
tipo_ripartizione ENUM('millesimi_proprieta', 'millesimi_riscaldamento', 'millesimi_acqua', 'millesimi_ascensore', 'fissa') NOT NULL,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (movimento_bancario_id) REFERENCES movimenti_bancari(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_movimento (movimento_bancario_id),
|
|
INDEX idx_unita (unita_immobiliare_id),
|
|
INDEX idx_tipo_ripartizione (tipo_ripartizione)
|
|
);
|
|
```
|
|
|
|
### Tabelle Comunicazioni
|
|
```sql
|
|
-- Tickets/Comunicazioni
|
|
CREATE TABLE tickets (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
stabile_id BIGINT UNSIGNED NOT NULL,
|
|
user_id BIGINT UNSIGNED NOT NULL,
|
|
unita_immobiliare_id BIGINT UNSIGNED NULL,
|
|
assigned_to BIGINT UNSIGNED NULL,
|
|
titolo VARCHAR(255) NOT NULL,
|
|
descrizione TEXT NOT NULL,
|
|
categoria ENUM('manutenzione', 'amministrativo', 'contabile', 'segnalazione', 'richiesta', 'reclamo') NOT NULL DEFAULT 'segnalazione',
|
|
priorita ENUM('bassa', 'media', 'alta', 'urgente') NOT NULL DEFAULT 'media',
|
|
stato ENUM('aperto', 'in_lavorazione', 'in_attesa', 'risolto', 'chiuso') NOT NULL DEFAULT 'aperto',
|
|
data_scadenza DATE NULL,
|
|
data_risoluzione TIMESTAMP NULL,
|
|
is_public BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (stabile_id) REFERENCES stabili(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id) ON DELETE SET NULL,
|
|
FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL,
|
|
|
|
INDEX idx_stabile (stabile_id),
|
|
INDEX idx_user (user_id),
|
|
INDEX idx_unita (unita_immobiliare_id),
|
|
INDEX idx_assigned (assigned_to),
|
|
INDEX idx_categoria (categoria),
|
|
INDEX idx_priorita (priorita),
|
|
INDEX idx_stato (stato),
|
|
INDEX idx_data_scadenza (data_scadenza),
|
|
INDEX idx_created_at (created_at)
|
|
);
|
|
|
|
-- Risposte ai tickets
|
|
CREATE TABLE ticket_risposte (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
ticket_id BIGINT UNSIGNED NOT NULL,
|
|
user_id BIGINT UNSIGNED NOT NULL,
|
|
messaggio TEXT NOT NULL,
|
|
is_internal BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP NULL,
|
|
updated_at TIMESTAMP NULL,
|
|
|
|
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
INDEX idx_ticket (ticket_id),
|
|
INDEX idx_user (user_id),
|
|
INDEX idx_internal (is_internal),
|
|
INDEX idx_created_at (created_at)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 4.2 Migrazioni Laravel
|
|
|
|
### Migration Base Users
|
|
**File**: `database/migrations/2024_01_01_000000_create_users_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('users', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->string('name');
|
|
$table->string('email')->unique();
|
|
$table->timestamp('email_verified_at')->nullable();
|
|
$table->string('password');
|
|
$table->boolean('is_active')->default(true);
|
|
$table->timestamp('last_login_at')->nullable();
|
|
$table->string('profile_photo_path', 2048)->nullable();
|
|
$table->string('phone', 20)->nullable();
|
|
$table->text('address')->nullable();
|
|
$table->rememberToken();
|
|
$table->timestamps();
|
|
|
|
$table->index(['email']);
|
|
$table->index(['is_active']);
|
|
$table->index(['last_login_at']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('users');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Migration Comuni Italiani
|
|
**File**: `database/migrations/2024_01_02_000000_create_comuni_italiani_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('comuni_italiani', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->string('codice_catastale', 4)->unique();
|
|
$table->string('denominazione');
|
|
$table->string('denominazione_tedesca')->nullable();
|
|
$table->string('denominazione_altra')->nullable();
|
|
$table->string('codice_provincia', 2);
|
|
$table->string('provincia');
|
|
$table->string('regione');
|
|
$table->string('cap', 5)->nullable();
|
|
$table->string('prefisso', 10)->nullable();
|
|
$table->string('email_pec')->nullable();
|
|
$table->timestamps();
|
|
|
|
$table->index(['codice_catastale']);
|
|
$table->index(['codice_provincia']);
|
|
$table->index(['denominazione']);
|
|
$table->index(['cap']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('comuni_italiani');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Migration Stabili
|
|
**File**: `database/migrations/2024_01_03_000000_create_stabili_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('stabili', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->string('denominazione');
|
|
$table->string('indirizzo');
|
|
$table->foreignId('comune_id')->nullable()->constrained('comuni_italiani')->onDelete('set null');
|
|
$table->string('cap', 10)->nullable();
|
|
$table->string('codice_fiscale', 16)->nullable();
|
|
$table->string('partita_iva', 11)->nullable();
|
|
$table->foreignId('amministratore_id')->nullable()->constrained('users')->onDelete('set null');
|
|
$table->boolean('is_active')->default(true);
|
|
$table->text('note')->nullable();
|
|
$table->timestamps();
|
|
|
|
$table->index(['denominazione']);
|
|
$table->index(['amministratore_id']);
|
|
$table->index(['comune_id']);
|
|
$table->index(['is_active']);
|
|
$table->index(['codice_fiscale']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('stabili');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Migration Soggetti
|
|
**File**: `database/migrations/2024_01_04_000000_create_soggetti_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('soggetti', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->enum('tipo_soggetto', ['persona_fisica', 'persona_giuridica']);
|
|
$table->string('nome')->nullable();
|
|
$table->string('cognome')->nullable();
|
|
$table->string('ragione_sociale')->nullable();
|
|
$table->string('codice_fiscale', 16)->nullable();
|
|
$table->string('partita_iva', 11)->nullable();
|
|
$table->date('data_nascita')->nullable();
|
|
$table->string('luogo_nascita')->nullable();
|
|
$table->string('indirizzo')->nullable();
|
|
$table->foreignId('comune_id')->nullable()->constrained('comuni_italiani')->onDelete('set null');
|
|
$table->string('cap', 10)->nullable();
|
|
$table->string('telefono', 20)->nullable();
|
|
$table->string('email')->nullable();
|
|
$table->boolean('is_active')->default(true);
|
|
$table->text('note')->nullable();
|
|
$table->timestamps();
|
|
|
|
$table->index(['codice_fiscale']);
|
|
$table->index(['partita_iva']);
|
|
$table->index(['tipo_soggetto']);
|
|
$table->index(['cognome', 'nome']);
|
|
$table->index(['ragione_sociale']);
|
|
$table->index(['is_active']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('soggetti');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Migration Unità Immobiliari
|
|
**File**: `database/migrations/2024_01_05_000000_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');
|
|
$table->string('numero', 50);
|
|
$table->string('piano', 20)->nullable();
|
|
$table->string('scala', 20)->nullable();
|
|
$table->string('interno', 20)->nullable();
|
|
$table->enum('tipo', ['appartamento', 'negozio', 'garage', 'cantina', 'altro'])->default('appartamento');
|
|
$table->string('categoria_catastale', 10)->nullable();
|
|
$table->string('classe_catastale', 10)->nullable();
|
|
$table->string('consistenza', 20)->nullable();
|
|
$table->decimal('superficie_catastale', 8, 2)->nullable();
|
|
$table->decimal('superficie_commerciale', 8, 2)->nullable();
|
|
$table->decimal('rendita_catastale', 10, 2)->nullable();
|
|
$table->decimal('millesimi_proprieta', 8, 4)->nullable();
|
|
$table->decimal('millesimi_riscaldamento', 8, 4)->nullable();
|
|
$table->decimal('millesimi_acqua', 8, 4)->nullable();
|
|
$table->decimal('millesimi_ascensore', 8, 4)->nullable();
|
|
$table->boolean('is_active')->default(true);
|
|
$table->text('note')->nullable();
|
|
$table->timestamps();
|
|
|
|
$table->index(['stabile_id']);
|
|
$table->index(['numero']);
|
|
$table->index(['tipo']);
|
|
$table->index(['is_active']);
|
|
$table->unique(['stabile_id', 'numero']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('unita_immobiliari');
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 4.3 Relazioni e Vincoli
|
|
|
|
### Tabelle Ponte
|
|
**File**: `database/migrations/2024_01_06_000000_create_diritti_reali_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('diritti_reali', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->foreignId('soggetto_id')->constrained('soggetti')->onDelete('cascade');
|
|
$table->foreignId('unita_immobiliare_id')->constrained('unita_immobiliari')->onDelete('cascade');
|
|
$table->enum('tipo_diritto', ['proprietario', 'nudo_proprietario', 'usufruttuario', 'inquilino', 'comodatario']);
|
|
$table->decimal('quota_proprieta', 8, 4)->nullable();
|
|
$table->date('data_inizio')->nullable();
|
|
$table->date('data_fine')->nullable();
|
|
$table->boolean('is_active')->default(true);
|
|
$table->text('note')->nullable();
|
|
$table->timestamps();
|
|
|
|
$table->index(['soggetto_id']);
|
|
$table->index(['unita_immobiliare_id']);
|
|
$table->index(['tipo_diritto']);
|
|
$table->index(['is_active']);
|
|
$table->index(['data_inizio']);
|
|
$table->index(['data_fine']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('diritti_reali');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Tabella User-Unità
|
|
**File**: `database/migrations/2024_01_07_000000_create_user_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('user_unita_immobiliari', function (Blueprint $table) {
|
|
$table->id();
|
|
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
|
$table->foreignId('unita_immobiliare_id')->constrained('unita_immobiliari')->onDelete('cascade');
|
|
$table->enum('tipo_accesso', ['proprietario', 'inquilino', 'amministratore']);
|
|
$table->date('data_inizio')->nullable();
|
|
$table->date('data_fine')->nullable();
|
|
$table->boolean('is_active')->default(true);
|
|
$table->timestamps();
|
|
|
|
$table->index(['user_id']);
|
|
$table->index(['unita_immobiliare_id']);
|
|
$table->index(['tipo_accesso']);
|
|
$table->index(['is_active']);
|
|
$table->unique(['user_id', 'unita_immobiliare_id']);
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('user_unita_immobiliari');
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 4.4 Modelli Eloquent
|
|
|
|
### Modello Stabile Completo
|
|
**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;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
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)->orderBy('numero');
|
|
}
|
|
|
|
public function documenti(): HasMany
|
|
{
|
|
return $this->hasMany(Documento::class)->orderBy('created_at', 'desc');
|
|
}
|
|
|
|
public function tickets(): HasMany
|
|
{
|
|
return $this->hasMany(Ticket::class)->orderBy('created_at', 'desc');
|
|
}
|
|
|
|
public function movimentiBancari(): HasMany
|
|
{
|
|
return $this->hasMany(MovimentoBancario::class)->orderBy('data_movimento', 'desc');
|
|
}
|
|
|
|
// Scope
|
|
public function scopeActive(Builder $query): Builder
|
|
{
|
|
return $query->where('is_active', true);
|
|
}
|
|
|
|
public function scopeByAmministratore(Builder $query, int $amministratoreId): Builder
|
|
{
|
|
return $query->where('amministratore_id', $amministratoreId);
|
|
}
|
|
|
|
// Accessors
|
|
public function getIndirizzoCompletoAttribute(): string
|
|
{
|
|
$indirizzo = $this->indirizzo;
|
|
|
|
if ($this->comune) {
|
|
$indirizzo .= ', ' . $this->comune->denominazione;
|
|
}
|
|
|
|
if ($this->cap) {
|
|
$indirizzo .= ' (' . $this->cap . ')';
|
|
}
|
|
|
|
return $indirizzo;
|
|
}
|
|
|
|
public function getTotalUnitaAttribute(): int
|
|
{
|
|
return $this->unitaImmobiliari()->count();
|
|
}
|
|
|
|
public function getTotalMillesimiAttribute(): float
|
|
{
|
|
return $this->unitaImmobiliari()->sum('millesimi_proprieta') ?? 0.0;
|
|
}
|
|
|
|
public function getSaldoTotaleAttribute(): float
|
|
{
|
|
return $this->movimentiBancari()->sum('importo') ?? 0.0;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Modello UnitaImmobiliare
|
|
**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\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
|
|
class UnitaImmobiliare extends Model
|
|
{
|
|
protected $fillable = [
|
|
'stabile_id',
|
|
'numero',
|
|
'piano',
|
|
'scala',
|
|
'interno',
|
|
'tipo',
|
|
'categoria_catastale',
|
|
'classe_catastale',
|
|
'consistenza',
|
|
'superficie_catastale',
|
|
'superficie_commerciale',
|
|
'rendita_catastale',
|
|
'millesimi_proprieta',
|
|
'millesimi_riscaldamento',
|
|
'millesimi_acqua',
|
|
'millesimi_ascensore',
|
|
'is_active',
|
|
'note',
|
|
];
|
|
|
|
protected $casts = [
|
|
'superficie_catastale' => 'decimal:2',
|
|
'superficie_commerciale' => 'decimal:2',
|
|
'rendita_catastale' => 'decimal:2',
|
|
'millesimi_proprieta' => 'decimal:4',
|
|
'millesimi_riscaldamento' => 'decimal:4',
|
|
'millesimi_acqua' => 'decimal:4',
|
|
'millesimi_ascensore' => 'decimal:4',
|
|
'is_active' => 'boolean',
|
|
];
|
|
|
|
// Relazioni
|
|
public function stabile(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Stabile::class);
|
|
}
|
|
|
|
public function soggetti(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(Soggetto::class, 'diritti_reali')
|
|
->withPivot(['tipo_diritto', 'quota_proprieta', 'data_inizio', 'data_fine', 'is_active'])
|
|
->withTimestamps();
|
|
}
|
|
|
|
public function users(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(User::class, 'user_unita_immobiliari')
|
|
->withPivot(['tipo_accesso', 'data_inizio', 'data_fine', 'is_active'])
|
|
->withTimestamps();
|
|
}
|
|
|
|
public function documenti(): HasMany
|
|
{
|
|
return $this->hasMany(Documento::class);
|
|
}
|
|
|
|
public function tickets(): HasMany
|
|
{
|
|
return $this->hasMany(Ticket::class);
|
|
}
|
|
|
|
public function ripartizioniSpese(): HasMany
|
|
{
|
|
return $this->hasMany(RipartizioneSpesa::class);
|
|
}
|
|
|
|
// Accessors
|
|
public function getNumeroCompletoAttribute(): string
|
|
{
|
|
$numero = $this->numero;
|
|
|
|
if ($this->piano) {
|
|
$numero .= ' (Piano ' . $this->piano . ')';
|
|
}
|
|
|
|
if ($this->scala) {
|
|
$numero .= ' - Scala ' . $this->scala;
|
|
}
|
|
|
|
if ($this->interno) {
|
|
$numero .= ' - Interno ' . $this->interno;
|
|
}
|
|
|
|
return $numero;
|
|
}
|
|
|
|
public function getTipoDisplayAttribute(): string
|
|
{
|
|
$tipi = [
|
|
'appartamento' => 'Appartamento',
|
|
'negozio' => 'Negozio',
|
|
'garage' => 'Garage',
|
|
'cantina' => 'Cantina',
|
|
'altro' => 'Altro',
|
|
];
|
|
|
|
return $tipi[$this->tipo] ?? 'Non definito';
|
|
}
|
|
|
|
// Helper Methods
|
|
public function getProprietari()
|
|
{
|
|
return $this->soggetti()->wherePivot('tipo_diritto', 'proprietario')
|
|
->wherePivot('is_active', true);
|
|
}
|
|
|
|
public function getInquilini()
|
|
{
|
|
return $this->soggetti()->wherePivot('tipo_diritto', 'inquilino')
|
|
->wherePivot('is_active', true);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4.5 Seeder e Dati Base
|
|
|
|
### Seeder Comuni Italiani
|
|
**File**: `database/seeders/ComuniItalianiSeeder.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use Illuminate\Database\Seeder;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class ComuniItalianiSeeder extends Seeder
|
|
{
|
|
public function run(): void
|
|
{
|
|
// Verifica se i comuni sono già stati caricati
|
|
if (DB::table('comuni_italiani')->count() > 0) {
|
|
$this->command->info('Comuni italiani già presenti nel database');
|
|
return;
|
|
}
|
|
|
|
// Carica da file CSV
|
|
$csvFile = database_path('data/comuni_italiani.csv');
|
|
|
|
if (!file_exists($csvFile)) {
|
|
$this->command->error('File comuni_italiani.csv non trovato in database/data/');
|
|
return;
|
|
}
|
|
|
|
$handle = fopen($csvFile, 'r');
|
|
$header = fgetcsv($handle, 1000, ';');
|
|
|
|
$comuni = [];
|
|
while (($data = fgetcsv($handle, 1000, ';')) !== false) {
|
|
$comuni[] = [
|
|
'codice_catastale' => $data[0],
|
|
'denominazione' => $data[1],
|
|
'denominazione_tedesca' => $data[2] ?: null,
|
|
'denominazione_altra' => $data[3] ?: null,
|
|
'codice_provincia' => $data[4],
|
|
'provincia' => $data[5],
|
|
'regione' => $data[6],
|
|
'cap' => $data[7] ?: null,
|
|
'prefisso' => $data[8] ?: null,
|
|
'email_pec' => $data[9] ?: null,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
|
|
fclose($handle);
|
|
|
|
// Inserimento batch per performance
|
|
$chunks = array_chunk($comuni, 1000);
|
|
foreach ($chunks as $chunk) {
|
|
DB::table('comuni_italiani')->insert($chunk);
|
|
}
|
|
|
|
$this->command->info('Caricati ' . count($comuni) . ' comuni italiani');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Seeder Dati Demo
|
|
**File**: `database/seeders/DemoDataSeeder.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use Illuminate\Database\Seeder;
|
|
use App\Models\User;
|
|
use App\Models\Stabile;
|
|
use App\Models\UnitaImmobiliare;
|
|
use App\Models\Soggetto;
|
|
use App\Models\ComuneItaliano;
|
|
|
|
class DemoDataSeeder extends Seeder
|
|
{
|
|
public function run(): void
|
|
{
|
|
// Trova comuni per demo
|
|
$milano = ComuneItaliano::where('denominazione', 'Milano')->first();
|
|
$roma = ComuneItaliano::where('denominazione', 'Roma')->first();
|
|
|
|
if (!$milano || !$roma) {
|
|
$this->command->error('Comuni Milano e Roma non trovati. Esegui prima ComuniItalianiSeeder');
|
|
return;
|
|
}
|
|
|
|
// Crea utenti amministratore
|
|
$admin1 = User::create([
|
|
'name' => 'Admin Milano',
|
|
'email' => 'admin.milano@netgescon.it',
|
|
'password' => bcrypt('password'),
|
|
'is_active' => true,
|
|
]);
|
|
$admin1->assignRole('admin');
|
|
|
|
$admin2 = User::create([
|
|
'name' => 'Admin Roma',
|
|
'email' => 'admin.roma@netgescon.it',
|
|
'password' => bcrypt('password'),
|
|
'is_active' => true,
|
|
]);
|
|
$admin2->assignRole('amministratore');
|
|
|
|
// Crea stabili
|
|
$stabile1 = Stabile::create([
|
|
'denominazione' => 'Condominio Via Roma 123',
|
|
'indirizzo' => 'Via Roma 123',
|
|
'comune_id' => $milano->id,
|
|
'cap' => '20100',
|
|
'codice_fiscale' => '80123456789',
|
|
'amministratore_id' => $admin1->id,
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$stabile2 = Stabile::create([
|
|
'denominazione' => 'Residenza I Pini',
|
|
'indirizzo' => 'Via dei Pini 45',
|
|
'comune_id' => $roma->id,
|
|
'cap' => '00100',
|
|
'codice_fiscale' => '80987654321',
|
|
'amministratore_id' => $admin2->id,
|
|
'is_active' => true,
|
|
]);
|
|
|
|
// Crea unità immobiliari
|
|
for ($i = 1; $i <= 12; $i++) {
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile1->id,
|
|
'numero' => (string) $i,
|
|
'piano' => $i <= 4 ? '1' : ($i <= 8 ? '2' : '3'),
|
|
'tipo' => 'appartamento',
|
|
'superficie_catastale' => rand(60, 120),
|
|
'millesimi_proprieta' => rand(70, 130) / 1000,
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
|
|
for ($i = 1; $i <= 8; $i++) {
|
|
UnitaImmobiliare::create([
|
|
'stabile_id' => $stabile2->id,
|
|
'numero' => (string) $i,
|
|
'piano' => $i <= 4 ? '1' : '2',
|
|
'tipo' => 'appartamento',
|
|
'superficie_catastale' => rand(80, 150),
|
|
'millesimi_proprieta' => rand(100, 180) / 1000,
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
|
|
// Crea soggetti demo
|
|
$soggetto1 = Soggetto::create([
|
|
'tipo_soggetto' => 'persona_fisica',
|
|
'nome' => 'Mario',
|
|
'cognome' => 'Rossi',
|
|
'codice_fiscale' => 'RSSMRA70A01H501Z',
|
|
'indirizzo' => 'Via Roma 123',
|
|
'comune_id' => $milano->id,
|
|
'telefono' => '3331234567',
|
|
'email' => 'mario.rossi@email.it',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$soggetto2 = Soggetto::create([
|
|
'tipo_soggetto' => 'persona_fisica',
|
|
'nome' => 'Anna',
|
|
'cognome' => 'Verdi',
|
|
'codice_fiscale' => 'VRDNNA75B01F205X',
|
|
'indirizzo' => 'Via dei Pini 45',
|
|
'comune_id' => $roma->id,
|
|
'telefono' => '3339876543',
|
|
'email' => 'anna.verdi@email.it',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
// Collega soggetti a unità immobiliari
|
|
$unita1 = UnitaImmobiliare::where('stabile_id', $stabile1->id)->first();
|
|
$unita2 = UnitaImmobiliare::where('stabile_id', $stabile2->id)->first();
|
|
|
|
$unita1->soggetti()->attach($soggetto1->id, [
|
|
'tipo_diritto' => 'proprietario',
|
|
'quota_proprieta' => 1.0000,
|
|
'data_inizio' => now()->subYear(),
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$unita2->soggetti()->attach($soggetto2->id, [
|
|
'tipo_diritto' => 'proprietario',
|
|
'quota_proprieta' => 1.0000,
|
|
'data_inizio' => now()->subYear(),
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$this->command->info('Dati demo creati con successo');
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4.6 Gestione Conflitti Migrazioni
|
|
|
|
### Comando Verifica Stato Database
|
|
**File**: `app/Console/Commands/CheckDatabaseStatus.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class CheckDatabaseStatus extends Command
|
|
{
|
|
protected $signature = 'db:check-status';
|
|
protected $description = 'Verifica lo stato del database e rileva possibili conflitti';
|
|
|
|
public function handle()
|
|
{
|
|
$this->info('🔍 Verifica stato database NetGescon...');
|
|
|
|
// Verifica connessione database
|
|
try {
|
|
DB::connection()->getPdo();
|
|
$this->info('✅ Connessione database OK');
|
|
} catch (\Exception $e) {
|
|
$this->error('❌ Errore connessione database: ' . $e->getMessage());
|
|
return 1;
|
|
}
|
|
|
|
// Verifica tabelle principali
|
|
$requiredTables = [
|
|
'users', 'roles', 'permissions', 'model_has_roles',
|
|
'comuni_italiani', 'stabili', 'soggetti', 'unita_immobiliari',
|
|
'diritti_reali', 'documenti', 'tickets', 'movimenti_bancari'
|
|
];
|
|
|
|
$this->info('📋 Verifica tabelle principali...');
|
|
foreach ($requiredTables as $table) {
|
|
if (Schema::hasTable($table)) {
|
|
$count = DB::table($table)->count();
|
|
$this->info("✅ {$table}: {$count} record");
|
|
} else {
|
|
$this->warn("⚠️ {$table}: TABELLA MANCANTE");
|
|
}
|
|
}
|
|
|
|
// Verifica migrazioni
|
|
$this->info('🔄 Verifica migrazioni...');
|
|
$pendingMigrations = DB::table('migrations')
|
|
->pluck('migration')
|
|
->toArray();
|
|
|
|
if (empty($pendingMigrations)) {
|
|
$this->warn('⚠️ Nessuna migrazione eseguita');
|
|
} else {
|
|
$this->info('✅ Migrazioni eseguite: ' . count($pendingMigrations));
|
|
}
|
|
|
|
// Verifica conflitti campi
|
|
$this->info('⚡ Verifica conflitti campi...');
|
|
$this->checkTableConflicts();
|
|
|
|
// Verifica indici
|
|
$this->info('📊 Verifica indici database...');
|
|
$this->checkIndexes();
|
|
|
|
$this->info('✅ Verifica completata!');
|
|
return 0;
|
|
}
|
|
|
|
private function checkTableConflicts()
|
|
{
|
|
$conflicts = [];
|
|
|
|
// Verifica campi duplicati in users
|
|
if (Schema::hasTable('users')) {
|
|
$columns = Schema::getColumnListing('users');
|
|
$expectedColumns = ['id', 'name', 'email', 'password', 'is_active', 'last_login_at'];
|
|
|
|
foreach ($expectedColumns as $col) {
|
|
if (!in_array($col, $columns)) {
|
|
$conflicts[] = "users.{$col} mancante";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verifica campi stabili
|
|
if (Schema::hasTable('stabili')) {
|
|
$columns = Schema::getColumnListing('stabili');
|
|
if (!in_array('amministratore_id', $columns)) {
|
|
$conflicts[] = 'stabili.amministratore_id mancante';
|
|
}
|
|
if (!in_array('is_active', $columns)) {
|
|
$conflicts[] = 'stabili.is_active mancante';
|
|
}
|
|
}
|
|
|
|
if (!empty($conflicts)) {
|
|
$this->error('❌ Conflitti rilevati:');
|
|
foreach ($conflicts as $conflict) {
|
|
$this->error(" - {$conflict}");
|
|
}
|
|
} else {
|
|
$this->info('✅ Nessun conflitto rilevato');
|
|
}
|
|
}
|
|
|
|
private function checkIndexes()
|
|
{
|
|
$tables = ['users', 'stabili', 'unita_immobiliari', 'soggetti'];
|
|
|
|
foreach ($tables as $table) {
|
|
if (Schema::hasTable($table)) {
|
|
$indexes = collect(DB::select("SHOW INDEX FROM {$table}"))
|
|
->pluck('Key_name')
|
|
->unique()
|
|
->values()
|
|
->toArray();
|
|
|
|
$this->info(" {$table}: " . count($indexes) . " indici");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Comando Reset Database
|
|
**File**: `app/Console/Commands/ResetDatabase.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
|
|
class ResetDatabase extends Command
|
|
{
|
|
protected $signature = 'db:reset-clean {--force}';
|
|
protected $description = 'Reset completo database con pulizia conflitti';
|
|
|
|
public function handle()
|
|
{
|
|
if (!$this->option('force')) {
|
|
if (!$this->confirm('⚠️ Questa operazione cancellerà TUTTI i dati. Continuare?')) {
|
|
$this->info('Operazione annullata');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
$this->info('🔄 Inizio reset database...');
|
|
|
|
// Drop tutte le tabelle
|
|
$this->info('🗑️ Rimozione tabelle esistenti...');
|
|
$this->dropAllTables();
|
|
|
|
// Pulisci tabella migrazioni
|
|
$this->info('🧹 Pulizia migrazioni...');
|
|
try {
|
|
DB::statement('DROP TABLE IF EXISTS migrations');
|
|
} catch (\Exception $e) {
|
|
// Ignore se non esiste
|
|
}
|
|
|
|
// Ricrea tutto
|
|
$this->info('⚡ Ricreazione database...');
|
|
Artisan::call('migrate:fresh');
|
|
|
|
$this->info('🌱 Esecuzione seeder...');
|
|
Artisan::call('db:seed');
|
|
|
|
$this->info('✅ Reset database completato!');
|
|
return 0;
|
|
}
|
|
|
|
private function dropAllTables()
|
|
{
|
|
$tables = DB::select('SHOW TABLES');
|
|
$dbName = DB::getDatabaseName();
|
|
|
|
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
|
|
|
foreach ($tables as $table) {
|
|
$tableName = $table->{"Tables_in_{$dbName}"};
|
|
DB::statement("DROP TABLE IF EXISTS {$tableName}");
|
|
}
|
|
|
|
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4.7 Triggers e Codici Univoci
|
|
|
|
### 4.7.1 Sistema Codici Univoci
|
|
Il sistema NetGescon utilizza codici univoci alfanumerici di 8 caratteri per identificare univocamente utenti, amministratori e altre entità critiche.
|
|
|
|
**Caratteristiche:**
|
|
- **Lunghezza**: 8 caratteri
|
|
- **Formato**: Alfanumerico (A-Z, 0-9)
|
|
- **Unicità**: Garantita tramite trigger SQL
|
|
- **Generazione**: Automatica all'inserimento
|
|
|
|
### 4.7.2 Trigger per Amministratori
|
|
|
|
```sql
|
|
-- Trigger per generare codice_univoco automaticamente per amministratori
|
|
DELIMITER $$
|
|
|
|
CREATE TRIGGER generate_codice_univoco_amministratori
|
|
BEFORE INSERT ON amministratori
|
|
FOR EACH ROW
|
|
BEGIN
|
|
DECLARE codice_temp VARCHAR(8);
|
|
DECLARE codice_exists INT DEFAULT 1;
|
|
|
|
-- Solo se il codice non è già fornito
|
|
IF NEW.codice_univoco IS NULL OR NEW.codice_univoco = '' THEN
|
|
WHILE codice_exists > 0 DO
|
|
SET codice_temp = CONCAT(
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1)
|
|
);
|
|
|
|
SELECT COUNT(*) INTO codice_exists
|
|
FROM amministratori
|
|
WHERE codice_univoco = codice_temp;
|
|
END WHILE;
|
|
|
|
SET NEW.codice_univoco = codice_temp;
|
|
END IF;
|
|
END$$
|
|
|
|
DELIMITER ;
|
|
```
|
|
|
|
### 4.7.3 Trigger per Users
|
|
|
|
```sql
|
|
-- Trigger per generare codice_univoco per users
|
|
DELIMITER $$
|
|
|
|
CREATE TRIGGER generate_codice_univoco_users
|
|
BEFORE INSERT ON users
|
|
FOR EACH ROW
|
|
BEGIN
|
|
DECLARE codice_temp VARCHAR(8);
|
|
DECLARE codice_exists INT DEFAULT 1;
|
|
|
|
-- Solo se il codice non è già fornito
|
|
IF NEW.codice_univoco IS NULL OR NEW.codice_univoco = '' THEN
|
|
WHILE codice_exists > 0 DO
|
|
SET codice_temp = CONCAT(
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(1 + (RAND() * 36)), 1)
|
|
);
|
|
|
|
SELECT COUNT(*) INTO codice_exists
|
|
FROM users
|
|
WHERE codice_univoco = codice_temp;
|
|
END WHILE;
|
|
|
|
SET NEW.codice_univoco = codice_temp;
|
|
END IF;
|
|
END$$
|
|
|
|
DELIMITER ;
|
|
```
|
|
|
|
### 4.7.4 Migration Laravel per Triggers
|
|
|
|
```php
|
|
<?php
|
|
// database/migrations/2025_07_17_210000_create_codice_univoco_triggers.php
|
|
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
return new class extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
// Trigger per amministratori
|
|
DB::unprepared('
|
|
DROP TRIGGER IF EXISTS generate_codice_univoco_amministratori;
|
|
|
|
DELIMITER $$
|
|
CREATE TRIGGER generate_codice_univoco_amministratori
|
|
BEFORE INSERT ON amministratori
|
|
FOR EACH ROW
|
|
BEGIN
|
|
DECLARE codice_temp VARCHAR(8);
|
|
DECLARE codice_exists INT DEFAULT 1;
|
|
|
|
IF NEW.codice_univoco IS NULL OR NEW.codice_univoco = "" THEN
|
|
WHILE codice_exists > 0 DO
|
|
SET codice_temp = CONCAT(
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1),
|
|
SUBSTRING("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", FLOOR(1 + (RAND() * 36)), 1)
|
|
);
|
|
|
|
SELECT COUNT(*) INTO codice_exists
|
|
FROM amministratori
|
|
WHERE codice_univoco = codice_temp;
|
|
END WHILE;
|
|
|
|
SET NEW.codice_univoco = codice_temp;
|
|
END IF;
|
|
END$$
|
|
DELIMITER ;
|
|
');
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
DB::unprepared('DROP TRIGGER IF EXISTS generate_codice_univoco_amministratori');
|
|
}
|
|
};
|
|
```
|
|
|
|
### 4.7.5 Vantaggi del Sistema
|
|
|
|
**✅ Automazione Completa**
|
|
- Generazione automatica senza intervento manuale
|
|
- Nessun rischio di dimenticanza o errore umano
|
|
|
|
**✅ Sicurezza**
|
|
- Codici difficili da indovinare
|
|
- Distribuzione uniforme dei caratteri
|
|
- Impossibilità di duplicati
|
|
|
|
**✅ Performance**
|
|
- Operazione a livello database
|
|
- Nessun overhead applicativo
|
|
- Controllo di unicità ottimizzato
|
|
|
|
**✅ Manutenibilità**
|
|
- Trigger gestiti tramite migrations
|
|
- Versionamento del codice
|
|
- Rollback automatico
|
|
|
|
---
|
|
|
|
## 4.10 Sincronizzazione Multi-Macchina
|
|
|
|
### 4.10.1 Architettura di Sincronizzazione
|
|
|
|
NetGescon supporta la sincronizzazione automatica tra più macchine per garantire consistenza e aggiornamenti distribuiti.
|
|
|
|
**Componenti:**
|
|
- **Macchina di Sviluppo**: Windows WSL (locale)
|
|
- **Macchina Master**: Linux Ubuntu 24.04 (192.168.0.43)
|
|
- **Macchine Client**: Istanze replicate del sistema
|
|
|
|
### 4.10.2 Script di Sincronizzazione
|
|
|
|
#### Script PowerShell (Windows)
|
|
```powershell
|
|
# netgescon-sync.ps1 - Script di sincronizzazione Windows
|
|
$LOCAL_PROJECT_PATH = "U:\home\michele\netgescon\netgescon-laravel"
|
|
$REMOTE_HOST = "192.168.0.43"
|
|
$REMOTE_USER = "michele"
|
|
$REMOTE_PATH = "/var/www/netgescon"
|
|
|
|
# Funzione di sincronizzazione migrations
|
|
function Sync-Migrations {
|
|
Write-Log "Sincronizzando migrations..."
|
|
scp -r "$LOCAL_PROJECT_PATH\database\migrations\*" "$REMOTE_USER@$REMOTE_HOST`:$REMOTE_PATH/database/migrations/"
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Log "Migrations sincronizzate" "SUCCESS"
|
|
return $true
|
|
} else {
|
|
Write-Log "Errore sincronizzazione migrations" "ERROR"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# Esecuzione migrations remote
|
|
function Invoke-RemoteMigrations {
|
|
Write-Log "Eseguo migrations remote..."
|
|
ssh "$REMOTE_USER@$REMOTE_HOST" "cd $REMOTE_PATH && php artisan migrate --force"
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Log "Migrations remote eseguite" "SUCCESS"
|
|
return $true
|
|
} else {
|
|
Write-Log "Errore esecuzione migrations remote" "ERROR"
|
|
return $false
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Script Bash (Linux)
|
|
```bash
|
|
#!/bin/bash
|
|
# netgescon-sync.sh - Script di sincronizzazione Linux
|
|
|
|
LOCAL_PROJECT_PATH="/mnt/u/home/michele/netgescon/netgescon-laravel"
|
|
REMOTE_HOST="192.168.0.43"
|
|
REMOTE_USER="michele"
|
|
REMOTE_PATH="/var/www/netgescon"
|
|
|
|
# Sincronizza migrations
|
|
sync_migrations() {
|
|
info "Sincronizzando migrations..."
|
|
rsync -avz --delete \
|
|
"$LOCAL_PROJECT_PATH/database/migrations/" \
|
|
"$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/database/migrations/"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log "✅ Migrations sincronizzate"
|
|
return 0
|
|
else
|
|
error "❌ Errore sincronizzazione migrations"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Esegui migrations remote
|
|
run_remote_migrations() {
|
|
info "Eseguo migrations remote..."
|
|
ssh "$REMOTE_USER@$REMOTE_HOST" "
|
|
cd $REMOTE_PATH
|
|
php artisan migrate --force
|
|
"
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log "✅ Migrations remote eseguite"
|
|
return 0
|
|
else
|
|
error "❌ Errore esecuzione migrations remote"
|
|
return 1
|
|
fi
|
|
}
|
|
```
|
|
|
|
### 4.10.3 Flusso di Sincronizzazione
|
|
|
|
1. **Sviluppo Locale** (Windows WSL)
|
|
2. **Sincronizzazione** → **Macchina Master** (192.168.0.43)
|
|
3. **Distribuzione** → **Macchine Client** (altre istanze)
|
|
|
|
### 4.10.4 Comandi di Utilizzo
|
|
|
|
```bash
|
|
# Avvia script di sincronizzazione
|
|
./netgescon-sync.sh
|
|
|
|
# Opzioni disponibili:
|
|
# 1. Sincronizza solo migrations
|
|
# 2. Sincronizza migrations + seeders
|
|
# 3. Sincronizza + esegui migrations
|
|
# 4. Sincronizza + esegui migrations + seeding
|
|
# 5. Full sync + restart services
|
|
```
|
|
|
|
```powershell
|
|
# Avvia script PowerShell
|
|
.\netgescon-sync.ps1
|
|
|
|
# Menu interattivo con opzioni di sincronizzazione
|
|
# Logging automatico in C:\temp\netgescon_sync_*.log
|
|
```
|
|
|
|
### 4.10.5 Backup Automatico
|
|
|
|
Ogni sincronizzazione include:
|
|
- **Backup automatico** della macchina remota
|
|
- **Logging dettagliato** di tutte le operazioni
|
|
- **Rollback capability** in caso di errori
|
|
- **Verifica connessione** prima di ogni operazione
|
|
|
|
---
|
|
|
|
## 4.8 Backup e Ripristino
|
|
|
|
### Script Backup Automatico
|
|
**File**: `scripts/backup-database.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
# Configurazione
|
|
DB_NAME="netgescon_db"
|
|
DB_USER="netgescon"
|
|
DB_PASS="netgescon2024"
|
|
BACKUP_DIR="/var/backups/netgescon"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Crea directory backup
|
|
mkdir -p $BACKUP_DIR
|
|
|
|
# Backup completo
|
|
echo "🗄️ Backup database $DB_NAME..."
|
|
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/netgescon_$DATE.sql
|
|
|
|
# Backup solo struttura
|
|
mysqldump -u $DB_USER -p$DB_PASS --no-data $DB_NAME > $BACKUP_DIR/netgescon_structure_$DATE.sql
|
|
|
|
# Backup solo dati
|
|
mysqldump -u $DB_USER -p$DB_PASS --no-create-info $DB_NAME > $BACKUP_DIR/netgescon_data_$DATE.sql
|
|
|
|
# Comprimi backup
|
|
gzip $BACKUP_DIR/netgescon_$DATE.sql
|
|
gzip $BACKUP_DIR/netgescon_structure_$DATE.sql
|
|
gzip $BACKUP_DIR/netgescon_data_$DATE.sql
|
|
|
|
# Rimuovi backup più vecchi di 7 giorni
|
|
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
|
|
|
|
echo "✅ Backup completato: $BACKUP_DIR/netgescon_$DATE.sql.gz"
|
|
```
|
|
|
|
### Script Ripristino
|
|
**File**: `scripts/restore-database.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
if [ $# -eq 0 ]; then
|
|
echo "Uso: $0 <file_backup.sql>"
|
|
echo "Esempio: $0 /var/backups/netgescon/netgescon_20240117_120000.sql.gz"
|
|
exit 1
|
|
fi
|
|
|
|
BACKUP_FILE=$1
|
|
DB_NAME="netgescon_db"
|
|
DB_USER="netgescon"
|
|
DB_PASS="netgescon2024"
|
|
|
|
if [ ! -f "$BACKUP_FILE" ]; then
|
|
echo "❌ File backup non trovato: $BACKUP_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Conferma operazione
|
|
echo "⚠️ Ripristino database da: $BACKUP_FILE"
|
|
read -p "Questa operazione sovrascriverà i dati esistenti. Continuare? (y/N): " -n 1 -r
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
echo "Operazione annullata"
|
|
exit 0
|
|
fi
|
|
|
|
# Decomprimi se necessario
|
|
if [[ $BACKUP_FILE == *.gz ]]; then
|
|
echo "📦 Decompressione backup..."
|
|
gunzip -c $BACKUP_FILE > /tmp/restore_temp.sql
|
|
RESTORE_FILE="/tmp/restore_temp.sql"
|
|
else
|
|
RESTORE_FILE=$BACKUP_FILE
|
|
fi
|
|
|
|
# Ripristino
|
|
echo "🔄 Ripristino database..."
|
|
mysql -u $DB_USER -p$DB_PASS $DB_NAME < $RESTORE_FILE
|
|
|
|
# Pulizia
|
|
if [ -f "/tmp/restore_temp.sql" ]; then
|
|
rm /tmp/restore_temp.sql
|
|
fi
|
|
|
|
echo "✅ Ripristino completato!"
|
|
```
|
|
|
|
---
|
|
|
|
**📝 COMPLETATO: Capitolo 4 - Database e Strutture**
|
|
|
|
Questo capitolo fornisce una guida completa per:
|
|
- ✅ Schema database completo con tutte le tabelle
|
|
- ✅ Migrazioni Laravel strutturate
|
|
- ✅ Relazioni e vincoli foreign key
|
|
- ✅ Modelli Eloquent con relazioni
|
|
- ✅ Seeder per dati base e demo
|
|
- ✅ Gestione conflitti migrazioni
|
|
- ✅ Troubleshooting errori comuni
|
|
- ✅ Backup e ripristino automatico
|
|
|
|
**🎯 Questo capitolo risolve il problema segnalato dei conflitti nelle migrazioni fornendo:**
|
|
- Comandi di verifica stato database
|
|
- Script di reset pulito
|
|
- Troubleshooting per errori comuni
|
|
- Backup automatico prima di operazioni rischiose
|
|
|
|
**🔄 Prossimo capitolo da completare: API e Integrazioni**
|