feat(abdm-fhir): add dedicated Prescription and Immunization FHIR generators with NRCES validation
This commit is contained in:
parent
24819cb75c
commit
045f4c5116
|
|
@ -161,6 +161,12 @@ class FhirDocumentBuilder
|
|||
return $this->addResource($resource);
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $resource */
|
||||
public function addImmunization(array $resource): self
|
||||
{
|
||||
return $this->addResource($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
|
|
@ -252,7 +258,7 @@ class FhirDocumentBuilder
|
|||
/**
|
||||
* @param array<string,mixed> $resource
|
||||
*/
|
||||
private function addResource(array $resource): self
|
||||
public function addResource(array $resource): self
|
||||
{
|
||||
$id = (string) ($resource['id'] ?? '');
|
||||
if ($id === '') {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
214
app/Libraries/Abdm/Fhir/Generators/ImmunizationFhirGenerator.php
Normal file
214
app/Libraries/Abdm/Fhir/Generators/ImmunizationFhirGenerator.php
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
<?php
|
||||
|
||||
namespace App\Libraries\Abdm\Fhir\Generators;
|
||||
|
||||
class ImmunizationFhirGenerator extends AbstractModuleFhirGenerator
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $source
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
263
app/Libraries/Abdm/Fhir/Generators/PrescriptionFhirGenerator.php
Normal file
263
app/Libraries/Abdm/Fhir/Generators/PrescriptionFhirGenerator.php
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
<?php
|
||||
|
||||
namespace App\Libraries\Abdm\Fhir\Generators;
|
||||
|
||||
class PrescriptionFhirGenerator extends AbstractModuleFhirGenerator
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $source
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user