diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index ec3d188..272c657 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -2017,9 +2017,15 @@ class AbdmGateway extends BaseController return $this->response->setJSON(['ok' => 0, 'error' => 'lab_req_id and patient_id are required']); } - // -- Load lab request -------------------------------------------------- + $selectCols = ['id', 'patient_name', 'lab_type', 'charge_id', 'report_name', 'Report_Data', 'report_data_Impression', 'status', 'reported_time']; + if ($this->db->fieldExists('charge_item_id', 'lab_request')) { + $selectCols[] = 'charge_item_id'; + } + if ($this->db->fieldExists('lab_repo_id', 'lab_request')) { + $selectCols[] = 'lab_repo_id'; + } $labReq = $this->db->table('lab_request') - ->select('id, patient_name, lab_type, charge_id, Report_Data, report_data_Impression, status, reported_time') + ->select(implode(', ', $selectCols)) ->where('id', $labReqId) ->get(1) ->getRow(); @@ -2056,18 +2062,25 @@ class AbdmGateway extends BaseController $patientBirthYear = $this->resolvePatientBirthYear($patientRow, str_contains($abhaId, '@') ? $abhaId : '', str_contains($abhaId, '@') ? '' : $abhaId); // -- Load test / charge name -------------------------------------------- - $testTitle = ''; + $testTitle = trim((string) ($labReq->report_name ?? '')); $labType = (int) ($labReq->lab_type ?? 0); $isImaging = in_array($labType, [1, 2, 3, 4, 6], true); - // Check lab_repo first + // Check lab_repo if testTitle empty $labRepoId = (int) ($labReq->lab_repo_id ?? 0); - if ($labRepoId > 0 && $this->db->tableExists('lab_repo')) { + if ($testTitle === '' && $labRepoId > 0 && $this->db->tableExists('lab_repo')) { $repoRow = $this->db->table('lab_repo')->select('Repo, RepoName')->where('mstRepoKey', $labRepoId)->get(1)->getRowArray() ?? []; $testTitle = trim((string) ($repoRow['RepoName'] ?? $repoRow['Repo'] ?? '')); } - // Fallback: check charge_id in invoice_item or item_master + // Check charge_item_id in invoice_item + $chargeItemId = (int) ($labReq->charge_item_id ?? 0); + if ($testTitle === '' && $chargeItemId > 0 && $this->db->tableExists('invoice_item')) { + $invItem = $this->db->table('invoice_item')->select('item_name')->where('id', $chargeItemId)->get(1)->getRowArray() ?? []; + $testTitle = trim((string) ($invItem['item_name'] ?? '')); + } + + // Fallback: check charge_id in invoice_item, item_master or charge_master $chargeId = (int) ($labReq->charge_id ?? 0); if ($testTitle === '' && $chargeId > 0) { if ($this->db->tableExists('invoice_item')) { @@ -2078,10 +2091,14 @@ class AbdmGateway extends BaseController $itRow = $this->db->table('item_master')->select('item_name')->where('id', $chargeId)->get(1)->getRowArray() ?? []; $testTitle = trim((string) ($itRow['item_name'] ?? '')); } + if ($testTitle === '' && $this->db->tableExists('charge_master')) { + $cmRow = $this->db->table('charge_master')->select('charge_name')->where('id', $chargeId)->get(1)->getRowArray() ?? []; + $testTitle = trim((string) ($cmRow['charge_name'] ?? '')); + } } if ($testTitle === '') { - $testTitle = $isImaging ? 'Radiology Report' : $this->mapLabTypeToTitle($labType); + $testTitle = $this->mapLabTypeToTitle($labType) ?: ($isImaging ? 'Radiology Report' : 'Laboratory Report'); } // -- Load hospital profile --------------------------------------------- @@ -2103,18 +2120,23 @@ class AbdmGateway extends BaseController 'abhaAddress' => $abhaId, ]; + $rawImpression = trim((string) ($labReq->report_data_Impression ?? '')); + $cleanImpression = trim(html_entity_decode(strip_tags($rawImpression), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); + $diagnosticReport = [ 'id' => (string) $labReqId, 'title' => $testTitle ?: ($isImaging ? 'Radiology Report' : 'Laboratory Report'), 'status' => $labReq->status == 1 ? 'final' : 'preliminary', - 'conclusion' => trim((string) ($labReq->report_data_Impression ?? '')), + 'conclusion' => $cleanImpression, 'reported_at' => $reportedAt, 'report_html' => trim((string) ($labReq->Report_Data ?? '')), + 'lab_type' => $labType, ]; if ($isImaging) { $diagnosticReport['is_imaging'] = true; $diagnosticReport['report_domain'] = 'imaging'; - $diagnosticReport['section_title'] = 'Diagnostic imaging report'; + $modalityName = $this->mapLabTypeToTitle($labType) ?: 'Diagnostic imaging'; + $diagnosticReport['section_title'] = $modalityName . ' report'; $diagnosticReport['section_snomed_code'] = '371531008'; $diagnosticReport['section_snomed_display'] = 'Diagnostic imaging report'; } @@ -2292,8 +2314,15 @@ class AbdmGateway extends BaseController ]); } + $selectCols = ['id', 'patient_name', 'lab_type', 'charge_id', 'report_name', 'Report_Data', 'report_data_Impression', 'status', 'reported_time']; + if ($this->db->fieldExists('charge_item_id', 'lab_request')) { + $selectCols[] = 'charge_item_id'; + } + if ($this->db->fieldExists('lab_repo_id', 'lab_request')) { + $selectCols[] = 'lab_repo_id'; + } $labReq = $this->db->table('lab_request') - ->select('id, patient_name, lab_type, charge_id, Report_Data, report_data_Impression, status, reported_time') + ->select(implode(', ', $selectCols)) ->where('id', $labReqId) ->get(1) ->getRow(); @@ -2314,14 +2343,33 @@ class AbdmGateway extends BaseController $patName = trim((string) ($labReq->patient_name ?? '')); } - $testTitle = ''; - $chargeId = (int) ($labReq->charge_id ?? 0); - if ($chargeId > 0 && $this->db->tableExists('charge_master')) { - $chargeRow = $this->db->table('charge_master')->select('charge_name')->where('id', $chargeId)->get(1)->getRowArray() ?? []; - $testTitle = trim((string) ($chargeRow['charge_name'] ?? '')); + $testTitle = trim((string) ($labReq->report_name ?? '')); + $labType = (int) ($labReq->lab_type ?? 0); + $isImaging = in_array($labType, [1, 2, 3, 4, 6], true); + + if ($testTitle === '') { + $labRepoId = (int) ($labReq->lab_repo_id ?? 0); + if ($labRepoId > 0 && $this->db->tableExists('lab_repo')) { + $repoRow = $this->db->table('lab_repo')->select('Repo, RepoName')->where('mstRepoKey', $labRepoId)->get(1)->getRowArray() ?? []; + $testTitle = trim((string) ($repoRow['RepoName'] ?? $repoRow['Repo'] ?? '')); + } } if ($testTitle === '') { - $testTitle = $this->mapLabTypeToTitle((int) ($labReq->lab_type ?? 0)); + $chargeItemId = (int) ($labReq->charge_item_id ?? 0); + if ($chargeItemId > 0 && $this->db->tableExists('invoice_item')) { + $invItem = $this->db->table('invoice_item')->select('item_name')->where('id', $chargeItemId)->get(1)->getRowArray() ?? []; + $testTitle = trim((string) ($invItem['item_name'] ?? '')); + } + } + if ($testTitle === '') { + $chargeId = (int) ($labReq->charge_id ?? 0); + if ($chargeId > 0 && $this->db->tableExists('charge_master')) { + $chargeRow = $this->db->table('charge_master')->select('charge_name')->where('id', $chargeId)->get(1)->getRowArray() ?? []; + $testTitle = trim((string) ($chargeRow['charge_name'] ?? '')); + } + } + if ($testTitle === '') { + $testTitle = $this->mapLabTypeToTitle($labType) ?: ($isImaging ? 'Radiology Report' : 'Laboratory Report'); } $hospitalProfile = $this->getHospitalProfileForFhir(); @@ -2329,21 +2377,25 @@ class AbdmGateway extends BaseController $reportedRaw = trim((string) ($labReq->reported_time ?? '')); $reportedAt = $reportedRaw !== '' ? (new \DateTime($reportedRaw, new \DateTimeZone('Asia/Kolkata')))->format('Y-m-d\TH:i:sP') : ''; + $rawImpression = trim((string) ($labReq->report_data_Impression ?? '')); + $cleanImpression = trim(html_entity_decode(strip_tags($rawImpression), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); + $diagnosticReport = [ 'id' => (string) $labReqId, - 'title' => $testTitle ?: 'Laboratory Report', + 'title' => $testTitle ?: ($isImaging ? 'Radiology Report' : 'Laboratory Report'), 'status' => ((string) ($labReq->status ?? '0')) === '1' ? 'final' : 'preliminary', - 'conclusion' => trim((string) ($labReq->report_data_Impression ?? '')), + 'conclusion' => $cleanImpression, 'reported_at' => $reportedAt, 'report_html' => trim((string) ($labReq->Report_Data ?? '')), + 'lab_type' => $labType, ]; - $isImaging = (int) ($labReq->lab_type ?? 0) === 6; if ($isImaging) { $diagnosticReport['is_imaging'] = true; $diagnosticReport['report_domain'] = 'imaging'; - $diagnosticReport['section_title'] = 'Computed tomography imaging report'; + $modalityName = $this->mapLabTypeToTitle($labType) ?: 'Diagnostic imaging'; + $diagnosticReport['section_title'] = $modalityName . ' report'; $diagnosticReport['section_snomed_code'] = '371531008'; - $diagnosticReport['section_snomed_display'] = 'Computed tomography imaging report'; + $diagnosticReport['section_snomed_display'] = 'Diagnostic imaging report'; } $labRepoRow = $this->db->table('lab_request lr') @@ -6757,6 +6809,20 @@ class AbdmGateway extends BaseController array $hospitalProfile, int $labReqId ): ?array { + // Option 1: If Plain Paper template is available in diagnosis_print_templates, prefer it + $labType = (int) ($diagnosticReport['lab_type'] ?? 0); + $plainPdf = $this->resolvePlainTemplatePdfBytes($labReqId, $labType); + if ($plainPdf !== null) { + return [ + 'content_type' => 'application/pdf', + 'data_base64' => base64_encode($plainPdf), + 'title' => 'Digital-Share-LAB-' . $labReqId . '.pdf', + 'size' => strlen($plainPdf), + 'hash' => base64_encode(sha1($plainPdf, true)), + ]; + } + + // Option 2: Fallback to digital share template with cleaned text and proper title $escape = static fn (string $value): string => htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $contentHtml = trim((string) ($diagnosticReport['report_html'] ?? '')); if ($contentHtml === '' && $observations !== []) { @@ -6780,7 +6846,8 @@ class AbdmGateway extends BaseController return null; } - $conclusion = trim((string) ($diagnosticReport['conclusion'] ?? '')); + $rawConclusion = trim((string) ($diagnosticReport['conclusion'] ?? '')); + $conclusion = trim(html_entity_decode(strip_tags($rawConclusion), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); if ($conclusion !== '') { $contentHtml .= '
Conclusion: ' . $escape($conclusion) . '
'; } @@ -6803,6 +6870,60 @@ class AbdmGateway extends BaseController ]); } + /** + * Attempt to render PDF using Plain Paper template from diagnosis_print_templates if available. + */ + private function resolvePlainTemplatePdfBytes(int $labReqId, int $labType = 0): ?string + { + if ($labReqId <= 0) { + return null; + } + + try { + if ($labType <= 0 && $this->db->tableExists('lab_request')) { + $lr = $this->db->table('lab_request')->select('lab_type')->where('id', $labReqId)->get(1)->getRowArray(); + $labType = (int) ($lr['lab_type'] ?? 0); + } + + $plainTemplateId = 0; + if ($labType > 0 && $this->db->tableExists('diagnosis_print_templates')) { + $plainRow = $this->db->table('diagnosis_print_templates') + ->select('id') + ->where('modality', $labType) + ->where('status', 1) + ->like('template_name', 'plain') + ->orderBy('is_default', 'DESC') + ->orderBy('id', 'ASC') + ->get(1) + ->getRowArray(); + + if (! empty($plainRow['id'])) { + $plainTemplateId = (int) $plainRow['id']; + } + } + + if ($plainTemplateId <= 0) { + return null; + } + + $diag = new \App\Controllers\Diagnosis(); + $request = \Config\Services::request(); + $response = \Config\Services::response(); + $logger = \Config\Services::logger(); + $diag->initController($request, $response, $logger); + + $pdfBytes = $diag->generateSingleReportPdfBytes($labReqId, $plainTemplateId); + if (is_string($pdfBytes) && str_starts_with($pdfBytes, '%PDF-')) { + return $pdfBytes; + } + + return null; + } catch (\Throwable $e) { + log_message('error', 'resolvePlainTemplatePdfBytes error: ' . $e->getMessage()); + return null; + } + } + /** * Render the stable PDF layout used for digital/PHR sharing. * @@ -8482,19 +8603,17 @@ class AbdmGateway extends BaseController } /** - * Map numeric lab_type code to a human-readable title. + * Map numeric lab_type / modality code to a human-readable title. */ private function mapLabTypeToTitle(int $labType): string { return match ($labType) { - 1 => 'Haematology', - 2 => 'Biochemistry', - 3 => 'Serology', - 4 => 'Microbiology', - 5 => 'Pathology / Cytology', - 6 => 'Radiology', - 7 => 'Urology', - 8 => 'Molecular Diagnostics', + 1 => 'Ultrasound', + 2 => 'MRI', + 3 => 'X-Ray', + 4 => 'CT-Scan', + 5 => 'Pathology', + 6 => 'ECHO', default => '', }; } diff --git a/app/Controllers/Diagnosis.php b/app/Controllers/Diagnosis.php index 4a92abf..6e8ea2d 100644 --- a/app/Controllers/Diagnosis.php +++ b/app/Controllers/Diagnosis.php @@ -1451,12 +1451,45 @@ class Diagnosis extends BaseController $labReqId = (int) $labReqId; $templateId = (int) ($this->request->getGet('template_id') ?? 0); - set_time_limit(300); + set_time_limit(300); if ($labReqId <= 0) { return '

Invalid request id

'; } + $pdfBytes = $this->generateSingleReportPdfBytes($labReqId, $templateId); + if ($pdfBytes === null) { + return '

Report not found

'; + } + + $fileName = 'Report_' . $labReqId . '.pdf'; + + return $this->response + ->setHeader('Content-Type', 'application/pdf') + ->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + ->setHeader('Pragma', 'no-cache') + ->setHeader('Expires', '0') + ->setHeader('Content-Disposition', 'inline; filename="' . $fileName . '"') + ->setBody($pdfBytes); + } + + /** + * Generate single diagnosis/radiology report PDF binary bytes. + * + * @param int $labReqId lab_request ID + * @param int $templateId specific template ID (or 0 for automatic/default) + * @param bool $preferPlain when true and templateId is 0, prefer Plain Paper template if available + * @return string|null binary PDF data or null if not found/error + */ + public function generateSingleReportPdfBytes(int $labReqId, int $templateId = 0, bool $preferPlain = false): ?string + { + $labReqId = (int) $labReqId; + if ($labReqId <= 0) { + return null; + } + + set_time_limit(300); + $sql = "SELECT l.*, m.invoice_code, m.attach_id, p.p_fname, p.p_rname, p.p_relative, p.p_code, p.gender, p.dob, p.age, p.age_in, p.age_in_month, p.estimate_dob, p.mphone1, p.mphone2, p.add1, p.add2, p.city, p.district, p.state, @@ -1471,7 +1504,7 @@ class Diagnosis extends BaseController $row = $this->db->query($sql, [$labReqId])->getRow(); if (! $row) { - return '

Report not found

'; + return null; } $reportData = trim((string) ($row->Report_Data ?? '')); @@ -1509,8 +1542,24 @@ class Diagnosis extends BaseController $ageText = (string) $row->age; } - $printSetting = $this->getDiagnosisTemplateSetting((int) ($row->lab_type ?? 0), $templateId); - $isPlainPaper = false; + $labType = (int) ($row->lab_type ?? 0); + if ($templateId <= 0 && $preferPlain && $labType > 0 && $this->db->tableExists('diagnosis_print_templates')) { + $plainRow = $this->db->table('diagnosis_print_templates') + ->select('id') + ->where('modality', $labType) + ->where('status', 1) + ->like('template_name', 'plain') + ->orderBy('is_default', 'DESC') + ->orderBy('id', 'ASC') + ->get(1) + ->getRowArray(); + if (! empty($plainRow['id'])) { + $templateId = (int) $plainRow['id']; + } + } + + $printSetting = $this->getDiagnosisTemplateSetting($labType, $templateId); + $isPlainPaper = stripos((string) ($printSetting['template_name'] ?? ''), 'plain') !== false; // Compute display values here so they are available for token substitution. $patientName = trim((string) ($row->p_fname ?? '')); @@ -1518,11 +1567,11 @@ class Diagnosis extends BaseController $relativeText = trim((string) ($row->p_rname ?? '')); $repoTitle = trim((string) ($row->report_name ?? $row->repo_title ?? $row->RepoGrp ?? 'Report')); $headRow = $this->db->table('diagnosis_head_name') - ->where('d_type', (int) ($row->lab_type ?? 0)) + ->where('d_type', $labType) ->get(1) ->getRow(); - $doctorTokenData = $this->resolveMappedReportDoctorTokenData((int) ($row->lab_type ?? 0), $headRow); + $doctorTokenData = $this->resolveMappedReportDoctorTokenData($labType, $headRow); $patientAddress = trim(($row->add1 ?? '') . ' ' . ($row->add2 ?? '') . ' ' . ($row->city ?? '')); $patientPhone = trim((string) ($row->mphone1 ?? $row->mphone2 ?? '')); @@ -1552,23 +1601,23 @@ class Diagnosis extends BaseController // Render the optional patient info template (tokens resolved). $rawPatientInfoHtml = trim((string) ($printSetting['patient_info_html'] ?? '')); - if ($rawPatientInfoHtml !== '') { - $patientInfoHtml = $this->applyPdfTokens($rawPatientInfoHtml, $tokens); - } elseif (($printSetting['id'] ?? 0) <= 0) { - $patientInfoHtml = $this->buildDefaultPatientInfoHtml([ - 'invoice_code' => $row->invoice_code ?? '', - 'patient_name' => $patientName, - 'relative_name' => $relativeName, - 'relative_text' => $relativeText, - 'age_text' => $ageText, - 'gender_text' => $genderText, - 'uhid' => $row->p_code ?? '', - 'collected_time' => $row->collected_time ?? '', - 'reported_time' => $row->reported_time ?? '', - ]); - } else { - $patientInfoHtml = ''; - } + if ($rawPatientInfoHtml !== '') { + $patientInfoHtml = $this->applyPdfTokens($rawPatientInfoHtml, $tokens); + } elseif (($printSetting['id'] ?? 0) <= 0) { + $patientInfoHtml = $this->buildDefaultPatientInfoHtml([ + 'invoice_code' => $row->invoice_code ?? '', + 'patient_name' => $patientName, + 'relative_name' => $relativeName, + 'relative_text' => $relativeText, + 'age_text' => $ageText, + 'gender_text' => $genderText, + 'uhid' => $row->p_code ?? '', + 'collected_time' => $row->collected_time ?? '', + 'reported_time' => $row->reported_time ?? '', + ]); + } else { + $patientInfoHtml = ''; + } $reportHtml = trim((string) ($printSetting['content_prefix_html'] ?? '')) . $reportHtml . trim((string) ($printSetting['content_suffix_html'] ?? '')); @@ -1607,9 +1656,11 @@ class Diagnosis extends BaseController . $autoFooterHtml, $tokens); $suffixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_suffix_html'] ?? '')), $tokens); $pdfHtml = $this->applyPdfTokens($pdfHtml, $tokens); - $pdfHtml = mpdf_normalize_font_weight_css($pdfHtml); - $prefixHtml = mpdf_normalize_font_weight_css($prefixHtml); - $suffixHtml = mpdf_normalize_font_weight_css($suffixHtml); + if (function_exists('mpdf_normalize_font_weight_css')) { + $pdfHtml = mpdf_normalize_font_weight_css($pdfHtml); + $prefixHtml = mpdf_normalize_font_weight_css($prefixHtml); + $suffixHtml = mpdf_normalize_font_weight_css($suffixHtml); + } $marginTop = $this->cmToMm($printSetting['page_margin_top_cm'] ?? 1.2, 1.2); $marginBottom = $this->cmToMm($printSetting['page_margin_bottom_cm'] ?? 1.2, 1.2); @@ -1620,7 +1671,7 @@ class Diagnosis extends BaseController $mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf'; if (! is_dir($mpdfTempDir)) { - mkdir($mpdfTempDir, 0755, true); + @mkdir($mpdfTempDir, 0755, true); } $mpdf = new Mpdf([ @@ -1644,7 +1695,9 @@ class Diagnosis extends BaseController if (! $headerHasTags) { $plainHeader = $this->applyPdfTokens($rawHeaderHtml . $rawFirstPageHeaderHtml, $tokens); - $plainHeader = mpdf_normalize_font_weight_css($plainHeader); + if (function_exists('mpdf_normalize_font_weight_css')) { + $plainHeader = mpdf_normalize_font_weight_css($plainHeader); + } if ($plainHeader !== '') { $mpdf->SetHTMLHeader($plainHeader, 'O'); $mpdf->SetHTMLHeader($plainHeader, 'E'); @@ -1656,7 +1709,9 @@ class Diagnosis extends BaseController if ($plainFooter === '') { $plainFooter = $this->applyPdfTokens($rawLastPageFooterHtml, $tokens); } - $plainFooter = mpdf_normalize_font_weight_css($plainFooter); + if (function_exists('mpdf_normalize_font_weight_css')) { + $plainFooter = mpdf_normalize_font_weight_css($plainFooter); + } if ($plainFooter !== '') { $mpdf->SetHTMLFooter($plainFooter, 'O'); @@ -1705,13 +1760,7 @@ class Diagnosis extends BaseController $invoiceCode = preg_replace('/[^A-Za-z0-9\-_]/', '_', (string) ($row->invoice_code ?? 'invoice')); $fileName = 'Report_' . $invoiceCode . '_' . $labReqId . '.pdf'; - return $this->response - ->setHeader('Content-Type', 'application/pdf') - ->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') - ->setHeader('Pragma', 'no-cache') - ->setHeader('Expires', '0') - ->setHeader('Content-Disposition', 'inline; filename="' . $fileName . '"') - ->setBody($mpdf->Output($fileName, 'S')); + return $mpdf->Output($fileName, 'S'); } private function getDiagnosisPrintTemplates(int $labType): array @@ -1737,7 +1786,8 @@ class Diagnosis extends BaseController $defaults = array_merge($legacy, [ 'page_size' => 'A4', 'page_margin_top_cm' => 6.1, - 'id' => 0, + 'id' => 0, + 'template_name' => '', 'page_margin_bottom_cm' => 2.5, 'page_margin_left_cm' => 0.7, 'page_margin_right_cm' => 0.7, @@ -1808,7 +1858,8 @@ class Diagnosis extends BaseController $pageSize = strtoupper(trim((string) ($defaults['page_size'] ?? 'A4'))); - $defaults['id'] = (int) ($row['id'] ?? 0); + $defaults['id'] = (int) ($row['id'] ?? 0); + $defaults['template_name'] = (string) ($row['template_name'] ?? ''); if (! in_array($pageSize, ['A4', 'A4-L', 'LETTER', 'LEGAL'], true)) { $pageSize = 'A4'; }