feat: upgrade IPD Document Master to OPD-style print builder

- Add paper print settings to IPD templates (page size/custom size/margins)\n- Add Header HTML, Footer HTML, and Style (CSS) support\n- Update IPD PDF renderer to honor template paper settings and header/footer\n- Extend IPD placeholders with logo/QR support: {{H_logo}}, {{H_logo_abs}}, {{hospital_logo_html}}, {{qr_content}}, {{qr_code_html}}\n- Improve token rendering with robust curly placeholder parser for body/header/footer\n- Keep legacy sticker forms aligned with custom label dimensions and migrate old default rows
This commit is contained in:
DevSoftTech Server 2026-04-08 07:56:43 +05:30
parent 503c26a70f
commit 1a2dbf5366
3 changed files with 376 additions and 5 deletions

View File

@ -861,8 +861,26 @@ class Ipd extends BaseController
$html = view('billing/ipd/ipd_form_print', $data);
$customTemplate = $this->getActiveIpdDocumentTemplate($formNo);
$templatePageSize = '';
$templateCustomWidth = 210;
$templateCustomHeight = 297;
$templateMarginTop = 0.8;
$templateMarginBottom = 0.8;
$templateMarginLeft = 0.8;
$templateMarginRight = 0.8;
$templateMarginHeader = 0.5;
$templateMarginFooter = 0.5;
if ($customTemplate !== null) {
$html = $this->renderIpdDocumentTemplateHtml($customTemplate, $data);
$templatePageSize = strtoupper(trim((string) ($customTemplate['page_size'] ?? '')));
$templateCustomWidth = (int) ($customTemplate['custom_width_mm'] ?? 210);
$templateCustomHeight = (int) ($customTemplate['custom_height_mm'] ?? 297);
$templateMarginTop = (float) ($customTemplate['page_margin_top_cm'] ?? 0.8);
$templateMarginBottom = (float) ($customTemplate['page_margin_bottom_cm'] ?? 0.8);
$templateMarginLeft = (float) ($customTemplate['page_margin_left_cm'] ?? 0.8);
$templateMarginRight = (float) ($customTemplate['page_margin_right_cm'] ?? 0.8);
$templateMarginHeader = (float) ($customTemplate['margin_header_cm'] ?? 0.5);
$templateMarginFooter = (float) ($customTemplate['margin_footer_cm'] ?? 0.5);
}
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
@ -876,12 +894,33 @@ class Ipd extends BaseController
'margin_bottom' => 8,
'margin_left' => 8,
'margin_right' => 8,
'margin_header' => 5,
'margin_footer' => 5,
'default_font' => 'freeserif',
'autoScriptToLang' => true,
'autoLangToFont' => true,
];
if (in_array($formNo, [10, 11], true)) {
if ($customTemplate !== null && $templatePageSize !== '') {
if ($templatePageSize === 'CUSTOM') {
$templateCustomWidth = max(20, min(600, $templateCustomWidth));
$templateCustomHeight = max(20, min(1000, $templateCustomHeight));
$mpdfConfig['format'] = [$templateCustomWidth, $templateCustomHeight];
} elseif ($templatePageSize === 'A4-L') {
$mpdfConfig['format'] = 'A4';
$mpdfConfig['orientation'] = 'L';
} else {
$allowedFormats = ['A4', 'A5', 'A6', 'LETTER', 'LEGAL'];
$mpdfConfig['format'] = in_array($templatePageSize, $allowedFormats, true) ? $templatePageSize : 'A4';
}
$mpdfConfig['margin_top'] = max(0, min(250, (int) round($templateMarginTop * 10)));
$mpdfConfig['margin_bottom'] = max(0, min(250, (int) round($templateMarginBottom * 10)));
$mpdfConfig['margin_left'] = max(0, min(250, (int) round($templateMarginLeft * 10)));
$mpdfConfig['margin_right'] = max(0, min(250, (int) round($templateMarginRight * 10)));
$mpdfConfig['margin_header'] = max(0, min(250, (int) round($templateMarginHeader * 10)));
$mpdfConfig['margin_footer'] = max(0, min(250, (int) round($templateMarginFooter * 10)));
} elseif (in_array($formNo, [10, 11], true)) {
$mpdfConfig['format'] = $formNo === 10 ? [50.8, 152.4] : [50.8, 203.2];
$mpdfConfig['margin_top'] = 2;
$mpdfConfig['margin_bottom'] = 2;
@ -2278,7 +2317,12 @@ class Ipd extends BaseController
}
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : 'Hospital';
$hospitalAddress = defined('H_address_1') ? (string) constant('H_address_1') : '';
$hospitalAddress1 = defined('H_address_1') ? (string) constant('H_address_1') : '';
$hospitalAddress2 = defined('H_address_2') ? (string) constant('H_address_2') : '';
$hospitalAddress = trim($hospitalAddress1 . ', ' . $hospitalAddress2, ', ');
$hospitalPhone = defined('H_phone_No') ? (string) constant('H_phone_No') : '';
$hospitalEmail = defined('H_Email') ? (string) constant('H_Email') : '';
$hospitalLogo = defined('H_logo') ? (string) constant('H_logo') : '';
return [
'form_no' => $formNo,
@ -2289,6 +2333,11 @@ class Ipd extends BaseController
'insurance_name' => $insuranceName,
'hospital_name' => $hospitalName,
'hospital_address' => $hospitalAddress,
'hospital_address_1' => $hospitalAddress1,
'hospital_address_2' => $hospitalAddress2,
'hospital_phone' => $hospitalPhone,
'hospital_email' => $hospitalEmail,
'hospital_logo' => $hospitalLogo,
'generated_at' => date('d-m-Y h:i A'),
];
}
@ -2300,7 +2349,7 @@ class Ipd extends BaseController
}
$row = $this->db->table('ipd_document_templates')
->select('id, form_no, template_name, template_html')
->select('id, form_no, template_name, page_size, custom_width_mm, custom_height_mm, page_margin_top_cm, page_margin_bottom_cm, page_margin_left_cm, page_margin_right_cm, margin_header_cm, margin_footer_cm, header_html, footer_html, template_css, template_html')
->where('form_no', $formNo)
->where('status', 1)
->orderBy('id', 'DESC')
@ -2338,24 +2387,100 @@ class Ipd extends BaseController
$ageGender = '-';
}
$qrContent = trim(
(string) ($person->p_code ?? '')
. ':' . (string) ($ipd->ipd_code ?? '')
. ':P-' . date('Y-m-d H:i:s')
. ':C-' . (string) ($person->p_fname ?? '')
, ':');
$hospitalLogo = trim((string) ($data['hospital_logo'] ?? ''));
$hospitalLogoAbs = $hospitalLogo !== '' ? (FCPATH . 'assets/images/' . $hospitalLogo) : '';
$hospitalLogoHtml = $hospitalLogoAbs !== ''
? '<img style="width: 75px;vertical-align: top;" src="' . esc($hospitalLogoAbs, 'attr') . '" />'
: '';
$qrCodeHtml = $qrContent !== ''
? '<barcode code="' . esc($qrContent, 'attr') . '" size="0.8" type="QR" error="M" class="barcode" />'
: '';
$replacements = [
'{{FORM_TITLE}}' => esc((string) ($formMeta['title'] ?? 'IPD Form')),
'{{HOSPITAL_NAME}}' => esc((string) ($data['hospital_name'] ?? 'Hospital')),
'{{HOSPITAL_ADDRESS}}' => esc((string) ($data['hospital_address'] ?? '')),
'{{H_Name}}' => esc((string) ($data['hospital_name'] ?? 'Hospital')),
'{{H_address_1}}' => esc((string) ($data['hospital_address_1'] ?? '')),
'{{H_address_2}}' => esc((string) ($data['hospital_address_2'] ?? '')),
'{{H_phone_No}}' => esc((string) ($data['hospital_phone'] ?? '')),
'{{H_Email}}' => esc((string) ($data['hospital_email'] ?? '')),
'{{H_logo}}' => esc($hospitalLogo),
'{{H_logo_abs}}' => esc($hospitalLogoAbs),
'{{hospital_logo_html}}' => $hospitalLogoHtml,
'{{HOSPITAL_LOGO_HTML}}' => $hospitalLogoHtml,
'{{PATIENT_NAME}}' => esc($patientName),
'{{pName}}' => esc($patientName),
'{{AGE}}' => esc($age),
'{{GENDER}}' => esc($gender),
'{{AGE_GENDER}}' => esc($ageGender),
'{{age_sex}}' => esc($ageGender),
'{{UHID}}' => esc((string) ($person->p_code ?? '-')),
'{{IPD_CODE}}' => esc((string) ($ipd->ipd_code ?? '-')),
'{{ADMIT_DATE}}' => esc((string) ($ipd->str_register_date ?? ($ipd->register_date ?? '-'))),
'{{DOCTORS}}' => esc(! empty($doctorNames) ? implode(', ', $doctorNames) : '-'),
'{{doctor_name}}' => esc(! empty($doctorNames) ? implode(', ', $doctorNames) : '-'),
'{{doctor_sign_html}}' => '',
'{{phoneno}}' => esc((string) ($person->mphone1 ?? '-')),
'{{INSURANCE_NAME}}' => esc((string) ($data['insurance_name'] ?? 'Direct')),
'{{CURRENT_DATE}}' => esc(date('d-m-Y')),
'{{CURRENT_DATETIME}}' => esc((string) ($data['generated_at'] ?? date('d-m-Y h:i A'))),
'{{print_time}}' => esc((string) ($data['generated_at'] ?? date('d-m-Y h:i A'))),
'{{qr_content}}' => esc($qrContent),
'{{QR_CONTENT}}' => esc($qrContent),
'{{qr_code_html}}' => $qrCodeHtml,
'{{QR_CODE_HTML}}' => $qrCodeHtml,
];
$html = strtr($templateHtml, $replacements);
$headerHtml = trim((string) ($templateRow['header_html'] ?? ''));
$footerHtml = trim((string) ($templateRow['footer_html'] ?? ''));
$templateCss = trim((string) ($templateRow['template_css'] ?? ''));
$bodyHtml = $this->applyIpdTemplateTokens($templateHtml, $replacements);
$renderedHeader = $headerHtml !== '' ? $this->applyIpdTemplateTokens($headerHtml, $replacements) : '';
$renderedFooter = $footerHtml !== '' ? $this->applyIpdTemplateTokens($footerHtml, $replacements) : '';
$pageTop = (float) ($templateRow['page_margin_top_cm'] ?? 0.8);
$pageBottom = (float) ($templateRow['page_margin_bottom_cm'] ?? 0.8);
$pageLeft = (float) ($templateRow['page_margin_left_cm'] ?? 0.8);
$pageRight = (float) ($templateRow['page_margin_right_cm'] ?? 0.8);
$marginHeader = (float) ($templateRow['margin_header_cm'] ?? 0.5);
$marginFooter = (float) ($templateRow['margin_footer_cm'] ?? 0.5);
$pageStyle = '<style>@page {'
. 'margin-top:' . max(0, min(25, $pageTop)) . 'cm;'
. 'margin-bottom:' . max(0, min(25, $pageBottom)) . 'cm;'
. 'margin-left:' . max(0, min(25, $pageLeft)) . 'cm;'
. 'margin-right:' . max(0, min(25, $pageRight)) . 'cm;'
. 'margin-header:' . max(0, min(25, $marginHeader)) . 'cm;'
. 'margin-footer:' . max(0, min(25, $marginFooter)) . 'cm;'
. ($renderedHeader !== '' ? 'header: html_myHeader;' : '')
. ($renderedFooter !== '' ? 'footer: html_myFooter;' : '')
. '}</style>';
$userStyle = $templateCss !== '' ? ('<style>' . $templateCss . '</style>') : '';
$headerBlock = '';
if ($renderedHeader !== '') {
$headerBlock = preg_match('/<\s*htmlpageheader\b/i', $renderedHeader)
? $renderedHeader
: ('<htmlpageheader name="myHeader">' . $renderedHeader . '</htmlpageheader>');
}
$footerBlock = '';
if ($renderedFooter !== '') {
$footerBlock = preg_match('/<\s*htmlpagefooter\b/i', $renderedFooter)
? $renderedFooter
: ('<htmlpagefooter name="myFooter">' . $renderedFooter . '</htmlpagefooter>');
}
$html = $pageStyle . $userStyle . $headerBlock . $footerBlock . $bodyHtml;
// Force a Unicode-capable fallback stack so Devanagari text renders in mPDF.
$fontStyle = '<style>body,table,td,th,p,span,div{font-family:freeserif,dejavusans,sans-serif !important;}</style>';
@ -2363,6 +2488,57 @@ class Ipd extends BaseController
return $fontStyle . $html;
}
/**
* @param array<string, string> $replacements
*/
private function applyIpdTemplateTokens(string $html, array $replacements): string
{
if ($html === '') {
return '';
}
$tokenMap = [];
foreach ($replacements as $token => $value) {
$cleanToken = trim($token, '{} ');
if ($cleanToken === '') {
continue;
}
$tokenMap[$cleanToken] = $value;
$lower = strtolower($cleanToken);
if (! array_key_exists($lower, $tokenMap)) {
$tokenMap[$lower] = $value;
}
$upper = strtoupper($cleanToken);
if (! array_key_exists($upper, $tokenMap)) {
$tokenMap[$upper] = $value;
}
}
return (string) preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', static function (array $match) use ($tokenMap) {
$name = (string) ($match[1] ?? '');
if ($name === '') {
return (string) ($match[0] ?? '');
}
if (array_key_exists($name, $tokenMap)) {
return $tokenMap[$name];
}
$lower = strtolower($name);
if (array_key_exists($lower, $tokenMap)) {
return $tokenMap[$lower];
}
$upper = strtoupper($name);
if (array_key_exists($upper, $tokenMap)) {
return $tokenMap[$upper];
}
return (string) ($match[0] ?? '');
}, $html);
}
private function calculateCashBalanceTotals(array $rows): array
{
$totalPaid = 0.0;

View File

@ -1366,6 +1366,7 @@ HTML;
private function ensureIpdDocumentTemplateTable(): void
{
if ($this->db->tableExists('ipd_document_templates')) {
$this->ensureIpdDocumentTemplateColumns();
return;
}
@ -1373,6 +1374,18 @@ HTML;
id INT NOT NULL AUTO_INCREMENT,
form_no INT NOT NULL,
template_name VARCHAR(160) NOT NULL,
page_size VARCHAR(16) NOT NULL DEFAULT 'A4',
custom_width_mm INT NOT NULL DEFAULT 210,
custom_height_mm INT NOT NULL DEFAULT 297,
page_margin_top_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80,
page_margin_bottom_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80,
page_margin_left_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80,
page_margin_right_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80,
margin_header_cm DECIMAL(5,2) NOT NULL DEFAULT 0.50,
margin_footer_cm DECIMAL(5,2) NOT NULL DEFAULT 0.50,
header_html LONGTEXT NULL,
footer_html LONGTEXT NULL,
template_css LONGTEXT NULL,
template_html LONGTEXT NOT NULL,
status TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@ -1383,6 +1396,40 @@ HTML;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$this->db->query($sql);
$this->ensureIpdDocumentTemplateColumns();
}
private function ensureIpdDocumentTemplateColumns(): void
{
if (! $this->db->tableExists('ipd_document_templates')) {
return;
}
$columns = [
'page_size' => "ALTER TABLE ipd_document_templates ADD COLUMN page_size VARCHAR(16) NOT NULL DEFAULT 'A4' AFTER template_name",
'custom_width_mm' => "ALTER TABLE ipd_document_templates ADD COLUMN custom_width_mm INT NOT NULL DEFAULT 210 AFTER page_size",
'custom_height_mm' => "ALTER TABLE ipd_document_templates ADD COLUMN custom_height_mm INT NOT NULL DEFAULT 297 AFTER custom_width_mm",
'page_margin_top_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN page_margin_top_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80 AFTER custom_height_mm",
'page_margin_bottom_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN page_margin_bottom_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80 AFTER page_margin_top_cm",
'page_margin_left_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN page_margin_left_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80 AFTER page_margin_bottom_cm",
'page_margin_right_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN page_margin_right_cm DECIMAL(5,2) NOT NULL DEFAULT 0.80 AFTER page_margin_left_cm",
'margin_header_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN margin_header_cm DECIMAL(5,2) NOT NULL DEFAULT 0.50 AFTER page_margin_right_cm",
'margin_footer_cm' => "ALTER TABLE ipd_document_templates ADD COLUMN margin_footer_cm DECIMAL(5,2) NOT NULL DEFAULT 0.50 AFTER margin_header_cm",
'header_html' => "ALTER TABLE ipd_document_templates ADD COLUMN header_html LONGTEXT NULL AFTER margin_footer_cm",
'footer_html' => "ALTER TABLE ipd_document_templates ADD COLUMN footer_html LONGTEXT NULL AFTER header_html",
'template_css' => "ALTER TABLE ipd_document_templates ADD COLUMN template_css LONGTEXT NULL AFTER footer_html",
];
foreach ($columns as $col => $sql) {
try {
$exists = $this->db->query("SHOW COLUMNS FROM ipd_document_templates LIKE '" . $col . "'")->getRowArray();
if (empty($exists)) {
$this->db->query($sql);
}
} catch (\Throwable $e) {
// Keep template screen usable even if schema alter fails in restricted env.
}
}
}
private function defaultIpdDocumentTemplates(): array
@ -1616,10 +1663,35 @@ HTML,
$table->insert([
'form_no' => $formNo,
'template_name' => $templateName,
'page_size' => in_array($formNo, [10, 11], true) ? 'CUSTOM' : 'A4',
'custom_width_mm' => $formNo === 10 ? 51 : ($formNo === 11 ? 51 : 210),
'custom_height_mm' => $formNo === 10 ? 152 : ($formNo === 11 ? 203 : 297),
'page_margin_top_cm' => in_array($formNo, [10, 11], true) ? 0.2 : 0.8,
'page_margin_bottom_cm' => in_array($formNo, [10, 11], true) ? 0.2 : 0.8,
'page_margin_left_cm' => in_array($formNo, [10, 11], true) ? 0.2 : 0.8,
'page_margin_right_cm' => in_array($formNo, [10, 11], true) ? 0.2 : 0.8,
'margin_header_cm' => 0.5,
'margin_footer_cm' => 0.5,
'template_html' => (string) ($row['template_html'] ?? ''),
'status' => 1,
]);
}
// Upgrade legacy sticker templates created before page-size columns existed.
$this->db->table('ipd_document_templates')
->whereIn('form_no', [10, 11])
->where('page_size', 'A4')
->where('custom_width_mm', 210)
->where('custom_height_mm', 297)
->where('template_name LIKE', 'Legacy Sticker%')
->set('page_size', 'CUSTOM')
->set('custom_width_mm', 'CASE WHEN form_no=10 THEN 51 ELSE 51 END', false)
->set('custom_height_mm', 'CASE WHEN form_no=10 THEN 152 ELSE 203 END', false)
->set('page_margin_top_cm', 0.2)
->set('page_margin_bottom_cm', 0.2)
->set('page_margin_left_cm', 0.2)
->set('page_margin_right_cm', 0.2)
->update();
}
public function ipd_document_templates()
@ -1638,6 +1710,24 @@ HTML,
$formNo = (int) ($this->request->getPost('form_no') ?? 0);
$templateName = trim((string) ($this->request->getPost('template_name') ?? ''));
$templateHtml = (string) ($this->request->getPost('template_html') ?? '');
$headerHtml = (string) ($this->request->getPost('header_html') ?? '');
$footerHtml = (string) ($this->request->getPost('footer_html') ?? '');
$templateCss = (string) ($this->request->getPost('template_css') ?? '');
$pageSize = strtoupper(trim((string) ($this->request->getPost('page_size') ?? 'A4')));
if (! in_array($pageSize, ['A4', 'A4-L', 'A5', 'A6', 'LETTER', 'LEGAL', 'CUSTOM'], true)) {
$pageSize = 'A4';
}
$customWidthMm = (int) ($this->request->getPost('custom_width_mm') ?? 210);
$customHeightMm = (int) ($this->request->getPost('custom_height_mm') ?? 297);
$customWidthMm = max(20, min(600, $customWidthMm));
$customHeightMm = max(20, min(1000, $customHeightMm));
$marginTop = max(0, min(25, (float) ($this->request->getPost('page_margin_top_cm') ?? 0.8)));
$marginBottom = max(0, min(25, (float) ($this->request->getPost('page_margin_bottom_cm') ?? 0.8)));
$marginLeft = max(0, min(25, (float) ($this->request->getPost('page_margin_left_cm') ?? 0.8)));
$marginRight = max(0, min(25, (float) ($this->request->getPost('page_margin_right_cm') ?? 0.8)));
$marginHeader = max(0, min(25, (float) ($this->request->getPost('margin_header_cm') ?? 0.5)));
$marginFooter = max(0, min(25, (float) ($this->request->getPost('margin_footer_cm') ?? 0.5)));
$status = (int) ($this->request->getPost('status') ?? 1) === 1 ? 1 : 0;
if (! in_array($formNo, [1, 3, 5, 8, 9, 10, 11], true)) {
@ -1651,6 +1741,18 @@ HTML,
$data = [
'form_no' => $formNo,
'template_name' => $templateName,
'page_size' => $pageSize,
'custom_width_mm' => $customWidthMm,
'custom_height_mm' => $customHeightMm,
'page_margin_top_cm' => $marginTop,
'page_margin_bottom_cm' => $marginBottom,
'page_margin_left_cm' => $marginLeft,
'page_margin_right_cm' => $marginRight,
'margin_header_cm' => $marginHeader,
'margin_footer_cm' => $marginFooter,
'header_html' => $headerHtml,
'footer_html' => $footerHtml,
'template_css' => $templateCss,
'template_html' => $templateHtml,
'status' => $status,
];

View File

@ -8,6 +8,18 @@ $editId = (int) ($edit['id'] ?? 0);
$formNo = (int) ($edit['form_no'] ?? 1);
$templateName = (string) ($edit['template_name'] ?? '');
$templateHtml = (string) ($edit['template_html'] ?? '<h3>{{FORM_TITLE}}</h3><p>{{PATIENT_NAME}}</p>');
$headerHtml = (string) ($edit['header_html'] ?? '');
$footerHtml = (string) ($edit['footer_html'] ?? '');
$templateCss = (string) ($edit['template_css'] ?? '');
$pageSize = strtoupper((string) ($edit['page_size'] ?? 'A4'));
$customWidthMm = (int) ($edit['custom_width_mm'] ?? 210);
$customHeightMm = (int) ($edit['custom_height_mm'] ?? 297);
$marginTop = (string) ($edit['page_margin_top_cm'] ?? '0.8');
$marginBottom = (string) ($edit['page_margin_bottom_cm'] ?? '0.8');
$marginLeft = (string) ($edit['page_margin_left_cm'] ?? '0.8');
$marginRight = (string) ($edit['page_margin_right_cm'] ?? '0.8');
$marginHeader = (string) ($edit['margin_header_cm'] ?? '0.5');
$marginFooter = (string) ($edit['margin_footer_cm'] ?? '0.5');
$status = (int) ($edit['status'] ?? 1);
$formMap = [
@ -36,7 +48,11 @@ $formMap = [
Placeholders: <code>{{FORM_TITLE}}</code>, <code>{{HOSPITAL_NAME}}</code>, <code>{{HOSPITAL_ADDRESS}}</code>,
<code>{{PATIENT_NAME}}</code>, <code>{{AGE}}</code>, <code>{{GENDER}}</code>, <code>{{AGE_GENDER}}</code>, <code>{{UHID}}</code>,
<code>{{IPD_CODE}}</code>, <code>{{ADMIT_DATE}}</code>, <code>{{DOCTORS}}</code>, <code>{{INSURANCE_NAME}}</code>,
<code>{{CURRENT_DATE}}</code>, <code>{{CURRENT_DATETIME}}</code>.
<code>{{CURRENT_DATE}}</code>, <code>{{CURRENT_DATETIME}}</code>,
<code>{{H_Name}}</code>, <code>{{H_address_1}}</code>, <code>{{H_address_2}}</code>, <code>{{H_phone_No}}</code>, <code>{{H_Email}}</code>,
<code>{{H_logo}}</code>, <code>{{H_logo_abs}}</code>, <code>{{hospital_logo_html}}</code>,
<code>{{doctor_name}}</code>, <code>{{doctor_sign_html}}</code>, <code>{{pName}}</code>, <code>{{age_sex}}</code>, <code>{{phoneno}}</code>, <code>{{print_time}}</code>,
<code>{{qr_content}}</code>, <code>{{qr_code_html}}</code>.
</div>
<div class="alert alert-warning py-2 small">
@ -67,6 +83,66 @@ $formMap = [
<option value="0" <?= $status === 0 ? 'selected' : '' ?>>Inactive</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label small">Page Size</label>
<select name="page_size" id="ipd_page_size" class="form-select form-select-sm">
<option value="A4" <?= $pageSize === 'A4' ? 'selected' : '' ?>>A4</option>
<option value="A4-L" <?= $pageSize === 'A4-L' ? 'selected' : '' ?>>A4 Landscape</option>
<option value="A5" <?= $pageSize === 'A5' ? 'selected' : '' ?>>A5</option>
<option value="A6" <?= $pageSize === 'A6' ? 'selected' : '' ?>>A6</option>
<option value="LETTER" <?= $pageSize === 'LETTER' ? 'selected' : '' ?>>Letter</option>
<option value="LEGAL" <?= $pageSize === 'LEGAL' ? 'selected' : '' ?>>Legal</option>
<option value="CUSTOM" <?= $pageSize === 'CUSTOM' ? 'selected' : '' ?>>Custom (mm)</option>
</select>
</div>
<div class="col-md-3" id="ipd_custom_width_wrap" style="display:none;">
<label class="form-label small">Custom Width (mm)</label>
<input type="number" name="custom_width_mm" class="form-control form-control-sm" value="<?= esc((string) $customWidthMm) ?>" min="20" max="600" step="1">
</div>
<div class="col-md-3" id="ipd_custom_height_wrap" style="display:none;">
<label class="form-label small">Custom Height (mm)</label>
<input type="number" name="custom_height_mm" class="form-control form-control-sm" value="<?= esc((string) $customHeightMm) ?>" min="20" max="1000" step="1">
</div>
<div class="col-md-2">
<label class="form-label small">Top (cm)</label>
<input type="number" name="page_margin_top_cm" class="form-control form-control-sm" value="<?= esc($marginTop) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-md-2">
<label class="form-label small">Bottom (cm)</label>
<input type="number" name="page_margin_bottom_cm" class="form-control form-control-sm" value="<?= esc($marginBottom) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-md-2">
<label class="form-label small">Left (cm)</label>
<input type="number" name="page_margin_left_cm" class="form-control form-control-sm" value="<?= esc($marginLeft) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-md-2">
<label class="form-label small">Right (cm)</label>
<input type="number" name="page_margin_right_cm" class="form-control form-control-sm" value="<?= esc($marginRight) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-md-2">
<label class="form-label small">Header (cm)</label>
<input type="number" name="margin_header_cm" class="form-control form-control-sm" value="<?= esc($marginHeader) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-md-2">
<label class="form-label small">Footer (cm)</label>
<input type="number" name="margin_footer_cm" class="form-control form-control-sm" value="<?= esc($marginFooter) ?>" step="0.1" min="0" max="25">
</div>
<div class="col-12">
<label class="form-label small">Header HTML</label>
<textarea name="header_html" rows="4" class="form-control" style="font-family:Consolas,Monaco,monospace;"><?= esc($headerHtml) ?></textarea>
</div>
<div class="col-12">
<label class="form-label small">Footer HTML</label>
<textarea name="footer_html" rows="4" class="form-control" style="font-family:Consolas,Monaco,monospace;"><?= esc($footerHtml) ?></textarea>
</div>
<div class="col-12">
<label class="form-label small">Style (CSS)</label>
<textarea name="template_css" rows="4" class="form-control" style="font-family:Consolas,Monaco,monospace;" placeholder="Example: .title{font-size:16px;font-weight:bold;}"><?= esc($templateCss) ?></textarea>
<small class="text-muted">Simple CSS textbox, similar to OPD template settings.</small>
</div>
<div class="col-12">
<label class="form-label small">Template HTML</label>
<textarea name="template_html" id="ipd_document_template_editor" rows="12" class="form-control" required><?= esc($templateHtml) ?></textarea>
@ -123,6 +199,9 @@ $formMap = [
var form = document.getElementById('ipd_document_template_form');
var editorFieldId = 'ipd_document_template_editor';
var submitBtn = form ? form.querySelector('button[type="submit"]') : null;
var pageSizeEl = document.getElementById('ipd_page_size');
var customWidthWrap = document.getElementById('ipd_custom_width_wrap');
var customHeightWrap = document.getElementById('ipd_custom_height_wrap');
function initEditor() {
if (!window.CKEDITOR) {
@ -165,6 +244,20 @@ $formMap = [
initEditor();
function toggleCustomSize() {
if (!pageSizeEl || !customWidthWrap || !customHeightWrap) {
return;
}
var isCustom = pageSizeEl.value === 'CUSTOM';
customWidthWrap.style.display = isCustom ? '' : 'none';
customHeightWrap.style.display = isCustom ? '' : 'none';
}
if (pageSizeEl) {
pageSizeEl.addEventListener('change', toggleCustomSize);
}
toggleCustomSize();
if (form) {
form.addEventListener('submit', function (event) {
event.preventDefault();