fix(abdm): evaluate cooling from clinical event date and prevent reset on taskboard view
This commit is contained in:
parent
376cb7eeb1
commit
734b9a2bc4
|
|
@ -157,10 +157,7 @@ class AbdmTaskBoard extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
$taskType = (string) ($task['task_type'] ?? '');
|
$taskType = (string) ($task['task_type'] ?? '');
|
||||||
$lastModified = ! empty($task['updated_at'])
|
$lastModified = AbdmTaskBoardSyncService::resolveTaskClinicalTimestamp($task, $this->db);
|
||||||
? (string) $task['updated_at']
|
|
||||||
: (! empty($task['created_at']) ? (string) $task['created_at'] : null);
|
|
||||||
|
|
||||||
$cooling = AbdmTaskBoardSyncService::calculateCooling($taskType, $lastModified);
|
$cooling = AbdmTaskBoardSyncService::calculateCooling($taskType, $lastModified);
|
||||||
|
|
||||||
$task['cooling_active'] = ! empty($cooling['is_cooling_active']);
|
$task['cooling_active'] = ! empty($cooling['is_cooling_active']);
|
||||||
|
|
@ -428,7 +425,10 @@ class AbdmTaskBoard extends BaseController
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$select = 'r.id, r.patient_id, r.patient_name, r.lab_type, r.charge_id, r.status, ' . implode(', ', $abhaSelectParts);
|
$labFields = $this->db->getFieldNames('lab_request') ?? [];
|
||||||
|
$dateFields = array_intersect(['reported_time', 'collected_time', 'Request_Date'], $labFields);
|
||||||
|
$dateSelect = ! empty($dateFields) ? (', r.' . implode(', r.', $dateFields)) : '';
|
||||||
|
$select = 'r.id, r.patient_id, r.patient_name, r.lab_type, r.charge_id, r.status' . $dateSelect . ', ' . implode(', ', $abhaSelectParts);
|
||||||
|
|
||||||
$rows = $this->db->table('lab_request r')
|
$rows = $this->db->table('lab_request r')
|
||||||
->select($select)
|
->select($select)
|
||||||
|
|
@ -466,6 +466,10 @@ class AbdmTaskBoard extends BaseController
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$clinTs = ! empty($row['reported_time'])
|
||||||
|
? $row['reported_time']
|
||||||
|
: (! empty($row['collected_time']) ? $row['collected_time'] : ($row['Request_Date'] ?? ''));
|
||||||
|
|
||||||
$this->taskService->createOrRefreshTask(
|
$this->taskService->createOrRefreshTask(
|
||||||
$taskType,
|
$taskType,
|
||||||
'diagnosis',
|
'diagnosis',
|
||||||
|
|
@ -476,9 +480,10 @@ class AbdmTaskBoard extends BaseController
|
||||||
$abha,
|
$abha,
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'lab_type' => $labType,
|
'lab_type' => $labType,
|
||||||
'invoice_id' => (int) ($row['charge_id'] ?? 0),
|
'invoice_id' => (int) ($row['charge_id'] ?? 0),
|
||||||
'trigger' => 'task_board.backfill',
|
'clinical_timestamp' => (string) $clinTs,
|
||||||
|
'trigger' => 'task_board.backfill',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1217,6 +1222,18 @@ class AbdmTaskBoard extends BaseController
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', 'health_document_publish')
|
||||||
|
->where('entity_type', 'doctor_document')
|
||||||
|
->where('entity_id', (string) $docId)
|
||||||
|
->whereIn('status', ['pending', 'in_progress', 'completed'])
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$patientName = trim((string) ($row['p_fname'] ?? ''));
|
$patientName = trim((string) ($row['p_fname'] ?? ''));
|
||||||
$this->taskService->createOrRefreshTask(
|
$this->taskService->createOrRefreshTask(
|
||||||
'health_document_publish',
|
'health_document_publish',
|
||||||
|
|
@ -1228,8 +1245,9 @@ class AbdmTaskBoard extends BaseController
|
||||||
$abhaId,
|
$abhaId,
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'patient_doc_id' => $docId,
|
'patient_doc_id' => $docId,
|
||||||
'trigger' => 'patient_doc.compiled',
|
'clinical_timestamp' => (string) (! empty($row['created_at']) ? $row['created_at'] : ($row['date_issue'] ?? '')),
|
||||||
|
'trigger' => 'patient_doc.compiled',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1253,6 +1271,18 @@ class AbdmTaskBoard extends BaseController
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', 'health_document_publish')
|
||||||
|
->where('entity_type', 'patient_document')
|
||||||
|
->where('entity_id', (string) $fileId)
|
||||||
|
->whereIn('status', ['pending', 'in_progress', 'completed'])
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$patientName = trim((string) ($fRow['p_fname'] ?? ''));
|
$patientName = trim((string) ($fRow['p_fname'] ?? ''));
|
||||||
$this->taskService->createOrRefreshTask(
|
$this->taskService->createOrRefreshTask(
|
||||||
'health_document_publish',
|
'health_document_publish',
|
||||||
|
|
@ -1264,8 +1294,9 @@ class AbdmTaskBoard extends BaseController
|
||||||
$abhaId,
|
$abhaId,
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'file_upload_id' => $fileId,
|
'file_upload_id' => $fileId,
|
||||||
'trigger' => 'file_upload_data.created',
|
'clinical_timestamp' => (string) ($fRow['insert_date'] ?? ''),
|
||||||
|
'trigger' => 'file_upload_data.created',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1317,6 +1348,18 @@ class AbdmTaskBoard extends BaseController
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', 'wellness_record_publish')
|
||||||
|
->where('entity_type', 'opd_vitals')
|
||||||
|
->where('entity_id', (string) $rxId)
|
||||||
|
->whereIn('status', ['pending', 'in_progress', 'completed'])
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$patientName = trim((string) ($row['p_fname'] ?? ''));
|
$patientName = trim((string) ($row['p_fname'] ?? ''));
|
||||||
$this->taskService->createOrRefreshTask(
|
$this->taskService->createOrRefreshTask(
|
||||||
'wellness_record_publish',
|
'wellness_record_publish',
|
||||||
|
|
@ -1329,8 +1372,9 @@ class AbdmTaskBoard extends BaseController
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'opd_prescription_id' => $rxId,
|
'opd_prescription_id' => $rxId,
|
||||||
'opd_session_id' => $opdId,
|
'opd_session_id' => $opdId,
|
||||||
'trigger' => 'vitals.backfilled',
|
'clinical_timestamp' => (string) ($row['date_opd_visit'] ?? ''),
|
||||||
|
'trigger' => 'vitals.backfilled',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2540,9 +2540,9 @@ class DoctorDocument extends BaseController
|
||||||
$visitDate = (string) ($source['visit_date'] ?? date('Y-m-d'));
|
$visitDate = (string) ($source['visit_date'] ?? date('Y-m-d'));
|
||||||
$doctorName = (string) ($source['doctor_name'] ?? '');
|
$doctorName = (string) ($source['doctor_name'] ?? '');
|
||||||
|
|
||||||
if ($patientId > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\Abdm\Task\AbdmTaskService')) {
|
if ($patientId > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\AbdmWorkTaskService')) {
|
||||||
try {
|
try {
|
||||||
$taskService = new \App\Libraries\Abdm\Task\AbdmTaskService();
|
$taskService = new \App\Libraries\AbdmWorkTaskService();
|
||||||
$taskService->createOrRefreshTask(
|
$taskService->createOrRefreshTask(
|
||||||
'health_document_publish',
|
'health_document_publish',
|
||||||
'patient_doc',
|
'patient_doc',
|
||||||
|
|
@ -2554,6 +2554,7 @@ class DoctorDocument extends BaseController
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'patient_doc_id' => $patientDocId,
|
'patient_doc_id' => $patientDocId,
|
||||||
|
'clinical_timestamp' => (string) ($source['visit_date'] ?? date('Y-m-d H:i:s')),
|
||||||
'trigger' => 'patient_doc.compiled',
|
'trigger' => 'patient_doc.compiled',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6043,9 +6043,9 @@ class Patient extends BaseController
|
||||||
$adapter = new \App\Libraries\Abdm\Fhir\Support\GatewayPayloadAdapter();
|
$adapter = new \App\Libraries\Abdm\Fhir\Support\GatewayPayloadAdapter();
|
||||||
$gatewayPayload = $adapter->toGatewayPayload($generatorOutput, $source, $hfrId);
|
$gatewayPayload = $adapter->toGatewayPayload($generatorOutput, $source, $hfrId);
|
||||||
|
|
||||||
if ($pid > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\Abdm\Task\AbdmTaskService')) {
|
if ($pid > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\AbdmWorkTaskService')) {
|
||||||
try {
|
try {
|
||||||
$taskService = new \App\Libraries\Abdm\Task\AbdmTaskService();
|
$taskService = new \App\Libraries\AbdmWorkTaskService();
|
||||||
$taskService->createOrRefreshTask(
|
$taskService->createOrRefreshTask(
|
||||||
'health_document_publish',
|
'health_document_publish',
|
||||||
'file_upload_data',
|
'file_upload_data',
|
||||||
|
|
@ -6057,6 +6057,7 @@ class Patient extends BaseController
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'file_upload_id' => $fileUploadId,
|
'file_upload_id' => $fileUploadId,
|
||||||
|
'clinical_timestamp' => date('Y-m-d H:i:s'),
|
||||||
'trigger' => 'file_upload_data.created',
|
'trigger' => 'file_upload_data.created',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -6167,9 +6168,9 @@ class Patient extends BaseController
|
||||||
$patientName = (string) ($source['patient']['name'] ?? 'Patient');
|
$patientName = (string) ($source['patient']['name'] ?? 'Patient');
|
||||||
$abhaDigits = (string) ($source['patient']['abha_id'] ?? '');
|
$abhaDigits = (string) ($source['patient']['abha_id'] ?? '');
|
||||||
|
|
||||||
if ($patientId > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\Abdm\Task\AbdmTaskService')) {
|
if ($patientId > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\AbdmWorkTaskService')) {
|
||||||
try {
|
try {
|
||||||
$taskService = new \App\Libraries\Abdm\Task\AbdmTaskService();
|
$taskService = new \App\Libraries\AbdmWorkTaskService();
|
||||||
$taskService->createOrRefreshTask(
|
$taskService->createOrRefreshTask(
|
||||||
'wellness_record_publish',
|
'wellness_record_publish',
|
||||||
'opd_prescription',
|
'opd_prescription',
|
||||||
|
|
@ -6181,6 +6182,7 @@ class Patient extends BaseController
|
||||||
'submit',
|
'submit',
|
||||||
[
|
[
|
||||||
'opd_session_id' => $opdSessionId,
|
'opd_session_id' => $opdSessionId,
|
||||||
|
'clinical_timestamp' => (string) ($source['visit_date'] ?? date('Y-m-d H:i:s')),
|
||||||
'trigger' => 'vitals.saved',
|
'trigger' => 'vitals.saved',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -557,6 +557,9 @@ class AbdmTaskBoardSyncService
|
||||||
return $summary;
|
return $summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-discover any newly completed clinical records with ABHA
|
||||||
|
$this->backfillMissingWorkTasks(30, 100);
|
||||||
|
|
||||||
$supportedTaskTypes = [
|
$supportedTaskTypes = [
|
||||||
'opd_prescription_publish',
|
'opd_prescription_publish',
|
||||||
'lab_report_publish',
|
'lab_report_publish',
|
||||||
|
|
@ -567,11 +570,12 @@ class AbdmTaskBoardSyncService
|
||||||
'ipd_discharge_publish',
|
'ipd_discharge_publish',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Fetch candidate batch wider than $limit to bypass cooling or retrying items without queue starvation
|
||||||
$tasks = $this->db->table('abdm_work_tasks')
|
$tasks = $this->db->table('abdm_work_tasks')
|
||||||
->whereIn('task_type', $supportedTaskTypes)
|
->whereIn('task_type', $supportedTaskTypes)
|
||||||
->whereIn('status', ['pending', 'in_progress'])
|
->whereIn('status', ['pending', 'in_progress'])
|
||||||
->orderBy('id', 'ASC')
|
->orderBy('id', 'ASC')
|
||||||
->limit(max(1, $limit))
|
->limit(max(60, $limit * 3))
|
||||||
->get()
|
->get()
|
||||||
->getResultArray();
|
->getResultArray();
|
||||||
|
|
||||||
|
|
@ -579,6 +583,8 @@ class AbdmTaskBoardSyncService
|
||||||
return $summary;
|
return $summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$processedCount = 0;
|
||||||
|
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
$taskId = (int) $task['id'];
|
$taskId = (int) $task['id'];
|
||||||
$taskType = (string) $task['task_type'];
|
$taskType = (string) $task['task_type'];
|
||||||
|
|
@ -586,8 +592,8 @@ class AbdmTaskBoardSyncService
|
||||||
$patientId = (int) ($task['patient_id'] ?? 0);
|
$patientId = (int) ($task['patient_id'] ?? 0);
|
||||||
$abhaId = trim((string) ($task['abha_id'] ?? ''));
|
$abhaId = trim((string) ($task['abha_id'] ?? ''));
|
||||||
|
|
||||||
// Check cooling period from the latest modification/refresh timestamp
|
// Check cooling period from the actual clinical event/modification timestamp
|
||||||
$lastModified = ! empty($task['updated_at']) ? $task['updated_at'] : ($task['created_at'] ?? date('Y-m-d H:i:s'));
|
$lastModified = self::resolveTaskClinicalTimestamp($task, $this->db);
|
||||||
$cooling = self::calculateCooling($taskType, $lastModified);
|
$cooling = self::calculateCooling($taskType, $lastModified);
|
||||||
|
|
||||||
if ($cooling['is_cooling_active']) {
|
if ($cooling['is_cooling_active']) {
|
||||||
|
|
@ -614,6 +620,10 @@ class AbdmTaskBoardSyncService
|
||||||
'patient' => $task['patient_name'] ?? '',
|
'patient' => $task['patient_name'] ?? '',
|
||||||
'status' => 'dry_run_eligible',
|
'status' => 'dry_run_eligible',
|
||||||
];
|
];
|
||||||
|
$processedCount++;
|
||||||
|
if ($processedCount >= $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -636,11 +646,29 @@ class AbdmTaskBoardSyncService
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$summary['failed']++;
|
$summary['failed']++;
|
||||||
$this->taskService->markTaskStatus(
|
$payload = ! empty($task['payload_json']) ? json_decode((string) $task['payload_json'], true) : [];
|
||||||
$taskId,
|
$retryCount = (int) ($payload['retry_count'] ?? 0) + 1;
|
||||||
'pending', // remain pending for retry on next cron tick
|
$payload['retry_count'] = $retryCount;
|
||||||
'Cron retry pending: ' . ($result['error'] ?? 'Sync failed')
|
$payload['last_error'] = (string) ($result['error'] ?? 'Sync failed');
|
||||||
);
|
$payload['last_failed_at'] = Time::now('Asia/Kolkata')->toDateTimeString();
|
||||||
|
|
||||||
|
$errJson = (string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
$this->db->table('abdm_work_tasks')->where('id', $taskId)->update(['payload_json' => $errJson]);
|
||||||
|
|
||||||
|
if ($retryCount >= 5) {
|
||||||
|
$this->taskService->markTaskStatus(
|
||||||
|
$taskId,
|
||||||
|
'failed',
|
||||||
|
'Max cron retries (5) exceeded: ' . ($result['error'] ?? 'Sync failed')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->taskService->markTaskStatus(
|
||||||
|
$taskId,
|
||||||
|
'pending', // remain pending for retry on next cron tick
|
||||||
|
'Cron retry #' . $retryCount . ' pending: ' . ($result['error'] ?? 'Sync failed')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$summary['details'][] = [
|
$summary['details'][] = [
|
||||||
'task_id' => $taskId,
|
'task_id' => $taskId,
|
||||||
'task_type' => $taskType,
|
'task_type' => $taskType,
|
||||||
|
|
@ -649,6 +677,11 @@ class AbdmTaskBoardSyncService
|
||||||
'error' => $result['error'] ?? 'Sync failed',
|
'error' => $result['error'] ?? 'Sync failed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$processedCount++;
|
||||||
|
if ($processedCount >= $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $summary;
|
return $summary;
|
||||||
|
|
@ -724,7 +757,8 @@ class AbdmTaskBoardSyncService
|
||||||
$abhaNumber = trim((string) $patientRow['abha_id']);
|
$abhaNumber = trim((string) $patientRow['abha_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$visitDate = date('Y-m-d');
|
$clinTs = self::resolveTaskClinicalTimestamp($task, $this->db);
|
||||||
|
$visitDate = ! empty($clinTs) && strtotime($clinTs) > 0 ? date('Y-m-d', strtotime($clinTs)) : date('Y-m-d');
|
||||||
$prefix = match ($taskType) {
|
$prefix = match ($taskType) {
|
||||||
'radiology_report_publish' => 'RAD-',
|
'radiology_report_publish' => 'RAD-',
|
||||||
'lab_report_publish' => 'LAB-',
|
'lab_report_publish' => 'LAB-',
|
||||||
|
|
@ -942,5 +976,286 @@ class AbdmTaskBoardSyncService
|
||||||
{
|
{
|
||||||
return self::calculateCooling($taskType, $lastModified);
|
return self::calculateCooling($taskType, $lastModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the actual clinical / event timestamp for a task to accurately measure cooling.
|
||||||
|
* Checks task payload meta, underlying clinical record tables, and falls back to created_at.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $task
|
||||||
|
*/
|
||||||
|
public static function resolveTaskClinicalTimestamp(array $task, ?BaseConnection $db = null): string
|
||||||
|
{
|
||||||
|
$payload = ! empty($task['payload_json']) ? json_decode((string) $task['payload_json'], true) : [];
|
||||||
|
$meta = is_array($payload) ? ($payload['meta'] ?? []) : [];
|
||||||
|
|
||||||
|
// 1. Direct meta timestamps
|
||||||
|
foreach (['clinical_timestamp', 'event_date', 'reported_time', 'collected_time', 'given_date', 'date_opd_visit', 'discharge_date'] as $key) {
|
||||||
|
if (! empty($meta[$key]) && strtotime((string) $meta[$key]) > 0) {
|
||||||
|
return (string) $meta[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$taskType = (string) ($task['task_type'] ?? '');
|
||||||
|
$entityType = (string) ($task['entity_type'] ?? '');
|
||||||
|
$entityId = (int) ($task['entity_id'] ?? 0);
|
||||||
|
|
||||||
|
if ($entityId > 0) {
|
||||||
|
try {
|
||||||
|
$dbConn = $db ?? db_connect();
|
||||||
|
|
||||||
|
// 2. Health Documents: file_upload_data or patient_doc
|
||||||
|
if (in_array($entityType, ['patient_document', 'file_upload_data'], true) || $taskType === 'health_document_publish') {
|
||||||
|
if ($dbConn->tableExists('file_upload_data')) {
|
||||||
|
$fRow = $dbConn->table('file_upload_data')->select('insert_date')->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
if (! empty($fRow['insert_date']) && strtotime((string) $fRow['insert_date']) > 0) {
|
||||||
|
return (string) $fRow['insert_date'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($dbConn->tableExists('patient_doc')) {
|
||||||
|
$pRow = $dbConn->table('patient_doc')->select('created_at, date_issue')->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
$date = ! empty($pRow['created_at']) ? $pRow['created_at'] : ($pRow['date_issue'] ?? null);
|
||||||
|
if (! empty($date) && strtotime((string) $date) > 0) {
|
||||||
|
return (string) $date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Lab / Radiology: lab_request
|
||||||
|
if ($entityType === 'lab_request' || in_array($taskType, ['lab_report_publish', 'radiology_report_publish'], true)) {
|
||||||
|
if ($dbConn->tableExists('lab_request')) {
|
||||||
|
$fields = $dbConn->getFieldNames('lab_request');
|
||||||
|
$selectCols = array_intersect(['reported_time', 'collected_time', 'Request_Date'], $fields);
|
||||||
|
if (! empty($selectCols)) {
|
||||||
|
$lRow = $dbConn->table('lab_request')->select(implode(', ', $selectCols))->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
foreach (['reported_time', 'collected_time', 'Request_Date'] as $col) {
|
||||||
|
if (! empty($lRow[$col]) && strtotime((string) $lRow[$col]) > 0) {
|
||||||
|
return (string) $lRow[$col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Immunization: immunization_records
|
||||||
|
if ($entityType === 'immunization' || $taskType === 'immunization_record_publish') {
|
||||||
|
if ($dbConn->tableExists('immunization_records')) {
|
||||||
|
$iRow = $dbConn->table('immunization_records')->select('given_date, created_at')->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
$date = ! empty($iRow['given_date']) ? $iRow['given_date'] : ($iRow['created_at'] ?? null);
|
||||||
|
if (! empty($date) && strtotime((string) $date) > 0) {
|
||||||
|
return (string) $date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Wellness: opd_prescription
|
||||||
|
if ($entityType === 'opd_prescription' || $entityType === 'opd_vitals' || $taskType === 'wellness_record_publish') {
|
||||||
|
if ($dbConn->tableExists('opd_prescription')) {
|
||||||
|
$fields = $dbConn->getFieldNames('opd_prescription');
|
||||||
|
$dateCol = in_array('date_opd_visit', $fields, true) ? 'date_opd_visit' : 'id';
|
||||||
|
$rxRow = $dbConn->table('opd_prescription')->select($dateCol)->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
if (! empty($rxRow['date_opd_visit']) && strtotime((string) $rxRow['date_opd_visit']) > 0) {
|
||||||
|
return (string) $rxRow['date_opd_visit'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. IPD Discharge: ipd_discharge
|
||||||
|
if ($entityType === 'ipd_discharge' || $taskType === 'ipd_discharge_publish') {
|
||||||
|
if ($dbConn->tableExists('ipd_discharge')) {
|
||||||
|
$dRow = $dbConn->table('ipd_discharge')->select('discharge_date, created_at')->where('id', $entityId)->get(1)->getRowArray();
|
||||||
|
$date = ! empty($dRow['discharge_date']) ? $dRow['discharge_date'] : ($dRow['created_at'] ?? null);
|
||||||
|
if (! empty($date) && strtotime((string) $date) > 0) {
|
||||||
|
return (string) $date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable) {
|
||||||
|
// Silently fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to task's created_at, or updated_at
|
||||||
|
return ! empty($task['created_at'])
|
||||||
|
? (string) $task['created_at']
|
||||||
|
: (! empty($task['updated_at']) ? (string) $task['updated_at'] : date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backfill missing work tasks for finalized clinical records with ABHA.
|
||||||
|
* Ensures cron discovers and queues tasks even if staff haven't opened the web Task Board.
|
||||||
|
*/
|
||||||
|
public function backfillMissingWorkTasks(int $days = 30, int $limit = 100): void
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_work_tasks') || ! $this->db->tableExists('patient_master')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$patientFields = $this->db->getFieldNames('patient_master') ?? [];
|
||||||
|
$abhaCol = $this->resolveFirstExistingColumn($patientFields, ['abha_id', 'abha_no', 'abha_address', 'abha']);
|
||||||
|
if ($abhaCol === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sinceDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
||||||
|
|
||||||
|
// 1. Health Documents: file_upload_data
|
||||||
|
if ($this->db->tableExists('file_upload_data')) {
|
||||||
|
$fileRows = $this->db->table('file_upload_data f')
|
||||||
|
->select('f.id, f.pid, f.insert_date, p.p_fname, p.' . $abhaCol . ' as abha_id', false)
|
||||||
|
->join('patient_master p', 'p.id = f.pid', 'inner')
|
||||||
|
->where('p.' . $abhaCol . ' !=', '')
|
||||||
|
->where('f.insert_date >=', $sinceDate)
|
||||||
|
->orderBy('f.id', 'DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
foreach ($fileRows as $fRow) {
|
||||||
|
$fileId = (int) ($fRow['id'] ?? 0);
|
||||||
|
$patientId = (int) ($fRow['pid'] ?? 0);
|
||||||
|
$abhaId = trim((string) ($fRow['abha_id'] ?? ''));
|
||||||
|
if ($fileId <= 0 || $patientId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', 'health_document_publish')
|
||||||
|
->where('entity_type', 'patient_document')
|
||||||
|
->where('entity_id', (string) $fileId)
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->taskService->createOrRefreshTask(
|
||||||
|
'health_document_publish',
|
||||||
|
'file_upload_data',
|
||||||
|
'patient_document',
|
||||||
|
(string) $fileId,
|
||||||
|
$patientId,
|
||||||
|
trim((string) ($fRow['p_fname'] ?? '')),
|
||||||
|
$abhaId,
|
||||||
|
'submit',
|
||||||
|
[
|
||||||
|
'file_upload_id' => $fileId,
|
||||||
|
'clinical_timestamp' => (string) ($fRow['insert_date'] ?? ''),
|
||||||
|
'trigger' => 'cron.backfill',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Health Documents: patient_doc
|
||||||
|
if ($this->db->tableExists('patient_doc')) {
|
||||||
|
$docRows = $this->db->table('patient_doc pd')
|
||||||
|
->select('pd.id, pd.p_id, pd.date_issue, pd.created_at, p.p_fname, p.' . $abhaCol . ' as abha_id', false)
|
||||||
|
->join('patient_master p', 'p.id = pd.p_id', 'inner')
|
||||||
|
->where('p.' . $abhaCol . ' !=', '')
|
||||||
|
->where('pd.created_at >=', $sinceDate)
|
||||||
|
->orderBy('pd.id', 'DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
foreach ($docRows as $dRow) {
|
||||||
|
$docId = (int) ($dRow['id'] ?? 0);
|
||||||
|
$patientId = (int) ($dRow['p_id'] ?? 0);
|
||||||
|
$abhaId = trim((string) ($dRow['abha_id'] ?? ''));
|
||||||
|
if ($docId <= 0 || $patientId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', 'health_document_publish')
|
||||||
|
->where('entity_type', 'doctor_document')
|
||||||
|
->where('entity_id', (string) $docId)
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->taskService->createOrRefreshTask(
|
||||||
|
'health_document_publish',
|
||||||
|
'patient_doc',
|
||||||
|
'doctor_document',
|
||||||
|
(string) $docId,
|
||||||
|
$patientId,
|
||||||
|
trim((string) ($dRow['p_fname'] ?? '')),
|
||||||
|
$abhaId,
|
||||||
|
'submit',
|
||||||
|
[
|
||||||
|
'patient_doc_id' => $docId,
|
||||||
|
'clinical_timestamp' => (string) (! empty($dRow['created_at']) ? $dRow['created_at'] : ($dRow['date_issue'] ?? '')),
|
||||||
|
'trigger' => 'cron.backfill',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Lab & Radiology: lab_request
|
||||||
|
if ($this->db->tableExists('lab_request')) {
|
||||||
|
$labFields = $this->db->getFieldNames('lab_request') ?? [];
|
||||||
|
$dateFields = array_intersect(['reported_time', 'collected_time', 'Request_Date'], $labFields);
|
||||||
|
$dateSelect = ! empty($dateFields) ? (', r.' . implode(', r.', $dateFields)) : '';
|
||||||
|
|
||||||
|
$labRows = $this->db->table('lab_request r')
|
||||||
|
->select('r.id, r.patient_id, r.patient_name, r.lab_type, r.charge_id, r.status' . $dateSelect . ', p.' . $abhaCol . ' as abha_id', false)
|
||||||
|
->join('patient_master p', 'p.id = r.patient_id', 'inner')
|
||||||
|
->where('r.status >=', 2)
|
||||||
|
->where('p.' . $abhaCol . ' !=', '')
|
||||||
|
->orderBy('r.id', 'DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
foreach ($labRows as $lRow) {
|
||||||
|
$labReqId = (int) ($lRow['id'] ?? 0);
|
||||||
|
$patientId = (int) ($lRow['patient_id'] ?? 0);
|
||||||
|
$abhaId = trim((string) ($lRow['abha_id'] ?? ''));
|
||||||
|
if ($labReqId <= 0 || $patientId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$labType = (int) ($lRow['lab_type'] ?? 0);
|
||||||
|
$taskType = in_array($labType, [1, 2, 3, 4, 6], true) ? 'radiology_report_publish' : 'lab_report_publish';
|
||||||
|
|
||||||
|
$exists = $this->db->table('abdm_work_tasks')
|
||||||
|
->select('id')
|
||||||
|
->where('task_type', $taskType)
|
||||||
|
->where('entity_type', 'lab_request')
|
||||||
|
->where('entity_id', (string) $labReqId)
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($exists)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$clinTs = ! empty($lRow['reported_time'])
|
||||||
|
? $lRow['reported_time']
|
||||||
|
: (! empty($lRow['collected_time']) ? $lRow['collected_time'] : ($lRow['Request_Date'] ?? ''));
|
||||||
|
|
||||||
|
$this->taskService->createOrRefreshTask(
|
||||||
|
$taskType,
|
||||||
|
'diagnosis',
|
||||||
|
'lab_request',
|
||||||
|
(string) $labReqId,
|
||||||
|
$patientId,
|
||||||
|
trim((string) ($lRow['patient_name'] ?? '')),
|
||||||
|
$abhaId,
|
||||||
|
'submit',
|
||||||
|
[
|
||||||
|
'lab_type' => $labType,
|
||||||
|
'invoice_id' => (int) ($lRow['charge_id'] ?? 0),
|
||||||
|
'clinical_timestamp' => (string) $clinTs,
|
||||||
|
'trigger' => 'cron.backfill',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,28 +59,54 @@ class AbdmWorkTaskService
|
||||||
->get(1)
|
->get(1)
|
||||||
->getRowArray();
|
->getRowArray();
|
||||||
|
|
||||||
$row = [
|
$encodedPayload = (string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
'source_module' => $sourceModule,
|
|
||||||
'patient_id' => $patientId > 0 ? $patientId : null,
|
|
||||||
'patient_name' => $patientName !== '' ? $patientName : null,
|
|
||||||
'abha_id' => $abhaId !== '' ? $abhaId : null,
|
|
||||||
'action_mode' => $actionMode !== '' ? $actionMode : null,
|
|
||||||
'payload_json' => (string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
|
||||||
'updated_at' => $now,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (! empty($existing)) {
|
if (! empty($existing)) {
|
||||||
$this->db->table('abdm_work_tasks')->where('id', (int) $existing['id'])->update($row);
|
$existingPayload = (string) ($existing['payload_json'] ?? '');
|
||||||
|
$existingAbha = trim((string) ($existing['abha_id'] ?? ''));
|
||||||
|
$existingPatientId = (int) ($existing['patient_id'] ?? 0);
|
||||||
|
|
||||||
|
// Only update if meaningful data changed (avoids cooling period reset on idempotent refresh)
|
||||||
|
$needsUpdate = ($existingAbha !== $abhaId)
|
||||||
|
|| ($existingPatientId !== $patientId)
|
||||||
|
|| ($existingPayload !== $encodedPayload);
|
||||||
|
|
||||||
|
if ($needsUpdate) {
|
||||||
|
$updateRow = [
|
||||||
|
'source_module' => $sourceModule,
|
||||||
|
'patient_id' => $patientId > 0 ? $patientId : null,
|
||||||
|
'patient_name' => $patientName !== '' ? $patientName : null,
|
||||||
|
'abha_id' => $abhaId !== '' ? $abhaId : null,
|
||||||
|
'action_mode' => $actionMode !== '' ? $actionMode : null,
|
||||||
|
'payload_json' => $encodedPayload,
|
||||||
|
'updated_at' => $now,
|
||||||
|
];
|
||||||
|
$this->db->table('abdm_work_tasks')->where('id', (int) $existing['id'])->update($updateRow);
|
||||||
|
}
|
||||||
|
|
||||||
return (int) $existing['id'];
|
return (int) $existing['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$row['task_code'] = $this->generateTaskCode();
|
$createdAt = ! empty($meta['clinical_timestamp']) && strtotime((string) $meta['clinical_timestamp']) > 0
|
||||||
$row['task_type'] = $taskType;
|
? date('Y-m-d H:i:s', strtotime((string) $meta['clinical_timestamp']))
|
||||||
$row['entity_type'] = $entityType;
|
: $now;
|
||||||
$row['entity_id'] = $entityId;
|
|
||||||
$row['status'] = 'pending';
|
$row = [
|
||||||
$row['priority'] = 'normal';
|
'task_code' => $this->generateTaskCode(),
|
||||||
$row['created_at'] = $now;
|
'task_type' => $taskType,
|
||||||
|
'source_module' => $sourceModule,
|
||||||
|
'entity_type' => $entityType,
|
||||||
|
'entity_id' => $entityId,
|
||||||
|
'patient_id' => $patientId > 0 ? $patientId : null,
|
||||||
|
'patient_name' => $patientName !== '' ? $patientName : null,
|
||||||
|
'abha_id' => $abhaId !== '' ? $abhaId : null,
|
||||||
|
'action_mode' => $actionMode !== '' ? $actionMode : null,
|
||||||
|
'payload_json' => $encodedPayload,
|
||||||
|
'status' => 'pending',
|
||||||
|
'priority' => 'normal',
|
||||||
|
'created_at' => $createdAt,
|
||||||
|
'updated_at' => $now,
|
||||||
|
];
|
||||||
|
|
||||||
$this->db->table('abdm_work_tasks')->insert($row);
|
$this->db->table('abdm_work_tasks')->insert($row);
|
||||||
$insertId = (int) $this->db->insertID();
|
$insertId = (int) $this->db->insertID();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ crontab -l 2>/dev/null | awk -v start="$BLOCK_START" -v end="$BLOCK_END" '
|
||||||
cat >> "$TMP_CRON" <<EOF
|
cat >> "$TMP_CRON" <<EOF
|
||||||
$BLOCK_START
|
$BLOCK_START
|
||||||
* * * * * cd $PROJECT_DIR && $PHP_BIN spark abdm:push-sync --limit=20 --worker=cron-abdm-m2 >> $LOG_DIR/abdm_push_sync.log 2>&1
|
* * * * * cd $PROJECT_DIR && $PHP_BIN spark abdm:push-sync --limit=20 --worker=cron-abdm-m2 >> $LOG_DIR/abdm_push_sync.log 2>&1
|
||||||
|
* * * * * cd $PROJECT_DIR && $PHP_BIN spark abdm:taskboard-sync --limit=20 >> $LOG_DIR/abdm_taskboard_sync.log 2>&1
|
||||||
* * * * * cd $PROJECT_DIR && $PHP_BIN spark abdm:sms-notify --limit=20 >> $LOG_DIR/abdm_sms_notify.log 2>&1
|
* * * * * cd $PROJECT_DIR && $PHP_BIN spark abdm:sms-notify --limit=20 >> $LOG_DIR/abdm_sms_notify.log 2>&1
|
||||||
* * * * * cd $PROJECT_DIR && $PHP_BIN spark bridge:sync --limit 10 >> $LOG_DIR/bridge_sync.log 2>&1
|
* * * * * cd $PROJECT_DIR && $PHP_BIN spark bridge:sync --limit 10 >> $LOG_DIR/bridge_sync.log 2>&1
|
||||||
* * * * * cd $PROJECT_DIR && $PHP_BIN spark snomed:process-coding >> $LOG_DIR/snomed_process.log 2>&1
|
* * * * * cd $PROJECT_DIR && $PHP_BIN spark snomed:process-coding >> $LOG_DIR/snomed_process.log 2>&1
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,8 @@ cmd_install() {
|
||||||
$BLOCK_START
|
$BLOCK_START
|
||||||
# ABDM M2 Care Context Push Sync (Every 1 minute)
|
# ABDM M2 Care Context Push Sync (Every 1 minute)
|
||||||
* * * * * cd $project_dir && $php_bin spark abdm:push-sync --limit=20 --worker=cron-abdm-m2 >> $log_dir/abdm_push_sync.log 2>&1
|
* * * * * cd $project_dir && $php_bin spark abdm:push-sync --limit=20 --worker=cron-abdm-m2 >> $log_dir/abdm_push_sync.log 2>&1
|
||||||
|
# ABDM Work Task Board Auto-Link Sync (Every 1 minute)
|
||||||
|
* * * * * cd $project_dir && $php_bin spark abdm:taskboard-sync --limit=20 >> $log_dir/abdm_taskboard_sync.log 2>&1
|
||||||
# ABDM Deep-Link SMS Notification Sync (Every 1 minute)
|
# ABDM Deep-Link SMS Notification Sync (Every 1 minute)
|
||||||
* * * * * cd $project_dir && $php_bin spark abdm:sms-notify --limit=20 >> $log_dir/abdm_sms_notify.log 2>&1
|
* * * * * cd $project_dir && $php_bin spark abdm:sms-notify --limit=20 >> $log_dir/abdm_sms_notify.log 2>&1
|
||||||
# ABDM Bridge Records Sync (Every 1 minute)
|
# ABDM Bridge Records Sync (Every 1 minute)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user