Deploy Ladill Queue / deploy (push) Successful in 2m17s
Counters gain destination/staff metadata; tickets can be pre-assigned to a service point with assigned_only queues for healthcare while shared_pool preserves generic bank/government behavior. Display and announcements expose ticket, staff, and destination clearly for Care and public boards. Co-authored-by: Cursor <cursoragent@cursor.com>
223 lines
9.0 KiB
PHP
223 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Api\Concerns\ScopesApiToAccount;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Branch;
|
|
use App\Models\Counter;
|
|
use App\Models\ServiceQueue;
|
|
use App\Services\Qms\AuditLogger;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class CounterController extends Controller
|
|
{
|
|
use ScopesApiToAccount;
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$this->authorizeAbility($request, 'counters.view');
|
|
$owner = $this->ownerRef($request);
|
|
$organization = $this->organization($request);
|
|
|
|
$counters = Counter::owned($owner)
|
|
->where('organization_id', $organization->id)
|
|
->with(['serviceQueues', 'branch'])
|
|
->orderBy('name')
|
|
->get()
|
|
->map(fn (Counter $c) => $this->serializeCounter($c));
|
|
|
|
return response()->json(['data' => $counters]);
|
|
}
|
|
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$this->authorizeAbility($request, 'counters.manage');
|
|
$owner = $this->ownerRef($request);
|
|
$organization = $this->organization($request);
|
|
|
|
$validated = $request->validate([
|
|
'name' => ['required', 'string', 'max:255'],
|
|
'code' => ['nullable', 'string', 'max:50'],
|
|
'destination' => ['nullable', 'string', 'max:255'],
|
|
'staff_ref' => ['nullable', 'string', 'max:191'],
|
|
'staff_display_name' => ['nullable', 'string', 'max:255'],
|
|
'branch_id' => ['nullable', 'integer'],
|
|
'branch_name' => ['nullable', 'string', 'max:255'],
|
|
'department_id' => ['nullable', 'integer'],
|
|
'queue_ids' => ['nullable', 'array'],
|
|
'queue_ids.*' => ['uuid'],
|
|
'queue_uuids' => ['nullable', 'array'],
|
|
'queue_uuids.*' => ['uuid'],
|
|
'external_key' => ['nullable', 'string', 'max:191'],
|
|
'is_active' => ['nullable', 'boolean'],
|
|
]);
|
|
|
|
$branch = $this->resolveBranch($request, $organization->id, $owner, $validated);
|
|
abort_unless($branch, 422, 'A valid branch_id or branch_name is required.');
|
|
|
|
$externalKey = isset($validated['external_key']) ? trim((string) $validated['external_key']) : '';
|
|
if ($externalKey !== '') {
|
|
$existing = Counter::owned($owner)
|
|
->where('organization_id', $organization->id)
|
|
->where('settings->external_key', $externalKey)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$existing->update([
|
|
'name' => $validated['name'],
|
|
'code' => $validated['code'] ?? $existing->code,
|
|
'destination' => array_key_exists('destination', $validated)
|
|
? $validated['destination']
|
|
: $existing->destination,
|
|
'staff_ref' => array_key_exists('staff_ref', $validated)
|
|
? $validated['staff_ref']
|
|
: $existing->staff_ref,
|
|
'staff_display_name' => array_key_exists('staff_display_name', $validated)
|
|
? $validated['staff_display_name']
|
|
: $existing->staff_display_name,
|
|
'branch_id' => $branch->id,
|
|
'department_id' => $validated['department_id'] ?? $existing->department_id,
|
|
'is_active' => array_key_exists('is_active', $validated)
|
|
? (bool) $validated['is_active']
|
|
: true,
|
|
'settings' => array_merge($existing->settings ?? [], ['external_key' => $externalKey]),
|
|
]);
|
|
$this->syncQueues($existing, $owner, $validated);
|
|
AuditLogger::record($owner, 'counter.updated', $organization->id, $owner, Counter::class, $existing->id);
|
|
|
|
return response()->json(['data' => $this->serializeCounter($existing->fresh(['serviceQueues', 'branch']))]);
|
|
}
|
|
}
|
|
|
|
$counter = Counter::create([
|
|
'owner_ref' => $owner,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $branch->id,
|
|
'department_id' => $validated['department_id'] ?? null,
|
|
'name' => $validated['name'],
|
|
'code' => $validated['code'] ?? null,
|
|
'destination' => $validated['destination'] ?? null,
|
|
'staff_ref' => $validated['staff_ref'] ?? null,
|
|
'staff_display_name' => $validated['staff_display_name'] ?? null,
|
|
'status' => 'offline',
|
|
'is_active' => array_key_exists('is_active', $validated) ? (bool) $validated['is_active'] : true,
|
|
'settings' => $externalKey !== '' ? ['external_key' => $externalKey] : null,
|
|
]);
|
|
|
|
$this->syncQueues($counter, $owner, $validated);
|
|
|
|
AuditLogger::record($owner, 'counter.created', $organization->id, $owner, Counter::class, $counter->id);
|
|
|
|
return response()->json(['data' => $this->serializeCounter($counter->fresh(['serviceQueues', 'branch']))], 201);
|
|
}
|
|
|
|
public function update(Request $request, Counter $counter): JsonResponse
|
|
{
|
|
$this->authorizeAbility($request, 'counters.manage');
|
|
$this->authorizeOwner($request, $counter);
|
|
|
|
$validated = $request->validate([
|
|
'name' => ['sometimes', 'string', 'max:255'],
|
|
'code' => ['nullable', 'string', 'max:50'],
|
|
'destination' => ['nullable', 'string', 'max:255'],
|
|
'staff_ref' => ['nullable', 'string', 'max:191'],
|
|
'staff_display_name' => ['nullable', 'string', 'max:255'],
|
|
'is_active' => ['sometimes', 'boolean'],
|
|
'queue_ids' => ['nullable', 'array'],
|
|
'queue_ids.*' => ['uuid'],
|
|
'queue_uuids' => ['nullable', 'array'],
|
|
'queue_uuids.*' => ['uuid'],
|
|
]);
|
|
|
|
$counter->update(array_filter([
|
|
'name' => $validated['name'] ?? null,
|
|
'code' => array_key_exists('code', $validated) ? $validated['code'] : null,
|
|
'destination' => array_key_exists('destination', $validated) ? $validated['destination'] : null,
|
|
'staff_ref' => array_key_exists('staff_ref', $validated) ? $validated['staff_ref'] : null,
|
|
'staff_display_name' => array_key_exists('staff_display_name', $validated)
|
|
? $validated['staff_display_name']
|
|
: null,
|
|
'is_active' => array_key_exists('is_active', $validated) ? (bool) $validated['is_active'] : null,
|
|
], fn ($v) => $v !== null));
|
|
|
|
if (array_key_exists('queue_ids', $validated) || array_key_exists('queue_uuids', $validated)) {
|
|
$this->syncQueues($counter, $this->ownerRef($request), $validated);
|
|
}
|
|
|
|
AuditLogger::record(
|
|
$this->ownerRef($request),
|
|
'counter.updated',
|
|
$counter->organization_id,
|
|
$this->ownerRef($request),
|
|
Counter::class,
|
|
$counter->id,
|
|
);
|
|
|
|
return response()->json(['data' => $this->serializeCounter($counter->fresh(['serviceQueues', 'branch']))]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $validated
|
|
*/
|
|
protected function resolveBranch(Request $request, int $organizationId, string $owner, array $validated): ?Branch
|
|
{
|
|
if (! empty($validated['branch_id'])) {
|
|
return Branch::owned($owner)
|
|
->where('organization_id', $organizationId)
|
|
->where('id', (int) $validated['branch_id'])
|
|
->first();
|
|
}
|
|
|
|
$branchName = trim((string) ($validated['branch_name'] ?? ''));
|
|
if ($branchName === '') {
|
|
return null;
|
|
}
|
|
|
|
return Branch::owned($owner)
|
|
->where('organization_id', $organizationId)
|
|
->whereRaw('LOWER(name) = ?', [strtolower($branchName)])
|
|
->first();
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $validated
|
|
*/
|
|
protected function syncQueues(Counter $counter, string $owner, array $validated): void
|
|
{
|
|
$uuids = $validated['queue_uuids'] ?? $validated['queue_ids'] ?? null;
|
|
if ($uuids === null) {
|
|
return;
|
|
}
|
|
|
|
$queueIds = ServiceQueue::owned($owner)->whereIn('uuid', $uuids)->pluck('id');
|
|
$counter->serviceQueues()->sync($queueIds);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function serializeCounter(Counter $c): array
|
|
{
|
|
return [
|
|
'uuid' => $c->uuid,
|
|
'name' => $c->name,
|
|
'code' => $c->code,
|
|
'destination' => $c->displayDestination(),
|
|
'staff_ref' => $c->staff_ref,
|
|
'staff_display_name' => $c->staff_display_name,
|
|
'status' => $c->status,
|
|
'is_active' => $c->is_active,
|
|
'branch' => $c->branch?->name,
|
|
'department_id' => $c->department_id,
|
|
'external_key' => data_get($c->settings, 'external_key'),
|
|
'queues' => $c->serviceQueues->map(fn (ServiceQueue $q) => [
|
|
'uuid' => $q->uuid,
|
|
'name' => $q->name,
|
|
'prefix' => $q->prefix,
|
|
])->values()->all(),
|
|
];
|
|
}
|
|
}
|