From db76ad975919bf9ce5946f8feb11559ac2b2382a Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 7 Oct 2026 13:24:05 +0530 Subject: [PATCH] fix(abdm): resolve patient flipping, duplicate document contexts, and health document FHIR fetch --- app/Controllers/AbdmFhirInspector.php | 33 +++++-- app/Controllers/AbdmGateway.php | 105 ++++++++++++++++++--- app/Controllers/Api/v1/NursingApi.php | 32 ++++--- app/Controllers/DoctorDocument.php | 51 ++++++++++ app/Views/abdm/fhir_inspector.php | 104 ++++++++++++-------- app/Views/partials/abdm_hip_link_modal.php | 64 +++++++++++-- public/App/Nursing/index.html | 16 +++- 7 files changed, 325 insertions(+), 80 deletions(-) diff --git a/app/Controllers/AbdmFhirInspector.php b/app/Controllers/AbdmFhirInspector.php index 5da0399..3228bd8 100644 --- a/app/Controllers/AbdmFhirInspector.php +++ b/app/Controllers/AbdmFhirInspector.php @@ -148,7 +148,7 @@ class AbdmFhirInspector extends BaseController ->getRowArray(); if (! empty($hr)) { - $rawBundle = $hr['fhir_bundle'] ?? $hr['bundle'] ?? null; + $rawBundle = $hr['record_data'] ?? $hr['fhir_bundle'] ?? $hr['bundle'] ?? null; $decoded = null; if (is_string($rawBundle) && $rawBundle !== '') { $decoded = json_decode($rawBundle, true); @@ -186,12 +186,31 @@ class AbdmFhirInspector extends BaseController return $opdController->fhir_bundle_preview($opdId, $sessionId); } - // Health Document: DOC-file-{id} or DOC-{id} - if (preg_match('/^DOC-(?:file-)?(\d+)/i', $ref, $m)) { - $docId = (int) $m[1]; - $docController = new \App\Controllers\DoctorDocument(); - $docController->initController($this->request, $this->response, service('logger')); - return $docController->health_document_fhir_preview($docId); + // Health Document: DOC-file-{id}, DOC-{pid}-{ts}, DOC-{id} + if (preg_match('/^DOC-/i', $ref)) { + $docId = 0; + if (preg_match('/^DOC-file-(\d+)/i', $ref, $m)) { + $docId = (int) $m[1]; + } elseif (preg_match('/^DOC-(\d+)-(\d+)/i', $ref, $m)) { + $c1 = (int) $m[1]; + $c2 = (int) $m[2]; + if ($c2 > 1000000000 && $this->db->tableExists('file_upload_data')) { + $fRow = $this->db->table('file_upload_data')->where('file_name LIKE', '%' . $c2 . '%')->get(1)->getRowArray(); + if ($fRow) { + $docId = (int) $fRow['id']; + } + } + if ($docId <= 0) { + $docId = $c1; + } + } elseif (preg_match('/^DOC-(\d+)/i', $ref, $m)) { + $docId = (int) $m[1]; + } + if ($docId > 0) { + $docController = new \App\Controllers\DoctorDocument(); + $docController->initController($this->request, $this->response, service('logger')); + return $docController->health_document_fhir_preview($docId); + } } // Diagnostic Report: LAB-{id} or RAD-{id} diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index 78f855b..d919a97 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -5156,6 +5156,51 @@ class AbdmGateway extends BaseController } } + // Strategy 0d: Live On-Demand Fresh Generation for Health Document Record (DOC-file-{id}, DOC-{id}) + if (preg_match('/^DOC-(?:file-)?(\d+)/i', $ref, $docM)) { + $docTargetId = (int) $docM[1]; + try { + $docPayload = $this->buildHealthDocumentRecordPayload(0, '', $docTargetId); + if ($docPayload !== null && !empty($docPayload['bundle'])) { + $records[] = [ + 'careContextReference' => $ref, + 'hiType' => 'HealthDocumentRecord', + 'display' => $docPayload['care_context_display'] ?? 'Health Document Record', + 'bundle' => $docPayload['bundle'], + ]; + // Persist or cache to health_records for rapid subsequent fetches + if ($db->tableExists('health_records')) { + $exHr = $db->table('health_records')->where('care_context_reference', $ref)->get(1)->getRowArray(); + if ($exHr) { + if (empty($exHr['record_data'])) { + $db->table('health_records')->where('id', (int) $exHr['id'])->update([ + 'record_data' => json_encode($docPayload['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), + 'push_status' => 'linked', + 'updated_at' => date('Y-m-d H:i:s'), + ]); + } + } else { + $db->table('health_records')->insert([ + 'patient_id' => (int) ($docPayload['patient_id'] ?? 0), + 'abha_id' => (string) ($docPayload['abha_id'] ?? ''), + 'hi_type' => 'HealthDocumentRecord', + 'entity_type' => $docPayload['entity_type'] ?? 'patient_document', + 'entity_id' => (string) $docTargetId, + 'care_context_reference' => $ref, + 'record_data' => json_encode($docPayload['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), + 'push_status' => 'linked', + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + } + } + continue; + } + } catch (\Throwable $ex) { + log_message('warning', 'Live HealthDocument FHIR generation in recordsFetch failed: ' . $ex->getMessage()); + } + } + // Strategy A: Check if health_records already has stored FHIR record_data $hrRow = null; if ($db->tableExists('health_records')) { @@ -5851,6 +5896,12 @@ class AbdmGateway extends BaseController continue; } + // ONLY process OPD Consult and Prescription care contexts! + // Never overwrite HealthDocument, Wellness, Lab, Radiology, or Invoice records! + if (! preg_match('/^(?:OPD|PRESCRIPTION|PRESC)-/i', $ref)) { + continue; + } + $sessionId = 0; if (preg_match('/(?:OPD|PRESCRIPTION)-\d+-S(\d+)/i', $ref, $m)) { $sessionId = (int) $m[1]; @@ -7108,15 +7159,21 @@ class AbdmGateway extends BaseController $aliasRef = 'DOC-' . $fd['id'] . '-' . $cleanDate; if ($this->db->tableExists('health_records')) { - $existingHr = $this->db->table('health_records') + $hrQuery = $this->db->table('health_records') ->where('patient_id', $patientId) ->groupStart() ->where('entity_id', (string) $fd['id']) - ->orWhere('care_context_reference LIKE', 'DOC-%' . $fd['id'] . '%') - ->groupEnd() - ->orderBy('id', 'DESC') - ->get(1) - ->getRowArray(); + ->orWhere('care_context_reference LIKE', 'DOC-%' . $fd['id'] . '%'); + if (! empty($fd['file_name'])) { + $hrQuery->orWhere('attachment_path LIKE', '%' . $fd['file_name']); + $hrQuery->orWhere('record_data LIKE', '%' . $fd['file_name'] . '%'); + if (preg_match('/_(\d{10})_/', (string) $fd['file_name'], $tsM)) { + $hrQuery->orWhere('care_context_reference LIKE', 'DOC-%' . $tsM[1] . '%'); + $hrQuery->orWhere('entity_id', $tsM[1]); + } + } + $hrQuery->groupEnd(); + $existingHr = $hrQuery->orderBy('id', 'DESC')->get(1)->getRowArray(); if (! empty($existingHr['care_context_reference'])) { $ccRef = trim((string) $existingHr['care_context_reference']); } @@ -9643,17 +9700,39 @@ class AbdmGateway extends BaseController private function buildHealthDocumentRecordPayload(int $patientId, string $abhaId = '', int $recordId = 0, array $patientRow = []): ?array { - if (empty($patientRow)) { - $patientRow = $this->loadPatientRow($patientId); - if (empty($patientRow)) { - return null; - } - } - $targetRecordId = $recordId > 0 ? $recordId : (int) ($this->request->getPost('record_id') ?? $this->request->getPost('patient_doc_id') ?? $this->request->getPost('document_id') ?? $this->request->getPost('entity_id') ?? 0); + if ($patientId <= 0 && $targetRecordId > 0) { + if ($this->db->tableExists('file_upload_data')) { + $fRow = $this->db->table('file_upload_data')->select('pid')->where('id', $targetRecordId)->get(1)->getRowArray(); + if (! empty($fRow['pid'])) { + $patientId = (int) $fRow['pid']; + } + } + if ($patientId <= 0 && $this->db->tableExists('patient_doc')) { + $pRow = $this->db->table('patient_doc')->select('p_id')->where('id', $targetRecordId)->get(1)->getRowArray(); + if (! empty($pRow['p_id'])) { + $patientId = (int) $pRow['p_id']; + } + } + if ($patientId <= 0 && $this->db->tableExists('health_records')) { + $hrRow = $this->db->table('health_records')->select('patient_id')->groupStart()->where('entity_id', (string) $targetRecordId)->orWhere('care_context_reference LIKE', 'DOC-%' . $targetRecordId . '%')->groupEnd()->get(1)->getRowArray(); + if (! empty($hrRow['patient_id'])) { + $patientId = (int) $hrRow['patient_id']; + } + } + } + + if (empty($patientRow) && $patientId > 0) { + $patientRow = $this->loadPatientRow($patientId); + } + + if (empty($patientRow)) { + return null; + } + if ($targetRecordId > 0 && class_exists('\App\Controllers\DoctorDocument')) { try { $docController = new \App\Controllers\DoctorDocument(); diff --git a/app/Controllers/Api/v1/NursingApi.php b/app/Controllers/Api/v1/NursingApi.php index 54da2d7..8fd4dc7 100644 --- a/app/Controllers/Api/v1/NursingApi.php +++ b/app/Controllers/Api/v1/NursingApi.php @@ -633,6 +633,8 @@ class NursingApi extends BaseController 'ai_status' => 'pending', 'ai_alert_flag' => 0, ]); + + return (int) $db->insertID(); } /** @@ -1300,15 +1302,27 @@ class NursingApi extends BaseController $hiType = 'ImmunizationRecord'; } + // Register in file_upload_data for HMS Scan Doc List popup first + $fileUploadId = $this->registerFileUploadData([ + 'filename' => $filename, + 'public_path' => $imageUrl, + 'p_id' => $patientId, + 'upload_by' => $nurseName, + 'doc_type' => $docCategory, + 'file_size_kb' => round(strlen($binary) / 1024, 2), + ]); + + $cleanDate = date('Ymd', strtotime($docDate)); + $careContextRef = 'DOC-file-' . ($fileUploadId > 0 ? $fileUploadId : time()) . '-' . $cleanDate; + $healthRecordId = null; if ($db->tableExists('health_records')) { - $careContextRef = 'DOC-' . $patientId . '-' . time(); $db->table('health_records')->insert([ 'patient_id' => $patientId, 'abha_id' => $patient['abha_id'] ?? null, 'hi_type' => $hiType, - 'entity_type' => 'document', - 'entity_id' => (string) time(), + 'entity_type' => 'patient_document', + 'entity_id' => (string) ($fileUploadId > 0 ? $fileUploadId : time()), 'attachment_path' => $imageUrl, 'push_status' => 'pending', 'care_context_reference' => $careContextRef, @@ -1318,6 +1332,7 @@ class NursingApi extends BaseController 'remarks' => $remarks, 'uploaded_by' => $nurseName, 'image_url' => $imageUrl, + 'file_upload_id' => $fileUploadId, ]), 'created_by_name' => $nurseName, 'created_at' => date('Y-m-d H:i:s'), @@ -1326,22 +1341,13 @@ class NursingApi extends BaseController $healthRecordId = $db->insertID(); } - // Register in file_upload_data for HMS Scan Doc List popup - $this->registerFileUploadData([ - 'filename' => $filename, - 'public_path' => $imageUrl, - 'p_id' => $patientId, - 'upload_by' => $nurseName, - 'doc_type' => $docCategory, - 'file_size_kb' => round(strlen($binary) / 1024, 2), - ]); - return $this->response->setJSON([ 'status' => 1, 'message' => 'ABDM M2 Health Document uploaded successfully', 'image_url' => $imageUrl, 'health_record_id' => $healthRecordId, 'hi_type' => $hiType, + 'care_context_reference' => $careContextRef, ]); } } diff --git a/app/Controllers/DoctorDocument.php b/app/Controllers/DoctorDocument.php index 6b9fa20..07d1fd6 100644 --- a/app/Controllers/DoctorDocument.php +++ b/app/Controllers/DoctorDocument.php @@ -2134,6 +2134,22 @@ class DoctorDocument extends BaseController if (is_array($fileRow) && ! empty($fileRow)) { return $this->buildFileUploadHealthDocumentSource($fileRow); } + if ($patientDocId > 1000000000) { + $fileRow = $this->db->table('file_upload_data')->where('file_name LIKE', '%' . $patientDocId . '%')->get(1)->getRowArray(); + if (is_array($fileRow) && ! empty($fileRow)) { + return $this->buildFileUploadHealthDocumentSource($fileRow); + } + } + } + if ($this->db->tableExists('health_records')) { + $hr = $this->db->table('health_records')->groupStart()->where('care_context_reference LIKE', '%' . $patientDocId . '%')->orWhere('entity_id', (string) $patientDocId)->groupEnd()->get(1)->getRowArray(); + if ($hr && ! empty($hr['attachment_path']) && $this->db->tableExists('file_upload_data')) { + $attFileName = basename((string) $hr['attachment_path']); + $fileRow = $this->db->table('file_upload_data')->where('file_name', $attFileName)->orWhere('orig_name', $attFileName)->get(1)->getRowArray(); + if (is_array($fileRow) && ! empty($fileRow)) { + return $this->buildFileUploadHealthDocumentSource($fileRow); + } + } } return []; } @@ -2559,6 +2575,28 @@ class DoctorDocument extends BaseController $cleanVisitDate = date('Ymd', strtotime($visitDate)); $ccRef = 'DOC-file-' . $fileUploadId . '-' . $cleanVisitDate; + if ($this->db->tableExists('health_records')) { + $hrQuery = $this->db->table('health_records') + ->where('patient_id', $pid) + ->groupStart() + ->where('entity_id', (string) $fileUploadId) + ->orWhere('care_context_reference LIKE', 'DOC-%' . $fileUploadId . '%'); + $fileName = trim((string) ($fileRow['file_name'] ?? '')); + if ($fileName !== '') { + $hrQuery->orWhere('attachment_path LIKE', '%' . $fileName); + $hrQuery->orWhere('record_data LIKE', '%' . $fileName . '%'); + if (preg_match('/_(\d{10})_/', $fileName, $tsM)) { + $hrQuery->orWhere('care_context_reference LIKE', 'DOC-%' . $tsM[1] . '%'); + $hrQuery->orWhere('entity_id', $tsM[1]); + } + } + $hrQuery->groupEnd(); + $existingHr = $hrQuery->orderBy('id', 'DESC')->get(1)->getRowArray(); + if (! empty($existingHr['care_context_reference'])) { + $ccRef = trim((string) $existingHr['care_context_reference']); + } + } + return [ 'record_id' => 'file-' . $fileUploadId, 'session_id' => (string) ($fileRow['opd_id'] ?? $fileUploadId), @@ -2673,6 +2711,7 @@ class DoctorDocument extends BaseController $fileName = trim((string) ($fileRow['file_name'] ?? $fileRow['orig_name'] ?? '')); $fullPath = trim((string) ($fileRow['full_path'] ?? '')); $dirPath = trim((string) ($fileRow['file_path'] ?? '')); + $publicPath = trim((string) ($fileRow['public_path'] ?? '')); $candidates = []; if ($fullPath !== '') { @@ -2682,11 +2721,19 @@ class DoctorDocument extends BaseController if ($dirPath !== '' && $fileName !== '') { $candidates[] = rtrim($dirPath, '/\\') . DIRECTORY_SEPARATOR . $fileName; } + if ($publicPath !== '' && defined('FCPATH')) { + $candidates[] = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . ltrim($publicPath, '/\\'); + $candidates[] = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . ltrim(preg_replace('#^/?uploads/#i', '', $publicPath), '/\\'); + } $baseDirs = [ defined('FCPATH') ? FCPATH : '', defined('FCPATH') ? FCPATH . 'uploads' : '', + defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'health_documents' : '', + defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'nabh_ipd' : '', + defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'patient_documents' : '', defined('ROOTPATH') ? ROOTPATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' : '', + defined('ROOTPATH') ? ROOTPATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'health_documents' : '', ]; $insertDate = ! empty($fileRow['insert_date']) ? (string) $fileRow['insert_date'] : ''; @@ -2713,6 +2760,10 @@ class DoctorDocument extends BaseController if (! empty($matches) && is_file($matches[0]) && is_readable($matches[0])) { return $matches[0]; } + $matchesDeep = glob(rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . $fileName); + if (! empty($matchesDeep) && is_file($matchesDeep[0]) && is_readable($matchesDeep[0])) { + return $matchesDeep[0]; + } } return ''; diff --git a/app/Views/abdm/fhir_inspector.php b/app/Views/abdm/fhir_inspector.php index 7ccecc6..df55fc8 100644 --- a/app/Views/abdm/fhir_inspector.php +++ b/app/Views/abdm/fhir_inspector.php @@ -39,7 +39,7 @@ ABDM Task Board - '; } else { - actionBtn = ''; + actionBtn = ''; } html += ''; @@ -605,14 +607,18 @@ var btnLinkHead = document.getElementById('btnLinkThisPatient'); if (btnLinkHead) { btnLinkHead.addEventListener('click', function() { - if (currentPatientData) { - openAbdmHipLinkModal(currentPatientData.id, currentPatientData.abha_address, { - name: currentPatientData.name, - gender: currentPatientData.gender, - yob: currentPatientData.year_of_birth, - phone: currentPatientData.phone, + var pData = window.inspCurrentPatientData || currentPatientData; + if (pData) { + openAbdmHipLinkModal(pData.id, pData.abha_address, { + name: pData.name, + gender: pData.gender, + yob: pData.year_of_birth, + phone: pData.phone, + uhid: pData.patient_ref || pData.p_code, onLinked: function() { - inspectPatient(currentPatientData.abha_address); + if (typeof window.inspectPatient === 'function' && pData.abha_address) { + window.inspectPatient(pData.abha_address); + } } }); } else { @@ -621,35 +627,59 @@ }); } - // Link Context Button (Row action) - document.addEventListener('click', function(e) { - var linkBtn = e.target.closest('.btn-table-link'); - if (linkBtn && currentPatientData) { - var ref = linkBtn.dataset.ref; - var type = linkBtn.dataset.type; - openAbdmHipLinkModal(currentPatientData.id, currentPatientData.abha_address, { - name: currentPatientData.name, - gender: currentPatientData.gender, - yob: currentPatientData.year_of_birth, - phone: currentPatientData.phone, - care_context: ref, - task_type: type, - onLinked: function() { - inspectPatient(currentPatientData.abha_address); - } - }); - } - }); + window.inspOpenFhirBundleModal = openFhirBundleModal; - // Preview FHIR Button (Row action) - document.addEventListener('click', function(e) { - var fhirBtn = e.target.closest('.btn-open-fhir'); - if (fhirBtn) { - var ref = fhirBtn.dataset.ref; - var hiType = fhirBtn.dataset.hitype || 'Record'; - openFhirBundleModal(ref, hiType); - } - }); + // Singleton Document-level Click Handler to prevent multiple listener duplication on SPA reloads + if (!window._inspDocClickListenerAttached) { + window._inspDocClickListenerAttached = true; + + document.addEventListener('click', function(e) { + var linkBtn = e.target.closest('.btn-table-link'); + if (linkBtn && window.inspCurrentPatientData) { + var pData = window.inspCurrentPatientData; + var ref = linkBtn.dataset.ref; + var type = linkBtn.dataset.type; + var entityId = linkBtn.dataset.entityId || ''; + var taskType = ''; + if (type === 'HealthDocumentRecord') { + taskType = 'health_document_publish'; + } else if (type === 'OPConsultRecord') { + taskType = 'opd_prescription_publish'; + } else if (type === 'DiagnosticReportRecord' || type === 'DiagnosticReport') { + taskType = 'lab_report_publish'; + } else if (type === 'WellnessRecord') { + taskType = 'wellness_record_publish'; + } else if (type === 'InvoiceRecord') { + taskType = 'invoice_publish'; + } + + openAbdmHipLinkModal(pData.id, pData.abha_address, { + name: pData.name, + gender: pData.gender, + yob: pData.year_of_birth, + phone: pData.phone, + uhid: pData.patient_ref || pData.p_code, + care_context: ref, + task_type: taskType, + entity_id: entityId, + onLinked: function() { + if (typeof window.inspectPatient === 'function' && pData.abha_address) { + window.inspectPatient(pData.abha_address); + } + } + }); + return; + } + + var fhirBtn = e.target.closest('.btn-open-fhir'); + if (fhirBtn && typeof window.inspOpenFhirBundleModal === 'function') { + var ref = fhirBtn.dataset.ref; + var hiType = fhirBtn.dataset.hitype || 'Record'; + window.inspOpenFhirBundleModal(ref, hiType); + return; + } + }); + } function openFhirBundleModal(ref, hiType) { var modalEl = document.getElementById('inspFhirModal'); diff --git a/app/Views/partials/abdm_hip_link_modal.php b/app/Views/partials/abdm_hip_link_modal.php index 5fa6689..7dea11a 100644 --- a/app/Views/partials/abdm_hip_link_modal.php +++ b/app/Views/partials/abdm_hip_link_modal.php @@ -406,7 +406,11 @@ /** * Load patient demographics and care contexts from server. */ + window._hipLoadSeq = 0; window.loadPatientCareContexts = function(patientId, abhaAddress, taskType, entityId, taskId, source) { + window._hipLoadSeq = (window._hipLoadSeq || 0) + 1; + var thisSeq = window._hipLoadSeq; + var loading = document.getElementById('hipCareContextsLoading'); var container = document.getElementById('hipCareContextsContainer'); if (loading) loading.classList.remove('d-none'); @@ -435,6 +439,18 @@ }) .then(function(r) { return r.json(); }) .then(function(data) { + // Discard stale response if a newer request was made + if (thisSeq !== window._hipLoadSeq) { + return; + } + // Discard response if patient mismatch with current context + if (window.currentHipTaskContext && window.currentHipTaskContext.patientId) { + var expectedPid = parseInt(window.currentHipTaskContext.patientId, 10); + if (data.patient && data.patient.id && parseInt(data.patient.id, 10) !== expectedPid) { + return; + } + } + if (loading) loading.classList.add('d-none'); updateCsrf(data); @@ -554,6 +570,12 @@ document.getElementById('hipGender').value = prefill.gender || 'M'; document.getElementById('hipYob').value = prefill.year_of_birth || prefill.yob || ''; document.getElementById('hipSmsPhone').value = prefill.phone || prefill.mphone1 || ''; + + var uhidBadge = document.getElementById('hipPatientUhidBadge'); + var initialUhid = prefill.uhid || prefill.patient_ref || prefill.p_code || prefill.uhid_no || ''; + if (uhidBadge && initialUhid) { + uhidBadge.textContent = 'UHID: ' + initialUhid; + } var alertBox = document.getElementById('hipLinkStatusAlert'); if (alertBox) { alertBox.classList.add('d-none'); alertBox.innerHTML = ''; } @@ -601,15 +623,41 @@ }; function syncAndPushTaskFhirRecord(selectedContexts, alertBox, abhaAddress) { - if (!window.currentHipTaskContext || !window.currentHipTaskContext.taskType || !window.currentHipTaskContext.entityId) { - return; - } - var ctxTaskType = window.currentHipTaskContext.taskType; - var ctxEntityId = window.currentHipTaskContext.entityId; - var ctxPatientId = window.currentHipTaskContext.patientId || 0; + var ctxTaskType = (window.currentHipTaskContext && window.currentHipTaskContext.taskType) ? window.currentHipTaskContext.taskType : ''; + var ctxEntityId = (window.currentHipTaskContext && window.currentHipTaskContext.entityId) ? window.currentHipTaskContext.entityId : ''; + var ctxPatientId = (window.currentHipTaskContext && window.currentHipTaskContext.patientId) ? window.currentHipTaskContext.patientId : 0; var firstRef = (selectedContexts && selectedContexts.length > 0) ? selectedContexts[0].ref : ''; var csrf = getCsrfData(); + // Auto-infer task type and entity ID from reference if not explicitly set + if (!ctxTaskType && firstRef) { + if (/^DOC-/i.test(firstRef)) { + ctxTaskType = 'health_document_publish'; + var dm = firstRef.match(/^DOC-(?:file-)?(\d+)/i); + if (dm && !ctxEntityId) ctxEntityId = dm[1]; + } else if (/^(?:OPD|PRESCRIPTION)-/i.test(firstRef)) { + ctxTaskType = 'opd_prescription_publish'; + var om = firstRef.match(/^(?:OPD|PRESCRIPTION)-(\d+)/i); + if (om && !ctxEntityId) ctxEntityId = om[1]; + } else if (/^WELLNESS-/i.test(firstRef)) { + ctxTaskType = 'wellness_record_publish'; + var wm = firstRef.match(/^WELLNESS-(?:(\d+)-W)?(\d+)/i); + if (wm && !ctxEntityId) ctxEntityId = wm[2] || wm[1]; + } else if (/^INV-/i.test(firstRef)) { + ctxTaskType = 'invoice_publish'; + var im = firstRef.match(/^INV-(?:[A-Z]+-)?(\d+)/i); + if (im && !ctxEntityId) ctxEntityId = im[1]; + } else if (/^(?:LAB|RAD)-/i.test(firstRef)) { + ctxTaskType = 'lab_report_publish'; + var lm = firstRef.match(/^(?:LAB|RAD)-(\d+)/i); + if (lm && !ctxEntityId) ctxEntityId = lm[1]; + } + } + + if (!ctxTaskType) { + return; + } + if (ctxTaskType === 'radiology_report_publish' || ctxTaskType === 'lab_report_publish') { var pushData = new URLSearchParams(); pushData.append('lab_req_id', ctxEntityId); @@ -748,6 +796,10 @@ } }).catch(function() {}); } else if (ctxTaskType === 'health_document_publish') { + if (!ctxEntityId && firstRef) { + var dm = firstRef.match(/^DOC-(?:file-)?(\d+)/i); + if (dm) ctxEntityId = dm[1]; + } var pushData = new URLSearchParams(); pushData.append('record_id', ctxEntityId); pushData.append('entity_id', ctxEntityId); diff --git a/public/App/Nursing/index.html b/public/App/Nursing/index.html index 32f6f13..1d9ddd0 100644 --- a/public/App/Nursing/index.html +++ b/public/App/Nursing/index.html @@ -506,6 +506,7 @@ document_date: new Date().toISOString().slice(0, 10), remarks: '' }); + const [isSavingDoc, setIsSavingDoc] = useState(false); // Vitals History Modal State const [wellnessHistoryPatient, setWellnessHistoryPatient] = useState(null); @@ -1142,11 +1143,12 @@ }; const handleSavePatientDocument = async () => { - if (!activeDocPatient) return; + if (!activeDocPatient || isSavingDoc) return; if (!scanImageBase64) { alert('Please capture or upload a document photo first.'); return; } + setIsSavingDoc(true); try { const payload = { patient_id: activeDocPatient.id, @@ -1171,6 +1173,8 @@ } } catch (err) { alert('Error uploading health document'); + } finally { + setIsSavingDoc(false); } }; @@ -3017,9 +3021,13 @@
- - +