fix(abdm): resolve patient flipping, duplicate document contexts, and health document FHIR fetch
This commit is contained in:
parent
d04f1d5c24
commit
db76ad9759
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 '';
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
<a href="javascript:load_form('<?= base_url('AbdmTaskBoard') ?>','ABDM Task Board')" class="btn btn-sm btn-outline-primary fw-semibold">
|
||||
<i class="bi bi-kanban me-1"></i> ABDM Task Board
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-success fw-semibold" onclick="openAbdmHipLinkModal()">
|
||||
<button type="button" class="btn btn-sm btn-outline-success fw-semibold" onclick="if (window.inspCurrentPatientData) { openAbdmHipLinkModal(window.inspCurrentPatientData.id, window.inspCurrentPatientData.abha_address, { name: window.inspCurrentPatientData.name, gender: window.inspCurrentPatientData.gender, yob: window.inspCurrentPatientData.year_of_birth, phone: window.inspCurrentPatientData.phone, uhid: window.inspCurrentPatientData.patient_ref || window.inspCurrentPatientData.p_code, onLinked: function() { if (typeof window.inspectPatient === 'function' && window.inspCurrentPatientData && window.inspCurrentPatientData.abha_address) { window.inspectPatient(window.inspCurrentPatientData.abha_address); } } }); } else { openAbdmHipLinkModal(); }">
|
||||
<i class="bi bi-link-45deg me-1"></i> Link Records to ABHA (HIP)
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-info fw-semibold" onclick="openAbdmHipSmsModal()">
|
||||
|
|
@ -364,11 +364,13 @@
|
|||
document.getElementById('kpiUnlinked').textContent = '0';
|
||||
document.getElementById('kpiFhir').textContent = '0';
|
||||
currentPatientData = null;
|
||||
window.inspCurrentPatientData = null;
|
||||
currentCareContexts = [];
|
||||
return;
|
||||
}
|
||||
|
||||
currentPatientData = res.patient;
|
||||
window.inspCurrentPatientData = res.patient;
|
||||
currentCareContexts = res.care_contexts || [];
|
||||
var linkedRefs = res.linked_refs || [];
|
||||
|
||||
|
|
@ -458,7 +460,7 @@
|
|||
} else if (!isFhir) {
|
||||
actionBtn = '<button type="button" class="btn btn-sm btn-outline-secondary py-0 px-2" disabled title="Clinical findings must be documented before linking to ABDM"><i class="bi bi-clock me-1"></i>Incomplete</button>';
|
||||
} else {
|
||||
actionBtn = '<button type="button" class="btn btn-sm btn-outline-success py-0 px-2 btn-table-link" data-ref="' + hesc(ref) + '" data-type="' + hesc(hiType) + '"><i class="bi bi-link-45deg me-1"></i>Link to ABHA</button>';
|
||||
actionBtn = '<button type="button" class="btn btn-sm btn-outline-success py-0 px-2 btn-table-link" data-ref="' + hesc(ref) + '" data-type="' + hesc(hiType) + '" data-entity-id="' + hesc(c.entity_id || '') + '"><i class="bi bi-link-45deg me-1"></i>Link to ABHA</button>';
|
||||
}
|
||||
|
||||
html += '<tr class="care-context-row">';
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
</div>
|
||||
|
||||
<div className="modal-footer p-2 bg-light">
|
||||
<button className="btn btn-secondary btn-sm" onClick={() => setActiveDocPatient(null)}>Cancel</button>
|
||||
<button className="btn btn-primary btn-sm fw-bold px-3" onClick={handleSavePatientDocument} disabled={!scanImageBase64}>
|
||||
<i className="bi bi-cloud-upload-fill me-1"></i> Upload ABDM Health Document
|
||||
<button className="btn btn-secondary btn-sm" onClick={() => setActiveDocPatient(null)} disabled={isSavingDoc}>Cancel</button>
|
||||
<button className="btn btn-primary btn-sm fw-bold px-3" onClick={handleSavePatientDocument} disabled={!scanImageBase64 || isSavingDoc}>
|
||||
{isSavingDoc ? (
|
||||
<span><span className="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Uploading...</span>
|
||||
) : (
|
||||
<span><i className="bi bi-cloud-upload-fill me-1"></i> Upload ABDM Health Document</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user