Route Care tickets to per-staff service points instead of shared branch queues.
Deploy Ladill Care / deploy (push) Successful in 1m40s

Provision one consultation point per doctor (room + identity) and role-based
points for pharmacy, lab, billing, and triage. Fixed-doctor appointments and
walk-ins issue only to the assigned point; Call next respects that assignment
and flags unresolved patients rather than random routing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-17 17:22:14 +00:00
co-authored by Cursor
parent c5151ad476
commit e73b39b678
22 changed files with 1331 additions and 97 deletions
+61 -3
View File
@@ -80,10 +80,18 @@ class CareQueueBridgeTest extends TestCase
'prefix' => 'C',
'active' => true,
'synced' => true,
'routing_mode' => 'assigned_only',
'queue_uuid' => 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'counter_uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'queue_external_key' => CareQueueContexts::queueExternalKey(CareQueueContexts::CONSULTATION, $this->branch->id),
'counter_external_key' => CareQueueContexts::counterExternalKey(CareQueueContexts::CONSULTATION, $this->branch->id),
'points' => [[
'kind' => 'practitioner',
'ref_id' => 0, // filled after practitioner create in tests that need it
'destination' => 'Consultation desk',
'counter_uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'synced' => true,
]],
]],
],
];
@@ -103,13 +111,38 @@ class CareQueueBridgeTest extends TestCase
public function test_check_in_issues_consultation_ticket_when_integration_on(): void
{
$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,
]);
$settings = $this->organization->settings;
$settings['department_queue_provisioning'][CareQueueContexts::CONSULTATION]['queues'][0]['points'] = [[
'kind' => 'practitioner',
'ref_id' => $practitioner->id,
'destination' => 'Room 1',
'staff_display_name' => 'Dr. Bridge',
'counter_uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'synced' => true,
]];
$this->organization->update(['settings' => $settings]);
Http::fake(function (\Illuminate\Http\Client\Request $request) {
if (str_contains($request->url(), '/tickets') && $request->method() === 'POST') {
$this->assertSame('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', $request['assigned_counter_id'] ?? null);
return Http::response([
'data' => [
'uuid' => 'cccccccc-cccc-cccc-cccc-cccccccccccc',
'ticket_number' => 'C001',
'status' => 'waiting',
'assigned_counter' => ['uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'],
'destination' => 'Room 1',
'staff_display_name' => 'Dr. Bridge',
],
], 201);
}
@@ -122,6 +155,7 @@ class CareQueueBridgeTest extends TestCase
'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(),
@@ -193,10 +227,29 @@ class CareQueueBridgeTest extends TestCase
public function test_call_next_backfills_missing_ticket_then_calls(): void
{
$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,
]);
$settings = $this->organization->settings;
$settings['department_queue_provisioning'][CareQueueContexts::CONSULTATION]['queues'][0]['points'] = [[
'kind' => 'practitioner',
'ref_id' => $practitioner->id,
'destination' => 'Room 3',
'counter_uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'synced' => true,
]];
$settings['department_queue_provisioning'][CareQueueContexts::CONSULTATION]['queues'][0]['counter_uuid'] = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
$this->organization->update(['settings' => $settings]);
$ticketSeq = 0;
Http::fake(function (\Illuminate\Http\Client\Request $request) use (&$ticketSeq) {
if (str_contains($request->url(), '/call-next') && $request->method() === 'POST') {
// First call: empty queue; second (after issue): ticket returned.
if ($ticketSeq < 1) {
return Http::response(['data' => null], 200);
}
@@ -219,6 +272,7 @@ class CareQueueBridgeTest extends TestCase
'uuid' => 'ffffffff-ffff-ffff-ffff-ffffffffffff',
'ticket_number' => 'C100',
'status' => 'waiting',
'assigned_counter' => ['uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'],
],
], 201);
}
@@ -231,6 +285,7 @@ class CareQueueBridgeTest extends TestCase
'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(),
@@ -240,7 +295,10 @@ class CareQueueBridgeTest extends TestCase
$this->actingAs($this->owner)
->from(route('care.queue.index'))
->post(route('care.queue.call-next'), ['branch_id' => $this->branch->id])
->post(route('care.queue.call-next'), [
'branch_id' => $this->branch->id,
'practitioner_id' => $practitioner->id,
])
->assertRedirect(route('care.queue.index'))
->assertSessionHas('success');
@@ -259,7 +317,7 @@ class CareQueueBridgeTest extends TestCase
->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 in the consultation queue.')
->assertSessionHas('info', 'No patients waiting at your consultation service point.')
->assertSessionMissing('error');
}
}