fix(abdm): map admission/discharge encounter period, split vitals by admission/discharge, and attach size/hash to pdf document reference

This commit is contained in:
DevSoftTech Server 2026-09-04 06:15:08 +05:30
parent c238fcfc40
commit 4e748dbaa8
4 changed files with 60 additions and 18 deletions

View File

@ -2718,11 +2718,24 @@ class AbdmGateway extends BaseController
$abhaDigits = preg_replace('/\D/', '', $rawAbha);
$abhaDigits = is_string($abhaDigits) ? $abhaDigits : '';
$admissionRaw = trim((string) ($ipdRow['register_date'] ?? ''));
$dischargeRaw = trim((string) ($ipdRow['discharge_date'] ?? ''));
$visitDate = $dischargeRaw !== ''
? date('Y-m-d', strtotime($dischargeRaw))
: ($admissionRaw !== '' ? date('Y-m-d', strtotime($admissionRaw)) : date('Y-m-d'));
$admissionDate = trim((string) ($ipdRow['register_date'] ?? ''));
$admissionTime = trim((string) ($ipdRow['reg_time'] ?? $ipdRow['reg_time_bak'] ?? ''));
$admissionRaw = $admissionDate;
if ($admissionTime !== '' && ! str_contains($admissionDate, ':')) {
$admissionRaw = trim($admissionDate . ' ' . $admissionTime);
}
$dischargeDate = trim((string) ($ipdRow['discharge_date'] ?? ''));
$dischargeTime = trim((string) ($ipdRow['discharge_time'] ?? $ipdRow['discharge_time_bak'] ?? ''));
$dischargeAmpm = trim((string) ($ipdRow['discharge_time_ampm'] ?? ''));
$dischargeRaw = $dischargeDate;
if ($dischargeTime !== '' && ! str_contains($dischargeDate, ':')) {
$dischargeRaw = trim($dischargeDate . ' ' . $dischargeTime . ($dischargeAmpm !== '' ? ' ' . $dischargeAmpm : ''));
}
$visitDate = $dischargeDate !== ''
? date('Y-m-d', strtotime($dischargeDate))
: ($admissionDate !== '' ? date('Y-m-d', strtotime($admissionDate)) : date('Y-m-d'));
$diagnoses = [];
foreach ($this->ipdRows('ipd_discharge_diagnosis', ['comp_report'], $ipdId) as $row) {

View File

@ -257,7 +257,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
$catText = trim((string) ($obs['category'] ?? $obs['category_text'] ?? $obs['stage'] ?? $obs['timing'] ?? ''));
$isAdm = (stripos($catText, 'Admission') !== false || stripos($text, 'Admission') !== false);
$isDis = (stripos($catText, 'Discharge') !== false || stripos($text, 'Discharge') !== false);
$obsCategoryDisplay = $isAdm ? 'General Examination on Admission' : ($isDis ? 'Examination on Discharge' : 'Vital Signs');
$obsCategoryDisplay = $isAdm ? 'Condition on Admission Time' : ($isDis ? 'Condition on Discharge Time' : 'Vital Signs');
$coding = [];
$loincCode = trim((string) ($obs['loinc_code'] ?? ''));
@ -484,7 +484,11 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
}
}
$builder->addDocumentReference([
$rawBytes = base64_decode($data);
$fileSize = is_string($rawBytes) ? strlen($rawBytes) : 0;
$sha1Hash = is_string($rawBytes) && $rawBytes !== '' ? base64_encode(sha1($rawBytes, true)) : '';
$docRef = [
'resourceType' => 'DocumentReference',
'id' => 'discharge-document-' . $recordId . '-' . $idx,
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference']],
@ -509,11 +513,18 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
'contentType' => (string) ($document['content_type'] ?? 'application/pdf'),
'language' => 'en-IN',
'data' => $data,
'size' => $fileSize,
'title' => $docTitle,
'creation' => (string) ($document['created_at'] ?? $timestamp),
],
]],
]);
];
if ($sha1Hash !== '') {
$docRef['content'][0]['attachment']['hash'] = $sha1Hash;
}
$builder->addDocumentReference($docRef);
}
$rawObservations = (array) ($source['observations'] ?? []);
@ -537,8 +548,24 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
];
if (! empty($admissionObsIndexed) && ! empty($dischargeObsIndexed)) {
$sectionDefinitions[] = ['title' => 'General Examination on Admission', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $admissionObsIndexed];
$sectionDefinitions[] = ['title' => 'Examination on Discharge', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $dischargeObsIndexed];
$sectionDefinitions[] = ['title' => 'Condition on Admission Time', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $admissionObsIndexed];
$sectionDefinitions[] = ['title' => 'Condition on Discharge Time', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $dischargeObsIndexed];
} elseif (! empty($admissionObsIndexed)) {
$sectionDefinitions[] = ['title' => 'Condition on Admission Time', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $admissionObsIndexed];
if (! empty($rawObservations)) {
$otherObs = array_diff_key($rawObservations, $admissionObsIndexed);
if (! empty($otherObs)) {
$sectionDefinitions[] = ['title' => 'Physical examination', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $otherObs];
}
}
} elseif (! empty($dischargeObsIndexed)) {
$sectionDefinitions[] = ['title' => 'Condition on Discharge Time', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $dischargeObsIndexed];
if (! empty($rawObservations)) {
$otherObs = array_diff_key($rawObservations, $dischargeObsIndexed);
if (! empty($otherObs)) {
$sectionDefinitions[] = ['title' => 'Physical examination', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items_indexed' => $otherObs];
}
}
} else {
$sectionDefinitions[] = ['title' => 'Physical examination', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items' => $rawObservations];
}

View File

@ -2371,14 +2371,16 @@
}
var admissionObservations = observations.filter(function(o) {
return getObservationCategoryText(o).toLowerCase() === 'condition on admission time';
var cat = getObservationCategoryText(o).toLowerCase();
return cat.indexOf('admission') !== -1;
});
var dischargeObservations = observations.filter(function(o) {
return getObservationCategoryText(o).toLowerCase() === 'condition on discharge time';
var cat = getObservationCategoryText(o).toLowerCase();
return cat.indexOf('discharge') !== -1;
});
var remainingObservations = observations.filter(function(o) {
var cat = getObservationCategoryText(o).toLowerCase();
return cat !== 'condition on admission time' && cat !== 'condition on discharge time';
return cat.indexOf('admission') === -1 && cat.indexOf('discharge') === -1;
});
var vitalObservations = remainingObservations.filter(isVitalObservation);
var labObservations = remainingObservations.filter(function(o) { return !isVitalObservation(o); });

View File

@ -302,10 +302,10 @@ final class FhirGeneratorsTest extends CIUnitTestCase
$bundle = $output['fhir_bundle'];
$composition = $bundle['entry'][0]['resource'];
// Verify sections: should contain both General Examination on Admission and Examination on Discharge
// Verify sections: should contain both Condition on Admission Time and Condition on Discharge Time
$sectionTitles = array_column($composition['section'], 'title');
$this->assertContains('General Examination on Admission', $sectionTitles);
$this->assertContains('Examination on Discharge', $sectionTitles);
$this->assertContains('Condition on Admission Time', $sectionTitles);
$this->assertContains('Condition on Discharge Time', $sectionTitles);
$this->assertContains('Care plan', $sectionTitles);
$this->assertContains('Document reference', $sectionTitles);
@ -333,8 +333,8 @@ final class FhirGeneratorsTest extends CIUnitTestCase
// Verify Observation Categories on Admission vs Discharge
$observations = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'Observation' && isset($e['resource']['category'])));
$this->assertGreaterThanOrEqual(14, count($observations));
$this->assertSame('General Examination on Admission', $observations[0]['resource']['category'][0]['text']);
$this->assertSame('Examination on Discharge', $observations[7]['resource']['category'][0]['text']);
$this->assertSame('Condition on Admission Time', $observations[0]['resource']['category'][0]['text']);
$this->assertSame('Condition on Discharge Time', $observations[7]['resource']['category'][0]['text']);
// Verify Encounter period & location
$encounters = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'Encounter'));