From e473be3ae7cd97138cb3797a8bf8fc392e512dec Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Mon, 3 Aug 2026 02:43:50 +0530 Subject: [PATCH] Fix diagnosis print template dual-PDF flow and settings save bug - Diagnosis.php: resolve doctor mapping via ABDM Report Doctor Mapping, compile pathology/diagnosis reports into separate Letter Head and Plain Paper PDFs, add compiled-pdf-links endpoint - Routes.php: add diagnosis/compiled-pdf-links route - pathology_detail.php: render/persist dual compiled PDF buttons - Setting/Template.php: per-modality Letter Head / Plain Paper template mapping via hospital_setting keys - Setting/Template/index.php: fix tile links using load_form (which only replaces #main) instead of load_form_div, which broke every internal AJAX reload (template type switch, modality switch, reset) on the diagnosis/discharge/document print template settings pages - diagnosis_print_settings.php: fix corrupted watermark_alpha number input (missing step attribute) that silently blocked form submission for templates with non-integer-step legacy values --- app/Config/Routes.php | 1 + app/Controllers/Diagnosis.php | 451 ++++++++++++++++-- app/Controllers/Setting/Template.php | 127 ++++- .../Template/diagnosis_print_settings.php | 145 +++++- app/Views/Setting/Template/index.php | 12 +- app/Views/diagnosis/pathology_detail.php | 72 +++ 6 files changed, 737 insertions(+), 71 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index cd6250a..96b2b65 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -1102,6 +1102,7 @@ $routes->match(['GET', 'POST'], 'diagnosis/get-template-xray/(:num)', 'Diagnosis $routes->post('diagnosis/remove-test/(:num)', 'Diagnosis::removeTest/$1'); $routes->post('diagnosis/update-combine-report', 'Diagnosis::updateCombineReport'); $routes->post('diagnosis/report-compile/(:num)/(:num)', 'Diagnosis::reportCompile/$1/$2'); +$routes->get('diagnosis/compiled-pdf-links/(:num)/(:num)', 'Diagnosis::compiledPdfLinks/$1/$2'); $routes->post('diagnosis/upload-report-file', 'Diagnosis::uploadReportFile'); $routes->get('diagnosis/report-file-list/(:num)/(:num)', 'Diagnosis::reportFileList/$1/$2'); $routes->get('diagnosis/report-file-list/(:num)/(:num)/(:num)', 'Diagnosis::reportFileList/$1/$2/$3'); diff --git a/app/Controllers/Diagnosis.php b/app/Controllers/Diagnosis.php index 8ae1111..2a1590f 100644 --- a/app/Controllers/Diagnosis.php +++ b/app/Controllers/Diagnosis.php @@ -1428,6 +1428,8 @@ class Diagnosis extends BaseController ->get(1) ->getRow(); + $doctorTokenData = $this->resolveMappedReportDoctorTokenData((int) ($row->lab_type ?? 0), $headRow); + $tokens = $this->buildPdfTokens([ 'invoice_code' => $row->invoice_code ?? '', 'patient_name' => $patientName, @@ -1439,9 +1441,9 @@ class Diagnosis extends BaseController 'collected_time' => $row->collected_time ?? '', 'reported_time' => $row->reported_time ?? '', 'report_title' => $repoTitle, - 'doctor_name' => $headRow->doc_name ?? '', - 'doctor_education' => $headRow->doc_edu ?? '', - 'technician_name' => $headRow->tech_name ?? '', + 'doctor_name' => $doctorTokenData['doctor_name'] ?? '', + 'doctor_education' => $doctorTokenData['doctor_education'] ?? '', + 'technician_name' => $doctorTokenData['technician_name'] ?? '', 'signature_image_url' => (string) ($printSetting['signature_image'] ?? ''), ]); @@ -1655,17 +1657,37 @@ class Diagnosis extends BaseController return $defaults; } - $builder = $this->db->table('diagnosis_print_templates') - ->where('modality', $labType) - ->where('status', 1); + $row = null; + // When template_id is explicitly requested, honor it first. + // Some legacy flows may save/select template IDs across modality switches. if ($templateId > 0) { - $builder->where('id', $templateId); - } else { - $builder->orderBy('is_default', 'DESC')->orderBy('id', 'ASC'); + $row = $this->db->table('diagnosis_print_templates') + ->where('modality', $labType) + ->where('status', 1) + ->where('id', $templateId) + ->get(1) + ->getRowArray(); + + if (! is_array($row)) { + $row = $this->db->table('diagnosis_print_templates') + ->where('status', 1) + ->where('id', $templateId) + ->get(1) + ->getRowArray(); + } + } + + if (! is_array($row)) { + $row = $this->db->table('diagnosis_print_templates') + ->where('modality', $labType) + ->where('status', 1) + ->orderBy('is_default', 'DESC') + ->orderBy('id', 'ASC') + ->get(1) + ->getRowArray(); } - $row = $builder->get(1)->getRowArray(); if (! is_array($row)) { return $defaults; } @@ -1736,6 +1758,146 @@ class Diagnosis extends BaseController return $defaults; } + /** + * Resolve report doctor token values using ABDM mapping first, with legacy fallback. + * + * @param object|array|null $legacyHeadRow + * @return array + */ + private function resolveMappedReportDoctorTokenData(int $labType, $legacyHeadRow = null): array + { + $legacyDoctorName = ''; + $legacyDoctorEducation = ''; + $legacyTechnicianName = ''; + + if (is_object($legacyHeadRow)) { + $legacyDoctorName = trim((string) ($legacyHeadRow->doc_name ?? '')); + $legacyDoctorEducation = trim((string) ($legacyHeadRow->doc_edu ?? '')); + $legacyTechnicianName = trim((string) ($legacyHeadRow->tech_name ?? '')); + } elseif (is_array($legacyHeadRow)) { + $legacyDoctorName = trim((string) ($legacyHeadRow['doc_name'] ?? '')); + $legacyDoctorEducation = trim((string) ($legacyHeadRow['doc_edu'] ?? '')); + $legacyTechnicianName = trim((string) ($legacyHeadRow['tech_name'] ?? '')); + } + + $out = [ + 'doctor_name' => $legacyDoctorName, + 'doctor_education' => $legacyDoctorEducation, + 'technician_name' => $legacyTechnicianName, + ]; + + $mappedDoctorId = $this->resolveMappedDoctorIdForLabType($labType); + if ($mappedDoctorId <= 0) { + return $out; + } + + $mappedDoctor = $this->loadMappedDoctorById($mappedDoctorId); + if (! is_array($mappedDoctor)) { + return $out; + } + + $mappedName = trim((string) ($mappedDoctor['name'] ?? '')); + $mappedEducation = trim((string) ($mappedDoctor['education'] ?? '')); + + if ($mappedName !== '') { + $out['doctor_name'] = $mappedName; + } + if ($mappedEducation !== '') { + $out['doctor_education'] = $mappedEducation; + } + + return $out; + } + + private function resolveMappedDoctorIdForLabType(int $labType): int + { + $keys = []; + + switch ($labType) { + case 5: + $keys = ['ABDM_DOC_PATHOLOGY']; + break; + case 3: + $keys = ['ABDM_DOC_XRAY', 'ABDM_DOC_RADIOLOGY']; + break; + case 4: + $keys = ['ABDM_DOC_CTSCAN', 'ABDM_DOC_RADIOLOGY']; + break; + case 1: + $keys = ['ABDM_DOC_ULTRASOUND', 'ABDM_DOC_RADIOLOGY']; + break; + case 2: + $keys = ['ABDM_DOC_MRI', 'ABDM_DOC_RADIOLOGY']; + break; + case 6: + $keys = ['ABDM_DOC_RADIOLOGY']; + break; + default: + $keys = ['ABDM_DOC_PATHOLOGY', 'ABDM_DOC_RADIOLOGY']; + break; + } + + foreach ($keys as $key) { + $id = (int) $this->readSetting($key); + if ($id > 0) { + return $id; + } + } + + return 0; + } + + /** + * @return array|null + */ + private function loadMappedDoctorById(int $doctorId): ?array + { + if ($doctorId <= 0 || ! $this->db->tableExists('doctor_master')) { + return null; + } + + $fields = $this->db->getFieldNames('doctor_master') ?? []; + $select = ['id']; + foreach (['p_title', 'p_fname', 'p_lname', 'education', 'qualification', 'degree', 'speciality', 'specialty', 'designation'] as $field) { + if (in_array($field, $fields, true)) { + $select[] = $field; + } + } + + $row = $this->db->table('doctor_master') + ->select(implode(',', array_unique($select))) + ->where('id', $doctorId) + ->get(1) + ->getRowArray() ?? []; + + if (empty($row)) { + return null; + } + + $title = trim((string) ($row['p_title'] ?? '')); + $first = trim((string) ($row['p_fname'] ?? '')); + $last = trim((string) ($row['p_lname'] ?? '')); + + $name = trim($title . ' ' . $first . ' ' . $last); + if ($name === '') { + $name = trim($first . ' ' . $last); + } + + $education = ''; + foreach (['education', 'qualification', 'degree', 'speciality', 'specialty', 'designation'] as $field) { + $value = trim((string) ($row[$field] ?? '')); + if ($value !== '') { + $education = $value; + break; + } + } + + return [ + 'name' => $name, + 'education' => $education, + ]; + } + /** * Build an associative array of {{token}} => value pairs for PDF template substitution. * All string values are HTML-escaped so they are safe to embed inside HTML attributes and text. @@ -2150,19 +2312,29 @@ class Diagnosis extends BaseController 'report_compile' => $compiledBy, ]); - $pdfStore = $this->autoStoreCompiledPdfAttachment($invoiceId, $labType, $rawData, $header); + $pdfStore = $this->autoStoreCompiledPdfAttachments($invoiceId, $labType, $rawData, $header); $message = 'Data Compile'; - if (($pdfStore['ok'] ?? false) && ! empty($pdfStore['file_id'])) { - $message .= ' + PDF stored'; - } elseif (! empty($pdfStore['error'])) { - $message .= ' (PDF store skipped: ' . (string) $pdfStore['error'] . ')'; + $storedCount = (int) count((array) ($pdfStore['file_ids'] ?? [])); + if ($storedCount > 0) { + $message .= ' + ' . $storedCount . ' PDF stored (Letter Head + Plain Paper)'; } + if (! empty($pdfStore['errors']) && is_array($pdfStore['errors'])) { + $message .= ' (some skipped: ' . implode(' | ', array_values($pdfStore['errors'])) . ')'; + } + + $compileTemplateIds = $this->resolveCompileTemplateIds($labType); + $compilePrintUrls = $this->buildCompilePrintUrls($invoiceId, $labType, $compileTemplateIds); + $compilePrintLabels = $this->buildCompileTemplateLabels($compileTemplateIds); return $this->response->setJSON([ 'status' => 'success', 'message' => $message, 'pdf_file_id' => (int) ($pdfStore['file_id'] ?? 0), + 'pdf_file_ids' => (array) ($pdfStore['file_ids'] ?? []), + 'pdf_file_urls' => $compilePrintUrls, + 'pdf_file_labels' => $compilePrintLabels, + 'pdf_file_urls_saved' => (array) ($pdfStore['file_urls'] ?? []), ]); } catch (\Throwable $e) { return $this->response->setJSON([ @@ -2173,13 +2345,77 @@ class Diagnosis extends BaseController } /** - * Generate compiled report PDF and persist metadata in file_upload_data. + * Generate two compiled report PDFs (letterhead + plain) and persist metadata. * Fail-open: returns error details without throwing so compile flow stays successful. * - * @return array{ok: bool, file_id?: int, error?: string} + * @return array{ok: bool, file_id?: int, file_ids?: array, file_urls?: array, errors?: array} */ - private function autoStoreCompiledPdfAttachment(int $invoiceId, int $labType, string $reportHtml, string $reportHeader = ''): array + private function autoStoreCompiledPdfAttachments(int $invoiceId, int $labType, string $reportHtml, string $reportHeader = ''): array { + $variants = [ + 'letterhead' => [ + 'label' => 'Letter Head', + // On pre-printed letter head paper, avoid injecting extra top patient block. + 'include_header' => false, + ], + 'plain' => [ + 'label' => 'Plain Paper', + // Plain paper needs generated report header details. + 'include_header' => true, + ], + ]; + + $fileIds = []; + $fileUrls = []; + $errors = []; + $firstFileId = 0; + + foreach ($variants as $variantKey => $cfg) { + $single = $this->autoStoreCompiledPdfAttachmentVariant( + $invoiceId, + $labType, + $reportHtml, + $reportHeader, + (string) $variantKey, + (string) ($cfg['label'] ?? $variantKey), + (bool) ($cfg['include_header'] ?? false) + ); + + if (($single['ok'] ?? false) && ! empty($single['file_id'])) { + $fileId = (int) $single['file_id']; + $fileIds[$variantKey] = $fileId; + if (! empty($single['file_url'])) { + $fileUrls[$variantKey] = (string) $single['file_url']; + } + if ($firstFileId <= 0) { + $firstFileId = $fileId; + } + } else { + $errors[$variantKey] = (string) ($single['error'] ?? 'store_failed'); + } + } + + return [ + 'ok' => ! empty($fileIds), + 'file_id' => $firstFileId, + 'file_ids' => $fileIds, + 'file_urls' => $fileUrls, + 'errors' => $errors, + ]; + } + + /** + * @return array{ok: bool, file_id?: int, file_url?: string, error?: string} + */ + private function autoStoreCompiledPdfAttachmentVariant( + int $invoiceId, + int $labType, + string $reportHtml, + string $reportHeader, + string $variantKey, + string $variantLabel, + bool $includeHeader + ): array { if ($invoiceId <= 0 || $labType <= 0) { return ['ok' => false, 'error' => 'invalid_context']; } @@ -2189,8 +2425,13 @@ class Diagnosis extends BaseController } try { - $compiledHtml = trim($reportHeader) . "\n" . trim($reportHtml); - if ($compiledHtml === '') { + $compiledHtml = ''; + if ($includeHeader) { + $compiledHtml .= trim($reportHeader) . "\n"; + } + $compiledHtml .= trim($reportHtml); + + if (trim($compiledHtml) === '') { return ['ok' => false, 'error' => 'empty_compiled_report']; } @@ -2220,7 +2461,7 @@ class Diagnosis extends BaseController $invoiceCode = trim((string) ($inv['invoice_code'] ?? '')); } $invoiceCodeSafe = preg_replace('/[^A-Za-z0-9\-_]/', '_', $invoiceCode !== '' ? $invoiceCode : ('INV_' . $invoiceId)); - $storedName = 'compiled_' . $invoiceCodeSafe . '_L' . $labType . '_' . date('Ymd_His') . '.pdf'; + $storedName = 'compiled_' . $invoiceCodeSafe . '_L' . $labType . '_' . $variantKey . '_' . date('Ymd_His') . '.pdf'; $targetDir = ROOTPATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'diagnosis' . DIRECTORY_SEPARATOR . date('Y') . DIRECTORY_SEPARATOR . date('m'); if (! is_dir($targetDir) && ! @mkdir($targetDir, 0775, true) && ! is_dir($targetDir)) { @@ -2236,12 +2477,12 @@ class Diagnosis extends BaseController $fields = $this->db->getFieldNames('file_upload_data'); - // Keep only one latest auto-compiled PDF per invoice/lab context. + // Keep only one latest auto-compiled PDF per invoice/lab context + variant. if (in_array('scan_type', $fields, true) && in_array('isdelete', $fields, true)) { $this->db->table('file_upload_data') ->where('charge_id', $invoiceId) ->where('charge_type', $labType) - ->where('scan_type', 'compiled_pdf_auto') + ->where('scan_type', 'compiled_pdf_auto_' . $variantKey) ->where('isdelete', 0) ->update(['isdelete' => 1]); } @@ -2254,8 +2495,8 @@ class Diagnosis extends BaseController $fieldMap = [ 'name' => $storedName, 'file_name' => $storedName, - 'orig_name' => 'Compiled_Report_' . ($invoiceCode !== '' ? $invoiceCode : $invoiceId) . '_' . $labType . '.pdf', - 'client_name' => 'Compiled_Report_' . ($invoiceCode !== '' ? $invoiceCode : $invoiceId) . '_' . $labType . '.pdf', + 'orig_name' => 'Compiled_Report_' . ($invoiceCode !== '' ? $invoiceCode : $invoiceId) . '_' . $labType . '_' . $variantKey . '.pdf', + 'client_name' => 'Compiled_Report_' . ($invoiceCode !== '' ? $invoiceCode : $invoiceId) . '_' . $labType . '_' . $variantKey . '.pdf', 'file_ext' => '.pdf', 'file_type' => 'application/pdf', 'full_path' => str_replace('\\', '/', $absolutePath), @@ -2267,17 +2508,17 @@ class Diagnosis extends BaseController 'image_type' => 'pdf', 'insert_date' => Time::now('Asia/Kolkata')->toDateTimeString(), 'insert_time' => Time::now('Asia/Kolkata')->toDateTimeString(), - 'file_desc' => 'Auto compiled report PDF', + 'file_desc' => 'Auto compiled report PDF (' . $variantLabel . ')', 'repo_id' => 0, 'charge_id' => $invoiceId, 'charge_type' => $labType, 'upload_by' => $uploadBy, 'upload_by_id' => $uploadById, - 'scan_type' => 'compiled_pdf_auto', + 'scan_type' => 'compiled_pdf_auto_' . $variantKey, 'show_type' => 0, 'isdelete' => 0, - 'document_type' => 'diagnosis-compiled-report', - 'content_description' => 'Auto-generated from diagnosis/report-compile', + 'document_type' => 'diagnosis-compiled-report-' . $variantKey, + 'content_description' => 'Auto-generated from diagnosis/report-compile (' . $variantLabel . ')', ]; foreach ($fieldMap as $key => $value) { @@ -2291,7 +2532,18 @@ class Diagnosis extends BaseController return ['ok' => false, 'error' => 'Unable to save compiled PDF metadata']; } - return ['ok' => true, 'file_id' => (int) $this->db->insertID()]; + $publicRoot = rtrim(str_replace('\\', '/', ROOTPATH . 'public'), '/'); + $absoluteNormalized = str_replace('\\', '/', $absolutePath); + $relativePublicPath = str_starts_with($absoluteNormalized, $publicRoot) + ? ltrim(substr($absoluteNormalized, strlen($publicRoot)), '/') + : ''; + $fileUrl = $relativePublicPath !== '' ? base_url($relativePublicPath) : ''; + + return [ + 'ok' => true, + 'file_id' => (int) $this->db->insertID(), + 'file_url' => $fileUrl, + ]; } catch (\Throwable $e) { return ['ok' => false, 'error' => $e->getMessage()]; } @@ -2367,6 +2619,8 @@ class Diagnosis extends BaseController $relativeText = trim((string) ($patient->p_rname ?? '')); $repoTitle = trim((string) ($itemType->group_desc ?? 'Lab Report')); + $doctorTokenData = $this->resolveMappedReportDoctorTokenData($labType, $head); + $tokens = $this->buildPdfTokens([ 'invoice_code' => $invoice->invoice_code ?? '', 'patient_name' => $patientName, @@ -2378,9 +2632,9 @@ class Diagnosis extends BaseController 'collected_time' => $invoiceRequest->collected_time ?? '', 'reported_time' => $invoiceRequest->reported_time ?? '', 'report_title' => $repoTitle, - 'doctor_name' => $head->doc_name ?? '', - 'doctor_education' => $head->doc_edu ?? '', - 'technician_name' => $head->tech_name ?? '', + 'doctor_name' => $doctorTokenData['doctor_name'] ?? '', + 'doctor_education' => $doctorTokenData['doctor_education'] ?? '', + 'technician_name' => $doctorTokenData['technician_name'] ?? '', 'signature_image_url' => (string) ($printSetting['signature_image'] ?? ''), ]); @@ -2602,6 +2856,137 @@ class Diagnosis extends BaseController ]); } + public function compiledPdfLinks($invoiceId, $labType) + { + $invoiceId = (int) $invoiceId; + $labType = (int) $labType; + + if ($invoiceId <= 0 || $labType <= 0) { + return $this->response->setJSON([ + 'status' => 'error', + 'message' => 'Invalid parameters', + 'pdf_file_urls' => [], + ]); + } + + $templateIds = $this->resolveCompileTemplateIds($labType); + $urls = $this->buildCompilePrintUrls($invoiceId, $labType, $templateIds); + $labels = $this->buildCompileTemplateLabels($templateIds); + + return $this->response->setJSON([ + 'status' => 'success', + 'pdf_file_urls' => $urls, + 'pdf_file_labels' => $labels, + ]); + } + + /** + * @return array{letterhead:string, plain:string} + */ + private function buildCompilePrintUrls(int $invoiceId, int $labType, array $templateIds = []): array + { + if (empty($templateIds)) { + $templateIds = $this->resolveCompileTemplateIds($labType); + } + + $base = base_url('Lab_Admin/print_pdf_create/' . $invoiceId . '/' . $labType . '/1'); + $letterheadUrl = $base; + if (($templateIds['letterhead'] ?? 0) > 0) { + $letterheadUrl .= '?template_id=' . (int) $templateIds['letterhead']; + } + + $plainUrl = $base; + if (($templateIds['plain'] ?? 0) > 0) { + $plainUrl .= '?template_id=' . (int) $templateIds['plain']; + } + + return [ + 'letterhead' => $letterheadUrl, + 'plain' => $plainUrl, + ]; + } + + /** + * @param array{letterhead:int, plain:int} $templateIds + * @return array{letterhead:string, plain:string} + */ + private function buildCompileTemplateLabels(array $templateIds): array + { + $letterheadName = $this->getDiagnosisTemplateNameById((int) ($templateIds['letterhead'] ?? 0)); + $plainName = $this->getDiagnosisTemplateNameById((int) ($templateIds['plain'] ?? 0)); + + return [ + 'letterhead' => $letterheadName !== '' ? $letterheadName : 'Default Template', + 'plain' => $plainName !== '' ? $plainName : 'Default Template', + ]; + } + + private function getDiagnosisTemplateNameById(int $templateId): string + { + if ($templateId <= 0 || ! $this->db->tableExists('diagnosis_print_templates')) { + return ''; + } + + $row = $this->db->table('diagnosis_print_templates') + ->select('template_name') + ->where('id', $templateId) + ->where('status', 1) + ->get(1) + ->getRowArray(); + + return trim((string) ($row['template_name'] ?? '')); + } + + /** + * @return array{letterhead:int, plain:int} + */ + private function resolveCompileTemplateIds(int $labType): array + { + $letterhead = (int) $this->readSetting('DIAG_COMPILE_TPL_LETTERHEAD_' . $labType); + $plain = (int) $this->readSetting('DIAG_COMPILE_TPL_PLAIN_' . $labType); + + if ($this->db->tableExists('diagnosis_print_templates')) { + if ($letterhead <= 0) { + $defaultRow = $this->db->table('diagnosis_print_templates') + ->select('id') + ->where('modality', $labType) + ->where('status', 1) + ->orderBy('is_default', 'DESC') + ->orderBy('id', 'ASC') + ->get(1) + ->getRowArray(); + $letterhead = (int) ($defaultRow['id'] ?? 0); + } + + if ($plain <= 0) { + $plainBuilder = $this->db->table('diagnosis_print_templates') + ->select('id') + ->where('modality', $labType) + ->where('status', 1); + + if ($letterhead > 0) { + $plainBuilder->where('id !=', $letterhead); + } + + $plainRow = $plainBuilder + ->orderBy('is_default', 'DESC') + ->orderBy('id', 'ASC') + ->get(1) + ->getRowArray(); + $plain = (int) ($plainRow['id'] ?? 0); + + if ($plain <= 0) { + $plain = $letterhead; + } + } + } + + return [ + 'letterhead' => $letterhead, + 'plain' => $plain, + ]; + } + public function imagingUploadGallery($invoiceId, $labType, $labReqId = 0) { $invoiceId = (int) $invoiceId; diff --git a/app/Controllers/Setting/Template.php b/app/Controllers/Setting/Template.php index 0a1d9ff..f18113c 100644 --- a/app/Controllers/Setting/Template.php +++ b/app/Controllers/Setting/Template.php @@ -2256,6 +2256,24 @@ HTML; $selectedTemplateId = (int) ($this->request->getGet('template_id') ?? 0); $isNewTemplate = (int) ($this->request->getGet('new') ?? 0) === 1; + $compileLetterheadTemplateId = (int) $this->readHospitalSettingValue('DIAG_COMPILE_TPL_LETTERHEAD_' . $modality); + $compilePlainTemplateId = (int) $this->readHospitalSettingValue('DIAG_COMPILE_TPL_PLAIN_' . $modality); + $currentTemplateType = strtolower(trim((string) ($this->request->getGet('template_type') ?? 'letterhead'))); + if (! in_array($currentTemplateType, ['letterhead', 'plain'], true)) { + $currentTemplateType = 'letterhead'; + } + + $mappedTemplateIdForType = $currentTemplateType === 'plain' + ? $compilePlainTemplateId + : $compileLetterheadTemplateId; + + if ($selectedTemplateId <= 0) { + if ($mappedTemplateIdForType > 0) { + $selectedTemplateId = $mappedTemplateIdForType; + } else { + $isNewTemplate = true; + } + } if (strtolower($this->request->getMethod()) === 'post') { $modality = (int) ($this->request->getPost('modality') ?? $modality); @@ -2263,8 +2281,28 @@ HTML; $modality = 3; } + $currentTemplateType = strtolower(trim((string) ($this->request->getPost('template_type') ?? $currentTemplateType))); + if (! in_array($currentTemplateType, ['letterhead', 'plain'], true)) { + $currentTemplateType = 'letterhead'; + } + + $compileLetterheadTemplateId = (int) ($this->request->getPost('compile_letterhead_template_id') ?? 0); + $compilePlainTemplateId = (int) ($this->request->getPost('compile_plain_template_id') ?? 0); + $selectedTemplateId = (int) ($this->request->getPost('template_id') ?? 0); + $mappedTemplateIdForType = $currentTemplateType === 'plain' + ? $compilePlainTemplateId + : $compileLetterheadTemplateId; + + if ($mappedTemplateIdForType > 0) { + $selectedTemplateId = $mappedTemplateIdForType; + } elseif ($currentTemplateType === 'plain' && $compileLetterheadTemplateId > 0 && $selectedTemplateId === $compileLetterheadTemplateId) { + $selectedTemplateId = 0; + } elseif ($currentTemplateType === 'letterhead' && $compilePlainTemplateId > 0 && $selectedTemplateId === $compilePlainTemplateId) { + $selectedTemplateId = 0; + } + if (! $columnsReady) { $notice = 'Print settings columns are missing. Please run migration first.'; $noticeType = 'danger'; @@ -2278,10 +2316,7 @@ HTML; ->getRowArray() ?? []; } - $templateName = trim((string) ($this->request->getPost('template_name') ?? '')); - if ($templateName === '') { - $templateName = 'Template ' . date('d-m-Y H:i'); - } + $templateName = $currentTemplateType === 'plain' ? 'Plain Paper' : 'Letter Head'; $removeBackground = (int) ($this->request->getPost('remove_background') ?? 0) === 1; $removeWatermarkImage = (int) ($this->request->getPost('remove_watermark_image') ?? 0) === 1; @@ -2384,7 +2419,7 @@ HTML; 'patient_info_html' => (string) ($this->request->getPost('patient_info_html') ?? ''), 'mpdf_prefix_html' => (string) ($this->request->getPost('mpdf_prefix_html') ?? ''), 'mpdf_suffix_html' => (string) ($this->request->getPost('mpdf_suffix_html') ?? ''), - 'is_default' => (int) ($this->request->getPost('is_default') ?? 0) === 1 ? 1 : 0, + 'is_default' => ! empty($existing) ? (int) ($existing['is_default'] ?? 0) : 0, 'status' => 1, ]; @@ -2393,11 +2428,18 @@ HTML; } if ($noticeType !== 'danger') { - if ($data['is_default'] === 1) { - $templateTable + if (empty($existing)) { + $hasDefaultTemplate = (bool) ($templateTable + ->select('id') ->where('modality', $modality) - ->set('is_default', 0) - ->update(); + ->where('status', 1) + ->where('is_default', 1) + ->get(1) + ->getRowArray()); + + if (! $hasDefaultTemplate && $currentTemplateType === 'letterhead') { + $data['is_default'] = 1; + } } if (! empty($existing) && $selectedTemplateId > 0) { @@ -2412,6 +2454,27 @@ HTML; $notice = (($modalityList[$modality] ?? 'Diagnosis') . ' print template saved.'); $noticeType = 'success'; + + if ($currentTemplateType === 'plain') { + $compilePlainTemplateId = $selectedTemplateId; + } else { + $compileLetterheadTemplateId = $selectedTemplateId; + } + + $letterheadSettingKey = 'DIAG_COMPILE_TPL_LETTERHEAD_' . $modality; + $plainSettingKey = 'DIAG_COMPILE_TPL_PLAIN_' . $modality; + + if ($compileLetterheadTemplateId > 0) { + $this->upsertHospitalSettingValue($letterheadSettingKey, (string) $compileLetterheadTemplateId); + } else { + $this->deleteHospitalSettingValue($letterheadSettingKey); + } + + if ($compilePlainTemplateId > 0) { + $this->upsertHospitalSettingValue($plainSettingKey, (string) $compilePlainTemplateId); + } else { + $this->deleteHospitalSettingValue($plainSettingKey); + } } } @@ -2455,6 +2518,9 @@ HTML; 'row' => $row, 'templates' => $templates, 'selected_template_id' => $selectedTemplateId, + 'compile_letterhead_template_id' => $compileLetterheadTemplateId, + 'compile_plain_template_id' => $compilePlainTemplateId, + 'current_template_type' => $currentTemplateType, 'notice' => $notice, 'notice_type' => $noticeType, 'columns_ready' => $columnsReady, @@ -2464,6 +2530,49 @@ HTML; ]); } + private function readHospitalSettingValue(string $name): string + { + if ($name === '' || ! $this->db->tableExists('hospital_setting')) { + return ''; + } + + $row = $this->db->table('hospital_setting') + ->select('s_value') + ->where('s_name', $name) + ->get(1) + ->getRowArray(); + + return trim((string) ($row['s_value'] ?? '')); + } + + private function upsertHospitalSettingValue(string $name, string $value): bool + { + if ($name === '' || ! $this->db->tableExists('hospital_setting')) { + return false; + } + + $table = $this->db->table('hospital_setting'); + $existing = $table->select('id')->where('s_name', $name)->get(1)->getRowArray(); + + if ($existing) { + return (bool) $table->where('id', (int) ($existing['id'] ?? 0))->update(['s_value' => $value]); + } + + return (bool) $table->insert([ + 's_name' => $name, + 's_value' => $value, + ]); + } + + private function deleteHospitalSettingValue(string $name): bool + { + if ($name === '' || ! $this->db->tableExists('hospital_setting')) { + return false; + } + + return (bool) $this->db->table('hospital_setting')->where('s_name', $name)->delete(); + } + public function document_print_settings() { if ($resp = $this->requireAnyPermission([ diff --git a/app/Views/Setting/Template/diagnosis_print_settings.php b/app/Views/Setting/Template/diagnosis_print_settings.php index 522c0f5..df2ef72 100644 --- a/app/Views/Setting/Template/diagnosis_print_settings.php +++ b/app/Views/Setting/Template/diagnosis_print_settings.php @@ -7,9 +7,22 @@ $columnsReady = (bool) ($columns_ready ?? false); $modalityList = $modality_list ?? []; $templates = $templates ?? []; $selectedTemplateId = (int) ($selected_template_id ?? 0); +$compileLetterheadTemplateId = (int) ($compile_letterhead_template_id ?? 0); +$compilePlainTemplateId = (int) ($compile_plain_template_id ?? 0); +$currentTemplateType = (string) ($current_template_type ?? 'letterhead'); $hasSignatureImageColumn = (bool) ($has_signature_image_column ?? false); $modalityLabel = (string) ($modalityList[$modality] ?? 'Diagnosis'); $panelTitle = $modalityLabel . ' Print Template'; + +$templateNameById = []; +foreach ($templates as $tpl) { + $tplId = (int) ($tpl['id'] ?? 0); + if ($tplId > 0) { + $templateNameById[$tplId] = (string) ($tpl['template_name'] ?? ('Template ' . $tplId)); + } +} +$compileLetterheadTemplateName = (string) ($templateNameById[$compileLetterheadTemplateId] ?? ($compileLetterheadTemplateId > 0 ? 'Template ' . $compileLetterheadTemplateId : 'Use modality default template')); +$compilePlainTemplateName = (string) ($templateNameById[$compilePlainTemplateId] ?? ($compilePlainTemplateId > 0 ? 'Template ' . $compilePlainTemplateId : 'Use modality default template')); ?>
@@ -35,6 +48,10 @@ $panelTitle = $modalityLabel . ' Print Template';
+ + + +
@@ -44,30 +61,19 @@ $panelTitle = $modalityLabel . ' Print Template';
-
- - -
-
-
- > - -
-
+
-
- -
- - - - - + New Template +
+
+ + +
+
+
@@ -136,7 +142,7 @@ $panelTitle = $modalityLabel . ' Print Template';
- +
@@ -317,10 +323,66 @@ $panelTitle = $modalityLabel . ' Print Template'; } }; + window.loadDiagnosisTemplateType = function (value) { + var type = String(value || 'letterhead'); + var templateId = 0; + var letterInput = document.getElementById('compile_letterhead_template_id'); + var plainInput = document.getElementById('compile_plain_template_id'); + var typeInput = document.getElementById('template_type_input'); + var templateNameInput = document.getElementById('template_name_input'); + + if (typeInput) { + typeInput.value = type; + } + if (templateNameInput) { + templateNameInput.value = type === 'plain' ? 'Plain Paper' : 'Letter Head'; + } + + if (type === 'plain') { + templateId = plainInput ? parseInt(plainInput.value || '0', 10) : 0; + } else { + templateId = letterInput ? parseInt(letterInput.value || '0', 10) : 0; + } + + var url = ''; + var query = ['template_type=' + encodeURIComponent(type)]; + if (templateId > 0) { + query.push('template_id=' + encodeURIComponent(String(templateId))); + } else { + query.push('new=1'); + } + url += '?' + query.join('&'); + + if (typeof load_form_div === 'function') { + load_form_div(url, 'maindiv', ''); + } else { + window.location.href = url; + } + }; + if (form) { form.addEventListener('submit', function (event) { event.preventDefault(); + var typeInput = document.getElementById('template_type_input'); + var currentType = typeInput ? String(typeInput.value || 'letterhead') : 'letterhead'; + var templateNameInput = document.getElementById('template_name_input'); + var templateIdInput = form.querySelector('input[name="template_id"]'); + var letterInput = document.getElementById('compile_letterhead_template_id'); + var plainInput = document.getElementById('compile_plain_template_id'); + + if (templateNameInput) { + templateNameInput.value = currentType === 'plain' ? 'Plain Paper' : 'Letter Head'; + } + + if (templateIdInput) { + if (currentType === 'plain') { + templateIdInput.value = plainInput ? String(parseInt(plainInput.value || '0', 10) || 0) : '0'; + } else { + templateIdInput.value = letterInput ? String(parseInt(letterInput.value || '0', 10) || 0) : '0'; + } + } + var submitBtn = form.querySelector('button[type="submit"]'); if (submitBtn) { submitBtn.disabled = true; @@ -341,6 +403,32 @@ $panelTitle = $modalityLabel . ' Print Template'; var message = (result && result.notice) ? result.notice : 'Unable to save settings.'; showNotice(status, message); + if (result && result.selected_template_id) { + var tplInput = form.querySelector('input[name="template_id"]'); + if (tplInput) { + tplInput.value = String(result.selected_template_id); + } + + var typeInput = document.getElementById('template_type_input'); + var currentType = typeInput ? String(typeInput.value || 'letterhead') : 'letterhead'; + if (currentType === 'plain') { + var plainInput = document.getElementById('compile_plain_template_id'); + if (plainInput) { + plainInput.value = String(result.selected_template_id); + } + } else { + var letterInput = document.getElementById('compile_letterhead_template_id'); + if (letterInput) { + letterInput.value = String(result.selected_template_id); + } + } + + var templateTypeSelector = document.getElementById('template_type_selector'); + if (templateTypeSelector) { + templateTypeSelector.value = currentType; + } + } + if (result && result.csrfName && result.csrfHash) { var csrfInput = form.querySelector('input[name="' + result.csrfName + '"]'); if (csrfInput) { @@ -358,5 +446,16 @@ $panelTitle = $modalityLabel . ' Print Template'; }); }); } + + var templateTypeSelector = document.getElementById('template_type_selector'); + if (templateTypeSelector) { + templateTypeSelector.addEventListener('change', function () { + var typeInput = document.getElementById('template_type_input'); + if (typeInput) { + typeInput.value = this.value; + } + window.loadDiagnosisTemplateType(this.value); + }); + } })(); diff --git a/app/Views/Setting/Template/index.php b/app/Views/Setting/Template/index.php index 8ae4154..e5e925c 100644 --- a/app/Views/Setting/Template/index.php +++ b/app/Views/Setting/Template/index.php @@ -202,7 +202,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + OPD Print Template @@ -211,7 +211,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + IPD Discharge Template @@ -220,7 +220,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + IPD Document Master @@ -230,7 +230,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + Pathology Print Template @@ -238,7 +238,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + Diagnosis Print Template @@ -247,7 +247,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
- + Document Print Template diff --git a/app/Views/diagnosis/pathology_detail.php b/app/Views/diagnosis/pathology_detail.php index ea3e4c9..2502504 100644 --- a/app/Views/diagnosis/pathology_detail.php +++ b/app/Views/diagnosis/pathology_detail.php @@ -205,6 +205,7 @@ if ($ageLabel === '') {
+
@@ -1252,6 +1253,7 @@ function compileReport() { .then(result => { if (result.status === 'success') { alert(result.message || 'Data Compile'); + renderCompiledPdfButtons(result); refreshTestList(); refreshLabDateShow(); } else { @@ -1264,6 +1266,75 @@ function compileReport() { }); } +function renderCompiledPdfButtons(result) { + const container = document.getElementById('compiledPdfButtons'); + if (!container) { + return; + } + + const urls = (result && result.pdf_file_urls && typeof result.pdf_file_urls === 'object') ? result.pdf_file_urls : {}; + const labels = (result && result.pdf_file_labels && typeof result.pdf_file_labels === 'object') ? result.pdf_file_labels : {}; + const letterUrl = urls.letterhead || ''; + const plainUrl = urls.plain || ''; + const letterLabel = labels.letterhead || 'Default Template'; + const plainLabel = labels.plain || 'Default Template'; + + if (!letterUrl && !plainUrl) { + container.style.display = 'none'; + container.innerHTML = ''; + return; + } + + let html = '
Compiled PDFs: '; + if (letterUrl) { + html += ' Letter Head - ' + escapeHtml(letterLabel) + ''; + } + if (plainUrl) { + html += ' Plain Paper - ' + escapeHtml(plainLabel) + ''; + } + html += '
'; + + container.innerHTML = html; + container.style.display = ''; +} + +function escapeHtml(value) { + return String(value || '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function loadCompiledPdfButtons() { + const invoiceInput = document.getElementById('invoiceId'); + if (!invoiceInput) { + return; + } + + const invoiceId = Number(invoiceInput.value || 0); + if (!invoiceId || !labType) { + return; + } + + fetch(baseUrl + 'diagnosis/compiled-pdf-links/' + invoiceId + '/' + labType, { + method: 'GET', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }) + .then(response => response.json()) + .then(result => { + if (result && result.status === 'success') { + renderCompiledPdfButtons(result); + } + }) + .catch(error => { + console.error('loadCompiledPdfButtons error:', error); + }); +} + function generateReportByTemplate(templateId) { const tplId = Number(templateId || 0); const invoiceId = document.getElementById('invoiceId').value; @@ -1292,4 +1363,5 @@ function showReports() { console.log('Loading data for invoice:', invoiceId, 'labType:', labType); refreshTestList(); refreshLabDateShow(); +loadCompiledPdfButtons();