From 9c92a04a74805222c96d6e03ffb5960cdf4d124f Mon Sep 17 00:00:00 2001 From: isaacclad Date: Fri, 17 Jul 2026 19:41:22 +0000 Subject: [PATCH] Fix ticket issue 500 on assigned-only queues. Reception create form omitted service point; catch that as validation and require a desk/room when the queue uses assigned routing. Co-authored-by: Cursor --- app/Http/Controllers/Qms/TicketController.php | 36 +++++++-- resources/views/qms/tickets/create.blade.php | 77 +++++++++++++++++-- tests/Feature/ServicePointRoutingTest.php | 42 ++++++++++ 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/Qms/TicketController.php b/app/Http/Controllers/Qms/TicketController.php index 85fbf7a..69cd758 100644 --- a/app/Http/Controllers/Qms/TicketController.php +++ b/app/Http/Controllers/Qms/TicketController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Qms; use App\Http\Controllers\Controller; use App\Http\Controllers\Qms\Concerns\ScopesToAccount; +use App\Models\Counter; use App\Models\ServiceQueue; use App\Models\Ticket; use App\Services\Qms\QueueEngine; @@ -57,6 +58,10 @@ class TicketController extends Controller $queues = ServiceQueue::owned($owner) ->where('organization_id', $organization->id) ->where('is_active', true) + ->with([ + 'branch', + 'counters' => fn ($q) => $q->where('is_active', true)->orderBy('name'), + ]) ->orderBy('name') ->get(); @@ -70,6 +75,7 @@ class TicketController extends Controller $validated = $request->validate([ 'service_queue_id' => ['required', 'exists:queue_service_queues,id'], + 'assigned_counter_id' => ['nullable', 'exists:queue_counters,id'], 'customer_name' => ['nullable', 'string', 'max:255'], 'customer_phone' => ['nullable', 'string', 'max:50'], 'priority' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('qms.ticket_priorities')))], @@ -78,11 +84,31 @@ class TicketController extends Controller $queue = ServiceQueue::findOrFail($validated['service_queue_id']); $this->authorizeOwner($request, $queue); - $ticket = $this->engine->issueTicket($queue, $owner, [ - ...$validated, - 'source' => 'reception', - 'actor_ref' => $owner, - ]); + if ($queue->usesAssignedOnlyRouting() && empty($validated['assigned_counter_id'])) { + return back() + ->withInput() + ->withErrors(['assigned_counter_id' => 'Select a service point for this queue.']); + } + + if (! empty($validated['assigned_counter_id'])) { + $counter = Counter::query()->findOrFail($validated['assigned_counter_id']); + $this->authorizeOwner($request, $counter); + if (! $queue->counters()->where('queue_counters.id', $counter->id)->exists()) { + return back() + ->withInput() + ->withErrors(['assigned_counter_id' => 'That service point is not linked to this queue.']); + } + } + + try { + $ticket = $this->engine->issueTicket($queue, $owner, [ + ...$validated, + 'source' => 'reception', + 'actor_ref' => $owner, + ]); + } catch (\RuntimeException $e) { + return back()->withInput()->withErrors(['service_queue_id' => $e->getMessage()]); + } return redirect()->route('qms.tickets.show', $ticket)->with('success', 'Ticket issued.'); } diff --git a/resources/views/qms/tickets/create.blade.php b/resources/views/qms/tickets/create.blade.php index bf09f9c..ada8334 100644 --- a/resources/views/qms/tickets/create.blade.php +++ b/resources/views/qms/tickets/create.blade.php @@ -1,29 +1,90 @@ -
+ @php + $queuePayload = $queues->map(fn ($queue) => [ + 'id' => $queue->id, + 'label' => $queue->name.($queue->branch ? ' ('.$queue->branch->name.')' : ''), + 'assigned_only' => $queue->usesAssignedOnlyRouting(), + 'counters' => $queue->counters->map(fn ($c) => [ + 'id' => $c->id, + 'label' => $c->name.($c->code ? ' ยท '.$c->code : ''), + ])->values(), + ])->values(); + @endphp +

Issue ticket

@csrf
- + + @error('service_queue_id') +

{{ $message }}

+ @enderror +
+
+ + +

This queue only serves tickets assigned to a desk or room.

+ @error('assigned_counter_id') +

{{ $message }}

+ @enderror
- +
- +
diff --git a/tests/Feature/ServicePointRoutingTest.php b/tests/Feature/ServicePointRoutingTest.php index f6910d9..f7aee9f 100644 --- a/tests/Feature/ServicePointRoutingTest.php +++ b/tests/Feature/ServicePointRoutingTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature; +use App\Http\Middleware\EnsurePlatformSession; use App\Models\Branch; use App\Models\Counter; use App\Models\DisplayScreen; @@ -19,6 +20,12 @@ class ServicePointRoutingTest extends TestCase { use RefreshDatabase; + protected function setUp(): void + { + parent::setUp(); + $this->withoutMiddleware(EnsurePlatformSession::class); + } + /** * @return array{0: User, 1: Organization, 2: Branch, 3: ServiceQueue, 4: Counter, 5: Counter} */ @@ -110,6 +117,41 @@ class ServicePointRoutingTest extends TestCase ]); } + public function test_web_issue_without_service_point_returns_validation_error_not_500(): void + { + [$user, , , $queue] = $this->setUpAssignedConsultation(); + + $this->actingAs($user) + ->from(route('qms.tickets.create')) + ->post(route('qms.tickets.store'), [ + 'service_queue_id' => $queue->id, + 'customer_name' => 'Walk-in', + 'priority' => 'walk_in', + ]) + ->assertRedirect(route('qms.tickets.create')) + ->assertSessionHasErrors('assigned_counter_id'); + + $this->assertSame(0, Ticket::query()->where('service_queue_id', $queue->id)->count()); + } + + public function test_web_issue_with_service_point_succeeds(): void + { + [$user, , , $queue, $room4] = $this->setUpAssignedConsultation(); + + $this->actingAs($user) + ->post(route('qms.tickets.store'), [ + 'service_queue_id' => $queue->id, + 'assigned_counter_id' => $room4->id, + 'customer_name' => 'Walk-in', + 'priority' => 'walk_in', + ]) + ->assertRedirect(); + + $ticket = Ticket::query()->where('service_queue_id', $queue->id)->first(); + $this->assertNotNull($ticket); + $this->assertSame($room4->id, $ticket->assigned_counter_id); + } + public function test_display_shows_latest_per_service_point_with_destination_and_staff(): void { [$user, , $branch, $queue, $room4, $room5] = $this->setUpAssignedConsultation();