'boolean', 'prioritario' => 'boolean', ]; } public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly(['numero', 'zona_id', 'tipologia_id', 'attivo', 'prioritario', 'pdf_path']) ->logOnlyDirty() ->dontSubmitEmptyLogs(); } // ─── Relationships ───────────────────────────────────────── public function zona() { return $this->belongsTo(Zona::class, 'zona_id'); } public function tipologia() { return $this->belongsTo(Tipologia::class, 'tipologia_id'); } public function assegnazioni() { return $this->hasMany(Assegnazione::class, 'territorio_id'); } public function assegnazioneCorrente() { return $this->hasOne(Assegnazione::class, 'territorio_id') ->whereNull('returned_at') ->latestOfMany('assigned_at'); } public function ultimaAssegnazione() { return $this->hasOne(Assegnazione::class, 'territorio_id') ->latestOfMany('assigned_at'); } // ─── Computed State ───────────────────────────────────────── public function getStatoAttribute(): string { if (!$this->attivo) { return 'inattivo'; } $corrente = $this->assegnazioneCorrente; if ($corrente) { $giorniAssegnato = Carbon::parse($corrente->assigned_at)->diffInDays(now()); $sogliaSmarrito = Setting::getValue('giorni_per_smarrito', 120); if ($giorniAssegnato > $sogliaSmarrito) { return 'da_rientrare'; } return 'assegnato'; } return 'in_reparto'; } public function getAssegnatarioAttribute(): ?Proclamatore { return $this->assegnazioneCorrente?->proclamatore; } /** * Days since last return (or creation if never assigned). */ public function getGiorniGiacenzaAttribute(): int { $ultima = $this->ultimaAssegnazione; if ($ultima && $ultima->returned_at) { return Carbon::parse($ultima->returned_at)->diffInDays(now()); } if (!$ultima) { return $this->created_at->diffInDays(now()); } // Currently assigned, no giacenza concept return 0; } /** * Is this territory "prioritario"? * Manual flag OR giacenza exceeds threshold (threshold always wins). */ public function getIsPrioritarioAttribute(): bool { if (!$this->attivo) { return false; } if ($this->prioritario) { return true; } // Threshold-based priority (only when in reparto) if ($this->stato === 'in_reparto') { $soglia = Setting::getValue('giorni_giacenza_prioritari', 180); return $this->giorni_giacenza > $soglia; } return false; } // ─── Scopes ───────────────────────────────────────────────── public function scopeAttivi($query) { return $query->where('attivo', true); } public function scopeInReparto($query) { return $query->attivi() ->whereDoesntHave('assegnazioni', function ($q) { $q->whereNull('returned_at'); }); } public function scopeAssegnato($query) { return $query->attivi() ->whereHas('assegnazioni', function ($q) { $q->whereNull('returned_at'); }); } public function scopeDaRientrare($query) { $soglia = Setting::getValue('giorni_per_smarrito', 120); return $query->attivi() ->whereHas('assegnazioneCorrente', function ($q) use ($soglia) { $q->where('assigned_at', '<=', now()->subDays($soglia)); }); } public function scopeDaAssegnare($query) { $soglia = Setting::getValue('giorni_giacenza_da_assegnare', 120); return $query->inReparto() ->where(function ($q) use ($soglia) { // Territories whose last assignment returned > soglia days ago $q->whereHas('assegnazioni', function ($sub) use ($soglia) { $sub->whereNotNull('returned_at') ->where('returned_at', '<=', now()->subDays($soglia)) ->whereRaw('id = (SELECT MAX(a2.id) FROM assegnazioni a2 WHERE a2.territorio_id = assegnazioni.territorio_id)'); }) // Or territories never assigned, created > soglia days ago ->orWhere(function ($sub) use ($soglia) { $sub->doesntHave('assegnazioni') ->where('created_at', '<=', now()->subDays($soglia)); }); }); } public function scopePrioritari($query) { $soglia = Setting::getValue('giorni_giacenza_prioritari', 180); return $query->inReparto() ->where(function ($q) use ($soglia) { // Manual priority flag $q->where('prioritario', true) // OR threshold-based ->orWhere(function ($sub) use ($soglia) { $sub->whereHas('assegnazioni', function ($a) use ($soglia) { $a->whereNotNull('returned_at') ->where('returned_at', '<=', now()->subDays($soglia)) ->whereRaw('id = (SELECT MAX(a2.id) FROM assegnazioni a2 WHERE a2.territorio_id = assegnazioni.territorio_id)'); }) ->orWhere(function ($never) use ($soglia) { $never->doesntHave('assegnazioni') ->where('created_at', '<=', now()->subDays($soglia)); }); }); }); } }