diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index 272c657..039b073 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -6241,6 +6241,70 @@ class AbdmGateway extends BaseController 'is_primary' => true, ]); } + } elseif ($taskType === 'immunization_record_publish' && $this->db->tableExists('immunization_records')) { + $imm = $this->db->table('immunization_records') + ->select('id, patient_id, vaccine_name, given_date, abdm_care_context_reference') + ->where('id', $taskEntityId) + ->get(1) + ->getRowArray(); + if (! empty($imm)) { + $admDate = ! empty($imm['given_date']) && strtotime((string) $imm['given_date']) > 0 + ? date('Y-m-d', strtotime((string) $imm['given_date'])) + : date('Y-m-d'); + $dateStr = date('d M Y', strtotime($admDate)); + $ccRef = ! empty($imm['abdm_care_context_reference']) + ? trim((string) $imm['abdm_care_context_reference']) + : ('IMM-' . $taskEntityId); + $display = 'ImmunizationRecord - ' . ($imm['vaccine_name'] ?? 'Vaccine') . ' (' . $dateStr . ')'; + + $addContext([ + 'careContextId' => $ccRef, + 'referenceNumber' => $ccRef, + 'display' => $display, + 'record_type' => 'ImmunizationRecord', + 'patient_id' => $patientId, + 'is_fhir_ready' => true, + 'is_primary' => true, + ]); + } + } elseif ($taskType === 'health_document_publish') { + if ($this->db->tableExists('patient_doc')) { + $pdoc = $this->db->table('patient_doc')->where('id', $taskEntityId)->get(1)->getRowArray(); + if (! empty($pdoc)) { + $docDate = ! empty($pdoc['date_issue']) ? $pdoc['date_issue'] : (! empty($pdoc['created_at']) ? $pdoc['created_at'] : date('Y-m-d')); + $dateStr = date('d M Y', strtotime((string) $docDate)); + $ccRef = 'DOC-' . $taskEntityId . '-' . date('Ymd', strtotime((string) $docDate)); + $display = 'HealthDocumentRecord - ' . (! empty($pdoc['doc_name']) ? $pdoc['doc_name'] : 'Medical Document') . ' (' . $dateStr . ')'; + $addContext([ + 'careContextId' => $ccRef, + 'referenceNumber' => $ccRef, + 'display' => $display, + 'record_type' => 'HealthDocumentRecord', + 'patient_id' => $patientId, + 'is_fhir_ready' => true, + 'is_primary' => true, + ]); + } + } + } elseif ($taskType === 'wellness_record_publish') { + if ($this->db->tableExists('opd_prescription')) { + $wopd = $this->db->table('opd_prescription')->where('id', $taskEntityId)->get(1)->getRowArray(); + if (! empty($wopd)) { + $vDate = ! empty($wopd['date_opd_visit']) ? $wopd['date_opd_visit'] : date('Y-m-d'); + $dateStr = date('d M Y', strtotime((string) $vDate)); + $ccRef = 'WELLNESS-' . $patientId . '-S' . $taskEntityId . '-' . date('Ymd', strtotime((string) $vDate)); + $display = 'WellnessRecord - ' . $dateStr; + $addContext([ + 'careContextId' => $ccRef, + 'referenceNumber' => $ccRef, + 'display' => $display, + 'record_type' => 'WellnessRecord', + 'patient_id' => $patientId, + 'is_fhir_ready' => true, + 'is_primary' => true, + ]); + } + } } } @@ -6419,20 +6483,33 @@ class AbdmGateway extends BaseController // 6. Query immunization_records / patient_immunization if exists if ($this->db->tableExists('immunization_records')) { - $immRows = $this->db->table('immunization_records') - ->select('id, patient_id, vaccine_name, given_date, abdm_care_context_reference') - ->where('patient_id', $patientId) - ->orderBy('id', 'DESC') - ->limit(10) + $immFields = $this->db->getFieldNames('immunization_records') ?? []; + $hasStatus = in_array('status', $immFields, true); + $builder = $this->db->table('immunization_records') + ->select('id, patient_id, vaccine_name, given_date, abdm_care_context_reference' . ($hasStatus ? ', status' : '')) + ->where('patient_id', $patientId); + + if ($hasStatus) { + // Prioritize completed records, and don't omit administered ones + $builder->orderBy("CASE WHEN status = 'completed' THEN 1 WHEN given_date IS NOT NULL AND given_date > '1970-01-01' THEN 2 ELSE 3 END", 'ASC', false); + } + $immRows = $builder->orderBy('id', 'DESC') + ->limit(30) ->get() ->getResultArray(); foreach ($immRows as $imm) { - $admDate = ! empty($imm['given_date']) ? date('Y-m-d', strtotime((string) $imm['given_date'])) : date('Y-m-d'); + // If status is present and 'due' (and not given), skip unadministered vaccines + if ($hasStatus && strtolower((string) ($imm['status'] ?? '')) === 'due' && (empty($imm['given_date']) || $imm['given_date'] === '0000-00-00 00:00:00')) { + continue; + } + $admDate = ! empty($imm['given_date']) && strtotime((string) $imm['given_date']) > 0 + ? date('Y-m-d', strtotime((string) $imm['given_date'])) + : date('Y-m-d'); $dateStr = date('d M Y', strtotime($admDate)); $ccRef = ! empty($imm['abdm_care_context_reference']) ? trim((string) $imm['abdm_care_context_reference']) - : ('IMM-' . $imm['id'] . '-' . str_replace('-', '', $admDate)); + : ('IMM-' . $imm['id']); $display = 'ImmunizationRecord - ' . ($imm['vaccine_name'] ?? 'Vaccine') . ' (' . $dateStr . ')'; $addContext([ diff --git a/app/Controllers/AbdmTaskBoard.php b/app/Controllers/AbdmTaskBoard.php index 33bcfa8..a48f499 100644 --- a/app/Controllers/AbdmTaskBoard.php +++ b/app/Controllers/AbdmTaskBoard.php @@ -147,7 +147,7 @@ class AbdmTaskBoard extends BaseController unset($task); } - // 2. Lookup health_records by entity_id + // 2. Lookup health_records by entity_id with strict entity_type / patient_id scoping $latestHr = []; if ($db->tableExists('health_records')) { $entityIds = []; @@ -161,23 +161,93 @@ class AbdmTaskBoard extends BaseController if ($entityIds !== []) { $rows = $db->table('health_records') - ->select('id, entity_type, entity_id, push_status, care_context_reference, linked_at, abdm_txn_id') + ->select('id, patient_id, entity_type, hi_type, entity_id, push_status, care_context_reference, linked_at, abdm_txn_id') ->whereIn('entity_id', $entityIds) ->orderBy('id', 'DESC') ->get() ->getResultArray(); foreach ($rows as $row) { + $pId = (int) ($row['patient_id'] ?? 0); + $eType = strtolower(trim((string) ($row['entity_type'] ?? ''))); + $hiType = strtolower(trim((string) ($row['hi_type'] ?? ''))); $eid = (string) ($row['entity_id'] ?? ''); - if ($eid !== '' && ! isset($latestHr[$eid])) { - $latestHr[$eid] = $row; + if ($eid !== '') { + if ($pId > 0 && $eType !== '') { + $k1 = $pId . '_' . $eType . '_' . $eid; + if (! isset($latestHr[$k1])) { + $latestHr[$k1] = $row; + } + } + if ($pId > 0 && $hiType !== '') { + $k2 = $pId . '_' . $hiType . '_' . $eid; + if (! isset($latestHr[$k2])) { + $latestHr[$k2] = $row; + } + } + if ($eType !== '') { + $k3 = $eType . '_' . $eid; + if (! isset($latestHr[$k3])) { + $latestHr[$k3] = $row; + } + } + if (! isset($latestHr[$eid])) { + $latestHr[$eid] = $row; + } } } } } foreach ($tasks as &$task) { + $pId = (int) ($task['patient_id'] ?? 0); + $taskType = strtolower(trim((string) ($task['task_type'] ?? ''))); + $taskEntityType = strtolower(trim((string) ($task['entity_type'] ?? ''))); $eid = (string) ($task['entity_id'] ?? ''); - $hr = $latestHr[$eid] ?? null; + + // Map task type or entity type to standard health_record entity types / hi_types + $candidateTypes = array_values(array_filter([$taskEntityType])); + if ($taskType === 'immunization_record_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['immunization', 'immunizationrecord']))); + } elseif ($taskType === 'opd_prescription_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['opd', 'opd_prescription', 'opconsultrecord']))); + } elseif ($taskType === 'ipd_discharge_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['ipd', 'ipd_discharge', 'dischargesummaryrecord']))); + } elseif ($taskType === 'lab_report_publish' || $taskType === 'radiology_report_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['lab', 'radiology', 'diagnosticreportrecord']))); + } elseif ($taskType === 'wellness_record_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['opd_vitals', 'opd', 'wellness', 'wellnessrecord']))); + } elseif ($taskType === 'health_document_publish') { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['patient_document', 'doctor_document', 'file_upload_data', 'patient_doc', 'healthdocumentrecord']))); + } elseif (in_array($taskType, ['invoice_publish', 'invoice_record_publish'], true)) { + $candidateTypes = array_values(array_unique(array_merge($candidateTypes, ['invoice', 'opd_invoice', 'charges_invoice', 'ipd_invoice', 'invoicerecord']))); + } + + $hr = null; + // 1) Match with patient_id and entity/hi type + if ($pId > 0) { + foreach ($candidateTypes as $cType) { + $key = $pId . '_' . strtolower($cType) . '_' . $eid; + if (isset($latestHr[$key])) { + $hr = $latestHr[$key]; + break; + } + } + } + // 2) Match with entity/hi type without patient_id + if ($hr === null) { + foreach ($candidateTypes as $cType) { + $key = strtolower($cType) . '_' . $eid; + if (isset($latestHr[$key])) { + $hr = $latestHr[$key]; + break; + } + } + } + // 3) Only fallback to raw $eid if task has no specific taskType or candidateTypes + if ($hr === null && empty($candidateTypes) && isset($latestHr[$eid])) { + $hr = $latestHr[$eid]; + } + $pushStatus = strtolower(trim((string) ($hr['push_status'] ?? ''))); $task['bridge_health_record_id'] = (int) ($hr['id'] ?? 0); $task['bridge_push_status'] = $pushStatus; @@ -185,7 +255,7 @@ class AbdmTaskBoard extends BaseController $careContext = trim((string) ($hr['care_context_reference'] ?? '')); if ($careContext === '') { $resultText = (string) ($task['last_action_result'] ?? ''); - if (preg_match('/(RAD-\d+-\d+|LAB-\d+-\d+|OPD-\d+-\d+|INVOICE-\w+-\d+-\d+|IMM-\d+-\d+|DIS-\d+-\d+)/i', $resultText, $ccm)) { + if (preg_match('/((?:RAD|LAB|OPD|INVOICE|IMM|DISCHARGE|DIS|WELLNESS|DOC)-[A-Za-z0-9_-]+)/i', $resultText, $ccm)) { $careContext = $ccm[1]; } } diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php index 9585471..4f4fa93 100644 --- a/app/Controllers/Opd_prescription.php +++ b/app/Controllers/Opd_prescription.php @@ -11887,6 +11887,7 @@ class Opd_prescription extends BaseController $masterTable = $this->findExistingTable(['opd_med_master']); $masterById = []; + $masterByName = []; if ($masterTable !== null) { $masterFields = $this->db->getFieldNames($masterTable) ?? []; $masterNameField = $this->resolveFirstField($masterFields, ['item_name', 'med_name']); @@ -11896,14 +11897,19 @@ class Opd_prescription extends BaseController $masterAtcField = $this->resolveFirstField($masterFields, ['atc_code', 'who_atc_code']); $medIds = []; + $medNames = []; foreach ($rows as $row) { $mid = (int) ($row['med_id'] ?? 0); if ($mid > 0) { $medIds[$mid] = $mid; } + $rawName = strtoupper(trim((string) ($row['med_name'] ?? ''))); + if ($rawName !== '') { + $medNames[$rawName] = $rawName; + } } - if (! empty($medIds)) { + if (! empty($medIds) || ! empty($medNames)) { $select = ['id']; if ($masterNameField !== null) { $select[] = $masterNameField . ' as item_name'; @@ -11921,17 +11927,54 @@ class Opd_prescription extends BaseController $select[] = $masterAtcField . ' as atc_code'; } - $masterRows = $this->db->table($masterTable) - ->select(implode(',', $select)) - ->whereIn('id', array_values($medIds)) - ->get() - ->getResultArray(); + $query = $this->db->table($masterTable)->select(implode(',', $select)); + if (! empty($medIds) && ! empty($medNames) && $masterNameField !== null) { + $query->groupStart() + ->whereIn('id', array_values($medIds)) + ->orWhereIn($masterNameField, array_values($medNames)) + ->groupEnd(); + } elseif (! empty($medIds)) { + $query->whereIn('id', array_values($medIds)); + } elseif (! empty($medNames) && $masterNameField !== null) { + $query->whereIn($masterNameField, array_values($medNames)); + } + + $masterRows = $query->get()->getResultArray(); foreach ($masterRows as $mrow) { $mid = (int) ($mrow['id'] ?? 0); if ($mid > 0) { $masterById[$mid] = $mrow; } + $iname = strtoupper(trim((string) ($mrow['item_name'] ?? ''))); + if ($iname !== '') { + $masterByName[$iname] = $mrow; + } + } + } + } + + // Secondary lookup in mst_items for SNOMED CT and generic name + $mstItemsByName = []; + if ($this->db->tableExists('mst_items')) { + $mstNames = []; + foreach ($rows as $row) { + $rawName = strtoupper(trim((string) ($row['med_name'] ?? ''))); + if ($rawName !== '') { + $mstNames[$rawName] = $rawName; + } + } + if (! empty($mstNames)) { + $mstRows = $this->db->table('mst_items') + ->select('item_name, generic_name, snomed_ct_code, snomed_display') + ->whereIn('item_name', array_values($mstNames)) + ->get() + ->getResultArray(); + foreach ($mstRows as $item) { + $k = strtoupper(trim((string) ($item['item_name'] ?? ''))); + if ($k !== '') { + $mstItemsByName[$k] = $item; + } } } } @@ -11944,11 +11987,29 @@ class Opd_prescription extends BaseController $medications = []; foreach ($rows as $row) { $dose = $this->resolveDoseMasterLabel($row['dosage'] ?? '', $doseMap); + if ($dose === '' && ! empty($row['dosage_str'])) { + $dose = trim((string) $row['dosage_str']); + } + $when = $this->resolveDoseMasterLabel($row['dosage_when'] ?? '', $whenMap); + if ($when === '' && ! empty($row['dosage_when_str'])) { + $when = trim((string) $row['dosage_when_str']); + } + $frequency = $this->resolveDoseMasterLabel($row['dosage_freq'] ?? '', $frequencyMap); + if ($frequency === '' && ! empty($row['dosage_freq_str'])) { + $frequency = trim((string) $row['dosage_freq_str']); + } + $route = $this->resolveDoseMasterLabel($row['dosage_where'] ?? '', $routeMap); + if ($route === '' && ! empty($row['dosage_where_str'])) { + $route = trim((string) $row['dosage_where_str']); + } + $quantity = trim((string) ($row['qty'] ?? '')); $duration = trim((string) ($row['no_of_days'] ?? '')); + $remark = trim((string) ($row['remark'] ?? '')); + if (stripos($when, 'week') !== false && stripos($frequency, 'week') !== false) { $frequency = ''; } @@ -11967,21 +12028,35 @@ class Opd_prescription extends BaseController $frequency, $duration, $quantity !== '' && $quantity !== '0' ? ('Qty: ' . $quantity) : '', + $remark !== '' ? ('Instructions: ' . $remark) : '', ]); $mid = (int) ($row['med_id'] ?? 0); + $rawDrugName = trim((string) ($row['med_name'] ?? '')); $master = $mid > 0 ? ($masterById[$mid] ?? []) : []; - $drugName = trim((string) ($row['med_name'] ?? '')); - if ($drugName === '') { - $drugName = trim((string) ($master['item_name'] ?? '')); + if (empty($master) && $rawDrugName !== '') { + $master = $masterByName[strtoupper($rawDrugName)] ?? []; } + $drugName = $rawDrugName !== '' ? $rawDrugName : trim((string) ($master['item_name'] ?? '')); $genericName = trim((string) ($master['genericname'] ?? '')); $medType = trim((string) ($row['med_type'] ?? '')); if ($medType === '') { $medType = trim((string) ($master['formulation'] ?? '')); } + $snomedCode = trim((string) ($master['snomed_code'] ?? '')); + $atcCode = strtoupper(trim((string) ($master['atc_code'] ?? ''))); + + // Fallback from mst_items if snomed_code is not in opd_med_master + if ($snomedCode === '' && ! empty($mstItemsByName[strtoupper($drugName)])) { + $mst = $mstItemsByName[strtoupper($drugName)]; + $snomedCode = trim((string) ($mst['snomed_ct_code'] ?? '')); + if ($genericName === '' && ! empty($mst['generic_name'])) { + $genericName = trim((string) $mst['generic_name']); + } + } + $medications[] = [ 'drug_name' => $drugName, 'status' => 'active', @@ -11991,8 +12066,8 @@ class Opd_prescription extends BaseController 'med_id' => $mid, 'generic_name' => $genericName, 'med_type' => $medType, - 'snomed_code' => trim((string) ($master['snomed_code'] ?? '')), - 'atc_code' => strtoupper(trim((string) ($master['atc_code'] ?? ''))), + 'snomed_code' => $snomedCode, + 'atc_code' => $atcCode, ]; } diff --git a/app/Controllers/Patient.php b/app/Controllers/Patient.php index 599134a..7fb1c13 100644 --- a/app/Controllers/Patient.php +++ b/app/Controllers/Patient.php @@ -1113,6 +1113,7 @@ class Patient extends BaseController } $data['profile_file_path'] = $profile_file_path; + $data['abhaPhotoAvailable'] = $abha_profile_photo_base64 !== ''; if ($edit == 0) { return view('billing/Person_profile_V', $data); diff --git a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php index f32ace6..0ad4621 100644 --- a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php +++ b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php @@ -770,6 +770,12 @@ class AbdmTaskBoardSyncService }; $cleanVisitDate = str_replace('-', '', $visitDate); $careContextRef = $prefix . $entityId . '-' . $cleanVisitDate; + if ($taskType === 'immunization_record_publish' && $this->db->tableExists('immunization_records')) { + $immRec = $this->db->table('immunization_records')->select('abdm_care_context_reference')->where('id', (int) $entityId)->get(1)->getRowArray(); + if (! empty($immRec['abdm_care_context_reference'])) { + $careContextRef = trim((string) $immRec['abdm_care_context_reference']); + } + } $careContextDisplay = $hiType . ' ' . $visitDate; $pushData = [ diff --git a/app/Views/abdm/task_board.php b/app/Views/abdm/task_board.php index baee141..4aebfab 100644 --- a/app/Views/abdm/task_board.php +++ b/app/Views/abdm/task_board.php @@ -3685,6 +3685,8 @@ task_type: taskType, entity_id: entityId, onLinked: function (res) { + row.setAttribute('data-task-status', 'completed'); + row.setAttribute('data-is-linked', '1'); var statusCell = row.children[5]; if (statusCell) { var refHtml = ''; @@ -3693,6 +3695,10 @@ } statusCell.innerHTML = 'LINKED' + refHtml; } + var actionCell = row.children[6]; + if (actionCell) { + actionCell.innerHTML = 'Done'; + } } }); } diff --git a/app/Views/billing/Person_Edit_V.php b/app/Views/billing/Person_Edit_V.php index 87aee09..9cbe527 100644 --- a/app/Views/billing/Person_Edit_V.php +++ b/app/Views/billing/Person_Edit_V.php @@ -9,7 +9,11 @@ user(); + try { + $user = function_exists('auth') ? auth()->user() : null; + } catch (\Throwable $e) { + $user = null; + } $patientAbhaId = (string) ($data[0]->abha_id ?? $data[0]->abha_no ?? $data[0]->abha ?? $data[0]->abha_address ?? ''); $abhaAddress = trim((string) ($data[0]->abha_address ?? '')); $abhaVerifiedStatus = trim((string) ($data[0]->abha_verified_status ?? '')); @@ -17,6 +21,7 @@ $abhaKycVerified = (int) ($data[0]->abha_kyc_verified ?? 0) === 1; $abhaMobileVerified = (int) ($data[0]->abha_mobile_verified ?? 0) === 1; $abhaLinkedAt = trim((string) ($data[0]->abdm_linked_at ?? '')); + $abhaPhotoAvailable = isset($abhaPhotoAvailable) ? (bool) $abhaPhotoAvailable : (trim((string) ($data[0]->abha_profile_photo_base64 ?? '')) !== ''); $isAbhaVerified = in_array(strtoupper($abhaVerifiedStatus), ['VERIFIED', 'ACTIVE', 'LINKED', '1', 'YES', 'Y', 'TRUE'], true) || strtoupper($abhaVerificationType) === 'VERIFIED' || ($abhaKycVerified && $abhaMobileVerified); @@ -131,7 +136,7 @@
' + (pRes.care_context_reference || firstRef) + '.' + (pRes.care_context_reference || firstRef) + '.' + (pRes.care_context_reference || firstRef) + '.' + (pRes.care_context_reference || firstRef) + '.