Files
ladill-care/app/Services/Care/CareQueueProvisioner.php
T
isaaccladandCursor 015a4cc7fe
Deploy Ladill Care / deploy (push) Successful in 1m45s
Wire Care lists into Ladill Queue workflows instead of reception panels.
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>
2026-07-17 16:27:48 +00:00

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);
}
}