diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index 039b073..c1e0f9d 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -4983,10 +4983,11 @@ class AbdmGateway extends BaseController continue; } - // Strategy 0: Live On-Demand Fresh Generation for OPD Consult Record - if (preg_match('/(?:OPD|PRESCRIPTION)-(\d+)(?:-S(\d+))?/i', $ref, $m)) { - $targetPatientId = (int) $m[1]; - $targetSessionId = (int) ($m[2] ?? 0); + // Strategy 0: Live On-Demand Fresh Generation for OPD Consult & Prescription Records + if (preg_match('/^(OPD|PRESCRIPTION|PRESC)-(\d+)(?:-S(\d+))?/i', $ref, $m)) { + $refPrefix = strtoupper($m[1]); + $targetPatientId = (int) $m[2]; + $targetSessionId = (int) ($m[3] ?? 0); $targetOpdId = 0; if ($targetSessionId > 0 && $db->tableExists('opd_prescription')) { @@ -5005,14 +5006,21 @@ class AbdmGateway extends BaseController if ($targetOpdId > 0) { try { + $isPrescriptionReq = in_array($refPrefix, ['PRESC', 'PRESCRIPTION'], true); + $targetBundleType = $isPrescriptionReq ? 'PrescriptionRecord' : 'OPConsultRecord'; + $targetHiType = $isPrescriptionReq ? 'PrescriptionRecord' : 'OPConsultRecord'; + $targetDisplay = $isPrescriptionReq + ? ('Prescription - ' . date('d M Y')) + : ('Consultation Record - ' . date('d M Y')); + $opdCtrl = new \App\Controllers\Opd_prescription(); $opdCtrl->initController($this->request, $this->response, service('logger')); - $regen = $opdCtrl->regenerateFhirBundleInternal($targetOpdId, $targetSessionId); + $regen = $opdCtrl->regenerateFhirBundleInternal($targetOpdId, $targetSessionId, $targetBundleType); if (!empty($regen['ok']) && !empty($regen['bundle'])) { $records[] = [ 'careContextReference' => $ref, - 'hiType' => 'OPConsultRecord', - 'display' => 'Consultation Record - ' . date('d M Y'), + 'hiType' => $targetHiType, + 'display' => $targetDisplay, 'bundle' => $regen['bundle'], ]; continue; @@ -5047,6 +5055,82 @@ 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); + + $targetPatientId = 0; + $targetSessionId = 0; + + // 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 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']; + } + } + // 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 ($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; + } + } catch (\Throwable $ex) { + log_message('warning', 'Live Wellness FHIR generation in recordsFetch failed: ' . $ex->getMessage()); + } + } + } + // Strategy A: Check if health_records already has stored FHIR record_data $hrRow = null; if ($db->tableExists('health_records')) { @@ -5100,13 +5184,14 @@ class AbdmGateway extends BaseController // Strategy B: Build compliant FHIR Document Bundle from patient_master & OPD // STRICTLY restricted to OPD/consultation care contexts. // NEVER synthesize dummy consultation records for invoices, discharge, labs, radiology, etc. - if (preg_match('/^(?:OPD|PRESCRIPTION|CONSULT)/i', $ref)) { + if (preg_match('/^(?:OPD|PRESCRIPTION|PRESC|CONSULT)/i', $ref)) { $bundle = $this->assembleFhirBundleForCareContext($ref); if ($bundle !== null) { + $isPresc = (bool) preg_match('/^(?:PRESC|PRESCRIPTION)/i', $ref); $records[] = [ 'careContextReference' => $ref, - 'hiType' => 'OPConsultRecord', - 'display' => 'OPConsultRecord - ' . date('d M Y'), + 'hiType' => $isPresc ? 'PrescriptionRecord' : 'OPConsultRecord', + 'display' => ($isPresc ? 'PrescriptionRecord - ' : 'OPConsultRecord - ') . date('d M Y'), 'bundle' => $bundle, ]; } @@ -6292,7 +6377,9 @@ class AbdmGateway extends BaseController 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)); - $ccRef = 'WELLNESS-' . $patientId . '-S' . $taskEntityId . '-' . date('Ymd', 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; $addContext([ 'careContextId' => $ccRef, @@ -6386,10 +6473,9 @@ class AbdmGateway extends BaseController } } - // 4. Query opd_prescription (OPD consultations & prescriptions) + // 4. Query opd_prescription (OPD consultations, prescriptions & wellness records) if ($this->db->tableExists('opd_prescription')) { $prescRows = $this->db->table('opd_prescription') - ->select('id, p_id, date_opd_visit, session_id, p_datetime') ->where('p_id', $patientId) ->orderBy('id', 'DESC') ->limit(30) @@ -6401,10 +6487,12 @@ class AbdmGateway extends BaseController ? $pr['date_opd_visit'] : date('Y-m-d', strtotime((string) ($pr['p_datetime'] ?? 'now'))); $dateStr = date('d M Y', strtotime($visitDate)); + $cleanDate = str_replace('-', '', $visitDate); $sessionId = ! empty($pr['session_id']) ? $pr['session_id'] : $pr['id']; - $ccRef = 'OPD-' . $patientId . '-S' . $sessionId . '-' . str_replace('-', '', $visitDate); - $display = 'OPConsultRecord - ' . $dateStr; + // 4a. Consultation Record + $ccRef = 'OPD-' . $patientId . '-S' . $sessionId . '-' . $cleanDate; + $display = 'OPConsultRecord - ' . $dateStr; $addContext([ 'careContextId' => $ccRef, 'referenceNumber' => $ccRef, @@ -6414,6 +6502,44 @@ class AbdmGateway extends BaseController 'is_fhir_ready' => true, 'is_primary' => false, ]); + + // 4b. Prescription Record (if prescribed medicines exist) + $hasMeds = false; + if ($this->db->tableExists('opd_prescrption_prescribed')) { + $hasMeds = ($this->db->table('opd_prescrption_prescribed')->where('prescription_id', (int) $pr['id'])->countAllResults() > 0); + } + if (! $hasMeds && $this->db->tableExists('opd_prescription_prescribed')) { + $hasMeds = ($this->db->table('opd_prescription_prescribed')->where('opd_pre_id', (int) $pr['id'])->countAllResults() > 0); + } + if ($hasMeds) { + $prescCcRef = 'PRESC-' . $patientId . '-S' . $sessionId . '-' . $cleanDate; + $prescDisplay = 'Prescription - ' . $dateStr; + $addContext([ + 'careContextId' => $prescCcRef, + 'referenceNumber' => $prescCcRef, + 'display' => $prescDisplay, + 'record_type' => 'PrescriptionRecord', + 'patient_id' => $patientId, + 'is_fhir_ready' => true, + 'is_primary' => false, + ]); + } + + // 4c. Wellness Record (if vitals exist) + $hasVitals = ! empty($pr['bp']) || ! empty($pr['pulse']) || ! empty($pr['temp']) || ! empty($pr['spo2']) || ! empty($pr['weight']) || ! empty($pr['height']); + if ($hasVitals) { + $wellCcRef = 'WELLNESS-' . $sessionId . '-' . $cleanDate; + $wellDisplay = 'Wellness Record - ' . $dateStr; + $addContext([ + 'careContextId' => $wellCcRef, + 'referenceNumber' => $wellCcRef, + 'display' => $wellDisplay, + 'record_type' => 'WellnessRecord', + 'patient_id' => $patientId, + 'is_fhir_ready' => true, + 'is_primary' => false, + ]); + } } } diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php index 4f4fa93..9ce5386 100644 --- a/app/Controllers/Opd_prescription.php +++ b/app/Controllers/Opd_prescription.php @@ -4844,7 +4844,7 @@ class Opd_prescription extends BaseController * * @return array */ - public function regenerateFhirBundleInternal(int $opdId, int $sessionId): array + public function regenerateFhirBundleInternal(int $opdId, int $sessionId, string $bundleType = 'OPConsultRecord'): array { if ($opdId <= 0) { @@ -4932,7 +4932,8 @@ class Opd_prescription extends BaseController if ($prescriptionPdf !== null) { $clinicalContext['attachments'][] = $prescriptionPdf; } - $clinicalContext['bundle_type'] = 'OPConsultRecord'; + $normalizedBundleType = in_array($bundleType, ['Prescription', 'PrescriptionRecord'], true) ? 'PrescriptionRecord' : 'OPConsultRecord'; + $clinicalContext['bundle_type'] = $normalizedBundleType; $bundle = $this->fhirR4Builder->buildOpConsultBundle($patient, $encounter, $medications, $conditions, $clinicalContext); $bundleJson = (string) json_encode($bundle, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); @@ -4947,10 +4948,10 @@ class Opd_prescription extends BaseController } $generatedAt = Time::now('Asia/Kolkata')->toDateTimeString(); - // Overwrite the latest stored bundle for this opd_id / session + // Overwrite the latest stored bundle for this opd_id / session and bundle_type $existingBuilder = $this->db->table('opd_fhir_documents') ->where('opd_id', $opdId) - ->whereIn('bundle_type', ['OPConsultRecord', 'MedicationRequestBundle', 'PrescriptionRecord']); + ->where('bundle_type', $normalizedBundleType); if ($sessionId > 0) { $existingBuilder->where('opd_session_id', $sessionId); } @@ -4960,7 +4961,7 @@ class Opd_prescription extends BaseController $this->db->table('opd_fhir_documents') ->where('id', (int) $existing['id']) ->update([ - 'bundle_type' => 'OPConsultRecord', + 'bundle_type' => $normalizedBundleType, 'bundle_json' => $bundleJson, 'generated_by' => $generatedBy, 'generated_at' => $generatedAt, @@ -4970,7 +4971,7 @@ class Opd_prescription extends BaseController $this->db->table('opd_fhir_documents')->insert([ 'opd_id' => $opdId, 'opd_session_id' => $sessionId, - 'bundle_type' => 'OPConsultRecord', + 'bundle_type' => $normalizedBundleType, 'bundle_json' => $bundleJson, 'generated_by' => $generatedBy, 'generated_at' => $generatedAt, @@ -4982,7 +4983,9 @@ class Opd_prescription extends BaseController 'ok' => 1, 'message' => 'FHIR bundle regenerated successfully', 'document_id' => $documentId, + 'bundle_type' => $normalizedBundleType, 'bundle' => $bundle, + 'medications' => $medications, 'generated_at' => $generatedAt, 'entry_count' => isset($bundle['entry']) ? count($bundle['entry']) : 0, ]; @@ -11830,6 +11833,45 @@ class Opd_prescription extends BaseController ]); } + // Also generate and store PrescriptionRecord FHIR bundle if medicines were prescribed + $prescBundleJson = null; + if (! empty($medications)) { + $prescContext = $clinicalContext; + $prescContext['bundle_type'] = 'PrescriptionRecord'; + $prescBundle = $this->fhirR4Builder->buildOpConsultBundle($patient, $encounter, $medications, $conditions, $prescContext); + $prescBundleJson = (string) json_encode($prescBundle, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + $existingPrescDoc = $this->db->table('opd_fhir_documents') + ->where('opd_id', $opdId) + ->where('opd_session_id', $sessionId) + ->where('bundle_type', 'PrescriptionRecord') + ->orderBy('id', 'DESC') + ->get(1) + ->getRowArray(); + + if (! empty($existingPrescDoc['id'])) { + $this->db->table('opd_fhir_documents') + ->where('id', (int) $existingPrescDoc['id']) + ->update([ + 'bundle_json' => $prescBundleJson, + 'generated_by' => $generatedBy, + 'generated_at' => $now, + 'updated_at' => $now, + ]); + } else { + $this->db->table('opd_fhir_documents')->insert([ + 'opd_id' => $opdId, + 'opd_session_id' => $sessionId, + 'bundle_type' => 'PrescriptionRecord', + 'bundle_json' => $prescBundleJson, + 'generated_by' => $generatedBy, + 'generated_at' => $now, + 'created_at' => $now, + 'updated_at' => $now, + ]); + } + } + if (! $inserted) { return false; } @@ -11839,8 +11881,10 @@ class Opd_prescription extends BaseController $patientId = (int) ($patientRow['id'] ?? ($opdRow['p_id'] ?? 0)); $visitDateRaw = trim((string) ($opdRow['date_opd_visit'] ?? $opdRow['apointment_date'] ?? '')); $visitDate = $visitDateRaw !== '' ? date('Y-m-d', strtotime($visitDateRaw)) : date('Y-m-d'); - $careContextRef = 'OPD-' . $patientId . '-S' . $sessionId . '-' . str_replace('-', '', $visitDate); + $cleanDate = str_replace('-', '', $visitDate); + $careContextRef = 'OPD-' . $patientId . '-S' . $sessionId . '-' . $cleanDate; + // 1. Upsert OPConsultRecord $existingHr = $this->db->table('health_records') ->select('id') ->where('care_context_reference', $careContextRef) @@ -11865,6 +11909,74 @@ class Opd_prescription extends BaseController $hrPayload['created_at'] = Time::now('Asia/Kolkata')->toDateTimeString(); $this->db->table('health_records')->insert($hrPayload); } + + // 2. Upsert PrescriptionRecord (if medications exist) + if (! empty($medications) && ! empty($prescBundleJson)) { + $prescCcRef = 'PRESC-' . $patientId . '-S' . $sessionId . '-' . $cleanDate; + $existingPrescHr = $this->db->table('health_records') + ->select('id') + ->where('care_context_reference', $prescCcRef) + ->get(1) + ->getRowArray(); + $prescHrPayload = [ + 'patient_id' => $patientId > 0 ? $patientId : null, + 'abha_id' => $abhaAddress !== '' ? $abhaAddress : null, + 'hi_type' => 'PrescriptionRecord', + 'entity_type' => 'opd_prescription', + 'entity_id' => (string) $sessionId, + 'record_data' => $prescBundleJson, + 'care_context_reference' => $prescCcRef, + 'push_status' => 'local_discovery_ready', + 'updated_at' => Time::now('Asia/Kolkata')->toDateTimeString(), + ]; + if (! empty($existingPrescHr)) { + $this->db->table('health_records')->where('id', (int) $existingPrescHr['id'])->update($prescHrPayload); + } else { + $prescHrPayload['created_at'] = Time::now('Asia/Kolkata')->toDateTimeString(); + $this->db->table('health_records')->insert($prescHrPayload); + } + } + + // 3. Upsert WellnessRecord (if vitals exist) + $hasVitals = ! empty($clinicalContext['observations']); + if ($hasVitals && class_exists('\App\Controllers\DoctorDocument')) { + try { + $docCtrl = new \App\Controllers\DoctorDocument(); + $wSource = $docCtrl->buildWellnessRecordSource($patientId, $sessionId); + if (! empty($wSource) && ! empty($wSource['vitals'])) { + $wFactory = new \App\Libraries\Abdm\Fhir\FhirGeneratorFactory(); + $wGen = $wFactory->wellness()->generate($wSource); + if (! empty($wGen['bundle'])) { + $wellBundleJson = json_encode($wGen['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $wellCcRef = 'WELLNESS-' . $sessionId . '-' . $cleanDate; + $existingWellHr = $this->db->table('health_records') + ->select('id') + ->where('care_context_reference', $wellCcRef) + ->get(1) + ->getRowArray(); + $wellHrPayload = [ + 'patient_id' => $patientId > 0 ? $patientId : null, + 'abha_id' => $abhaAddress !== '' ? $abhaAddress : null, + 'hi_type' => 'WellnessRecord', + 'entity_type' => 'opd_vitals', + 'entity_id' => (string) $sessionId, + 'record_data' => $wellBundleJson, + 'care_context_reference' => $wellCcRef, + 'push_status' => 'local_discovery_ready', + 'updated_at' => Time::now('Asia/Kolkata')->toDateTimeString(), + ]; + if (! empty($existingWellHr)) { + $this->db->table('health_records')->where('id', (int) $existingWellHr['id'])->update($wellHrPayload); + } else { + $wellHrPayload['created_at'] = Time::now('Asia/Kolkata')->toDateTimeString(); + $this->db->table('health_records')->insert($wellHrPayload); + } + } + } + } catch (\Throwable $we) { + log_message('warning', '[storePrescriptionFhirBundle] auto wellness error: ' . $we->getMessage()); + } + } } catch (\Throwable $e) { log_message('warning', '[storePrescriptionFhirBundle] auto health_records insert error: ' . $e->getMessage()); } diff --git a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php index 0ad4621..2aaf04b 100644 --- a/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php +++ b/app/Libraries/Abdm/Sync/AbdmTaskBoardSyncService.php @@ -517,6 +517,115 @@ class AbdmTaskBoardSyncService } } + // 5b. If medications exist and we linked OPConsultRecord, also link PrescriptionRecord care context to bridge + $sessionId = (int) ($docRow['opd_session_id'] ?? 0); + if ($isSuccess && $sessionId > 0) { + $hasMeds = false; + if ($this->db->tableExists('opd_prescrption_prescribed')) { + $hasMeds = ($this->db->table('opd_prescrption_prescribed')->where('prescription_id', $sessionId)->countAllResults() > 0); + } + if (! $hasMeds && $this->db->tableExists('opd_prescription_prescribed')) { + $hasMeds = ($this->db->table('opd_prescription_prescribed')->where('opd_pre_id', $sessionId)->countAllResults() > 0); + } + if ($hasMeds) { + $cleanDate = str_replace('-', '', $visitDate); + $prescCcRef = 'PRESC-' . $patientId . '-S' . $sessionId . '-' . $cleanDate; + $prescDisplay = 'Prescription - ' . $visitDate; + try { + $opdCtrl = new \App\Controllers\Opd_prescription(); + $opdCtrl->initController(\Config\Services::request(), \Config\Services::response(), service('logger')); + $prescRegen = $opdCtrl->regenerateFhirBundleInternal($opdId, $sessionId, 'PrescriptionRecord'); + $prescBundleJson = ! empty($prescRegen['bundle']) ? json_encode($prescRegen['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null; + + $prescPushData = [ + 'patient_id' => (string) $patientId, + 'patient_name' => $patientName !== '' ? $patientName : ('PATIENT-' . $patientId), + 'abha_id' => $abhaNumber, + 'abha_address' => $abhaAddress, + 'year_of_birth' => $yearOfBirth, + 'gender' => $gender, + 'hi_type' => 'PrescriptionRecord', + 'record_type' => 'PrescriptionRecord', + 'visit_date' => $visitDate, + 'care_context_reference' => $prescCcRef, + 'care_context_display' => $prescDisplay, + 'notes' => $prescDisplay, + 'queue_id' => $prescCcRef, + ]; + if (! empty($prescRegen['bundle'])) { + $prescPushData['bundle'] = $prescRegen['bundle']; + $prescPushData['fhir_bundle'] = $prescRegen['bundle']; + } + $prescRes = $this->connector->pushRecord($prescPushData); + $this->recordLinkedState( + $patientId, + $effectiveAbha, + 'PrescriptionRecord', + 'opd_prescription', + (string) $sessionId, + $prescCcRef, + (string) ($prescRes['queue_id'] ?? $prescCcRef), + (int) ($prescRes['record_id'] ?? 0), + $prescBundleJson + ); + } catch (\Throwable $pe) { + log_message('warning', '[linkAndPushOpdRecord] Prescription care context push error: ' . $pe->getMessage()); + } + } + + // 5c. If vitals exist, also link WellnessRecord care context to bridge + try { + $pRow = $this->db->table('opd_prescription')->where('id', $sessionId)->get(1)->getRowArray(); + $hasVitals = ! empty($pRow['bp']) || ! empty($pRow['pulse']) || ! empty($pRow['temp']) || ! empty($pRow['spo2']) || ! empty($pRow['weight']); + if ($hasVitals && class_exists('\App\Controllers\DoctorDocument')) { + $cleanDate = str_replace('-', '', $visitDate); + $wellCcRef = 'WELLNESS-' . $sessionId . '-' . $cleanDate; + $docCtrl = new \App\Controllers\DoctorDocument(); + $wSource = $docCtrl->buildWellnessRecordSource($patientId, $sessionId); + if (! empty($wSource) && ! empty($wSource['vitals'])) { + $wFactory = new \App\Libraries\Abdm\Fhir\FhirGeneratorFactory(); + $wGen = $wFactory->wellness()->generate($wSource); + $wellBundleJson = ! empty($wGen['bundle']) ? json_encode($wGen['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null; + $wellDisplay = 'Wellness Record - ' . $visitDate; + + $wellPushData = [ + 'patient_id' => (string) $patientId, + 'patient_name' => $patientName !== '' ? $patientName : ('PATIENT-' . $patientId), + 'abha_id' => $abhaNumber, + 'abha_address' => $abhaAddress, + 'year_of_birth' => $yearOfBirth, + 'gender' => $gender, + 'hi_type' => 'WellnessRecord', + 'record_type' => 'WellnessRecord', + 'visit_date' => $visitDate, + 'care_context_reference' => $wellCcRef, + 'care_context_display' => $wellDisplay, + 'notes' => $wellDisplay, + 'queue_id' => $wellCcRef, + ]; + if (! empty($wGen['bundle'])) { + $wellPushData['bundle'] = $wGen['bundle']; + $wellPushData['fhir_bundle'] = $wGen['bundle']; + } + $wellRes = $this->connector->pushRecord($wellPushData); + $this->recordLinkedState( + $patientId, + $effectiveAbha, + 'WellnessRecord', + 'opd_vitals', + (string) $sessionId, + $wellCcRef, + (string) ($wellRes['queue_id'] ?? $wellCcRef), + (int) ($wellRes['record_id'] ?? 0), + $wellBundleJson + ); + } + } + } catch (\Throwable $we) { + log_message('warning', '[linkAndPushOpdRecord] Wellness care context push error: ' . $we->getMessage()); + } + } + if ($isSuccess) { return [ 'ok' => 1, @@ -794,6 +903,25 @@ class AbdmTaskBoardSyncService 'queue_id' => $careContextRef, ]; + $bundleJson = null; + if ($taskType === 'wellness_record_publish' && class_exists('\App\Controllers\DoctorDocument')) { + try { + $docCtrl = new \App\Controllers\DoctorDocument(); + $wSource = $docCtrl->buildWellnessRecordSource($patientId, $entityId); + if (! empty($wSource)) { + $wFactory = new \App\Libraries\Abdm\Fhir\FhirGeneratorFactory(); + $wGen = $wFactory->wellness()->generate($wSource); + if (! empty($wGen['bundle'])) { + $pushData['bundle'] = $wGen['bundle']; + $pushData['fhir_bundle'] = $wGen['bundle']; + $bundleJson = (string) json_encode($wGen['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + } + } catch (\Throwable $we) { + log_message('warning', '[processIndividualWorkTask] Wellness bundle generation error: ' . $we->getMessage()); + } + } + try { $result = $this->connector->pushRecord($pushData); $ok = (int) ($result['ok'] ?? 0); @@ -812,7 +940,8 @@ class AbdmTaskBoardSyncService (string) $entityId, $careContextRef, $queueId, - (int) ($result['record_id'] ?? 0) + (int) ($result['record_id'] ?? 0), + $bundleJson ); return ['ok' => 1, 'queue_id' => $queueId]; @@ -832,7 +961,8 @@ class AbdmTaskBoardSyncService string $entityId, string $careContextRef, string $queueId, - int $bridgeRecordId + int $bridgeRecordId, + ?string $recordData = null ): void { $now = Time::now('Asia/Kolkata')->toDateTimeString(); @@ -854,6 +984,9 @@ class AbdmTaskBoardSyncService 'abdm_txn_id' => $queueId, 'updated_at' => $now, ]; + if ($recordData !== null && $recordData !== '') { + $hrData['record_data'] = $recordData; + } if ($bridgeRecordId > 0 && in_array('bridge_record_id', $this->db->getFieldNames('health_records') ?? [], true)) { $hrData['bridge_record_id'] = $bridgeRecordId; } diff --git a/app/Libraries/FhirR4Builder.php b/app/Libraries/FhirR4Builder.php index 6e7f2cd..c68c2d8 100644 --- a/app/Libraries/FhirR4Builder.php +++ b/app/Libraries/FhirR4Builder.php @@ -718,10 +718,10 @@ class FhirR4Builder 'type' => [ 'coding' => [[ 'system' => 'http://snomed.info/sct', - 'code' => '371530004', - 'display' => 'Clinical consultation report', + 'code' => $isOpConsult ? '371530004' : '440545006', + 'display' => $isOpConsult ? 'Clinical consultation report' : 'Prescription record', ]], - 'text' => 'Clinical Consultation report', + 'text' => $isOpConsult ? 'Clinical Consultation report' : 'Prescription record', ], 'subject' => ['reference' => $patientRef, 'display' => 'Patient'], 'encounter' => ['reference' => $encounterRef, 'display' => 'Encounter'], diff --git a/tests/unit/Abdm/CareContextDiscoveryTest.php b/tests/unit/Abdm/CareContextDiscoveryTest.php index d510aa0..56af1d2 100644 --- a/tests/unit/Abdm/CareContextDiscoveryTest.php +++ b/tests/unit/Abdm/CareContextDiscoveryTest.php @@ -41,9 +41,23 @@ final class CareContextDiscoveryTest extends CIUnitTestCase $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_prescription') . " ( id INTEGER PRIMARY KEY, p_id INTEGER, + opd_id INTEGER, date_opd_visit TEXT, session_id INTEGER, - p_datetime TEXT + p_datetime TEXT, + bp TEXT, + pulse TEXT, + temp TEXT, + spo2 TEXT, + weight TEXT, + height TEXT + )"); + + $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_prescrption_prescribed') . " ( + id INTEGER PRIMARY KEY, + prescription_id INTEGER, + medicine_name TEXT, + dosage_summary TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_master') . " ( @@ -442,5 +456,82 @@ final class CareContextDiscoveryTest extends CIUnitTestCase $this->assertTrue($invoiceCtx['is_primary']); $this->assertTrue($invoiceCtx['is_fhir_ready']); } + + public function testDiscoversPrescriptionAndWellnessCareContextsWhenPresent(): void + { + $db = \Config\Database::connect(); + $db->table('opd_prescription')->insert([ + 'id' => 901, + 'p_id' => 15350, + 'opd_id' => 15350, + 'session_id' => 901, + 'date_opd_visit' => '2026-10-06', + 'bp' => '120/80', + 'pulse' => '72', + 'temp' => '98.4', + 'spo2' => '98', + 'weight' => '70', + 'height' => '175', + ]); + + $db->table('opd_prescrption_prescribed')->insert([ + 'id' => 101, + 'prescription_id' => 901, + 'medicine_name' => 'CAP PANTOP DSR', + 'dosage_summary' => '1 Tab | OD | 5 days', + ]); + + $refGateway = new ReflectionClass($this->gateway); + $method = $refGateway->getMethod('findCareContextsForPatient'); + $method->setAccessible(true); + + [$v3, $full] = $method->invoke($this->gateway, 15350, 'P-15350', 'DEVENDER SINGH'); + + $refs = array_column($v3, 'referenceNumber'); + $this->assertContains('OPD-15350-S901-20261006', $refs); + $this->assertContains('PRESC-15350-S901-20261006', $refs); + $this->assertContains('WELLNESS-901-20261006', $refs); + + $types = array_column($full, 'record_type'); + $this->assertContains('OPConsultRecord', $types); + $this->assertContains('PrescriptionRecord', $types); + $this->assertContains('WellnessRecord', $types); + } + + public function testDiscoversWellnessTaskBoardCareContext(): void + { + $db = \Config\Database::connect(); + $db->table('opd_prescription')->insert([ + 'id' => 33723, + 'p_id' => 15350, + 'opd_id' => 15350, + 'session_id' => 33723, + 'date_opd_visit' => '2026-10-06', + 'bp' => '130/85', + 'pulse' => '75', + ]); + + $refGateway = new ReflectionClass($this->gateway); + $method = $refGateway->getMethod('findCareContextsForPatient'); + $method->setAccessible(true); + + [$v3, $full] = $method->invoke( + $this->gateway, + 15350, + 'P-15350', + 'DEVENDER SINGH', + 'wellness_record_publish', + '33723' + ); + + $refs = array_column($v3, 'referenceNumber'); + $this->assertContains('WELLNESS-33723-20261006', $refs); + + $primary = $full[0]; + $this->assertSame('WELLNESS-33723-20261006', $primary['careContextId']); + $this->assertSame('WellnessRecord', $primary['record_type']); + $this->assertTrue($primary['is_primary']); + } } +