Fix discharge FHIR record full data: medications SNOMED 721912009, dietary advice care plan, and full PDF attachment

This commit is contained in:
DevSoftTech Server 2026-10-17 02:44:32 +05:30
parent 7dce405942
commit a363e2d0a5
3 changed files with 220 additions and 35 deletions

View File

@ -3586,6 +3586,70 @@ class AbdmGateway extends BaseController
]; ];
} }
// Dietary Advice from food_ids
$foodIds = is_array($instructionMeta['food_ids'] ?? null) ? ($instructionMeta['food_ids'] ?? []) : [];
if (empty($foodIds) && $this->db && $this->db->tableExists('ipd_discharge_drug_food_interaction')) {
try {
$legacyFoodRow = $this->db->table('ipd_discharge_drug_food_interaction')->where('ipd_id', $ipdId)->get(1)->getRowArray();
if (! empty($legacyFoodRow['food_id_list'])) {
$foodIds = array_filter(array_map('intval', explode(',', (string) $legacyFoodRow['food_id_list'])));
}
} catch (\Throwable $e) {
}
}
if (! empty($foodIds) && $this->db && $this->db->tableExists('ipd_discharge_master_food')) {
try {
$foodRows = $this->db->table('ipd_discharge_master_food')
->select('id,food_short,food_desc,food_desc_lang')
->whereIn('id', array_map('intval', $foodIds))
->get()
->getResultArray();
$dietParts = [];
foreach ($foodRows as $fRow) {
$heading = trim((string) ($fRow['food_short'] ?? ''));
$line = trim((string) ($fRow['food_desc_lang'] ?? ''));
if ($line === '') {
$line = trim((string) ($fRow['food_desc'] ?? ''));
}
if ($heading !== '' && $line !== '') {
$dietParts[] = $heading . ': ' . $line;
} elseif ($line !== '') {
$dietParts[] = $line;
} elseif ($heading !== '') {
$dietParts[] = $heading;
}
}
if (! empty($dietParts)) {
$carePlans[] = [
'title' => 'Dietary Advice',
'description' => implode("\n", $dietParts),
'created_at' => $this->toIsoDateTimeOrNow($dischargeRaw),
];
}
} catch (\Throwable $e) {
}
}
// Other Advice
$otherText = trim((string) ($instructionMeta['other_text'] ?? ''));
if ($otherText !== '') {
$carePlans[] = [
'title' => 'Other Advice',
'description' => strip_tags($otherText),
'created_at' => $this->toIsoDateTimeOrNow($dischargeRaw),
];
}
// Discharge Instructions / Remarks
$compRemark = trim((string) ($instructionRow['comp_remark'] ?? ''));
if ($compRemark !== '') {
$carePlans[] = [
'title' => 'Discharge Instructions',
'description' => strip_tags($compRemark),
'created_at' => $this->toIsoDateTimeOrNow($dischargeRaw),
];
}
$reviewAfter = trim((string) ($instructionRow['review_after'] ?? '')); $reviewAfter = trim((string) ($instructionRow['review_after'] ?? ''));
if ($reviewAfter !== '') { if ($reviewAfter !== '') {
$reviewDate = ''; $reviewDate = '';
@ -3695,9 +3759,19 @@ class AbdmGateway extends BaseController
'room' => (string) ($locationInfo['room'] ?? ''), 'room' => (string) ($locationInfo['room'] ?? ''),
'bed' => (string) ($locationInfo['bed'] ?? ''), 'bed' => (string) ($locationInfo['bed'] ?? ''),
], ],
'ui_complaints' => $chiefComplaints,
'ui_complaint_narrative' => $chiefComplaintNarrative,
'ui_final_diagnosis' => $diagnoses,
'ui_clinical_history' => $diagnosisNarrative,
'ui_physical_exam' => $observations,
'ui_investigations' => $investigations,
'ui_surgeries' => $procedures,
'ui_discharge_medicine' => $medications,
'ui_discharge_summary' => $carePlans,
'chief_complaints' => $chiefComplaints, 'chief_complaints' => $chiefComplaints,
'chief_complaint_narrative' => $chiefComplaintNarrative, 'chief_complaint_narrative' => $chiefComplaintNarrative,
'diagnoses' => $diagnoses, 'diagnoses' => $diagnoses,
'conditions' => $diagnoses,
'diagnosis_narrative' => $diagnosisNarrative, 'diagnosis_narrative' => $diagnosisNarrative,
'procedures' => $procedures, 'procedures' => $procedures,
'medications' => $medications, 'medications' => $medications,
@ -3846,13 +3920,14 @@ class AbdmGateway extends BaseController
$abdmTemplate = $this->resolveAbdmDischargeTemplate(); $abdmTemplate = $this->resolveAbdmDischargeTemplate();
$abdmTemplateId = (int) ($abdmTemplate['id'] ?? 0); $abdmTemplateId = (int) ($abdmTemplate['id'] ?? 0);
$generatedBinary = null;
try { try {
$dischargeCtrl = new \App\Controllers\Ipd_discharge(); $dischargeCtrl = new \App\Controllers\Ipd_discharge();
$request = \Config\Services::request(); $request = \Config\Services::request();
$response = \Config\Services::response(); $response = \Config\Services::response();
$logger = \Config\Services::logger(); $logger = \Config\Services::logger();
$dischargeCtrl->initController($request, $response, $logger); $dischargeCtrl->initController($request, $response, $logger);
$dischargeCtrl->generateDischargeSummaryPdfBinary($ipdId, true, $abdmTemplateId > 0 ? $abdmTemplateId : null); $generatedBinary = $dischargeCtrl->generateDischargeSummaryPdfBinary($ipdId, true, $abdmTemplateId > 0 ? $abdmTemplateId : null);
} catch (\Throwable $e) { } catch (\Throwable $e) {
log_message('error', 'Auto-generating IPD Discharge Summary PDF failed for IPD {ipd}: {msg}', [ log_message('error', 'Auto-generating IPD Discharge Summary PDF failed for IPD {ipd}: {msg}', [
'ipd' => $ipdId, 'ipd' => $ipdId,
@ -3873,10 +3948,9 @@ class AbdmGateway extends BaseController
foreach ($definitions as $definition) { foreach ($definitions as $definition) {
$path = $directory . DIRECTORY_SEPARATOR . $definition['file']; $path = $directory . DIRECTORY_SEPARATOR . $definition['file'];
if (! is_file($path) || filesize($path) === 0) { $binary = ($definition['file'] === 'discharge-summary.pdf' && is_string($generatedBinary) && str_starts_with($generatedBinary, '%PDF-'))
continue; ? $generatedBinary
} : (is_file($path) ? file_get_contents($path) : null);
$binary = file_get_contents($path);
if (! is_string($binary) || ! str_starts_with($binary, '%PDF-')) { if (! is_string($binary) || ! str_starts_with($binary, '%PDF-')) {
continue; continue;
} }

View File

@ -9019,8 +9019,9 @@ class Ipd_discharge extends BaseController
$abdmTemplate = $this->resolveAbdmDischargeTemplate(); $abdmTemplate = $this->resolveAbdmDischargeTemplate();
$abdmTemplateId = (int) ($abdmTemplate['id'] ?? 0); $abdmTemplateId = (int) ($abdmTemplate['id'] ?? 0);
$generatedBinary = null;
try { try {
$this->generateDischargeSummaryPdfBinary($ipdId, true, $abdmTemplateId > 0 ? $abdmTemplateId : null); $generatedBinary = $this->generateDischargeSummaryPdfBinary($ipdId, true, $abdmTemplateId > 0 ? $abdmTemplateId : null);
} catch (\Throwable $e) { } catch (\Throwable $e) {
log_message('error', 'Auto-generating IPD Discharge Summary PDF failed for IPD {ipd}: {msg}', [ log_message('error', 'Auto-generating IPD Discharge Summary PDF failed for IPD {ipd}: {msg}', [
'ipd' => $ipdId, 'ipd' => $ipdId,
@ -9041,10 +9042,9 @@ class Ipd_discharge extends BaseController
foreach ($definitions as $definition) { foreach ($definitions as $definition) {
$path = $directory . DIRECTORY_SEPARATOR . $definition['file']; $path = $directory . DIRECTORY_SEPARATOR . $definition['file'];
if (! is_file($path) || filesize($path) === 0) { $binary = ($definition['file'] === 'discharge-summary.pdf' && is_string($generatedBinary) && str_starts_with($generatedBinary, '%PDF-'))
continue; ? $generatedBinary
} : (is_file($path) ? file_get_contents($path) : null);
$binary = file_get_contents($path);
if (! is_string($binary) || ! str_starts_with($binary, '%PDF-')) { if (! is_string($binary) || ! str_starts_with($binary, '%PDF-')) {
continue; continue;
} }
@ -9597,6 +9597,62 @@ class Ipd_discharge extends BaseController
]; ];
} }
// Dietary Advice from food_ids
$foodIds = is_array($instructionMeta['food_ids'] ?? null) ? ($instructionMeta['food_ids'] ?? []) : [];
if (empty($foodIds) && $this->tableHasColumns('ipd_discharge_drug_food_interaction', ['ipd_id', 'food_id_list'])) {
$legacyFoodRow = $this->firstRowByIpd('ipd_discharge_drug_food_interaction', $ipdId);
$foodIds = $this->parseFoodIdCsv((string) ($legacyFoodRow['food_id_list'] ?? ''));
}
if (! empty($foodIds) && $this->tableHasColumns('ipd_discharge_master_food', ['id', 'food_short', 'food_desc'])) {
$foodRows = $this->db->table('ipd_discharge_master_food')
->select('id,food_short,food_desc,food_desc_lang')
->whereIn('id', array_map('intval', $foodIds))
->get()
->getResultArray();
$dietParts = [];
foreach ($foodRows as $fRow) {
$heading = trim((string) ($fRow['food_short'] ?? ''));
$line = trim((string) ($fRow['food_desc_lang'] ?? ''));
if ($line === '') {
$line = trim((string) ($fRow['food_desc'] ?? ''));
}
if ($heading !== '' && $line !== '') {
$dietParts[] = $heading . ': ' . $line;
} elseif ($line !== '') {
$dietParts[] = $line;
} elseif ($heading !== '') {
$dietParts[] = $heading;
}
}
if (! empty($dietParts)) {
$carePlanRows[] = [
'title' => 'Dietary Advice',
'description' => implode("\n", $dietParts),
'created_at' => $dischargeIso,
];
}
}
// Other Advice
$otherText = trim((string) ($instructionMeta['other_text'] ?? ''));
if ($otherText !== '') {
$carePlanRows[] = [
'title' => 'Other Advice',
'description' => strip_tags($otherText),
'created_at' => $dischargeIso,
];
}
// Discharge Instructions / Remarks
$compRemark = trim((string) ($instructionRow['comp_remark'] ?? ''));
if ($compRemark !== '') {
$carePlanRows[] = [
'title' => 'Discharge Instructions',
'description' => strip_tags($compRemark),
'created_at' => $dischargeIso,
];
}
$reviewAfter = trim((string) ($instructionRow['review_after'] ?? '')); $reviewAfter = trim((string) ($instructionRow['review_after'] ?? ''));
if ($reviewAfter !== '') { if ($reviewAfter !== '') {
$reviewDate = ''; $reviewDate = '';
@ -9773,7 +9829,17 @@ class Ipd_discharge extends BaseController
'ui_course_treatment' => $courseRows, 'ui_course_treatment' => $courseRows,
'ui_course_treatment_narrative' => $courseRemark, 'ui_course_treatment_narrative' => $courseRemark,
'ui_discharge_medicine' => $medicationRows, 'ui_discharge_medicine' => $medicationRows,
'ui_discharge_summary' => $carePlanRows,
'chief_complaints' => $chiefComplaintsList,
'chief_complaint_narrative' => $complaintRemarkText,
'diagnoses' => $conditionRows,
'conditions' => $conditionRows,
'diagnosis_narrative' => $uiClinicalHistoryText,
'procedures' => $procedureRows,
'medications' => $medicationRows,
'observations' => $observationRows,
'allergies' => $allergyRows, 'allergies' => $allergyRows,
'care_plans' => $carePlanRows,
'documents' => $documentsList, 'documents' => $documentsList,
'template' => $this->resolveAbdmDischargeTemplate(), 'template' => $this->resolveAbdmDischargeTemplate(),
]; ];

View File

@ -230,12 +230,16 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
// ── Discharge Medications ─────────────────────────────────────────────── // ── Discharge Medications ───────────────────────────────────────────────
foreach ((array) ($source['ui_discharge_medicine'] ?? $source['medications'] ?? []) as $idx => $med) { $rawMeds = ! empty($source['medications']) ? (array) $source['medications'] : (array) ($source['ui_discharge_medicine'] ?? []);
$name = trim((string) ($med['name'] ?? '')); $validMeds = [];
if (! $this->isMeaningfulValue($name)) { foreach ($rawMeds as $med) {
continue; if (is_array($med) && $this->isMeaningfulValue((string) ($med['name'] ?? ''))) {
$validMeds[] = $med;
} }
}
foreach ($validMeds as $idx => $med) {
$name = trim((string) ($med['name'] ?? ''));
$medCode = trim((string) ($med['code'] ?? '')); $medCode = trim((string) ($med['code'] ?? ''));
$medCoding = []; $medCoding = [];
if ($medCode !== '' && $this->isMeaningfulValue($medCode)) { if ($medCode !== '' && $this->isMeaningfulValue($medCode)) {
@ -442,7 +446,11 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
// ── Care Plans / Discharge Advice & Hospital Course ──────────────────── // ── Care Plans / Discharge Advice & Hospital Course ────────────────────
$carePlansList = (array) ($source['ui_discharge_summary'] ?? $source['care_plans'] ?? []); $rawCarePlans = array_merge(
(array) ($source['care_plans'] ?? []),
(array) ($source['ui_discharge_summary'] ?? []),
(array) ($source['ui_care_plans'] ?? [])
);
// Include hospital course / course of treatment if provided // Include hospital course / course of treatment if provided
$courseItems = (array) ($source['ui_course_treatment'] ?? $source['course_treatment'] ?? []); $courseItems = (array) ($source['ui_course_treatment'] ?? $source['course_treatment'] ?? []);
@ -458,18 +466,37 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
$courseParts[] = $courseNarrative; $courseParts[] = $courseNarrative;
} }
if (! empty($courseParts)) { if (! empty($courseParts)) {
$carePlansList[] = [ $rawCarePlans[] = [
'title' => 'Course of Treatment in Hospital', 'title' => 'Course of Treatment in Hospital',
'description' => implode("\n", array_unique($courseParts)), 'description' => implode("\n", array_unique($courseParts)),
]; ];
} }
foreach ($carePlansList as $idx => $cp) { $validCarePlans = [];
$seenCp = [];
foreach ($rawCarePlans as $cp) {
if (! is_array($cp)) {
continue;
}
$title = $this->cleanPlainText((string) ($cp['title'] ?? 'Discharge Advice')); $title = $this->cleanPlainText((string) ($cp['title'] ?? 'Discharge Advice'));
$description = $this->cleanPlainText((string) ($cp['description'] ?? '')); $description = $this->cleanPlainText((string) ($cp['description'] ?? ''));
if (! $this->isMeaningfulValue($description)) { if (! $this->isMeaningfulValue($description)) {
continue; continue;
} }
$key = $title . '|' . $description;
if (isset($seenCp[$key])) {
continue;
}
$seenCp[$key] = true;
$validCarePlans[] = [
'title' => $title !== '' ? $title : 'Discharge Advice',
'description' => $description,
];
}
foreach ($validCarePlans as $idx => $cp) {
$title = $cp['title'];
$description = $cp['description'];
$carePlanResource = [ $carePlanResource = [
'resourceType' => 'CarePlan', 'resourceType' => 'CarePlan',
@ -477,11 +504,11 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/CarePlan']], 'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/CarePlan']],
'text' => [ 'text' => [
'status' => 'generated', 'status' => 'generated',
'div' => '<div xmlns="http://www.w3.org/1999/xhtml"><p><b>' . htmlspecialchars($title !== '' ? $title : 'Discharge Advice') . ':</b> ' . htmlspecialchars($description) . '</p></div>', 'div' => '<div xmlns="http://www.w3.org/1999/xhtml"><p><b>' . htmlspecialchars($title) . ':</b> ' . htmlspecialchars($description) . '</p></div>',
], ],
'status' => 'active', 'status' => 'active',
'intent' => 'plan', 'intent' => 'plan',
'title' => $title !== '' ? $title : 'Discharge Advice', 'title' => $title,
'description' => $description, 'description' => $description,
'subject' => ['reference' => $patientRef, 'display' => (string) ($source['patient']['name'] ?? $source['patient_name'] ?? 'Patient')], 'subject' => ['reference' => $patientRef, 'display' => (string) ($source['patient']['name'] ?? $source['patient_name'] ?? 'Patient')],
'category' => [ 'category' => [
@ -490,10 +517,10 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
[ [
'system' => 'http://snomed.info/sct', 'system' => 'http://snomed.info/sct',
'code' => '734163000', 'code' => '734163000',
'display' => 'Care plan' 'display' => 'Care plan',
] ],
] ],
] ],
], ],
]; ];
@ -588,9 +615,21 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
} }
// Medications // Medications
$meds = (array) ($source['ui_discharge_medicine'] ?? $source['medications'] ?? []); if (! empty($validMeds)) {
if (! empty($meds)) { $sectionDefinitions[] = [
$sectionDefinitions[] = ['title' => 'Medications', 'code' => '1003606003', 'display' => 'Medication history section', 'prefix' => 'discharge-medication-', 'items' => $meds]; 'title' => 'Medications',
'code' => '721912009',
'display' => 'Medication summary document',
'prefix' => 'discharge-medication-',
'items' => $validMeds,
'additional_coding' => [
[
'system' => 'http://snomed.info/sct',
'code' => '1003606003',
'display' => 'Medication history section',
],
],
];
} }
// Investigations // Investigations
@ -601,8 +640,8 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
} }
// Care plans / Discharge advice & hospital course // Care plans / Discharge advice & hospital course
if (! empty($carePlansList)) { if (! empty($validCarePlans)) {
$sectionDefinitions[] = ['title' => 'Care Plan', 'code' => '734163000', 'display' => 'Care plan', 'prefix' => 'discharge-careplan-', 'items' => $carePlansList]; $sectionDefinitions[] = ['title' => 'Care Plan', 'code' => '734163000', 'display' => 'Care plan', 'prefix' => 'discharge-careplan-', 'items' => $validCarePlans];
} }
// Document Reference // Document Reference
@ -633,13 +672,19 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
if ($references === []) { if ($references === []) {
continue; continue;
} }
$coding = [[
'system' => 'http://snomed.info/sct',
'code' => $definition['code'],
'display' => $definition['display'],
]];
if (! empty($definition['additional_coding']) && is_array($definition['additional_coding'])) {
foreach ($definition['additional_coding'] as $ac) {
$coding[] = $ac;
}
}
$sec = [ $sec = [
'title' => $definition['title'], 'title' => $definition['title'],
'code' => ['coding' => [[ 'code' => ['coding' => $coding],
'system' => 'http://snomed.info/sct',
'code' => $definition['code'],
'display' => $definition['display'],
]]],
'entry' => $references, 'entry' => $references,
]; ];
$sections[] = $sec; $sections[] = $sec;
@ -943,8 +988,8 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
} }
$vitals = (array) ($source['observations'] ?? []); $vitals = (array) ($source['observations'] ?? []);
$procedures = (array) ($source['procedures'] ?? []); $procedures = (array) ($source['procedures'] ?? []);
$medications = (array) ($source['medications'] ?? []); $medications = ! empty($source['medications']) ? (array) $source['medications'] : (array) ($source['ui_discharge_medicine'] ?? []);
$carePlans = (array) ($source['care_plans'] ?? []); $carePlans = ! empty($source['care_plans']) ? (array) $source['care_plans'] : (array) ($source['ui_care_plans'] ?? $source['ui_discharge_summary'] ?? []);
if ($templateHtml !== '') { if ($templateHtml !== '') {
$tokens = [ $tokens = [