' . $rawHtml . '
');
$contentType = 'text/html';
} else {
$docDataBase64 = base64_encode('' . esc($docTitle) . '
Uploaded Document ID: ' . $fileUploadId . '
';
$mimeType = 'text/html';
}
$patientName = trim((string) ($patientRow['p_fname'] ?? ''));
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$candidate = trim((string) ($patientRow[$field] ?? ''));
if ($candidate !== '') {
$abhaIdRaw = $candidate;
break;
}
}
$abhaDigits = preg_replace('/\D/', '', $abhaIdRaw);
$abhaDigits = is_string($abhaDigits) ? $abhaDigits : '';
$abhaAddress = trim((string) ($patientRow['abha_address'] ?? ''));
$hfrId = trim($this->getHospitalSettingValue('ABDM_HFR_ID'));
if ($hfrId === '') {
$hfrId = 'HFR-IN-HMS';
}
$hospitalName = $this->getHospitalSettingValue('H_Name');
if ($hospitalName === '' && defined('H_Name')) {
$hospitalName = (string) constant('H_Name');
}
$visitDate = ! empty($fileRow['insert_date']) ? date('Y-m-d', strtotime((string) $fileRow['insert_date'])) : date('Y-m-d');
$completedAt = ! empty($fileRow['insert_date']) ? date(DATE_ATOM, strtotime((string) $fileRow['insert_date'])) : date(DATE_ATOM);
$drId = (int) ($fileRow['doc_id'] ?? $fileRow['upload_by_id'] ?? 1);
$drName = 'Doctor';
if ($drId > 0 && $this->db->tableExists('doctor_master')) {
$docRow = $this->db->table('doctor_master')->where('id', $drId)->get(1)->getRowArray();
if (is_array($docRow)) {
$drName = trim(trim((string) ($docRow['p_fname'] ?? '')) . ' ' . trim((string) ($docRow['p_lname'] ?? '')));
}
}
if ($drName === '' || $drName === 'Doctor') {
$drName = 'Medical Officer';
}
$encounterId = 'encounter-file-' . $fileUploadId;
$encounter = [
'id' => $encounterId,
'status' => 'finished',
'class' => 'AMB',
'start' => $visitDate . 'T09:00:00+05:30',
'end' => $visitDate . 'T09:30:00+05:30',
'period' => [
'start' => $visitDate . 'T09:00:00+05:30',
'end' => $visitDate . 'T09:30:00+05:30',
],
'hospitalization' => false,
];
$cleanVisitDate = date('Ymd', strtotime($visitDate));
$ccRef = 'DOC-file-' . $fileUploadId . '-' . $cleanVisitDate;
if ($this->db->tableExists('health_records')) {
$hrQuery = $this->db->table('health_records')
->where('patient_id', $pid)
->groupStart()
->where('entity_id', (string) $fileUploadId)
->orWhere('care_context_reference LIKE', 'DOC-%' . $fileUploadId . '%');
$fileName = trim((string) ($fileRow['file_name'] ?? ''));
if ($fileName !== '') {
$hrQuery->orWhere('attachment_path LIKE', '%' . $fileName);
$hrQuery->orWhere('record_data LIKE', '%' . $fileName . '%');
if (preg_match('/_(\d{10})_/', $fileName, $tsM)) {
$hrQuery->orWhere('care_context_reference LIKE', 'DOC-%' . $tsM[1] . '%');
$hrQuery->orWhere('entity_id', $tsM[1]);
}
}
$hrQuery->groupEnd();
$existingHr = $hrQuery->orderBy('id', 'DESC')->get(1)->getRowArray();
if (! empty($existingHr['care_context_reference'])) {
$ccRef = trim((string) $existingHr['care_context_reference']);
}
}
return [
'record_id' => 'file-' . $fileUploadId,
'session_id' => (string) ($fileRow['opd_id'] ?? $fileUploadId),
'visit_date' => $visitDate,
'completed_at' => $completedAt,
'care_context_reference' => $ccRef,
'care_context_display' => $docTitle . ' (' . date('d M Y', strtotime($visitDate)) . ')',
'document_title' => $docTitle,
'document_data_base64' => $bytes !== '' ? base64_encode($bytes) : '',
'content_type' => $mimeType,
'doctor_name' => $drName,
'hfr_id' => $hfrId,
'organization' => [
'id' => $hfrId,
'name' => $hospitalName,
],
'encounter' => $encounter,
'encounter_reference' => 'urn:uuid:' . $encounterId,
'patient' => [
'id' => (string) $pid,
'uhid' => (string) ($patientRow['uhid_no'] ?? $patientRow['uhid'] ?? $patientRow['patient_code'] ?? $pid),
'name' => $patientName,
'gender' => strtolower((string) ($patientRow['gender'] ?? '')) === 'm' ? 'male' : (strtolower((string) ($patientRow['gender'] ?? '')) === 'f' ? 'female' : 'unknown'),
'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 > 0 ? $drId : 1),
'name' => $drName,
],
];
}
private function enqueueHealthDocumentFhirSync(int $patientDocId): void
{
if ($patientDocId <= 0) {
return;
}
$source = $this->buildHealthDocumentSource($patientDocId);
if (empty($source)) {
return;
}
$hfrId = (string) ($source['hfr_id'] ?? 'HFR-IN-HMS');
$patientId = (int) ($source['patient']['id'] ?? 0);
$patientName = (string) ($source['patient']['name'] ?? '');
$abhaDigits = (string) ($source['patient']['abha_id'] ?? '');
$abhaAddress = (string) ($source['patient']['abha_address'] ?? '');
$docTitle = (string) ($source['document_title'] ?? 'Medical Certificate');
$visitDate = (string) ($source['visit_date'] ?? date('Y-m-d'));
$doctorName = (string) ($source['doctor_name'] ?? '');
if ($patientId > 0 && preg_match('/^\d{14}$/', $abhaDigits) === 1 && class_exists('\App\Libraries\AbdmWorkTaskService')) {
try {
$taskService = new \App\Libraries\AbdmWorkTaskService();
$taskService->createOrRefreshTask(
'health_document_publish',
'patient_doc',
'doctor_document',
(string) $patientDocId,
$patientId,
$patientName,
$abhaDigits,
'submit',
[
'patient_doc_id' => $patientDocId,
'clinical_timestamp' => (string) ($source['visit_date'] ?? date('Y-m-d H:i:s')),
'trigger' => 'patient_doc.compiled',
]
);
} catch (\Throwable $e) {
log_message('warning', 'Unable to refresh task for patient_doc {id}: {msg}', ['id' => $patientDocId, 'msg' => $e->getMessage()]);
}
}
$factory = new \App\Libraries\Abdm\Fhir\FhirGeneratorFactory();
$generatorOutput = $factory->healthDocument()->generate($source);
$adapter = new \App\Libraries\Abdm\Fhir\Support\GatewayPayloadAdapter();
$gatewayPayload = $adapter->toGatewayPayload($generatorOutput, $source, $hfrId);
$syncPayload = [
'local_record_id' => 'patient-doc-' . $patientDocId,
'local_patient_id' => $patientId,
'hi_type' => 'HealthDocumentRecord',
'care_context_reference' => (string) ($gatewayPayload['care_context_reference'] ?? ('DOC-' . $patientDocId)),
'care_context_display' => (string) ($gatewayPayload['care_context_display'] ?? ($docTitle . ' ' . $visitDate)),
'visit_date' => $visitDate,
'department' => '',
'doctor_name' => $doctorName,
'consent_id' => '',
'hfr_id' => $hfrId,
'source_updated_at' => date('Y-m-d H:i:s'),
'patient_name' => $patientName,
'mobile' => (string) ($source['patient']['mobile'] ?? ''),
'gender' => (string) ($source['patient']['gender'] ?? ''),
'dob' => (string) ($source['patient']['dob'] ?? ''),
'abha_id' => $abhaDigits,
'abha_address' => $abhaAddress,
'fhir_bundle' => (array) ($gatewayPayload['fhir_bundle'] ?? []),
];
$outbox = new \App\Libraries\Abdm\Sync\AbdmSyncOutboxService();
$outbox->enqueueRecordSync($syncPayload);
// Auto-push to Bridge Gateway so care context is linked immediately
try {
$hasValidAbha = (str_contains($abhaAddress, '@') || (strlen(preg_replace('/\D/', '', $abhaDigits)) === 14));
if ($hasValidAbha && class_exists('\App\Controllers\AbdmGateway')) {
\App\Controllers\AbdmGateway::autoPushRecord('health_document', (int) $patientDocId, (int) $patientId);
}
} catch (\Throwable $e) {
log_message('warning', '[DoctorDocument] ABDM health document auto-push failed: ' . $e->getMessage());
}
}
private function resolveFileUploadPath(array $fileRow): string
{
$fileName = trim((string) ($fileRow['file_name'] ?? $fileRow['orig_name'] ?? ''));
$fullPath = trim((string) ($fileRow['full_path'] ?? ''));
$dirPath = trim((string) ($fileRow['file_path'] ?? ''));
$publicPath = trim((string) ($fileRow['public_path'] ?? ''));
$candidates = [];
if ($fullPath !== '') {
$candidates[] = $fullPath;
$candidates[] = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $fullPath);
}
if ($dirPath !== '' && $fileName !== '') {
$candidates[] = rtrim($dirPath, '/\\') . DIRECTORY_SEPARATOR . $fileName;
}
if ($publicPath !== '' && defined('FCPATH')) {
$candidates[] = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . ltrim($publicPath, '/\\');
$candidates[] = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . ltrim(preg_replace('#^/?uploads/#i', '', $publicPath), '/\\');
}
$baseDirs = [
defined('FCPATH') ? FCPATH : '',
defined('FCPATH') ? FCPATH . 'uploads' : '',
defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'health_documents' : '',
defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'nabh_ipd' : '',
defined('FCPATH') ? FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'patient_documents' : '',
defined('ROOTPATH') ? ROOTPATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' : '',
defined('ROOTPATH') ? ROOTPATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'health_documents' : '',
];
$insertDate = ! empty($fileRow['insert_date']) ? (string) $fileRow['insert_date'] : '';
$dateSubdir = $insertDate !== '' ? date('Ymd', strtotime($insertDate)) : '';
if ($fileName !== '') {
foreach ($baseDirs as $base) {
if ($base === '') continue;
if ($dateSubdir !== '') {
$candidates[] = rtrim($base, '/\\') . DIRECTORY_SEPARATOR . $dateSubdir . DIRECTORY_SEPARATOR . $fileName;
}
$candidates[] = rtrim($base, '/\\') . DIRECTORY_SEPARATOR . $fileName;
}
}
foreach ($candidates as $path) {
if ($path !== '' && is_file($path) && is_readable($path)) {
return $path;
}
}
if ($fileName !== '' && defined('FCPATH')) {
$matches = glob(rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . $fileName);
if (! empty($matches) && is_file($matches[0]) && is_readable($matches[0])) {
return $matches[0];
}
$matchesDeep = glob(rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . $fileName);
if (! empty($matchesDeep) && is_file($matchesDeep[0]) && is_readable($matchesDeep[0])) {
return $matchesDeep[0];
}
}
return '';
}
}