677 lines
26 KiB
PHP
677 lines
26 KiB
PHP
<?php
|
|
namespace App\Filament\Pages\Fornitore;
|
|
|
|
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
|
|
use App\Models\Fornitore;
|
|
use App\Models\FornitoreDipendente;
|
|
use App\Models\Ticket;
|
|
use App\Models\TicketAttachment;
|
|
use App\Models\TicketIntervento;
|
|
use App\Models\User;
|
|
use BackedEnum;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Livewire\WithFileUploads;
|
|
use UnitEnum;
|
|
|
|
class TicketInterventoScheda extends Page
|
|
{
|
|
use ResolvesOperatoreContext;
|
|
use WithFileUploads;
|
|
|
|
protected static ?string $title = 'Scheda intervento';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-clipboard-document-list';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
|
|
|
|
protected static ?string $slug = 'fornitore/tickets/{record}';
|
|
|
|
protected static bool $shouldRegisterNavigation = false;
|
|
|
|
protected string $view = 'filament.pages.fornitore.ticket-intervento-scheda';
|
|
|
|
public TicketIntervento $intervento;
|
|
|
|
public Fornitore $fornitore;
|
|
|
|
public ?FornitoreDipendente $dipendente = null;
|
|
|
|
/** @var array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string} */
|
|
public array $caller = [
|
|
'contatto' => '-',
|
|
'telefono' => '',
|
|
'email' => '',
|
|
'problema' => '',
|
|
'sorgente' => '',
|
|
'riferimento' => '',
|
|
];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $storicoRows = [];
|
|
|
|
/** @var array<int, array<string, mixed>> */
|
|
public array $dipendentiOptions = [];
|
|
|
|
public ?int $dipendenteId = null;
|
|
|
|
public string $rapportoFornitore = '';
|
|
|
|
public ?int $tempoMinuti = null;
|
|
|
|
/** @var array<int, string> */
|
|
public array $articoliUtilizzati = ['', '', ''];
|
|
|
|
public string $apparatoMarca = '';
|
|
|
|
public string $apparatoModello = '';
|
|
|
|
public string $apparatoSeriale = '';
|
|
|
|
/** @var array<int, mixed> */
|
|
public array $fotoLavoro = [];
|
|
|
|
/** @var array<int, mixed> */
|
|
public array $documentiLavoro = [];
|
|
|
|
/** @var array<int, string> */
|
|
public array $descrizioneFile = ['', '', '', '', '', ''];
|
|
|
|
public string $messaggioVeloce = '';
|
|
|
|
public bool $lavoroStandard = false;
|
|
|
|
public bool $richiestaProforma = false;
|
|
|
|
public string $proformaCodice = '';
|
|
|
|
public string $proformaNote = '';
|
|
|
|
public string $qrToken = '';
|
|
|
|
public ?array $attachmentPreview = null;
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = Auth::user();
|
|
|
|
return $user instanceof User
|
|
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'fornitore']);
|
|
}
|
|
|
|
public function mount(int | string $record): void
|
|
{
|
|
$recordId = (int) $record;
|
|
abort_unless($recordId > 0, 404);
|
|
|
|
$user = Auth::user();
|
|
$isAdmin = $user instanceof User && $user->hasAnyRole(['super-admin', 'admin', 'amministratore']);
|
|
|
|
if ($isAdmin) {
|
|
$intervento = $this->resolveInterventoRecord($recordId);
|
|
abort_unless($intervento instanceof TicketIntervento, 404);
|
|
|
|
[$fornitore, $dipendente] = $this->resolveOperatoreContext((int) $intervento->fornitore_id);
|
|
} else {
|
|
[$fornitore, $dipendente] = $this->resolveOperatoreContext();
|
|
abort_unless($fornitore instanceof Fornitore, 403, 'Profilo fornitore non collegato.');
|
|
|
|
$intervento = $this->resolveInterventoRecord($recordId, $fornitore, $dipendente);
|
|
abort_unless($intervento instanceof TicketIntervento, 404, 'Ticket/intervento non disponibile per questo fornitore.');
|
|
}
|
|
|
|
$this->intervento = $intervento;
|
|
|
|
$this->fornitore = $fornitore;
|
|
$this->dipendente = $dipendente;
|
|
|
|
$this->authorizeIntervento($this->intervento, $this->fornitore);
|
|
$this->authorizeDipendenteIntervento($this->intervento, $this->dipendente);
|
|
|
|
$this->reloadIntervento();
|
|
}
|
|
|
|
public function assignDipendente(): void
|
|
{
|
|
if ($this->dipendente instanceof FornitoreDipendente) {
|
|
Notification::make()->title('Assegnazione consentita solo al coordinatore fornitore')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$validated = $this->validate([
|
|
'dipendenteId' => ['nullable', 'integer'],
|
|
]);
|
|
|
|
$dipendente = null;
|
|
$selectedId = (int) ($validated['dipendenteId'] ?? 0);
|
|
|
|
if ($selectedId > 0) {
|
|
$dipendente = FornitoreDipendente::query()
|
|
->where('fornitore_id', (int) $this->fornitore->id)
|
|
->where('attivo', true)
|
|
->find($selectedId);
|
|
|
|
if (! $dipendente instanceof FornitoreDipendente) {
|
|
Notification::make()->title('Collaboratore non valido per questo fornitore')->danger()->send();
|
|
return;
|
|
}
|
|
}
|
|
|
|
$this->intervento->eseguito_da_dipendente_id = $dipendente?->id;
|
|
$this->intervento->save();
|
|
|
|
$stamp = now()->format('d/m/Y H:i');
|
|
$message = $dipendente
|
|
? '[' . $stamp . '] Intervento assegnato al collaboratore fornitore: ' . $dipendente->ruolo_operativo_label . '.'
|
|
: '[' . $stamp . '] Assegnazione collaboratore rimossa: intervento riportato al coordinamento del fornitore.';
|
|
|
|
$this->intervento->ticket->messages()->create([
|
|
'user_id' => Auth::id(),
|
|
'messaggio' => $message,
|
|
'canale' => 'interno',
|
|
'direzione' => 'inbound',
|
|
'inviato_il' => now(),
|
|
]);
|
|
|
|
$this->reloadIntervento();
|
|
|
|
Notification::make()->title('Assegnazione aggiornata')->success()->send();
|
|
}
|
|
|
|
public function startIntervento(): void
|
|
{
|
|
$this->intervento->update([
|
|
'stato' => 'in_corso',
|
|
'preso_in_carico_da_user_id' => Auth::id(),
|
|
'eseguito_da_dipendente_id' => $this->dipendente?->id ?? $this->intervento->eseguito_da_dipendente_id,
|
|
'iniziato_at' => $this->intervento->iniziato_at ?? now(),
|
|
'terminato_at' => null,
|
|
]);
|
|
|
|
$this->intervento->ticket->update(['stato' => 'In Lavorazione']);
|
|
|
|
$this->reloadIntervento();
|
|
|
|
Notification::make()->title('Intervento preso in carico')->success()->send();
|
|
}
|
|
|
|
public function stopIntervento(): void
|
|
{
|
|
if (! $this->intervento->iniziato_at) {
|
|
Notification::make()->title('Avvia prima il timer di intervento')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$endedAt = now();
|
|
$minutes = max(1, $this->intervento->iniziato_at->diffInMinutes($endedAt));
|
|
|
|
$this->intervento->update([
|
|
'terminato_at' => $endedAt,
|
|
'tempo_minuti' => $minutes,
|
|
'stato' => $this->intervento->stato === 'assegnato' ? 'in_corso' : $this->intervento->stato,
|
|
]);
|
|
|
|
$this->tempoMinuti = $minutes;
|
|
$this->reloadIntervento();
|
|
|
|
Notification::make()->title('Timer intervento fermato')->body('Tempo rilevato: ' . $minutes . ' minuti.')->success()->send();
|
|
}
|
|
|
|
public function updatedFotoLavoro(): void
|
|
{
|
|
$this->syncRapportoDescriptionSlots();
|
|
}
|
|
|
|
public function updatedDocumentiLavoro(): void
|
|
{
|
|
$this->syncRapportoDescriptionSlots();
|
|
}
|
|
|
|
public function saveRapporto(): void
|
|
{
|
|
$validated = $this->validate([
|
|
'rapportoFornitore' => ['required', 'string', 'max:5000'],
|
|
'tempoMinuti' => ['nullable', 'integer', 'min:1', 'max:1440'],
|
|
'articoliUtilizzati' => ['nullable', 'array'],
|
|
'articoliUtilizzati.*' => ['nullable', 'string', 'max:120'],
|
|
'fotoLavoro' => ['nullable', 'array', 'max:10'],
|
|
'fotoLavoro.*' => ['nullable', 'image', 'max:10240'],
|
|
'documentiLavoro' => ['nullable', 'array', 'max:10'],
|
|
'documentiLavoro.*' => ['nullable', 'file', 'max:10240', 'mimes:pdf,doc,docx,xls,xlsx,txt,jpg,jpeg,png,webp'],
|
|
'descrizioneFile' => ['nullable', 'array'],
|
|
'descrizioneFile.*' => ['nullable', 'string', 'max:255'],
|
|
'apparatoMarca' => ['nullable', 'string', 'max:120'],
|
|
'apparatoModello' => ['nullable', 'string', 'max:120'],
|
|
'apparatoSeriale' => ['nullable', 'string', 'max:120'],
|
|
'messaggioVeloce' => ['nullable', 'string', 'max:1500'],
|
|
'lavoroStandard' => ['nullable', 'boolean'],
|
|
'richiestaProforma' => ['nullable', 'boolean'],
|
|
'proformaCodice' => ['nullable', 'string', 'max:80'],
|
|
'proformaNote' => ['nullable', 'string', 'max:2000'],
|
|
'qrToken' => ['nullable', 'string', 'max:40'],
|
|
]);
|
|
|
|
$rapporto = trim((string) $validated['rapportoFornitore']);
|
|
$apparato = $this->buildApparatoSummary(
|
|
(string) ($validated['apparatoMarca'] ?? ''),
|
|
(string) ($validated['apparatoModello'] ?? ''),
|
|
(string) ($validated['apparatoSeriale'] ?? '')
|
|
);
|
|
|
|
if ($apparato !== '') {
|
|
$rapporto .= "\n\n" . $apparato;
|
|
}
|
|
|
|
$computedTempoMinuti = $validated['tempoMinuti'] ?? null;
|
|
$computedTerminatedAt = $this->intervento->terminato_at ?? now();
|
|
|
|
if (! $computedTempoMinuti && $this->intervento->iniziato_at) {
|
|
$computedTempoMinuti = max(1, $this->intervento->iniziato_at->diffInMinutes($computedTerminatedAt));
|
|
}
|
|
|
|
$payload = [
|
|
'rapporto_fornitore' => $rapporto,
|
|
'tempo_minuti' => $computedTempoMinuti,
|
|
'terminato_at' => $computedTerminatedAt,
|
|
'stato' => ! empty($validated['lavoroStandard']) ? 'fatturabile' : 'in_verifica',
|
|
'eseguito_da_dipendente_id' => $this->dipendente?->id ?? $this->intervento->eseguito_da_dipendente_id,
|
|
];
|
|
|
|
$articoli = collect($validated['articoliUtilizzati'] ?? [])
|
|
->map(fn($item) => trim((string) $item))
|
|
->filter()
|
|
->values()
|
|
->all();
|
|
|
|
if ($articoli !== []) {
|
|
$payload['articoli_utilizzati'] = $articoli;
|
|
}
|
|
|
|
$allFiles = array_merge($this->fotoLavoro, $this->documentiLavoro);
|
|
$firstPhotoPath = null;
|
|
|
|
if ($allFiles !== [] && Schema::hasTable('ticket_attachments')) {
|
|
foreach ($allFiles as $index => $file) {
|
|
if (! is_object($file) || ! method_exists($file, 'store')) {
|
|
continue;
|
|
}
|
|
|
|
$originalName = (string) (method_exists($file, 'getClientOriginalName') ? $file->getClientOriginalName() : ('file-' . $index));
|
|
$mime = (string) (method_exists($file, 'getClientMimeType') ? $file->getClientMimeType() : 'application/octet-stream');
|
|
$isImage = str_starts_with($mime, 'image/');
|
|
|
|
$stored = app(\App\Services\Support\TicketAttachmentUploadService::class)->store(
|
|
$file,
|
|
'ticket-interventi/allegati/' . $this->intervento->id,
|
|
[
|
|
'stored_basename' => pathinfo($this->buildStoredUploadDisplayName($index, $isImage, $originalName), PATHINFO_FILENAME),
|
|
'display_name' => $originalName,
|
|
]
|
|
);
|
|
|
|
if ($isImage && $firstPhotoPath === null) {
|
|
$firstPhotoPath = (string) $stored['path'];
|
|
}
|
|
|
|
TicketAttachment::query()->create([
|
|
'ticket_id' => (int) $this->intervento->ticket_id,
|
|
'ticket_update_id' => null,
|
|
'user_id' => (int) Auth::id(),
|
|
'file_path' => (string) $stored['path'],
|
|
'original_file_name' => (string) $stored['original_name'],
|
|
'mime_type' => (string) $stored['mime'],
|
|
'size' => (int) $stored['size'],
|
|
'description' => trim((string) ($this->descrizioneFile[$index] ?? '')) ?: ($isImage ? 'Foto lavoro fornitore' : 'Allegato rapporto fornitore'),
|
|
]);
|
|
}
|
|
}
|
|
|
|
if ($firstPhotoPath !== null) {
|
|
$payload['foto_path'] = $firstPhotoPath;
|
|
}
|
|
|
|
$tokenInserito = trim((string) ($validated['qrToken'] ?? ''));
|
|
if ($tokenInserito !== '' && strcasecmp($tokenInserito, (string) $this->intervento->qr_token) === 0) {
|
|
$payload['qr_scansionato_at'] = now();
|
|
}
|
|
|
|
$this->intervento->update($payload);
|
|
|
|
$stamp = now()->format('d/m/Y H:i');
|
|
$this->intervento->ticket->messages()->create([
|
|
'user_id' => Auth::id(),
|
|
'messaggio' => '[' . $stamp . '] Rapporto intervento caricato dal fornitore.',
|
|
'canale' => 'interno',
|
|
'direzione' => 'inbound',
|
|
'inviato_il' => now(),
|
|
]);
|
|
|
|
$proformaCodice = trim((string) ($validated['proformaCodice'] ?? ''));
|
|
$proformaNote = trim((string) ($validated['proformaNote'] ?? ''));
|
|
if (! empty($validated['richiestaProforma']) || $proformaCodice !== '' || $proformaNote !== '') {
|
|
$this->intervento->ticket->messages()->create([
|
|
'user_id' => Auth::id(),
|
|
'messaggio' => '[' . $stamp . '] Proforma fornitore ' . ($proformaCodice !== '' ? ('codice ' . $proformaCodice) : '(senza codice)') . ($proformaNote !== '' ? (' - ' . $proformaNote) : ''),
|
|
'canale' => 'interno',
|
|
'direzione' => 'inbound',
|
|
'inviato_il' => now(),
|
|
]);
|
|
}
|
|
|
|
$quickMessage = trim((string) ($validated['messaggioVeloce'] ?? ''));
|
|
if ($quickMessage !== '') {
|
|
$this->intervento->ticket->messages()->create([
|
|
'user_id' => Auth::id(),
|
|
'messaggio' => '[' . $stamp . '] Messaggio veloce fornitore: ' . $quickMessage,
|
|
'canale' => 'interno',
|
|
'direzione' => 'inbound',
|
|
'inviato_il' => now(),
|
|
]);
|
|
}
|
|
|
|
$this->fotoLavoro = [];
|
|
$this->documentiLavoro = [];
|
|
$this->descrizioneFile = [];
|
|
$this->messaggioVeloce = '';
|
|
$this->proformaCodice = '';
|
|
$this->proformaNote = '';
|
|
$this->dispatch('fornitore-local-previews-clear');
|
|
|
|
$this->reloadIntervento();
|
|
|
|
Notification::make()->title('Rapporto inviato in verifica amministratore')->success()->send();
|
|
}
|
|
|
|
public function inviaSollecito(): void
|
|
{
|
|
$stamp = now()->format('d/m/Y H:i');
|
|
$this->intervento->ticket->messages()->create([
|
|
'user_id' => Auth::id(),
|
|
'messaggio' => '[' . $stamp . '] Sollecito fornitore su ticket/intervento in corso.',
|
|
'canale' => 'interno',
|
|
'direzione' => 'inbound',
|
|
'inviato_il' => now(),
|
|
]);
|
|
|
|
$this->reloadIntervento();
|
|
|
|
Notification::make()->title('Sollecito inviato allo studio amministrativo')->success()->send();
|
|
}
|
|
|
|
public function getTicketsUrl(): string
|
|
{
|
|
return TicketOperativi::getUrl(['fornitore' => (int) $this->fornitore->id], panel: 'admin-filament');
|
|
}
|
|
|
|
public function getAttachmentPublicUrl(TicketAttachment $attachment): string
|
|
{
|
|
return route('filament.tickets.attachments.view', ['attachment' => (int) $attachment->id]);
|
|
}
|
|
|
|
public function openAttachmentPreview(int $attachmentId): void
|
|
{
|
|
$attachment = $this->intervento->ticket->attachments->firstWhere('id', $attachmentId);
|
|
if (! $attachment instanceof TicketAttachment) {
|
|
return;
|
|
}
|
|
|
|
$this->attachmentPreview = [
|
|
'id' => (int) $attachment->id,
|
|
'name' => (string) ($attachment->original_file_name ?? 'allegato'),
|
|
'description' => (string) ($attachment->description ?? ''),
|
|
'url' => $this->getAttachmentPublicUrl($attachment),
|
|
'is_image' => $attachment->isImage(),
|
|
'is_pdf' => $attachment->isPdf(),
|
|
'mime' => $attachment->resolvedMimeType(),
|
|
];
|
|
}
|
|
|
|
public function closeAttachmentPreview(): void
|
|
{
|
|
$this->attachmentPreview = null;
|
|
}
|
|
|
|
public function getCollaboratoriUrl(): string
|
|
{
|
|
return Collaboratori::getUrl(['fornitore' => (int) $this->fornitore->id], panel: 'admin-filament');
|
|
}
|
|
|
|
public function canAssignDipendente(): bool
|
|
{
|
|
return ! ($this->dipendente instanceof FornitoreDipendente) && count($this->dipendentiOptions) > 0;
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array<string, mixed>>
|
|
*/
|
|
public function getRapportinoUploadsProperty(): array
|
|
{
|
|
$rows = [];
|
|
|
|
foreach (array_merge($this->fotoLavoro, $this->documentiLavoro) as $index => $file) {
|
|
if (! is_object($file)) {
|
|
continue;
|
|
}
|
|
|
|
$name = (string) (method_exists($file, 'getClientOriginalName') ? $file->getClientOriginalName() : ('file-' . $index));
|
|
$size = (int) (method_exists($file, 'getSize') ? $file->getSize() : 0);
|
|
$mime = (string) (method_exists($file, 'getClientMimeType') ? $file->getClientMimeType() : 'application/octet-stream');
|
|
$isImage = str_starts_with($mime, 'image/');
|
|
$isPdf = $mime === 'application/pdf' || str_ends_with(strtolower($name), '.pdf');
|
|
$previewUrl = null;
|
|
|
|
if ($isImage && method_exists($file, 'temporaryUrl')) {
|
|
try {
|
|
$previewUrl = (string) $file->temporaryUrl();
|
|
} catch (\Throwable) {
|
|
$previewUrl = null;
|
|
}
|
|
}
|
|
|
|
$rows[] = [
|
|
'index' => $index,
|
|
'name' => $name,
|
|
'size' => $size,
|
|
'mime' => $mime,
|
|
'is_image' => $isImage,
|
|
'is_pdf' => $isPdf,
|
|
'preview_url' => $previewUrl,
|
|
'description' => (string) ($this->descrizioneFile[$index] ?? ''),
|
|
];
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
protected function resolveInterventoRecord(int $recordId, ?Fornitore $fornitore = null, ?FornitoreDipendente $dipendente = null): ?TicketIntervento
|
|
{
|
|
$query = TicketIntervento::query();
|
|
|
|
if ($fornitore instanceof Fornitore) {
|
|
$query->where('fornitore_id', (int) $fornitore->id);
|
|
}
|
|
|
|
if ($dipendente instanceof FornitoreDipendente) {
|
|
$query->where(function (Builder $builder) use ($dipendente): void {
|
|
$builder->whereNull('eseguito_da_dipendente_id')
|
|
->orWhere('eseguito_da_dipendente_id', (int) $dipendente->id);
|
|
});
|
|
}
|
|
|
|
return $query
|
|
->where(function (Builder $builder) use ($recordId): void {
|
|
$builder->whereKey($recordId)
|
|
->orWhere('ticket_id', $recordId);
|
|
})
|
|
->orderByRaw('CASE WHEN id = ? THEN 0 ELSE 1 END', [$recordId])
|
|
->latest('id')
|
|
->first();
|
|
}
|
|
|
|
protected function syncRapportoDescriptionSlots(): void
|
|
{
|
|
$next = [];
|
|
|
|
foreach (array_merge($this->fotoLavoro, $this->documentiLavoro) as $index => $_file) {
|
|
$next[$index] = (string) ($this->descrizioneFile[$index] ?? '');
|
|
}
|
|
|
|
$this->descrizioneFile = $next;
|
|
}
|
|
|
|
protected function buildStoredUploadDisplayName(int $index, bool $isImage, string $originalName): string
|
|
{
|
|
$extension = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION));
|
|
$baseName = 'ticket-' . str_pad((string) $this->intervento->ticket_id, 6, '0', STR_PAD_LEFT)
|
|
. '-int-' . str_pad((string) $this->intervento->id, 6, '0', STR_PAD_LEFT)
|
|
. '-' . ($isImage ? 'foto-' : 'doc-') . str_pad((string) ($index + 1), 2, '0', STR_PAD_LEFT);
|
|
|
|
return $baseName . ($extension !== '' ? '.' . $extension : '');
|
|
}
|
|
|
|
protected function reloadIntervento(): void
|
|
{
|
|
$this->intervento->load([
|
|
'ticket.stabile',
|
|
'ticket.unitaImmobiliare',
|
|
'ticket.soggettoRichiedente',
|
|
'ticket.messages.user',
|
|
'ticket.attachments.user',
|
|
'ticket.apertoDaUser',
|
|
'ticket.assegnatoAUser',
|
|
'ticket.assegnatoAFornitore',
|
|
'eseguitoDaDipendente',
|
|
]);
|
|
|
|
$this->intervento->refresh();
|
|
$this->intervento->load([
|
|
'ticket.stabile',
|
|
'ticket.unitaImmobiliare',
|
|
'ticket.soggettoRichiedente',
|
|
'ticket.messages.user',
|
|
'ticket.attachments.user',
|
|
'ticket.apertoDaUser',
|
|
'ticket.assegnatoAUser',
|
|
'ticket.assegnatoAFornitore',
|
|
'eseguitoDaDipendente',
|
|
]);
|
|
|
|
$this->caller = $this->resolveCallerData($this->intervento->ticket);
|
|
$this->storicoRows = TicketIntervento::query()
|
|
->with(['ticket'])
|
|
->where('fornitore_id', (int) $this->fornitore->id)
|
|
->where('id', '!=', (int) $this->intervento->id)
|
|
->when(
|
|
(int) ($this->intervento->ticket?->soggetto_richiedente_id ?? 0) > 0,
|
|
fn($query) => $query->whereHas('ticket', fn($ticketQuery) => $ticketQuery->where('soggetto_richiedente_id', (int) $this->intervento->ticket->soggetto_richiedente_id)),
|
|
fn($query) => $query->whereHas('ticket', fn($ticketQuery) => $ticketQuery->where('stabile_id', (int) ($this->intervento->ticket?->stabile_id ?? 0)))
|
|
)
|
|
->latest('id')
|
|
->limit(12)
|
|
->get()
|
|
->map(fn(TicketIntervento $old) => [
|
|
'ticket_id' => (int) $old->ticket_id,
|
|
'titolo' => (string) ($old->ticket->titolo ?? '-'),
|
|
'stato' => (string) $old->stato,
|
|
'updated_at' => optional($old->updated_at)->format('d/m/Y H:i') ?: '-',
|
|
'note' => (string) ($old->note_amministratore ?? ''),
|
|
])
|
|
->all();
|
|
|
|
$this->dipendentiOptions = $this->fornitore->dipendenti()
|
|
->where('attivo', true)
|
|
->with('fornitoreEsterno')
|
|
->orderBy('cognome')
|
|
->orderBy('nome')
|
|
->get()
|
|
->map(fn(FornitoreDipendente $dipendente) => [
|
|
'id' => (int) $dipendente->id,
|
|
'label' => $dipendente->ruolo_operativo_label . ($dipendente->email ? ' · ' . $dipendente->email : ''),
|
|
])
|
|
->all();
|
|
|
|
$this->dipendenteId = (int) ($this->intervento->eseguito_da_dipendente_id ?? 0) ?: null;
|
|
$this->rapportoFornitore = (string) ($this->intervento->rapporto_fornitore ?? '');
|
|
$this->tempoMinuti = $this->intervento->tempo_minuti ? (int) $this->intervento->tempo_minuti : null;
|
|
$this->articoliUtilizzati = array_pad((array) ($this->intervento->articoli_utilizzati ?? []), 3, '');
|
|
$this->qrToken = '';
|
|
}
|
|
|
|
/**
|
|
* @return array{contatto:string,telefono:string,email:string,problema:string,sorgente:string,riferimento:string}
|
|
*/
|
|
protected function resolveCallerData(?Ticket $ticket): array
|
|
{
|
|
$parsed = $this->extractCallerData((string) ($ticket?->descrizione ?? ''));
|
|
|
|
$caller = [
|
|
'contatto' => $parsed['contatto'],
|
|
'telefono' => $parsed['telefono'],
|
|
'email' => '',
|
|
'problema' => $parsed['problema'] !== '' ? $parsed['problema'] : (string) ($ticket?->titolo ?? ''),
|
|
'sorgente' => 'Descrizione ticket',
|
|
'riferimento' => $this->buildCallerReference($ticket),
|
|
];
|
|
|
|
$soggetto = $ticket?->soggettoRichiedente;
|
|
if ($soggetto) {
|
|
$label = trim((string) ($soggetto->ragione_sociale ?: trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? ''))));
|
|
if ($label !== '') {
|
|
$caller['contatto'] = $label;
|
|
}
|
|
|
|
$caller['telefono'] = trim((string) ($soggetto->telefono ?? '')) ?: $caller['telefono'];
|
|
$caller['email'] = trim((string) ($soggetto->email ?? ''));
|
|
$caller['sorgente'] = 'Richiedente collegato al ticket';
|
|
|
|
return $caller;
|
|
}
|
|
|
|
$openedBy = $ticket?->apertoDaUser;
|
|
if ($openedBy instanceof User) {
|
|
$caller['contatto'] = trim((string) ($openedBy->name ?? '')) ?: $caller['contatto'];
|
|
$caller['email'] = trim((string) ($openedBy->email ?? ''));
|
|
$caller['sorgente'] = 'Utente che ha aperto il ticket';
|
|
}
|
|
|
|
return $caller;
|
|
}
|
|
|
|
protected function buildCallerReference(?Ticket $ticket): string
|
|
{
|
|
if (! $ticket instanceof Ticket) {
|
|
return '';
|
|
}
|
|
|
|
$parts = [];
|
|
|
|
$stabile = $ticket->stabile;
|
|
if ($stabile) {
|
|
$denominazione = trim((string) ($stabile->denominazione ?? ''));
|
|
$indirizzo = trim(implode(' ', array_filter([
|
|
$stabile->indirizzo ?? null,
|
|
$stabile->cap ?? null,
|
|
$stabile->citta ?? null,
|
|
])));
|
|
|
|
if ($denominazione !== '') {
|
|
$parts[] = $denominazione;
|
|
}
|
|
|
|
if ($indirizzo !== '') {
|
|
$parts[] = $indirizzo;
|
|
}
|
|
}
|
|
|
|
$luogoIntervento = trim((string) ($ticket->luogo_intervento ?? ''));
|
|
if ($luogoIntervento !== '') {
|
|
$parts[] = 'Luogo: ' . $luogoIntervento;
|
|
}
|
|
|
|
return implode(' · ', array_unique($parts));
|
|
}
|
|
}
|