fix(abdm): sanitize care context display, fix discharge care_context_reference and bundle generation, fix taskboard sync patientId

This commit is contained in:
DevSoftTech Server 2026-10-07 17:59:14 +05:30
parent f7761218d0
commit 5130c6b18a
4 changed files with 140 additions and 7 deletions

View File

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

View File

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

View File

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

View File

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