feat(diagnosis): add print templates with image upload and optimize PDF rendering
This commit is contained in:
parent
68dfbaf006
commit
1dee940ac0
|
|
@ -5,6 +5,8 @@ namespace App\Controllers;
|
|||
use App\Controllers\BaseController;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use Exception;
|
||||
use Mpdf\HTMLParserMode;
|
||||
use Mpdf\Mpdf;
|
||||
|
||||
class Diagnosis extends BaseController
|
||||
{
|
||||
|
|
@ -341,6 +343,7 @@ class Diagnosis extends BaseController
|
|||
'lab_type' => $labType,
|
||||
'lab_type_name' => $this->getLabTypeName($labType),
|
||||
'lab_type_route' => $this->getLabTypeRoute($labType),
|
||||
'print_templates' => $this->getDiagnosisPrintTemplates($labType),
|
||||
];
|
||||
|
||||
return view('diagnosis/pathology_detail', $data);
|
||||
|
|
@ -421,6 +424,7 @@ class Diagnosis extends BaseController
|
|||
'invoiceId' => $invoiceId,
|
||||
'labType' => $labType,
|
||||
'lab_type' => $labType,
|
||||
'print_templates' => $this->getDiagnosisPrintTemplates($labType),
|
||||
];
|
||||
|
||||
$view = view('diagnosis/lab_invoice_main_test_list', $data);
|
||||
|
|
@ -1046,6 +1050,7 @@ class Diagnosis extends BaseController
|
|||
public function openReportEditorPage($labReqId)
|
||||
{
|
||||
$labReqId = (int) $labReqId;
|
||||
$editReason = trim((string) ($this->request->getGet('edit_reason') ?? ''));
|
||||
|
||||
$reportRow = $this->db->table('lab_request')->where('id', $labReqId)->get()->getRow();
|
||||
if (! $reportRow) {
|
||||
|
|
@ -1111,6 +1116,7 @@ class Diagnosis extends BaseController
|
|||
return view('diagnosis/lab_final_report_editor', [
|
||||
'report_format' => [$reportRow],
|
||||
'radiology_ultrasound_template' => $templates,
|
||||
'edit_reason' => $editReason,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -1139,14 +1145,28 @@ class Diagnosis extends BaseController
|
|||
public function finalUpdateXray($labReqId)
|
||||
{
|
||||
$labReqId = (int) $labReqId;
|
||||
$htmlData = (string) $this->request->getPost('HTMLData');
|
||||
$impression = (string) $this->request->getPost('report_data_Impression');
|
||||
$htmlData = (string) ($this->request->getPost('HTMLData') ?? $this->request->getPost('report_data') ?? '');
|
||||
$impression = (string) ($this->request->getPost('report_data_Impression') ?? $this->request->getPost('report_data_impression') ?? '');
|
||||
$editReason = trim((string) ($this->request->getPost('edit_reason') ?? ''));
|
||||
|
||||
if ($labReqId <= 0) {
|
||||
return $this->response->setJSON(['status' => 'error', 'message' => 'Invalid request id']);
|
||||
}
|
||||
|
||||
try {
|
||||
$before = $this->db->table('lab_request')
|
||||
->select('id, status, report_edit_req_no, Report_Data, report_data_Impression')
|
||||
->where('id', $labReqId)
|
||||
->get(1)
|
||||
->getRowArray() ?? [];
|
||||
|
||||
if ((int) ($before['status'] ?? 0) === 2 && $editReason === '') {
|
||||
return $this->response->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Edit reason is required for verified report changes (NABH audit).',
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->table('lab_request')
|
||||
->where('id', $labReqId)
|
||||
->update([
|
||||
|
|
@ -1154,6 +1174,35 @@ class Diagnosis extends BaseController
|
|||
'report_data_Impression' => $impression,
|
||||
]);
|
||||
|
||||
$fields = $this->db->getFieldNames('lab_request');
|
||||
if (in_array('report_edit_req_no', $fields, true) && (int) ($before['status'] ?? 0) === 2) {
|
||||
$nextEditNo = (int) ($before['report_edit_req_no'] ?? 0) + 1;
|
||||
$this->db->table('lab_request')
|
||||
->where('id', $labReqId)
|
||||
->update(['report_edit_req_no' => $nextEditNo]);
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('lab_log')) {
|
||||
$user = service('auth')->user();
|
||||
$userId = (int) ($user->id ?? 0);
|
||||
$userName = $user ? trim(($user->first_name ?? '') . ' ' . ($user->last_name ?? '')) : 'System';
|
||||
|
||||
$beforeHash = md5((string) ($before['Report_Data'] ?? '') . '|' . (string) ($before['report_data_Impression'] ?? ''));
|
||||
$afterHash = md5($htmlData . '|' . $impression);
|
||||
$reasonLog = $editReason !== '' ? $editReason : 'NA';
|
||||
|
||||
$this->db->table('lab_log')->insert([
|
||||
'lab_repo_id' => $labReqId,
|
||||
'log_by_id' => $userId,
|
||||
'log_by' => $userName,
|
||||
'log_type_id' => 0,
|
||||
'log_type' => 'Report Edit',
|
||||
'log_Faults_id' => 0,
|
||||
'log_Faults' => 'Radiology',
|
||||
'comments' => 'Editor save [reason:' . substr($reasonLog, 0, 350) . '] [before:' . substr($beforeHash, 0, 8) . ' after:' . substr($afterHash, 0, 8) . ']',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->response->setJSON(['status' => 'success', 'message' => 'Saved']);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setJSON(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
|
|
@ -1212,23 +1261,624 @@ class Diagnosis extends BaseController
|
|||
public function printSingleReport($labReqId)
|
||||
{
|
||||
$labReqId = (int) $labReqId;
|
||||
$templateId = (int) ($this->request->getGet('template_id') ?? 0);
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
if ($labReqId <= 0) {
|
||||
return '<h3>Invalid request id</h3>';
|
||||
}
|
||||
|
||||
$row = $this->db->table('lab_request')
|
||||
->where('id', $labReqId)
|
||||
->get()
|
||||
->getRow();
|
||||
$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,
|
||||
r.Title as repo_title, g.RepoGrp
|
||||
FROM lab_request l
|
||||
LEFT JOIN invoice_master m ON m.id = l.charge_id
|
||||
LEFT JOIN patient_master p ON p.id = m.attach_id
|
||||
LEFT JOIN lab_repo r ON r.mstRepoKey = l.lab_repo_id
|
||||
LEFT JOIN lab_rgroups g ON g.mstRGrpKey = r.GrpKey
|
||||
WHERE l.id = ?";
|
||||
|
||||
$row = $this->db->query($sql, [$labReqId])->getRow();
|
||||
|
||||
if (! $row) {
|
||||
return '<h3>Report not found</h3>';
|
||||
}
|
||||
|
||||
return view('diagnosis/print_single_report', [
|
||||
'report' => $row,
|
||||
$reportData = trim((string) ($row->Report_Data ?? ''));
|
||||
$impression = trim((string) ($row->report_data_Impression ?? ''));
|
||||
|
||||
$reportHtml = '';
|
||||
if ($reportData !== '') {
|
||||
$reportHtml .= $reportData;
|
||||
}
|
||||
|
||||
if ($impression !== '') {
|
||||
$reportHtml .= '<div style="margin-top:10px;"><b>Impression :</b><br/>' . nl2br($impression) . '</div>';
|
||||
}
|
||||
|
||||
if ($reportHtml === '') {
|
||||
$reportHtml = $this->resolveFallbackReportHtml((int) ($row->lab_repo_id ?? 0), (int) ($row->charge_item_id ?? 0), (int) ($row->lab_type ?? 0));
|
||||
}
|
||||
|
||||
if ($reportHtml === '') {
|
||||
$reportHtml = '<p>No report data available for this request. Please use Edit and save report content.</p>';
|
||||
}
|
||||
|
||||
$genderValue = (int) ($row->gender ?? 0);
|
||||
$genderText = $genderValue === 1 ? 'Male' : ($genderValue === 2 ? 'Female' : '-');
|
||||
|
||||
$ageText = '-';
|
||||
if (function_exists('get_age_1')) {
|
||||
$ageText = (string) get_age_1(
|
||||
$row->dob ?? null,
|
||||
$row->age ?? '',
|
||||
$row->age_in_month ?? '',
|
||||
$row->estimate_dob ?? ''
|
||||
);
|
||||
} elseif (! empty($row->age)) {
|
||||
$ageText = (string) $row->age;
|
||||
}
|
||||
|
||||
$printSetting = $this->getDiagnosisTemplateSetting((int) ($row->lab_type ?? 0), $templateId);
|
||||
$isPlainPaper = false;
|
||||
|
||||
// Compute display values here so they are available for token substitution.
|
||||
$patientName = trim((string) ($row->p_fname ?? ''));
|
||||
$relativeName = trim((string) ($row->p_relative ?? ''));
|
||||
$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))
|
||||
->get(1)
|
||||
->getRow();
|
||||
|
||||
$tokens = $this->buildPdfTokens([
|
||||
'invoice_code' => $row->invoice_code ?? '',
|
||||
'patient_name' => $patientName,
|
||||
'relative_type' => $relativeName,
|
||||
'relative_name' => $relativeText,
|
||||
'age' => $ageText,
|
||||
'gender' => $genderText,
|
||||
'uhid' => $row->p_code ?? '',
|
||||
'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 ?? '',
|
||||
'signature_image_url' => (string) ($printSetting['signature_image'] ?? ''),
|
||||
]);
|
||||
|
||||
// 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 = '';
|
||||
}
|
||||
|
||||
$reportHtml = trim((string) ($printSetting['content_prefix_html'] ?? '')) . $reportHtml . trim((string) ($printSetting['content_suffix_html'] ?? ''));
|
||||
|
||||
$pdfHtml = view('diagnosis/pdf_single_report', [
|
||||
'report' => $row,
|
||||
'reportHtml' => $reportHtml,
|
||||
'isPlainPaper' => $isPlainPaper,
|
||||
'ageText' => $ageText,
|
||||
'genderText' => $genderText,
|
||||
'printSetting' => $printSetting,
|
||||
'patientName' => $patientName,
|
||||
'relativeName' => $relativeName,
|
||||
'relativeText' => $relativeText,
|
||||
'repoTitle' => $repoTitle,
|
||||
'patientInfoHtml' => $patientInfoHtml,
|
||||
]);
|
||||
|
||||
$rawHeaderHtml = trim((string) ($printSetting['header_html'] ?? ''));
|
||||
$rawFirstPageHeaderHtml = trim((string) ($printSetting['first_page_header_html'] ?? ''));
|
||||
$rawFooterHtml = trim((string) ($printSetting['footer_html'] ?? ''));
|
||||
$rawLastPageFooterHtml = trim((string) ($printSetting['last_page_footer_html'] ?? ''));
|
||||
|
||||
$autoHeaderHtml = $this->buildAutoMpdfHeaderBlock(
|
||||
$rawHeaderHtml,
|
||||
$rawFirstPageHeaderHtml,
|
||||
'diag_auto_header_single'
|
||||
);
|
||||
$autoFooterHtml = $this->buildAutoMpdfFooterBlock(
|
||||
$rawFooterHtml,
|
||||
$rawLastPageFooterHtml,
|
||||
'diag_auto_footer_single'
|
||||
);
|
||||
|
||||
$prefixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_prefix_html'] ?? ''))
|
||||
. $autoHeaderHtml
|
||||
. $autoFooterHtml, $tokens);
|
||||
$suffixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_suffix_html'] ?? '')), $tokens);
|
||||
|
||||
$marginTop = $this->cmToMm($printSetting['page_margin_top_cm'] ?? 1.2, 1.2);
|
||||
$marginBottom = $this->cmToMm($printSetting['page_margin_bottom_cm'] ?? 1.2, 1.2);
|
||||
$marginLeft = $this->cmToMm($printSetting['page_margin_left_cm'] ?? 1.0, 1.0);
|
||||
$marginRight = $this->cmToMm($printSetting['page_margin_right_cm'] ?? 1.0, 1.0);
|
||||
$marginHeader = $this->cmToMm($printSetting['margin_header_cm'] ?? 0.5, 0.5);
|
||||
$marginFooter = $this->cmToMm($printSetting['margin_footer_cm'] ?? 1.5, 1.5);
|
||||
|
||||
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
|
||||
if (! is_dir($mpdfTempDir)) {
|
||||
mkdir($mpdfTempDir, 0755, true);
|
||||
}
|
||||
|
||||
$mpdf = new Mpdf([
|
||||
'format' => (string) ($printSetting['page_size'] ?? 'A4'),
|
||||
'margin_top' => $marginTop,
|
||||
'margin_bottom' => $marginBottom,
|
||||
'margin_left' => $marginLeft,
|
||||
'margin_right' => $marginRight,
|
||||
'margin_header' => $marginHeader,
|
||||
'margin_footer' => $marginFooter,
|
||||
'tempDir' => $mpdfTempDir,
|
||||
'autoScriptToLang' => false,
|
||||
'autoLanguageDetection' => false,
|
||||
'autoArabic' => false,
|
||||
'autoVietnamese' => false,
|
||||
]);
|
||||
|
||||
// For plain HTML (without explicit mPDF tags), set header/footer via API.
|
||||
$headerHasTags = preg_match('/<\s*(htmlpageheader|sethtmlpageheader)\b/i', $rawHeaderHtml . $rawFirstPageHeaderHtml) === 1;
|
||||
$footerHasTags = preg_match('/<\s*(htmlpagefooter|sethtmlpagefooter)\b/i', $rawFooterHtml . $rawLastPageFooterHtml) === 1;
|
||||
|
||||
if (! $headerHasTags) {
|
||||
$plainHeader = $this->applyPdfTokens($rawHeaderHtml . $rawFirstPageHeaderHtml, $tokens);
|
||||
if ($plainHeader !== '') {
|
||||
$mpdf->SetHTMLHeader($plainHeader, 'O');
|
||||
$mpdf->SetHTMLHeader($plainHeader, 'E');
|
||||
}
|
||||
}
|
||||
|
||||
if (! $footerHasTags) {
|
||||
$plainFooter = $this->applyPdfTokens($rawFooterHtml, $tokens);
|
||||
if ($plainFooter === '') {
|
||||
$plainFooter = $this->applyPdfTokens($rawLastPageFooterHtml, $tokens);
|
||||
}
|
||||
|
||||
if ($plainFooter !== '') {
|
||||
$mpdf->SetHTMLFooter($plainFooter, 'O');
|
||||
$mpdf->SetHTMLFooter($plainFooter, 'E');
|
||||
}
|
||||
}
|
||||
|
||||
$backgroundImage = trim((string) ($printSetting['page_background_image'] ?? ''));
|
||||
if ($backgroundImage !== '') {
|
||||
$absolutePath = FCPATH . ltrim(str_replace('\\', '/', $backgroundImage), '/');
|
||||
if (is_file($absolutePath)) {
|
||||
$mpdf->SetWatermarkImage($absolutePath, 1, [210, 297], 'F');
|
||||
$mpdf->showWatermarkImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
$watermarkType = (string) ($printSetting['watermark_type'] ?? 'none');
|
||||
$watermarkAlpha = (float) ($printSetting['watermark_alpha'] ?? 0.12);
|
||||
if ($watermarkType === 'text') {
|
||||
$text = trim((string) ($printSetting['watermark_text'] ?? ''));
|
||||
if ($text !== '') {
|
||||
$mpdf->SetWatermarkText($text, $watermarkAlpha);
|
||||
$mpdf->showWatermarkText = true;
|
||||
}
|
||||
} elseif ($watermarkType === 'image') {
|
||||
$wmImage = trim((string) ($printSetting['watermark_image'] ?? ''));
|
||||
if ($wmImage !== '') {
|
||||
$absolutePath = FCPATH . ltrim(str_replace('\\', '/', $wmImage), '/');
|
||||
if (is_file($absolutePath)) {
|
||||
$mpdf->SetWatermarkImage($absolutePath, $watermarkAlpha);
|
||||
$mpdf->showWatermarkImage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($prefixHtml !== '') {
|
||||
$mpdf->WriteHTML($prefixHtml, HTMLParserMode::HTML_HEADER_BUFFER);
|
||||
}
|
||||
|
||||
$mpdf->WriteHTML($pdfHtml);
|
||||
|
||||
if ($suffixHtml !== '') {
|
||||
$mpdf->WriteHTML($suffixHtml, HTMLParserMode::HTML_HEADER_BUFFER, false, false);
|
||||
}
|
||||
|
||||
$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'));
|
||||
}
|
||||
|
||||
private function getDiagnosisPrintTemplates(int $labType): array
|
||||
{
|
||||
if ($labType <= 0 || ! $this->db->tableExists('diagnosis_print_templates')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->db->table('diagnosis_print_templates')
|
||||
->select('id, template_name, is_default')
|
||||
->where('modality', $labType)
|
||||
->where('status', 1)
|
||||
->orderBy('is_default', 'DESC')
|
||||
->orderBy('template_name', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
}
|
||||
|
||||
private function getDiagnosisTemplateSetting(int $labType, int $templateId = 0): array
|
||||
{
|
||||
$legacy = $this->getDiagnosisPrintSetting($labType);
|
||||
|
||||
$defaults = array_merge($legacy, [
|
||||
'page_size' => 'A4',
|
||||
'page_margin_top_cm' => 1.2,
|
||||
'id' => 0,
|
||||
'page_margin_bottom_cm' => 1.2,
|
||||
'page_margin_left_cm' => 1.0,
|
||||
'page_margin_right_cm' => 1.0,
|
||||
'margin_header_cm' => 0.5,
|
||||
'margin_footer_cm' => 1.5,
|
||||
'page_background_image' => '',
|
||||
'watermark_type' => 'none',
|
||||
'watermark_text' => '',
|
||||
'watermark_image' => '',
|
||||
'signature_image' => '',
|
||||
'watermark_alpha' => 0.12,
|
||||
'header_html' => '',
|
||||
'first_page_header_html' => '',
|
||||
'content_prefix_html' => '',
|
||||
'content_suffix_html' => '',
|
||||
'footer_html' => '',
|
||||
'last_page_footer_html' => '',
|
||||
]);
|
||||
|
||||
if ($labType <= 0 || ! $this->db->tableExists('diagnosis_print_templates')) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
$builder = $this->db->table('diagnosis_print_templates')
|
||||
->where('modality', $labType)
|
||||
->where('status', 1);
|
||||
|
||||
if ($templateId > 0) {
|
||||
$builder->where('id', $templateId);
|
||||
} else {
|
||||
$builder->orderBy('is_default', 'DESC')->orderBy('id', 'ASC');
|
||||
}
|
||||
|
||||
$row = $builder->get(1)->getRowArray();
|
||||
if (! is_array($row)) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
foreach ($defaults as $key => $value) {
|
||||
if (array_key_exists($key, $row) && $row[$key] !== null && $row[$key] !== '') {
|
||||
if (is_float($value)) {
|
||||
$defaults[$key] = (float) $row[$key];
|
||||
} else {
|
||||
$defaults[$key] = (string) $row[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pageSize = strtoupper(trim((string) ($defaults['page_size'] ?? 'A4')));
|
||||
|
||||
$defaults['id'] = (int) ($row['id'] ?? 0);
|
||||
if (! in_array($pageSize, ['A4', 'A4-L', 'LETTER', 'LEGAL'], true)) {
|
||||
$pageSize = 'A4';
|
||||
}
|
||||
$defaults['page_size'] = $pageSize;
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
private function getDiagnosisPrintSetting(int $labType): array
|
||||
{
|
||||
$defaults = [
|
||||
'letter_margin_top' => 12.0,
|
||||
'letter_margin_left' => 10.0,
|
||||
'letter_margin_right' => 10.0,
|
||||
'letter_margin_bottom' => 12.0,
|
||||
'plain_header_html' => '',
|
||||
'plain_background_image' => '',
|
||||
'mpdf_prefix_html' => '',
|
||||
'mpdf_suffix_html' => '',
|
||||
'patient_info_html' => '',
|
||||
];
|
||||
|
||||
if ($labType <= 0 || ! $this->db->tableExists('diagnosis_head_name')) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('diagnosis_head_name');
|
||||
$select = ['d_type'];
|
||||
foreach (array_keys($defaults) as $field) {
|
||||
if (in_array($field, $fields, true)) {
|
||||
$select[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
$row = $this->db->table('diagnosis_head_name')
|
||||
->select(implode(',', $select))
|
||||
->where('d_type', $labType)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
if (! is_array($row)) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
foreach ($defaults as $key => $defaultValue) {
|
||||
if (array_key_exists($key, $row) && $row[$key] !== null && $row[$key] !== '') {
|
||||
$defaults[$key] = is_float($defaultValue) ? (float) $row[$key] : (string) $row[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private function buildPdfTokens(array $data): array
|
||||
{
|
||||
$e = static function (string $v): string {
|
||||
return htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
};
|
||||
|
||||
$age = (string) ($data['age'] ?? '-');
|
||||
$gender = (string) ($data['gender'] ?? '-');
|
||||
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : 'Hospital';
|
||||
$hospitalAddress1 = defined('H_address_1') ? (string) constant('H_address_1') : '';
|
||||
$hospitalPhone = defined('H_phone_No') ? (string) constant('H_phone_No') : '';
|
||||
$hospitalLogoName = defined('H_logo') ? trim((string) constant('H_logo')) : '';
|
||||
$hospitalLogoUrl = $this->resolvePdfAssetPath([
|
||||
$hospitalLogoName !== '' ? 'assets/images/' . ltrim($hospitalLogoName, '/\\') : '',
|
||||
'assets/img/logo.png',
|
||||
'assets/images/no_image.svg',
|
||||
]);
|
||||
$signatureImageUrl = $this->resolvePdfAssetPath([
|
||||
(string) ($data['signature_image_url'] ?? ''),
|
||||
'assets/images/drPreetiSingh.jpg',
|
||||
]);
|
||||
|
||||
return [
|
||||
'{{invoice_code}}' => $e((string) ($data['invoice_code'] ?? '')),
|
||||
'{{patient_name}}' => $e((string) ($data['patient_name'] ?? '')),
|
||||
'{{relative_type}}' => $e((string) ($data['relative_type'] ?? '')),
|
||||
'{{relative_name}}' => $e((string) ($data['relative_name'] ?? '')),
|
||||
'{{relative}}' => $e(trim((string) ($data['relative_type'] ?? '') . ' ' . (string) ($data['relative_name'] ?? ''))),
|
||||
'{{age}}' => $e($age),
|
||||
'{{gender}}' => $e($gender),
|
||||
'{{age_sex}}' => $e($age . ' / ' . $gender),
|
||||
'{{uhid}}' => $e((string) ($data['uhid'] ?? '')),
|
||||
'{{collected_time}}' => $e((string) ($data['collected_time'] ?? '')),
|
||||
'{{reported_time}}' => $e((string) ($data['reported_time'] ?? '')),
|
||||
'{{printed_time}}' => date('d-m-Y h:i A'),
|
||||
'{{report_title}}' => $e((string) ($data['report_title'] ?? '')),
|
||||
'{{doctor_name}}' => $e((string) ($data['doctor_name'] ?? '')),
|
||||
'{{doctor_education}}' => $e((string) ($data['doctor_education'] ?? '')),
|
||||
'{{technician_name}}' => $e((string) ($data['technician_name'] ?? '')),
|
||||
'{{hospital_name}}' => $e($hospitalName),
|
||||
'{{hospital_address_1}}' => $e($hospitalAddress1),
|
||||
'{{hospital_phone}}' => $e($hospitalPhone),
|
||||
'{{hospital_logo_name}}' => $e($hospitalLogoName),
|
||||
'{{hospital_logo_url}}' => $e($hospitalLogoUrl),
|
||||
'{{hospital_logo_path}}' => $e($hospitalLogoUrl),
|
||||
'{{signature_image_url}}' => $e($signatureImageUrl),
|
||||
'{{signature_image_path}}' => $e($signatureImageUrl),
|
||||
];
|
||||
}
|
||||
|
||||
private function resolvePdfAssetPath(array $candidates): string
|
||||
{
|
||||
foreach ($candidates as $candidate) {
|
||||
$candidate = trim((string) $candidate);
|
||||
|
||||
if ($candidate === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/^https?:\/\//i', $candidate) === 1 || str_starts_with($candidate, 'data:')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/^[A-Za-z]:[\\\\\/]/', $candidate) === 1) {
|
||||
$absolutePath = $candidate;
|
||||
} else {
|
||||
$relativePath = ltrim(str_replace('\\', '/', $candidate), '/');
|
||||
$absolutePath = str_starts_with($relativePath, 'public/')
|
||||
? ROOTPATH . $relativePath
|
||||
: FCPATH . $relativePath;
|
||||
}
|
||||
|
||||
$absolutePath = str_replace('\\', '/', $absolutePath);
|
||||
|
||||
if (is_file($absolutePath)) {
|
||||
return $absolutePath;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all {{token}} placeholders in $html using the token map returned by buildPdfTokens().
|
||||
*/
|
||||
private function applyPdfTokens(string $html, array $tokens): string
|
||||
{
|
||||
return str_replace(array_keys($tokens), array_values($tokens), $html);
|
||||
}
|
||||
|
||||
private function buildDefaultPatientInfoHtml(array $data): string
|
||||
{
|
||||
$e = static function (string $v): string {
|
||||
return htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
};
|
||||
$ageSex = $e((string) ($data['age_text'] ?? '-')) . ' / ' . $e((string) ($data['gender_text'] ?? '-'));
|
||||
$relative = $e(trim((string) ($data['relative_name'] ?? '') . ' ' . (string) ($data['relative_text'] ?? '')));
|
||||
|
||||
return '<table width="100%" style="border-collapse:collapse;margin-bottom:8px;font-size:11px;">'
|
||||
. '<tr>'
|
||||
. '<td width="50%" style="vertical-align:top;">'
|
||||
. '<b>Invoice ID:</b> ' . $e((string) ($data['invoice_code'] ?? '')) . '<br>'
|
||||
. '<b>Patient:</b> ' . $e((string) ($data['patient_name'] ?? '')) . '<br>'
|
||||
. '<b>Relative:</b> ' . $relative . '<br>'
|
||||
. '<b>Age/Sex:</b> ' . $ageSex
|
||||
. '</td>'
|
||||
. '<td width="50%" style="vertical-align:top;text-align:right;">'
|
||||
. '<b>UHID:</b> ' . $e((string) ($data['uhid'] ?? '')) . '<br>'
|
||||
. '<b>Collected:</b> ' . $e((string) ($data['collected_time'] ?? '')) . '<br>'
|
||||
. '<b>Reported:</b> ' . $e((string) ($data['reported_time'] ?? '')) . '<br>'
|
||||
. '<b>Printed:</b> ' . $e(date('d-m-Y h:i A'))
|
||||
. '</td>'
|
||||
. '</tr></table>';
|
||||
}
|
||||
|
||||
private function buildAutoMpdfHeaderBlock(string $headerHtml, string $firstPageHeaderHtml, string $name): string
|
||||
{
|
||||
$headerHtml = trim($headerHtml);
|
||||
$firstPageHeaderHtml = trim($firstPageHeaderHtml);
|
||||
$combined = $headerHtml . $firstPageHeaderHtml;
|
||||
|
||||
if ($combined === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (preg_match('/<\s*(htmlpageheader|sethtmlpageheader)\b/i', $combined) === 1) {
|
||||
return $combined;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function buildAutoMpdfFooterBlock(string $footerHtml, string $lastPageFooterHtml, string $name): string
|
||||
{
|
||||
$footerHtml = trim($footerHtml);
|
||||
$lastPageFooterHtml = trim($lastPageFooterHtml);
|
||||
$combined = $footerHtml . $lastPageFooterHtml;
|
||||
|
||||
if ($combined === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (preg_match('/<\s*(htmlpagefooter|sethtmlpagefooter)\b/i', $combined) === 1) {
|
||||
return $combined;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function normalizeMarginMm($rawValue, float $default): float
|
||||
{
|
||||
if ($rawValue === null || $rawValue === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$value = (float) $rawValue;
|
||||
if (! is_finite($value)) {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
$value = max(0.0, min(60.0, $value));
|
||||
|
||||
return round($value, 2);
|
||||
}
|
||||
|
||||
private function cmToMm($rawCmValue, float $defaultCm): float
|
||||
{
|
||||
if ($rawCmValue === null || $rawCmValue === '') {
|
||||
return round($defaultCm * 10.0, 2);
|
||||
}
|
||||
|
||||
$cm = (float) $rawCmValue;
|
||||
if (! is_finite($cm)) {
|
||||
$cm = $defaultCm;
|
||||
}
|
||||
|
||||
$cm = max(0.0, min(25.0, $cm));
|
||||
|
||||
return round($cm * 10.0, 2);
|
||||
}
|
||||
|
||||
private function resolveFallbackReportHtml(int $labRepoId, int $chargeItemId, int $labType): string
|
||||
{
|
||||
$html = '';
|
||||
|
||||
if ($labRepoId > 0) {
|
||||
$repoRow = $this->db->table('lab_repo')
|
||||
->select('HTMLData')
|
||||
->where('mstRepoKey', $labRepoId)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
$html = trim((string) ($repoRow['HTMLData'] ?? ''));
|
||||
}
|
||||
|
||||
if ($html !== '') {
|
||||
return $html;
|
||||
}
|
||||
|
||||
if (! $this->db->tableExists('radiology_ultrasound_template') || $chargeItemId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$invoiceItem = $this->db->table('invoice_item')
|
||||
->select('item_id')
|
||||
->where('id', $chargeItemId)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
$chargeId = (int) ($invoiceItem['item_id'] ?? 0);
|
||||
|
||||
$builder = $this->db->table('radiology_ultrasound_template')
|
||||
->select('Findings, Impression')
|
||||
->where('Modality', $labType);
|
||||
|
||||
if ($chargeId > 0) {
|
||||
$builder->groupStart()
|
||||
->where('charge_id', $chargeId)
|
||||
->orWhere('charge_id', 0)
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
$template = $builder->orderBy('id', 'ASC')->get(1)->getRowArray();
|
||||
if (! is_array($template)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$findings = trim((string) ($template['Findings'] ?? ''));
|
||||
$impression = trim((string) ($template['Impression'] ?? ''));
|
||||
|
||||
if ($findings === '' && $impression === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = $findings;
|
||||
if ($impression !== '') {
|
||||
$html .= '<div style="margin-top:10px;"><b>Impression :</b><br/>' . nl2br($impression) . '</div>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function removeTest($labReqId)
|
||||
|
|
@ -1419,6 +2069,9 @@ class Diagnosis extends BaseController
|
|||
$labType = (int) $labType;
|
||||
$print = (int) $print;
|
||||
$printOnType = (int) $printOnType;
|
||||
$templateId = (int) ($this->request->getGet('template_id') ?? 0);
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
if ($invoiceId <= 0 || $labType <= 0) {
|
||||
return '<h3>Invalid parameters</h3>';
|
||||
|
|
@ -1450,31 +2103,233 @@ class Diagnosis extends BaseController
|
|||
. ' / Invoice ID :' . ($invoice->invoice_code ?? '')
|
||||
. ' / Person Name :' . ($patient->p_fname ?? '');
|
||||
|
||||
$isPlainPaper = ($printOnType === 1);
|
||||
$isPlainPaper = false;
|
||||
|
||||
if ($print > 0) {
|
||||
return view('diagnosis/print_compiled_report', [
|
||||
'invoiceRequest' => $invoiceRequest,
|
||||
'invoice' => $invoice,
|
||||
'patient' => $patient,
|
||||
'head' => $head,
|
||||
'reportHead' => $reportHead,
|
||||
'isPlainPaper' => $isPlainPaper,
|
||||
'labType' => $labType,
|
||||
'invoiceId' => $invoiceId,
|
||||
]);
|
||||
$genderValue = (int) ($patient->gender ?? 0);
|
||||
$genderText = $genderValue === 1 ? 'Male' : ($genderValue === 2 ? 'Female' : '-');
|
||||
|
||||
$ageText = '-';
|
||||
if (function_exists('get_age_1')) {
|
||||
$ageText = (string) get_age_1(
|
||||
$patient->dob ?? null,
|
||||
$patient->age ?? '',
|
||||
$patient->age_in_month ?? '',
|
||||
$patient->estimate_dob ?? ''
|
||||
);
|
||||
} elseif (! empty($patient->age)) {
|
||||
$ageText = (string) $patient->age;
|
||||
}
|
||||
|
||||
return view('diagnosis/print_compiled_report', [
|
||||
'invoiceRequest' => $invoiceRequest,
|
||||
'invoice' => $invoice,
|
||||
'patient' => $patient,
|
||||
'head' => $head,
|
||||
'reportHead' => $reportHead,
|
||||
'isPlainPaper' => $isPlainPaper,
|
||||
'labType' => $labType,
|
||||
'invoiceId' => $invoiceId,
|
||||
$reportHtml = trim((string) ($invoiceRequest->report_data ?? ''));
|
||||
if ($reportHtml === '') {
|
||||
$reportHtml = '<p>No compiled report data available for this request.</p>';
|
||||
}
|
||||
|
||||
$printSetting = $this->getDiagnosisTemplateSetting($labType, $templateId);
|
||||
|
||||
$patientName = trim((string) ($patient->p_fname ?? ''));
|
||||
$relativeName = trim((string) ($patient->p_relative ?? ''));
|
||||
$relativeText = trim((string) ($patient->p_rname ?? ''));
|
||||
$repoTitle = trim((string) ($itemType->group_desc ?? 'Lab Report'));
|
||||
|
||||
$tokens = $this->buildPdfTokens([
|
||||
'invoice_code' => $invoice->invoice_code ?? '',
|
||||
'patient_name' => $patientName,
|
||||
'relative_type' => $relativeName,
|
||||
'relative_name' => $relativeText,
|
||||
'age' => $ageText,
|
||||
'gender' => $genderText,
|
||||
'uhid' => $patient->p_code ?? '',
|
||||
'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 ?? '',
|
||||
'signature_image_url' => (string) ($printSetting['signature_image'] ?? ''),
|
||||
]);
|
||||
|
||||
$rawPatientInfoHtml = trim((string) ($printSetting['patient_info_html'] ?? ''));
|
||||
if ($rawPatientInfoHtml !== '') {
|
||||
$patientInfoHtml = $this->applyPdfTokens($rawPatientInfoHtml, $tokens);
|
||||
} elseif (($printSetting['id'] ?? 0) <= 0) {
|
||||
$patientInfoHtml = $this->buildDefaultPatientInfoHtml([
|
||||
'invoice_code' => $invoice->invoice_code ?? '',
|
||||
'patient_name' => $patientName,
|
||||
'relative_name' => $relativeName,
|
||||
'relative_text' => $relativeText,
|
||||
'age_text' => $ageText,
|
||||
'gender_text' => $genderText,
|
||||
'uhid' => $patient->p_code ?? '',
|
||||
'collected_time' => $invoiceRequest->collected_time ?? '',
|
||||
'reported_time' => $invoiceRequest->reported_time ?? '',
|
||||
]);
|
||||
} else {
|
||||
$patientInfoHtml = '';
|
||||
}
|
||||
|
||||
$reportHtml = trim((string) ($printSetting['content_prefix_html'] ?? '')) . $reportHtml . trim((string) ($printSetting['content_suffix_html'] ?? ''));
|
||||
|
||||
$marginTop = $this->cmToMm($printSetting['page_margin_top_cm'] ?? 1.2, 1.2);
|
||||
$marginBottom = $this->cmToMm($printSetting['page_margin_bottom_cm'] ?? 1.2, 1.2);
|
||||
$marginLeft = $this->cmToMm($printSetting['page_margin_left_cm'] ?? 1.0, 1.0);
|
||||
$marginRight = $this->cmToMm($printSetting['page_margin_right_cm'] ?? 1.0, 1.0);
|
||||
$marginHeader = $this->cmToMm($printSetting['margin_header_cm'] ?? 0.5, 0.5);
|
||||
$marginFooter = $this->cmToMm($printSetting['margin_footer_cm'] ?? 1.5, 1.5);
|
||||
|
||||
$pdfHtml = view('diagnosis/pdf_compiled_report', [
|
||||
'invoiceRequest' => $invoiceRequest,
|
||||
'invoice' => $invoice,
|
||||
'patient' => $patient,
|
||||
'reportHead' => $reportHead,
|
||||
'reportHtml' => $reportHtml,
|
||||
'isPlainPaper' => $isPlainPaper,
|
||||
'ageText' => $ageText,
|
||||
'genderText' => $genderText,
|
||||
'printSetting' => $printSetting,
|
||||
'labType' => $labType,
|
||||
'invoiceId' => $invoiceId,
|
||||
'itemType' => $itemType,
|
||||
'patientName' => $patientName,
|
||||
'relativeName' => $relativeName,
|
||||
'relativeText' => $relativeText,
|
||||
'repoTitle' => $repoTitle,
|
||||
'patientInfoHtml' => $patientInfoHtml,
|
||||
]);
|
||||
|
||||
$rawHeaderHtml = trim((string) ($printSetting['header_html'] ?? ''));
|
||||
$rawFirstPageHeaderHtml = trim((string) ($printSetting['first_page_header_html'] ?? ''));
|
||||
$rawFooterHtml = trim((string) ($printSetting['footer_html'] ?? ''));
|
||||
$rawLastPageFooterHtml = trim((string) ($printSetting['last_page_footer_html'] ?? ''));
|
||||
|
||||
$autoHeaderHtml = $this->buildAutoMpdfHeaderBlock(
|
||||
$rawHeaderHtml,
|
||||
$rawFirstPageHeaderHtml,
|
||||
'diag_auto_header_compiled'
|
||||
);
|
||||
$autoFooterHtml = $this->buildAutoMpdfFooterBlock(
|
||||
$rawFooterHtml,
|
||||
$rawLastPageFooterHtml,
|
||||
'diag_auto_footer_compiled'
|
||||
);
|
||||
|
||||
$prefixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_prefix_html'] ?? ''))
|
||||
. $autoHeaderHtml
|
||||
. $autoFooterHtml, $tokens);
|
||||
$suffixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_suffix_html'] ?? '')), $tokens);
|
||||
|
||||
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
|
||||
if (! is_dir($mpdfTempDir)) {
|
||||
mkdir($mpdfTempDir, 0755, true);
|
||||
}
|
||||
|
||||
$t_start = microtime(true);
|
||||
$mpdf = new Mpdf([
|
||||
'format' => (string) ($printSetting['page_size'] ?? 'A4'),
|
||||
'margin_top' => $marginTop,
|
||||
'margin_bottom' => $marginBottom,
|
||||
'margin_left' => $marginLeft,
|
||||
'margin_right' => $marginRight,
|
||||
'margin_header' => $marginHeader,
|
||||
'margin_footer' => $marginFooter,
|
||||
'tempDir' => $mpdfTempDir,
|
||||
'autoScriptToLang' => false,
|
||||
'autoLanguageDetection' => false,
|
||||
'autoArabic' => false,
|
||||
'autoVietnamese' => false,
|
||||
]);
|
||||
$t_mpdf_init = microtime(true);
|
||||
|
||||
// For plain HTML (without explicit mPDF tags), set header/footer via API.
|
||||
$headerHasTags = preg_match('/<\s*(htmlpageheader|sethtmlpageheader)\b/i', $rawHeaderHtml . $rawFirstPageHeaderHtml) === 1;
|
||||
$footerHasTags = preg_match('/<\s*(htmlpagefooter|sethtmlpagefooter)\b/i', $rawFooterHtml . $rawLastPageFooterHtml) === 1;
|
||||
|
||||
if (! $headerHasTags) {
|
||||
$plainHeader = $this->applyPdfTokens($rawHeaderHtml . $rawFirstPageHeaderHtml, $tokens);
|
||||
if ($plainHeader !== '') {
|
||||
$mpdf->SetHTMLHeader($plainHeader, 'O');
|
||||
$mpdf->SetHTMLHeader($plainHeader, 'E');
|
||||
}
|
||||
}
|
||||
|
||||
if (! $footerHasTags) {
|
||||
$plainFooter = $this->applyPdfTokens($rawFooterHtml, $tokens);
|
||||
if ($plainFooter === '') {
|
||||
$plainFooter = $this->applyPdfTokens($rawLastPageFooterHtml, $tokens);
|
||||
}
|
||||
|
||||
if ($plainFooter !== '') {
|
||||
$mpdf->SetHTMLFooter($plainFooter, 'O');
|
||||
$mpdf->SetHTMLFooter($plainFooter, 'E');
|
||||
}
|
||||
}
|
||||
|
||||
$backgroundImage = trim((string) ($printSetting['page_background_image'] ?? ''));
|
||||
if ($backgroundImage !== '') {
|
||||
$absolutePath = FCPATH . ltrim(str_replace('\\', '/', $backgroundImage), '/');
|
||||
if (is_file($absolutePath)) {
|
||||
$mpdf->SetWatermarkImage($absolutePath, 1, [210, 297], 'F');
|
||||
$mpdf->showWatermarkImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
$watermarkType = (string) ($printSetting['watermark_type'] ?? 'none');
|
||||
$watermarkAlpha = (float) ($printSetting['watermark_alpha'] ?? 0.12);
|
||||
if ($watermarkType === 'text') {
|
||||
$text = trim((string) ($printSetting['watermark_text'] ?? ''));
|
||||
if ($text !== '') {
|
||||
$mpdf->SetWatermarkText($text, $watermarkAlpha);
|
||||
$mpdf->showWatermarkText = true;
|
||||
}
|
||||
} elseif ($watermarkType === 'image') {
|
||||
$wmImage = trim((string) ($printSetting['watermark_image'] ?? ''));
|
||||
if ($wmImage !== '') {
|
||||
$absolutePath = FCPATH . ltrim(str_replace('\\', '/', $wmImage), '/');
|
||||
if (is_file($absolutePath)) {
|
||||
$mpdf->SetWatermarkImage($absolutePath, $watermarkAlpha);
|
||||
$mpdf->showWatermarkImage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($prefixHtml !== '') {
|
||||
$mpdf->WriteHTML($prefixHtml, HTMLParserMode::HTML_HEADER_BUFFER);
|
||||
}
|
||||
$t_prefix = microtime(true);
|
||||
|
||||
$mpdf->WriteHTML($pdfHtml);
|
||||
$t_body = microtime(true);
|
||||
|
||||
if ($suffixHtml !== '') {
|
||||
$mpdf->WriteHTML($suffixHtml, HTMLParserMode::HTML_HEADER_BUFFER, false, false);
|
||||
}
|
||||
$t_suffix = microtime(true);
|
||||
|
||||
$invoiceCode = preg_replace('/[^A-Za-z0-9\-_]/', '_', (string) ($invoice->invoice_code ?? 'invoice'));
|
||||
$fileName = 'Compiled_Report_' . $invoiceCode . '_' . $labType . '.pdf';
|
||||
|
||||
$pdfOutput = $mpdf->Output($fileName, 'S');
|
||||
$t_output = microtime(true);
|
||||
|
||||
log_message('debug', sprintf(
|
||||
'[PDF_TIMING printPdfCreate] invoice=%s lab=%s | new_Mpdf()=%.2fs | WriteHTML(prefix)=%.2fs | WriteHTML(body)=%.2fs | WriteHTML(suffix)=%.2fs | Output()=%.2fs | TOTAL=%.2fs | body_bytes=%d',
|
||||
$invoiceId, $labType,
|
||||
$t_mpdf_init - $t_start,
|
||||
$t_prefix - $t_mpdf_init,
|
||||
$t_body - $t_prefix,
|
||||
$t_suffix - $t_body,
|
||||
$t_output - $t_suffix,
|
||||
$t_output - $t_start,
|
||||
strlen($pdfHtml)
|
||||
));
|
||||
|
||||
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($pdfOutput);
|
||||
}
|
||||
|
||||
public function reportFileList($invoiceId, $labType, $delete = 0)
|
||||
|
|
|
|||
|
|
@ -638,6 +638,318 @@ class Template extends BaseController
|
|||
return $this->response->setBody($html);
|
||||
}
|
||||
|
||||
public function diagnosis_print_settings(int $modality = 3)
|
||||
{
|
||||
if ($resp = $this->requireAnyPermission(['template.pathology', 'template.ultrasound', 'template.xray', 'template.ct', 'template.mri', 'template.echo'])) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$modalityList = [
|
||||
5 => 'Pathology',
|
||||
1 => 'Ultrasound',
|
||||
2 => 'MRI',
|
||||
3 => 'X-Ray',
|
||||
4 => 'CT-Scan',
|
||||
6 => 'Echo',
|
||||
];
|
||||
|
||||
if (! in_array($modality, [1, 2, 3, 4, 5, 6], true)) {
|
||||
$modality = 3;
|
||||
}
|
||||
|
||||
$notice = '';
|
||||
$noticeType = 'success';
|
||||
|
||||
if (! $this->db->tableExists('diagnosis_print_templates')) {
|
||||
return view('Setting/Template/diagnosis_print_settings', [
|
||||
'modality' => $modality,
|
||||
'row' => [],
|
||||
'templates' => [],
|
||||
'selected_template_id' => 0,
|
||||
'notice' => 'diagnosis_print_templates table not found. Please run migration.',
|
||||
'notice_type' => 'danger',
|
||||
'columns_ready' => false,
|
||||
'modality_list' => $modalityList,
|
||||
]);
|
||||
}
|
||||
|
||||
$columnsReady = true;
|
||||
$templateTable = $this->db->table('diagnosis_print_templates');
|
||||
$templateFields = $this->db->getFieldNames('diagnosis_print_templates');
|
||||
$hasSignatureImageColumn = in_array('signature_image', $templateFields, true);
|
||||
|
||||
// Backward-compatible: if this DB has not yet received the signature column,
|
||||
// try to add it automatically so template-level signature upload can work.
|
||||
if (! $hasSignatureImageColumn) {
|
||||
try {
|
||||
$this->db->query("ALTER TABLE diagnosis_print_templates ADD COLUMN signature_image VARCHAR(255) NULL AFTER watermark_image");
|
||||
$templateFields = $this->db->getFieldNames('diagnosis_print_templates');
|
||||
$hasSignatureImageColumn = in_array('signature_image', $templateFields, true);
|
||||
} catch (\Throwable $e) {
|
||||
// Keep page usable even when ALTER permission is not available.
|
||||
$hasSignatureImageColumn = false;
|
||||
}
|
||||
}
|
||||
|
||||
$selectedTemplateId = (int) ($this->request->getGet('template_id') ?? 0);
|
||||
$isNewTemplate = (int) ($this->request->getGet('new') ?? 0) === 1;
|
||||
|
||||
if (strtolower($this->request->getMethod()) === 'post') {
|
||||
$modality = (int) ($this->request->getPost('modality') ?? $modality);
|
||||
if (! in_array($modality, [1, 2, 3, 4, 5, 6], true)) {
|
||||
$modality = 3;
|
||||
}
|
||||
|
||||
$selectedTemplateId = (int) ($this->request->getPost('template_id') ?? 0);
|
||||
|
||||
if (! $columnsReady) {
|
||||
$notice = 'Print settings columns are missing. Please run migration first.';
|
||||
$noticeType = 'danger';
|
||||
} else {
|
||||
$existing = [];
|
||||
if ($selectedTemplateId > 0) {
|
||||
$existing = $templateTable
|
||||
->where('id', $selectedTemplateId)
|
||||
->where('modality', $modality)
|
||||
->get(1)
|
||||
->getRowArray() ?? [];
|
||||
}
|
||||
|
||||
$templateName = trim((string) ($this->request->getPost('template_name') ?? ''));
|
||||
if ($templateName === '') {
|
||||
$templateName = 'Template ' . date('d-m-Y H:i');
|
||||
}
|
||||
|
||||
$removeBackground = (int) ($this->request->getPost('remove_background') ?? 0) === 1;
|
||||
$removeWatermarkImage = (int) ($this->request->getPost('remove_watermark_image') ?? 0) === 1;
|
||||
$removeSignatureImage = (int) ($this->request->getPost('remove_signature_image') ?? 0) === 1;
|
||||
|
||||
$backgroundPath = (string) ($existing['page_background_image'] ?? '');
|
||||
$watermarkImagePath = (string) ($existing['watermark_image'] ?? '');
|
||||
$signatureImagePath = (string) ($existing['signature_image'] ?? '');
|
||||
|
||||
if ($removeBackground) {
|
||||
$backgroundPath = '';
|
||||
}
|
||||
if ($removeWatermarkImage) {
|
||||
$watermarkImagePath = '';
|
||||
}
|
||||
if ($removeSignatureImage) {
|
||||
$signatureImagePath = '';
|
||||
}
|
||||
|
||||
$bgUpload = $this->request->getFile('page_background_image');
|
||||
if ($bgUpload && $bgUpload->isValid() && ! $bgUpload->hasMoved()) {
|
||||
$ext = strtolower((string) $bgUpload->getExtension());
|
||||
if (in_array($ext, ['png', 'jpg', 'jpeg', 'webp'], true)) {
|
||||
$folder = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'diagnosis_print_bg';
|
||||
if (! is_dir($folder)) {
|
||||
@mkdir($folder, 0777, true);
|
||||
}
|
||||
|
||||
$newName = 'diag_tpl_bg_' . $modality . '_' . date('Ymd_His') . '.' . $ext;
|
||||
$bgUpload->move($folder, $newName, true);
|
||||
$backgroundPath = 'uploads/diagnosis_print_bg/' . $newName;
|
||||
} else {
|
||||
$notice = 'Background image must be PNG/JPG/JPEG/WEBP.';
|
||||
$noticeType = 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
$wmUpload = $this->request->getFile('watermark_image');
|
||||
if ($wmUpload && $wmUpload->isValid() && ! $wmUpload->hasMoved()) {
|
||||
$ext = strtolower((string) $wmUpload->getExtension());
|
||||
if (in_array($ext, ['png', 'jpg', 'jpeg', 'webp'], true)) {
|
||||
$folder = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'diagnosis_print_wm';
|
||||
if (! is_dir($folder)) {
|
||||
@mkdir($folder, 0777, true);
|
||||
}
|
||||
|
||||
$newName = 'diag_tpl_wm_' . $modality . '_' . date('Ymd_His') . '.' . $ext;
|
||||
$wmUpload->move($folder, $newName, true);
|
||||
$watermarkImagePath = 'uploads/diagnosis_print_wm/' . $newName;
|
||||
} else {
|
||||
$notice = 'Watermark image must be PNG/JPG/JPEG/WEBP.';
|
||||
$noticeType = 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasSignatureImageColumn) {
|
||||
$sigUpload = $this->request->getFile('signature_image');
|
||||
if ($sigUpload && $sigUpload->isValid() && ! $sigUpload->hasMoved()) {
|
||||
$ext = strtolower((string) $sigUpload->getExtension());
|
||||
if (in_array($ext, ['png', 'jpg', 'jpeg', 'webp'], true)) {
|
||||
$folder = rtrim(FCPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'diagnosis_print_sign';
|
||||
if (! is_dir($folder)) {
|
||||
@mkdir($folder, 0777, true);
|
||||
}
|
||||
|
||||
$newName = 'diag_tpl_sign_' . $modality . '_' . date('Ymd_His') . '.' . $ext;
|
||||
$sigUpload->move($folder, $newName, true);
|
||||
$signatureImagePath = 'uploads/diagnosis_print_sign/' . $newName;
|
||||
} else {
|
||||
$notice = 'Signature image must be PNG/JPG/JPEG/WEBP.';
|
||||
$noticeType = 'danger';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'modality' => $modality,
|
||||
'template_name' => $templateName,
|
||||
'page_size' => trim((string) ($this->request->getPost('page_size') ?? 'A4')) ?: 'A4',
|
||||
'page_margin_top_cm' => $this->normalizeMarginCm($this->request->getPost('page_margin_top_cm'), 1.2),
|
||||
'page_margin_bottom_cm' => $this->normalizeMarginCm($this->request->getPost('page_margin_bottom_cm'), 1.2),
|
||||
'page_margin_left_cm' => $this->normalizeMarginCm($this->request->getPost('page_margin_left_cm'), 1.0),
|
||||
'page_margin_right_cm' => $this->normalizeMarginCm($this->request->getPost('page_margin_right_cm'), 1.0),
|
||||
'margin_header_cm' => $this->normalizeMarginCm($this->request->getPost('margin_header_cm'), 0.5),
|
||||
'margin_footer_cm' => $this->normalizeMarginCm($this->request->getPost('margin_footer_cm'), 1.5),
|
||||
'page_background_image' => $backgroundPath,
|
||||
'watermark_type' => in_array((string) $this->request->getPost('watermark_type'), ['none', 'text', 'image'], true)
|
||||
? (string) $this->request->getPost('watermark_type')
|
||||
: 'none',
|
||||
'watermark_text' => (string) ($this->request->getPost('watermark_text') ?? ''),
|
||||
'watermark_image' => $watermarkImagePath,
|
||||
'signature_image' => $signatureImagePath,
|
||||
'watermark_alpha' => $this->normalizeWatermarkAlpha($this->request->getPost('watermark_alpha')),
|
||||
'header_html' => (string) ($this->request->getPost('header_html') ?? ''),
|
||||
'first_page_header_html' => (string) ($this->request->getPost('first_page_header_html') ?? ''),
|
||||
'content_prefix_html' => (string) ($this->request->getPost('content_prefix_html') ?? ''),
|
||||
'content_suffix_html' => (string) ($this->request->getPost('content_suffix_html') ?? ''),
|
||||
'footer_html' => (string) ($this->request->getPost('footer_html') ?? ''),
|
||||
'last_page_footer_html' => (string) ($this->request->getPost('last_page_footer_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,
|
||||
'status' => 1,
|
||||
];
|
||||
|
||||
if (! $hasSignatureImageColumn) {
|
||||
unset($data['signature_image']);
|
||||
}
|
||||
|
||||
if ($noticeType !== 'danger') {
|
||||
if ($data['is_default'] === 1) {
|
||||
$templateTable
|
||||
->where('modality', $modality)
|
||||
->set('is_default', 0)
|
||||
->update();
|
||||
}
|
||||
|
||||
if (! empty($existing) && $selectedTemplateId > 0) {
|
||||
$templateTable
|
||||
->where('id', $selectedTemplateId)
|
||||
->where('modality', $modality)
|
||||
->update($data);
|
||||
} else {
|
||||
$templateTable->insert($data);
|
||||
$selectedTemplateId = (int) $this->db->insertID();
|
||||
}
|
||||
|
||||
$notice = (($modalityList[$modality] ?? 'Diagnosis') . ' print template saved.');
|
||||
$noticeType = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->isAJAX()) {
|
||||
return $this->response->setJSON([
|
||||
'status' => $noticeType === 'success' ? 'success' : 'error',
|
||||
'notice' => $notice,
|
||||
'notice_type' => $noticeType,
|
||||
'modality' => $modality,
|
||||
'selected_template_id' => $selectedTemplateId,
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$templates = $templateTable
|
||||
->select('id, template_name, is_default')
|
||||
->where('modality', $modality)
|
||||
->where('status', 1)
|
||||
->orderBy('is_default', 'DESC')
|
||||
->orderBy('template_name', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
if (! $isNewTemplate && $selectedTemplateId <= 0 && ! empty($templates)) {
|
||||
$selectedTemplateId = (int) ($templates[0]['id'] ?? 0);
|
||||
}
|
||||
|
||||
$row = [];
|
||||
if ($selectedTemplateId > 0) {
|
||||
$row = $templateTable
|
||||
->where('id', $selectedTemplateId)
|
||||
->where('modality', $modality)
|
||||
->get(1)
|
||||
->getRowArray() ?? [];
|
||||
}
|
||||
|
||||
return view('Setting/Template/diagnosis_print_settings', [
|
||||
'modality' => $modality,
|
||||
'row' => $row,
|
||||
'templates' => $templates,
|
||||
'selected_template_id' => $selectedTemplateId,
|
||||
'notice' => $notice,
|
||||
'notice_type' => $noticeType,
|
||||
'columns_ready' => $columnsReady,
|
||||
'modality_list' => $modalityList,
|
||||
'has_signature_image_column' => $hasSignatureImageColumn,
|
||||
'has_signature_image_column' => $hasSignatureImageColumn,
|
||||
]);
|
||||
}
|
||||
|
||||
private function normalizeWatermarkAlpha($rawValue): float
|
||||
{
|
||||
if ($rawValue === null || $rawValue === '') {
|
||||
return 0.12;
|
||||
}
|
||||
|
||||
$value = (float) $rawValue;
|
||||
if (! is_finite($value)) {
|
||||
$value = 0.12;
|
||||
}
|
||||
|
||||
$value = max(0.01, min(1.0, $value));
|
||||
|
||||
return round($value, 2);
|
||||
}
|
||||
|
||||
private function normalizeMarginCm($rawValue, float $default): float
|
||||
{
|
||||
if ($rawValue === null || $rawValue === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$value = (float) $rawValue;
|
||||
if (! is_finite($value)) {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
// Allow large top margins like 6.1cm for heavy report headers.
|
||||
$value = max(0.0, min(25.0, $value));
|
||||
|
||||
return round($value, 2);
|
||||
}
|
||||
|
||||
private function normalizeMarginMm($rawValue, float $default): float
|
||||
{
|
||||
if ($rawValue === null || $rawValue === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$value = (float) $rawValue;
|
||||
if (! is_finite($value)) {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
$value = max(0.0, min(60.0, $value));
|
||||
|
||||
return round($value, 2);
|
||||
}
|
||||
|
||||
private function renderOptionTable(int $mstTestKey): string
|
||||
{
|
||||
$sql = "select *, if(option_bold=1,'Bold','') as option_bold_str
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddDiagnosisHeadPrintSettings extends Migration
|
||||
{
|
||||
private function hasDiagnosisHeadTable(): bool
|
||||
{
|
||||
$query = $this->db->query("SHOW TABLES LIKE 'diagnosis_head_name'");
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
private function diagnosisHeadFields(): array
|
||||
{
|
||||
$fields = [];
|
||||
$query = $this->db->query("SHOW COLUMNS FROM diagnosis_head_name");
|
||||
foreach ($query->getResultArray() as $row) {
|
||||
$fields[] = (string) ($row['Field'] ?? '');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (! in_array('letter_margin_top', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'letter_margin_top' => [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'default' => '12.00',
|
||||
'null' => true,
|
||||
'after' => 'print_page_direct',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('letter_margin_left', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'letter_margin_left' => [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'default' => '10.00',
|
||||
'null' => true,
|
||||
'after' => 'letter_margin_top',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('letter_margin_right', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'letter_margin_right' => [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'default' => '10.00',
|
||||
'null' => true,
|
||||
'after' => 'letter_margin_left',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('letter_margin_bottom', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'letter_margin_bottom' => [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'default' => '12.00',
|
||||
'null' => true,
|
||||
'after' => 'letter_margin_right',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('plain_header_html', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'plain_header_html' => [
|
||||
'type' => 'longtext',
|
||||
'null' => true,
|
||||
'after' => 'letter_margin_bottom',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('plain_background_image', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'plain_background_image' => [
|
||||
'type' => 'varchar',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
'after' => 'plain_header_html',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (in_array('plain_background_image', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'plain_background_image');
|
||||
}
|
||||
if (in_array('plain_header_html', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'plain_header_html');
|
||||
}
|
||||
if (in_array('letter_margin_bottom', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'letter_margin_bottom');
|
||||
}
|
||||
if (in_array('letter_margin_right', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'letter_margin_right');
|
||||
}
|
||||
if (in_array('letter_margin_left', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'letter_margin_left');
|
||||
}
|
||||
if (in_array('letter_margin_top', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'letter_margin_top');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddDiagnosisHeadMpdfBlocks extends Migration
|
||||
{
|
||||
private function hasDiagnosisHeadTable(): bool
|
||||
{
|
||||
$query = $this->db->query("SHOW TABLES LIKE 'diagnosis_head_name'");
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
private function diagnosisHeadFields(): array
|
||||
{
|
||||
$fields = [];
|
||||
$query = $this->db->query("SHOW COLUMNS FROM diagnosis_head_name");
|
||||
foreach ($query->getResultArray() as $row) {
|
||||
$fields[] = (string) ($row['Field'] ?? '');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (! in_array('mpdf_prefix_html', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'mpdf_prefix_html' => [
|
||||
'type' => 'longtext',
|
||||
'null' => true,
|
||||
'after' => 'plain_background_image',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('mpdf_suffix_html', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'mpdf_suffix_html' => [
|
||||
'type' => 'longtext',
|
||||
'null' => true,
|
||||
'after' => 'mpdf_prefix_html',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (in_array('mpdf_suffix_html', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'mpdf_suffix_html');
|
||||
}
|
||||
|
||||
if (in_array('mpdf_prefix_html', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'mpdf_prefix_html');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddDiagnosisPatientInfoHtml extends Migration
|
||||
{
|
||||
private function hasDiagnosisHeadTable(): bool
|
||||
{
|
||||
$query = $this->db->query("SHOW TABLES LIKE 'diagnosis_head_name'");
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
private function diagnosisHeadFields(): array
|
||||
{
|
||||
$fields = [];
|
||||
$query = $this->db->query("SHOW COLUMNS FROM diagnosis_head_name");
|
||||
foreach ($query->getResultArray() as $row) {
|
||||
$fields[] = (string) ($row['Field'] ?? '');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (! in_array('patient_info_html', $fields, true)) {
|
||||
$this->forge->addColumn('diagnosis_head_name', [
|
||||
'patient_info_html' => [
|
||||
'type' => 'longtext',
|
||||
'null' => true,
|
||||
'after' => 'mpdf_suffix_html',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (! $this->hasDiagnosisHeadTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->diagnosisHeadFields();
|
||||
|
||||
if (in_array('patient_info_html', $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_head_name', 'patient_info_html');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class CreateDiagnosisPrintTemplates extends Migration
|
||||
{
|
||||
private function hasTemplateTable(): bool
|
||||
{
|
||||
$query = $this->db->query("SHOW TABLES LIKE 'diagnosis_print_templates'");
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if ($this->hasTemplateTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'INT',
|
||||
'constraint' => 11,
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
],
|
||||
'modality' => [
|
||||
'type' => 'INT',
|
||||
'constraint' => 11,
|
||||
'default' => 3,
|
||||
],
|
||||
'template_name' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 120,
|
||||
],
|
||||
'is_default' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
'status' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 1,
|
||||
],
|
||||
'page_size' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 20,
|
||||
'default' => 'A4',
|
||||
],
|
||||
'page_background_image' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
],
|
||||
'watermark_type' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 10,
|
||||
'default' => 'none',
|
||||
],
|
||||
'watermark_text' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
],
|
||||
'watermark_image' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
],
|
||||
'watermark_alpha' => [
|
||||
'type' => 'DECIMAL',
|
||||
'constraint' => '4,2',
|
||||
'default' => '0.12',
|
||||
],
|
||||
'header_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'first_page_header_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'content_prefix_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'content_suffix_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'footer_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'last_page_footer_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'patient_info_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'mpdf_prefix_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'mpdf_suffix_html' => [
|
||||
'type' => 'LONGTEXT',
|
||||
'null' => true,
|
||||
],
|
||||
'created_at DATETIME DEFAULT CURRENT_TIMESTAMP',
|
||||
'updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
|
||||
]);
|
||||
|
||||
$this->forge->addKey('id', true);
|
||||
$this->forge->addKey(['modality', 'status']);
|
||||
$this->forge->createTable('diagnosis_print_templates', true, ['ENGINE' => 'InnoDB', 'DEFAULT CHARSET' => 'utf8mb4']);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if ($this->hasTemplateTable()) {
|
||||
$this->forge->dropTable('diagnosis_print_templates', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddDiagnosisTemplateMarginsCm extends Migration
|
||||
{
|
||||
private function hasTemplateTable(): bool
|
||||
{
|
||||
$query = $this->db->query("SHOW TABLES LIKE 'diagnosis_print_templates'");
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
private function templateFields(): array
|
||||
{
|
||||
$fields = [];
|
||||
$query = $this->db->query("SHOW COLUMNS FROM diagnosis_print_templates");
|
||||
foreach ($query->getResultArray() as $row) {
|
||||
$fields[] = (string) ($row['Field'] ?? '');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if (! $this->hasTemplateTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->templateFields();
|
||||
|
||||
$add = [];
|
||||
if (! in_array('page_margin_top_cm', $fields, true)) {
|
||||
$add['page_margin_top_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '1.20',
|
||||
'after' => 'page_size',
|
||||
];
|
||||
}
|
||||
if (! in_array('page_margin_bottom_cm', $fields, true)) {
|
||||
$add['page_margin_bottom_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '1.20',
|
||||
'after' => 'page_margin_top_cm',
|
||||
];
|
||||
}
|
||||
if (! in_array('page_margin_left_cm', $fields, true)) {
|
||||
$add['page_margin_left_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '1.00',
|
||||
'after' => 'page_margin_bottom_cm',
|
||||
];
|
||||
}
|
||||
if (! in_array('page_margin_right_cm', $fields, true)) {
|
||||
$add['page_margin_right_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '1.00',
|
||||
'after' => 'page_margin_left_cm',
|
||||
];
|
||||
}
|
||||
if (! in_array('margin_header_cm', $fields, true)) {
|
||||
$add['margin_header_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '0.50',
|
||||
'after' => 'page_margin_right_cm',
|
||||
];
|
||||
}
|
||||
if (! in_array('margin_footer_cm', $fields, true)) {
|
||||
$add['margin_footer_cm'] = [
|
||||
'type' => 'decimal',
|
||||
'constraint' => '6,2',
|
||||
'null' => true,
|
||||
'default' => '1.50',
|
||||
'after' => 'margin_header_cm',
|
||||
];
|
||||
}
|
||||
|
||||
if (! empty($add)) {
|
||||
$this->forge->addColumn('diagnosis_print_templates', $add);
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (! $this->hasTemplateTable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->templateFields();
|
||||
$dropList = [
|
||||
'page_margin_top_cm',
|
||||
'page_margin_bottom_cm',
|
||||
'page_margin_left_cm',
|
||||
'page_margin_right_cm',
|
||||
'margin_header_cm',
|
||||
'margin_footer_cm',
|
||||
];
|
||||
|
||||
foreach ($dropList as $field) {
|
||||
if (in_array($field, $fields, true)) {
|
||||
$this->forge->dropColumn('diagnosis_print_templates', $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
app/Views/Setting/Template/diagnosis_print_settings.php
Normal file
362
app/Views/Setting/Template/diagnosis_print_settings.php
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
<?php
|
||||
$row = $row ?? [];
|
||||
$notice = (string) ($notice ?? '');
|
||||
$noticeType = (string) ($notice_type ?? 'success');
|
||||
$modality = (int) ($modality ?? 3);
|
||||
$columnsReady = (bool) ($columns_ready ?? false);
|
||||
$modalityList = $modality_list ?? [];
|
||||
$templates = $templates ?? [];
|
||||
$selectedTemplateId = (int) ($selected_template_id ?? 0);
|
||||
$hasSignatureImageColumn = (bool) ($has_signature_image_column ?? false);
|
||||
$modalityLabel = (string) ($modalityList[$modality] ?? 'Diagnosis');
|
||||
$panelTitle = $modalityLabel . ' Print Template';
|
||||
?>
|
||||
|
||||
<section class="content">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0"><?= esc($panelTitle) ?> Settings</h6>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="javascript:load_form_div('<?= base_url('setting/template/diagnosis_print_settings/' . $modality) ?>','maindiv','<?= esc($panelTitle) ?>');">Reset</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="diag_print_notice">
|
||||
<?php if ($notice !== ''): ?>
|
||||
<div class="alert alert-<?= esc($noticeType) ?> py-2 mb-3"><?= esc($notice) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (! $columnsReady): ?>
|
||||
<div class="alert alert-warning py-2 mb-3">
|
||||
Required table/columns are missing.
|
||||
Run migration: <b>php spark migrate</b>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="<?= base_url('setting/template/diagnosis_print_settings/' . $modality) ?>" enctype="multipart/form-data" id="diag_print_setting_form">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="template_id" value="<?= (int) ($row['id'] ?? $selectedTemplateId) ?>">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Modality</label>
|
||||
<select class="form-select form-select-sm" name="modality" onchange="changeDiagnosisModality(this.value)">
|
||||
<?php foreach ($modalityList as $key => $label): ?>
|
||||
<option value="<?= (int) $key ?>" <?= (int) $key === $modality ? 'selected' : '' ?>><?= esc($label) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label small">Template Name</label>
|
||||
<input type="text" class="form-control form-control-sm" name="template_name" value="<?= esc((string) ($row['template_name'] ?? '')) ?>" placeholder="e.g. Standard <?= esc($modalityLabel) ?> Template" required>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" name="is_default" id="is_default" value="1" <?= ((int) ($row['is_default'] ?? 0) === 1) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="is_default">Set as default template for this modality</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 mb-2">
|
||||
<label class="form-label small mb-1">Template List</label>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<?php foreach ($templates as $tpl): ?>
|
||||
<?php
|
||||
$tplId = (int) ($tpl['id'] ?? 0);
|
||||
$tplName = (string) ($tpl['template_name'] ?? ('Template ' . $tplId));
|
||||
$activeClass = ($tplId === (int) ($row['id'] ?? $selectedTemplateId)) ? 'btn-primary' : 'btn-outline-primary';
|
||||
?>
|
||||
<a class="btn btn-sm <?= $activeClass ?>" href="javascript:load_form_div('<?= base_url('setting/template/diagnosis_print_settings/' . $modality . '?template_id=' . $tplId) ?>','maindiv','<?= esc($panelTitle) ?>');"><?= esc($tplName) ?></a>
|
||||
<?php endforeach; ?>
|
||||
<a class="btn btn-sm btn-outline-success" href="javascript:load_form_div('<?= base_url('setting/template/diagnosis_print_settings/' . $modality . '?new=1') ?>','maindiv','<?= esc($panelTitle) ?>');">+ New Template</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="mb-2">1. Page Size, Page Background Image, Page Watermark</h6>
|
||||
<div class="alert alert-light border py-2 small mb-2">
|
||||
Legacy mapping: margin-top/bottom/left/right and margin-header/footer from old @page CSS are now configurable here in cm.
|
||||
Example old values: top 6.1, bottom 2.5, left 0.7, right 0.7, header 0.5, footer 1.5.
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Page Size</label>
|
||||
<select class="form-select form-select-sm" name="page_size">
|
||||
<?php $pageSize = strtoupper((string) ($row['page_size'] ?? 'A4')); ?>
|
||||
<option value="A4" <?= $pageSize === 'A4' ? 'selected' : '' ?>>A4</option>
|
||||
<option value="A4-L" <?= $pageSize === 'A4-L' ? 'selected' : '' ?>>A4 Landscape</option>
|
||||
<option value="LETTER" <?= $pageSize === 'LETTER' ? 'selected' : '' ?>>Letter</option>
|
||||
<option value="LEGAL" <?= $pageSize === 'LEGAL' ? 'selected' : '' ?>>Legal</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Top Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="page_margin_top_cm" value="<?= esc((string) ($row['page_margin_top_cm'] ?? '1.2')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Bottom Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="page_margin_bottom_cm" value="<?= esc((string) ($row['page_margin_bottom_cm'] ?? '1.2')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Left Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="page_margin_left_cm" value="<?= esc((string) ($row['page_margin_left_cm'] ?? '1.0')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Right Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="page_margin_right_cm" value="<?= esc((string) ($row['page_margin_right_cm'] ?? '1.0')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Header Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="margin_header_cm" value="<?= esc((string) ($row['margin_header_cm'] ?? '0.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Footer Margin (cm)</label>
|
||||
<input type="number" step="0.1" min="0" max="25" class="form-control form-control-sm" name="margin_footer_cm" value="<?= esc((string) ($row['margin_footer_cm'] ?? '1.5')) ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Page Background Image</label>
|
||||
<input type="file" class="form-control form-control-sm" name="page_background_image" accept=".png,.jpg,.jpeg,.webp,image/png,image/jpeg,image/webp">
|
||||
<?php if (! empty($row['page_background_image'])): ?>
|
||||
<small class="text-muted d-block mt-1">Current: <?= esc((string) $row['page_background_image']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" name="remove_background" id="remove_background" value="1">
|
||||
<label class="form-check-label" for="remove_background">Remove BG</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Watermark Type</label>
|
||||
<?php $watermarkType = (string) ($row['watermark_type'] ?? 'none'); ?>
|
||||
<select class="form-select form-select-sm" name="watermark_type">
|
||||
<option value="none" <?= $watermarkType === 'none' ? 'selected' : '' ?>>None</option>
|
||||
<option value="text" <?= $watermarkType === 'text' ? 'selected' : '' ?>>Text</option>
|
||||
<option value="image" <?= $watermarkType === 'image' ? 'selected' : '' ?>>Image</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Watermark Alpha</label>
|
||||
<input type="number" step="0.01" min="0.01" max="1" class="form-control form-control-sm" name="watermark_alpha" value="<?= esc((string) ($row['watermark_alpha'] ?? '0.12')) ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Watermark Text</label>
|
||||
<input type="text" class="form-control form-control-sm" name="watermark_text" value="<?= esc((string) ($row['watermark_text'] ?? '')) ?>" placeholder="CONFIDENTIAL">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Watermark Image</label>
|
||||
<input type="file" class="form-control form-control-sm" name="watermark_image" accept=".png,.jpg,.jpeg,.webp,image/png,image/jpeg,image/webp">
|
||||
<?php if (! empty($row['watermark_image'])): ?>
|
||||
<small class="text-muted d-block mt-1">Current: <?= esc((string) $row['watermark_image']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" name="remove_watermark_image" id="remove_watermark_image" value="1">
|
||||
<label class="form-check-label" for="remove_watermark_image">Remove WM</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Signature Image (for {{signature_image_url}})</label>
|
||||
<input type="file" class="form-control form-control-sm" name="signature_image" accept=".png,.jpg,.jpeg,.webp,image/png,image/jpeg,image/webp" <?= $hasSignatureImageColumn ? '' : 'disabled' ?>>
|
||||
<?php if (! $hasSignatureImageColumn): ?>
|
||||
<small class="text-danger d-block mt-1">Signature column missing in DB. Please refresh this page once.</small>
|
||||
<?php elseif (! empty($row['signature_image'])): ?>
|
||||
<small class="text-muted d-block mt-1">Current: <?= esc((string) $row['signature_image']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" name="remove_signature_image" id="remove_signature_image" value="1" <?= $hasSignatureImageColumn ? '' : 'disabled' ?>>
|
||||
<label class="form-check-label" for="remove_signature_image">Remove Sign</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mb-2">2. Header</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Header HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="header_html" rows="6" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['header_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mb-2 mt-3">3. Header for First Page</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">First Page Header HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="first_page_header_html" rows="5" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['first_page_header_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<h6 class="mb-0">4. Content Part</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#pdfTemplateTipsModal" title="Template Help Tips">
|
||||
? Help Tips
|
||||
</button>
|
||||
</div>
|
||||
<div class="alert alert-secondary py-2 small mb-2">
|
||||
Main content customization with token support. Click <b>Help Tips</b> for available tokens and mPDF constants/snippets.
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Patient Info HTML Template (tokens supported)</label>
|
||||
<textarea class="form-control" name="patient_info_html" rows="8" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['patient_info_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Content Prefix HTML (before report body)</label>
|
||||
<textarea class="form-control" name="content_prefix_html" rows="4" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['content_prefix_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Content Suffix HTML (after report body)</label>
|
||||
<textarea class="form-control" name="content_suffix_html" rows="4" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['content_suffix_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h6 class="mb-2">5. Footer</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Footer HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="footer_html" rows="5" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['footer_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mb-2 mt-3">6. Footer for Last Page</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Last Page Footer HTML / mPDF tags</label>
|
||||
<textarea class="form-control" name="last_page_footer_html" rows="5" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['last_page_footer_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mb-2 mt-3">Advanced mPDF Prefix/Suffix (Optional)</h6>
|
||||
<div class="alert alert-info py-2 small mb-2">
|
||||
Optional raw blocks. Use if you need extra mPDF tags beyond the 6 template sections.
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label small">mPDF Prefix HTML (before content)</label>
|
||||
<textarea class="form-control" name="mpdf_prefix_html" rows="8" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['mpdf_prefix_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label small">mPDF Suffix HTML (after content)</label>
|
||||
<textarea class="form-control" name="mpdf_suffix_html" rows="5" style="font-family:Consolas,Monaco,monospace;"><?= esc((string) ($row['mpdf_suffix_html'] ?? '')) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm" <?= ! $columnsReady ? 'disabled' : '' ?>>Save Settings</button>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="pdfTemplateTipsModal" tabindex="-1" aria-labelledby="pdfTemplateTipsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title" id="pdfTemplateTipsModalLabel">PDF Template Help Tips</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body small">
|
||||
<p class="mb-2"><b>Patient Tokens</b></p>
|
||||
<p style="font-family:Consolas,Monaco,monospace; line-height:1.8;">
|
||||
{{invoice_code}}, {{patient_name}}, {{relative_type}}, {{relative_name}}, {{relative}}, {{age}}, {{gender}}, {{age_sex}}, {{uhid}}, {{collected_time}}, {{reported_time}}, {{printed_time}}, {{report_title}}
|
||||
</p>
|
||||
<p style="font-family:Consolas,Monaco,monospace; line-height:1.8;">
|
||||
{{hospital_logo_url}}, {{signature_image_url}}, {{doctor_name}}, {{doctor_education}}, {{technician_name}}
|
||||
</p>
|
||||
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Signature image usage: <img src="{{signature_image_url}}" width="100px" /></p>
|
||||
|
||||
<p class="mb-2 mt-3"><b>mPDF Constants / Tags (use in Prefix or Suffix)</b></p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Page No: {PAGENO}</p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Total Pages: {nbpg}</p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Page Break: <pagebreak /></p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Page Size (A4): <style>@page { size: A4; }</style></p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Page Size (Landscape): <style>@page { size: A4-L; }</style></p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Header Tag: <htmlpageheader name="myHeader">...</htmlpageheader></p>
|
||||
<p class="mb-1" style="font-family:Consolas,Monaco,monospace;">Footer Tag: <htmlpagefooter name="myFooter">...</htmlpagefooter></p>
|
||||
|
||||
<p class="mb-2 mt-3"><b>Quick Footer Example</b></p>
|
||||
<pre class="bg-light border rounded p-2 mb-0" style="font-family:Consolas,Monaco,monospace; white-space:pre-wrap;"><htmlpagefooter name="myFooter" style="display:none">
|
||||
<div style="text-align:right; font-size:10px;">Page {PAGENO} of {nbpg}</div>
|
||||
</htmlpagefooter>
|
||||
<sethtmlpagefooter name="myFooter" value="on" /></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var form = document.getElementById('diag_print_setting_form');
|
||||
var noticeBox = document.getElementById('diag_print_notice');
|
||||
|
||||
function showNotice(type, message) {
|
||||
if (!noticeBox) {
|
||||
return;
|
||||
}
|
||||
var safeType = (type === 'success') ? 'success' : 'danger';
|
||||
var safeText = String(message || '').replace(/</g, '<').replace(/>/g, '>');
|
||||
noticeBox.innerHTML = '<div class="alert alert-' + safeType + ' py-2 mb-3">' + safeText + '</div>';
|
||||
}
|
||||
|
||||
window.changeDiagnosisModality = function (value) {
|
||||
var selected = parseInt(value || 0, 10);
|
||||
if (!selected) {
|
||||
return;
|
||||
}
|
||||
var url = '<?= base_url('setting/template/diagnosis_print_settings') ?>/' + selected;
|
||||
if (typeof load_form_div === 'function') {
|
||||
load_form_div(url, 'maindiv', '<?= esc($panelTitle) ?>');
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var submitBtn = form.querySelector('button[type="submit"]');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: new FormData(form),
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function (result) {
|
||||
var status = (result && result.notice_type) ? result.notice_type : 'danger';
|
||||
var message = (result && result.notice) ? result.notice : 'Unable to save settings.';
|
||||
showNotice(status, message);
|
||||
|
||||
if (result && result.csrfName && result.csrfHash) {
|
||||
var csrfInput = form.querySelector('input[name="' + result.csrfName + '"]');
|
||||
if (csrfInput) {
|
||||
csrfInput.value = result.csrfHash;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
showNotice('danger', 'Save failed: ' + error.message);
|
||||
})
|
||||
.finally(function () {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
39
app/Views/diagnosis/pdf_compiled_report.php
Normal file
39
app/Views/diagnosis/pdf_compiled_report.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; color: #111; font-size: 12px; margin: 0; padding: 0; }
|
||||
.section-title { font-size: 20px; font-weight: 700; text-align: center; margin: 4px 0 8px; }
|
||||
table.meta-table { width: 100%; border-collapse: collapse; margin-bottom: 8px; }
|
||||
table.meta-table td { vertical-align: top; padding: 0 4px; font-size: 11px; }
|
||||
.label { font-weight: 700; display: inline-block; min-width: 72px; }
|
||||
.divider { border-top: 1px solid #999; margin: 6px 0 8px; }
|
||||
.report-content { font-size: 12px; line-height: 1.45; }
|
||||
.plain-header { margin-bottom: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$patientName = trim((string) ($patient->p_fname ?? ''));
|
||||
$relativeName = trim((string) ($patient->p_relative ?? ''));
|
||||
$relativeText = trim((string) ($patient->p_rname ?? ''));
|
||||
$plainHeaderHtml = (string) ($printSetting['plain_header_html'] ?? '');
|
||||
$title = trim((string) ($itemType->group_desc ?? 'Compiled Report'));
|
||||
?>
|
||||
|
||||
<?php if (($isPlainPaper ?? false) && trim($plainHeaderHtml) !== ''): ?>
|
||||
<div class="plain-header"><?= $plainHeaderHtml ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="section-title"><?= esc($title) ?></div>
|
||||
|
||||
<?php $patientInfoHtml = (string) ($patientInfoHtml ?? ''); ?>
|
||||
<?php if ($patientInfoHtml !== ''): ?>
|
||||
<?= $patientInfoHtml ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="report-content"><?= $reportHtml ?? '' ?></div>
|
||||
</body>
|
||||
</html>
|
||||
42
app/Views/diagnosis/pdf_single_report.php
Normal file
42
app/Views/diagnosis/pdf_single_report.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; color: #111; font-size: 12px; margin: 0; padding: 0; }
|
||||
.letterhead { margin-bottom: 8px; }
|
||||
.letterhead-title { font-size: 24px; font-weight: 700; line-height: 1.1; }
|
||||
.letterhead-sub { font-size: 11px; color: #222; line-height: 1.25; }
|
||||
.section-title { font-size: 20px; font-weight: 700; text-align: center; margin: 4px 0 8px; }
|
||||
table.meta-table { width: 100%; border-collapse: collapse; margin-bottom: 8px; }
|
||||
table.meta-table td { vertical-align: top; padding: 0 4px; font-size: 11px; }
|
||||
.label { font-weight: 700; display: inline-block; min-width: 72px; }
|
||||
.divider { border-top: 1px solid #999; margin: 6px 0 8px; }
|
||||
.report-content { font-size: 12px; line-height: 1.45; }
|
||||
.plain-header { margin-bottom: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$patientName = (string) ($patientName ?? trim((string) ($report->p_fname ?? '')));
|
||||
$relativeName = (string) ($relativeName ?? trim((string) ($report->p_relative ?? '')));
|
||||
$relativeText = (string) ($relativeText ?? trim((string) ($report->p_rname ?? '')));
|
||||
$repoTitle = (string) ($repoTitle ?? trim((string) ($report->report_name ?? $report->repo_title ?? $report->RepoGrp ?? 'Report')));
|
||||
$plainHeaderHtml = (string) ($printSetting['plain_header_html'] ?? '');
|
||||
$patientInfoHtml = (string) ($patientInfoHtml ?? '');
|
||||
?>
|
||||
|
||||
<?php if (($isPlainPaper ?? false) && trim($plainHeaderHtml) !== ''): ?>
|
||||
<div class="plain-header"><?= $plainHeaderHtml ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<?php if ($patientInfoHtml !== ''): ?>
|
||||
<?= $patientInfoHtml ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="section-title"><?= esc($repoTitle) ?></div>
|
||||
<div class="report-content"><?= $reportHtml ?? '' ?></div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user