IPD payment receipt: fix modal loader, add PDF print route, and bank mode details

This commit is contained in:
DevSoftTech Server 2026-04-03 13:29:03 +05:30
parent e282289227
commit 435192f81a
8 changed files with 293 additions and 26 deletions

View File

@ -379,6 +379,7 @@ $routes->group('billing', function($routes) {
$routes->post('ipd/payment/deduct', 'Billing\\Ipd::deductPayment');
$routes->post('ipd/payment/tpa', 'Billing\\Ipd::tpaPayment');
$routes->get('ipd/payment/receipt/(:num)/(:num)', 'Billing\\Ipd::ipdCashPrintPdf/$1/$2');
$routes->get('ipd/payment/pdf-receipt/(:num)/(:num)', 'Billing\\Ipd::pdfPaymentReceipt/$1/$2');
$routes->get('ipd/cash-balance', 'Billing\\Ipd::cashBalance');
$routes->get('ipd/cash-balance/report', 'Billing\\Ipd::cashBalanceReport');
$routes->get('ipd/cash-balance/export', 'Billing\\Ipd::cashBalanceExport');

View File

@ -2053,11 +2053,13 @@ class Ipd extends BaseController
return $permission;
}
$payment = $this->db->table('payment_history')
->select("*,date_format(payment_date,'%d-%m-%Y %h:%i %p') as payment_date_str,if(credit_debit=0,'','Return') as Amount_str", false)
->where('payof_type', 4)
->where('payof_id', $ipdId)
->where('id', $payId)
$payment = $this->db->table('payment_history p')
->select("p.*,date_format(p.payment_date,'%d-%m-%Y %h:%i %p') as payment_date_str,if(p.credit_debit=0,'','Return') as Amount_str,s.pay_type,b.bank_name", false)
->join('hospital_bank_payment_source s', 's.id = p.pay_bank_id', 'left')
->join('hospital_bank b', 'b.id = s.bank_id', 'left')
->where('p.payof_type', 4)
->where('p.payof_id', $ipdId)
->where('p.id', $payId)
->get()
->getResult();
@ -2085,6 +2087,79 @@ class Ipd extends BaseController
]);
}
public function pdfPaymentReceipt(int $ipdId, int $payId)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
$payment = $this->db->table('payment_history p')
->select("p.*,date_format(p.payment_date,'%d-%m-%Y %h:%i %p') as payment_date_str,if(p.credit_debit=0,'','Return') as Amount_str,s.pay_type,b.bank_name", false)
->join('hospital_bank_payment_source s', 's.id = p.pay_bank_id', 'left')
->join('hospital_bank b', 'b.id = s.bank_id', 'left')
->where('p.payof_type', 4)
->where('p.payof_id', $ipdId)
->where('p.id', $payId)
->get()
->getResult();
$ipdMaster = $this->db->table('ipd_master')
->where('id', $ipdId)
->get()
->getResult();
if (empty($ipdMaster)) {
return $this->response->setStatusCode(404)->setBody('IPD not found');
}
$patient = $this->db->table('patient_master')
->select("*,if(gender=1,'Male','Female') as xgender", false)
->where('id', $ipdMaster[0]->p_id)
->get()
->getResult();
$html = view('billing/ipd/ipd_payment_receipt_pdf', [
'ipd_payment' => $payment,
'ipd_master' => $ipdMaster,
'patient_master' => $patient,
'ipd_id' => $ipdId,
'payid' => $payId,
]);
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
if (! is_dir($mpdfTempDir)) {
mkdir($mpdfTempDir, 0755, true);
}
$mpdf = new Mpdf([
'tempDir' => $mpdfTempDir,
'format' => 'A4',
]);
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : '';
if ($hospitalName !== '') {
$mpdf->SetWatermarkText($hospitalName);
$mpdf->showWatermarkText = false;
}
// Strip BOM and sanitize to valid UTF-8 before passing to mPDF
$html = ltrim($html, "\xEF\xBB\xBF");
$html = mb_convert_encoding($html, 'UTF-8', 'UTF-8');
$mpdf->WriteHTML($html);
$fileName = 'IPD_Payment_Receipt_' . $payId . '_' . date('YmdHis') . '.pdf';
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', 'inline; filename="' . $fileName . '"')
->setBody($mpdf->Output($fileName, 'S'));
}
private function parseDateRange(string $range): array
{
$start = date('Y-m-d');

View File

@ -604,8 +604,7 @@ class IpdBillingModel extends Model
public function getPayments(int $ipdId): array
{
return $this->db->table('payment_history')
->select("*,date_format(payment_date,'%d-%M-%Y') as pay_date_str,")
->select("concat(if(payment_mode=1,'Cash','BANK'),if(credit_debit=0,'','-Return')) as pay_mode", false)
->select("*,date_format(payment_date,'%d-%M-%Y') as pay_date_str,concat(if(payment_mode=1,'Cash','Bank'),if(credit_debit=0,'','-Return')) as pay_mode", false)
->where('payof_type', 4)
->where('payof_id', $ipdId)
->orderBy('id', 'DESC')

View File

@ -125,7 +125,7 @@ if ($person) {
}
if (data.ipd_id && data.payid) {
load_report_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal-bodyc');
load_form_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal-bodyc');
}
}, 'json');
});
@ -157,7 +157,7 @@ if ($person) {
}
if (data.ipd_id && data.payid) {
load_report_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal-bodyc');
load_form_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal-bodyc');
}
}, 'json');
});

View File

@ -99,7 +99,7 @@
}
if (data.ipd_id && data.payid) {
load_report_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal_ded-bodyc');
load_form_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal_ded-bodyc');
}
}, 'json');
});
@ -125,7 +125,7 @@
}
if (data.ipd_id && data.payid) {
load_report_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal_ded-bodyc');
load_form_div('<?= site_url('billing/ipd/payment/receipt') ?>/' + data.ipd_id + '/' + data.payid, 'payModal_ded-bodyc');
}
}, 'json');
});

View File

@ -0,0 +1,123 @@
<?php
$payment = $ipd_payment[0] ?? null;
$ipd = $ipd_master[0] ?? null;
$patient = $patient_master[0] ?? null;
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : 'Hospital';
$hospitalAddress1 = defined('H_address_1') ? (string) constant('H_address_1') : '';
$hospitalAddress2 = defined('H_address_2') ? (string) constant('H_address_2') : '';
$hospitalPhone = defined('H_phone_No') ? (string) constant('H_phone_No') : '';
$hospitalLogoName = defined('H_logo') ? trim((string) constant('H_logo')) : '';
// Absolute path for mPDF logo loading
$logoPath = '';
if ($hospitalLogoName !== '') {
$candidate = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . ltrim($hospitalLogoName, '/\\');
if (is_file($candidate)) {
$logoPath = $candidate;
}
}
$barContent = ':IPD-' . (int)$ipd_id . ':' . (int)$payid . ':' . ($payment->amount ?? 0) . ':PT' . date('dmYhis');
?>
<style>
@page {
sheet-size: A4;
margin-top: 4cm;
margin-bottom: 14cm;
margin-left: 0.5cm;
margin-right: 0.5cm;
margin-header: 0.5cm;
header: html_myHeader;
}
body { font-family: Arial, sans-serif; font-size: 12px; color: #333; }
</style>
<htmlpageheader name="myHeader">
<table cellspacing="0" style="font-size:10px;width:100%;border-style:inset;">
<tr>
<td style="width:20%;vertical-align:top;">
<?php if ($logoPath !== ''): ?>
<img style="width:90px;vertical-align:top;" src="<?= $logoPath ?>" />
<?php endif; ?>
</td>
<td style="width:60%;vertical-align:top;">
<p align="center" style="font-size:26px;"><b><?= htmlspecialchars($hospitalName) ?></b></p>
<p align="center" style="font-size:15px;">
<?= htmlspecialchars(trim($hospitalAddress1 . ', ' . $hospitalAddress2, ', ')) ?>
<?php if ($hospitalPhone !== ''): ?><br>Phone: <?= htmlspecialchars($hospitalPhone) ?><?php endif; ?>
</p>
</td>
<td style="width:20%;vertical-align:top;text-align:right;">
<barcode code="<?= htmlspecialchars($barContent) ?>" size="0.8" type="QR" error="M" class="barcode" />
</td>
</tr>
</table>
<h3 align="center">IPD Payment <?= htmlspecialchars($payment->Amount_str ?? '') ?> Receipt : <?= (int) $payid ?></h3>
</htmlpageheader>
<table width="100%" style="font-size:12px;">
<tr>
<td width="50%">
Patient Info.<br/>
UHID : <?= htmlspecialchars($patient->p_code ?? '') ?><br>
Name : <strong><?= htmlspecialchars($patient->title ?? '') ?> <?= htmlspecialchars(strtoupper($patient->p_fname ?? '')) ?></strong><br>
<?= htmlspecialchars($patient->p_relative ?? '') ?> <?= htmlspecialchars(strtoupper($patient->p_rname ?? '')) ?><br>
Sex : <b><?= htmlspecialchars($patient->xgender ?? '') ?> <?= htmlspecialchars($patient->age ?? '') ?></b> P.No. :<?= htmlspecialchars($patient->mphone1 ?? '') ?>
</td>
<td width="50%" style="text-align:right;">
IPD No. : <?= htmlspecialchars($ipd->ipd_code ?? '') ?><br>
Date : <strong><?= htmlspecialchars($payment->payment_date_str ?? '') ?></strong>
</td>
</tr>
</table>
<hr/>
<?php
$payMode = ((int) ($payment->payment_mode ?? 0) === 1) ? 'Cash' : 'Bank';
$isBankPayment = ((int) ($payment->payment_mode ?? 0) !== 1);
$paymentSource = trim((string) ($payment->pay_type ?? ''));
$bankName = trim((string) ($payment->bank_name ?? ''));
$cardTranId = trim((string) ($payment->card_tran_id ?? ''));
$bankMachine = trim((string) ($payment->bankcard_machine ?? ''));
$cardNumber = trim((string) ($payment->cust_card ?? ''));
$cardRemark = trim((string) ($payment->card_remark ?? ''));
?>
<p style="font-size:15px;">
<b>IPD <?= htmlspecialchars($payment->Amount_str ?? '') ?> Amount : </b>Rs. <?= number_format((float)($payment->amount ?? 0), 2) ?><br/>
<b>Payment Mode : </b><?= htmlspecialchars($payMode) ?><br/>
<?php if ($isBankPayment): ?>
<?php if ($paymentSource !== ''): ?>
<b>Bank/UPI Type : </b><?= htmlspecialchars($paymentSource) ?><br/>
<?php endif; ?>
<?php if ($bankName !== ''): ?>
<b>Bank Name : </b><?= htmlspecialchars($bankName) ?><br/>
<?php endif; ?>
<?php if ($cardTranId !== ''): ?>
<b>Transaction ID : </b><?= htmlspecialchars($cardTranId) ?><br/>
<?php endif; ?>
<?php if ($bankMachine !== ''): ?>
<b>Bank/Card Machine : </b><?= htmlspecialchars($bankMachine) ?><br/>
<?php endif; ?>
<?php if ($cardNumber !== ''): ?>
<b>Card/Reference No : </b><?= htmlspecialchars($cardNumber) ?><br/>
<?php endif; ?>
<?php if ($cardRemark !== ''): ?>
<b>Bank Remark : </b><?= htmlspecialchars($cardRemark) ?><br/>
<?php endif; ?>
<?php endif; ?>
<b>Amount In Words : </b>Rs.<?= htmlspecialchars(number_to_word((float)($payment->amount ?? 0))) ?> Only
</p>
<p>
<b>Prepared By :</b><?= htmlspecialchars($payment->update_by ?? '') ?>
</p>
<table width="100%" style="margin-top:30px;">
<tr>
<td width="60%">&nbsp;</td>
<td width="40%" style="text-align:center;">
<div style="border-top:1px solid #333;padding-top:4px;font-size:11px;"><b>Authorised Signature</b></div>
</td>
</tr>
</table>
<p style="text-align:center;font-size:9px;color:#666;margin-top:20px;border-top:1px dashed #aaa;padding-top:6px;">
This is a computer-generated receipt and does not require a physical signature.
</p>

View File

@ -2,27 +2,90 @@
$payment = $ipd_payment[0] ?? null;
$ipd = $ipd_master[0] ?? null;
$patient = $patient_master[0] ?? null;
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : 'Hospital';
$hospitalAddress1 = defined('H_address_1') ? (string) constant('H_address_1') : '';
$hospitalAddress2 = defined('H_address_2') ? (string) constant('H_address_2') : '';
$hospitalPhone = defined('H_phone_No') ? (string) constant('H_phone_No') : '';
$hospitalLogoName = defined('H_logo') ? trim((string) constant('H_logo')) : '';
$logoSrc = '';
if ($hospitalLogoName !== '') {
$logoSrc = base_url('assets/images/' . rawurlencode($hospitalLogoName));
}
// QR Code Data: IPD-{IPD_ID}:{PAYMENT_ID}:{AMOUNT}:{TIMESTAMP}
$qrData = 'IPD-' . $ipd->id . ':' . $payid . ':' . $payment->amount . ':' . date('dmYHi');
$qrCodeUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=' . urlencode($qrData);
?>
<div class="card">
<div class="card-header">
<strong>IPD Payment <?= esc($payment->Amount_str ?? '') ?> Receipt : <?= esc($payid ?? '') ?></strong>
<div class="card-header d-flex justify-content-between align-items-center flex-wrap">
<div>
<strong>IPD Payment Receipt #<?= esc($payid ?? '') ?></strong>
<br><small class="text-muted"><?= esc($payment->Amount_str ?? '') ?></small>
</div>
<button class="btn btn-sm btn-primary" onclick="window.open('<?= site_url('billing/ipd/payment/pdf-receipt/' . (int) $ipd_id . '/' . (int) $payid) ?>', '_blank');" title="Print Receipt as PDF">
<i class="bi bi-printer"></i> Print Receipt
</button>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>UHID :</strong> <?= esc($patient->p_code ?? '') ?></p>
<p><strong>Name :</strong> <?= esc($patient->title ?? '') ?> <?= esc($patient->p_fname ?? '') ?></p>
<p><strong>Sex :</strong> <?= esc($patient->xgender ?? '') ?> <?= esc($patient->age ?? '') ?></p>
<p><strong>Phone :</strong> <?= esc($patient->mphone1 ?? '') ?></p>
<!-- Hospital Header -->
<div class="row mb-3 pb-3 border-bottom">
<div class="col-md-2 text-center">
<?php if ($logoSrc !== ''): ?>
<img src="<?= esc($logoSrc) ?>" alt="Hospital Logo" style="max-height:80px; max-width:100%;">
<?php else: ?>
<div class="text-muted"><small>No Logo</small></div>
<?php endif; ?>
</div>
<div class="col-md-6 text-end">
<p><strong>IPD No. :</strong> <?= esc($ipd->ipd_code ?? '') ?></p>
<p><strong>Date :</strong> <?= esc($payment->payment_date_str ?? '') ?></p>
<div class="col-md-8 text-center">
<h5 class="mb-1"><strong><?= esc($hospitalName) ?></strong></h5>
<small>
<?php
$address = trim($hospitalAddress1 . ', ' . $hospitalAddress2, ', ');
if ($address !== '') echo esc($address) . '<br>';
if ($hospitalPhone !== '') echo 'Phone: ' . esc($hospitalPhone);
?>
</small>
</div>
<div class="col-md-2 text-center">
<img src="<?= esc($qrCodeUrl) ?>" alt="QR Code" style="max-height:80px;">
<small class="d-block mt-1">Receipt #<?= esc($payid ?? '') ?></small>
</div>
</div>
<!-- Receipt Details -->
<div class="row mb-3">
<div class="col-md-6">
<p class="mb-1"><strong>Patient Information</strong></p>
<p class="mb-1"><strong>UHID :</strong> <?= esc($patient->p_code ?? '') ?></p>
<p class="mb-1"><strong>Name :</strong> <?= esc($patient->title ?? '') ?> <?= esc($patient->p_fname ?? '') ?></p>
<p class="mb-1"><strong>Gender :</strong> <?= esc($patient->xgender ?? '') ?> / <strong>Age :</strong> <?= esc($patient->age ?? '') ?></p>
<p class="mb-0"><strong>Phone :</strong> <?= esc($patient->mphone1 ?? '') ?></p>
</div>
<div class="col-md-6 text-end">
<p class="mb-1"><strong>Payment Information</strong></p>
<p class="mb-1"><strong>IPD Code :</strong> <?= esc($ipd->ipd_code ?? '') ?></p>
<p class="mb-1"><strong>Payment Date :</strong> <?= esc($payment->payment_date_str ?? '') ?></p>
<p class="mb-0"><strong>Receipt #:</strong> <?= esc($payid ?? '') ?></p>
</div>
</div>
<hr>
<p><strong>IPD <?= esc($payment->Amount_str ?? '') ?> Amount :</strong> Rs. <?= esc($payment->amount ?? '') ?></p>
<p><strong>Amount in Words :</strong> Rs. <?= number_to_word((float) ($payment->amount ?? 0)) ?></p>
<p><strong>Prepared By :</strong> <?= esc($payment->update_by ?? '') ?></p>
<!-- Amount Details -->
<div class="row mb-3">
<div class="col-md-12">
<p class="mb-2"><strong>Payment Amount : </strong><span class="text-success" style="font-size: 1.3em;">Rs. <?= esc(number_format((float) ($payment->amount ?? 0), 2)) ?></span></p>
<p class="mb-2"><strong>Amount in Words : </strong>Rs. <?= esc(number_to_word((float) ($payment->amount ?? 0))) ?></p>
<p class="mb-0"><strong>Prepared By :</strong> <?= esc($payment->update_by ?? '') ?></p>
</div>
</div>
<hr class="my-3">
<div class="text-center mt-4">
<p class="small text-muted">This is a computer-generated receipt. No signature is required.</p>
</div>
</div>
</div>

View File

@ -12,6 +12,7 @@
<th>Date</th>
<th class="text-end">Amount</th>
<th>Mode</th>
<th>Print</th>
</tr>
</thead>
<tbody>
@ -24,11 +25,16 @@
<td><?= esc($row->pay_date_str ?? '') ?></td>
<td class="text-end"><?= esc($row->amount ?? '') ?></td>
<td><?= esc($row->pay_mode ?? '') ?></td>
<td>
<a href="<?= site_url('billing/ipd/payment/pdf-receipt/' . (int)($ipd_info->id ?? 0) . '/' . (int)($row->id ?? 0)) ?>" target="_blank" class="btn btn-sm btn-outline-primary" title="Print Receipt">
<i class="bi bi-printer"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="5" class="text-center text-muted">No payments found.</td>
<td colspan="6" class="text-center text-muted">No payments found.</td>
</tr>
<?php endif; ?>
</tbody>