'array', 'metadati_ocr' => 'array', 'dimensione_file' => 'integer', 'data_documento' => 'date', 'data_scadenza' => 'date', 'data_upload' => 'datetime', 'data_approvazione' => 'datetime', 'data_archiviazione' => 'datetime', 'importo_collegato' => 'decimal:2', 'approvato' => 'boolean', 'archiviato' => 'boolean', 'urgente' => 'boolean', 'is_demo' => 'boolean', 'collegato_budget' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /** * Relazioni Eloquent */ public function documentable(): MorphTo { return $this->morphTo(); } public function stabile(): BelongsTo { return $this->belongsTo(Stabile::class); } public function unita(): BelongsTo { return $this->belongsTo(UnitaImmobiliare::class); } public function utente(): BelongsTo { return $this->belongsTo(User::class); } public function approvatoDa(): BelongsTo { return $this->belongsTo(User::class, 'approvato_da'); } /** * Scopes per query comuni */ public function scopeTipo($query, $tipo) { return $query->where('tipo_documento', $tipo); } public function scopeUrgenti($query) { return $query->where('urgente', true); } public function scopeNonApprovati($query) { return $query->where('approvato', false); } public function scopeInScadenza($query, $giorni = 30) { return $query->whereNotNull('data_scadenza') ->where('data_scadenza', '<=', Carbon::now()->addDays($giorni)); } public function scopePerTipologia($query, $tipologia) { return $query->where('tipologia', $tipologia); } public function scopePerCategoria($query, $categoria) { return $query->where('categoria_spesa', $categoria); } public function scopeDemo($query) { return $query->where('is_demo', true); } public function scopeReali($query) { return $query->where('is_demo', false); } /** * Accessor e Mutators */ public function getTagsArrayAttribute() { return $this->tags ? explode(',', $this->tags) : []; } public function setTagsArrayAttribute($value) { $this->attributes['tags'] = is_array($value) ? implode(',', $value) : $value; } public function getDimensioneFormattataAttribute() { if (!$this->dimensione_file) return null; $size = $this->dimensione_file; $units = ['B', 'KB', 'MB', 'GB']; for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) { $size /= 1024; } return round($size, 2) . ' ' . $units[$i]; } public function getGiorniAllaScadenzaAttribute() { if (!$this->data_scadenza) return null; return Carbon::now()->diffInDays($this->data_scadenza, false); } public function getStatoScadenzaAttribute() { $giorni = $this->giorni_alla_scadenza; if ($giorni === null) return 'nessuna_scadenza'; if ($giorni < 0) return 'scaduto'; if ($giorni <= 7) return 'scadenza_imminente'; if ($giorni <= 30) return 'in_scadenza'; return 'valido'; } /** * Metodi di utilità */ public function generaNumeroProtocollo() { $prefisso = strtoupper(substr($this->tipologia ?? 'DOC', 0, 4)); $anno = Carbon::now()->year; // Trova il prossimo numero progressivo per l'anno $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); } public function hasTag($tag) { return in_array($tag, $this->tags_array); } public function addTag($tag) { $tags = $this->tags_array; if (!in_array($tag, $tags)) { $tags[] = $tag; $this->tags_array = $tags; } return $this; } public function removeTag($tag) { $tags = $this->tags_array; $index = array_search($tag, $tags); if ($index !== false) { unset($tags[$index]); $this->tags_array = array_values($tags); } return $this; } public function approva($userId = null) { $this->approvato = true; $this->data_approvazione = Carbon::now(); if ($userId) { $this->approvato_da = $userId; } return $this->save(); } public function archivia() { $this->archiviato = true; $this->data_archiviazione = Carbon::now(); return $this->save(); } /** * Ricerca full-text (compatibile con entrambi i sistemi) */ public function scopeRicerca($query, $termine) { return $query->where(function ($q) use ($termine) { $q->where('nome', 'like', "%{$termine}%") ->orWhere('nome_file', 'like', "%{$termine}%") ->orWhere('fornitore', 'like', "%{$termine}%") ->orWhere('note', 'like', "%{$termine}%") ->orWhere('descrizione', 'like', "%{$termine}%") ->orWhere('contenuto_ocr', 'like', "%{$termine}%"); }); } /** * Statistiche */ public static function statistichePerStabile($stabileId) { return static::where('stabile_id', $stabileId) ->selectRaw(' COUNT(*) as totale_documenti, COUNT(CASE WHEN approvato = 1 THEN 1 END) as approvati, COUNT(CASE WHEN urgente = 1 THEN 1 END) as urgenti, COUNT(CASE WHEN data_scadenza IS NOT NULL AND data_scadenza <= DATE_ADD(NOW(), INTERVAL 30 DAY) THEN 1 END) as in_scadenza, SUM(CASE WHEN importo_collegato IS NOT NULL THEN importo_collegato ELSE 0 END) as valore_totale ') ->first(); } /** * Accessor per URL download */ public function getUrlDownloadAttribute() { return route('admin.documenti.download', $this->id); } /** * Accessor per dimensione leggibile */ public function getDimensioneLeggibileAttribute() { $bytes = $this->dimensione_file; $units = ['B', 'KB', 'MB', 'GB']; for ($i = 0; $bytes > 1024; $i++) { $bytes /= 1024; } return round($bytes, 2) . ' ' . $units[$i]; } }