diff --git a/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php b/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php index ee1e268..7520b2d 100644 --- a/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php +++ b/app/Libraries/Abdm/Fhir/FhirDocumentBuilder.php @@ -161,6 +161,12 @@ class FhirDocumentBuilder return $this->addResource($resource); } + /** @param array $resource */ + public function addImmunization(array $resource): self + { + return $this->addResource($resource); + } + /** * @return array */ @@ -252,7 +258,7 @@ class FhirDocumentBuilder /** * @param array $resource */ - private function addResource(array $resource): self + public function addResource(array $resource): self { $id = (string) ($resource['id'] ?? ''); if ($id === '') { diff --git a/app/Libraries/Abdm/Fhir/FhirGeneratorFactory.php b/app/Libraries/Abdm/Fhir/FhirGeneratorFactory.php index 2be70bc..3f16065 100644 --- a/app/Libraries/Abdm/Fhir/FhirGeneratorFactory.php +++ b/app/Libraries/Abdm/Fhir/FhirGeneratorFactory.php @@ -4,9 +4,11 @@ namespace App\Libraries\Abdm\Fhir; use App\Libraries\Abdm\Fhir\Generators\DischargeFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\HealthDocumentFhirGenerator; +use App\Libraries\Abdm\Fhir\Generators\ImmunizationFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\InvoiceFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\LabFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\OpdFhirGenerator; +use App\Libraries\Abdm\Fhir\Generators\PrescriptionFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\RadiologyFhirGenerator; use App\Libraries\Abdm\Fhir\Generators\WellnessFhirGenerator; @@ -42,6 +44,16 @@ class FhirGeneratorFactory return new HealthDocumentFhirGenerator(); } + public function prescription(): PrescriptionFhirGenerator + { + return new PrescriptionFhirGenerator(); + } + + public function immunization(): ImmunizationFhirGenerator + { + return new ImmunizationFhirGenerator(); + } + public function wellness(): WellnessFhirGenerator { return new WellnessFhirGenerator(); diff --git a/app/Libraries/Abdm/Fhir/Generators/ImmunizationFhirGenerator.php b/app/Libraries/Abdm/Fhir/Generators/ImmunizationFhirGenerator.php new file mode 100644 index 0000000..37e97c9 --- /dev/null +++ b/app/Libraries/Abdm/Fhir/Generators/ImmunizationFhirGenerator.php @@ -0,0 +1,214 @@ + $source + * @return array + */ + public function generate(array $source): array + { + $timestamp = (string) ($source['completed_at'] ?? date(DATE_ATOM)); + $recordId = (string) ($source['record_id'] ?? '0'); + $patientId = (string) ($source['patient']['id'] ?? '0'); + $visitDate = (string) ($source['visit_date'] ?? date('Y-m-d')); + + $careContextReference = 'IMMUNIZATION-' . $recordId . '-' . $visitDate; + $careContextDisplay = 'Immunization ' . $visitDate; + + $builder = new \App\Libraries\Abdm\Fhir\FhirDocumentBuilder(); + $builder + ->buildBundleMeta('immunization-' . $recordId . '-' . strtotime($timestamp), $timestamp) + ->addPatient($this->buildBasePatient($source)); + + $patientRef = 'urn:uuid:patient-' . $patientId; + + $practitioner = $this->buildPractitioner($source); + if (! is_array($practitioner)) { + $fallbackDoctorName = trim((string) ($source['doctor_name'] ?? 'Dr. Attending Medical Officer')); + $docId = (string) ($source['practitioner']['id'] ?? $source['doctor']['id'] ?? '1'); + $practitioner = [ + 'resourceType' => 'Practitioner', + 'id' => 'practitioner-' . ($docId !== '' && $docId !== '0' ? $docId : '1'), + 'name' => [[ + 'text' => $fallbackDoctorName !== '' ? $fallbackDoctorName : 'Dr. Attending Medical Officer', + ]], + ]; + } + $builder->addPractitioner($practitioner); + + $organization = $this->buildOrganization($source); + if (! is_array($organization)) { + $orgId = (string) ($source['organization']['id'] ?? $source['hfr_id'] ?? 'IN0510000871'); + $orgName = (string) ($source['organization']['name'] ?? 'Hospital'); + $organization = [ + 'resourceType' => 'Organization', + 'id' => 'organization-' . ($orgId !== '' ? $orgId : 'IN0510000871'), + 'name' => $orgName, + ]; + } + $builder->addOrganization($organization); + + $immunizationRefs = []; + $resolvedCount = 0; + $unresolvedCount = 0; + $fallbackCount = 0; + + foreach ((array) ($source['immunizations'] ?? $source['vaccines'] ?? []) as $idx => $imm) { + $vaccineName = trim((string) ($imm['vaccine_name'] ?? $imm['name'] ?? '')); + if (! $this->isMeaningfulValue($vaccineName)) { + continue; + } + + $code = trim((string) ($imm['code'] ?? $imm['snomed_code'] ?? '')); + $coding = []; + if ($code !== '') { + $coding[] = [ + 'system' => 'http://snomed.info/sct', + 'code' => $code, + 'display' => $vaccineName, + ]; + $resolvedCount++; + } else { + $coding[] = [ + 'system' => 'http://snomed.info/sct', + 'code' => '41000179103', + 'display' => $vaccineName, + ]; + $fallbackCount++; + } + + $immId = 'immunization-' . $recordId . '-' . $idx; + $immunizationRefs[] = [ + 'reference' => 'urn:uuid:' . $immId, + 'type' => 'Immunization', + ]; + + $immResource = [ + 'resourceType' => 'Immunization', + 'id' => $immId, + 'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Immunization']], + 'status' => 'completed', + 'vaccineCode' => [ + 'coding' => $coding, + 'text' => $vaccineName, + ], + 'patient' => ['reference' => $patientRef], + 'occurrenceDateTime' => (string) ($imm['occurrence_date'] ?? $imm['given_date'] ?? $timestamp), + 'primarySource' => true, + ]; + + $builder->addResource($immResource); + } + + $sectionEntries = $immunizationRefs; + + // Support certificate / scan document reference + $docData = (string) ($source['certificate_pdf_base64'] ?? $source['document_data_base64'] ?? $source['pdf_base64'] ?? ''); + if ($docData !== '') { + $docRefId = 'immunization-doc-' . $recordId; + $builder->addDocumentReference([ + 'resourceType' => 'DocumentReference', + 'id' => $docRefId, + 'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference']], + 'status' => 'current', + 'docStatus' => 'final', + 'type' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '41000179103', + 'display' => 'Immunization record', + ]], + 'text' => 'Immunization Certificate PDF', + ], + 'subject' => ['reference' => $patientRef], + 'date' => $timestamp, + 'description' => 'Vaccination Certificate PDF', + 'content' => [[ + 'attachment' => [ + 'contentType' => 'application/pdf', + 'language' => 'en-IN', + 'data' => $docData, + 'title' => 'Vaccine Certificate.pdf', + 'creation' => $timestamp, + ], + ]], + ]); + $sectionEntries[] = [ + 'reference' => 'urn:uuid:' . $docRefId, + 'type' => 'DocumentReference', + ]; + } + + $authorRef = 'urn:uuid:' . (string) $practitioner['id']; + $authorDisplay = (string) ($practitioner['name'][0]['text'] ?? 'Dr. Attending Medical Officer'); + $custodianRef = 'urn:uuid:' . (string) $organization['id']; + + $composition = [ + 'resourceType' => 'Composition', + 'id' => 'composition-' . $recordId, + 'meta' => [ + 'versionId' => '1', + 'lastUpdated' => $timestamp, + 'profile' => [ + 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ImmunizationRecord', + ], + ], + 'status' => 'final', + 'type' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '41000179103', + 'display' => 'Immunization record', + ]], + 'text' => 'Immunization record', + ], + 'title' => 'Immunization record', + 'date' => $timestamp, + 'subject' => [ + 'reference' => $patientRef, + 'display' => (string) ($source['patient']['name'] ?? 'Patient'), + ], + 'author' => [[ + 'reference' => $authorRef, + 'display' => $authorDisplay, + ]], + 'custodian' => [ + 'reference' => $custodianRef, + 'display' => (string) ($organization['name'] ?? 'Hospital'), + ], + 'section' => [ + [ + 'title' => 'Immunization record', + 'code' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '41000179103', + 'display' => 'Immunization record', + ]], + ], + 'entry' => $sectionEntries, + ], + ], + ]; + + $builder->buildComposition($composition); + + $bundle = $builder->toBundle(); + $validation = $this->validator->validate($bundle, 'immunization', [ + 'resolved' => $resolvedCount, + 'unresolved' => $unresolvedCount, + 'fallback_used' => $fallbackCount, + ]); + + return [ + 'hi_type' => 'ImmunizationRecord', + 'care_context_reference' => $careContextReference, + 'care_context_display' => $careContextDisplay, + 'fhir_bundle' => $bundle, + 'validation' => $validation->toArray(), + ]; + } +} diff --git a/app/Libraries/Abdm/Fhir/Generators/PrescriptionFhirGenerator.php b/app/Libraries/Abdm/Fhir/Generators/PrescriptionFhirGenerator.php new file mode 100644 index 0000000..62807eb --- /dev/null +++ b/app/Libraries/Abdm/Fhir/Generators/PrescriptionFhirGenerator.php @@ -0,0 +1,263 @@ + $source + * @return array + */ + public function generate(array $source): array + { + $timestamp = (string) ($source['completed_at'] ?? date(DATE_ATOM)); + $recordId = (string) ($source['record_id'] ?? '0'); + $patientId = (string) ($source['patient']['id'] ?? '0'); + $sessionId = (string) ($source['session_id'] ?? '0'); + $visitDate = (string) ($source['visit_date'] ?? date('Y-m-d')); + + $careContextReference = 'PRESCRIPTION-' . $recordId . ($sessionId !== '0' ? '-S' . $sessionId : '') . '-' . $visitDate; + $careContextDisplay = 'Prescription ' . $visitDate; + + $builder = new \App\Libraries\Abdm\Fhir\FhirDocumentBuilder(); + $builder + ->buildBundleMeta('prescription-' . $recordId . '-' . strtotime($timestamp), $timestamp) + ->addPatient($this->buildBasePatient($source)); + + $patientRef = 'urn:uuid:patient-' . $patientId; + + $practitioner = $this->buildPractitioner($source); + if (! is_array($practitioner)) { + $fallbackDoctorName = trim((string) ($source['doctor_name'] ?? 'Dr. Attending Medical Officer')); + $docId = (string) ($source['practitioner']['id'] ?? $source['doctor']['id'] ?? '1'); + $practitioner = [ + 'resourceType' => 'Practitioner', + 'id' => 'practitioner-' . ($docId !== '' && $docId !== '0' ? $docId : '1'), + 'name' => [[ + 'text' => $fallbackDoctorName !== '' ? $fallbackDoctorName : 'Dr. Attending Medical Officer', + ]], + ]; + } + $builder->addPractitioner($practitioner); + + $organization = $this->buildOrganization($source); + if (! is_array($organization)) { + $orgId = (string) ($source['organization']['id'] ?? $source['hfr_id'] ?? 'IN0510000871'); + $orgName = (string) ($source['organization']['name'] ?? 'Hospital'); + $organization = [ + 'resourceType' => 'Organization', + 'id' => 'organization-' . ($orgId !== '' ? $orgId : 'IN0510000871'), + 'name' => $orgName, + ]; + } + $builder->addOrganization($organization); + + $encounter = $this->buildEncounter($source); + $encounterRef = null; + if (is_array($encounter)) { + $builder->addEncounter($encounter); + $encounterRef = 'urn:uuid:' . (string) $encounter['id']; + } + + $medicationRefs = []; + $resolvedCount = 0; + $unresolvedCount = 0; + $fallbackCount = 0; + + foreach ((array) ($source['medications'] ?? []) as $idx => $med) { + $drugName = trim((string) ($med['name'] ?? $med['drug_name'] ?? '')); + if (! $this->isMeaningfulValue($drugName)) { + continue; + } + + $dosageText = trim((string) ($med['dosage'] ?? '')); + $medType = trim((string) ($med['formulation'] ?? $med['med_type'] ?? '')); + $routeText = trim((string) ($med['route_text'] ?? '')); + if (! $this->isMeaningfulValue($routeText)) { + $medTypeUpper = strtoupper($medType); + if (in_array($medTypeUpper, ['INJ', 'INJECTION', 'IV', 'IM'], true)) { + $routeText = 'Injection'; + $routeCode = '47625008'; + } elseif (in_array($medTypeUpper, ['CREAM', 'OINT', 'OINTMENT', 'GEL', 'LOTION'], true)) { + $routeText = 'Topical'; + $routeCode = '6064005'; + } else { + $routeText = 'Oral'; + $routeCode = '260548002'; + } + } else { + $routeCode = '260548002'; + } + + $dosageInstruction = [[ + 'text' => $dosageText, + 'route' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => $routeCode, + 'display' => $routeText, + ]], + 'text' => $routeText, + ], + ]]; + + $code = trim((string) ($med['code'] ?? $med['snomed_code'] ?? '')); + $coding = []; + if ($code !== '' && $code !== '371530004') { + $coding[] = [ + 'system' => 'http://snomed.info/sct', + 'code' => $code, + 'display' => $drugName, + ]; + $resolvedCount++; + } else { + $coding[] = [ + 'system' => 'http://snomed.info/sct', + 'code' => '105904009', + 'display' => $drugName, + ]; + $fallbackCount++; + } + + $medId = 'medication-' . $recordId . '-' . $idx; + $medicationRefs[] = [ + 'reference' => 'urn:uuid:' . $medId, + 'type' => 'MedicationRequest', + ]; + + $medResource = [ + 'resourceType' => 'MedicationRequest', + 'id' => $medId, + 'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/MedicationRequest']], + 'status' => 'active', + 'intent' => 'order', + 'subject' => ['reference' => $patientRef], + 'authoredOn' => $timestamp, + 'requester' => ['reference' => 'urn:uuid:' . (string) $practitioner['id']], + 'medicationCodeableConcept' => [ + 'coding' => $coding, + 'text' => $drugName, + ], + 'dosageInstruction' => $dosageInstruction, + ]; + + if ($encounterRef !== null) { + $medResource['encounter'] = ['reference' => $encounterRef]; + } + + $builder->addMedicationRequest($medResource); + } + + $sectionEntries = $medicationRefs; + + // Support attached scanned or digital prescription copy (Binary or DocumentReference) + $docData = (string) ($source['prescription_pdf_base64'] ?? $source['document_data_base64'] ?? $source['pdf_base64'] ?? ''); + if ($docData !== '') { + $docRefId = 'prescription-doc-' . $recordId; + $builder->addDocumentReference([ + 'resourceType' => 'DocumentReference', + 'id' => $docRefId, + 'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference']], + 'status' => 'current', + 'docStatus' => 'final', + 'type' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '440545006', + 'display' => 'Prescription record', + ]], + 'text' => 'Prescription PDF', + ], + 'subject' => ['reference' => $patientRef], + 'date' => $timestamp, + 'description' => 'Prescription Copy PDF', + 'content' => [[ + 'attachment' => [ + 'contentType' => 'application/pdf', + 'language' => 'en-IN', + 'data' => $docData, + 'title' => 'Prescription.pdf', + 'creation' => $timestamp, + ], + ]], + ]); + $sectionEntries[] = [ + 'reference' => 'urn:uuid:' . $docRefId, + 'type' => 'DocumentReference', + ]; + } + + $authorRef = 'urn:uuid:' . (string) $practitioner['id']; + $authorDisplay = (string) ($practitioner['name'][0]['text'] ?? 'Dr. Attending Medical Officer'); + $custodianRef = 'urn:uuid:' . (string) $organization['id']; + + $composition = [ + 'resourceType' => 'Composition', + 'id' => 'composition-' . $recordId, + 'meta' => [ + 'versionId' => '1', + 'lastUpdated' => $timestamp, + 'profile' => [ + 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/PrescriptionRecord', + ], + ], + 'status' => 'final', + 'type' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '440545006', + 'display' => 'Prescription record', + ]], + 'text' => 'Prescription record', + ], + 'title' => 'Prescription record', + 'date' => $timestamp, + 'subject' => [ + 'reference' => $patientRef, + 'display' => (string) ($source['patient']['name'] ?? 'Patient'), + ], + 'author' => [[ + 'reference' => $authorRef, + 'display' => $authorDisplay, + ]], + 'custodian' => [ + 'reference' => $custodianRef, + 'display' => (string) ($organization['name'] ?? 'Hospital'), + ], + 'section' => [ + [ + 'title' => 'Prescription record', + 'code' => [ + 'coding' => [[ + 'system' => 'http://snomed.info/sct', + 'code' => '440545006', + 'display' => 'Prescription record', + ]], + ], + 'entry' => $sectionEntries, + ], + ], + ]; + + if ($encounterRef !== null) { + $composition['encounter'] = ['reference' => $encounterRef]; + } + + $builder->buildComposition($composition); + + $bundle = $builder->toBundle(); + $validation = $this->validator->validate($bundle, 'prescription', [ + 'resolved' => $resolvedCount, + 'unresolved' => $unresolvedCount, + 'fallback_used' => $fallbackCount, + ]); + + return [ + 'hi_type' => 'PrescriptionRecord', + 'care_context_reference' => $careContextReference, + 'care_context_display' => $careContextDisplay, + 'fhir_bundle' => $bundle, + 'validation' => $validation->toArray(), + ]; + } +} diff --git a/tests/unit/Abdm/FhirGeneratorsTest.php b/tests/unit/Abdm/FhirGeneratorsTest.php index 7129673..f8b611a 100644 --- a/tests/unit/Abdm/FhirGeneratorsTest.php +++ b/tests/unit/Abdm/FhirGeneratorsTest.php @@ -1122,6 +1122,140 @@ final class FhirGeneratorsTest extends CIUnitTestCase $encExp = array_values(array_filter($outExp['fhir_bundle']['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'Encounter'))[0]['resource']; $this->assertSame('exp', $encExp['hospitalization']['dischargeDisposition']['coding'][0]['code']); } + + public function testPrescriptionGeneratorHappyPath(): void + { + $src = [ + 'record_id' => 9001, + 'session_id' => 1, + 'visit_date' => '2026-06-27', + 'completed_at' => '2026-06-27T10:00:00+05:30', + 'patient' => [ + 'id' => 12, + 'name' => 'Devender Singh', + 'gender' => 'male', + 'dob' => '1979-03-28', + 'abha_id' => '1234-5678-9012', + ], + 'encounter' => [ + 'id' => 'E101', + 'start' => '2026-06-27T09:30:00+05:30', + 'end' => '2026-06-27T10:00:00+05:30', + ], + 'doctor' => [ + 'id' => 'D9', + 'name' => 'Dr. R K Sundriyal', + ], + 'organization' => [ + 'id' => 'H1', + 'name' => 'Chamunda Hospital', + ], + 'medications' => [ + [ + 'name' => 'Paracetamol 500mg', + 'code' => '387593006', + 'dosage' => '1 tablet twice daily for 3 days', + 'formulation' => 'TAB', + ], + [ + 'name' => 'Amoxicillin 500mg', + 'code' => '372687004', + 'dosage' => '1 capsule thrice daily after meals', + 'formulation' => 'CAP', + ], + ], + 'prescription_pdf_base64' => base64_encode('%PDF-1.4 test prescription'), + ]; + + $out = $this->factory->prescription()->generate($src); + $this->assertSame('PrescriptionRecord', $out['hi_type']); + $this->assertSame('PRESCRIPTION-9001-S1-2026-06-27', $out['care_context_reference']); + $this->assertTrue((bool) ($out['validation']['valid'] ?? false)); + + $bundle = $out['fhir_bundle']; + $this->assertSame('Bundle', $bundle['resourceType']); + $this->assertSame('document', $bundle['type']); + + $composition = $bundle['entry'][0]['resource']; + $this->assertSame('Composition', $composition['resourceType']); + $this->assertContains('https://nrces.in/ndhm/fhir/r4/StructureDefinition/PrescriptionRecord', $composition['meta']['profile']); + $this->assertSame('440545006', $composition['type']['coding'][0]['code']); + $this->assertCount(1, $composition['section']); + $this->assertSame('440545006', $composition['section'][0]['code']['coding'][0]['code']); + + // Check medication requests and document reference + $medRequests = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'MedicationRequest')); + $this->assertCount(2, $medRequests); + $this->assertSame('active', $medRequests[0]['resource']['status']); + $this->assertSame('order', $medRequests[0]['resource']['intent']); + + $docRefs = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'DocumentReference')); + $this->assertCount(1, $docRefs); + $this->assertSame('application/pdf', $docRefs[0]['resource']['content'][0]['attachment']['contentType']); + } + + public function testImmunizationGeneratorHappyPath(): void + { + $src = [ + 'record_id' => 9002, + 'visit_date' => '2026-06-27', + 'completed_at' => '2026-06-27T10:30:00+05:30', + 'patient' => [ + 'id' => 12, + 'name' => 'Devender Singh', + 'gender' => 'male', + 'dob' => '1979-03-28', + 'abha_id' => '1234-5678-9012', + ], + 'doctor' => [ + 'id' => 'D9', + 'name' => 'Dr. R K Sundriyal', + ], + 'organization' => [ + 'id' => 'H1', + 'name' => 'Chamunda Hospital', + ], + 'immunizations' => [ + [ + 'vaccine_name' => 'COVID-19 vaccine', + 'code' => '840534001', + 'given_date' => '2026-06-27T10:15:00+05:30', + ], + [ + 'vaccine_name' => 'Hepatitis B vaccine', + 'code' => '836377005', + 'given_date' => '2026-06-27T10:20:00+05:30', + ], + ], + 'certificate_pdf_base64' => base64_encode('%PDF-1.4 vaccine certificate'), + ]; + + $out = $this->factory->immunization()->generate($src); + $this->assertSame('ImmunizationRecord', $out['hi_type']); + $this->assertSame('IMMUNIZATION-9002-2026-06-27', $out['care_context_reference']); + $this->assertTrue((bool) ($out['validation']['valid'] ?? false)); + + $bundle = $out['fhir_bundle']; + $this->assertSame('Bundle', $bundle['resourceType']); + $this->assertSame('document', $bundle['type']); + + $composition = $bundle['entry'][0]['resource']; + $this->assertSame('Composition', $composition['resourceType']); + $this->assertContains('https://nrces.in/ndhm/fhir/r4/StructureDefinition/ImmunizationRecord', $composition['meta']['profile']); + $this->assertSame('41000179103', $composition['type']['coding'][0]['code']); + $this->assertCount(1, $composition['section']); + $this->assertSame('41000179103', $composition['section'][0]['code']['coding'][0]['code']); + + // Check immunization resources and certificate attachment + $immResources = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'Immunization')); + $this->assertCount(2, $immResources); + $this->assertSame('completed', $immResources[0]['resource']['status']); + $this->assertTrue($immResources[0]['resource']['primarySource']); + + $docRefs = array_values(array_filter($bundle['entry'], static fn (array $e): bool => ($e['resource']['resourceType'] ?? '') === 'DocumentReference')); + $this->assertCount(1, $docRefs); + $this->assertSame('application/pdf', $docRefs[0]['resource']['content'][0]['attachment']['contentType']); + } }