fix(abdm): resolve patient flipping, duplicate document contexts, and health document FHIR fetch

This commit is contained in:
DevSoftTech Server 2026-10-07 13:24:05 +05:30
parent d04f1d5c24
commit db76ad9759
7 changed files with 325 additions and 80 deletions

View File

@ -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}

View File

@ -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();

View File

@ -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,
]);
}
}

View File

@ -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 '';

View File

@ -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');

View File

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

View File

@ -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>