resolveOperatoreContext(); $stato = (string) $request->query('stato', 'aperti'); $query = TicketIntervento::query() ->with(['ticket.stabile']) ->where('fornitore_id', $fornitore->id) ->orderByDesc('created_at'); if ($stato === 'chiusi') { $query->whereIn('stato', ['chiuso', 'fatturato']); } elseif ($stato === 'fatturabili') { $query->whereIn('stato', ['fatturabile', 'fatturato']); } else { $query->whereNotIn('stato', ['chiuso']); } $interventi = $query->paginate(15)->withQueryString(); $fatturabili = TicketIntervento::query() ->where('fornitore_id', $fornitore->id) ->whereIn('stato', ['fatturabile', 'fatturato']) ->orderByDesc('updated_at') ->limit(100) ->get(); return view('fornitore.tickets.index', [ 'fornitore' => $fornitore, 'interventi' => $interventi, 'stato' => $stato, 'fatturabili' => $fatturabili, ]); } public function show(TicketIntervento $intervento) { [$fornitore] = $this->resolveOperatoreContext(); $this->authorizeIntervento($intervento, $fornitore); $intervento->load(['ticket.stabile', 'ticket.messages.user', 'eseguitoDaDipendente']); return view('fornitore.tickets.show', [ 'fornitore' => $fornitore, 'intervento' => $intervento, ]); } public function start(TicketIntervento $intervento) { [$fornitore, $dipendente] = $this->resolveOperatoreContext(); $this->authorizeIntervento($intervento, $fornitore); $intervento->update([ 'stato' => 'in_corso', 'preso_in_carico_da_user_id' => Auth::id(), 'eseguito_da_dipendente_id' => $dipendente?->id, 'iniziato_at' => $intervento->iniziato_at ?? now(), ]); $intervento->ticket->update(['stato' => 'In Lavorazione']); return redirect()->route('fornitore.tickets.show', $intervento) ->with('success', 'Intervento preso in carico.'); } public function complete(Request $request, TicketIntervento $intervento) { [$fornitore, $dipendente] = $this->resolveOperatoreContext(); $this->authorizeIntervento($intervento, $fornitore); $validated = $request->validate([ 'rapporto_fornitore' => 'required|string|max:5000', 'tempo_minuti' => 'nullable|integer|min:1|max:1440', 'articoli_utilizzati' => 'nullable|array', 'articoli_utilizzati.*' => 'nullable|string|max:120', 'foto_lavoro' => 'nullable|image|max:5120', 'qr_token' => 'nullable|string|max:40', ]); $payload = [ 'rapporto_fornitore' => $validated['rapporto_fornitore'], 'tempo_minuti' => $validated['tempo_minuti'] ?? null, 'terminato_at' => now(), 'stato' => 'in_verifica', 'eseguito_da_dipendente_id' => $dipendente?->id ?? $intervento->eseguito_da_dipendente_id, ]; $articoli = collect($validated['articoli_utilizzati'] ?? []) ->map(fn($item) => trim((string) $item)) ->filter() ->values() ->all(); if ($articoli !== []) { $payload['articoli_utilizzati'] = $articoli; } if ($request->hasFile('foto_lavoro')) { $payload['foto_path'] = $request->file('foto_lavoro') ->store('ticket-interventi/foto/' . $intervento->id, 'public'); } $tokenInserito = trim((string) ($validated['qr_token'] ?? '')); if ($tokenInserito !== '' && strcasecmp($tokenInserito, (string) $intervento->qr_token) === 0) { $payload['qr_scansionato_at'] = now(); } $intervento->update($payload); $intervento->ticket->messages()->create([ 'user_id' => Auth::id(), 'messaggio' => 'Rapporto intervento caricato dal fornitore.', 'canale' => 'interno', 'direzione' => 'inbound', 'inviato_il' => now(), ]); return redirect()->route('fornitore.tickets.show', $intervento) ->with('success', 'Intervento inviato in verifica amministratore.'); } /** * @return array{0:Fornitore,1:?FornitoreDipendente} */ private function resolveOperatoreContext(): array { $user = Auth::user(); if ($user && $user->hasAnyRole(['super-admin', 'admin', 'amministratore'])) { $fornitoreId = (int) request()->query('fornitore', 0); if ($fornitoreId <= 0) { $interventoRoute = request()->route('intervento'); if ($interventoRoute instanceof TicketIntervento) { $fornitoreId = (int) ($interventoRoute->fornitore_id ?? 0); } } if ($fornitoreId > 0) { $fornitoreAdmin = Fornitore::query()->find($fornitoreId); abort_unless($fornitoreAdmin, 404); return [$fornitoreAdmin, null]; } } $dipendente = null; $fornitore = Fornitore::query() ->where('email', (string) $user->email) ->first(); if (! $fornitore && $user) { $dipendente = FornitoreDipendente::query() ->where('attivo', true) ->where(function ($q) use ($user): void { $q->where('user_id', (int) $user->id) ->orWhereRaw('LOWER(email) = ?', [mb_strtolower((string) $user->email)]); }) ->orderByDesc('id') ->first(); if ($dipendente) { $fornitore = Fornitore::query()->find((int) $dipendente->fornitore_id); $dipendente->ultimo_accesso_at = now(); $dipendente->save(); } } abort_unless($fornitore, 403, 'Profilo fornitore non collegato.'); return [$fornitore, $dipendente]; } private function authorizeIntervento(TicketIntervento $intervento, Fornitore $fornitore): void { abort_unless((int) $intervento->fornitore_id === (int) $fornitore->id, 403); } }