fix(abdm): resolve wellness patient mismatch in fetch, sugar LOINC codes, duplicate notifications and hi_type mapping

This commit is contained in:
DevSoftTech Server 2026-10-07 14:20:41 +05:30
parent db76ad9759
commit bd02f895c5
5 changed files with 270 additions and 133 deletions

View File

@ -5080,79 +5080,110 @@ class AbdmGateway extends BaseController
}
}
// Strategy 0c: Live On-Demand Fresh Generation for Wellness Record
if (preg_match('/^WELLNESS-(?:(\d+)-)?(?:S)?(\d+)/i', $ref, $wMatch)) {
$wPart1 = (int) ($wMatch[1] ?? 0);
$wPart2 = (int) ($wMatch[2] ?? 0);
// Strategy 0c: Live Retrieval & On-Demand Fresh Generation for Wellness Record
if (str_starts_with(strtoupper($ref), 'WELLNESS-')) {
$foundBundle = null;
$foundDisplay = null;
$targetPatientId = 0;
$targetSessionId = 0;
// 1. Check health_records first for this exact care context reference
if ($db->tableExists('health_records')) {
$hrRow = $db->table('health_records')
->where('care_context_reference', $ref)
->groupStart()
->where('record_data IS NOT NULL AND record_data !=', '')
->orWhere('fhir_bundle_enc IS NOT NULL AND fhir_bundle_enc !=', '')
->groupEnd()
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
// Check if part2 is a date (e.g. 20261006)
$isDatePart2 = ($wPart2 >= 19700101 && $wPart2 <= 21001231);
$candidateEntityId = $isDatePart2 ? $wPart1 : $wPart2;
$candidatePatientId = $isDatePart2 ? 0 : $wPart1;
if ($db->tableExists('opd_prescription')) {
// Try finding by prescription id
if ($candidateEntityId > 0) {
$pRow = $db->table('opd_prescription')
->select('id, p_id, opd_id')
->where('id', $candidateEntityId)
->get(1)
->getRowArray();
if ($pRow) {
$targetSessionId = (int) $pRow['id'];
$targetPatientId = (int) $pRow['p_id'];
if ($hrRow && ! empty($hrRow['record_data'])) {
$bundleData = json_decode((string) $hrRow['record_data'], true);
if (is_array($bundleData) && ! empty($bundleData)) {
$foundBundle = $bundleData;
$foundDisplay = $hrRow['care_context_display'] ?? null;
}
} elseif ($hrRow && ! empty($hrRow['fhir_bundle_enc'])) {
try {
$enc = new FhirEncryptionService();
$decrypted = $enc->decrypt((string) $hrRow['fhir_bundle_enc']);
$bundleData = json_decode($decrypted, true);
if (is_array($bundleData) && ! empty($bundleData)) {
$foundBundle = $bundleData;
$foundDisplay = $hrRow['care_context_display'] ?? null;
}
} catch (\Throwable) {
}
}
// If not found, check by session_id or opd_id
if ($targetPatientId <= 0 && $candidateEntityId > 0) {
$pRow = $db->table('opd_prescription')
->select('id, p_id, opd_id')
->where('session_id', $candidateEntityId)
->orWhere('opd_id', $candidateEntityId)
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
if ($pRow) {
$targetSessionId = (int) $pRow['id'];
$targetPatientId = (int) $pRow['p_id'];
}
}
// 2. Check patient_wellness_records if not found in health_records
if ($foundBundle === null && $db->tableExists('patient_wellness_records')) {
$wRow = $db->table('patient_wellness_records')->where('care_context_reference', $ref)->get(1)->getRowArray();
if (! $wRow && preg_match('/-W(\d+)/i', $ref, $wIdM)) {
$wRow = $db->table('patient_wellness_records')->where('id', (int) $wIdM[1])->get(1)->getRowArray();
}
// If candidatePatientId is set, or if candidateEntityId was patient_id
if ($targetPatientId <= 0) {
$testPid = $candidatePatientId > 0 ? $candidatePatientId : $candidateEntityId;
if ($testPid > 0) {
$pRow = $db->table('opd_prescription')
->select('id, p_id, opd_id')
->where('p_id', $testPid)
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
if ($pRow) {
$targetPatientId = (int) $pRow['p_id'];
$targetSessionId = (int) $pRow['id'];
if ($wRow) {
if (! empty($wRow['fhir_bundle_json'])) {
$foundBundle = json_decode((string) $wRow['fhir_bundle_json'], true);
}
if (empty($foundBundle)) {
$wellnessPayload = $this->buildWellnessRecordPayload((int) $wRow['patient_id'], (int) $wRow['id'], '');
if ($wellnessPayload !== null && ! empty($wellnessPayload['bundle'])) {
$foundBundle = $wellnessPayload['bundle'];
$foundDisplay = $wellnessPayload['care_context_display'] ?? null;
}
}
}
}
if ($targetPatientId > 0) {
try {
$wellnessPayload = $this->buildWellnessRecordPayload($targetPatientId, $targetSessionId, '');
if ($wellnessPayload !== null && !empty($wellnessPayload['bundle'])) {
$records[] = [
'careContextReference' => $ref,
'hiType' => 'WellnessRecord',
'display' => $wellnessPayload['care_context_display'] ?? ('Wellness Record - ' . date('d M Y')),
'bundle' => $wellnessPayload['bundle'],
];
continue;
// 3. Fallback: Parse patient ID and OPD session ID for legacy OPD vitals references
if ($foundBundle === null) {
$targetPatientId = 0;
$targetSessionId = 0;
if (preg_match('/^WELLNESS-(?:(\d+)-)?(?:S(\d+)|W(\d+)|(\d+))(?:-(\d+))?/i', $ref, $wMatch)) {
$pPart = (int) ($wMatch[1] ?? 0);
$sPart = (int) (! empty($wMatch[2]) ? $wMatch[2] : (! empty($wMatch[3]) ? $wMatch[3] : ($wMatch[4] ?? 0)));
$dPart = (int) ($wMatch[5] ?? 0);
if ($pPart > 0) {
$targetPatientId = $pPart;
$targetSessionId = $sPart;
} elseif ($sPart > 0) {
if ($db->tableExists('patient_master') && $db->table('patient_master')->where('id', $sPart)->countAllResults() > 0) {
$targetPatientId = $sPart;
} elseif ($db->tableExists('opd_prescription')) {
$pRow = $db->table('opd_prescription')->select('p_id, id')->where('id', $sPart)->get(1)->getRowArray();
if ($pRow) {
$targetPatientId = (int) $pRow['p_id'];
$targetSessionId = (int) $pRow['id'];
}
}
}
} catch (\Throwable $ex) {
log_message('warning', 'Live Wellness FHIR generation in recordsFetch failed: ' . $ex->getMessage());
}
if ($targetPatientId > 0) {
try {
$wellnessPayload = $this->buildWellnessRecordPayload($targetPatientId, $targetSessionId, '');
if ($wellnessPayload !== null && ! empty($wellnessPayload['bundle'])) {
$foundBundle = $wellnessPayload['bundle'];
$foundDisplay = $wellnessPayload['care_context_display'] ?? null;
}
} catch (\Throwable $ex) {
log_message('warning', 'Live Wellness FHIR generation in recordsFetch failed: ' . $ex->getMessage());
}
}
}
if ($foundBundle !== null && ! empty($foundBundle)) {
$records[] = [
'careContextReference' => $ref,
'hiType' => 'WellnessRecord',
'display' => $foundDisplay ?? ('Wellness Record - ' . date('d M Y')),
'bundle' => $foundBundle,
];
continue;
}
}
@ -6584,15 +6615,15 @@ class AbdmGateway extends BaseController
]);
}
}
if ($this->db->tableExists('opd_prescription')) {
$wopd = $this->db->table('opd_prescription')->where('id', $taskEntityId)->get(1)->getRowArray();
if (empty($wRec) && $this->db->tableExists('opd_prescription')) {
$wopd = $this->db->table('opd_prescription')->where('id', $taskEntityId)->where('p_id', $patientId)->get(1)->getRowArray();
if (! empty($wopd)) {
$vDate = ! empty($wopd['date_opd_visit']) ? $wopd['date_opd_visit'] : date('Y-m-d');
$dateStr = date('d M Y', strtotime((string) $vDate));
$cleanDate = date('Ymd', strtotime((string) $vDate));
// Reference format matching task board (e.g. WELLNESS-33723-20261006)
$ccRef = 'WELLNESS-' . $taskEntityId . '-' . $cleanDate;
$display = 'WellnessRecord - ' . $dateStr;
$display = 'Wellness Record - ' . $dateStr;
$addContext([
'careContextId' => $ccRef,
'referenceNumber' => $ccRef,
@ -6632,7 +6663,10 @@ class AbdmGateway extends BaseController
if (str_starts_with($ccRef, 'OPD-') || str_starts_with($ccRef, 'INVOICE-')) {
continue;
}
$hiType = trim((string) ($row['hi_type'] ?? 'HealthDocumentRecord'));
$hiType = trim((string) ($row['hi_type'] ?? ''));
if ($hiType === '' || ($hiType === 'HealthDocumentRecord' && str_starts_with($ccRef, 'WELLNESS-'))) {
$hiType = str_starts_with($ccRef, 'WELLNESS-') ? 'WellnessRecord' : 'HealthDocumentRecord';
}
$dateStr = date('d M Y', strtotime((string) ($row['created_at'] ?? $row['updated_at'] ?? 'now')));
$display = ($hiType === 'WellnessRecord' ? 'Wellness & Vitals Record - ' : ($hiType . ' - ')) . $dateStr;
@ -8478,12 +8512,9 @@ class AbdmGateway extends BaseController
}
$careContextReference = trim((string) ($data['care_context_reference'] ?? ''));
if (($data['reuse_existing'] ?? false) === true && $careContextReference !== '') {
if ($careContextReference !== '') {
$existing = $this->db->table('health_records')
->select('id')
->where('hi_type', (string) ($data['hi_type'] ?? 'unknown'))
->where('entity_type', (string) ($data['entity_type'] ?? ''))
->where('entity_id', (string) ($data['entity_id'] ?? ''))
->where('care_context_reference', $careContextReference)
->orderBy('id', 'DESC')
->get(1)
@ -8491,7 +8522,7 @@ class AbdmGateway extends BaseController
if (! empty($existing)) {
$existingId = (int) ($existing['id'] ?? 0);
$update = $insert;
unset($update['push_status'], $update['push_at'], $update['created_at']);
unset($update['created_at']);
$this->db->table('health_records')->where('id', $existingId)->update($update);
return $existingId;
}
@ -9514,6 +9545,7 @@ class AbdmGateway extends BaseController
'notes' => $ccDisplay,
'queue_id' => $ccRef,
'record_data' => $bundle,
'skip_auto_link' => true,
]);
$this->logGatewayPushResolution('additional_hi_record', $result);
$queueId = $this->extractGatewayPushQueueId($result);
@ -9590,14 +9622,21 @@ class AbdmGateway extends BaseController
// 1. Check patient_wellness_records first (New M2 Comprehensive Wellness & Vitals)
if ($this->db->tableExists('patient_wellness_records')) {
$wBuilder = $this->db->table('patient_wellness_records')->where('patient_id', $patientId);
$wRow = null;
if ($opdId > 0) {
$wBuilder->groupStart()
->where('id', $opdId)
->orWhere('patient_id', $patientId)
->groupEnd();
$wRow = $this->db->table('patient_wellness_records')->where('id', $opdId)->get(1)->getRowArray();
if ($wRow && (int) ($wRow['patient_id'] ?? 0) !== $patientId) {
$patientId = (int) $wRow['patient_id'];
$patientRow = $this->loadPatientRow($patientId);
}
}
if (empty($wRow)) {
$wRow = $this->db->table('patient_wellness_records')
->where('patient_id', $patientId)
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
}
$wRow = $wBuilder->orderBy('id', 'DESC')->get(1)->getRowArray();
if (! empty($wRow)) {
$bundle = ! empty($wRow['fhir_bundle_json']) ? json_decode((string) $wRow['fhir_bundle_json'], true) : [];
$visitDate = date('Y-m-d', strtotime((string) ($wRow['recorded_at'] ?? 'now')));
@ -9608,6 +9647,25 @@ class AbdmGateway extends BaseController
: ('WELLNESS-' . $patientId . '-W' . $entityId . '-' . $cleanDate);
$ccDisplay = 'Wellness Record - ' . date('d/m/Y', strtotime($visitDate));
if (empty($bundle) && class_exists('\App\Controllers\DoctorDocument')) {
try {
$docCtrl = new \App\Controllers\DoctorDocument();
$wSource = $docCtrl->buildWellnessRecordSource($patientId, (int) $wRow['id'], 'wellness');
if (! empty($wSource)) {
$wFactory = new FhirGeneratorFactory();
$wGen = $wFactory->wellness()->generate($wSource);
if (! empty($wGen['bundle'])) {
$bundle = $wGen['bundle'];
$this->db->table('patient_wellness_records')->where('id', (int) $wRow['id'])->update([
'fhir_bundle_json' => json_encode($bundle, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'care_context_reference' => $ccRef,
]);
}
}
} catch (\Throwable $e) {
}
}
if (! empty($bundle)) {
return [
'hi_type' => 'WellnessRecord',

View File

@ -712,7 +712,7 @@ class DoctorDocument extends BaseController
]);
}
public function buildWellnessRecordSource(int $patientId, int $opdSessionId = 0): array
public function buildWellnessRecordSource(int $patientId, int $opdSessionId = 0, string $sourceEntity = ''): array
{
if ($patientId <= 0 || ! $this->db->tableExists('patient_master')) {
return [];
@ -723,8 +723,26 @@ class DoctorDocument extends BaseController
return [];
}
$wRow = [];
$isWellnessDirect = ($sourceEntity === 'wellness');
if ($this->db->tableExists('patient_wellness_records')) {
if ($opdSessionId > 0) {
$wRow = $this->db->table('patient_wellness_records')->where('id', $opdSessionId)->get(1)->getRowArray() ?? [];
if (! empty($wRow)) {
$isWellnessDirect = true;
if (! empty($wRow['patient_id']) && (int) $wRow['patient_id'] !== $patientId) {
$patientId = (int) $wRow['patient_id'];
$patientRow = $this->db->table('patient_master')->where('id', $patientId)->get(1)->getRowArray() ?? $patientRow;
}
}
}
if (empty($wRow) && $isWellnessDirect) {
$wRow = $this->db->table('patient_wellness_records')->where('patient_id', $patientId)->orderBy('id', 'DESC')->get(1)->getRowArray() ?? [];
}
}
$rxRow = [];
if ($this->db->tableExists('opd_prescription')) {
if (! $isWellnessDirect && $this->db->tableExists('opd_prescription')) {
$builder = $this->db->table('opd_prescription')->where('p_id', $patientId);
if ($opdSessionId > 0) {
$builder->groupStart()
@ -758,53 +776,81 @@ class DoctorDocument extends BaseController
}
};
$addVital('8480-6', 'Systolic blood pressure', $rxRow['bp'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8462-4', 'Diastolic blood pressure', $rxRow['diastolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8867-4', 'Heart rate', $rxRow['pulse'] ?? null, '/min', '/min');
$addVital('8302-2', 'Body height', $rxRow['height'] ?? null, 'cm', 'cm');
$addVital('29463-7', 'Body weight', $rxRow['weight'] ?? null, 'kg', 'kg');
$addVital('39156-5', 'Body Mass Index', $rxRow['bmi'] ?? null, 'kg/m2', 'kg/m2');
$tempVal = $rxRow['temp'] ?? null;
if ($tempVal !== null && is_numeric($tempVal)) {
$tempVal = (float) $tempVal > 45 ? round((((float) $tempVal - 32) * 5) / 9, 1) : round((float) $tempVal, 1);
}
$addVital('8310-5', 'Body temperature', $tempVal, 'Cel', 'Cel');
$addVital('9279-1', 'Respiratory rate', $rxRow['rr_min'] ?? null, '/min', '/min');
$addVital('59408-5', 'Oxygen saturation in Arterial blood by Pulse oximetry', $rxRow['spo2'] ?? null, '%', '%');
$addVital('2339-0', 'Blood Glucose', $rxRow['glucose'] ?? null, 'mg/dL', 'mg/dL');
// If no vitals found in opd_prescription, check patient_wellness_records
if (empty($vitals) && $this->db->tableExists('patient_wellness_records')) {
$wBuilder = $this->db->table('patient_wellness_records')->where('patient_id', $patientId);
if ($opdSessionId > 0) {
$wBuilder->groupStart()
->where('id', $opdSessionId)
->orWhere('patient_id', $patientId)
->groupEnd();
if (! empty($wRow)) {
// Populate comprehensive vitals directly from patient_wellness_records
$addVital('8480-6', 'Systolic blood pressure', $wRow['bp_systolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8462-4', 'Diastolic blood pressure', $wRow['bp_diastolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8867-4', 'Heart rate', $wRow['pulse_rate'] ?? null, '/min', '/min');
$addVital('8302-2', 'Body height', $wRow['height_cm'] ?? null, 'cm', 'cm');
$addVital('29463-7', 'Body weight', $wRow['weight_kg'] ?? null, 'kg', 'kg');
$addVital('39156-5', 'Body Mass Index', $wRow['bmi'] ?? null, 'kg/m2', 'kg/m2');
$tC = $wRow['temperature_c'] ?? null;
if ($tC === null && ! empty($wRow['temperature_f'])) {
$tC = round((((float) $wRow['temperature_f'] - 32) * 5) / 9, 1);
}
$wRow = $wBuilder->orderBy('id', 'DESC')->get(1)->getRowArray();
if (! empty($wRow)) {
$addVital('8480-6', 'Systolic blood pressure', $wRow['bp_systolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8462-4', 'Diastolic blood pressure', $wRow['bp_diastolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8867-4', 'Heart rate', $wRow['pulse_rate'] ?? null, '/min', '/min');
$addVital('8302-2', 'Body height', $wRow['height_cm'] ?? null, 'cm', 'cm');
$addVital('29463-7', 'Body weight', $wRow['weight_kg'] ?? null, 'kg', 'kg');
$addVital('39156-5', 'Body Mass Index', $wRow['bmi'] ?? null, 'kg/m2', 'kg/m2');
$tC = $wRow['temperature_c'] ?? null;
if ($tC === null && ! empty($wRow['temperature_f'])) {
$tC = round((((float) $wRow['temperature_f'] - 32) * 5) / 9, 1);
$addVital('8310-5', 'Body temperature', $tC, 'Cel', 'Cel');
$addVital('9279-1', 'Respiratory rate', $wRow['resp_rate'] ?? null, '/min', '/min');
$addVital('59408-5', 'Oxygen saturation in Arterial blood by Pulse oximetry', $wRow['spo2'] ?? null, '%', '%');
$addVital('2339-0', 'Random Blood Glucose', $wRow['sugar_random'] ?? null, 'mg/dL', 'mg/dL');
$addVital('1558-6', 'Fasting Blood Glucose', $wRow['sugar_fasting'] ?? null, 'mg/dL', 'mg/dL');
$addVital('14760-3', 'Glucose [Mass/volume] in Blood 2 hours post meal', $wRow['sugar_pp'] ?? null, 'mg/dL', 'mg/dL');
$addVital('4548-4', 'Hemoglobin A1c', $wRow['hba1c'] ?? null, '%', '%');
$addVital('718-7', 'Hemoglobin', $wRow['hemoglobin'] ?? null, 'g/dL', 'g/dL');
$addVital('56115-9', 'Waist circumference', $wRow['waist_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('56114-2', 'Hip circumference', $wRow['hip_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('8280-0', 'Waist to hip ratio', $wRow['waist_hip_ratio'] ?? null, 'ratio', '{ratio}');
$addVital('55423-8', 'Number of steps in 24 hour Measured', $wRow['daily_steps'] ?? null, '{steps}', '{steps}');
$addVital('93832-4', 'Sleep duration', $wRow['sleep_hours'] ?? null, 'h', 'h');
$addVital('55411-3', 'Exercise duration', $wRow['exercise_min_per_day'] ?? null, 'min/d', 'min/d');
$addVital('72514-3', 'Pain severity - 0-10 verbal numeric rating', $wRow['pain_score'] ?? null, '{score}', '{score}');
} else {
// OPD prescription vitals fallback
$addVital('8480-6', 'Systolic blood pressure', $rxRow['bp'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8462-4', 'Diastolic blood pressure', $rxRow['diastolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8867-4', 'Heart rate', $rxRow['pulse'] ?? null, '/min', '/min');
$addVital('8302-2', 'Body height', $rxRow['height'] ?? null, 'cm', 'cm');
$addVital('29463-7', 'Body weight', $rxRow['weight'] ?? null, 'kg', 'kg');
$addVital('39156-5', 'Body Mass Index', $rxRow['bmi'] ?? null, 'kg/m2', 'kg/m2');
$tempVal = $rxRow['temp'] ?? null;
if ($tempVal !== null && is_numeric($tempVal)) {
$tempVal = (float) $tempVal > 45 ? round((((float) $tempVal - 32) * 5) / 9, 1) : round((float) $tempVal, 1);
}
$addVital('8310-5', 'Body temperature', $tempVal, 'Cel', 'Cel');
$addVital('9279-1', 'Respiratory rate', $rxRow['rr_min'] ?? null, '/min', '/min');
$addVital('59408-5', 'Oxygen saturation in Arterial blood by Pulse oximetry', $rxRow['spo2'] ?? null, '%', '%');
$addVital('2339-0', 'Blood Glucose', $rxRow['glucose'] ?? null, 'mg/dL', 'mg/dL');
// If no vitals found in opd_prescription, check patient_wellness_records
if (empty($vitals) && $this->db->tableExists('patient_wellness_records')) {
$wBuilder = $this->db->table('patient_wellness_records')->where('patient_id', $patientId);
if ($opdSessionId > 0) {
$wBuilder->where('id', $opdSessionId);
}
$wRow = $wBuilder->orderBy('id', 'DESC')->get(1)->getRowArray();
if (! empty($wRow)) {
$addVital('8480-6', 'Systolic blood pressure', $wRow['bp_systolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8462-4', 'Diastolic blood pressure', $wRow['bp_diastolic'] ?? null, 'mmHg', 'mm[Hg]');
$addVital('8867-4', 'Heart rate', $wRow['pulse_rate'] ?? null, '/min', '/min');
$addVital('8302-2', 'Body height', $wRow['height_cm'] ?? null, 'cm', 'cm');
$addVital('29463-7', 'Body weight', $wRow['weight_kg'] ?? null, 'kg', 'kg');
$addVital('39156-5', 'Body Mass Index', $wRow['bmi'] ?? null, 'kg/m2', 'kg/m2');
$tC = $wRow['temperature_c'] ?? null;
if ($tC === null && ! empty($wRow['temperature_f'])) {
$tC = round((((float) $wRow['temperature_f'] - 32) * 5) / 9, 1);
}
$addVital('8310-5', 'Body temperature', $tC, 'Cel', 'Cel');
$addVital('9279-1', 'Respiratory rate', $wRow['resp_rate'] ?? null, '/min', '/min');
$addVital('59408-5', 'Oxygen saturation in Arterial blood by Pulse oximetry', $wRow['spo2'] ?? null, '%', '%');
$addVital('2339-0', 'Random Blood Glucose', $wRow['sugar_random'] ?? null, 'mg/dL', 'mg/dL');
$addVital('1558-6', 'Fasting Blood Glucose', $wRow['sugar_fasting'] ?? null, 'mg/dL', 'mg/dL');
$addVital('14760-3', 'Glucose [Mass/volume] in Blood 2 hours post meal', $wRow['sugar_pp'] ?? null, 'mg/dL', 'mg/dL');
$addVital('4548-4', 'Hemoglobin A1c', $wRow['hba1c'] ?? null, '%', '%');
$addVital('718-7', 'Hemoglobin', $wRow['hemoglobin'] ?? null, 'g/dL', 'g/dL');
$addVital('56115-9', 'Waist circumference', $wRow['waist_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('56114-2', 'Hip circumference', $wRow['hip_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('8280-0', 'Waist to hip ratio', $wRow['waist_hip_ratio'] ?? null, 'ratio', '{ratio}');
}
$addVital('8310-5', 'Body temperature', $tC, 'Cel', 'Cel');
$addVital('9279-1', 'Respiratory rate', $wRow['resp_rate'] ?? null, '/min', '/min');
$addVital('59408-5', 'Oxygen saturation in Arterial blood by Pulse oximetry', $wRow['spo2'] ?? null, '%', '%');
$addVital('2339-0', 'Random Blood Glucose', $wRow['sugar_random'] ?? null, 'mg/dL', 'mg/dL');
$addVital('1558-6', 'Fasting Blood Glucose', $wRow['sugar_fasting'] ?? null, 'mg/dL', 'mg/dL');
$addVital('4548-4', 'Hemoglobin A1c', $wRow['hba1c'] ?? null, '%', '%');
$addVital('718-7', 'Hemoglobin', $wRow['hemoglobin'] ?? null, 'g/dL', 'g/dL');
$addVital('8280-0', 'Waist Circumference', $wRow['waist_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('98337-9', 'Hip Circumference', $wRow['hip_circumference_cm'] ?? null, 'cm', 'cm');
$addVital('72287-6', 'Waist-hip ratio', $wRow['waist_hip_ratio'] ?? null, '{ratio}', '{ratio}');
}
}
@ -862,21 +908,36 @@ class DoctorDocument extends BaseController
$hospitalName = (string) constant('H_Name');
}
$doctorName = trim(trim((string) ($doctorRow['p_fname'] ?? '')) . ' ' . trim((string) ($doctorRow['p_lname'] ?? '')));
$doctorName = ! empty($wRow['recorded_by'])
? trim((string) $wRow['recorded_by'])
: trim(trim((string) ($doctorRow['p_fname'] ?? '')) . ' ' . trim((string) ($doctorRow['p_lname'] ?? '')));
if ($doctorName === '') {
$doctorName = 'Doctor';
$doctorName = 'Attending Medical Officer';
}
$visitDate = ! empty($rxRow['date_opd_visit']) ? date('Y-m-d', strtotime((string) $rxRow['date_opd_visit'])) : date('Y-m-d');
$completedAt = ! empty($rxRow['insert_date']) ? date(DATE_ATOM, strtotime((string) $rxRow['insert_date'])) : date(DATE_ATOM);
$visitDate = ! empty($wRow['recorded_at'])
? date('Y-m-d', strtotime((string) $wRow['recorded_at']))
: (! empty($rxRow['date_opd_visit']) ? date('Y-m-d', strtotime((string) $rxRow['date_opd_visit'])) : date('Y-m-d'));
$completedAt = ! empty($wRow['recorded_at'])
? date(DATE_ATOM, strtotime((string) $wRow['recorded_at']))
: (! empty($rxRow['insert_date']) ? date(DATE_ATOM, strtotime((string) $rxRow['insert_date'])) : date(DATE_ATOM));
$recId = ! empty($wRow['id']) ? (string) $wRow['id'] : (string) ($rxRow['id'] ?? $patientId);
$rawGender = strtolower(trim((string) ($patientRow['gender'] ?? '')));
$cleanGender = match ($rawGender) {
'1', 'm', 'male' => 'male',
'2', 'f', 'female' => 'female',
default => 'unknown',
};
return [
'record_id' => (string) ($rxRow['id'] ?? $patientId),
'record_id' => $recId,
'session_id' => (string) $opdSessionId,
'visit_date' => $visitDate,
'completed_at' => $completedAt,
'encounter' => [
'id' => (string) ($rxRow['id'] ?? $patientId),
'id' => $recId,
'class_code' => 'AMB',
'class_display' => 'ambulatory',
'start' => $completedAt,
@ -897,14 +958,14 @@ class DoctorDocument extends BaseController
'id' => (string) $patientId,
'uhid' => (string) ($patientRow['uhid_no'] ?? $patientRow['uhid'] ?? $patientRow['patient_code'] ?? $patientId),
'name' => $patientName,
'gender' => strtolower((string) ($patientRow['gender'] ?? '')) === 'm' ? 'male' : (strtolower((string) ($patientRow['gender'] ?? '')) === 'f' ? 'female' : 'unknown'),
'gender' => $cleanGender,
'dob' => ! empty($patientRow['dob']) ? date('Y-m-d', strtotime((string) $patientRow['dob'])) : '',
'abha_id' => $abhaDigits,
'abha_address' => $abhaAddress,
'mobile' => (string) ($patientRow['mphone1'] ?? ''),
],
'practitioner' => [
'id' => (string) $drId,
'id' => (string) ($wRow['recorded_by_id'] ?? $drId ?: 1),
'name' => $doctorName,
],
];

View File

@ -1510,6 +1510,9 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
if ($abhaId !== '') {
$recordPushPayload['abha_id'] = $abhaId;
}
if (! empty($data['skip_auto_link'])) {
$recordPushPayload['skip_auto_link'] = true;
}
$res = $this->post('/v3/records/push', $recordPushPayload);
return self::normalizePushRecordResponse($res);

View File

@ -91,7 +91,7 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
// Body measurement LOINC codes
$bodyMeasLoincCodes = ['8302-2', '29463-7', '39156-5', '56115-9', '56114-2', '8280-0'];
// General assessment LOINC codes (blood sugar, lab POC, Hb)
$generalAssessLoincCodes = ['2339-0', '1558-6', '1521-4', '4548-4', '718-7', '25428-4', '1753-3'];
$generalAssessLoincCodes = ['2339-0', '1558-6', '1521-4', '14760-3', '2345-7', '4548-4', '718-7', '25428-4', '1753-3'];
// Physical activity LOINC codes
$activityLoincCodes = ['55423-8', '93832-4', '55411-3', '41981-2'];
@ -216,7 +216,7 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
$numericVal = round($numericVal, 1);
} elseif ($loincCode === '8280-0' || $loincCode === '4548-4') {
$numericVal = round($numericVal, 2);
} elseif (in_array($loincCode, ['8867-4', '59408-5', '9279-1', '2339-0', '1558-6', '1521-4'], true)) {
} elseif (in_array($loincCode, ['8867-4', '59408-5', '9279-1', '2339-0', '1558-6', '1521-4', '14760-3', '2345-7'], true)) {
$numericVal = (float) round($numericVal);
} else {
$numericVal = round($numericVal, 2);

View File

@ -852,6 +852,21 @@
var ref = cb.value;
var display = cb.nextElementSibling ? cb.nextElementSibling.textContent : ref;
var itemHiType = cb.dataset.hiType || '';
if (/^WELLNESS-/i.test(ref)) {
itemHiType = 'WellnessRecord';
} else if (/^DOC-/i.test(ref)) {
itemHiType = 'HealthDocumentRecord';
} else if (/^INVOICE-/i.test(ref)) {
itemHiType = 'InvoiceRecord';
} else if (/^(?:LAB|RAD)-/i.test(ref)) {
itemHiType = 'DiagnosticReportRecord';
} else if (/^IMM-/i.test(ref)) {
itemHiType = 'ImmunizationRecord';
} else if (/^(?:DISCHARGE|IPD)-/i.test(ref)) {
itemHiType = 'DischargeSummaryRecord';
} else if (/^(?:OPD|PRESC)-/i.test(ref)) {
itemHiType = 'OPConsultRecord';
}
if (!dominantHiType && itemHiType) {
dominantHiType = itemHiType;
}