'sp-owner', 'name' => 'Owner', 'email' => 'sp@example.com', 'password' => bcrypt('password'), ]); $org = app(OrganizationResolver::class)->completeOnboarding($user, [ 'organization_name' => 'Clinic', 'industry' => 'healthcare', 'appointment_mode' => 'hybrid', 'branch_name' => 'Main', 'timezone' => 'UTC', ]); $branch = Branch::first(); $queue = ServiceQueue::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Consultation', 'prefix' => 'C', 'strategy' => 'fifo', 'is_active' => true, 'settings' => ['routing_mode' => 'assigned_only'], ]); $room4 = Counter::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Dr. Mensah', 'destination' => 'Consultation Room 4', 'staff_ref' => 'doc-mensah', 'staff_display_name' => 'Dr. Mensah', 'status' => 'available', 'is_active' => true, ]); $room5 = Counter::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Dr. Okai', 'destination' => 'Consultation Room 5', 'staff_ref' => 'doc-okai', 'staff_display_name' => 'Dr. Okai', 'status' => 'available', 'is_active' => true, ]); $room4->serviceQueues()->sync([$queue->id]); $room5->serviceQueues()->sync([$queue->id]); return [$user, $org, $branch, $queue, $room4, $room5]; } public function test_call_next_cannot_steal_ticket_assigned_to_another_service_point(): void { [$user, , , $queue, $room4, $room5] = $this->setUpAssignedConsultation(); $engine = app(QueueEngine::class); $ticket = $engine->issueTicket($queue, $user->public_id, [ 'customer_name' => 'Ada', 'assigned_counter_uuid' => $room4->uuid, ]); $this->assertSame($room4->id, $ticket->assigned_counter_id); $stolen = $engine->callNext($queue, $room5, $user->public_id); $this->assertNull($stolen); $called = $engine->callNext($queue, $room4, $user->public_id); $this->assertNotNull($called); $this->assertSame($ticket->id, $called->id); $this->assertSame('called', $called->status); $this->assertSame($room4->id, $called->counter_id); } public function test_assigned_only_queue_requires_service_point_on_issue(): void { [$user, , , $queue] = $this->setUpAssignedConsultation(); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('assigned service point'); app(QueueEngine::class)->issueTicket($queue, $user->public_id, [ 'customer_name' => 'No Point', ]); } public function test_display_shows_latest_per_service_point_with_destination_and_staff(): void { [$user, , $branch, $queue, $room4, $room5] = $this->setUpAssignedConsultation(); $engine = app(QueueEngine::class); $t1 = $engine->issueTicket($queue, $user->public_id, [ 'assigned_counter_uuid' => $room4->uuid, 'customer_name' => 'Patient A', ]); $t2 = $engine->issueTicket($queue, $user->public_id, [ 'assigned_counter_uuid' => $room5->uuid, 'customer_name' => 'Patient B', ]); $engine->callTicket($t1, $room4, $user->public_id); $engine->callTicket($t2, $room5, $user->public_id); $screen = DisplayScreen::create([ 'owner_ref' => $user->public_id, 'organization_id' => $queue->organization_id, 'branch_id' => $branch->id, 'name' => 'Lobby', 'layout' => 'standard', 'service_queue_ids' => [$queue->id], 'is_active' => true, ]); $payload = app(DisplayService::class)->payload($screen); $this->assertCount(2, $payload['now_serving']); $destinations = collect($payload['now_serving'])->pluck('destination')->all(); $this->assertContains('Consultation Room 4', $destinations); $this->assertContains('Consultation Room 5', $destinations); $staff = collect($payload['now_serving'])->pluck('staff_display_name')->all(); $this->assertContains('Dr. Mensah', $staff); $this->assertContains('Dr. Okai', $staff); $mensah = collect($payload['now_serving'])->firstWhere('staff_display_name', 'Dr. Mensah'); $this->assertStringContainsString('Dr. Mensah', (string) ($mensah['announcement_text'] ?? '')); $this->assertStringContainsString('Consultation Room 4', (string) ($mensah['announcement_text'] ?? '')); } public function test_shared_pool_still_allows_unassigned_call_next(): void { $user = User::create([ 'public_id' => 'bank-owner', 'name' => 'Bank', 'email' => 'bank@example.com', 'password' => bcrypt('password'), ]); $org = app(OrganizationResolver::class)->completeOnboarding($user, [ 'organization_name' => 'Bank', 'industry' => 'banking', 'appointment_mode' => 'walk_in_only', 'branch_name' => 'Main', 'timezone' => 'UTC', ]); $branch = Branch::first(); $queue = ServiceQueue::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Teller', 'prefix' => 'T', 'strategy' => 'fifo', 'is_active' => true, 'settings' => ['routing_mode' => 'shared_pool'], ]); $window = Counter::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Window 1', 'destination' => 'Window 1', 'status' => 'available', 'is_active' => true, ]); $window->serviceQueues()->sync([$queue->id]); $engine = app(QueueEngine::class); $ticket = $engine->issueTicket($queue, $user->public_id, ['customer_name' => 'Customer']); $this->assertNull($ticket->assigned_counter_id); $called = $engine->callNext($queue, $window, $user->public_id); $this->assertNotNull($called); $this->assertSame($ticket->id, $called->id); } public function test_care_api_can_issue_assigned_ticket(): void { config(['qms.service_api_keys.care' => 'test-care-key']); $owner = 'care-sp-owner'; $headers = [ 'Authorization' => 'Bearer test-care-key', 'Accept' => 'application/json', ]; $this->postJson('/api/v1/integrations/provision', [ 'owner' => $owner, 'organization_name' => 'Care Clinic', 'branch_name' => 'Main', ], $headers)->assertSuccessful(); $queueUuid = $this->postJson('/api/v1/queues', [ 'owner' => $owner, 'name' => 'Consultation', 'prefix' => 'C', 'branch_name' => 'Main', 'external_key' => 'care:dept:consultation:queue:1', 'routing_mode' => 'assigned_only', ], $headers)->assertCreated()->json('data.uuid'); $counterUuid = $this->postJson('/api/v1/counters', [ 'owner' => $owner, 'name' => 'Dr. Mensah', 'destination' => 'Room 4', 'staff_display_name' => 'Dr. Mensah', 'branch_name' => 'Main', 'queue_uuids' => [$queueUuid], 'external_key' => 'care:point:consultation:practitioner:9', ], $headers)->assertCreated()->json('data.uuid'); $this->postJson('/api/v1/tickets', [ 'owner' => $owner, 'service_queue_id' => $queueUuid, 'customer_name' => 'Ada', 'assigned_counter_id' => $counterUuid, ], $headers) ->assertCreated() ->assertJsonPath('data.assigned_counter.uuid', $counterUuid) ->assertJsonPath('data.destination', 'Room 4') ->assertJsonPath('data.staff_display_name', 'Dr. Mensah'); } }