authorizeAbility($request, 'appointments.view'); abort_unless(app(CarePermissions::class)->handlesFloorCare($this->member($request)), 403); $organization = $this->organization($request); $member = $this->member($request); $owner = $this->ownerRef($request); $resolver = app(OrganizationResolver::class); $practitionerScope = $resolver->practitionerScope($organization, $member); $lockToPractitioner = $practitionerScope !== null; $branches = $this->branchContext->branches($request, $organization, $member); $branchId = $this->branchContext->resolve($request, $organization, $member); $practitionerId = $lockToPractitioner ? ($practitionerScope[0] ?? null) : ($request->input('practitioner_id') ? (int) $request->input('practitioner_id') : null); $queueIntegration = ['enabled' => false]; $queueContext = CareQueueContexts::CONSULTATION; $specialtyLabel = null; if ($lockToPractitioner && $practitionerId) { $prac = Practitioner::owned($owner)->with('organization')->find($practitionerId); $deskContext = $this->queueBridge->contextForPractitioner($organization, $prac); if ($deskContext !== CareQueueContexts::CONSULTATION) { $queueContext = $deskContext; $specialtyLabel = (string) (config("care.specialty_modules.{$deskContext}.label") ?? config("care.specialty_modules.{$deskContext}.nav_label") ?? ucfirst(str_replace('_', ' ', $deskContext))); } } if ($this->queueBridge->isEnabled($organization)) { $syncBranches = $lockToPractitioner ? Practitioner::owned($owner) ->whereIn('id', $practitionerScope ?: [0]) ->pluck('branch_id') ->map(fn ($id) => (int) $id) ->unique() ->filter() ->values() ->all() : array_filter([$branchId]); foreach ($syncBranches as $syncBranchId) { $this->queueBridge->syncMissingAppointmentTickets($organization, $syncBranchId, 100); } if ($branchId > 0) { $queueIntegration = $this->queueBridge->listMeta($organization, $queueContext, $branchId); } elseif ($syncBranches !== []) { $queueIntegration = $this->queueBridge->listMeta( $organization, $queueContext, (int) $syncBranches[0], ); } } if ($lockToPractitioner) { $queue = $this->appointments->queueForPractitioners( $owner, $practitionerScope ?? [], $branchId > 0 ? $branchId : null, ); $inConsultation = Appointment::owned($owner) ->where('organization_id', $organization->id) ->where('status', Appointment::STATUS_IN_CONSULTATION) ->whereIn('practitioner_id', $practitionerScope ?: [0]) ->with(['patient', 'practitioner', 'consultation', 'visit', 'organization']) ->orderBy('started_at') ->get(); } else { $queue = $branchId ? $this->appointments->queue($owner, $branchId, $practitionerId) : collect(); $inConsultation = Appointment::owned($owner) ->where('organization_id', $organization->id) ->where('status', Appointment::STATUS_IN_CONSULTATION) ->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) ->when($practitionerId, fn ($q) => $q->where('practitioner_id', $practitionerId)) ->with(['patient', 'practitioner', 'consultation', 'visit', 'organization']) ->orderBy('started_at') ->get(); } $canManageQueue = app(CarePermissions::class)->can($member, 'queue.manage'); $canConsult = app(CarePermissions::class)->can($member, 'consultations.manage'); $todayQuery = Appointment::owned($owner) ->where('organization_id', $organization->id) ->whereDate('scheduled_at', today()); if ($lockToPractitioner) { $todayQuery->whereIn('practitioner_id', $practitionerScope ?: [0]); } elseif ($branchId) { $todayQuery->where('branch_id', $branchId); } $heroStats = [ 'waiting' => $queue->count(), 'in_consultation' => $inConsultation->count(), 'today' => $todayQuery->count(), ]; return view('care.queue.index', [ 'organization' => $organization, 'branches' => $branches, 'branchId' => $branchId, 'canSwitchBranch' => $this->branchContext->canSwitch($member), 'lockToPractitioner' => $lockToPractitioner, 'practitioners' => $lockToPractitioner ? collect() : Practitioner::owned($owner) ->where('organization_id', $organization->id) ->where('is_active', true) ->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) ->orderBy('name') ->get(), 'practitionerId' => $practitionerId, 'queue' => $queue, 'inConsultation' => $inConsultation, 'canManageQueue' => $canManageQueue, 'canConsult' => $canConsult, 'heroStats' => $heroStats, 'queueIntegration' => $queueIntegration, 'queueContext' => $queueContext, 'specialtyLabel' => $specialtyLabel, ]); } public function callNext(Request $request, CareQueueSessionAdvance $advance): RedirectResponse { abort_unless(app(CarePermissions::class)->handlesFloorCare($this->member($request)), 403); $this->authorizeAbility($request, 'appointments.view'); $organization = $this->organization($request); abort_unless($this->queueBridge->isEnabled($organization), 404); $member = $this->member($request); $owner = $this->ownerRef($request); $practitionerScope = app(OrganizationResolver::class)->practitionerScope($organization, $member); $branchId = $this->branchContext->resolve($request, $organization, $member); $practitionerId = $request->input('practitioner_id') ? (int) $request->input('practitioner_id') : null; if ($practitionerScope !== null) { abort_unless($practitionerScope !== [], 422); $practitionerId = $practitionerScope[0]; $prac = Practitioner::owned($owner)->find($practitionerId); $branchId = (int) ($prac?->branch_id ?: $branchId); } abort_unless($branchId > 0, 422); $context = CareQueueContexts::CONSULTATION; if ($practitionerId) { $prac = Practitioner::owned($owner)->find($practitionerId); $specialtyContext = $this->queueBridge->contextForPractitioner($organization, $prac); if ($specialtyContext !== CareQueueContexts::CONSULTATION) { $context = $specialtyContext; } } if ($request->filled('appointment_id')) { $current = Appointment::owned($owner) ->where('organization_id', $organization->id) ->findOrFail((int) $request->input('appointment_id')); $advance->endCurrentEncounter($current, $owner, $owner); } $result = $this->queueBridge->callNext( $organization, $context, $branchId, $member, $practitionerId, ); $ticket = $result['ticket'] ?? null; if (! $ticket) { $label = $context === CareQueueContexts::CONSULTATION ? 'consultation' : $context; return back()->with('info', "No patients waiting at your {$label} service point."); } $entity = $result['entity'] ?? null; if ($entity instanceof Appointment) { return $advance->redirectToOpenedPatient($entity, $ticket); } return back()->with('success', 'Called '.$ticket['ticket_number'].' — '.($ticket['customer_name'] ?? 'patient').'.'); } public function completeTicket(Request $request, Appointment $appointment): RedirectResponse { $this->authorizeAbility($request, 'consultations.manage'); $this->authorizeAppointment($request, $appointment); $organization = $this->organization($request); abort_unless($this->queueBridge->isEnabled($organization), 404); $ticket = $this->queueBridge->complete($organization, $appointment); if (! $ticket) { return back()->with('error', 'Could not complete queue ticket.'); } return back()->with('success', 'Completed '.$ticket['ticket_number'].'.'); } public function recall(Request $request, Appointment $appointment): RedirectResponse { $this->authorizeAbility($request, 'consultations.manage'); $this->authorizeAppointment($request, $appointment); $organization = $this->organization($request); abort_unless($this->queueBridge->isEnabled($organization), 404); $ticket = $this->queueBridge->recall($organization, $appointment); if (! $ticket) { return back()->with('error', 'Could not call patient again.'); } return back()->with('success', 'Called again '.$ticket['ticket_number'].'.'); } public function start(Request $request, Appointment $appointment): RedirectResponse { $this->authorizeAbility($request, 'consultations.manage'); $this->authorizeAppointment($request, $appointment); $practitionerId = $request->input('practitioner_id') ? (int) $request->input('practitioner_id') : $appointment->practitioner_id; try { $this->appointments->startConsultation( $appointment, $this->ownerRef($request), $practitionerId, $this->ownerRef($request), ); } catch (\InvalidArgumentException $e) { return back()->with('error', $e->getMessage()); } $consultation = $this->consultations->startFromAppointment( $appointment->fresh(), $this->ownerRef($request), $this->ownerRef($request), ); $organization = $this->organization($request); $appointment = $appointment->fresh(['visit', 'department']); $specialtyContext = $this->queueBridge->contextForAppointment($organization, $appointment); if ($specialtyContext !== CareQueueContexts::CONSULTATION && $appointment->visit) { $clinical = app(\App\Services\Care\SpecialtyClinicalRecordService::class); $shell = app(\App\Services\Care\SpecialtyShellService::class); $preferredTab = $specialtyContext === 'dentistry' ? 'odontogram' : (collect(array_keys($shell->workspaceTabs($specialtyContext))) ->first(fn (string $tab) => $clinical->recordTypeForTab($specialtyContext, $tab) !== null) ?? 'clinical_notes'); return redirect() ->route('care.specialty.workspace', [ 'module' => $specialtyContext, 'visit' => $appointment->visit, 'tab' => $preferredTab, ]) ->with('success', 'Encounter started.'); } return redirect()->route('care.consultations.show', $consultation) ->with('success', 'Consultation started.'); } protected function authorizeAppointment(Request $request, Appointment $appointment): void { $this->authorizeOwner($request, $appointment); abort_unless($appointment->organization_id === $this->organization($request)->id, 404); $this->authorizeBranch($request, (int) $appointment->branch_id); } }