Gate specialty mutate CTAs on Care abilities, not only module manage.
Deploy Ladill Care / deploy (push) Successful in 1m3s
Deploy Ladill Care / deploy (push) Successful in 1m3s
Nurses can manage limited specialties but lack consultations.manage, so hide Start/stage/chart edits that 403 while keeping queue/vitals/billing aligned with what those routes actually authorize. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -77,7 +77,13 @@ class EmergencyWorkspaceController extends Controller
|
|||||||
SpecialtyModuleService $modules,
|
SpecialtyModuleService $modules,
|
||||||
EmergencyVitalsService $vitals,
|
EmergencyVitalsService $vitals,
|
||||||
): RedirectResponse {
|
): RedirectResponse {
|
||||||
$this->authorizeAbility($request, 'consultations.manage');
|
$permissions = app(\App\Services\Care\CarePermissions::class);
|
||||||
|
$member = $this->member($request);
|
||||||
|
abort_unless(
|
||||||
|
$permissions->can($member, 'vitals.manage')
|
||||||
|
|| $permissions->can($member, 'consultations.manage'),
|
||||||
|
403,
|
||||||
|
);
|
||||||
$this->assertEmergencyManage($request, $modules);
|
$this->assertEmergencyManage($request, $modules);
|
||||||
$this->assertVisit($request, $visit);
|
$this->assertVisit($request, $visit);
|
||||||
|
|
||||||
|
|||||||
@@ -637,8 +637,20 @@ class SpecialtyModuleController extends Controller
|
|||||||
$services = $shell->provisionedServices($organization, $module);
|
$services = $shell->provisionedServices($organization, $module);
|
||||||
|
|
||||||
$permissions = app(\App\Services\Care\CarePermissions::class);
|
$permissions = app(\App\Services\Care\CarePermissions::class);
|
||||||
|
// Match every ability that mutate routes actually check — module canManage alone is not enough.
|
||||||
$canConsult = $permissions->can($member, 'consultations.manage') && $canManageSpecialty;
|
$canConsult = $permissions->can($member, 'consultations.manage') && $canManageSpecialty;
|
||||||
|
$canAdvanceStage = $canConsult;
|
||||||
|
$canStartConsultation = $canConsult;
|
||||||
|
$canCallNext = $canManageSpecialty;
|
||||||
|
$canManageClinical = $canManageSpecialty;
|
||||||
|
$canManageVitals = $canManageSpecialty && (
|
||||||
|
$permissions->can($member, 'vitals.manage')
|
||||||
|
|| $permissions->can($member, 'consultations.manage')
|
||||||
|
);
|
||||||
$canManageQueue = $permissions->can($member, 'appointments.manage') && $canManageSpecialty;
|
$canManageQueue = $permissions->can($member, 'appointments.manage') && $canManageSpecialty;
|
||||||
|
$canBookAppointments = $permissions->can($member, 'appointments.manage');
|
||||||
|
$canViewPatients = $permissions->can($member, 'patients.view');
|
||||||
|
$canViewAppointments = $permissions->can($member, 'appointments.view');
|
||||||
$branchLabel = $branchId
|
$branchLabel = $branchId
|
||||||
? (string) (\App\Models\Branch::owned($owner)->whereKey($branchId)->value('name') ?? '')
|
? (string) (\App\Models\Branch::owned($owner)->whereKey($branchId)->value('name') ?? '')
|
||||||
: '';
|
: '';
|
||||||
@@ -880,7 +892,15 @@ class SpecialtyModuleController extends Controller
|
|||||||
'practitionerScope' => $practitionerScope,
|
'practitionerScope' => $practitionerScope,
|
||||||
'lockToPractitioner' => $practitionerScope !== null,
|
'lockToPractitioner' => $practitionerScope !== null,
|
||||||
'canConsult' => $canConsult,
|
'canConsult' => $canConsult,
|
||||||
|
'canAdvanceStage' => $canAdvanceStage,
|
||||||
|
'canStartConsultation' => $canStartConsultation,
|
||||||
|
'canCallNext' => $canCallNext,
|
||||||
|
'canManageClinical' => $canManageClinical,
|
||||||
|
'canManageVitals' => $canManageVitals,
|
||||||
'canManageQueue' => $canManageQueue,
|
'canManageQueue' => $canManageQueue,
|
||||||
|
'canBookAppointments' => $canBookAppointments,
|
||||||
|
'canViewPatients' => $canViewPatients,
|
||||||
|
'canViewAppointments' => $canViewAppointments,
|
||||||
'canManageSpecialty' => $canManageSpecialty,
|
'canManageSpecialty' => $canManageSpecialty,
|
||||||
'canViewSpecialty' => $canViewSpecialty,
|
'canViewSpecialty' => $canViewSpecialty,
|
||||||
'canReferSpecialty' => $canReferSpecialty,
|
'canReferSpecialty' => $canReferSpecialty,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canConsult ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">Dental imaging</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Dental imaging</h3>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canConsult ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">Lab cases</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Lab cases</h3>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canConsult ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">Dental visit notes</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Dental visit notes</h3>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
$selected = old('tooth_code', request('tooth', ($dentalRows['upper'][0] ?? '16')));
|
$selected = old('tooth_code', request('tooth', ($dentalRows['upper'][0] ?? '16')));
|
||||||
$selectedData = $teethMap[$selected] ?? ['status' => 'present', 'surfaces' => [], 'conditions' => [], 'notes' => null];
|
$selectedData = $teethMap[$selected] ?? ['status' => 'present', 'surfaces' => [], 'conditions' => [], 'notes' => null];
|
||||||
$dentition = $dentalChart->dentition ?? 'adult';
|
$dentition = $dentalChart->dentition ?? 'adult';
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
$plan = $dentalPlan;
|
$plan = $dentalPlan;
|
||||||
$openItems = $plan?->items?->whereIn('status', ['proposed', 'accepted']) ?? collect();
|
$openItems = $plan?->items?->whereIn('status', ['proposed', 'accepted']) ?? collect();
|
||||||
$currentStage = $workspaceVisit->specialty_stage;
|
$currentStage = $workspaceVisit->specialty_stage;
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
$stageFlow = [
|
$stageFlow = [
|
||||||
'waiting' => ['next' => 'chair', 'label' => 'Seat at chair'],
|
'waiting' => ['next' => 'chair', 'label' => 'Seat at chair'],
|
||||||
'chair' => ['next' => 'procedure', 'label' => 'Start procedure'],
|
'chair' => ['next' => 'procedure', 'label' => 'Start procedure'],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
}
|
}
|
||||||
$teeth = array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []);
|
$teeth = array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []);
|
||||||
$probeTeeth = array_slice($teeth, 0, 8);
|
$probeTeeth = array_slice($teeth, 0, 8);
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@php
|
@php
|
||||||
$plan = $dentalPlan;
|
$plan = $dentalPlan;
|
||||||
$history = ($dentalPlanHistory ?? collect())->filter(fn ($p) => ! $plan || $p->id !== $plan->id);
|
$history = ($dentalPlanHistory ?? collect())->filter(fn ($p) => ! $plan || $p->id !== $plan->id);
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canConsult ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">Recalls</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Recalls</h3>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canConsult ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">Treat today</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Treat today</h3>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@php
|
@php
|
||||||
$record = $emergencyDisposition;
|
$record = $emergencyDisposition;
|
||||||
$payload = old('payload', $record?->payload ?? []);
|
$payload = old('payload', $record?->payload ?? []);
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
$currentStage = $workspaceVisit->specialty_stage;
|
$currentStage = $workspaceVisit->specialty_stage;
|
||||||
$stageFlow = $emergencyStageFlow ?? [];
|
$stageFlow = $emergencyStageFlow ?? [];
|
||||||
$latest = $emergencyLatestVitals;
|
$latest = $emergencyLatestVitals;
|
||||||
$canManage = $canManageSpecialty ?? true;
|
$canManage = $canConsult ?? false;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
@php $canManage = $canManageVitals ?? false; @endphp
|
||||||
|
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||||
|
|||||||
@@ -8,7 +8,14 @@
|
|||||||
$appointment = $workspaceVisit?->appointment;
|
$appointment = $workspaceVisit?->appointment;
|
||||||
$openConsultation = $appointment?->consultation;
|
$openConsultation = $appointment?->consultation;
|
||||||
$ticketCalled = $appointment && $appointment->queue_ticket_status === 'called';
|
$ticketCalled = $appointment && $appointment->queue_ticket_status === 'called';
|
||||||
$canStartConsultation = $appointment && in_array($appointment->status, [
|
$canCallNext = $canCallNext ?? ($canManageSpecialty ?? false);
|
||||||
|
$canConsult = $canConsult ?? false;
|
||||||
|
$canAdvanceStage = $canAdvanceStage ?? $canConsult;
|
||||||
|
$canStartConsultationFlag = $canStartConsultation ?? $canConsult;
|
||||||
|
$canBookAppointments = $canBookAppointments ?? false;
|
||||||
|
$canViewPatients = $canViewPatients ?? true;
|
||||||
|
$canViewAppointments = $canViewAppointments ?? true;
|
||||||
|
$appointmentReadyToStart = $appointment && in_array($appointment->status, [
|
||||||
\App\Models\Appointment::STATUS_WAITING,
|
\App\Models\Appointment::STATUS_WAITING,
|
||||||
\App\Models\Appointment::STATUS_CHECKED_IN,
|
\App\Models\Appointment::STATUS_CHECKED_IN,
|
||||||
], true);
|
], true);
|
||||||
@@ -17,7 +24,8 @@
|
|||||||
$appointment->status === \App\Models\Appointment::STATUS_IN_CONSULTATION
|
$appointment->status === \App\Models\Appointment::STATUS_IN_CONSULTATION
|
||||||
|| ($openConsultation && $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
|| ($openConsultation && $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||||
);
|
);
|
||||||
$canCompleteConsultation = $consultationInProgress
|
$canCompleteConsultation = $canConsult
|
||||||
|
&& $consultationInProgress
|
||||||
&& $openConsultation
|
&& $openConsultation
|
||||||
&& $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED;
|
&& $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED;
|
||||||
$chartTab = collect($workspaceTabs ?? [])
|
$chartTab = collect($workspaceTabs ?? [])
|
||||||
@@ -40,7 +48,7 @@
|
|||||||
$currentStage = $workspaceVisit?->specialty_stage;
|
$currentStage = $workspaceVisit?->specialty_stage;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if ($onWorkspace && ($canManageSpecialty ?? true))
|
@if ($onWorkspace && $canCallNext)
|
||||||
@include('care.partials.queue-ops', [
|
@include('care.partials.queue-ops', [
|
||||||
'queueIntegration' => $queueIntegration ?? null,
|
'queueIntegration' => $queueIntegration ?? null,
|
||||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||||
@@ -50,7 +58,7 @@
|
|||||||
])
|
])
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (($canManageSpecialty ?? true) && $ticketCalled && $appointment)
|
@if ($canConsult && $ticketCalled && $appointment)
|
||||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="{{ $btnAccent }}">Call again</button>
|
<button type="submit" class="{{ $btnAccent }}">Call again</button>
|
||||||
@@ -58,25 +66,25 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($workspaceVisit)
|
@if ($workspaceVisit)
|
||||||
@if (($canManageSpecialty ?? true) && $moduleKey === 'dentistry' && $currentStage && isset($stageFlow[$currentStage]))
|
@if ($canAdvanceStage && $moduleKey === 'dentistry' && $currentStage && isset($stageFlow[$currentStage]))
|
||||||
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
||||||
<button type="submit" class="{{ $btnAccent }}">{{ $stageFlow[$currentStage]['label'] }}</button>
|
<button type="submit" class="{{ $btnAccent }}">{{ $stageFlow[$currentStage]['label'] }}</button>
|
||||||
</form>
|
</form>
|
||||||
@elseif (($canManageSpecialty ?? true) && $moduleKey === 'dentistry' && ! $currentStage)
|
@elseif ($canAdvanceStage && $moduleKey === 'dentistry' && ! $currentStage)
|
||||||
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="stage" value="chair">
|
<input type="hidden" name="stage" value="chair">
|
||||||
<button type="submit" class="{{ $btnAccent }}">Seat at chair</button>
|
<button type="submit" class="{{ $btnAccent }}">Seat at chair</button>
|
||||||
</form>
|
</form>
|
||||||
@elseif (($canManageSpecialty ?? true) && $moduleKey === 'emergency' && $currentStage && isset($stageFlow[$currentStage]) && ($stageFlow[$currentStage]['next'] ?? null) !== $currentStage)
|
@elseif ($canAdvanceStage && $moduleKey === 'emergency' && $currentStage && isset($stageFlow[$currentStage]) && ($stageFlow[$currentStage]['next'] ?? null) !== $currentStage)
|
||||||
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
||||||
<button type="submit" class="{{ $btnAccent }}">{{ $stageFlow[$currentStage]['label'] }}</button>
|
<button type="submit" class="{{ $btnAccent }}">{{ $stageFlow[$currentStage]['label'] }}</button>
|
||||||
</form>
|
</form>
|
||||||
@elseif (($canManageSpecialty ?? true) && $moduleKey === 'emergency' && ! $currentStage)
|
@elseif ($canAdvanceStage && $moduleKey === 'emergency' && ! $currentStage)
|
||||||
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="stage" value="arrival">
|
<input type="hidden" name="stage" value="arrival">
|
||||||
@@ -84,7 +92,7 @@
|
|||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (($canManageSpecialty ?? true) && $canCompleteConsultation)
|
@if ($canCompleteConsultation)
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="{{ $btnPrimary }}"
|
class="{{ $btnPrimary }}"
|
||||||
@@ -99,7 +107,7 @@
|
|||||||
class="{{ $btnSecondary }}">
|
class="{{ $btnSecondary }}">
|
||||||
Open chart
|
Open chart
|
||||||
</a>
|
</a>
|
||||||
@elseif (($canManageSpecialty ?? true) && $canStartConsultation)
|
@elseif ($canStartConsultationFlag && $appointmentReadyToStart)
|
||||||
<form method="POST" action="{{ route('care.specialty.consultation.start', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="w-full" @click.stop>
|
<form method="POST" action="{{ route('care.specialty.consultation.start', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="w-full" @click.stop>
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="{{ $btnPrimary }}">
|
<button type="submit" class="{{ $btnPrimary }}">
|
||||||
@@ -116,12 +124,12 @@
|
|||||||
<button type="submit" class="{{ $btnAccent }}">Refer to specialty queue</button>
|
<button type="submit" class="{{ $btnAccent }}">Refer to specialty queue</button>
|
||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
@if ($workspaceVisit->patient)
|
@if ($canViewPatients && $workspaceVisit->patient)
|
||||||
<a href="{{ route('care.patients.show', $workspaceVisit->patient) }}" class="{{ $btnSecondary }}">
|
<a href="{{ route('care.patients.show', $workspaceVisit->patient) }}" class="{{ $btnSecondary }}">
|
||||||
Patient chart
|
Patient chart
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@if ($workspaceVisit->appointment)
|
@if ($canViewAppointments && $workspaceVisit->appointment)
|
||||||
<a href="{{ route('care.appointments.show', $workspaceVisit->appointment) }}" class="{{ $btnSecondary }}">
|
<a href="{{ route('care.appointments.show', $workspaceVisit->appointment) }}" class="{{ $btnSecondary }}">
|
||||||
Appointment
|
Appointment
|
||||||
</a>
|
</a>
|
||||||
@@ -132,6 +140,8 @@
|
|||||||
Back to queue
|
Back to queue
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('care.appointments.create') }}" class="{{ $btnAccent }}">
|
@if ($canBookAppointments)
|
||||||
Register / book
|
<a href="{{ route('care.appointments.create') }}" class="{{ $btnAccent }}">
|
||||||
</a>
|
Register / book
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (! ($canManageSpecialty ?? true))
|
@if (! ($canManageClinical ?? $canManageSpecialty ?? true))
|
||||||
<dl class="mt-4 grid gap-3 text-sm sm:grid-cols-2">
|
<dl class="mt-4 grid gap-3 text-sm sm:grid-cols-2">
|
||||||
@forelse ($clinicalFields as $field)
|
@forelse ($clinicalFields as $field)
|
||||||
@php
|
@php
|
||||||
|
|||||||
@@ -43,9 +43,9 @@
|
|||||||
@elseif ($activeTab === 'billing')
|
@elseif ($activeTab === 'billing')
|
||||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||||
<h3 class="text-sm font-semibold text-slate-900">
|
<h3 class="text-sm font-semibold text-slate-900">
|
||||||
{{ ($canManageSpecialty ?? true) ? 'Add specialty service' : 'Visit billing' }}
|
{{ ($canManageClinical ?? $canManageSpecialty ?? true) ? 'Add specialty service' : 'Visit billing' }}
|
||||||
</h3>
|
</h3>
|
||||||
@if ($canManageSpecialty ?? true)
|
@if ($canManageClinical ?? $canManageSpecialty ?? true)
|
||||||
<form method="POST" action="{{ route('care.specialty.services.add', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="mt-3 space-y-3">
|
<form method="POST" action="{{ route('care.specialty.services.add', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="mt-3 space-y-3">
|
||||||
@csrf
|
@csrf
|
||||||
<select name="service_code" required class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
<select name="service_code" required class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($workspaceVisit->bill)
|
@if ($workspaceVisit->bill)
|
||||||
<div @class(['mt-6 border-t border-slate-100 pt-4' => ($canManageSpecialty ?? true), 'mt-3' => ! ($canManageSpecialty ?? true)])>
|
<div @class(['mt-6 border-t border-slate-100 pt-4' => ($canManageClinical ?? $canManageSpecialty ?? true), 'mt-3' => ! ($canManageClinical ?? $canManageSpecialty ?? true)])>
|
||||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Current invoice {{ $workspaceVisit->bill->invoice_number }}</p>
|
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Current invoice {{ $workspaceVisit->bill->invoice_number }}</p>
|
||||||
<ul class="mt-2 space-y-1 text-sm">
|
<ul class="mt-2 space-y-1 text-sm">
|
||||||
@forelse ($workspaceVisit->bill->lineItems as $line)
|
@forelse ($workspaceVisit->bill->lineItems as $line)
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<a href="{{ route('care.bills.show', $workspaceVisit->bill) }}" class="mt-3 inline-flex text-sm font-medium text-indigo-600">Open bill</a>
|
<a href="{{ route('care.bills.show', $workspaceVisit->bill) }}" class="mt-3 inline-flex text-sm font-medium text-indigo-600">Open bill</a>
|
||||||
</div>
|
</div>
|
||||||
@elseif (! ($canManageSpecialty ?? true))
|
@elseif (! ($canManageClinical ?? $canManageSpecialty ?? true))
|
||||||
<p class="mt-3 text-sm text-slate-500">No invoice on this visit yet.</p>
|
<p class="mt-3 text-sm text-slate-500">No invoice on this visit yet.</p>
|
||||||
@endif
|
@endif
|
||||||
</section>
|
</section>
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
<h3 class="text-sm font-semibold text-slate-900">Documents</h3>
|
<h3 class="text-sm font-semibold text-slate-900">Documents</h3>
|
||||||
<p class="mt-1 text-xs text-slate-500">Specialty documents attach to the patient chart for this episode.</p>
|
<p class="mt-1 text-xs text-slate-500">Specialty documents attach to the patient chart for this episode.</p>
|
||||||
|
|
||||||
@if ($canManageSpecialty ?? true)
|
@if ($canManageClinical ?? $canManageSpecialty ?? true)
|
||||||
<form method="POST" action="{{ route('care.specialty.documents.upload', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
<form method="POST" action="{{ route('care.specialty.documents.upload', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="block w-full text-sm">
|
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="block w-full text-sm">
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<a href="{{ route('care.specialty.emergency.reports') }}" class="text-slate-600 hover:text-slate-900">Reports</a>
|
<a href="{{ route('care.specialty.emergency.reports') }}" class="text-slate-600 hover:text-slate-900">Reports</a>
|
||||||
@endif
|
@endif
|
||||||
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">History</a>
|
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">History</a>
|
||||||
@if ($canManageSpecialty ?? true)
|
@if ($canManageClinical ?? $canManageSpecialty ?? true)
|
||||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">Billing</a>
|
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">Billing</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@@ -34,6 +34,10 @@
|
|||||||
You have <strong>view-only</strong> access. Results and history are available; clinical edits require specialty access.
|
You have <strong>view-only</strong> access. Results and history are available; clinical edits require specialty access.
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@elseif (($canManageSpecialty ?? false) && ! ($canConsult ?? false))
|
||||||
|
<div class="rounded-xl border border-sky-200 bg-sky-50 px-4 py-3 text-sm text-sky-950">
|
||||||
|
You can work this specialty floor (queue, vitals, billing support) but starting encounters and clinical chart edits require a clinician with consultation access.
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="lg:grid lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-start lg:gap-6">
|
<div class="lg:grid lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-start lg:gap-6">
|
||||||
@@ -51,7 +55,9 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$completeConsultation = $workspaceVisit?->appointment?->consultation;
|
$completeConsultation = ($canConsult ?? false)
|
||||||
|
? $workspaceVisit?->appointment?->consultation
|
||||||
|
: null;
|
||||||
@endphp
|
@endphp
|
||||||
@if ($completeConsultation && $completeConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
@if ($completeConsultation && $completeConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||||
<x-confirm-dialog
|
<x-confirm-dialog
|
||||||
@@ -116,7 +122,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-wrap gap-3 text-sm">
|
<div class="flex flex-wrap gap-3 text-sm">
|
||||||
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">History</a>
|
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">History</a>
|
||||||
@if ($canManageSpecialty ?? true)
|
@if ($canManageClinical ?? $canManageSpecialty ?? true)
|
||||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">Billing</a>
|
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">Billing</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +136,7 @@
|
|||||||
'canConsult' => $canConsult ?? false,
|
'canConsult' => $canConsult ?? false,
|
||||||
'lockToPractitioner' => $lockToPractitioner ?? false,
|
'lockToPractitioner' => $lockToPractitioner ?? false,
|
||||||
'showFilters' => true,
|
'showFilters' => true,
|
||||||
'queueCallNextRoute' => ($canManageSpecialty ?? true) ? 'care.specialty.call-next' : null,
|
'queueCallNextRoute' => ($canCallNext ?? $canManageSpecialty ?? false) ? 'care.specialty.call-next' : null,
|
||||||
'queueCallNextParams' => ['module' => $moduleKey],
|
'queueCallNextParams' => ['module' => $moduleKey],
|
||||||
'startRouteName' => 'care.queue.start',
|
'startRouteName' => 'care.queue.start',
|
||||||
'inCareLabel' => 'In care',
|
'inCareLabel' => 'In care',
|
||||||
|
|||||||
@@ -340,4 +340,232 @@ class CareSpecialtyAccessTest extends TestCase
|
|||||||
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'cardiology'));
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'cardiology'));
|
||||||
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'emergency'));
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'emergency'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{0: \App\Models\User, 1: Member, 2: \App\Models\Visit}
|
||||||
|
*/
|
||||||
|
protected function dentistryVisitForRole(string $role, string $suffix): array
|
||||||
|
{
|
||||||
|
[$user, $member] = $this->makeStaff($role, $suffix);
|
||||||
|
if ($role === 'doctor') {
|
||||||
|
Practitioner::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'member_id' => $member->id,
|
||||||
|
'user_ref' => $user->public_id,
|
||||||
|
'name' => 'Dr '.$suffix,
|
||||||
|
'specialty' => 'Dentistry',
|
||||||
|
'is_active' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dept = Department::query()->where('type', 'dental')->firstOrFail();
|
||||||
|
$patient = Patient::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_number' => 'P-DEN-'.$suffix,
|
||||||
|
'first_name' => 'Efua',
|
||||||
|
'last_name' => 'Boateng',
|
||||||
|
'gender' => 'female',
|
||||||
|
'date_of_birth' => '1992-06-01',
|
||||||
|
]);
|
||||||
|
$visit = \App\Models\Visit::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
'specialty_stage' => 'waiting',
|
||||||
|
]);
|
||||||
|
Appointment::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'department_id' => $dept->id,
|
||||||
|
'visit_id' => $visit->id,
|
||||||
|
'type' => Appointment::TYPE_WALK_IN,
|
||||||
|
'status' => Appointment::STATUS_WAITING,
|
||||||
|
'scheduled_at' => now(),
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
'waiting_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [$user, $member, $visit];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_nurse_on_dentistry_does_not_see_consultation_mutate_actions(): void
|
||||||
|
{
|
||||||
|
[$nurseUser, $nurseMember, $visit] = $this->dentistryVisitForRole('nurse', 'den-nurse');
|
||||||
|
|
||||||
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $nurseMember, 'dentistry'));
|
||||||
|
$this->assertFalse(app(\App\Services\Care\CarePermissions::class)->can($nurseMember, 'consultations.manage'));
|
||||||
|
|
||||||
|
$html = $this->actingAs($nurseUser)
|
||||||
|
->get(route('care.specialty.workspace', ['module' => 'dentistry', 'visit' => $visit, 'tab' => 'overview']))
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('Efua Boateng')
|
||||||
|
->assertSee('consultation access')
|
||||||
|
->assertDontSee('Seat at chair')
|
||||||
|
->assertDontSee('Register / book')
|
||||||
|
->assertSee('Back to queue')
|
||||||
|
->assertSee('Patient chart')
|
||||||
|
->getContent();
|
||||||
|
|
||||||
|
$this->assertStringContainsString('Waiting', $html);
|
||||||
|
$this->assertStringNotContainsString(
|
||||||
|
route('care.specialty.dentistry.stage', $visit, absolute: false),
|
||||||
|
$html,
|
||||||
|
);
|
||||||
|
$this->assertStringNotContainsString(
|
||||||
|
route('care.specialty.consultation.start', ['module' => 'dentistry', 'visit' => $visit], absolute: false),
|
||||||
|
$html,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_nurse_dentistry_mutate_posts_are_forbidden(): void
|
||||||
|
{
|
||||||
|
[$nurseUser, , $visit] = $this->dentistryVisitForRole('nurse', 'den-nurse-post');
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->post(route('care.specialty.dentistry.stage', $visit), ['stage' => 'chair'])
|
||||||
|
->assertForbidden();
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->post(route('care.specialty.consultation.start', ['module' => 'dentistry', 'visit' => $visit]))
|
||||||
|
->assertForbidden();
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->post(route('care.specialty.dentistry.tooth', $visit), [
|
||||||
|
'tooth_code' => '16',
|
||||||
|
'status' => 'present',
|
||||||
|
])
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_doctor_on_dentistry_sees_and_can_advance_stage(): void
|
||||||
|
{
|
||||||
|
[$doctorUser, $doctorMember, $visit] = $this->dentistryVisitForRole('doctor', 'den-doc');
|
||||||
|
|
||||||
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $doctorMember, 'dentistry'));
|
||||||
|
$this->assertTrue(app(\App\Services\Care\CarePermissions::class)->can($doctorMember, 'consultations.manage'));
|
||||||
|
|
||||||
|
$this->actingAs($doctorUser)
|
||||||
|
->get(route('care.specialty.workspace', ['module' => 'dentistry', 'visit' => $visit, 'tab' => 'overview']))
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('Seat at chair')
|
||||||
|
->assertSee('Start')
|
||||||
|
->assertDontSee('consultation access');
|
||||||
|
|
||||||
|
$this->actingAs($doctorUser)
|
||||||
|
->post(route('care.specialty.dentistry.stage', $visit), ['stage' => 'chair'])
|
||||||
|
->assertRedirect();
|
||||||
|
|
||||||
|
$this->assertSame('chair', $visit->fresh()->specialty_stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_on_dentistry_sees_mutate_actions(): void
|
||||||
|
{
|
||||||
|
$dept = Department::query()->where('type', 'dental')->firstOrFail();
|
||||||
|
$patient = Patient::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_number' => 'P-DEN-ADMIN',
|
||||||
|
'first_name' => 'Admin',
|
||||||
|
'last_name' => 'Patient',
|
||||||
|
'gender' => 'male',
|
||||||
|
'date_of_birth' => '1980-01-01',
|
||||||
|
]);
|
||||||
|
$visit = \App\Models\Visit::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
'specialty_stage' => 'waiting',
|
||||||
|
]);
|
||||||
|
Appointment::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'department_id' => $dept->id,
|
||||||
|
'visit_id' => $visit->id,
|
||||||
|
'type' => Appointment::TYPE_WALK_IN,
|
||||||
|
'status' => Appointment::STATUS_WAITING,
|
||||||
|
'scheduled_at' => now(),
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
'waiting_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs($this->owner)
|
||||||
|
->get(route('care.specialty.workspace', ['module' => 'dentistry', 'visit' => $visit, 'tab' => 'overview']))
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('Seat at chair')
|
||||||
|
->assertSee('Start');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_nurse_can_save_emergency_vitals_with_vitals_manage(): void
|
||||||
|
{
|
||||||
|
[$nurseUser, $nurseMember] = $this->makeStaff('nurse', 'er-vitals');
|
||||||
|
$this->assertTrue(app(\App\Services\Care\CarePermissions::class)->can($nurseMember, 'vitals.manage'));
|
||||||
|
$this->assertFalse(app(\App\Services\Care\CarePermissions::class)->can($nurseMember, 'consultations.manage'));
|
||||||
|
|
||||||
|
$dept = Department::query()->where('type', 'emergency')->firstOrFail();
|
||||||
|
$patient = Patient::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_number' => 'P-ER-VITALS',
|
||||||
|
'first_name' => 'Kojo',
|
||||||
|
'last_name' => 'Mensah',
|
||||||
|
'gender' => 'male',
|
||||||
|
'date_of_birth' => '1988-01-01',
|
||||||
|
]);
|
||||||
|
$visit = \App\Models\Visit::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
]);
|
||||||
|
Appointment::create([
|
||||||
|
'owner_ref' => $this->owner->public_id,
|
||||||
|
'organization_id' => $this->organization->id,
|
||||||
|
'branch_id' => $this->branch->id,
|
||||||
|
'patient_id' => $patient->id,
|
||||||
|
'department_id' => $dept->id,
|
||||||
|
'visit_id' => $visit->id,
|
||||||
|
'type' => Appointment::TYPE_WALK_IN,
|
||||||
|
'status' => Appointment::STATUS_IN_CONSULTATION,
|
||||||
|
'scheduled_at' => now(),
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->get(route('care.specialty.workspace', ['module' => 'emergency', 'visit' => $visit, 'tab' => 'vitals']))
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('Save vitals')
|
||||||
|
->assertDontSee('Seat at chair')
|
||||||
|
->assertDontSee('Start triage');
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->post(route('care.specialty.emergency.vitals', $visit), [
|
||||||
|
'bp_systolic' => 120,
|
||||||
|
'bp_diastolic' => 80,
|
||||||
|
'pulse' => 72,
|
||||||
|
])
|
||||||
|
->assertRedirect();
|
||||||
|
|
||||||
|
$this->actingAs($nurseUser)
|
||||||
|
->post(route('care.specialty.emergency.stage', $visit), ['stage' => 'treatment'])
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,6 +294,8 @@ class CareSpecialtyShellTest extends TestCase
|
|||||||
|
|
||||||
public function test_workspace_includes_timeline_tab_and_complete_action(): void
|
public function test_workspace_includes_timeline_tab_and_complete_action(): void
|
||||||
{
|
{
|
||||||
|
Member::query()->where('user_ref', $this->owner->public_id)->update(['role' => 'doctor']);
|
||||||
|
|
||||||
app(SpecialtyModuleService::class)->activate(
|
app(SpecialtyModuleService::class)->activate(
|
||||||
$this->organization,
|
$this->organization,
|
||||||
$this->owner->public_id,
|
$this->owner->public_id,
|
||||||
@@ -378,6 +380,8 @@ class CareSpecialtyShellTest extends TestCase
|
|||||||
|
|
||||||
public function test_called_workspace_shows_call_again_until_session_starts(): void
|
public function test_called_workspace_shows_call_again_until_session_starts(): void
|
||||||
{
|
{
|
||||||
|
Member::query()->where('user_ref', $this->owner->public_id)->update(['role' => 'doctor']);
|
||||||
|
|
||||||
app(SpecialtyModuleService::class)->activate(
|
app(SpecialtyModuleService::class)->activate(
|
||||||
$this->organization,
|
$this->organization,
|
||||||
$this->owner->public_id,
|
$this->owner->public_id,
|
||||||
|
|||||||
Reference in New Issue
Block a user