Reuse the GP consultation prescribe modal, gate with prescriptions.manage, and return to the specialty workspace after create. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -193,6 +193,30 @@ class PrescriptionController extends Controller
|
||||
->with('success', 'Prescription created.');
|
||||
}
|
||||
|
||||
if ($request->input('return_to') === 'specialty') {
|
||||
$module = (string) $request->input('specialty_module', '');
|
||||
$visitId = (int) $request->input('specialty_visit_id', 0);
|
||||
$modules = app(\App\Services\Care\SpecialtyModuleService::class);
|
||||
$visit = $consultation->visit_id
|
||||
? \App\Models\Visit::query()->find($consultation->visit_id)
|
||||
: null;
|
||||
|
||||
if (
|
||||
$module !== ''
|
||||
&& $modules->definition($module)
|
||||
&& $visit
|
||||
&& (int) $visit->id === $visitId
|
||||
&& (int) $visit->id === (int) $consultation->visit_id
|
||||
) {
|
||||
return redirect()
|
||||
->route('care.specialty.workspace', [
|
||||
'module' => $module,
|
||||
'visit' => $visit,
|
||||
])
|
||||
->with('success', 'Prescription created.');
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('care.prescriptions.show', array_filter([
|
||||
'prescription' => $prescription,
|
||||
...$return->query($consultation),
|
||||
|
||||
@@ -829,6 +829,8 @@ class SpecialtyModuleController extends Controller
|
||||
&& $canManageSpecialty
|
||||
&& $permissions->can($member, 'blood_bank.manage'));
|
||||
$canStartConsultation = $canConsult;
|
||||
// Same ability as GP consultation chart — not consultations.manage alone.
|
||||
$canPrescribe = $permissions->can($member, 'prescriptions.manage') && $canManageSpecialty;
|
||||
$canCallNext = $canManageSpecialty;
|
||||
$canManageClinical = $canManageSpecialty;
|
||||
$canManageVitals = $canManageSpecialty && (
|
||||
@@ -1176,6 +1178,7 @@ class SpecialtyModuleController extends Controller
|
||||
'canConsult' => $canConsult,
|
||||
'canAdvanceStage' => $canAdvanceStage,
|
||||
'canStartConsultation' => $canStartConsultation,
|
||||
'canPrescribe' => $canPrescribe,
|
||||
'canCallNext' => $canCallNext,
|
||||
'canManageClinical' => $canManageClinical,
|
||||
'canManageVitals' => $canManageVitals,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{{-- Shared prescription line-item form body (create page + consultation modal). --}}
|
||||
{{-- Shared prescription line-item form body (create page + consultation / specialty modal). --}}
|
||||
@php
|
||||
$routes = $routes ?? config('care.medication_routes', []);
|
||||
$compact = $compact ?? false;
|
||||
$returnTo = $returnTo ?? ($compact ? 'consultation' : null);
|
||||
@endphp
|
||||
<form
|
||||
method="POST"
|
||||
@@ -50,8 +51,12 @@
|
||||
<textarea name="notes" rows="2" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></textarea>
|
||||
<input type="hidden" name="activate" value="1">
|
||||
<input type="hidden" name="from_consultation" value="{{ $consultation->uuid }}">
|
||||
@if ($compact)
|
||||
<input type="hidden" name="return_to" value="consultation">
|
||||
@if ($returnTo)
|
||||
<input type="hidden" name="return_to" value="{{ $returnTo }}">
|
||||
@if ($returnTo === 'specialty' && ! empty($specialtyModule) && ! empty($specialtyVisit))
|
||||
<input type="hidden" name="specialty_module" value="{{ $specialtyModule }}">
|
||||
<input type="hidden" name="specialty_visit_id" value="{{ $specialtyVisit->id }}">
|
||||
@endif
|
||||
@endif
|
||||
</section>
|
||||
<div @class(['flex flex-col-reverse gap-2 sm:flex-row sm:justify-end' => $compact, 'flex gap-3' => ! $compact])>
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
$btnAccent = $btn.' border border-indigo-200 bg-indigo-50 text-indigo-900 hover:bg-indigo-100';
|
||||
$onWorkspace = ($section ?? '') === 'workspace';
|
||||
$appointment = $workspaceVisit?->appointment;
|
||||
$openConsultation = $appointment?->consultation;
|
||||
$openConsultation = $appointment?->consultation
|
||||
?? $workspaceVisit?->consultations?->firstWhere('status', '!=', \App\Models\Consultation::STATUS_COMPLETED);
|
||||
$ticketCalled = $appointment && $appointment->queue_ticket_status === 'called';
|
||||
$canCallNext = $canCallNext ?? ($canManageSpecialty ?? false);
|
||||
$canConsult = $canConsult ?? false;
|
||||
$canAdvanceStage = $canAdvanceStage ?? $canConsult;
|
||||
$canStartConsultationFlag = $canStartConsultation ?? $canConsult;
|
||||
$canPrescribe = $canPrescribe ?? false;
|
||||
$canBookAppointments = $canBookAppointments ?? false;
|
||||
$canViewPatients = $canViewPatients ?? true;
|
||||
$canViewAppointments = $canViewAppointments ?? true;
|
||||
@@ -102,6 +104,16 @@
|
||||
</button>
|
||||
@endif
|
||||
|
||||
@if ($canPrescribe && $openConsultation && $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||
<button
|
||||
type="button"
|
||||
class="{{ $btnSecondary }}"
|
||||
@click.stop="$dispatch('open-modal', {{ \Illuminate\Support\Js::from('prescribe-'.$openConsultation->id) }})"
|
||||
>
|
||||
{{ $actions['prescribe'] ?? 'Prescribe' }}
|
||||
</button>
|
||||
@endif
|
||||
|
||||
@if ($consultationInProgress)
|
||||
<a href="{{ route('care.specialty.workspace', ['module' => $moduleKey, 'visit' => $workspaceVisit, 'tab' => $chartTab]) }}"
|
||||
class="{{ $btnSecondary }}">
|
||||
|
||||
@@ -80,7 +80,12 @@
|
||||
|
||||
@php
|
||||
$completeConsultation = ($canConsult ?? false)
|
||||
? $workspaceVisit?->appointment?->consultation
|
||||
? ($workspaceVisit?->appointment?->consultation
|
||||
?? $workspaceVisit?->consultations?->firstWhere('status', '!=', \App\Models\Consultation::STATUS_COMPLETED))
|
||||
: null;
|
||||
$prescribeConsultation = ($canPrescribe ?? false)
|
||||
? ($workspaceVisit?->appointment?->consultation
|
||||
?? $workspaceVisit?->consultations?->firstWhere('status', '!=', \App\Models\Consultation::STATUS_COMPLETED))
|
||||
: null;
|
||||
@endphp
|
||||
@if ($completeConsultation && $completeConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||
@@ -93,6 +98,29 @@
|
||||
variant="primary"
|
||||
/>
|
||||
@endif
|
||||
@if ($prescribeConsultation && $prescribeConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||
<x-modal :name="'prescribe-'.$prescribeConsultation->id" maxWidth="2xl">
|
||||
<div class="px-5 pb-6 pt-2 sm:px-6 sm:pb-6 sm:pt-4">
|
||||
<h2 class="pr-8 text-lg font-semibold text-slate-900">New prescription</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
{{ ($workspaceVisit->patient ?? $prescribeConsultation->patient)?->fullName() }}
|
||||
@if (($workspaceVisit->patient ?? $prescribeConsultation->patient)?->patient_number)
|
||||
({{ ($workspaceVisit->patient ?? $prescribeConsultation->patient)->patient_number }})
|
||||
@endif
|
||||
</p>
|
||||
<div class="mt-4 max-h-[70vh] overflow-y-auto pr-1">
|
||||
@include('care.prescriptions.partials.form', [
|
||||
'consultation' => $prescribeConsultation,
|
||||
'routes' => config('care.medication_routes'),
|
||||
'compact' => true,
|
||||
'returnTo' => 'specialty',
|
||||
'specialtyModule' => $moduleKey,
|
||||
'specialtyVisit' => $workspaceVisit,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</x-modal>
|
||||
@endif
|
||||
@elseif ($section === 'history')
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="flex items-start gap-2.5">
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Tests\Feature;
|
||||
use App\Http\Middleware\EnsurePlatformSession;
|
||||
use App\Models\Appointment;
|
||||
use App\Models\Branch;
|
||||
use App\Models\Consultation;
|
||||
use App\Models\Department;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
@@ -864,4 +865,126 @@ class CareSpecialtyAccessTest extends TestCase
|
||||
->post(route('care.specialty.emergency.stage', $visit), ['stage' => 'treatment'])
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_doctor_with_prescriptions_manage_sees_prescribe_on_specialty_workspace(): void
|
||||
{
|
||||
[$doctorUser, $doctorMember, $visit] = $this->dentistryVisitForRole('doctor', 'den-rx');
|
||||
$appointment = $visit->appointment()->firstOrFail();
|
||||
$appointment->update([
|
||||
'status' => Appointment::STATUS_IN_CONSULTATION,
|
||||
'started_at' => now(),
|
||||
'queue_ticket_status' => 'serving',
|
||||
]);
|
||||
$consultation = Consultation::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'visit_id' => $visit->id,
|
||||
'appointment_id' => $appointment->id,
|
||||
'patient_id' => $visit->patient_id,
|
||||
'status' => Consultation::STATUS_DRAFT,
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
$permissions = app(\App\Services\Care\CarePermissions::class);
|
||||
$this->assertTrue($permissions->can($doctorMember, 'prescriptions.manage'));
|
||||
$this->assertTrue($this->modules->memberCanManage($this->organization, $doctorMember, 'dentistry'));
|
||||
|
||||
$html = $this->actingAs($doctorUser)
|
||||
->get(route('care.specialty.workspace', [
|
||||
'module' => 'dentistry',
|
||||
'visit' => $visit,
|
||||
'tab' => 'overview',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSee('Prescribe')
|
||||
->assertSee('New prescription')
|
||||
->getContent();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
route('care.prescriptions.store', $consultation, absolute: false),
|
||||
$html,
|
||||
);
|
||||
$this->assertStringContainsString('name="return_to" value="specialty"', $html);
|
||||
$this->assertStringContainsString('name="specialty_module" value="dentistry"', $html);
|
||||
}
|
||||
|
||||
public function test_nurse_without_prescriptions_manage_does_not_see_prescribe_cta(): void
|
||||
{
|
||||
[$nurseUser, $nurseMember, $visit] = $this->dentistryVisitForRole('nurse', 'den-no-rx');
|
||||
$appointment = $visit->appointment()->firstOrFail();
|
||||
$appointment->update([
|
||||
'status' => Appointment::STATUS_IN_CONSULTATION,
|
||||
'started_at' => now(),
|
||||
'queue_ticket_status' => 'serving',
|
||||
]);
|
||||
Consultation::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'visit_id' => $visit->id,
|
||||
'appointment_id' => $appointment->id,
|
||||
'patient_id' => $visit->patient_id,
|
||||
'status' => Consultation::STATUS_DRAFT,
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
$permissions = app(\App\Services\Care\CarePermissions::class);
|
||||
$this->assertTrue($this->modules->memberCanManage($this->organization, $nurseMember, 'dentistry'));
|
||||
$this->assertFalse($permissions->can($nurseMember, 'prescriptions.manage'));
|
||||
|
||||
$this->actingAs($nurseUser)
|
||||
->get(route('care.specialty.workspace', [
|
||||
'module' => 'dentistry',
|
||||
'visit' => $visit,
|
||||
'tab' => 'overview',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertDontSee('Prescribe')
|
||||
->assertDontSee('New prescription')
|
||||
->assertDontSee(route('care.prescriptions.store', Consultation::query()->where('visit_id', $visit->id)->first(), absolute: false));
|
||||
}
|
||||
|
||||
public function test_specialty_prescribe_store_returns_to_workspace(): void
|
||||
{
|
||||
[$doctorUser, , $visit] = $this->dentistryVisitForRole('doctor', 'den-rx-store');
|
||||
$appointment = $visit->appointment()->firstOrFail();
|
||||
$appointment->update([
|
||||
'status' => Appointment::STATUS_IN_CONSULTATION,
|
||||
'started_at' => now(),
|
||||
'queue_ticket_status' => 'serving',
|
||||
]);
|
||||
$consultation = Consultation::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'visit_id' => $visit->id,
|
||||
'appointment_id' => $appointment->id,
|
||||
'patient_id' => $visit->patient_id,
|
||||
'status' => Consultation::STATUS_DRAFT,
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
$this->actingAs($doctorUser)
|
||||
->post(route('care.prescriptions.store', $consultation), [
|
||||
'activate' => true,
|
||||
'return_to' => 'specialty',
|
||||
'specialty_module' => 'dentistry',
|
||||
'specialty_visit_id' => $visit->id,
|
||||
'from_consultation' => $consultation->uuid,
|
||||
'items' => [
|
||||
[
|
||||
'name' => 'Amoxicillin 500mg',
|
||||
'dosage' => '1 capsule',
|
||||
'frequency' => 'TDS',
|
||||
'duration' => '5 days',
|
||||
'route' => 'oral',
|
||||
],
|
||||
],
|
||||
])
|
||||
->assertRedirect(route('care.specialty.workspace', [
|
||||
'module' => 'dentistry',
|
||||
'visit' => $visit,
|
||||
]));
|
||||
|
||||
$this->assertDatabaseHas('care_prescriptions', [
|
||||
'consultation_id' => $consultation->id,
|
||||
'visit_id' => $visit->id,
|
||||
'patient_id' => $visit->patient_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user