From 3c4dff62854b9b8b909a6b04487f2d8c13d9f31c Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 6 May 2026 02:06:18 +0530 Subject: [PATCH] Improve doctor document workflow and print performance --- app/Config/Routes.php | 1 + app/Controllers/DoctorDocument.php | 176 +++++++++++++++++- .../doctor_document/doc_letterhead_print.php | 6 +- app/Views/doctor_document/doc_pre_data.php | 77 ++++++-- .../doctor_document/patient_doc_edit.php | 51 +++-- .../doctor_document/patient_doc_master.php | 28 ++- app/Views/doctor_document/patient_search.php | 157 ++++++++++++++++ app/Views/doctor_document/workspace.php | 1 + app/Views/partials/sidebar.php | 6 + 9 files changed, 456 insertions(+), 47 deletions(-) create mode 100644 app/Views/doctor_document/patient_search.php diff --git a/app/Config/Routes.php b/app/Config/Routes.php index d5bfca8..0147f7d 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -605,6 +605,7 @@ $routes->post('Opd_prescription/section_template_track_usage', 'Opd_prescription $routes->get('Opd_prescription/section_template_list', 'Opd_prescription::section_template_list'); $routes->get('Opd_prescription/section_past_data', 'Opd_prescription::section_past_data'); $routes->get('Opd_prescription/template_workspace', 'Opd_prescription::template_workspace', ['filter' => 'permission:doctor_work.template_workspace.access,doctor_work.access']); +$routes->get('doctor_work/patient_search', 'DoctorDocument::patient_search', ['filter' => 'permission:doctor_work.template_workspace.access,doctor_work.access']); $routes->get('doctor_work/document_workspace', 'DoctorDocument::workspace', ['filter' => 'permission:doctor_work.template_workspace.access,doctor_work.access']); // Legacy-compatible doctor document templates and issue flow. diff --git a/app/Controllers/DoctorDocument.php b/app/Controllers/DoctorDocument.php index 3510e29..3c68426 100644 --- a/app/Controllers/DoctorDocument.php +++ b/app/Controllers/DoctorDocument.php @@ -148,8 +148,15 @@ class DoctorDocument extends BaseController $logoFile = defined('H_logo') ? (string) constant('H_logo') : 'logo.png'; $logoAbsPath = FCPATH . 'assets/images/' . $logoFile; + $logoSrc = $this->buildImageDataUriFromPath($logoAbsPath); + if ($logoSrc === '' && is_file($logoAbsPath)) { + $logoSrc = str_replace('\\', '/', $logoAbsPath); + } + if ($logoSrc === '') { + $logoSrc = base_url('assets/images/' . $logoFile); + } $logoHtml = ''; + . $logoSrc . '" />'; $age = (string) ($doc['age'] ?? ''); $gender = (string) ($doc['gender'] ?? ''); @@ -158,7 +165,16 @@ class DoctorDocument extends BaseController $drSignImg = ''; $drSignFile = (string) ($doc['dr_sign'] ?? $doc['sign_image'] ?? $doc['doctor_sign'] ?? ''); if ($drSignFile !== '') { - $drSignImg = ''; + $drSignAbsPath = FCPATH . 'assets/images/' . ltrim($drSignFile, '/\\'); + $drSignSrc = $this->buildImageDataUriFromPath($drSignAbsPath); + if ($drSignSrc === '' && is_file($drSignAbsPath)) { + $drSignSrc = str_replace('\\', '/', $drSignAbsPath); + } + if ($drSignSrc === '') { + $drSignSrc = base_url('assets/images/' . $drSignFile); + } + + $drSignImg = ''; } $map = [ @@ -187,6 +203,29 @@ class DoctorDocument extends BaseController return str_replace(array_keys($map), array_values($map), $html); } + private function buildImageDataUriFromPath(string $absolutePath): string + { + $absolutePath = trim($absolutePath); + if ($absolutePath === '' || ! is_file($absolutePath) || ! is_readable($absolutePath)) { + return ''; + } + + $bytes = @file_get_contents($absolutePath); + if ($bytes === false || $bytes === '') { + return ''; + } + + $mime = 'image/png'; + if (function_exists('mime_content_type')) { + $detected = @mime_content_type($absolutePath); + if (is_string($detected) && str_starts_with($detected, 'image/')) { + $mime = $detected; + } + } + + return 'data:' . $mime . ';base64,' . base64_encode($bytes); + } + private function hasColumn(string $table, string $column): bool { if (! $this->db->tableExists($table)) { @@ -226,6 +265,128 @@ class DoctorDocument extends BaseController return view('doctor_document/workspace'); } + public function patient_search() + { + if ($resp = $this->ensureAccess()) { + return $resp; + } + + $search = trim((string) $this->request->getGet('q')); + $patients = $this->searchDoctorWorkPatients($search); + $countsByPatient = $this->getDoctorWorkPatientCounts(array_map(static fn(array $row): int => (int) ($row['id'] ?? 0), $patients)); + + foreach ($patients as &$patientRow) { + $patientId = (int) ($patientRow['id'] ?? 0); + $patientCounts = $countsByPatient[$patientId] ?? []; + $patientRow['opd_count'] = (int) ($patientCounts['opd_count'] ?? 0); + $patientRow['lab_count'] = (int) ($patientCounts['lab_count'] ?? 0); + $patientRow['ipd_count'] = (int) ($patientCounts['ipd_count'] ?? 0); + } + unset($patientRow); + + return view('doctor_document/patient_search', [ + 'search' => $search, + 'patients' => $patients, + ]); + } + + /** + * @return list> + */ + private function searchDoctorWorkPatients(string $search): array + { + $search = preg_replace('/[^A-Za-z0-9 _.@\-]/', '', trim($search)) ?? ''; + + if ($search === '') { + return []; + } + + $searchTokens = preg_split('/\s+/', $search) ?: []; + $whereParts = ['1=1']; + + foreach ($searchTokens as $token) { + $token = trim((string) $token); + if ($token === '') { + continue; + } + + $escaped = $this->db->escape($token); + $likeToken = $this->db->escapeLikeString($token); + + if (is_numeric($token)) { + $whereParts[] = "(p.p_code LIKE '%{$likeToken}' ESCAPE '!' OR p.mphone1 = {$escaped} OR p.udai = {$escaped})"; + continue; + } + + if (ctype_alpha($token)) { + $whereParts[] = "(p.p_fname LIKE '%{$likeToken}%' ESCAPE '!' OR p.email1 = {$escaped})"; + continue; + } + + $whereParts[] = "(p.p_code LIKE '{$likeToken}' ESCAPE '!' OR p.email1 = {$escaped})"; + } + + $sql = "SELECT p.*, DATE_FORMAT(p.last_visit,'%d-%m-%Y') AS Last_Visit + FROM patient_master p + WHERE " . implode(' AND ', $whereParts) . " + GROUP BY p.id + ORDER BY p.last_visit DESC + LIMIT 100"; + + return $this->db->query($sql)->getResultArray(); + } + + /** + * @param list $patientIds + * @return array> + */ + private function getDoctorWorkPatientCounts(array $patientIds): array + { + $patientIds = array_values(array_unique(array_filter(array_map('intval', $patientIds), static fn(int $id): bool => $id > 0))); + if ($patientIds === []) { + return []; + } + + $countsByPatient = []; + foreach ($patientIds as $patientId) { + $countsByPatient[$patientId] = [ + 'opd_count' => 0, + 'lab_count' => 0, + 'ipd_count' => 0, + ]; + } + + $countSources = [ + ['table' => 'opd_master', 'column' => 'p_id', 'alias' => 'opd_count'], + ['table' => 'lab_request', 'column' => 'patient_id', 'alias' => 'lab_count'], + ['table' => 'ipd_master', 'column' => 'p_id', 'alias' => 'ipd_count'], + ]; + + foreach ($countSources as $source) { + if (! $this->hasColumn($source['table'], $source['column'])) { + continue; + } + + $rows = $this->db->table($source['table']) + ->select($source['column'] . ' AS patient_id, COUNT(*) AS total_count', false) + ->whereIn($source['column'], $patientIds) + ->groupBy($source['column']) + ->get() + ->getResultArray(); + + foreach ($rows as $row) { + $patientId = (int) ($row['patient_id'] ?? 0); + if ($patientId <= 0 || ! array_key_exists($patientId, $countsByPatient)) { + continue; + } + + $countsByPatient[$patientId][$source['alias']] = (int) ($row['total_count'] ?? 0); + } + } + + return $countsByPatient; + } + public function open_by_key() { if ($resp = $this->ensureAccess()) { @@ -1087,6 +1248,16 @@ class DoctorDocument extends BaseController $patientDoc ); + $logoFile = defined('H_logo') ? (string) constant('H_logo') : 'logo.png'; + $logoAbsPath = FCPATH . 'assets/images/' . $logoFile; + $hospitalLogoSrc = $this->buildImageDataUriFromPath($logoAbsPath); + if ($hospitalLogoSrc === '' && is_file($logoAbsPath)) { + $hospitalLogoSrc = str_replace('\\', '/', $logoAbsPath); + } + if ($hospitalLogoSrc === '') { + $hospitalLogoSrc = base_url('assets/images/' . $logoFile); + } + $data = [ 'content' => $content, 'print_on_type' => $resolvedPrintType, @@ -1102,6 +1273,7 @@ class DoctorDocument extends BaseController 'custom_header_html' => $customHeaderHtml, 'custom_footer_html' => $customFooterHtml, 'has_selected_print_template' => is_array($selectedPrintTemplate), + 'hospital_logo_src' => $hospitalLogoSrc, ]; $mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf'; diff --git a/app/Views/doctor_document/doc_letterhead_print.php b/app/Views/doctor_document/doc_letterhead_print.php index 313d62f..cce958d 100644 --- a/app/Views/doctor_document/doc_letterhead_print.php +++ b/app/Views/doctor_document/doc_letterhead_print.php @@ -9,6 +9,10 @@ $footerMargin = number_format((float) ($print_footer_margin ?? 1.50), 2, '.', '' $customHeaderHtml = (string) ($custom_header_html ?? ''); $customFooterHtml = (string) ($custom_footer_html ?? ''); $hasSelectedPrintTemplate = ! empty($has_selected_print_template); +$hospitalLogoSrc = trim((string) ($hospital_logo_src ?? '')); +if ($hospitalLogoSrc === '') { + $hospitalLogoSrc = base_url('assets/images/' . (defined('H_logo') ? constant('H_logo') : 'logo.png')); +} ?>