feat(abdm): add Encounter period admission/discharge times and location ward/bed mapping
This commit is contained in:
parent
4776bfb37b
commit
5714f9f755
|
|
@ -2947,6 +2947,7 @@ class AbdmGateway extends BaseController
|
|||
$hospital = $this->getHospitalProfileForFhir();
|
||||
$hfrId = trim((string) ($hospital['hfr_id'] ?? ''));
|
||||
$documents = $this->buildIpdPdfDocuments($ipdId, $dischargeRaw);
|
||||
$locationInfo = $this->resolveIpdLocation($ipdId);
|
||||
|
||||
$source = [
|
||||
'record_id' => (string) $ipdId,
|
||||
|
|
@ -2977,9 +2978,14 @@ class AbdmGateway extends BaseController
|
|||
],
|
||||
'encounter' => [
|
||||
'id' => trim((string) ($ipdRow['ipd_code'] ?? '')) ?: (string) $ipdId,
|
||||
'ipd_no' => trim((string) ($ipdRow['ipd_code'] ?? '')) ?: ('IPD-' . $ipdId),
|
||||
'class_code' => 'IMP',
|
||||
'start' => $this->toIsoDateTimeOrNow($admissionRaw),
|
||||
'end' => $this->toIsoDateTimeOrNow($dischargeRaw),
|
||||
'location_display' => (string) ($locationInfo['display'] ?? ''),
|
||||
'ward' => (string) ($locationInfo['ward'] ?? ''),
|
||||
'room' => (string) ($locationInfo['room'] ?? ''),
|
||||
'bed' => (string) ($locationInfo['bed'] ?? ''),
|
||||
],
|
||||
'chief_complaints' => $chiefComplaints,
|
||||
'chief_complaint_narrative' => $chiefComplaintNarrative,
|
||||
|
|
@ -3010,6 +3016,82 @@ class AbdmGateway extends BaseController
|
|||
];
|
||||
}
|
||||
|
||||
private function resolveIpdLocation(int $ipdId): array
|
||||
{
|
||||
$bedNo = '';
|
||||
$wardName = '';
|
||||
$roomName = '';
|
||||
|
||||
if ($ipdId > 0) {
|
||||
if ($this->db->tableExists('bed_master')) {
|
||||
$row = $this->db->table('bed_master bm')
|
||||
->select('bm.bed_number, wm.ward_name, wm.ward_type')
|
||||
->join('ward_master wm', 'wm.id = bm.ward_id', 'left')
|
||||
->where('bm.current_ipd_id', $ipdId)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($row)) {
|
||||
$bedNo = trim((string) ($row['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($row['ward_name'] ?? $row['ward_type'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('bed_assignment_history')) {
|
||||
$bahRow = $this->db->table('bed_assignment_history bah')
|
||||
->select('bm.bed_number, wm.ward_name, wm.ward_type')
|
||||
->join('bed_master bm', 'bm.id = bah.bed_id', 'left')
|
||||
->join('ward_master wm', 'wm.id = bah.ward_id', 'left')
|
||||
->where('bah.ipd_id', $ipdId)
|
||||
->orderBy('bah.id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($bahRow)) {
|
||||
$bedNo = trim((string) ($bahRow['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($bahRow['ward_name'] ?? $bahRow['ward_type'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('ward_beds')) {
|
||||
$wbRow = $this->db->table('ward_beds wb')
|
||||
->select('wb.bed_number, wm.ward_name')
|
||||
->join('ward_master wm', 'wm.id = wb.ward_id', 'left')
|
||||
->where('wb.ipd_id', $ipdId)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($wbRow)) {
|
||||
$bedNo = trim((string) ($wbRow['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($wbRow['ward_name'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('ipd_master')) {
|
||||
$ipd = $this->db->table('ipd_master')->where('id', $ipdId)->get(1)->getRowArray() ?? [];
|
||||
$bedNo = trim((string) ($ipd['bed_no'] ?? $ipd['bed_id'] ?? ''));
|
||||
$roomName = trim((string) ($ipd['room_no'] ?? $ipd['room_id'] ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
if ($wardName !== '') {
|
||||
$parts[] = 'Ward: ' . $wardName;
|
||||
}
|
||||
if ($roomName !== '') {
|
||||
$parts[] = 'Room: ' . $roomName;
|
||||
}
|
||||
if ($bedNo !== '') {
|
||||
$parts[] = 'Bed: ' . $bedNo;
|
||||
}
|
||||
|
||||
$display = ! empty($parts) ? implode(', ', $parts) : ($bedNo !== '' ? ('Bed ' . $bedNo) : '');
|
||||
|
||||
return [
|
||||
'ward' => $wardName,
|
||||
'room' => $roomName,
|
||||
'bed' => $bedNo,
|
||||
'display' => $display,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<int,array<string,string>> */
|
||||
private function buildIpdPdfDocuments(int $ipdId, string $dischargeRaw): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9310,6 +9310,7 @@ class Ipd_discharge extends BaseController
|
|||
}
|
||||
|
||||
$documentsList = $this->buildIpdPdfDocumentsList($ipdId, $dischargeIso);
|
||||
$locationInfo = $this->resolveIpdLocation($ipdId);
|
||||
|
||||
$genderRaw = trim((string) ($patientRow['gender'] ?? ''));
|
||||
if ($genderRaw === '') {
|
||||
|
|
@ -9351,6 +9352,10 @@ class Ipd_discharge extends BaseController
|
|||
'class_code' => 'IMP',
|
||||
'start' => $admissionIso,
|
||||
'end' => $dischargeIso,
|
||||
'location_display' => (string) ($locationInfo['display'] ?? ''),
|
||||
'ward' => (string) ($locationInfo['ward'] ?? ''),
|
||||
'room' => (string) ($locationInfo['room'] ?? ''),
|
||||
'bed' => (string) ($locationInfo['bed'] ?? ''),
|
||||
],
|
||||
'chief_complaints' => $chiefComplaintsList,
|
||||
'chief_complaint_narrative' => $complaintRemarkText,
|
||||
|
|
@ -9437,6 +9442,82 @@ class Ipd_discharge extends BaseController
|
|||
return date(DATE_ATOM, $ts);
|
||||
}
|
||||
|
||||
private function resolveIpdLocation(int $ipdId): array
|
||||
{
|
||||
$bedNo = '';
|
||||
$wardName = '';
|
||||
$roomName = '';
|
||||
|
||||
if ($ipdId > 0) {
|
||||
if ($this->db->tableExists('bed_master')) {
|
||||
$row = $this->db->table('bed_master bm')
|
||||
->select('bm.bed_number, wm.ward_name, wm.ward_type')
|
||||
->join('ward_master wm', 'wm.id = bm.ward_id', 'left')
|
||||
->where('bm.current_ipd_id', $ipdId)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($row)) {
|
||||
$bedNo = trim((string) ($row['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($row['ward_name'] ?? $row['ward_type'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('bed_assignment_history')) {
|
||||
$bahRow = $this->db->table('bed_assignment_history bah')
|
||||
->select('bm.bed_number, wm.ward_name, wm.ward_type')
|
||||
->join('bed_master bm', 'bm.id = bah.bed_id', 'left')
|
||||
->join('ward_master wm', 'wm.id = bah.ward_id', 'left')
|
||||
->where('bah.ipd_id', $ipdId)
|
||||
->orderBy('bah.id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($bahRow)) {
|
||||
$bedNo = trim((string) ($bahRow['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($bahRow['ward_name'] ?? $bahRow['ward_type'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('ward_beds')) {
|
||||
$wbRow = $this->db->table('ward_beds wb')
|
||||
->select('wb.bed_number, wm.ward_name')
|
||||
->join('ward_master wm', 'wm.id = wb.ward_id', 'left')
|
||||
->where('wb.ipd_id', $ipdId)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (! empty($wbRow)) {
|
||||
$bedNo = trim((string) ($wbRow['bed_number'] ?? ''));
|
||||
$wardName = trim((string) ($wbRow['ward_name'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($bedNo === '' && $this->db->tableExists('ipd_master')) {
|
||||
$ipd = $this->db->table('ipd_master')->where('id', $ipdId)->get(1)->getRowArray() ?? [];
|
||||
$bedNo = trim((string) ($ipd['bed_no'] ?? $ipd['bed_id'] ?? ''));
|
||||
$roomName = trim((string) ($ipd['room_no'] ?? $ipd['room_id'] ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
if ($wardName !== '') {
|
||||
$parts[] = 'Ward: ' . $wardName;
|
||||
}
|
||||
if ($roomName !== '') {
|
||||
$parts[] = 'Room: ' . $roomName;
|
||||
}
|
||||
if ($bedNo !== '') {
|
||||
$parts[] = 'Bed: ' . $bedNo;
|
||||
}
|
||||
|
||||
$display = ! empty($parts) ? implode(', ', $parts) : ($bedNo !== '' ? ('Bed ' . $bedNo) : '');
|
||||
|
||||
return [
|
||||
'ward' => $wardName,
|
||||
'room' => $roomName,
|
||||
'bed' => $bedNo,
|
||||
'display' => $display,
|
||||
];
|
||||
}
|
||||
|
||||
private function getNextVisitOptions(string $baseDate): array
|
||||
{
|
||||
$fallbackDays = [3, 4, 5, 7, 10, 15, 20, 30, 60];
|
||||
|
|
|
|||
|
|
@ -206,7 +206,57 @@ abstract class AbstractModuleFhirGenerator
|
|||
];
|
||||
}
|
||||
|
||||
return [
|
||||
$start = trim((string) ($enc['start'] ?? $enc['admission_date'] ?? $source['admission_date'] ?? $source['completed_at'] ?? ''));
|
||||
if ($start === '') {
|
||||
$start = date(DATE_ATOM);
|
||||
} else {
|
||||
$ts = strtotime($start);
|
||||
$start = $ts !== false ? date(DATE_ATOM, $ts) : $start;
|
||||
}
|
||||
|
||||
$end = trim((string) ($enc['end'] ?? $enc['discharge_date'] ?? $source['discharge_date'] ?? $source['completed_at'] ?? ''));
|
||||
if ($end === '') {
|
||||
$end = date(DATE_ATOM);
|
||||
} else {
|
||||
$ts = strtotime($end);
|
||||
$end = $ts !== false ? date(DATE_ATOM, $ts) : $end;
|
||||
}
|
||||
|
||||
$locationList = [];
|
||||
$locDisplay = trim((string) ($enc['location_display'] ?? $enc['location'] ?? $source['location_display'] ?? $source['location'] ?? ''));
|
||||
$ward = trim((string) ($enc['ward'] ?? $enc['ward_name'] ?? $source['ward'] ?? $source['ward_name'] ?? ''));
|
||||
$room = trim((string) ($enc['room'] ?? $enc['room_name'] ?? $source['room'] ?? $source['room_name'] ?? ''));
|
||||
$bed = trim((string) ($enc['bed'] ?? $enc['bed_no'] ?? $enc['bed_number'] ?? $source['bed'] ?? $source['bed_no'] ?? $source['bed_number'] ?? ''));
|
||||
|
||||
if ($locDisplay === '') {
|
||||
$locParts = [];
|
||||
if ($ward !== '') {
|
||||
$locParts[] = 'Ward: ' . $ward;
|
||||
}
|
||||
if ($room !== '') {
|
||||
$locParts[] = 'Room: ' . $room;
|
||||
}
|
||||
if ($bed !== '') {
|
||||
$locParts[] = 'Bed: ' . $bed;
|
||||
}
|
||||
$locDisplay = implode(', ', $locParts);
|
||||
}
|
||||
|
||||
if ($locDisplay !== '') {
|
||||
$locItem = [
|
||||
'location' => [
|
||||
'display' => $locDisplay,
|
||||
],
|
||||
'status' => 'active',
|
||||
'period' => [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
],
|
||||
];
|
||||
$locationList[] = $locItem;
|
||||
}
|
||||
|
||||
$encounterResource = [
|
||||
'resourceType' => 'Encounter',
|
||||
'id' => 'encounter-' . $id,
|
||||
'meta' => ['profile' => [
|
||||
|
|
@ -222,11 +272,17 @@ abstract class AbstractModuleFhirGenerator
|
|||
'reference' => 'urn:uuid:patient-' . (string) ($source['patient']['id'] ?? ''),
|
||||
],
|
||||
'period' => [
|
||||
'start' => (string) ($enc['start'] ?? $source['completed_at'] ?? date(DATE_ATOM)),
|
||||
'end' => (string) ($enc['end'] ?? $source['completed_at'] ?? date(DATE_ATOM)),
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
],
|
||||
'identifier' => $identifiers,
|
||||
];
|
||||
|
||||
if (! empty($locationList)) {
|
||||
$encounterResource['location'] = $locationList;
|
||||
}
|
||||
|
||||
return $encounterResource;
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $source */
|
||||
|
|
|
|||
|
|
@ -229,6 +229,9 @@ final class FhirGeneratorsTest extends CIUnitTestCase
|
|||
'class_code' => 'IMP',
|
||||
'start' => '2026-09-01T04:53:00+05:30',
|
||||
'end' => '2026-09-04T04:58:00+05:30',
|
||||
'location_display' => 'Ward: General Ward, Bed: GW-05',
|
||||
'ward' => 'General Ward',
|
||||
'bed' => 'GW-05',
|
||||
],
|
||||
'doctor' => ['id' => 9, 'name' => 'Dr. Sanjay Kumar', 'registration_number' => 'DOC12345'],
|
||||
'organization' => ['id' => 'IN0510000000', 'name' => 'DevSoft Tech'],
|
||||
|
|
@ -333,6 +336,18 @@ final class FhirGeneratorsTest extends CIUnitTestCase
|
|||
$this->assertSame('General Examination on Admission', $observations[0]['resource']['category'][0]['text']);
|
||||
$this->assertSame('Examination on Discharge', $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'));
|
||||
$this->assertCount(1, $encounters);
|
||||
$encounterRes = $encounters[0]['resource'];
|
||||
$this->assertSame('2026-09-01T04:53:00+05:30', $encounterRes['period']['start'] ?? null);
|
||||
$this->assertSame('2026-09-04T04:58:00+05:30', $encounterRes['period']['end'] ?? null);
|
||||
$this->assertNotEmpty($encounterRes['location'] ?? []);
|
||||
$this->assertSame('Ward: General Ward, Bed: GW-05', $encounterRes['location'][0]['location']['display'] ?? null);
|
||||
$this->assertSame('active', $encounterRes['location'][0]['status'] ?? null);
|
||||
$this->assertSame('2026-09-01T04:53:00+05:30', $encounterRes['location'][0]['period']['start'] ?? null);
|
||||
$this->assertSame('2026-09-04T04:58:00+05:30', $encounterRes['location'][0]['period']['end'] ?? null);
|
||||
|
||||
// Verify all references resolve
|
||||
$fullUrls = array_column($bundle['entry'], 'fullUrl');
|
||||
$encoded = json_encode($bundle, JSON_UNESCAPED_SLASHES);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user