authorizeAbility($request, 'console.use'); $this->authorizeOwner($request, $counter); $owner = $this->ownerRef($request); $organization = $this->organization($request); $counter->load(['serviceQueues', 'branch']); $queues = $counter->serviceQueues; $currentTicket = Ticket::owned($owner) ->where('counter_id', $counter->id) ->whereIn('status', ['called', 'serving', 'on_hold']) ->with('serviceQueue') ->latest('called_at') ->first(); $waitingByQueue = collect($queues)->mapWithKeys(function (ServiceQueue $queue) { return [$queue->id => $this->engine->waitingTickets($queue, 10)]; }); $canTransfer = $this->plans->hasFeature($organization, 'transfer'); $allQueues = $canTransfer ? ServiceQueue::owned($owner) ->where('organization_id', $counter->organization_id) ->where('branch_id', $counter->branch_id) ->where('is_active', true) ->orderBy('name') ->get() : collect(); return view('qms.console.show', compact( 'counter', 'queues', 'currentTicket', 'waitingByQueue', 'allQueues', 'canTransfer', )); } public function action(Request $request, Counter $counter): RedirectResponse { $this->authorizeAbility($request, 'console.use'); $this->authorizeOwner($request, $counter); $actor = $this->ownerRef($request); $validated = $request->validate([ 'action' => ['required', 'string'], 'queue_id' => ['nullable', 'integer'], 'ticket_id' => ['nullable', 'integer'], 'to_queue_id' => ['required_if:action,transfer', 'nullable', 'integer'], 'reason' => ['nullable', 'string', 'max:500'], ]); if ($validated['action'] === 'transfer' && ! $this->plans->hasFeature($this->organization($request), 'transfer')) { return redirect() ->route('qms.pro.index') ->with('error', 'Handing off tickets between queues requires Queue Pro.'); } try { $message = match ($validated['action']) { 'call_next' => $this->handleCallNext($counter, (int) $validated['queue_id'], $actor), 'recall' => $this->handleTicketAction($validated['ticket_id'], 'recall', $actor), 'start' => $this->handleTicketAction($validated['ticket_id'], 'start', $actor), 'hold' => $this->handleTicketAction($validated['ticket_id'], 'hold', $actor), 'skip' => $this->handleTicketAction($validated['ticket_id'], 'skip', $actor), 'complete' => $this->handleTicketAction($validated['ticket_id'], 'complete', $actor), 'no_show' => $this->handleTicketAction($validated['ticket_id'], 'no_show', $actor), 'transfer' => $this->handleTransfer($validated['ticket_id'], (int) $validated['to_queue_id'], $counter, $validated['reason'] ?? null, $actor), 'available' => tap(null, fn () => $counter->update(['status' => 'available'])), 'offline' => tap(null, fn () => $counter->update(['status' => 'offline'])), default => null, }; } catch (\RuntimeException $e) { return back()->with('error', $e->getMessage()); } return $message ? back()->with('success', $message) : back(); } protected function handleCallNext(Counter $counter, int $queueId, string $actor): ?string { $queue = ServiceQueue::findOrFail($queueId); $ticket = $this->engine->callNext($queue, $counter, $actor); return $ticket ? "Called {$ticket->ticket_number}." : 'No tickets waiting in that queue.'; } protected function handleTicketAction(?int $ticketId, string $action, string $actor): ?string { if (! $ticketId) { return null; } $ticket = Ticket::findOrFail($ticketId); match ($action) { 'recall' => $this->engine->recall($ticket, $actor), 'start' => $this->engine->startServing($ticket, $actor), 'hold' => $this->engine->hold($ticket, $actor), 'skip' => $this->engine->skip($ticket, $actor), 'complete' => $this->engine->completeTicket($ticket, $actor), 'no_show' => $this->engine->markNoShow($ticket, $actor), default => null, }; return null; } protected function handleTransfer(?int $ticketId, int $toQueueId, Counter $counter, ?string $reason, string $actor): ?string { if (! $ticketId || ! $toQueueId) { return null; } $ticket = Ticket::findOrFail($ticketId); $toQueue = ServiceQueue::findOrFail($toQueueId); if ( $toQueue->organization_id !== $counter->organization_id || $toQueue->branch_id !== $counter->branch_id || $toQueue->owner_ref !== $counter->owner_ref ) { throw new \RuntimeException('You can only hand off to queues at this branch.'); } $this->engine->transfer($ticket, $toQueue, $counter, $reason, $actor); return "Handed off {$ticket->ticket_number} to {$toQueue->name}. Ticket number unchanged."; } }