authorizeAbility($request, 'tickets.view'); $owner = $this->ownerRef($request); $organization = $this->organization($request); $tickets = Ticket::owned($owner) ->where('organization_id', $organization->id) ->when($request->query('queue'), fn ($q, $uuid) => $q->whereHas('serviceQueue', fn ($sq) => $sq->where('uuid', $uuid))) ->when($request->query('status'), fn ($q, $status) => $q->where('status', $status)) ->with(['serviceQueue', 'counter']) ->latest('issued_at') ->paginate(50); return response()->json([ 'data' => $tickets->getCollection()->map(fn (Ticket $t) => TicketPresenter::toArray($t)), 'meta' => [ 'current_page' => $tickets->currentPage(), 'last_page' => $tickets->lastPage(), 'total' => $tickets->total(), ], ]); } public function store(Request $request): JsonResponse { $this->authorizeAbility($request, 'tickets.issue'); $owner = $this->ownerRef($request); $validated = $request->validate([ 'service_queue_id' => ['required', 'uuid'], 'customer_name' => ['nullable', 'string', 'max:255'], 'customer_phone' => ['nullable', 'string', 'max:50'], 'customer_email' => ['nullable', 'email', 'max:255'], 'priority' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('qms.ticket_priorities')))], 'source' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('qms.ticket_sources')))], ]); $queue = ServiceQueue::where('uuid', $validated['service_queue_id'])->firstOrFail(); $this->authorizeOwner($request, $queue); $ticket = $this->engine->issueTicket($queue, $owner, [ ...$validated, 'source' => $validated['source'] ?? 'api', 'actor_ref' => $owner, ]); return response()->json(['data' => TicketPresenter::toArray($ticket)], 201); } public function show(Request $request, Ticket $ticket): JsonResponse { $this->authorizeAbility($request, 'tickets.view'); $this->authorizeOwner($request, $ticket); return response()->json(['data' => TicketPresenter::toArray($ticket->load(['serviceQueue', 'counter']))]); } public function callNext(Request $request, ServiceQueue $serviceQueue): JsonResponse { $this->authorizeAbility($request, 'console.use'); $this->authorizeOwner($request, $serviceQueue); $validated = $request->validate([ 'counter_id' => ['required', 'uuid'], ]); $counter = \App\Models\Counter::where('uuid', $validated['counter_id'])->firstOrFail(); $this->authorizeOwner($request, $counter); $ticket = $this->engine->callNext($serviceQueue, $counter, $this->ownerRef($request)); return response()->json(['data' => $ticket ? TicketPresenter::toArray($ticket) : null]); } public function action(Request $request, Ticket $ticket): JsonResponse { $this->authorizeAbility($request, 'tickets.manage'); $this->authorizeOwner($request, $ticket); $actor = $this->ownerRef($request); $validated = $request->validate([ 'action' => ['required', 'string', 'in:recall,start,hold,skip,complete,no_show,cancel,delay,transfer'], 'minutes' => ['nullable', 'integer', 'min:5', 'max:120'], 'to_queue_id' => ['required_if:action,transfer', 'nullable', 'uuid'], 'reason' => ['nullable', 'string', 'max:500'], ]); if ($validated['action'] === 'transfer') { abort_unless( $this->plans->hasFeature($this->organization($request), 'transfer'), 403, 'Handing off tickets between queues requires Queue Pro.', ); $toQueue = ServiceQueue::where('uuid', $validated['to_queue_id'])->firstOrFail(); $this->authorizeOwner($request, $toQueue); $result = $this->engine->transfer($ticket, $toQueue, null, $validated['reason'] ?? null, $actor); return response()->json(['data' => TicketPresenter::toArray($result->load(['serviceQueue', 'counter']))]); } $result = match ($validated['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), 'cancel' => $this->engine->cancel($ticket, $actor), 'delay' => $this->engine->delayArrival($ticket, (int) ($validated['minutes'] ?? 15), $actor), default => $ticket, }; return response()->json(['data' => TicketPresenter::toArray($result->load(['serviceQueue', 'counter']))]); } }