Assign every demo waiter to a doctor and sync Queue tickets.
Deploy Ladill Care / deploy (push) Successful in 1m54s
Deploy Ladill Care / deploy (push) Successful in 1m54s
Link DemoWorld doctor staff to practitioners, clear unresolved routing, distribute staff across branches, and restore scoped consultation/pharmacy ticket sync so demos are not left Unassigned. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -114,6 +114,7 @@ class DemoTenantSeeder
|
|||||||
$departments = $this->departmentsByBranch($branches, $ownerRef);
|
$departments = $this->departmentsByBranch($branches, $ownerRef);
|
||||||
}
|
}
|
||||||
$practitioners = $this->seedPractitioners($organization, $branches, $departments, $ownerRef, $volumes);
|
$practitioners = $this->seedPractitioners($organization, $branches, $departments, $ownerRef, $volumes);
|
||||||
|
$this->linkStaffDoctorsToPractitioners($organization, $ownerRef, $plan, $branches, $practitioners);
|
||||||
$patients = $this->seedPatients($organization, $branches, $ownerRef, $volumes);
|
$patients = $this->seedPatients($organization, $branches, $ownerRef, $volumes);
|
||||||
$appointmentCount = $this->seedAppointmentsAndClinical(
|
$appointmentCount = $this->seedAppointmentsAndClinical(
|
||||||
$organization,
|
$organization,
|
||||||
@@ -124,6 +125,7 @@ class DemoTenantSeeder
|
|||||||
$ownerRef,
|
$ownerRef,
|
||||||
$volumes,
|
$volumes,
|
||||||
);
|
);
|
||||||
|
$this->assignAllDemoWaiters($organization, $ownerRef, $branches, $practitioners);
|
||||||
if (in_array($plan, ['pro', 'enterprise'], true)) {
|
if (in_array($plan, ['pro', 'enterprise'], true)) {
|
||||||
$appointmentCount += $this->seedSpecialtyDemoData(
|
$appointmentCount += $this->seedSpecialtyDemoData(
|
||||||
$organization->fresh(),
|
$organization->fresh(),
|
||||||
@@ -173,10 +175,126 @@ class DemoTenantSeeder
|
|||||||
*/
|
*/
|
||||||
private function syncQueueTicketsForDemo(Organization $organization, array $branches): void
|
private function syncQueueTicketsForDemo(Organization $organization, array $branches): void
|
||||||
{
|
{
|
||||||
// Intentionally no-op during demo seed. Queue HTTP (branch × context) can
|
if (! data_get($organization->settings, 'queue_integration_enabled')) {
|
||||||
// take many minutes on Enterprise volumes and hung production reseeds.
|
return;
|
||||||
// Specialty/care waiting lists still come from local appointments; Queue
|
}
|
||||||
// tickets backfill lazily on first Ops / specialty page visit.
|
|
||||||
|
try {
|
||||||
|
$bridge = app(CareQueueBridge::class);
|
||||||
|
if (! $bridge->isEnabled($organization)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scoped per branch/context (not full-org) so demos finish quickly while
|
||||||
|
// still leaving waiters with real tickets instead of "Unassigned".
|
||||||
|
$contexts = [
|
||||||
|
CareQueueContexts::CONSULTATION,
|
||||||
|
CareQueueContexts::PHARMACY,
|
||||||
|
CareQueueContexts::LABORATORY,
|
||||||
|
CareQueueContexts::BILLING,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($branches as $branch) {
|
||||||
|
foreach ($contexts as $context) {
|
||||||
|
try {
|
||||||
|
$bridge->syncMissingTickets($organization, $context, (int) $branch->id, 50);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
// Continue — page load will retry for that context.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable) {
|
||||||
|
// Demo seed should not fail if Queue API is unreachable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Point DemoWorld doctor staff at a real practitioner so Call next / rooms work.
|
||||||
|
*
|
||||||
|
* @param list<Branch> $branches
|
||||||
|
* @param list<Practitioner> $practitioners
|
||||||
|
*/
|
||||||
|
private function linkStaffDoctorsToPractitioners(
|
||||||
|
Organization $organization,
|
||||||
|
string $ownerRef,
|
||||||
|
string $plan,
|
||||||
|
array $branches,
|
||||||
|
array $practitioners,
|
||||||
|
): void {
|
||||||
|
$doctorEmail = null;
|
||||||
|
$doctorName = null;
|
||||||
|
foreach (DemoWorld::staff($plan) as $staff) {
|
||||||
|
if (($staff['roles']['care'] ?? null) === 'doctor') {
|
||||||
|
$doctorEmail = strtolower((string) $staff['email']);
|
||||||
|
$doctorName = (string) $staff['name'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($doctorEmail === null || $practitioners === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$member = Member::query()
|
||||||
|
->where('organization_id', $organization->id)
|
||||||
|
->where('user_ref', $doctorEmail)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$byBranch = [];
|
||||||
|
foreach ($practitioners as $practitioner) {
|
||||||
|
$byBranch[(int) $practitioner->branch_id] ??= $practitioner;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($branches as $branch) {
|
||||||
|
$practitioner = $byBranch[(int) $branch->id] ?? null;
|
||||||
|
if (! $practitioner) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$practitioner->forceFill([
|
||||||
|
'user_ref' => $doctorEmail,
|
||||||
|
'member_id' => $member?->id,
|
||||||
|
'name' => $doctorName !== '' ? $doctorName : $practitioner->name,
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every waiting / checked-in demo appointment gets a doctor and a clean routing
|
||||||
|
* state so Queue sync can issue tickets (no amber Unassigned leftovers).
|
||||||
|
*
|
||||||
|
* @param list<Branch> $branches
|
||||||
|
* @param list<Practitioner> $practitioners
|
||||||
|
*/
|
||||||
|
private function assignAllDemoWaiters(
|
||||||
|
Organization $organization,
|
||||||
|
string $ownerRef,
|
||||||
|
array $branches,
|
||||||
|
array $practitioners,
|
||||||
|
): void {
|
||||||
|
if ($practitioners === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$byBranch = [];
|
||||||
|
foreach ($practitioners as $practitioner) {
|
||||||
|
$byBranch[(int) $practitioner->branch_id][] = $practitioner;
|
||||||
|
}
|
||||||
|
|
||||||
|
$waiters = Appointment::withTrashed()
|
||||||
|
->owned($ownerRef)
|
||||||
|
->where('organization_id', $organization->id)
|
||||||
|
->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($waiters as $index => $appointment) {
|
||||||
|
$branchPracs = $byBranch[(int) $appointment->branch_id] ?? $practitioners;
|
||||||
|
$practitioner = $branchPracs[$index % max(1, count($branchPracs))] ?? $practitioners[0];
|
||||||
|
|
||||||
|
$appointment->forceFill([
|
||||||
|
'practitioner_id' => $practitioner->id,
|
||||||
|
'queue_routing_status' => null,
|
||||||
|
'deleted_at' => null,
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -473,18 +591,25 @@ class DemoTenantSeeder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Member rows for DemoWorld staff (user_ref = email until SSO remaps to public_id).
|
* Local Member rows for DemoWorld staff (user_ref = email until SSO remaps to public_id).
|
||||||
|
* Multi-branch demos spread staff across branches so each site has a role roster.
|
||||||
*
|
*
|
||||||
* @param list<Branch> $branches
|
* @param list<Branch> $branches
|
||||||
*/
|
*/
|
||||||
private function seedStaffMembers(Organization $organization, string $ownerRef, string $plan, array $branches): void
|
private function seedStaffMembers(Organization $organization, string $ownerRef, string $plan, array $branches): void
|
||||||
{
|
{
|
||||||
$hq = $branches[0] ?? null;
|
$hq = $branches[0] ?? null;
|
||||||
foreach (DemoWorld::staff($plan) as $staff) {
|
$staffList = DemoWorld::staff($plan);
|
||||||
|
foreach ($staffList as $index => $staff) {
|
||||||
$role = $staff['roles']['care'] ?? null;
|
$role = $staff['roles']['care'] ?? null;
|
||||||
if (! is_string($role) || $role === '') {
|
if (! is_string($role) || $role === '') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$branch = $hq;
|
||||||
|
if ($role !== 'hospital_admin' && $branches !== []) {
|
||||||
|
$branch = $branches[$index % count($branches)];
|
||||||
|
}
|
||||||
|
|
||||||
Member::query()->updateOrCreate(
|
Member::query()->updateOrCreate(
|
||||||
[
|
[
|
||||||
'organization_id' => $organization->id,
|
'organization_id' => $organization->id,
|
||||||
@@ -493,7 +618,7 @@ class DemoTenantSeeder
|
|||||||
[
|
[
|
||||||
'owner_ref' => $ownerRef,
|
'owner_ref' => $ownerRef,
|
||||||
'role' => $role,
|
'role' => $role,
|
||||||
'branch_id' => in_array($role, ['hospital_admin'], true) ? null : $hq?->id,
|
'branch_id' => $role === 'hospital_admin' ? null : $branch?->id,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,5 +342,15 @@ class DemoSeedCommandTest extends TestCase
|
|||||||
->where('role', 'receptionist')
|
->where('role', 'receptionist')
|
||||||
->exists()
|
->exists()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$waiters = Appointment::query()
|
||||||
|
->where('owner_ref', $user->public_id)
|
||||||
|
->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
|
||||||
|
->get();
|
||||||
|
$this->assertNotEmpty($waiters);
|
||||||
|
foreach ($waiters as $waiter) {
|
||||||
|
$this->assertNotNull($waiter->practitioner_id, 'Demo waiters must all have a doctor assigned');
|
||||||
|
$this->assertNotSame('unresolved', $waiter->queue_routing_status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user