fix(abdm,diagnosis): use plain paper template for radiology fhir pdf, fix modality title and strip conclusion html

This commit is contained in:
DevSoftTech Server 2026-10-04 12:38:44 +05:30
parent 414dc42c05
commit 6270b0517a
2 changed files with 239 additions and 69 deletions

View File

@ -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 .= '<div class="share-conclusion"><strong>Conclusion:</strong> ' . $escape($conclusion) . '</div>';
}
@ -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 => '',
};
}

View File

@ -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 '<h3>Invalid request id</h3>';
}
$pdfBytes = $this->generateSingleReportPdfBytes($labReqId, $templateId);
if ($pdfBytes === null) {
return '<h3>Report not found</h3>';
}
$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 '<h3>Report not found</h3>';
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';
}