netgescon-master/app/Models/Amministratore.php
Pikappa2 2e47dd8bc0 feat: Complete millésimal tables system implementation
PHASE 2 COMPLETED: MILLÉSIMAL TABLES 

MODELS ENHANCED:
- TabellaMillesimale: Advanced methods for balance calculation, quota management
- DettaglioTabellaMillesimale: Validation, percentage calculation, expense quota
- Fixed Eloquent relationships with correct foreign keys (id instead of legacy keys)

FEATURES IMPLEMENTED:
 Automatic balance verification (totale_millesimi = 1000)
 Quota calculation for expense distribution
 Standard table types (proprietà_generale, scale, ascensore, etc.)
 Validation for positive millesimi values
 Automatic code generation for AnagraficaCondominiale (ANA prefix)
 Complete relationship testing (Amministratore→Stabili→Unità→Millesimi)

TESTING COMPLETED:
 Tabella millesimale creation and balance verification
 Unità immobiliari creation with correct field names
 Millesimi assignment and calculation (470.5882 + 529.4118 = 1000.0000)
 Expense quota calculation (€470.59 + €529.41 = €1000.00)
 Advanced features: riassunto, percentages, standard types

DATABASE STATUS:
- 1 Tabella Millesimale: 'Proprietà Generale' (balanced: SI)
- 2 Unità Immobiliari: Interno 1 & 2 with correct millesimi
- All relationships operational and tested

READY FOR PHASE 3: EXPENSE CATEGORIES & VOICE MANAGEMENT
2025-07-08 17:03:12 +02:00

406 lines
12 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; // Aggiunto per condomini()
use Illuminate\Database\Eloquent\SoftDeletes; // Aggiunto per soft deletes
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
class Amministratore extends Model
{
use HasFactory;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'amministratori';
// Rimossa la primaryKey personalizzata - usa 'id' standard Laravel
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'nome',
'cognome',
'denominazione_studio',
'partita_iva',
'codice_fiscale_studio',
'indirizzo_studio',
'cap_studio',
'citta_studio',
'provincia_studio',
'telefono_studio',
'email_studio',
'pec_studio',
'codice_univoco',
];
/**
* Get the user associated with the Amministratore.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* Get the condomini for the Amministratore.
*/
public function stabili(): HasMany
{
return $this->hasMany(Stabile::class, 'amministratore_id', 'id');
}
/**
* Get the fornitori for the Amministratore.
*/
public function fornitori(): HasMany
{
return $this->hasMany(Fornitore::class, 'amministratore_id', 'id');
}
protected static function boot()
{
parent::boot();
static::creating(function ($amministratore) {
if (empty($amministratore->codice_univoco)) {
do {
$code = Str::upper(Str::random(8));
} while (self::where('codice_univoco', $code)->exists());
$amministratore->codice_univoco = $code;
}
});
// Auto-crea cartelle dopo la creazione
static::created(function ($amministratore) {
$amministratore->createFolderStructure();
});
// Aggiorna codice_amministratore per compatibilità
static::creating(function ($amministratore) {
if (empty($amministratore->codice_amministratore)) {
$amministratore->codice_amministratore = $amministratore->codice_univoco ?? $amministratore->generateCodiceUnivoco();
}
});
}
/**
* Crea la struttura di cartelle per l'amministratore
*/
public function createFolderStructure(): void
{
$basePath = "amministratori/{$this->codice_univoco}";
$folders = [
'documenti/allegati',
'documenti/contratti',
'documenti/assemblee',
'documenti/preventivi',
'backup/database',
'backup/files',
'temp/upload',
'temp/processing',
'logs',
'exports',
];
foreach ($folders as $folder) {
Storage::disk('local')->makeDirectory("{$basePath}/{$folder}");
}
// Crea file README informativo
$readme = "# Cartella Amministratore: {$this->nome_completo}\n\n";
$readme .= "**Codice**: {$this->codice_amministratore}\n";
$readme .= "**Creato**: " . $this->created_at->format('d/m/Y H:i') . "\n\n";
$readme .= "## Struttura Cartelle\n\n";
$readme .= "- `documenti/` - Documenti dell'amministratore\n";
$readme .= "- `backup/` - Backup automatici\n";
$readme .= "- `temp/` - File temporanei\n";
$readme .= "- `logs/` - Log specifici\n";
$readme .= "- `exports/` - Esportazioni dati\n";
Storage::disk('local')->put("{$basePath}/README.md", $readme);
}
/**
* Ottieni il path della cartella dell'amministratore
*/
public function getFolderPath(): string
{
return "amministratori/{$this->codice_amministratore}";
}
/**
* Genera codice univoco alfanumerico 8 caratteri
*/
public function generateCodiceUnivoco(): string
{
do {
$codice = 'ADM' . strtoupper(substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 5));
} while (self::where('codice_amministratore', $codice)->exists());
return $codice;
}
/**
* Verifica se l'amministratore ha un database dedicato attivo
*/
public function hasDedicatedDatabase(): bool
{
return !empty($this->database_attivo);
}
/**
* Ottiene il nome del database dedicato per questo amministratore
*/
public function getDatabaseName(): string
{
return $this->database_attivo ?: "netgescon_{$this->codice_amministratore}";
}
/**
* Ottiene il percorso fisico dell'archivio amministratore
*/
public function getArchivePath(): string
{
return storage_path("app/amministratori/{$this->codice_amministratore}");
}
/**
* Ottiene il percorso del backup database
*/
public function getDatabaseBackupPath(): string
{
return $this->getArchivePath() . '/backup/database';
}
/**
* Verifica se l'archivio fisico esiste
*/
public function archiveExists(): bool
{
return is_dir($this->getArchivePath());
}
/**
* Ottiene informazioni dettagliate sull'archivio
*/
public function getArchiveInfo(): array
{
$archivePath = $this->getArchivePath();
if (!$this->archiveExists()) {
return [
'exists' => false,
'path' => $archivePath,
'size' => 0,
'files_count' => 0,
'last_backup' => null
];
}
$size = 0;
$filesCount = 0;
$lastBackup = null;
// Calcola dimensione totale e numero file
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($archivePath)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$size += $file->getSize();
$filesCount++;
// Trova ultimo backup database
if (str_contains($file->getPathname(), 'backup/database') &&
str_ends_with($file->getFilename(), '.sql')) {
$backupTime = filemtime($file->getPathname());
if (!$lastBackup || $backupTime > $lastBackup) {
$lastBackup = $backupTime;
}
}
}
}
return [
'exists' => true,
'path' => $archivePath,
'size' => $size,
'size_formatted' => $this->formatBytes($size),
'files_count' => $filesCount,
'last_backup' => $lastBackup ? date('Y-m-d H:i:s', $lastBackup) : null,
'directories' => [
'documenti' => is_dir($archivePath . '/documenti'),
'backup' => is_dir($archivePath . '/backup'),
'temp' => is_dir($archivePath . '/temp'),
'logs' => is_dir($archivePath . '/logs'),
'exports' => is_dir($archivePath . '/exports'),
]
];
}
/**
* Formatta bytes in formato leggibile
*/
private function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision) . ' ' . $units[$i];
}
/**
* Crea backup del database amministratore
*/
public function createDatabaseBackup(): array
{
try {
if (!$this->hasDedicatedDatabase()) {
throw new \Exception('Amministratore non ha database dedicato');
}
$backupPath = $this->getDatabaseBackupPath();
if (!is_dir($backupPath)) {
mkdir($backupPath, 0755, true);
}
$filename = "backup_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".sql";
$fullPath = $backupPath . '/' . $filename;
$dbName = $this->getDatabaseName();
$command = sprintf(
'mysqldump -h %s -u %s -p%s %s > %s',
env('DB_HOST', '127.0.0.1'),
env('DB_USERNAME'),
env('DB_PASSWORD'),
escapeshellarg($dbName),
escapeshellarg($fullPath)
);
exec($command, $output, $returnCode);
if ($returnCode !== 0) {
throw new \Exception('Errore durante backup database: ' . implode("\n", $output));
}
return [
'success' => true,
'filename' => $filename,
'path' => $fullPath,
'size' => filesize($fullPath),
'created_at' => date('Y-m-d H:i:s')
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Prepara archivio per migrazione/trasferimento
*/
public function prepareForMigration(): array
{
try {
// 1. Backup database
$dbBackup = $this->createDatabaseBackup();
if (!$dbBackup['success']) {
throw new \Exception('Errore backup database: ' . $dbBackup['error']);
}
// 2. Crea archivio ZIP dell'intera cartella
$archivePath = $this->getArchivePath();
$zipFilename = "migration_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".zip";
$zipPath = storage_path("app/migrations/{$zipFilename}");
if (!is_dir(dirname($zipPath))) {
mkdir(dirname($zipPath), 0755, true);
}
$zip = new \ZipArchive();
if ($zip->open($zipPath, \ZipArchive::CREATE) !== TRUE) {
throw new \Exception('Impossibile creare archivio ZIP');
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($archivePath)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = str_replace($archivePath . DIRECTORY_SEPARATOR, '', $file->getPathname());
$zip->addFile($file->getPathname(), $relativePath);
}
}
$zip->close();
// 3. Crea file metadata per migrazione
$metadata = [
'amministratore' => [
'codice' => $this->codice_amministratore,
'nome' => $this->nome,
'cognome' => $this->cognome,
'denominazione_studio' => $this->denominazione_studio,
'database_name' => $this->getDatabaseName(),
],
'migration' => [
'created_at' => date('Y-m-d H:i:s'),
'source_server' => env('APP_URL'),
'database_backup' => $dbBackup['filename'],
'archive_size' => filesize($zipPath),
'files_count' => $this->getArchiveInfo()['files_count'],
],
'requirements' => [
'php_version' => PHP_VERSION,
'laravel_version' => app()->version(),
'mysql_version' => DB::select('SELECT VERSION() as version')[0]->version,
]
];
$metadataPath = dirname($zipPath) . "/metadata_{$this->codice_amministratore}.json";
file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT));
return [
'success' => true,
'zip_file' => $zipPath,
'metadata_file' => $metadataPath,
'size' => filesize($zipPath),
'database_backup' => $dbBackup['filename']
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Attributo computed per nome completo
*/
public function getNomeCompletoAttribute(): string
{
return trim($this->nome . ' ' . $this->cognome);
}
}