+++fix: add thumbnail support for territori
This commit is contained in:
@@ -3,16 +3,19 @@
|
||||
namespace App\Livewire\Assegnazioni;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Computed;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Assegnazione;
|
||||
use App\Models\AnnoTeocratico;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Assegna extends Component
|
||||
{
|
||||
public ?int $territorio_id = null;
|
||||
public ?int $proclamatore_id = null;
|
||||
public string $assigned_at = '';
|
||||
public string $territorioSearch = '';
|
||||
|
||||
// Optional pre-selection from parent context
|
||||
public ?int $preselectedTerritorioId = null;
|
||||
@@ -80,10 +83,32 @@ class Assegna extends Component
|
||||
return $this->redirect(route('territori.show', $territorio), navigate: true);
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function selectedThumbnailUrl(): ?string
|
||||
{
|
||||
if (!$this->territorio_id) {
|
||||
return null;
|
||||
}
|
||||
$t = Territorio::find($this->territorio_id);
|
||||
return $t?->thumbnail_path ? Storage::url($t->thumbnail_path) : null;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$territoriDisponibili = Territorio::where('attivo', true)
|
||||
$territoriQuery = Territorio::where('attivo', true)
|
||||
->whereDoesntHave('assegnazioni', fn($q) => $q->aperte())
|
||||
->with(['zona', 'tipologia']);
|
||||
|
||||
if (trim($this->territorioSearch) !== '') {
|
||||
$term = trim($this->territorioSearch);
|
||||
$territoriQuery->where(function ($q) use ($term) {
|
||||
$q->where('numero', 'like', "%{$term}%")
|
||||
->orWhereHas('zona', fn($z) => $z->where('nome', 'like', "%{$term}%"))
|
||||
->orWhereHas('tipologia', fn($t) => $t->where('nome', 'like', "%{$term}%"));
|
||||
});
|
||||
}
|
||||
|
||||
$territoriDisponibili = $territoriQuery
|
||||
->orderBy('numero')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
use App\Models\Assegnazione;
|
||||
use App\Models\AnnoTeocratico;
|
||||
use App\Models\Campagna;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Zona;
|
||||
use App\Models\Tipologia;
|
||||
|
||||
@@ -21,6 +24,23 @@ class Registro extends Component
|
||||
public string $sortField = 'assigned_at';
|
||||
public string $sortDirection = 'desc';
|
||||
|
||||
// ─── Modal create/edit ──────────────────────────────────────
|
||||
public bool $showModal = false;
|
||||
public ?int $editingId = null;
|
||||
|
||||
public string $form_territorio_id = '';
|
||||
public string $form_proclamatore_id = '';
|
||||
public string $form_anno_id = '';
|
||||
public string $form_assigned_at = '';
|
||||
public string $form_returned_at = '';
|
||||
public bool $form_counted_in_campaign = false;
|
||||
public string $form_campaign_id = '';
|
||||
public string $form_note = '';
|
||||
|
||||
// ─── Delete confirm ─────────────────────────────────────────
|
||||
public bool $showDeleteConfirm = false;
|
||||
public ?int $deleteId = null;
|
||||
|
||||
protected $queryString = [
|
||||
'search' => ['except' => ''],
|
||||
'filtroAnno' => ['except' => ''],
|
||||
@@ -44,6 +64,91 @@ class Registro extends Component
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Admin CRUD ─────────────────────────────────────────────
|
||||
|
||||
public function openCreate(): void
|
||||
{
|
||||
abort_if(!auth()->user()->can('settings.manage'), 403);
|
||||
$this->editingId = null;
|
||||
$this->form_territorio_id = '';
|
||||
$this->form_proclamatore_id = '';
|
||||
$this->form_anno_id = '';
|
||||
$this->form_assigned_at = now()->format('Y-m-d');
|
||||
$this->form_returned_at = '';
|
||||
$this->form_counted_in_campaign = false;
|
||||
$this->form_campaign_id = '';
|
||||
$this->form_note = '';
|
||||
$this->resetValidation();
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function openEdit(int $id): void
|
||||
{
|
||||
abort_if(!auth()->user()->can('settings.manage'), 403);
|
||||
$a = Assegnazione::findOrFail($id);
|
||||
$this->editingId = $id;
|
||||
$this->form_territorio_id = (string) $a->territorio_id;
|
||||
$this->form_proclamatore_id = (string) $a->proclamatore_id;
|
||||
$this->form_anno_id = (string) $a->anno_teocratico_id;
|
||||
$this->form_assigned_at = $a->assigned_at?->format('Y-m-d') ?? '';
|
||||
$this->form_returned_at = $a->returned_at?->format('Y-m-d') ?? '';
|
||||
$this->form_counted_in_campaign = (bool) $a->counted_in_campaign;
|
||||
$this->form_campaign_id = (string) ($a->campaign_id ?? '');
|
||||
$this->form_note = $a->note ?? '';
|
||||
$this->resetValidation();
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
abort_if(!auth()->user()->can('settings.manage'), 403);
|
||||
$this->validate([
|
||||
'form_territorio_id' => 'required|exists:territori,id',
|
||||
'form_proclamatore_id' => 'required|exists:proclamatori,id',
|
||||
'form_anno_id' => 'required|exists:anni_teocratici,id',
|
||||
'form_assigned_at' => 'required|date',
|
||||
'form_returned_at' => 'nullable|date|after_or_equal:form_assigned_at',
|
||||
'form_campaign_id' => 'nullable|exists:campagne,id',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'territorio_id' => $this->form_territorio_id,
|
||||
'proclamatore_id' => $this->form_proclamatore_id,
|
||||
'anno_teocratico_id' => $this->form_anno_id,
|
||||
'assigned_at' => $this->form_assigned_at,
|
||||
'returned_at' => $this->form_returned_at ?: null,
|
||||
'counted_in_campaign' => $this->form_counted_in_campaign,
|
||||
'campaign_id' => $this->form_campaign_id ?: null,
|
||||
'note' => $this->form_note ?: null,
|
||||
];
|
||||
|
||||
if ($this->editingId) {
|
||||
Assegnazione::findOrFail($this->editingId)->update($data);
|
||||
} else {
|
||||
$data['created_by'] = auth()->id();
|
||||
Assegnazione::create($data);
|
||||
}
|
||||
|
||||
$this->showModal = false;
|
||||
}
|
||||
|
||||
public function askDelete(int $id): void
|
||||
{
|
||||
abort_if(!auth()->user()->can('settings.manage'), 403);
|
||||
$this->deleteId = $id;
|
||||
$this->showDeleteConfirm = true;
|
||||
}
|
||||
|
||||
public function deleteConfirmed(): void
|
||||
{
|
||||
abort_if(!auth()->user()->can('settings.manage'), 403);
|
||||
if ($this->deleteId) {
|
||||
Assegnazione::findOrFail($this->deleteId)->delete();
|
||||
}
|
||||
$this->deleteId = null;
|
||||
$this->showDeleteConfirm = false;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Assegnazione::with(['territorio.zona', 'proclamatore', 'annoTeocratico', 'campagna']);
|
||||
@@ -96,6 +201,9 @@ class Registro extends Component
|
||||
'anni' => AnnoTeocratico::orderByDesc('start_date')->get(),
|
||||
'zone' => Zona::attive()->orderBy('nome')->get(),
|
||||
'tipologie' => Tipologia::orderBy('nome')->get(),
|
||||
'territori' => Territorio::attivi()->orderBy('numero')->get(),
|
||||
'proclamatori' => Proclamatore::attivi()->orderBy('cognome')->orderBy('nome')->get(),
|
||||
'campagne' => Campagna::orderByDesc('created_at')->get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
714
app/Livewire/Settings/XmlExchange.php
Normal file
714
app/Livewire/Settings/XmlExchange.php
Normal file
@@ -0,0 +1,714 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Settings;
|
||||
|
||||
use App\Models\AnnoTeocratico;
|
||||
use App\Models\Assegnazione;
|
||||
use App\Models\Campagna;
|
||||
use App\Models\Proclamatore;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Tipologia;
|
||||
use App\Models\User;
|
||||
use App\Models\Zona;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
|
||||
class XmlExchange extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $sqlDump;
|
||||
public $xmlImport;
|
||||
public array $importStats = [];
|
||||
public array $importIssues = [];
|
||||
|
||||
public function convertLegacySqlToXml()
|
||||
{
|
||||
$this->validate([
|
||||
'sqlDump' => ['required', 'file', 'mimes:sql,txt'],
|
||||
]);
|
||||
|
||||
$content = file_get_contents($this->sqlDump->getRealPath());
|
||||
$dataset = $this->legacySqlToDataset($content ?: '');
|
||||
$xml = $this->datasetToXml($dataset, 'legacy-sql-conversion');
|
||||
|
||||
return response()->streamDownload(function () use ($xml) {
|
||||
echo $xml;
|
||||
}, 'termanager-conversion.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
|
||||
}
|
||||
|
||||
public function importXmlIntoApp(): void
|
||||
{
|
||||
$this->importStats = [];
|
||||
$this->importIssues = [];
|
||||
|
||||
$this->validate([
|
||||
'xmlImport' => ['required', 'file', 'mimes:xml,txt'],
|
||||
]);
|
||||
|
||||
$content = file_get_contents($this->xmlImport->getRealPath());
|
||||
if (! $content) {
|
||||
$this->addError('xmlImport', 'File XML non valido.');
|
||||
return;
|
||||
}
|
||||
|
||||
$xml = @simplexml_load_string($content);
|
||||
if (! $xml) {
|
||||
$this->addError('xmlImport', 'Impossibile leggere il file XML.');
|
||||
return;
|
||||
}
|
||||
|
||||
$actorId = auth()->id() ?? User::query()->value('id');
|
||||
if (! $actorId) {
|
||||
$this->addError('xmlImport', 'Serve almeno un utente nel sistema per importare i dati.');
|
||||
return;
|
||||
}
|
||||
|
||||
$stats = [
|
||||
'zone_importate' => 0,
|
||||
'tipologie_importate' => 0,
|
||||
'proclamatori_importati' => 0,
|
||||
'anni_importati' => 0,
|
||||
'campagne_importate' => 0,
|
||||
'territori_importati' => 0,
|
||||
'assegnazioni_importate' => 0,
|
||||
'duplicate_territori' => 0,
|
||||
'assegnazioni_saltate' => 0,
|
||||
];
|
||||
|
||||
DB::transaction(function () use ($xml, $actorId, &$stats) {
|
||||
Assegnazione::query()->delete();
|
||||
Campagna::query()->delete();
|
||||
Territorio::query()->withTrashed()->forceDelete();
|
||||
Proclamatore::query()->withTrashed()->forceDelete();
|
||||
Tipologia::query()->delete();
|
||||
Zona::query()->delete();
|
||||
AnnoTeocratico::query()->delete();
|
||||
Setting::query()->delete();
|
||||
|
||||
$settingsNode = $xml->settings;
|
||||
if ($settingsNode) {
|
||||
Setting::instance()->update([
|
||||
'congregazione_nome' => (string) ($settingsNode->congregazione_nome ?? ''),
|
||||
'giorni_giacenza_da_assegnare' => (int) ($settingsNode->giorni_giacenza_da_assegnare ?? 120),
|
||||
'giorni_giacenza_prioritari' => (int) ($settingsNode->giorni_giacenza_prioritari ?? 180),
|
||||
'giorni_per_smarrito' => (int) ($settingsNode->giorni_per_smarrito ?? 120),
|
||||
'home_limit_list' => (int) ($settingsNode->home_limit_list ?? 10),
|
||||
'audit_retention_days' => (int) ($settingsNode->audit_retention_days ?? 730),
|
||||
'setup_completed' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$zoneMap = [];
|
||||
foreach (($xml->zones->zone ?? []) as $zoneNode) {
|
||||
$z = Zona::create([
|
||||
'nome' => (string) $zoneNode->nome,
|
||||
'attivo' => ((int) ($zoneNode->attivo ?? 1)) === 1,
|
||||
]);
|
||||
$stats['zone_importate']++;
|
||||
$legacyId = (string) ($zoneNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$zoneMap[$legacyId] = $z->id;
|
||||
}
|
||||
}
|
||||
|
||||
$tipologiaMap = [];
|
||||
foreach (($xml->tipologie->tipologia ?? []) as $tipologiaNode) {
|
||||
$t = Tipologia::create([
|
||||
'nome' => (string) $tipologiaNode->nome,
|
||||
'attivo' => ((int) ($tipologiaNode->attivo ?? 1)) === 1,
|
||||
]);
|
||||
$stats['tipologie_importate']++;
|
||||
$legacyId = (string) ($tipologiaNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$tipologiaMap[$legacyId] = $t->id;
|
||||
}
|
||||
}
|
||||
|
||||
$proclamatoreMap = [];
|
||||
foreach (($xml->proclamatori->proclamatore ?? []) as $proclamatoreNode) {
|
||||
$p = Proclamatore::create([
|
||||
'nome' => (string) $proclamatoreNode->nome,
|
||||
'cognome' => (string) $proclamatoreNode->cognome,
|
||||
'attivo' => ((int) ($proclamatoreNode->attivo ?? 1)) === 1,
|
||||
]);
|
||||
$stats['proclamatori_importati']++;
|
||||
$legacyId = (string) ($proclamatoreNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$proclamatoreMap[$legacyId] = $p->id;
|
||||
}
|
||||
}
|
||||
|
||||
$annoMap = [];
|
||||
foreach (($xml->anni_teocratici->anno ?? []) as $annoNode) {
|
||||
$label = (string) $annoNode->label;
|
||||
$anno = AnnoTeocratico::create([
|
||||
'label' => $label,
|
||||
'start_date' => (string) $annoNode->start_date,
|
||||
'end_date' => (string) $annoNode->end_date,
|
||||
]);
|
||||
$stats['anni_importati']++;
|
||||
$legacyId = (string) ($annoNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$annoMap[$legacyId] = $anno->id;
|
||||
}
|
||||
}
|
||||
|
||||
$campagnaMap = [];
|
||||
foreach (($xml->campagne->campagna ?? []) as $campagnaNode) {
|
||||
$campagna = Campagna::create([
|
||||
'descrizione' => (string) $campagnaNode->descrizione,
|
||||
'start_date' => (string) $campagnaNode->start_date,
|
||||
'end_date' => (string) $campagnaNode->end_date,
|
||||
]);
|
||||
$stats['campagne_importate']++;
|
||||
$legacyId = (string) ($campagnaNode['legacy_id'] ?? '');
|
||||
if ($legacyId !== '') {
|
||||
$campagnaMap[$legacyId] = $campagna->id;
|
||||
}
|
||||
}
|
||||
|
||||
$territorioMap = [];
|
||||
$territoriNumeroVisti = [];
|
||||
foreach (($xml->territori->territorio ?? []) as $territorioNode) {
|
||||
$legacyZonaId = (string) ($territorioNode->legacy_zona_id ?? '');
|
||||
$legacyTipologiaId = (string) ($territorioNode->legacy_tipologia_id ?? '');
|
||||
$numero = trim((string) $territorioNode->numero);
|
||||
$legacyId = (string) ($territorioNode['legacy_id'] ?? '');
|
||||
|
||||
if ($numero === '' || isset($territoriNumeroVisti[$numero])) {
|
||||
$stats['duplicate_territori']++;
|
||||
$this->pushImportIssue('territorio', $legacyId, 'Territorio duplicato o numero vuoto (numero=' . ($numero !== '' ? $numero : 'vuoto') . ')');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($zoneMap[$legacyZonaId], $tipologiaMap[$legacyTipologiaId])) {
|
||||
$this->pushImportIssue('territorio', $legacyId, 'Riferimento zona/tipologia non trovato (zona=' . $legacyZonaId . ', tipologia=' . $legacyTipologiaId . ')');
|
||||
continue;
|
||||
}
|
||||
|
||||
$territorio = Territorio::create([
|
||||
'numero' => $numero,
|
||||
'zona_id' => $zoneMap[$legacyZonaId],
|
||||
'tipologia_id' => $tipologiaMap[$legacyTipologiaId],
|
||||
'confini' => (string) ($territorioNode->confini ?? ''),
|
||||
'note' => (string) ($territorioNode->note ?? ''),
|
||||
'attivo' => ((int) ($territorioNode->attivo ?? 1)) === 1,
|
||||
'prioritario' => ((int) ($territorioNode->prioritario ?? 0)) === 1,
|
||||
]);
|
||||
|
||||
$territoriNumeroVisti[$numero] = true;
|
||||
$stats['territori_importati']++;
|
||||
|
||||
if ($legacyId !== '') {
|
||||
$territorioMap[$legacyId] = $territorio->id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (($xml->assegnazioni->assegnazione ?? []) as $assegnazioneNode) {
|
||||
$legacyTerritorio = (string) ($assegnazioneNode->legacy_territorio_id ?? '');
|
||||
$legacyProclamatore = (string) ($assegnazioneNode->legacy_proclamatore_id ?? '');
|
||||
$legacyAnno = (string) ($assegnazioneNode->legacy_anno_id ?? '');
|
||||
$legacyId = (string) ($assegnazioneNode['legacy_id'] ?? '');
|
||||
$assignedAt = $this->normalizeDateForDb((string) ($assegnazioneNode->assigned_at ?? ''), false);
|
||||
$returnedAt = $this->normalizeDateForDb((string) ($assegnazioneNode->returned_at ?? ''), true);
|
||||
|
||||
if (!isset($territorioMap[$legacyTerritorio], $proclamatoreMap[$legacyProclamatore], $annoMap[$legacyAnno])) {
|
||||
$stats['assegnazioni_saltate']++;
|
||||
$this->pushImportIssue('assegnazione', $legacyId, 'Riferimenti mancanti (territorio=' . $legacyTerritorio . ', proclamatore=' . $legacyProclamatore . ', anno=' . $legacyAnno . ')');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($assignedAt === null) {
|
||||
$stats['assegnazioni_saltate']++;
|
||||
$this->pushImportIssue('assegnazione', $legacyId, 'Data assigned_at non valida o vuota');
|
||||
continue;
|
||||
}
|
||||
|
||||
$legacyCampagna = (string) ($assegnazioneNode->legacy_campagna_id ?? '');
|
||||
|
||||
Assegnazione::create([
|
||||
'territorio_id' => $territorioMap[$legacyTerritorio],
|
||||
'proclamatore_id' => $proclamatoreMap[$legacyProclamatore],
|
||||
'anno_teocratico_id' => $annoMap[$legacyAnno],
|
||||
'assigned_at' => $assignedAt,
|
||||
'returned_at' => $returnedAt,
|
||||
'counted_in_campaign' => ((int) ($assegnazioneNode->counted_in_campaign ?? 0)) === 1,
|
||||
'campaign_id' => $legacyCampagna !== '' && isset($campagnaMap[$legacyCampagna]) ? $campagnaMap[$legacyCampagna] : null,
|
||||
'note' => (string) ($assegnazioneNode->note ?? ''),
|
||||
'created_by' => $actorId,
|
||||
'returned_by' => $returnedAt !== null ? $actorId : null,
|
||||
]);
|
||||
$stats['assegnazioni_importate']++;
|
||||
}
|
||||
});
|
||||
|
||||
$this->importStats = $stats;
|
||||
|
||||
$message = 'Import XML completato con successo.';
|
||||
if ($stats['duplicate_territori'] > 0 || $stats['assegnazioni_saltate'] > 0) {
|
||||
$message .= ' Territori duplicati saltati: ' . $stats['duplicate_territori'] . '. Assegnazioni saltate: ' . $stats['assegnazioni_saltate'] . '.';
|
||||
}
|
||||
|
||||
session()->flash('success', $message);
|
||||
}
|
||||
|
||||
public function downloadImportLogPdf()
|
||||
{
|
||||
if (empty($this->importStats)) {
|
||||
session()->flash('error', 'Nessun log da scaricare. Esegui prima un import XML.');
|
||||
return;
|
||||
}
|
||||
|
||||
$stats = $this->importStats;
|
||||
$issues = $this->importIssues;
|
||||
$generatedAt = now()->format('d/m/Y H:i:s');
|
||||
|
||||
$html = view('pdf.import-log', compact('stats', 'issues', 'generatedAt'))->render();
|
||||
|
||||
return response()->streamDownload(function () use ($html) {
|
||||
echo Pdf::loadHTML($html)
|
||||
->setPaper('a4', 'portrait')
|
||||
->output();
|
||||
}, 'import-log-' . now()->format('Ymd-His') . '.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
public function exportCurrentAsXml()
|
||||
{
|
||||
$dataset = $this->currentDataset();
|
||||
$xml = $this->datasetToXml($dataset, 'current-app-export');
|
||||
|
||||
return response()->streamDownload(function () use ($xml) {
|
||||
echo $xml;
|
||||
}, 'termanager-export.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.settings.xml-exchange');
|
||||
}
|
||||
|
||||
private function currentDataset(): array
|
||||
{
|
||||
$settings = Setting::instance();
|
||||
|
||||
return [
|
||||
'settings' => [
|
||||
'congregazione_nome' => (string) ($settings->congregazione_nome ?? ''),
|
||||
'giorni_giacenza_da_assegnare' => (int) ($settings->giorni_giacenza_da_assegnare ?? 120),
|
||||
'giorni_giacenza_prioritari' => (int) ($settings->giorni_giacenza_prioritari ?? 180),
|
||||
'giorni_per_smarrito' => (int) ($settings->giorni_per_smarrito ?? 120),
|
||||
'home_limit_list' => (int) ($settings->home_limit_list ?? 10),
|
||||
'audit_retention_days' => (int) ($settings->audit_retention_days ?? 730),
|
||||
],
|
||||
'zones' => Zona::query()->orderBy('id')->get(['id', 'nome', 'attivo'])->toArray(),
|
||||
'tipologie' => Tipologia::query()->orderBy('id')->get(['id', 'nome', 'attivo'])->toArray(),
|
||||
'proclamatori' => Proclamatore::query()->withTrashed()->orderBy('id')->get(['id', 'nome', 'cognome', 'attivo'])->toArray(),
|
||||
'territori' => Territorio::query()->withTrashed()->orderBy('id')->get(['id', 'numero', 'zona_id', 'tipologia_id', 'confini', 'note', 'attivo', 'prioritario'])->toArray(),
|
||||
'anni_teocratici' => AnnoTeocratico::query()->orderBy('id')->get(['id', 'label', 'start_date', 'end_date'])->toArray(),
|
||||
'campagne' => Campagna::query()->orderBy('id')->get(['id', 'descrizione', 'start_date', 'end_date'])->toArray(),
|
||||
'assegnazioni' => Assegnazione::query()->orderBy('id')->get(['id', 'territorio_id', 'proclamatore_id', 'anno_teocratico_id', 'campaign_id', 'assigned_at', 'returned_at', 'counted_in_campaign', 'note'])->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
private function legacySqlToDataset(string $sql): array
|
||||
{
|
||||
$rows = $this->extractInsertRows($sql);
|
||||
|
||||
$congregazione = $rows['congregazione'][0][1] ?? 'Congregazione';
|
||||
$impostazioni = $rows['impostazioni'][0] ?? [1, 120, 10, 120];
|
||||
|
||||
$settings = [
|
||||
'congregazione_nome' => (string) $congregazione,
|
||||
'giorni_giacenza_da_assegnare' => (int) ($impostazioni[3] ?? 120),
|
||||
'giorni_giacenza_prioritari' => (int) ($impostazioni[1] ?? 180),
|
||||
'giorni_per_smarrito' => (int) ($impostazioni[3] ?? 120),
|
||||
'home_limit_list' => (int) ($impostazioni[2] ?? 10),
|
||||
'audit_retention_days' => 730,
|
||||
];
|
||||
|
||||
$zones = [];
|
||||
foreach (($rows['zona'] ?? []) as $r) {
|
||||
$zones[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'nome' => (string) ($r[1] ?? ''),
|
||||
'attivo' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$tipologie = [];
|
||||
foreach (($rows['tipologia'] ?? []) as $r) {
|
||||
$tipologie[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'nome' => (string) ($r[1] ?? ''),
|
||||
'attivo' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$proclamatori = [];
|
||||
foreach (($rows['proclamatore'] ?? []) as $r) {
|
||||
$proclamatori[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'cognome' => (string) ($r[1] ?? ''),
|
||||
'nome' => (string) ($r[2] ?? ''),
|
||||
'attivo' => ((int) ($r[3] ?? 1)) === 1 ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
$territori = [];
|
||||
foreach (($rows['territorio'] ?? []) as $r) {
|
||||
$territori[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'numero' => (string) ($r[1] ?? ''),
|
||||
'legacy_tipologia_id' => (string) ($r[2] ?? ''),
|
||||
'legacy_zona_id' => (string) ($r[3] ?? ''),
|
||||
'confini' => (string) ($r[4] ?? ''),
|
||||
'note' => (string) ($r[5] ?? ''),
|
||||
'attivo' => ((int) ($r[8] ?? 1)) === 1 ? 1 : 0,
|
||||
'prioritario' => ((int) ($r[11] ?? 0)) > 0 ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
$anni = [];
|
||||
foreach (($rows['annoServizio'] ?? []) as $r) {
|
||||
$label = (string) ($r[1] ?? '');
|
||||
[$startDate, $endDate] = $this->datesFromLegacyAnnoLabel($label);
|
||||
$anni[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'label' => str_replace(' - ', '-', $label),
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
];
|
||||
}
|
||||
|
||||
$campagne = [];
|
||||
foreach (($rows['campagna'] ?? []) as $r) {
|
||||
$campagne[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'descrizione' => (string) ($r[1] ?? ''),
|
||||
'start_date' => $this->toDateOnly((string) ($r[2] ?? '')),
|
||||
'end_date' => $this->toDateOnly((string) ($r[3] ?? '')),
|
||||
];
|
||||
}
|
||||
|
||||
$assegnazioni = [];
|
||||
foreach (($rows['assegnazione'] ?? []) as $r) {
|
||||
$assegnazioni[] = [
|
||||
'legacy_id' => (string) ($r[0] ?? ''),
|
||||
'legacy_territorio_id' => (string) ($r[1] ?? ''),
|
||||
'legacy_proclamatore_id' => (string) ($r[2] ?? ''),
|
||||
'legacy_anno_id' => (string) ($r[9] ?? ''),
|
||||
'legacy_campagna_id' => $r[8] === null ? '' : (string) $r[8],
|
||||
'assigned_at' => $this->toDateOnly((string) ($r[4] ?? '')),
|
||||
'returned_at' => $this->toDateOnly((string) ($r[5] ?? '')),
|
||||
'counted_in_campaign' => ((int) ($r[7] ?? 0)) === 1 ? 1 : 0,
|
||||
'note' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'settings' => $settings,
|
||||
'zones' => $zones,
|
||||
'tipologie' => $tipologie,
|
||||
'proclamatori' => $proclamatori,
|
||||
'territori' => $territori,
|
||||
'anni_teocratici' => $anni,
|
||||
'campagne' => $campagne,
|
||||
'assegnazioni' => $assegnazioni,
|
||||
];
|
||||
}
|
||||
|
||||
private function extractInsertRows(string $sql): array
|
||||
{
|
||||
$result = [];
|
||||
preg_match_all('/INSERT INTO `([^`]+)` VALUES\s*(.+?);/s', $sql, $matches, PREG_SET_ORDER);
|
||||
|
||||
foreach ($matches as $match) {
|
||||
$table = $match[1];
|
||||
$valuesPayload = $match[2];
|
||||
$tuples = $this->splitSqlTuples($valuesPayload);
|
||||
|
||||
foreach ($tuples as $tuple) {
|
||||
$result[$table][] = $this->parseSqlTuple($tuple);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function splitSqlTuples(string $payload): array
|
||||
{
|
||||
$tuples = [];
|
||||
$inString = false;
|
||||
$escape = false;
|
||||
$depth = 0;
|
||||
$current = '';
|
||||
|
||||
$len = strlen($payload);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$ch = $payload[$i];
|
||||
|
||||
if ($inString) {
|
||||
$current .= $ch;
|
||||
if ($escape) {
|
||||
$escape = false;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '\\\\') {
|
||||
$escape = true;
|
||||
continue;
|
||||
}
|
||||
if ($ch === "'") {
|
||||
$inString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === "'") {
|
||||
$inString = true;
|
||||
$current .= $ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === '(') {
|
||||
if ($depth === 0) {
|
||||
$current = '';
|
||||
}
|
||||
$depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === ')') {
|
||||
$depth--;
|
||||
if ($depth === 0) {
|
||||
$tuples[] = $current;
|
||||
$current = '';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($depth > 0) {
|
||||
$current .= $ch;
|
||||
}
|
||||
}
|
||||
|
||||
return $tuples;
|
||||
}
|
||||
|
||||
private function parseSqlTuple(string $tuple): array
|
||||
{
|
||||
$values = [];
|
||||
$inString = false;
|
||||
$escape = false;
|
||||
$current = '';
|
||||
|
||||
$len = strlen($tuple);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$ch = $tuple[$i];
|
||||
|
||||
if ($inString) {
|
||||
$current .= $ch;
|
||||
if ($escape) {
|
||||
$escape = false;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '\\\\') {
|
||||
$escape = true;
|
||||
continue;
|
||||
}
|
||||
if ($ch === "'") {
|
||||
$inString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === "'") {
|
||||
$inString = true;
|
||||
$current .= $ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === ',') {
|
||||
$values[] = $this->normalizeSqlValue($current);
|
||||
$current = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
$current .= $ch;
|
||||
}
|
||||
|
||||
if ($current !== '') {
|
||||
$values[] = $this->normalizeSqlValue($current);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function normalizeSqlValue(string $raw)
|
||||
{
|
||||
$raw = trim($raw);
|
||||
if (strcasecmp($raw, 'NULL') === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_starts_with($raw, "'") && str_ends_with($raw, "'")) {
|
||||
$v = substr($raw, 1, -1);
|
||||
$v = str_replace(["\\\\", "\\'", '\\"', '\\n', '\\r', '\\t'], ['\\', "'", '"', "\n", "\r", "\t"], $v);
|
||||
return html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
if (is_numeric($raw)) {
|
||||
return str_contains($raw, '.') ? (float) $raw : (int) $raw;
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
|
||||
private function datesFromLegacyAnnoLabel(string $label): array
|
||||
{
|
||||
if (preg_match('/(\d{4})\s*-\s*(\d{4})/', $label, $m)) {
|
||||
return [$m[1] . '-09-01', $m[2] . '-08-31'];
|
||||
}
|
||||
|
||||
$now = Carbon::now();
|
||||
return [$now->copy()->startOfYear()->toDateString(), $now->copy()->endOfYear()->toDateString()];
|
||||
}
|
||||
|
||||
private function toDateOnly(string $dateTime): string
|
||||
{
|
||||
if ($dateTime === '') {
|
||||
return '';
|
||||
}
|
||||
return substr($dateTime, 0, 10);
|
||||
}
|
||||
|
||||
private function normalizeDateForDb(string $raw, bool $nullable): ?string
|
||||
{
|
||||
$raw = trim($raw);
|
||||
if ($raw === '' || $raw === '0000-00-00' || $raw === '0000-00-00 00:00:00') {
|
||||
return $nullable ? null : null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Carbon::parse($raw)->toDateString();
|
||||
} catch (\Throwable $e) {
|
||||
return $nullable ? null : null;
|
||||
}
|
||||
}
|
||||
|
||||
private function pushImportIssue(string $entity, string $legacyId, string $reason): void
|
||||
{
|
||||
if (count($this->importIssues) >= 300) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->importIssues[] = [
|
||||
'entity' => $entity,
|
||||
'legacy_id' => $legacyId !== '' ? $legacyId : '-',
|
||||
'reason' => $reason,
|
||||
];
|
||||
}
|
||||
|
||||
private function datasetToXml(array $dataset, string $source): string
|
||||
{
|
||||
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><termanager2/>');
|
||||
$xml->addAttribute('version', '1.0');
|
||||
$xml->addAttribute('source', $source);
|
||||
$xml->addAttribute('generated_at', now()->toIso8601String());
|
||||
|
||||
$settings = $xml->addChild('settings');
|
||||
foreach ($dataset['settings'] as $key => $value) {
|
||||
$settings->addChild($key, htmlspecialchars((string) $value, ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
}
|
||||
|
||||
$zonesNode = $xml->addChild('zones');
|
||||
foreach ($dataset['zones'] as $zone) {
|
||||
$node = $zonesNode->addChild('zone');
|
||||
if (isset($zone['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $zone['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($zone['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('attivo', (string) ((int) ($zone['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
$tipologieNode = $xml->addChild('tipologie');
|
||||
foreach ($dataset['tipologie'] as $tipologia) {
|
||||
$node = $tipologieNode->addChild('tipologia');
|
||||
if (isset($tipologia['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $tipologia['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($tipologia['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('attivo', (string) ((int) ($tipologia['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
$proclamatoriNode = $xml->addChild('proclamatori');
|
||||
foreach ($dataset['proclamatori'] as $proclamatore) {
|
||||
$node = $proclamatoriNode->addChild('proclamatore');
|
||||
if (isset($proclamatore['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $proclamatore['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($proclamatore['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('cognome', htmlspecialchars((string) ($proclamatore['cognome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('attivo', (string) ((int) ($proclamatore['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
$territoriNode = $xml->addChild('territori');
|
||||
foreach ($dataset['territori'] as $territorio) {
|
||||
$node = $territoriNode->addChild('territorio');
|
||||
if (isset($territorio['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $territorio['legacy_id']);
|
||||
}
|
||||
$node->addChild('numero', htmlspecialchars((string) ($territorio['numero'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('legacy_zona_id', (string) ($territorio['legacy_zona_id'] ?? ($territorio['zona_id'] ?? '')));
|
||||
$node->addChild('legacy_tipologia_id', (string) ($territorio['legacy_tipologia_id'] ?? ($territorio['tipologia_id'] ?? '')));
|
||||
$node->addChild('confini', htmlspecialchars((string) ($territorio['confini'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('note', htmlspecialchars((string) ($territorio['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('attivo', (string) ((int) ($territorio['attivo'] ?? 1)));
|
||||
$node->addChild('prioritario', (string) ((int) ($territorio['prioritario'] ?? 0)));
|
||||
}
|
||||
|
||||
$anniNode = $xml->addChild('anni_teocratici');
|
||||
foreach ($dataset['anni_teocratici'] as $anno) {
|
||||
$node = $anniNode->addChild('anno');
|
||||
if (isset($anno['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $anno['legacy_id']);
|
||||
}
|
||||
$node->addChild('label', htmlspecialchars((string) ($anno['label'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('start_date', (string) ($anno['start_date'] ?? ''));
|
||||
$node->addChild('end_date', (string) ($anno['end_date'] ?? ''));
|
||||
}
|
||||
|
||||
$campagneNode = $xml->addChild('campagne');
|
||||
foreach ($dataset['campagne'] as $campagna) {
|
||||
$node = $campagneNode->addChild('campagna');
|
||||
if (isset($campagna['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $campagna['legacy_id']);
|
||||
}
|
||||
$node->addChild('descrizione', htmlspecialchars((string) ($campagna['descrizione'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('start_date', (string) ($campagna['start_date'] ?? ''));
|
||||
$node->addChild('end_date', (string) ($campagna['end_date'] ?? ''));
|
||||
}
|
||||
|
||||
$assegnazioniNode = $xml->addChild('assegnazioni');
|
||||
foreach ($dataset['assegnazioni'] as $assegnazione) {
|
||||
$node = $assegnazioniNode->addChild('assegnazione');
|
||||
if (isset($assegnazione['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $assegnazione['legacy_id']);
|
||||
}
|
||||
$node->addChild('legacy_territorio_id', (string) ($assegnazione['legacy_territorio_id'] ?? ($assegnazione['territorio_id'] ?? '')));
|
||||
$node->addChild('legacy_proclamatore_id', (string) ($assegnazione['legacy_proclamatore_id'] ?? ($assegnazione['proclamatore_id'] ?? '')));
|
||||
$node->addChild('legacy_anno_id', (string) ($assegnazione['legacy_anno_id'] ?? ($assegnazione['anno_teocratico_id'] ?? '')));
|
||||
$node->addChild('legacy_campagna_id', (string) ($assegnazione['legacy_campagna_id'] ?? ($assegnazione['campaign_id'] ?? '')));
|
||||
$node->addChild('assigned_at', (string) ($assegnazione['assigned_at'] ?? ''));
|
||||
$node->addChild('returned_at', (string) ($assegnazione['returned_at'] ?? ''));
|
||||
$node->addChild('counted_in_campaign', (string) ((int) ($assegnazione['counted_in_campaign'] ?? 0)));
|
||||
$node->addChild('note', htmlspecialchars((string) ($assegnazione['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
}
|
||||
|
||||
return $xml->asXML() ?: '';
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use Livewire\WithFileUploads;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Zona;
|
||||
use App\Models\Tipologia;
|
||||
use App\Services\TerritorioThumbnailService;
|
||||
|
||||
class TerritorioCreate extends Component
|
||||
{
|
||||
@@ -48,7 +49,9 @@ class TerritorioCreate extends Component
|
||||
];
|
||||
|
||||
if ($this->pdf) {
|
||||
$data['pdf_path'] = $this->pdf->store('territori-pdf', 'public');
|
||||
$pdfPath = $this->pdf->store('territori-pdf', 'public');
|
||||
$data['pdf_path'] = $pdfPath;
|
||||
$data['thumbnail_path'] = app(TerritorioThumbnailService::class)->generate($pdfPath);
|
||||
}
|
||||
|
||||
$territorio = Territorio::create($data);
|
||||
|
||||
@@ -7,6 +7,7 @@ use Livewire\WithFileUploads;
|
||||
use App\Models\Territorio;
|
||||
use App\Models\Zona;
|
||||
use App\Models\Tipologia;
|
||||
use App\Services\TerritorioThumbnailService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class TerritorioEdit extends Component
|
||||
@@ -60,11 +61,17 @@ class TerritorioEdit extends Component
|
||||
];
|
||||
|
||||
if ($this->pdf) {
|
||||
// Remove old PDF
|
||||
$thumbService = app(TerritorioThumbnailService::class);
|
||||
// Remove old PDF and thumbnail
|
||||
if ($this->territorio->pdf_path) {
|
||||
Storage::disk('public')->delete($this->territorio->pdf_path);
|
||||
}
|
||||
$data['pdf_path'] = $this->pdf->store('territori-pdf', 'public');
|
||||
if ($this->territorio->thumbnail_path) {
|
||||
$thumbService->delete($this->territorio->thumbnail_path);
|
||||
}
|
||||
$pdfPath = $this->pdf->store('territori-pdf', 'public');
|
||||
$data['pdf_path'] = $pdfPath;
|
||||
$data['thumbnail_path'] = $thumbService->generate($pdfPath);
|
||||
}
|
||||
|
||||
$this->territorio->update($data);
|
||||
@@ -77,7 +84,10 @@ class TerritorioEdit extends Component
|
||||
{
|
||||
if ($this->territorio->pdf_path) {
|
||||
Storage::disk('public')->delete($this->territorio->pdf_path);
|
||||
$this->territorio->update(['pdf_path' => null]);
|
||||
if ($this->territorio->thumbnail_path) {
|
||||
app(TerritorioThumbnailService::class)->delete($this->territorio->thumbnail_path);
|
||||
}
|
||||
$this->territorio->update(['pdf_path' => null, 'thumbnail_path' => null]);
|
||||
activity()->causedBy(auth()->user())
|
||||
->performedOn($this->territorio)
|
||||
->log('removed_pdf');
|
||||
|
||||
Reference in New Issue
Block a user