netgescon-master/app/Models/Documento.php

328 lines
8.4 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\MorphTo;
use Carbon\Carbon;
class Documento extends Model
{
use HasFactory;
/**
* NETGESCON MODEL: Documento
*
* Model per la gestione avanzata dei documenti condominiali
* con supporto OCR, tagging e integrazione contabile.
*
* @author GitHub Copilot
* @date 21/07/2025
* @version 1.0.0
*/
protected $table = 'documenti';
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',
'percorso_file',
'numero_pagine',
'dimensione_file',
'estensione',
'contenuto_ocr',
'metadati_ocr',
'approvato',
'archiviato',
'urgente',
'is_demo',
'movimento_contabile_id',
'collegato_budget',
'data_upload',
'data_approvazione',
'data_archiviazione',
'approvato_da'
];
protected $casts = [
'xml_data' => '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];
}
}