diff --git a/app/Http/Controllers/Api/ConsoleApiController.php b/app/Http/Controllers/Api/ConsoleApiController.php index 603b515..01dc5a3 100644 --- a/app/Http/Controllers/Api/ConsoleApiController.php +++ b/app/Http/Controllers/Api/ConsoleApiController.php @@ -5,8 +5,10 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\Concerns\ScopesApiToAccount; use App\Http\Controllers\Controller; use App\Models\Counter; +use App\Models\Organization; use App\Models\ServiceQueue; use App\Models\Ticket; +use App\Services\Qms\PlanService; use App\Services\Qms\QueueEngine; use App\Services\Qms\TicketPresenter; use Illuminate\Http\JsonResponse; @@ -18,6 +20,7 @@ class ConsoleApiController extends Controller public function __construct( protected QueueEngine $engine, + protected PlanService $plans, ) {} public function show(Request $request, Counter $counter): JsonResponse @@ -25,6 +28,8 @@ class ConsoleApiController extends Controller $this->authorizeAbility($request, 'console.use'); $this->authorizeOwner($request, $counter); $owner = $this->ownerRef($request); + $organization = $this->organization($request); + $canTransfer = $this->plans->hasFeature($organization, 'transfer'); $counter->load(['serviceQueues', 'branch']); @@ -42,12 +47,14 @@ class ConsoleApiController extends Controller )]; }); - $allQueues = ServiceQueue::owned($owner) - ->where('organization_id', $counter->organization_id) - ->where('branch_id', $counter->branch_id) - ->where('is_active', true) - ->orderBy('name') - ->get(['uuid', 'name', 'prefix']); + $allQueues = $canTransfer + ? ServiceQueue::owned($owner) + ->where('organization_id', $counter->organization_id) + ->where('branch_id', $counter->branch_id) + ->where('is_active', true) + ->orderBy('name') + ->get(['uuid', 'name', 'prefix']) + : collect(); return response()->json([ 'data' => [ @@ -65,6 +72,7 @@ class ConsoleApiController extends Controller ]), 'waiting_by_queue' => $waitingByQueue, 'transfer_queues' => $allQueues, + 'can_transfer' => $canTransfer, ], ]); } @@ -137,6 +145,12 @@ class ConsoleApiController extends Controller ?string $reason, string $actor, ): Ticket { + $organization = Organization::query()->findOrFail($counter->organization_id); + abort_unless( + $this->plans->hasFeature($organization, 'transfer'), + 403, + 'Handing off tickets between queues requires Queue Pro.', + ); abort_if($toQueueUuid === '', 422, 'to_queue_uuid is required for transfer.'); $toQueue = ServiceQueue::where('uuid', $toQueueUuid)->firstOrFail(); diff --git a/app/Http/Controllers/Api/TicketController.php b/app/Http/Controllers/Api/TicketController.php index 424294c..9646e01 100644 --- a/app/Http/Controllers/Api/TicketController.php +++ b/app/Http/Controllers/Api/TicketController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Api\Concerns\ScopesApiToAccount; use App\Http\Controllers\Controller; use App\Models\ServiceQueue; use App\Models\Ticket; +use App\Services\Qms\PlanService; use App\Services\Qms\QueueEngine; use App\Services\Qms\TicketPresenter; use Illuminate\Http\JsonResponse; @@ -17,6 +18,7 @@ class TicketController extends Controller public function __construct( protected QueueEngine $engine, + protected PlanService $plans, ) {} public function index(Request $request): JsonResponse @@ -108,6 +110,11 @@ class TicketController extends Controller ]); if ($validated['action'] === 'transfer') { + abort_unless( + $this->plans->hasFeature($this->organization($request), 'transfer'), + 403, + 'Handing off tickets between queues requires Queue Pro.', + ); $toQueue = ServiceQueue::where('uuid', $validated['to_queue_id'])->firstOrFail(); $this->authorizeOwner($request, $toQueue); $result = $this->engine->transfer($ticket, $toQueue, null, $validated['reason'] ?? null, $actor); diff --git a/app/Http/Controllers/Qms/ConsoleController.php b/app/Http/Controllers/Qms/ConsoleController.php index d2de24e..268f0b2 100644 --- a/app/Http/Controllers/Qms/ConsoleController.php +++ b/app/Http/Controllers/Qms/ConsoleController.php @@ -7,7 +7,7 @@ use App\Http\Controllers\Qms\Concerns\ScopesToAccount; use App\Models\Counter; use App\Models\ServiceQueue; use App\Models\Ticket; -use App\Services\Qms\OrganizationResolver; +use App\Services\Qms\PlanService; use App\Services\Qms\QueueEngine; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -19,6 +19,7 @@ class ConsoleController extends Controller public function __construct( protected QueueEngine $engine, + protected PlanService $plans, ) {} public function show(Request $request, Counter $counter): View @@ -26,6 +27,7 @@ class ConsoleController extends Controller $this->authorizeAbility($request, 'console.use'); $this->authorizeOwner($request, $counter); $owner = $this->ownerRef($request); + $organization = $this->organization($request); $counter->load(['serviceQueues', 'branch']); $queues = $counter->serviceQueues; @@ -41,14 +43,24 @@ class ConsoleController extends Controller return [$queue->id => $this->engine->waitingTickets($queue, 10)]; }); - $allQueues = ServiceQueue::owned($owner) - ->where('organization_id', $counter->organization_id) - ->where('branch_id', $counter->branch_id) - ->where('is_active', true) - ->orderBy('name') - ->get(); + $canTransfer = $this->plans->hasFeature($organization, 'transfer'); + $allQueues = $canTransfer + ? ServiceQueue::owned($owner) + ->where('organization_id', $counter->organization_id) + ->where('branch_id', $counter->branch_id) + ->where('is_active', true) + ->orderBy('name') + ->get() + : collect(); - return view('qms.console.show', compact('counter', 'queues', 'currentTicket', 'waitingByQueue', 'allQueues')); + return view('qms.console.show', compact( + 'counter', + 'queues', + 'currentTicket', + 'waitingByQueue', + 'allQueues', + 'canTransfer', + )); } public function action(Request $request, Counter $counter): RedirectResponse @@ -65,6 +77,12 @@ class ConsoleController extends Controller 'reason' => ['nullable', 'string', 'max:500'], ]); + if ($validated['action'] === 'transfer' && ! $this->plans->hasFeature($this->organization($request), 'transfer')) { + return redirect() + ->route('qms.pro.index') + ->with('error', 'Handing off tickets between queues requires Queue Pro.'); + } + try { $message = match ($validated['action']) { 'call_next' => $this->handleCallNext($counter, (int) $validated['queue_id'], $actor), diff --git a/config/qms.php b/config/qms.php index 76c60e8..2832ba9 100644 --- a/config/qms.php +++ b/config/qms.php @@ -202,6 +202,7 @@ return [ 'displays', 'branches', 'team', + 'transfer', 'routing', 'integrations', 'reports', @@ -219,6 +220,7 @@ return [ 'displays', 'branches', 'team', + 'transfer', 'routing', 'integrations', 'reports', diff --git a/resources/views/qms/console/show.blade.php b/resources/views/qms/console/show.blade.php index 2835218..d84740b 100644 --- a/resources/views/qms/console/show.blade.php +++ b/resources/views/qms/console/show.blade.php @@ -57,7 +57,7 @@ @php $handoffQueues = $allQueues->where('id', '!=', $currentTicket->service_queue_id)->values(); @endphp - @if ($handoffQueues->isNotEmpty()) + @if ($canTransfer && $handoffQueues->isNotEmpty())
@csrf @@ -82,6 +82,15 @@
+ @elseif (! $canTransfer) +
+

+ Hand off to another queue + Pro +

+

Pass customers between services while keeping their ticket number. Available on Queue Pro.

+ Upgrade to Pro → +
@endif diff --git a/resources/views/qms/pro/index.blade.php b/resources/views/qms/pro/index.blade.php index 3345c9a..cdd929c 100644 --- a/resources/views/qms/pro/index.blade.php +++ b/resources/views/qms/pro/index.blade.php @@ -96,6 +96,7 @@