Files
isaaccladandCursor a3521247e0
Deploy Ladill Care / deploy (push) Successful in 57s
Route pharmacy, lab, and billing tickets through a shared FIFO pool.
Stop pre-assigning prescriptions to a default counter so Call next follows arrival order and each pharmacist only serves one active patient at a time.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 19:06:36 +00:00

141 lines
4.3 KiB
PHP

<?php
namespace App\Services\Care;
/**
* Canonical Care → Ladill Queue department / specialty contexts.
*
* Mapping:
* - Department/service → Queue ServiceQueue (+ Queue Department)
* - Service point (room/desk/counter) → Queue Counter
* - Staff assignment → Counter staff_ref / staff_display_name
*/
class CareQueueContexts
{
public const RECEPTION = 'reception';
public const TRIAGE = 'triage';
public const CONSULTATION = 'consultation';
public const PHARMACY = 'pharmacy';
public const LABORATORY = 'laboratory';
public const BILLING = 'billing';
public const IMAGING = 'imaging';
public const ROUTING_ROUTED = 'routed';
public const ROUTING_UNRESOLVED = 'unresolved';
/**
* Core department queues provisioned for every branch when Queue integration is on.
* Consultation/triage stay assigned_only (doctor/nurse-specific).
* Pharmacy/lab/billing/imaging use a shared FIFO pool claimed by each counter on Call next.
*
* @return array<string, array{name: string, prefix: string, routing_mode: string, type: string}>
*/
public static function departments(): array
{
return [
self::RECEPTION => [
'name' => 'Reception',
'prefix' => 'R',
'routing_mode' => 'shared_pool',
'type' => 'reception',
],
self::TRIAGE => [
'name' => 'Triage',
'prefix' => 'T',
'routing_mode' => 'assigned_only',
'type' => 'consultation',
],
self::CONSULTATION => [
'name' => 'Consultation',
'prefix' => 'C',
'routing_mode' => 'assigned_only',
'type' => 'consultation',
],
self::PHARMACY => [
'name' => 'Pharmacy',
'prefix' => 'P',
// Shared FIFO pool — any pharmacist counter claims the next waiting ticket.
'routing_mode' => 'shared_pool',
'type' => 'pharmacy',
],
self::LABORATORY => [
'name' => 'Laboratory',
'prefix' => 'L',
'routing_mode' => 'shared_pool',
'type' => 'laboratory',
],
self::BILLING => [
'name' => 'Billing',
'prefix' => 'B',
'routing_mode' => 'shared_pool',
'type' => 'cashier',
],
self::IMAGING => [
'name' => 'Imaging',
'prefix' => 'I',
'routing_mode' => 'shared_pool',
'type' => 'laboratory',
],
];
}
public static function isDepartment(string $context): bool
{
return array_key_exists($context, self::departments());
}
public static function isSpecialty(string $context): bool
{
return array_key_exists($context, config('care.specialty_modules', []));
}
public static function usesAssignedRouting(string $context): bool
{
if (self::isSpecialty($context)) {
return true;
}
return (self::departments()[$context]['routing_mode'] ?? 'shared_pool') === 'assigned_only';
}
public static function queueExternalKey(string $context, int|string $careBranchId): string
{
if (self::isSpecialty($context)) {
return "care:specialty:{$context}:queue:{$careBranchId}";
}
return "care:dept:{$context}:queue:{$careBranchId}";
}
public static function departmentExternalKey(string $context, int|string $careBranchId): string
{
return "care:dept:{$context}:branch:{$careBranchId}";
}
/** @deprecated Prefer pointExternalKey — kept for legacy single-counter stubs. */
public static function counterExternalKey(string $context, int|string $careBranchId): string
{
if (self::isSpecialty($context)) {
return "care:specialty:{$context}:counter:{$careBranchId}";
}
return "care:dept:{$context}:counter:{$careBranchId}";
}
public static function pointExternalKey(
string $context,
int|string $careBranchId,
string $pointKind,
int|string $pointId,
): string {
return "care:point:{$context}:{$pointKind}:{$careBranchId}:{$pointId}";
}
}