Deploy Ladill Care / deploy (push) Successful in 34s
Hide the patient-flow Queue board from nurses, reception, and other non-doctors; specialty home routes now render the queue board list instead of auto-opening the first open visit. Co-authored-by: Cursor <cursoragent@cursor.com>
752 lines
28 KiB
PHP
752 lines
28 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Http\Middleware\EnsurePlatformSession;
|
|
use App\Models\Appointment;
|
|
use App\Models\Branch;
|
|
use App\Models\CareQueueTicket;
|
|
use App\Models\Member;
|
|
use App\Models\Organization;
|
|
use App\Models\Patient;
|
|
use App\Models\User;
|
|
use App\Services\Care\AppointmentService;
|
|
use App\Services\Care\CareQueueContexts;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class CareQueueBridgeTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected User $owner;
|
|
|
|
protected Organization $organization;
|
|
|
|
protected Branch $branch;
|
|
|
|
protected Patient $patient;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$this->owner = User::create([
|
|
'public_id' => 'bridge-owner',
|
|
'name' => 'Owner',
|
|
'email' => 'bridge@example.com',
|
|
]);
|
|
|
|
$this->organization = Organization::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'name' => 'Bridge Clinic',
|
|
'slug' => 'bridge-clinic',
|
|
'settings' => [
|
|
'onboarded' => true,
|
|
'facility_type' => 'clinic',
|
|
'plan' => 'pro',
|
|
'plan_expires_at' => now()->addMonth()->toIso8601String(),
|
|
'queue_integration_enabled' => true,
|
|
],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $this->owner->public_id,
|
|
'role' => 'receptionist',
|
|
]);
|
|
|
|
$this->branch = Branch::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'name' => 'Main',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$this->patient = Patient::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_number' => 'P-100',
|
|
'first_name' => 'Ada',
|
|
'last_name' => 'Lovelace',
|
|
'gender' => 'female',
|
|
'date_of_birth' => '1990-01-01',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Patient-flow board is doctor-only; reception keeps check-in / walk-in helpers.
|
|
*
|
|
* @return array{0: User, 1: Member}
|
|
*/
|
|
protected function makeDoctor(string $suffix = 'doc'): array
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'bridge-'.$suffix,
|
|
'name' => 'Bridge Doctor '.$suffix,
|
|
'email' => $suffix.'@bridge.example.com',
|
|
]);
|
|
$member = Member::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $user->public_id,
|
|
'role' => 'doctor',
|
|
'branch_id' => $this->branch->id,
|
|
]);
|
|
|
|
return [$user, $member];
|
|
}
|
|
|
|
public function test_check_in_issues_consultation_ticket_when_integration_on(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$practitioner = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Bridge',
|
|
'room' => 'Room 1',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$appointment = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'type' => Appointment::TYPE_SCHEDULED,
|
|
'status' => Appointment::STATUS_SCHEDULED,
|
|
'scheduled_at' => now()->addHour(),
|
|
]);
|
|
|
|
$result = app(AppointmentService::class)->checkIn($appointment, $this->owner->public_id, $this->owner->public_id);
|
|
|
|
$this->assertNotNull($result->queue_ticket_uuid);
|
|
$this->assertSame('waiting', $result->queue_ticket_status);
|
|
$this->assertStringStartsWith('C', (string) $result->queue_ticket_number);
|
|
$this->assertSame('Room 1', $result->queue_destination);
|
|
$this->assertSame('Dr. Bridge', $result->queue_staff_display_name);
|
|
$this->assertDatabaseHas('care_queue_tickets', [
|
|
'uuid' => $result->queue_ticket_uuid,
|
|
'ticket_number' => $result->queue_ticket_number,
|
|
'status' => CareQueueTicket::STATUS_WAITING,
|
|
'care_entity' => 'appointment',
|
|
'care_entity_id' => $result->id,
|
|
]);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_check_in_skips_ticket_when_integration_off(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$settings = $this->organization->settings;
|
|
$settings['queue_integration_enabled'] = false;
|
|
$this->organization->update(['settings' => $settings]);
|
|
|
|
$appointment = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'type' => Appointment::TYPE_SCHEDULED,
|
|
'status' => Appointment::STATUS_SCHEDULED,
|
|
'scheduled_at' => now()->addHour(),
|
|
]);
|
|
|
|
$result = app(AppointmentService::class)->checkIn(
|
|
$appointment->fresh(['organization', 'patient']),
|
|
$this->owner->public_id,
|
|
$this->owner->public_id,
|
|
);
|
|
|
|
$this->assertNull($result->queue_ticket_uuid);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_waiting_list_shows_ticket_number_when_integration_on(): void
|
|
{
|
|
Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'queue_position' => 1,
|
|
'queue_ticket_uuid' => 'dddddddd-dddd-dddd-dddd-dddddddddddd',
|
|
'queue_ticket_number' => 'C042',
|
|
'queue_ticket_status' => 'waiting',
|
|
]);
|
|
|
|
[$doctor] = $this->makeDoctor('wait');
|
|
|
|
$this->actingAs($doctor)
|
|
->get(route('care.queue.index'))
|
|
->assertOk()
|
|
->assertSee('C042')
|
|
->assertSee('Call next')
|
|
->assertSee('Serve');
|
|
}
|
|
|
|
public function test_call_next_backfills_missing_ticket_then_calls(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$practitioner = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Backfill',
|
|
'room' => 'Room 3',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'queue_position' => 1,
|
|
]);
|
|
|
|
[$doctor] = $this->makeDoctor('backfill');
|
|
|
|
$response = $this->actingAs($doctor)
|
|
->from(route('care.queue.index'))
|
|
->post(route('care.queue.call-next'), [
|
|
'branch_id' => $this->branch->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
])
|
|
->assertSessionHas('success');
|
|
|
|
$appointment = Appointment::query()->where('patient_id', $this->patient->id)->first();
|
|
$this->assertNotNull($appointment?->queue_ticket_uuid);
|
|
$this->assertSame('called', $appointment->queue_ticket_status);
|
|
$response->assertRedirect(route('care.appointments.show', $appointment));
|
|
$this->assertDatabaseHas('care_queue_tickets', [
|
|
'uuid' => $appointment->queue_ticket_uuid,
|
|
'status' => CareQueueTicket::STATUS_CALLED,
|
|
]);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_call_next_empty_queue_flashes_info_not_error(): void
|
|
{
|
|
Http::fake();
|
|
|
|
[$doctor] = $this->makeDoctor('empty');
|
|
|
|
$this->actingAs($doctor)
|
|
->from(route('care.queue.index'))
|
|
->post(route('care.queue.call-next'), ['branch_id' => $this->branch->id])
|
|
->assertRedirect(route('care.queue.index'))
|
|
->assertSessionHas('info', 'No patients waiting at your consultation service point.')
|
|
->assertSessionMissing('error');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_recall_reannounces_called_ticket_from_patient_queue(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$practitioner = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Recall',
|
|
'room' => 'Room 2',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$appointment = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_SCHEDULED,
|
|
'scheduled_at' => now(),
|
|
]);
|
|
|
|
$checkedIn = app(AppointmentService::class)->checkIn(
|
|
$appointment->fresh(['organization', 'patient', 'practitioner']),
|
|
$this->owner->public_id,
|
|
$this->owner->public_id,
|
|
);
|
|
|
|
$bridge = app(\App\Services\Care\CareQueueBridge::class);
|
|
$called = $bridge->callNext(
|
|
$this->organization->fresh(),
|
|
CareQueueContexts::CONSULTATION,
|
|
(int) $this->branch->id,
|
|
null,
|
|
$practitioner->id,
|
|
);
|
|
$this->assertNotNull($called['ticket']);
|
|
|
|
$appointment = $checkedIn->fresh();
|
|
$this->assertSame('called', $appointment->queue_ticket_status);
|
|
|
|
$doctorUser = User::create([
|
|
'public_id' => 'bridge-recall-doc',
|
|
'name' => 'Recall Doctor',
|
|
'email' => 'bridge-recall@example.com',
|
|
]);
|
|
Member::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $doctorUser->public_id,
|
|
'role' => 'doctor',
|
|
'branch_id' => $this->branch->id,
|
|
]);
|
|
\App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'member_id' => Member::query()->where('user_ref', $doctorUser->public_id)->value('id'),
|
|
'user_ref' => $doctorUser->public_id,
|
|
'name' => 'Recall Doctor',
|
|
'room' => 'Room 2',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$this->actingAs($doctorUser)
|
|
->from(route('care.queue.index'))
|
|
->post(route('care.queue.recall', $appointment))
|
|
->assertRedirect(route('care.queue.index'))
|
|
->assertSessionHas('success');
|
|
|
|
$this->assertSame('called', $appointment->fresh()->queue_ticket_status);
|
|
|
|
[$viewer] = $this->makeDoctor('recall-view');
|
|
|
|
$this->actingAs($viewer)
|
|
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
|
|
->assertOk()
|
|
->assertSee($appointment->queue_ticket_number);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_patient_queue_sync_issues_specialty_tickets(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$department = \App\Models\Department::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dentistry',
|
|
'type' => 'dental',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$practitioner = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'department_id' => $department->id,
|
|
'name' => 'Dentistry Clinic',
|
|
'room' => 'Dental Bay 1',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$settings = $this->organization->settings;
|
|
$settings['specialty_modules'] = ['dentistry' => true];
|
|
$settings['specialty_module_provisioning'] = [
|
|
'dentistry' => [
|
|
'active' => true,
|
|
'department_ids' => [$department->id],
|
|
'queues' => [[
|
|
'module' => 'dentistry',
|
|
'branch_id' => $this->branch->id,
|
|
'branch_name' => 'Main',
|
|
'name' => 'Dentistry',
|
|
'prefix' => 'DEN',
|
|
'active' => true,
|
|
'synced' => false,
|
|
]],
|
|
],
|
|
];
|
|
$this->organization->update(['settings' => $settings]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'department_id' => $department->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'queue_position' => 1,
|
|
'reason' => 'Toothache',
|
|
]);
|
|
|
|
// Board GET must remain read-only; ticket backfill is explicit (Call next / artisan).
|
|
[$doctor] = $this->makeDoctor('sync');
|
|
|
|
$this->actingAs($doctor)
|
|
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
|
|
->assertOk()
|
|
->assertSee('Toothache');
|
|
|
|
$appointment = Appointment::query()->where('patient_id', $this->patient->id)->first();
|
|
$this->assertNull($appointment?->queue_ticket_uuid);
|
|
|
|
$issued = app(\App\Services\Care\CareQueueBridge::class)
|
|
->syncMissingAppointmentTickets($this->organization->fresh(), (int) $this->branch->id, 10);
|
|
$this->assertSame(1, $issued);
|
|
|
|
$appointment = $appointment->fresh();
|
|
$this->assertNotNull($appointment?->queue_ticket_uuid);
|
|
$this->assertStringStartsWith('DEN', (string) $appointment->queue_ticket_number);
|
|
$this->assertDatabaseHas('care_service_queues', [
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'context' => 'dentistry',
|
|
]);
|
|
$this->assertDatabaseHas('care_queue_tickets', [
|
|
'uuid' => $appointment->queue_ticket_uuid,
|
|
'ticket_number' => $appointment->queue_ticket_number,
|
|
'status' => CareQueueTicket::STATUS_WAITING,
|
|
]);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_call_next_ends_current_session_and_opens_next_patient(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$practitioner = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Advance',
|
|
'room' => 'Room 4',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$currentPatient = Patient::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_number' => 'P-CUR',
|
|
'first_name' => 'Current',
|
|
'last_name' => 'Patient',
|
|
'gender' => 'female',
|
|
'date_of_birth' => '1991-01-01',
|
|
]);
|
|
|
|
$currentVisit = \App\Models\Visit::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $currentPatient->id,
|
|
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
$current = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $currentPatient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'visit_id' => $currentVisit->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_IN_CONSULTATION,
|
|
'scheduled_at' => now()->subHour(),
|
|
'waiting_at' => now()->subHour(),
|
|
'checked_in_at' => now()->subHour(),
|
|
'started_at' => now()->subMinutes(30),
|
|
'queue_ticket_status' => 'serving',
|
|
]);
|
|
|
|
\App\Models\Consultation::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'visit_id' => $currentVisit->id,
|
|
'appointment_id' => $current->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'patient_id' => $currentPatient->id,
|
|
'status' => \App\Models\Consultation::STATUS_DRAFT,
|
|
'started_at' => now()->subMinutes(30),
|
|
]);
|
|
|
|
$nextVisit = \App\Models\Visit::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'status' => \App\Models\Visit::STATUS_OPEN,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
$next = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'visit_id' => $nextVisit->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'checked_in_at' => now(),
|
|
'queue_position' => 1,
|
|
]);
|
|
|
|
app(\App\Services\Care\CareQueueBridge::class)->issueForAppointment(
|
|
$this->organization->fresh(),
|
|
$next->fresh(['patient', 'practitioner', 'organization']),
|
|
);
|
|
|
|
[$doctor] = $this->makeDoctor('advance');
|
|
|
|
$response = $this->actingAs($doctor)
|
|
->post(route('care.queue.call-next'), [
|
|
'branch_id' => $this->branch->id,
|
|
'practitioner_id' => $practitioner->id,
|
|
'appointment_id' => $current->id,
|
|
])
|
|
->assertSessionHas('success');
|
|
|
|
$this->assertSame(Appointment::STATUS_COMPLETED, $current->fresh()->status);
|
|
$this->assertSame(\App\Models\Consultation::STATUS_COMPLETED, $current->consultation->fresh()->status);
|
|
|
|
$next = $next->fresh();
|
|
$this->assertSame('called', $next->queue_ticket_status);
|
|
$response->assertRedirect(route('care.appointments.show', $next));
|
|
|
|
$this->actingAs($doctor)
|
|
->get(route('care.appointments.show', $next))
|
|
->assertOk()
|
|
->assertSee('Call again');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_assigned_doctor_call_next_succeeds_with_waiting_appointments(): void
|
|
{
|
|
Http::fake();
|
|
|
|
// Provision consultation desks for an existing GP first so ensure() early-returns
|
|
// with a points list that does not yet include the demo doctor we link next.
|
|
$other = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Other Desk',
|
|
'room' => 'Room 1',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
app(\App\Services\Care\CareQueueProvisioner::class)->ensure(
|
|
$this->organization->fresh(),
|
|
CareQueueContexts::CONSULTATION,
|
|
$this->branch->id,
|
|
);
|
|
|
|
$doctorUser = User::create([
|
|
'public_id' => 'bridge-assigned-doc',
|
|
'name' => 'Assigned Doctor',
|
|
'email' => 'bridge-assigned@example.com',
|
|
]);
|
|
$doctorMember = Member::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $doctorUser->public_id,
|
|
'role' => 'doctor',
|
|
'branch_id' => $this->branch->id,
|
|
]);
|
|
$doctor = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'member_id' => $doctorMember->id,
|
|
'user_ref' => $doctorUser->public_id,
|
|
'name' => 'Dr. Assigned',
|
|
'room' => 'Room 9',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $doctor->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'queue_position' => 1,
|
|
]);
|
|
|
|
// Board KPI / Assigned to you still lists the waiter.
|
|
$this->actingAs($doctorUser)
|
|
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
|
|
->assertOk()
|
|
->assertSee('Assigned to you')
|
|
->assertSee('Ada Lovelace');
|
|
|
|
$response = $this->actingAs($doctorUser)
|
|
->from(route('care.queue.index', ['branch_id' => $this->branch->id]))
|
|
->post(route('care.queue.call-next'), [
|
|
'branch_id' => $this->branch->id,
|
|
])
|
|
->assertSessionMissing('info')
|
|
->assertSessionHas('success');
|
|
|
|
$appointment = Appointment::query()->where('practitioner_id', $doctor->id)->first();
|
|
$this->assertNotNull($appointment?->queue_ticket_uuid);
|
|
$this->assertSame('called', $appointment->queue_ticket_status);
|
|
$response->assertRedirect(route('care.appointments.show', $appointment));
|
|
|
|
$this->assertDatabaseHas('care_service_points', [
|
|
'organization_id' => $this->organization->id,
|
|
'kind' => 'practitioner',
|
|
'ref_id' => $doctor->id,
|
|
'is_active' => true,
|
|
]);
|
|
$this->assertNotNull($other->fresh());
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_call_next_reclaims_ticket_from_wrong_desk_and_announces(): void
|
|
{
|
|
Http::fake();
|
|
|
|
$doctor = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Reclaim',
|
|
'room' => 'Room 7',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$other = \App\Models\Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'name' => 'Dr. Other',
|
|
'room' => 'Room 8',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
app(\App\Services\Care\CareQueueProvisioner::class)->ensure(
|
|
$this->organization->fresh(),
|
|
CareQueueContexts::CONSULTATION,
|
|
$this->branch->id,
|
|
);
|
|
|
|
$queue = \App\Models\CareServiceQueue::query()
|
|
->where('organization_id', $this->organization->id)
|
|
->where('context', CareQueueContexts::CONSULTATION)
|
|
->where('branch_id', $this->branch->id)
|
|
->firstOrFail();
|
|
|
|
$doctorPoint = \App\Models\CareServicePoint::query()
|
|
->where('service_queue_id', $queue->id)
|
|
->where('kind', 'practitioner')
|
|
->where('ref_id', $doctor->id)
|
|
->firstOrFail();
|
|
|
|
$otherPoint = \App\Models\CareServicePoint::query()
|
|
->where('service_queue_id', $queue->id)
|
|
->where('kind', 'practitioner')
|
|
->where('ref_id', $other->id)
|
|
->firstOrFail();
|
|
|
|
$ticketUuid = (string) \Illuminate\Support\Str::uuid();
|
|
CareQueueTicket::create([
|
|
'uuid' => $ticketUuid,
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'service_queue_id' => $queue->id,
|
|
'service_point_id' => $otherPoint->id,
|
|
'ticket_number' => 'C099',
|
|
'status' => CareQueueTicket::STATUS_WAITING,
|
|
'priority' => 'walk_in',
|
|
'customer_name' => 'Ada Lovelace',
|
|
'care_entity' => 'appointment',
|
|
'care_entity_id' => null,
|
|
]);
|
|
|
|
$appointment = Appointment::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $this->patient->id,
|
|
'practitioner_id' => $doctor->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_WAITING,
|
|
'scheduled_at' => now(),
|
|
'waiting_at' => now(),
|
|
'queue_position' => 1,
|
|
'queue_ticket_uuid' => $ticketUuid,
|
|
'queue_ticket_number' => 'C099',
|
|
'queue_ticket_status' => 'waiting',
|
|
]);
|
|
|
|
CareQueueTicket::where('uuid', $ticketUuid)->update([
|
|
'care_entity_id' => $appointment->id,
|
|
'care_entity_uuid' => $appointment->uuid,
|
|
]);
|
|
|
|
[$caller] = $this->makeDoctor('reclaim');
|
|
|
|
$this->actingAs($caller)
|
|
->from(route('care.queue.index'))
|
|
->post(route('care.queue.call-next'), [
|
|
'branch_id' => $this->branch->id,
|
|
'practitioner_id' => $doctor->id,
|
|
])
|
|
->assertSessionHas('success')
|
|
->assertRedirect(route('care.appointments.show', $appointment));
|
|
|
|
$appointment = $appointment->fresh();
|
|
$this->assertSame('called', $appointment->queue_ticket_status);
|
|
$this->assertSame($doctorPoint->uuid, $appointment->queue_service_point_uuid);
|
|
|
|
$this->assertDatabaseHas('care_queue_tickets', [
|
|
'uuid' => $ticketUuid,
|
|
'status' => CareQueueTicket::STATUS_CALLED,
|
|
'service_point_id' => $doctorPoint->id,
|
|
]);
|
|
|
|
$this->assertDatabaseHas('care_voice_announcements', [
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
$this->actingAs($caller)
|
|
->get(route('care.queue.index', [
|
|
'branch_id' => $this->branch->id,
|
|
'practitioner_id' => $doctor->id,
|
|
]))
|
|
->assertOk()
|
|
->assertSee('Called')
|
|
->assertSee('C099');
|
|
|
|
Http::assertNothingSent();
|
|
}
|
|
}
|