Deploy Ladill Care / deploy (push) Successful in 1m45s
When Queue integration is on, role pages issue tickets into their own department queues and drive Call next → Serve → Complete on the native lists. Integration off leaves existing UI unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
164 lines
5.7 KiB
PHP
164 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Care;
|
|
|
|
use App\Models\Branch;
|
|
use App\Models\Organization;
|
|
use App\Services\Queue\QueueClient;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Idempotently provisions Ladill Queue queues/counters for Care departments per branch.
|
|
*/
|
|
class CareQueueProvisioner
|
|
{
|
|
public function __construct(
|
|
protected QueueClient $queue,
|
|
protected PlanService $plans,
|
|
) {}
|
|
|
|
public function shouldProvision(Organization $organization): bool
|
|
{
|
|
return $this->plans->canUseQueueIntegration($organization)
|
|
&& (bool) data_get($organization->settings, 'queue_integration_enabled')
|
|
&& $this->queue->configured();
|
|
}
|
|
|
|
/**
|
|
* Provision core department queues for all org branches. Returns updated settings fragment.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function provisionOrganization(Organization $organization): array
|
|
{
|
|
if (! $this->shouldProvision($organization)) {
|
|
return (array) data_get($organization->settings, 'department_queue_provisioning', []);
|
|
}
|
|
|
|
$owner = (string) $organization->owner_ref;
|
|
$branches = Branch::owned($owner)
|
|
->where('organization_id', $organization->id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->get();
|
|
|
|
$provisioning = (array) data_get($organization->settings, 'department_queue_provisioning', []);
|
|
|
|
foreach (CareQueueContexts::departments() as $context => $meta) {
|
|
$queues = is_array($provisioning[$context]['queues'] ?? null)
|
|
? $provisioning[$context]['queues']
|
|
: [];
|
|
$byBranch = collect($queues)->keyBy(fn ($q) => (string) ($q['branch_id'] ?? ''));
|
|
|
|
foreach ($branches as $branch) {
|
|
$prior = $byBranch->get((string) $branch->id, []);
|
|
$stub = [
|
|
'name' => $meta['name'],
|
|
'prefix' => $meta['prefix'],
|
|
'module' => $context,
|
|
'branch_id' => $branch->id,
|
|
'branch_name' => $branch->name,
|
|
'active' => true,
|
|
'queue_external_key' => CareQueueContexts::queueExternalKey($context, $branch->id),
|
|
'counter_external_key' => CareQueueContexts::counterExternalKey($context, $branch->id),
|
|
'queue_uuid' => $prior['queue_uuid'] ?? null,
|
|
'counter_uuid' => $prior['counter_uuid'] ?? null,
|
|
'synced' => false,
|
|
];
|
|
|
|
try {
|
|
$synced = $this->queue->syncSpecialtyQueueStubs($owner, [$stub]);
|
|
$stub = $synced[0] ?? $stub;
|
|
} catch (\Throwable $e) {
|
|
Log::warning('care.queue_department_provision_failed', [
|
|
'context' => $context,
|
|
'branch_id' => $branch->id,
|
|
'message' => $e->getMessage(),
|
|
]);
|
|
}
|
|
|
|
$byBranch->put((string) $branch->id, $stub);
|
|
}
|
|
|
|
$provisioning[$context] = [
|
|
'queues' => $byBranch->values()->all(),
|
|
];
|
|
}
|
|
|
|
$settings = $organization->settings ?? [];
|
|
$settings['department_queue_provisioning'] = $provisioning;
|
|
$organization->update(['settings' => $settings]);
|
|
|
|
return $provisioning;
|
|
}
|
|
|
|
/**
|
|
* @return array{queue_uuid: ?string, counter_uuid: ?string, queue_external_key: string, counter_external_key: string, synced: bool}|null
|
|
*/
|
|
public function resourcesFor(
|
|
Organization $organization,
|
|
string $context,
|
|
int $branchId,
|
|
): ?array {
|
|
if (CareQueueContexts::isSpecialty($context)) {
|
|
$queues = data_get($organization->settings, "specialty_module_provisioning.{$context}.queues", []);
|
|
} else {
|
|
$queues = data_get($organization->settings, "department_queue_provisioning.{$context}.queues", []);
|
|
}
|
|
|
|
if (! is_array($queues)) {
|
|
return null;
|
|
}
|
|
|
|
$match = collect($queues)->first(
|
|
fn ($q) => (int) ($q['branch_id'] ?? 0) === $branchId && ($q['active'] ?? true)
|
|
);
|
|
|
|
if (! $match) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'queue_uuid' => $match['queue_uuid'] ?? null,
|
|
'counter_uuid' => $match['counter_uuid'] ?? null,
|
|
'queue_external_key' => (string) ($match['queue_external_key']
|
|
?? CareQueueContexts::queueExternalKey($context, $branchId)),
|
|
'counter_external_key' => (string) ($match['counter_external_key']
|
|
?? CareQueueContexts::counterExternalKey($context, $branchId)),
|
|
'synced' => (bool) ($match['synced'] ?? false),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Ensure resources exist for one context+branch (lazy provision).
|
|
*
|
|
* @return array{queue_uuid: ?string, counter_uuid: ?string, queue_external_key: string, counter_external_key: string, synced: bool}|null
|
|
*/
|
|
public function ensure(
|
|
Organization $organization,
|
|
string $context,
|
|
int $branchId,
|
|
): ?array {
|
|
$existing = $this->resourcesFor($organization, $context, $branchId);
|
|
if ($existing && ! empty($existing['queue_uuid']) && ! empty($existing['counter_uuid'])) {
|
|
return $existing;
|
|
}
|
|
|
|
if (! $this->shouldProvision($organization)) {
|
|
return $existing;
|
|
}
|
|
|
|
if (CareQueueContexts::isSpecialty($context)) {
|
|
return $existing;
|
|
}
|
|
|
|
if (! CareQueueContexts::isDepartment($context)) {
|
|
return null;
|
|
}
|
|
|
|
$this->provisionOrganization($organization->fresh());
|
|
|
|
return $this->resourcesFor($organization->fresh(), $context, $branchId);
|
|
}
|
|
}
|