From 5130c6b18a66a0af02f7973dfdbbd735a835f3c7 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 7 Oct 2026 17:59:14 +0530 Subject: [PATCH] fix(abdm): sanitize care context display, fix discharge care_context_reference and bundle generation, fix taskboard sync patientId --- app/Controllers/AbdmGateway.php | 75 ++++++++++++++++++- app/Controllers/Ipd_discharge.php | 39 ++++++++++ .../Generators/DischargeFhirGenerator.php | 13 +++- .../Abdm/Sync/AbdmTaskBoardSyncService.php | 20 ++++- 4 files changed, 140 insertions(+), 7 deletions(-) diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index 97278f1..f6b55d8 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -2218,7 +2218,8 @@ class AbdmGateway extends BaseController $dateClean = str_replace('-', '', $visitDate); $ccRef = $prefix . '-' . $labReqId . '-' . $dateClean; } - $careContextDisplay = ($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . ($testTitle !== '' ? $testTitle : ($isImaging ? 'Radiology Report' : 'Lab Report')) . ' (' . date('d M Y', strtotime($visitDate)) . ')'; + $cleanTestTitle = self::sanitizeCareContextDisplay($testTitle !== '' ? $testTitle : ($isImaging ? 'Radiology Report' : 'Lab Report')); + $careContextDisplay = self::sanitizeCareContextDisplay(($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . $cleanTestTitle . ' ' . date('d M Y', strtotime($visitDate))); $healthRecordId = $this->storeHealthRecord([ 'patient_id' => $patientId, @@ -3190,6 +3191,12 @@ class AbdmGateway extends BaseController $diagnoses[] = ['text' => $problem, 'code' => '']; } } + if (empty($diagnoses) && ! empty($chiefComplaints)) { + $diagnoses = $chiefComplaints; + } + if (empty($diagnoses) && empty($procedures)) { + $diagnoses[] = ['text' => 'Medical care and evaluation', 'code' => '']; + } $chiefComplaintNarrative = trim((string) (($this->ipdRows('ipd_discharge_complaint_remark', ['comp_remark'], $ipdId)[0]['comp_remark'] ?? ''))); $diagnosisNarrative = trim((string) (($this->ipdRows('ipd_discharge_diagnosis_remark', ['comp_remark'], $ipdId)[0]['comp_remark'] ?? ''))); @@ -3368,9 +3375,13 @@ class AbdmGateway extends BaseController $uniqueSuffix = $isNewRecord ? ('-v' . time()) : ''; + $ccRef = 'DISCHARGE-' . $ipdId . '-' . str_replace('-', '', (string) $visitDate) . $uniqueSuffix; + $ccDisplay = 'DischargeSummaryRecord - ' . date('d M Y', strtotime((string) $visitDate)); + $source = [ 'record_id' => (string) $ipdId, - 'care_context_reference' => 'DISCHARGE-' . $ipdId . '-S' . $ipdId . '-' . $visitDate . $uniqueSuffix, + 'care_context_reference' => $ccRef, + 'care_context_display' => $ccDisplay, 'bundle_identifier' => 'discharge-' . (trim((string) ($ipdRow['ipd_code'] ?? '')) ?: (string) $ipdId) . $uniqueSuffix, 'session_id' => (string) $ipdId, 'visit_date' => $visitDate, @@ -3379,6 +3390,9 @@ class AbdmGateway extends BaseController 'doctor_name' => $doctorName, 'discharge_status' => $dischargeStatus, 'problem' => trim((string) ($ipdRow['problem'] ?? '')), + 'diagnoses' => $diagnoses, + 'conditions' => $diagnoses, + 'chief_complaints' => $chiefComplaints, 'doctor' => [ 'id' => $doctorId > 0 ? (string) $doctorId : '', 'name' => $doctorName, @@ -6398,7 +6412,8 @@ class AbdmGateway extends BaseController $visitDate = date('Y-m-d', strtotime((string) $reportedDate)); $dateStr = date('d M Y', strtotime($visitDate)); $ccRef = $prefix . '-' . $taskEntityId . '-' . str_replace('-', '', $visitDate); - $display = ($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . $title . ' (' . $dateStr . ')'; + $cleanTitle = self::sanitizeCareContextDisplay($title); + $display = self::sanitizeCareContextDisplay(($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . $cleanTitle . ' ' . $dateStr); $hasFindings = trim((string) ($labReq['Report_Data'] ?? '')) !== '' || trim((string) ($labReq['report_data_Impression'] ?? '')) !== ''; $statusVal = (int) ($labReq['status'] ?? 0); @@ -6711,7 +6726,8 @@ class AbdmGateway extends BaseController $visitDate = date('Y-m-d', strtotime((string) $reportedDate)); $dateStr = date('d M Y', strtotime($visitDate)); $ccRef = $prefix . '-' . $lr['id'] . '-' . str_replace('-', '', $visitDate); - $display = ($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . $title . ' (' . $dateStr . ')'; + $cleanTitle = self::sanitizeCareContextDisplay($title); + $display = self::sanitizeCareContextDisplay(($isImaging ? 'Radiology Report - ' : 'Diagnostic Report - ') . $cleanTitle . ' ' . $dateStr); $hasFindings = trim((string) ($lr['Report_Data'] ?? '')) !== '' || trim((string) ($lr['report_data_Impression'] ?? '')) !== ''; $statusVal = (int) ($lr['status'] ?? 0); @@ -11939,6 +11955,17 @@ class AbdmGateway extends BaseController // Body: { abha_address, link_token_id, patient_ref, display, hi_type, care_contexts[] } // ========================================================================= + public static function sanitizeCareContextDisplay(string $display): string + { + $display = str_replace('&', 'and', $display); + $display = preg_replace('/[^a-zA-Z0-9 \-_.]/', ' ', $display); + $display = trim(preg_replace('/\s+/', ' ', (string) $display)); + if (mb_strlen($display) > 100) { + $display = mb_substr($display, 0, 100); + } + return $display; + } + public function hipLinkCareContext() { if (ENVIRONMENT !== 'testing' && ! $this->request->isAJAX()) { @@ -12015,6 +12042,46 @@ class AbdmGateway extends BaseController } } + if (! empty($body['display'])) { + $body['display'] = self::sanitizeCareContextDisplay((string) $body['display']); + } + if (! empty($body['care_contexts']) && is_array($body['care_contexts'])) { + foreach ($body['care_contexts'] as &$cc) { + if (isset($cc['display'])) { + $cc['display'] = self::sanitizeCareContextDisplay((string) $cc['display']); + } + } + unset($cc); + } + + // Auto-ensure health record bundle is pushed to bridge for linked discharge care contexts + if (! empty($body['care_contexts']) && is_array($body['care_contexts'])) { + foreach ($body['care_contexts'] as $ccItem) { + $refNo = (string) ($ccItem['referenceNumber'] ?? $ccItem['reference_number'] ?? $ccItem['ref'] ?? $ccItem['careContextId'] ?? ''); + if (str_starts_with($refNo, 'DISCHARGE-')) { + if (preg_match('/^DISCHARGE-(\d+)/', $refNo, $mIpd)) { + $targetIpdId = (int) $mIpd[1]; + if ($targetIpdId > 0 && class_exists('\App\Controllers\Ipd_discharge')) { + try { + $ipdCtrl = new \App\Controllers\Ipd_discharge(); + $ipdCtrl->initController($this->request, $this->response, $this->logger); + $refIpd = new \ReflectionClass($ipdCtrl); + if ($refIpd->hasMethod('enqueueIpdDischargeFhirSync')) { + $mSync = $refIpd->getMethod('enqueueIpdDischargeFhirSync'); + $mSync->setAccessible(true); + $ipdRow = $this->db->table('ipd_master')->where('id', $targetIpdId)->get(1)->getRowArray() ?? []; + $pRow = $this->db->table('patient_master')->where('id', $patientId)->get(1)->getRowArray() ?? []; + $mSync->invoke($ipdCtrl, $targetIpdId, $patientId, $ipdRow, $pRow); + } + } catch (\Throwable $eIpd) { + log_message('warning', 'hipLinkCareContext IPD sync error: ' . $eIpd->getMessage()); + } + } + } + } + } + } + $result = $this->connector->hipLinkCareContext($body); return $this->response->setJSON($result); } diff --git a/app/Controllers/Ipd_discharge.php b/app/Controllers/Ipd_discharge.php index 905718f..3698ad5 100644 --- a/app/Controllers/Ipd_discharge.php +++ b/app/Controllers/Ipd_discharge.php @@ -9420,6 +9420,13 @@ class Ipd_discharge extends BaseController $chiefComplaintsList[] = ['text' => $complaintRemarkText, 'code' => '']; } + if (empty($conditionRows) && ! empty($chiefComplaintsList)) { + $conditionRows = $chiefComplaintsList; + } + if (empty($conditionRows)) { + $conditionRows[] = ['text' => 'Medical care and evaluation', 'code' => '']; + } + $procedureRows = []; foreach ($this->byIpdRows('ipd_discharge_surgery', ['surgery_name', 'surgery_date'], 'id ASC', $ipdId) as $row) { $text = trim((string) ($row['surgery_name'] ?? '')); @@ -9686,8 +9693,13 @@ class Ipd_discharge extends BaseController $patientPin = trim((string) ($patientRow['p_pin'] ?? $patientRow['pincode'] ?? '')); $dischargeStatus = trim((string) ($ipdRow['discarge_patient_status'] ?? '')); + $ccRef = 'DISCHARGE-' . $ipdId . '-' . str_replace('-', '', (string) $visitDate); + $ccDisplay = 'DischargeSummaryRecord - ' . date('d M Y', strtotime((string) $visitDate)); + $source = [ 'record_id' => (string) $ipdId, + 'care_context_reference' => $ccRef, + 'care_context_display' => $ccDisplay, 'bundle_identifier' => 'discharge-' . $ipdNo, 'session_id' => (string) $ipdId, 'visit_date' => $visitDate, @@ -9696,6 +9708,9 @@ class Ipd_discharge extends BaseController 'doctor_name' => trim((string) ($ipdRow['r_doc_name'] ?? '')), 'discharge_status' => $dischargeStatus, 'problem' => trim((string) ($ipdRow['problem'] ?? '')), + 'diagnoses' => $conditionRows, + 'conditions' => $conditionRows, + 'chief_complaints' => $chiefComplaintsList, 'organization' => [ 'id' => $hfrId, 'name' => $hospitalName, @@ -9773,6 +9788,30 @@ class Ipd_discharge extends BaseController $outbox = new AbdmSyncOutboxService(); $outbox->enqueueRecordSync($syncPayload); + + // Also push synchronously to E-Atria bridge so the bundle is immediately accessible to PHR apps + try { + $connector = new \App\Libraries\Abdm\EAtriaBridgeConnector(); + $pushData = [ + 'patient_id' => (string) $patientId, + 'patient_name' => (string) ($gatewayPayload['patient_name'] ?? $patientName), + 'abha_id' => (string) ($gatewayPayload['abha_id'] ?? $abhaDigits), + 'abha_address' => (string) ($gatewayPayload['abha_address'] ?? $abhaAddress), + 'hi_type' => 'DischargeSummaryRecord', + 'record_type' => 'DischargeSummaryRecord', + 'visit_date' => (string) ($gatewayPayload['visit_date'] ?? $visitDate), + 'care_context_reference' => $ccRef, + 'care_context_display' => $ccDisplay, + 'notes' => $ccDisplay, + 'queue_id' => $ccRef, + 'bundle' => (array) ($gatewayPayload['fhir_bundle'] ?? []), + 'fhir_bundle' => (array) ($gatewayPayload['fhir_bundle'] ?? []), + 'record_data' => (array) ($gatewayPayload['fhir_bundle'] ?? []), + ]; + $connector->pushRecord($pushData); + } catch (\Throwable $pe) { + log_message('warning', '[enqueueIpdDischargeFhirSync] Direct bridge push error: ' . $pe->getMessage()); + } } private function normalizeFhirGender(string $gender): string diff --git a/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php b/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php index 2a14215..5408fb4 100644 --- a/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php +++ b/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php @@ -94,8 +94,17 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac $patientRef = 'urn:uuid:patient-' . $patientId; $encounterRef = is_array($encounter) ? 'urn:uuid:' . (string) ($encounter['id'] ?? '') : null; - $diagnosesList = (array) ($source['diagnoses'] ?? $source['conditions'] ?? []); - $chiefComplaintsList = (array) ($source['chief_complaints'] ?? []); + $diagnosesList = (array) ($source['diagnoses'] ?? $source['conditions'] ?? $source['ui_final_diagnosis'] ?? []); + $chiefComplaintsList = (array) ($source['chief_complaints'] ?? $source['ui_complaints'] ?? []); + + if (empty($diagnosesList) && empty($chiefComplaintsList)) { + $problem = trim((string) ($source['problem'] ?? '')); + if ($problem !== '') { + $diagnosesList = [['text' => $problem, 'code' => '']]; + } else { + $diagnosesList = [['text' => 'Medical care and evaluation', 'code' => '']]; + } + } $conditionGroups = [ [ diff --git a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php index f543ef2..9313f0d 100644 --- a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php +++ b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php @@ -168,7 +168,8 @@ class AbdmTaskBoardSyncService } $opdId = (int) ($row['opd_id'] ?? 0); - if ($opdId <= 0) { + $patientId = (int) ($row['p_id'] ?? 0); + if ($opdId <= 0 || $patientId <= 0) { continue; } @@ -895,6 +896,23 @@ class AbdmTaskBoardSyncService log_message('warning', '[processIndividualWorkTask] Wellness bundle generation error: ' . $we->getMessage()); } } + } elseif ($taskType === 'ipd_discharge_publish') { + if ($this->db->tableExists('abdm_sync_record')) { + $syncRec = $this->db->table('abdm_sync_record') + ->where('local_record_id', 'ipd-discharge-' . $entityId) + ->orWhere('care_context_reference', $careContextRef) + ->get(1) + ->getRowArray(); + if (! empty($syncRec['fhir_bundle_json'])) { + $decodedBundle = json_decode((string) $syncRec['fhir_bundle_json'], true); + if (is_array($decodedBundle) && ! empty($decodedBundle)) { + $pushData['bundle'] = $decodedBundle; + $pushData['fhir_bundle'] = $decodedBundle; + $pushData['record_data'] = $decodedBundle; + $bundleJson = (string) $syncRec['fhir_bundle_json']; + } + } + } } try {