Files
termanager2/app/Models/Campagna.php
Francesco Picone 426a95211d Revisione blocchi 2 e 3: file protetti, ripristino atomico, import sicuri
Blocco 2
- Esiti delle azioni Livewire come avvisi (trait Avvisa) invece di flash
  che comparivano alla pagina dopo.
- PDF, miniature e versioni dei territori non più serviti da nginx su
  /storage: TerritorioFileController con login, permessi e cache privata.
- Ripristino tutto o niente: verifica dell'archivio, una transazione con
  import, piantine, blocco account e scambio dei file annullabile.
- Backup formato 2 con piantine, storico PDF e area KML.
- Posto unico preso in un passo (Cache::lock) e rispettato anche da
  import XML e PDF.
- Import PDF da cartella: abbinamento corretto (1-A, 12 B, ambigui),
  caricamento a lotti, file non PDF ignorati, PDF sostituito nello storico.
- L'import XML elimina i file dei territori che non usa più; comando
  territori:file-orfani per quelli accumulati prima.

Blocco 3
- Password attuale richiesta al cambio volontario; AuthenticateSession
  chiude le sessioni aperte altrove.
- Niente nomi dei proclamatori in chiaro nel registro attività.
- Export XML senza remember_token; chi riceve l'hash dal file deve
  cambiare password.
- Campagne: ultimo giorno incluso, percentuale sui territori distinti.
- Coda: retry_after 960 s, stop_grace_period 16m, Redis volatile-lru,
  impostazioni rilette a ogni job.
- Azzeramento che cancella anche piantine, area KML e anteprime.
- Pulsanti profilo e menu che non si nascondevano; icona del sito.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:01:41 +02:00

106 lines
2.9 KiB
PHP

<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;
class Campagna extends Model
{
use LogsActivity;
protected $table = 'campagne';
protected $fillable = ['start_date', 'end_date', 'descrizione'];
protected function casts(): array
{
return [
'start_date' => 'date',
'end_date' => 'date',
];
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnly(['start_date', 'end_date', 'descrizione'])
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
/**
* Is this campaign currently active?
*/
public function getIsAttivaAttribute(): bool
{
$today = now()->toDateString();
return $this->start_date->toDateString() <= $today
&& $this->end_date->toDateString() >= $today;
}
/**
* Find the currently active campaign (if any).
*/
public static function attiva(): ?static
{
// Confronto sulla data, non sull'ora: end_date vale la mezzanotte del
// suo giorno, e contro now() la campagna spariva dalla home proprio
// nel suo ultimo giorno.
$oggi = now()->toDateString();
return static::whereDate('start_date', '<=', $oggi)
->whereDate('end_date', '>=', $oggi)
->first();
}
/**
* Assignments counted for this campaign.
*/
public function assegnazioniConteggiate()
{
return $this->hasMany(Assegnazione::class, 'campaign_id')
->where('counted_in_campaign', true);
}
/**
* All assignments with assigned_at in this campaign's range.
*/
public function assegnazioniNelRange()
{
return Assegnazione::where('assigned_at', '>=', $this->start_date)
->where('assigned_at', '<=', $this->end_date);
}
/**
* Quota dei territori attivi percorsi durante la campagna.
*
* Si contano i territori, non le assegnazioni: un territorio percorso due
* volte valeva doppio, e insieme ai territori poi disattivati (fuori dal
* denominatore ma ancora nel numeratore) la percentuale superava il 100%.
*/
public function getPercentualePercorrenzaAttribute(): float
{
$totaleAttivi = Territorio::where('attivo', true)->count();
if ($totaleAttivi === 0) {
return 0.0;
}
$percorsi = $this->assegnazioniConteggiate()
->whereHas('territorio', fn ($q) => $q->where('attivo', true))
->distinct()
->count('territorio_id');
return round(($percorsi / $totaleAttivi) * 100, 1);
}
public function scopeCompletate($query)
{
// Una campagna che finisce oggi è ancora in corso.
return $query->whereDate('end_date', '<', now()->toDateString())->orderByDesc('end_date');
}
}