'date', 'returned_at' => 'date', 'counted_in_campaign' => 'boolean', ]; } // ─── Relationships ───────────────────────────────────────── public function territorio() { return $this->belongsTo(Territorio::class, 'territorio_id')->withTrashed(); } public function proclamatore() { return $this->belongsTo(Proclamatore::class, 'proclamatore_id')->withTrashed(); } public function annoTeocratico() { return $this->belongsTo(AnnoTeocratico::class, 'anno_teocratico_id'); } public function campagna() { return $this->belongsTo(Campagna::class, 'campaign_id'); } public function creatoDa() { return $this->belongsTo(User::class, 'created_by'); } public function rientratoDa() { return $this->belongsTo(User::class, 'returned_by'); } // ─── Computed ─────────────────────────────────────────────── /** * Number of days between assignment and return (or today if still open). */ public function getGiorniAttribute(): int { $end = $this->returned_at ?? now(); return Carbon::parse($this->assigned_at)->diffInDays($end); } public function getIsApertaAttribute(): bool { return is_null($this->returned_at); } public function ensurePdfAccessCode(): string { if ($this->pdf_access_code) { return $this->pdf_access_code; } do { $code = strtoupper(Str::random(12)); } while (static::query()->where('pdf_access_code', $code)->exists()); $this->forceFill(['pdf_access_code' => $code])->saveQuietly(); return $code; } public function temporaryPdfViewerUrl(): ?string { if (! $this->is_aperta || ! $this->territorio?->pdf_path) { return null; } return route('assignments.pdf.viewer', [ 'assignment' => $this->id, 'code' => $this->ensurePdfAccessCode(), ]); } public function shortPdfUrl(): ?string { if (! $this->is_aperta || ! $this->territorio?->pdf_path) { return null; } return route('assignments.pdf.short', ['code' => $this->ensurePdfAccessCode()]); } // ─── Scopes ───────────────────────────────────────────────── public function scopeAperte($query) { return $query->whereNull('returned_at'); } public function scopeChiuse($query) { return $query->whereNotNull('returned_at'); } public function scopePerAnnoTeocratico($query, $annoId) { return $query->where('anno_teocratico_id', $annoId); } // ─── Business Logic ───────────────────────────────────────── /** * Check if a campaign prompt should be shown when returning this assignment. * Returns the matching campaign or null. */ public function campagnaApplicabile(?\Carbon\Carbon $returnDate = null): ?Campagna { $returnDate = $returnDate ?? now(); return Campagna::where('start_date', '<=', $returnDate) ->where('end_date', '>=', $this->assigned_at) ->first(); } }