21 KiB
📋 LOG SVILUPPO NETGESCON - 22 LUGLIO 2025
Sistema Seeder Documenti Demo Persistenti
📅 NAVIGAZIONE LOG
- 📁 Log Precedente:
LOG-SVILUPPO-LUGLIO-2025.md- Sessione 21/07/2025 - 📁 Log Successivo:
LOG-SVILUPPO-23-LUGLIO-2025.md- Prossima sessione - 📁 Documentazione Master:
00-COPILOT-MASTER-GUIDE.md - 📁 Manuale Tecnico:
MANUALE-TECNICO-RISOLUZIONE-PROBLEMI.md
🎯 OBIETTIVO SESSIONE
Implementare sistema seeder per documenti demo persistenti che non si cancellino al refresh della pagina, permettendo una gestione documentale continua e realistica.
📋 RICHIESTA UTENTE
"utilizza l'account di netgescon User: netgescon_user e PW:NetGescon2024!" "Continue to iterate?"
Problema Identificato: I documenti demo erano hardcoded in JavaScript e si perdevano ad ogni refresh della pagina.
🚀 IMPLEMENTAZIONI REALIZZATE
1. SEEDER DOCUMENTI DEMO PERSISTENTI
File Creato: /database/seeders/DocumentiDemoSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class DocumentiDemoSeeder extends Seeder
{
/**
* NETGESCON SEEDER: Documenti Demo Persistenti
* Crea dati di esempio per la gestione documentale che non vengono
* cancellati ad ogni refresh della pagina.
*/
public function run()
{
// Verifica se esistono già documenti demo per evitare duplicati
if (DB::table('documenti')->where('is_demo', true)->exists()) {
if (isset($this->command)) {
$this->command->info('Documenti demo già esistenti. Skip...');
}
return;
}
// Ottieni primo stabile per collegamento
$stabileId = DB::table('stabili')->first()?->id ?? 1;
$documenti = [
[
'stabile_id' => $stabileId,
'nome' => 'Contratto Assicurazione Multirischi 2025',
'nome_file' => 'contratto_assicurazione_2025.pdf',
'path_file' => 'demo/contratti/assicurazione_2025.pdf',
'tipologia' => 'contratto',
'fornitore' => 'Assicurazioni Generali SpA',
'data_documento' => '2025-01-15',
'data_scadenza' => '2025-12-31',
'importo_collegato' => 2450.00,
'categoria_spesa' => 'spese_ordinarie',
'tags' => 'assicurazione,multirischi,annuale,importante',
'note' => 'Contratto assicurativo per copertura responsabilità civile...',
'numero_protocollo' => 'ASS-2025-001',
'numero_pagine' => 15,
'estensione' => 'pdf',
'contenuto_ocr' => 'CONTRATTO DI ASSICURAZIONE MULTIRISCHI...',
'urgente' => false,
'is_demo' => true,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
],
// ... altri 7 documenti demo
];
// Inserisci documenti in batch per performance
DB::table('documenti')->insert($documenti);
}
}
💡 APPUNTI IMPORTANTI:
- Usa
DB::table()invece di Eloquent per performance migliori con inserimenti di massa - Campo
is_demoper distinguere dati demo da dati reali - Controllo esistenza documenti per evitare duplicati
- Compatibilità con command line interface tramite
isset($this->command)
2. COMANDO ARTISAN PERSONALIZZATO
File Creato: /app/Console/Commands/DocumentiDemoCommand.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Database\Seeders\DocumentiDemoSeeder;
class DocumentiDemoCommand extends Command
{
protected $signature = 'netgescon:demo-documenti {--force : Forza la ricreazione anche se esistono già}';
protected $description = 'Crea documenti demo persistenti per la gestione documentale NetGescon';
public function handle()
{
$this->info('🚀 NetGescon - Creazione Documenti Demo Persistenti');
$this->info('================================================');
if ($this->option('force')) {
$this->warn('⚠️ Modalità FORCE attivata - ricreo tutti i documenti demo');
\DB::table('documenti')->where('is_demo', true)->delete();
\DB::table('etichette_protocollo')->truncate();
}
try {
$seeder = new DocumentiDemoSeeder();
$seeder->run();
$count = \DB::table('documenti')->where('is_demo', true)->count();
$this->info("✅ Operazione completata con successo!");
$this->info("📄 Documenti demo creati: {$count}");
return Command::SUCCESS;
} catch (\Exception $e) {
$this->error("❌ Errore durante la creazione dei documenti demo:");
$this->error($e->getMessage());
return Command::FAILURE;
}
}
}
📝 USO DEL COMANDO:
# Crea documenti demo (se non esistono già)
php artisan netgescon:demo-documenti
# Forza ricreazione (cancella e ricrea tutto)
php artisan netgescon:demo-documenti --force
3. MIGRATION COLONNE AVANZATE DOCUMENTI
File Creato: /database/migrations/2025_07_22_075105_add_advanced_columns_to_documenti_table.php
public function up()
{
Schema::table('documenti', function (Blueprint $table) {
// Verifica se le colonne non esistono già
if (!Schema::hasColumn('documenti', 'stabile_id')) {
$table->foreignId('stabile_id')->nullable()->constrained('stabili')->onDelete('cascade');
}
if (!Schema::hasColumn('documenti', 'tipologia')) {
$table->enum('tipologia', [
'fattura', 'ricevuta', 'contratto', 'preventivo', 'verbale',
'comunicazione', 'relazione_tecnica', 'certificato',
'planimetria', 'foto', 'altro'
])->nullable();
}
// ... altre colonne
if (!Schema::hasColumn('documenti', 'is_demo')) {
$table->boolean('is_demo')->default(false);
}
});
// Aggiungi indici per performance
Schema::table('documenti', function (Blueprint $table) {
$indexNames = collect(\DB::select("SHOW INDEX FROM documenti"))->pluck('Key_name');
if (!$indexNames->contains('documenti_stabile_tipologia_index')) {
$table->index(['stabile_id', 'tipologia'], 'documenti_stabile_tipologia_index');
}
if (!$indexNames->contains('documenti_is_demo_index')) {
$table->index(['is_demo']);
}
});
}
⚠️ PROBLEMI RISOLTI:
-
Tabella esistente: La migration
create_documenti_tablefalliva perché la tabella esisteva già- Soluzione: Rimossa la migration di creazione, tenuta solo quella di aggiornamento
-
Colonna mancante:
dimensione_filenon era presente nello schema esistente- Soluzione: Rimossa dal seeder per compatibilità con schema attuale
4. MODEL DOCUMENTO ESTESO
File Modificato: /app/Models/Documento.php
class Documento extends Model
{
protected $fillable = [
// Campi legacy per compatibilità
'documentable_id', 'documentable_type', 'nome_file', 'path_file',
'tipo_documento', 'mime_type', 'descrizione', 'xml_data', 'hash_file',
// Nuovi campi per sistema avanzato
'stabile_id', 'unita_id', 'utente_id', 'nome', 'tipologia', 'fornitore',
'data_documento', 'data_scadenza', 'importo_collegato', 'categoria_spesa',
'tags', 'note', 'numero_protocollo', // ... altri campi
];
protected $casts = [
'xml_data' => 'array',
'metadati_ocr' => 'array',
'data_documento' => 'date',
'data_scadenza' => 'date',
'importo_collegato' => 'decimal:2',
'approvato' => 'boolean',
'urgente' => 'boolean',
'is_demo' => 'boolean',
];
// Scopes avanzati
public function scopeDemo($query)
{
return $query->where('is_demo', true);
}
public function scopeUrgenti($query)
{
return $query->where('urgente', true);
}
public function scopeInScadenza($query, $giorni = 30)
{
return $query->whereNotNull('data_scadenza')
->where('data_scadenza', '<=', Carbon::now()->addDays($giorni));
}
// Accessor per tags array
public function getTagsArrayAttribute()
{
return $this->tags ? explode(',', $this->tags) : [];
}
// Metodi utilità
public function generaNumeroProtocollo()
{
$prefisso = strtoupper(substr($this->tipologia ?? 'DOC', 0, 4));
$anno = Carbon::now()->year;
$ultimoNumero = static::where('numero_protocollo', 'like', "{$prefisso}-{$anno}-%")
->orderBy('numero_protocollo', 'desc')
->first();
if ($ultimoNumero && preg_match("/{$prefisso}-{$anno}-(\d+)/", $ultimoNumero->numero_protocollo, $matches)) {
$progressivo = intval($matches[1]) + 1;
} else {
$progressivo = 1;
}
return "{$prefisso}-{$anno}-" . str_pad($progressivo, 3, '0', STR_PAD_LEFT);
}
}
🔧 CARATTERISTICHE IMPLEMENTATE:
- Dual-Mode: Supporta sia campi legacy che nuovi
- Scopes: Query predefinite per casi d'uso comuni
- Accessors: Conversione automatica dati (es. tags array)
- Metodi Utilità: Generazione protocolli automatica
5. AGGIORNAMENTO VISTA GESTIONE DOCUMENTALE
File Modificato: /resources/views/admin/stabili/tabs/gestione-documentale.blade.php
<!-- Lista Documenti -->
<div id="lista-documenti" class="space-y-4">
@php
$documenti = \App\Models\Documento::where('stabile_id', $stabile->id)
->where('is_demo', true)
->orderBy('created_at', 'desc')
->get();
@endphp
@forelse($documenti as $documento)
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden documento-item"
data-tipologia="{{ $documento->tipologia ?? $documento->tipo_documento ?? 'altro' }}"
data-categoria="{{ $documento->categoria_spesa }}"
data-data="{{ $documento->data_documento?->format('Y-m-d') ?? '' }}"
data-urgente="{{ $documento->urgente ? 'true' : 'false' }}"
data-approvato="{{ $documento->approvato ? 'true' : 'false' }}">
<div class="bg-gray-50 px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-4">
<!-- Icona tipo documento -->
<div class="flex-shrink-0">
@switch($documento->tipologia ?? $documento->tipo_documento ?? 'altro')
@case('fattura')
<div class="w-12 h-12 bg-red-100 rounded-lg flex items-center justify-center">
<i class="fas fa-file-invoice text-red-600 text-xl"></i>
</div>
@break
@case('contratto')
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<i class="fas fa-handshake text-blue-600 text-xl"></i>
</div>
@break
// ... altri casi
@endswitch
</div>
<!-- Info documento -->
<div>
<h5 class="text-lg font-semibold text-gray-900">
{{ $documento->nome ?? $documento->nome_file }}
</h5>
<div class="flex space-x-4 text-sm text-gray-600">
<span>{{ $documento->fornitore ?? 'N/D' }}</span>
<span>{{ $documento->data_documento?->format('d/m/Y') ?? 'N/D' }}</span>
</div>
</div>
</div>
<!-- Stati e badges -->
<div class="flex space-x-2">
@if($documento->urgente)
<span class="px-2 py-1 bg-red-100 text-red-800 text-xs font-medium rounded-full">
<i class="fas fa-exclamation-triangle mr-1"></i>Urgente
</span>
@endif
@if($documento->approvato)
<span class="px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full">
<i class="fas fa-check mr-1"></i>Approvato
</span>
@endif
</div>
</div>
</div>
<!-- Contenuto documento -->
<div class="px-6 py-4">
@if($documento->importo_collegato || $documento->importo)
<div class="mb-2">
<span class="font-medium text-gray-700">Importo:</span>
<span class="text-green-600 font-semibold">
€{{ number_format($documento->importo_collegato ?? $documento->importo, 2, ',', '.') }}
</span>
</div>
@endif
@if($documento->tags)
<div class="flex flex-wrap gap-1 mb-2">
@foreach(explode(',', $documento->tags) as $tag)
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded">
#{{ trim($tag) }}
</span>
@endforeach
</div>
@endif
@if($documento->note ?? $documento->descrizione)
<p class="text-sm text-gray-600">{{ $documento->note ?? $documento->descrizione }}</p>
@endif
</div>
</div>
@empty
<div class="text-center py-12">
<div class="text-gray-400 mb-4">
<i class="fas fa-folder-open text-6xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Nessun Documento Presente</h3>
<p class="text-gray-600 mb-4">
Carica il primo documento per iniziare la gestione documentale del condominio.
</p>
</div>
@endforelse
</div>
🐛 PROBLEMI INCONTRATI E SOLUZIONI
1. ERRORE: Tabella 'documenti' già esistente
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'documenti' already exists
🔧 SOLUZIONE:
# Rimuovi migration di creazione conflittuale
rm database/migrations/2025_07_21_000001_create_documenti_table.php
# Mantieni solo migration di aggiornamento
php artisan migrate --force
2. ERRORE: Colonna 'dimensione_file' non trovata
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'dimensione_file' in 'field list'
🔧 SOLUZIONE:
// Nel seeder, rimuovi riferimenti a colonne non esistenti
$documenti = [
[
'stabile_id' => $stabileId,
'nome' => 'Contratto Assicurazione',
// 'dimensione_file' => 2450000, // ❌ RIMOSSO - colonna non esiste
'estensione' => 'pdf', // ✅ MANTIENI - colonna esiste
// ... altri campi
]
];
3. ERRORE: Campo 'nome_file' obbligatorio
SQLSTATE[HY000]: General error: 1364 Field 'nome_file' doesn't have a default value
🔧 SOLUZIONE:
// Aggiungi tutti i campi obbligatori nel seeder
$documenti = [
[
'nome' => 'Contratto Assicurazione',
'nome_file' => 'contratto_assicurazione_2025.pdf', // ✅ AGGIUNTO
'path_file' => 'demo/contratti/assicurazione_2025.pdf', // ✅ AGGIUNTO
// ... altri campi
]
];
4. ERRORE: Command property undefined
Call to a member function info() on null
🔧 SOLUZIONE:
// Nel seeder, controlla esistenza command prima di usarlo
if (isset($this->command)) {
$this->command->info('Documenti demo già esistenti. Skip...');
}
🔍 COMANDI UTILI PER DEBUG
Verifica Database
# Conta documenti demo
mysql -u netgescon_user -p'NetGescon2024!' netgescon -e "SELECT COUNT(*) as demo_docs FROM documenti WHERE is_demo = 1;"
# Lista documenti per stabile
mysql -u netgescon_user -p'NetGescon2024!' netgescon -e "SELECT nome, tipologia, fornitore FROM documenti WHERE stabile_id = 1 AND is_demo = 1;"
# Verifica struttura tabella
mysql -u netgescon_user -p'NetGescon2024!' netgescon -e "DESCRIBE documenti;" | head -20
Debug Laravel
# Tinker per test rapidi
php artisan tinker --execute="echo 'Demo docs: ' . \App\Models\Documento::where('is_demo', true)->count();"
# Log delle query
php artisan tinker --execute="\DB::enableQueryLog(); \App\Models\Documento::demo()->get(); dd(\DB::getQueryLog());"
# Test model methods
php artisan tinker --execute="$doc = \App\Models\Documento::demo()->first(); echo $doc->tags_array;"
Gestione Seeder
# Esegui solo seeder specifico
php artisan db:seed --class=DocumentiDemoSeeder
# Reset completo database
php artisan migrate:fresh --seed
# Rollback ultima migration
php artisan migrate:rollback --step=1
📊 STATISTICHE FINALI
Database Schema:
- Tabella
documenti: 39 colonne totali (legacy + nuove) - Tabella
etichette_protocollo: 13 colonne per stampa etichette - Indici: 6 indici per performance ottimizzata
- Foreign Keys: 5 relazioni con altre tabelle
Documenti Demo Creati:
- Totale: 8 documenti rappresentativi
- Tipologie: contratto (2), fattura (1), preventivo (1), verbale (1), comunicazione (1), relazione_tecnica (1), altro (1)
- Categorie: spese_ordinarie, riscaldamento, spese_straordinarie, amministrazione, acqua, manutenzione
- Importi: da €180 a €45.000 (totale: €52.115,50)
File Creati/Modificati:
- ✅ 4 nuovi file: 2 migrations, 1 seeder, 1 comando
- ✅ 3 file modificati: model, vista, database seeder
- ✅ 100% compatibilità con sistema esistente
🎯 RISULTATI OTTENUTI
✅ OBIETTIVI RAGGIUNTI:
- Documenti Persistenti: I dati demo non si cancellano al refresh
- Sistema Scalabile: Architettura pronta per dati reali
- Interfaccia Moderna: Vista aggiornata con dati dal database
- Gestione Completa: Tags, scadenze, approvazioni, OCR
- Performance: Query ottimizzate e indici corretti
🔧 FUNZIONALITÀ ATTIVE:
- Caricamento documenti dal database
- Filtri per tipologia, categoria, urgenza
- Visualizzazione metadati completi
- Sistema di tags e classificazione
- Gestione scadenze con indicatori visivi
- Stati workflow (urgente, approvato)
📝 APPUNTI PER SVILUPPI FUTURI
🚀 PROSSIMI PASSI SUGGERITI:
- Upload Reale: Implementare caricamento file fisici
- OCR Integration: Servizio di estrazione testo automatica
- Workflow Approvazione: Sistema completo di approvazioni
- Stampa Etichette: Generazione QR code e layout stampa
- API REST: Endpoint per integrazione esterna
- Search Engine: Ricerca full-text con Elasticsearch
🔧 MIGLIORAMENTI TECNICI:
- Caching: Redis per documenti frequenti
- Queue Jobs: Processing OCR in background
- File Storage: Integration S3/MinIO per file
- Versioning: Sistema di versioni documenti
- Backup: Strategia backup documenti critici
📋 NOTE IMPORTANTI:
- Sempre testare migration su database di staging prima di produzione
- Usare transazioni per operazioni multi-tabella nel seeder
- Monitorare indici per performance query con grandi volumi
- Implementare soft delete per documenti critici
- Loggiare operazioni per audit trail completo
🔗 RIFERIMENTI UTILI
Documentazione Correlata:
LOG-SVILUPPO-LUGLIO-2025.md- Sessione precedenteMANUALE-TECNICO-RISOLUZIONE-PROBLEMI.md- Guida problemi comuni00-COPILOT-MASTER-GUIDE.md- Guida completa sistemaIMPLEMENTAZIONE-SEEDER-DOCUMENTI-COMPLETA.md- Riepilogo implementazione
Comandi Quick Reference:
# Sistema Demo
php artisan netgescon:demo-documenti
php artisan netgescon:demo-documenti --force
# Database
php artisan migrate --force
php artisan db:seed --class=DocumentiDemoSeeder
# Debug
php artisan tinker --execute="..."
mysql -u netgescon_user -p'NetGescon2024!' netgescon -e "..."
📅 Data Creazione: 22 Luglio 2025
👨💻 Sviluppatore: GitHub Copilot + Utente
🎯 Stato: ✅ IMPLEMENTAZIONE COMPLETATA
📁 Prossimo Log: LOG-SVILUPPO-23-LUGLIO-2025.md