From 6525422af49ac99ff3809ee9c53e7ff122e6ca4c Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Fri, 4 Sep 2026 19:57:17 +0530 Subject: [PATCH] fix(fhir): fix ABHA PHR app parsing error in discharge FHIR bundle - correct Observation category codes for vitals vs admission/discharge conditions - add missing category to investigation Observations (NRCES profile requirement) - strip empty unit/code from valueQuantity to avoid PHR parser rejection - fix greedy regex in Composition narrative that could produce malformed XHTML - remove 0 from isPlaceholderValue to stop silent stripping of numeric FHIR values --- .../Abdm/Fhir/FhirDocumentBuilder.php | 12 +++++-- .../Generators/DischargeFhirGenerator.php | 35 +++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php b/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php index ccc182b..51bc3e7 100644 --- a/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php +++ b/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php @@ -310,14 +310,22 @@ class FhirDocumentBuilder return $clean === [] ? null : $clean; } - /** @param mixed $value */ + /** + * Returns true only for display-level placeholder strings that should never + * appear in a rendered FHIR bundle (pure "no data" sentinel values). + * NOTE: '0' is intentionally excluded — it is a valid FHIR quantity value, + * coded value, and identifier string. Its earlier inclusion caused silent + * data loss in observation valueQuantity and numeric identifiers. + * + * @param mixed $value + */ private function isPlaceholderValue($value): bool { if (! is_string($value)) { return false; } - return in_array(strtoupper(trim($value)), ['NA', 'N/A', 'NULL', 'NOT AVAILABLE'], true); + return in_array(strtoupper(trim($value)), ['NA', 'N/A', 'N / A', 'NULL', 'NOT AVAILABLE', 'UNDEFINED'], true); } private function newId(string $prefix): string diff --git a/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php b/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php index 724b680..fa105f1 100644 --- a/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php +++ b/app/Libraries/Abdm/Fhir/Generators/DischargeFhirGenerator.php @@ -270,6 +270,10 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac $isAdm = (stripos($catText, 'Admission') !== false || stripos($text, 'Admission') !== false); $isDis = (stripos($catText, 'Discharge') !== false || stripos($text, 'Discharge') !== false); $obsCategoryDisplay = $isAdm ? 'Condition on Admission Time' : ($isDis ? 'Condition on Discharge Time' : 'Vital Signs'); + // ABDM PHR: use exam category for admission/discharge condition observations; + // vital-signs category only for true vital measurements. + $obsCategoryCode = ($isAdm || $isDis) ? 'exam' : 'vital-signs'; + $obsCategorySystem = 'http://terminology.hl7.org/CodeSystem/observation-category'; $coding = []; $loincCode = trim((string) ($obs['loinc_code'] ?? '')); @@ -308,9 +312,9 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac 'category' => [[ 'text' => $obsCategoryDisplay, 'coding' => [[ - 'system' => 'http://terminology.hl7.org/CodeSystem/observation-category', - 'code' => 'vital-signs', - 'display' => 'Vital Signs', + 'system' => $obsCategorySystem, + 'code' => $obsCategoryCode, + 'display' => $obsCategoryDisplay, ]], ]], 'code' => [ @@ -397,12 +401,13 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac } } elseif (is_numeric($value)) { $unitInfo = $this->getVitalSignUnitInfo($text, trim((string) ($obs['unit'] ?? ''))); - $obsResource['valueQuantity'] = [ - 'value' => (float) $value, - 'unit' => $unitInfo['unit'], - 'system' => 'http://unitsofmeasure.org', - 'code' => $unitInfo['code'], - ]; + $vq = ['value' => (float) $value]; + if ($unitInfo['unit'] !== '') { + $vq['unit'] = $unitInfo['unit']; + $vq['system'] = 'http://unitsofmeasure.org'; + $vq['code'] = $unitInfo['code'] !== '' ? $unitInfo['code'] : $unitInfo['unit']; + } + $obsResource['valueQuantity'] = $vq; } else { $obsResource['valueString'] = $value; } @@ -457,6 +462,15 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac 'div' => '

Investigation: ' . htmlspecialchars($text) . ($resultValue !== '' ? (': ' . htmlspecialchars($resultValue)) : '') . '

', ], 'status' => 'final', + // NRCES Observation profile requires at least one category. + 'category' => [[ + 'coding' => [[ + 'system' => 'http://terminology.hl7.org/CodeSystem/observation-category', + 'code' => 'laboratory', + 'display' => 'Laboratory', + ]], + 'text' => 'Laboratory', + ]], 'code' => [ 'coding' => $coding, 'text' => $text, @@ -1391,7 +1405,8 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac if ($secTitle === '' || $secDiv === '') { continue; } - if (preg_match('/]*>(.*)<\/div>/is', $secDiv, $m)) { + // Non-greedy match to avoid swallowing sibling tags in multi-div strings. + if (preg_match('/]*>(.*?)<\/div>/is', $secDiv, $m)) { $inner = trim($m[1]); } else { $inner = $secDiv;