Restore legacy HMS flows and add IPD document master templates

This commit is contained in:
DevSoftTech Server 2026-03-24 04:24:19 +05:30
parent 7ee67ff537
commit 99ec71677a
28 changed files with 2601 additions and 118 deletions

View File

@ -150,7 +150,7 @@ class App extends BaseConfig
* @see https://www.php.net/manual/en/timezones.php for list of timezones
* supported by PHP.
*/
public string $appTimezone = 'UTC';
public string $appTimezone = 'Asia/Kolkata';
/**
* --------------------------------------------------------------------------

View File

@ -294,6 +294,10 @@ $routes->get('IpdNew/addipd/(:num)', 'Billing\\Ipd::legacyAddIpd/$1');
$routes->post('IpdNew/AddNew', 'Billing\\Ipd::legacyAddNew');
$routes->post('IpdNew/addnew', 'Billing\\Ipd::legacyAddNew');
$routes->get('IpdNew/ipd_panel/(:num)', 'Billing\\Ipd::panel/$1');
$routes->get('IpdNew/show_ipd_form/(:num)', 'Billing\\Ipd::showIpdForm/$1/1');
$routes->get('IpdNew/show_ipd_form/(:num)/(:num)', 'Billing\\Ipd::showIpdForm/$1/$2');
$routes->get('ipdNew/show_ipd_form/(:num)', 'Billing\\Ipd::showIpdForm/$1/1');
$routes->get('ipdNew/show_ipd_form/(:num)/(:num)', 'Billing\\Ipd::showIpdForm/$1/$2');
// Legacy compatibility: old discharge summary links should open current discharge tab.
$routes->match(['get', 'post'], 'Ipd_discharge/ipd_select/(:num)', 'Ipd_discharge::ipd_select/$1');
@ -346,6 +350,8 @@ $routes->group('billing', function($routes) {
$routes->post('ipd/panel/(:num)/discharge/charge/(:num)', 'Billing\\Ipd::updateDischargeCharge/$1/$2');
$routes->get('ipd/bill-print/(:num)', 'Billing\\Ipd::billPrint/$1');
$routes->get('ipd/bill-print/(:num)/(:num)', 'Billing\\Ipd::billPrint/$1/$2');
$routes->get('ipd/show-form/(:num)', 'Billing\\Ipd::showIpdForm/$1/1');
$routes->get('ipd/show-form/(:num)/(:num)', 'Billing\\Ipd::showIpdForm/$1/$2');
$routes->post('ipd/charge/add/(:num)', 'Billing\\Ipd::addIpdCharge/$1');
$routes->post('ipd/charge/update/(:num)', 'Billing\\Ipd::updateIpdCharge/$1');
$routes->post('ipd/charge/delete/(:num)', 'Billing\\Ipd::deleteIpdCharge/$1');
@ -457,6 +463,7 @@ $routes->post('Opd/showfee', 'Opd::showfee');
$routes->post('Opd/confirm_opd', 'Opd::confirm_opd');
$routes->post('Opd/confirm_payment', 'Opd::confirm_payment');
$routes->post('Opd/opd_discount_update/(:num)', 'Opd::opd_discount_update/$1');
$routes->post('Opd/opd_cancel/(:num)', 'Opd::opd_cancel/$1');
$routes->match(['get', 'post'], 'Opd/opd_load_doc/(:num)', 'Opd::opd_load_doc/$1');
$routes->match(['get', 'post'], 'Opd/opd_file_last_list', 'Opd::opd_file_last_list');
$routes->match(['get', 'post'], 'Opd/opd_file_last_list/(:num)', 'Opd::opd_file_last_list/$1');
@ -599,6 +606,8 @@ $routes->get('Opd_prescription/show_medical_item/(:num)', 'Opd_prescription::sho
$routes->get('Opd_prescription/prescribed_dose/(:num)/(:num)', 'Opd_prescription::prescribed_dose/$1/$2');
$routes->get('Invoice/list_req_payment', 'Invoice::list_req_payment');
$routes->get('Invoice/opdlist', 'Invoice::opdlist');
$routes->get('Invoice/chargeslist', 'Invoice::chargeslist');
$routes->post('Invoice/getRequestTable', 'Invoice::getRequestTable');
$routes->get('Invoice/payment_form/(:num)', 'Invoice::payment_form/$1');
$routes->post('Invoice/req_payment_process', 'Invoice::req_payment_process');
@ -633,6 +642,8 @@ $routes->post('Lab_Admin/test_item_search', 'Setting\Template::test_item_search'
$routes->match(['get', 'post'], 'setting/template/discharge_templates', 'Setting\Template::discharge_templates');
$routes->get('setting/template/discharge_templates/delete/(:num)', 'Setting\Template::discharge_template_delete/$1');
$routes->match(['get', 'post'], 'setting/template/ipd_document_templates', 'Setting\Template::ipd_document_templates');
$routes->get('setting/template/ipd_document_templates/delete/(:num)', 'Setting\Template::ipd_document_template_delete/$1');
$routes->match(['get', 'post'], 'setting/template/diagnosis_print_settings', 'Setting\Template::diagnosis_print_settings');
$routes->match(['get', 'post'], 'setting/template/diagnosis_print_settings/(:num)', 'Setting\Template::diagnosis_print_settings/$1');
$routes->match(['get', 'post'], 'setting/template/document_print_settings', 'Setting\Template::document_print_settings');
@ -901,6 +912,13 @@ $routes->group('setting', static function($routes) {
$routes->post('admin/reffer/types/update', 'Setting\\Reffer::updateType');
$routes->post('admin/reffer/types/delete', 'Setting\\Reffer::deleteType');
$routes->get('admin/bed-management', 'Setting\\BedManagement::index');
$routes->get('admin/departments', 'Setting\\BedManagement::departments');
$routes->post('admin/departments/save', 'Setting\\BedManagement::saveDepartment');
$routes->post('admin/departments/delete', 'Setting\\BedManagement::deleteDepartment');
$routes->get('admin/wards', 'Setting\\BedManagement::wards');
$routes->get('admin/bed-status', 'Setting\\BedManagement::bedStatus');
$routes->post('admin/wards/save', 'Setting\\BedManagement::saveWard');
$routes->post('admin/wards/delete', 'Setting\\BedManagement::deleteWard');
$routes->get('admin/bed-categories', 'Setting\\BedManagement::categories');
$routes->post('admin/bed-categories/save', 'Setting\\BedManagement::saveCategory');
$routes->post('admin/bed-categories/delete', 'Setting\\BedManagement::deleteCategory');

View File

@ -474,6 +474,13 @@ class Ipd extends BaseController
return view('billing/ipd/panel_discharge', $panelData);
}
if ($tab === 'documents') {
return view('billing/ipd/panel_documents', [
'ipd_id' => $ipdId,
'ipd_info' => $panelData['ipd_info'] ?? null,
]);
}
return view('billing/ipd/panel_placeholder', [
'title' => ucwords(str_replace('-', ' ', $tab)),
]);
@ -792,6 +799,67 @@ class Ipd extends BaseController
->setBody($mpdf->Output($fileName, 'S'));
}
public function showIpdForm(int $ipdId, int $formNo = 1)
{
$permission = $this->requireAnyPermission([
'billing.ipd.bill.print',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
$formMeta = $this->getIpdFormMeta($formNo);
if ($formMeta === null) {
return $this->response->setStatusCode(404)->setBody('Unsupported form ID');
}
$data = $this->buildIpdPrintableFormData($ipdId, $formNo);
if ($data === null) {
return $this->response->setStatusCode(404)->setBody('IPD not found');
}
$html = view('billing/ipd/ipd_form_print', $data);
$customTemplate = $this->getActiveIpdDocumentTemplate($formNo);
if ($customTemplate !== null) {
$html = $this->renderIpdDocumentTemplateHtml($customTemplate, $data);
}
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
if (! is_dir($mpdfTempDir)) {
mkdir($mpdfTempDir, 0755, true);
}
$mpdfConfig = [
'tempDir' => $mpdfTempDir,
'margin_top' => 8,
'margin_bottom' => 8,
'margin_left' => 8,
'margin_right' => 8,
];
if (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;
$mpdfConfig['margin_left'] = 2;
$mpdfConfig['margin_right'] = 2;
} else {
$mpdfConfig['format'] = 'A4';
}
$mpdf = new Mpdf($mpdfConfig);
$mpdf->WriteHTML($html);
$ipdCode = (string) ($data['ipd_info']->ipd_code ?? ('IPD-' . $ipdId));
$fileName = 'IPD_Form_' . (int) $formNo . '_' . preg_replace('/[^A-Za-z0-9_\-]/', '_', $ipdCode) . '.pdf';
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', 'inline; filename="' . $fileName . '"')
->setBody($mpdf->Output($fileName, 'S'));
}
public function addIpdCharge(int $ipdId)
{
$permission = $this->requireAnyPermission([
@ -1775,6 +1843,157 @@ class Ipd extends BaseController
return [$start, $end];
}
private function getIpdFormMeta(int $formNo): ?array
{
$meta = [
1 => ['title' => 'Face Form'],
3 => ['title' => 'Self Declaration Form'],
5 => ['title' => 'Admission Assessment Form'],
8 => ['title' => 'Progress Notes and Doctor Order'],
9 => ['title' => 'Fluid In / Out'],
10 => ['title' => 'Sticker [2 x 6]'],
11 => ['title' => 'Sticker [2 x 8]'],
];
return $meta[$formNo] ?? null;
}
private function buildIpdPrintableFormData(int $ipdId, int $formNo): ?array
{
$panelData = $this->ipdModel->getIpdPanelInfo($ipdId);
if (empty($panelData)) {
return null;
}
$ipdInfo = $panelData['ipd_info'] ?? null;
$personInfo = $panelData['person_info'] ?? null;
if ($ipdInfo === null || $personInfo === null) {
return null;
}
$personInfo->age = get_age_1(
$personInfo->dob ?? null,
$personInfo->age ?? '',
$personInfo->age_in_month ?? '',
$personInfo->estimate_dob ?? ''
);
$doctorRows = [];
if ($this->db->tableExists('ipd_master_doc_list') && $this->db->tableExists('doctor_master')) {
$doctorRows = $this->db->table('ipd_master_doc_list l')
->select("concat_ws(' ', 'Dr.', d.p_fname, d.p_mname, d.p_lname) as doc_name", false)
->join('doctor_master d', 'd.id = l.doc_id', 'inner')
->where('l.ipd_id', $ipdId)
->groupBy('d.id, d.p_fname, d.p_mname, d.p_lname')
->orderBy('d.p_fname', 'ASC')
->get()
->getResultArray();
}
$doctorNames = array_values(array_filter(array_map(
static fn ($row) => trim((string) ($row['doc_name'] ?? '')),
$doctorRows
)));
if (empty($doctorNames)) {
$fallbackDoctor = trim((string) ($ipdInfo->r_doc_name ?? ''));
if ($fallbackDoctor !== '') {
$doctorNames[] = $fallbackDoctor;
}
}
$insuranceName = trim((string) ($ipdInfo->ins_company_name ?? ''));
if ($insuranceName === '' && $this->db->tableExists('organization_case_master')) {
$insuranceRow = $this->db->table('organization_case_master o')
->select('h.ins_company_name')
->join('hc_insurance h', 'h.id = o.insurance_id', 'left')
->where('o.id', (int) ($ipdInfo->case_id ?? 0))
->get()
->getRowArray();
$insuranceName = trim((string) ($insuranceRow['ins_company_name'] ?? ''));
}
$hospitalName = defined('H_Name') ? (string) constant('H_Name') : 'Hospital';
$hospitalAddress = defined('H_address_1') ? (string) constant('H_address_1') : '';
return [
'form_no' => $formNo,
'form_meta' => $this->getIpdFormMeta($formNo),
'ipd_info' => $ipdInfo,
'person_info' => $personInfo,
'doctor_names' => $doctorNames,
'insurance_name' => $insuranceName,
'hospital_name' => $hospitalName,
'hospital_address' => $hospitalAddress,
'generated_at' => date('d-m-Y h:i A'),
];
}
private function getActiveIpdDocumentTemplate(int $formNo): ?array
{
if (! $this->db->tableExists('ipd_document_templates')) {
return null;
}
$row = $this->db->table('ipd_document_templates')
->select('id, form_no, template_name, template_html')
->where('form_no', $formNo)
->where('status', 1)
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
if (! is_array($row) || empty($row)) {
return null;
}
return $row;
}
private function renderIpdDocumentTemplateHtml(array $templateRow, array $data): string
{
$templateHtml = (string) ($templateRow['template_html'] ?? '');
if (trim($templateHtml) === '') {
return view('billing/ipd/ipd_form_print', $data);
}
$formMeta = $data['form_meta'] ?? [];
$ipd = $data['ipd_info'] ?? null;
$person = $data['person_info'] ?? null;
$doctorNames = is_array($data['doctor_names'] ?? null) ? $data['doctor_names'] : [];
$patientName = trim((string) (($person->title ?? '') . ' ' . ($person->p_fname ?? '')));
if ($patientName === '') {
$patientName = '-';
}
$age = (string) ($person->age ?? '-');
$gender = (string) ($person->xgender ?? '-');
$ageGender = trim($age . ' / ' . $gender);
if ($ageGender === '/') {
$ageGender = '-';
}
$replacements = [
'{{FORM_TITLE}}' => esc((string) ($formMeta['title'] ?? 'IPD Form')),
'{{HOSPITAL_NAME}}' => esc((string) ($data['hospital_name'] ?? 'Hospital')),
'{{HOSPITAL_ADDRESS}}' => esc((string) ($data['hospital_address'] ?? '')),
'{{PATIENT_NAME}}' => esc($patientName),
'{{AGE}}' => esc($age),
'{{GENDER}}' => esc($gender),
'{{AGE_GENDER}}' => 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) : '-'),
'{{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'))),
];
return strtr($templateHtml, $replacements);
}
private function calculateCashBalanceTotals(array $rows): array
{
$totalPaid = 0.0;

View File

@ -21,6 +21,64 @@ class Invoice extends BaseController
return view('Invoice/payment_request_list');
}
public function opdlist()
{
$search = trim((string) $this->request->getGet('q'));
$builder = $this->db->table('opd_master o')
->select('o.opd_id, o.opd_code, o.P_name, o.p_id, o.doc_name, o.apointment_date, o.payment_status, o.payment_mode, o.insurance_id')
->select('p.p_code, p.mphone1, p.email1')
->join('patient_master p', 'p.id = o.p_id', 'left')
->orderBy('o.opd_id', 'DESC')
->limit(200);
if ($search !== '') {
$builder->groupStart()
->like('o.opd_code', $search, 'both')
->orLike('p.p_code', $search, 'both')
->orLike('o.P_name', $search, 'both')
->orLike('p.mphone1', $search, 'both')
->orLike('p.email1', $search, 'both')
->groupEnd();
}
$rows = $builder->get()->getResult();
return view('Invoice/opd_invoice_list', [
'search' => $search,
'rows' => $rows,
]);
}
public function chargeslist()
{
$search = trim((string) $this->request->getGet('q'));
$builder = $this->db->table('invoice_master i')
->select('i.id as inv_id, i.invoice_code, i.inv_date, i.attach_id, i.insurance_id, i.invoice_status, i.payment_mode, i.payment_part_balance, i.net_amount, i.refer_by_other')
->select('p.p_code, p.p_fname, p.mphone1, p.email1')
->join('patient_master p', 'p.id = i.attach_id and i.attach_type=0', 'left')
->orderBy('i.id', 'DESC')
->limit(200);
if ($search !== '') {
$builder->groupStart()
->like('i.invoice_code', $search, 'both')
->orLike('p.p_code', $search, 'both')
->orLike('p.p_fname', $search, 'both')
->orLike('p.mphone1', $search, 'both')
->orLike('p.email1', $search, 'both')
->groupEnd();
}
$rows = $builder->get()->getResult();
return view('Invoice/charges_invoice_list', [
'search' => $search,
'rows' => $rows,
]);
}
public function getRequestTable()
{
if (! $this->request->isAJAX()) {

View File

@ -1204,6 +1204,12 @@ class Opd extends BaseController
$query = $this->db->query($sql);
$data['payment_history'] = $query->getResult();
$sql = "select id,payment_date,insert_time,payment_mode,credit_debit,amount,remark,card_tran_id,update_by,
(case payment_mode when 0 then 'Zero Amount' when 1 then 'Cash' when 2 then 'Bank/Online' when 3 then 'Return Cash' when 4 then 'Bank Return' else 'Other' end) as payment_mode_str
from payment_history where payof_type=1 and payof_id=" . (int) $opdId . " order by id desc";
$query = $this->db->query($sql);
$data['payment_history_rows'] = $query->getResult();
$paidAmount = 0;
if (!empty($data['payment_history']) && $data['payment_history'][0]->paid_amount) {
$paidAmount = (float) $data['payment_history'][0]->paid_amount;
@ -2923,6 +2929,95 @@ class Opd extends BaseController
]);
}
public function opd_cancel(int $opdId)
{
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON([
'update' => 0,
'error_text' => 'Invalid request',
]);
}
$opdId = (int) ($this->request->getPost('oid') ?? $opdId);
if ($opdId <= 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Invalid OPD ID',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$opdRow = $this->db->table('opd_master')->where('opd_id', $opdId)->get(1)->getRowArray();
if (empty($opdRow)) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'OPD not found',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$user = auth()->user();
$userId = (int) ($user->id ?? 0);
$userName = (string) ($user->username ?? $user->email ?? 'User');
$userLabel = $userName . '[' . $userId . ']:T-' . date('d-m-Y H:i:s');
$remarkInput = trim((string) $this->request->getPost('input_remark'));
$remarkPrefix = $remarkInput !== '' ? $remarkInput : 'Cancel OPD';
$statusRemark = $remarkPrefix . ' [Update By:' . $userLabel . ']';
$sql = "select coalesce(sum(if(credit_debit>0,amount*-1,amount)),0) as paid_amount
from payment_history where payof_type=1 and payof_id=" . (int) $opdId;
$query = $this->db->query($sql);
$paymentHistory = $query->getResult();
$paidAmount = (float) ($paymentHistory[0]->paid_amount ?? 0);
$refundPaymentId = 0;
$this->db->transStart();
if ($paidAmount > 0 && (int) ($opdRow['opd_status'] ?? 0) !== 3) {
$paymentModel = new PaymentModel();
$refundPaymentId = $paymentModel->insertPayment([
'payment_mode' => '1',
'payof_type' => '1',
'payof_id' => $opdId,
'payof_code' => (string) ($opdRow['opd_code'] ?? ''),
'credit_debit' => '1',
'amount' => $paidAmount,
'payment_date' => date('Y-m-d H:i:s'),
'remark' => 'Refund: ' . $remarkPrefix,
'update_by' => $userLabel,
'update_by_id' => $userId,
]);
}
$this->db->table('opd_master')
->where('opd_id', $opdId)
->update([
'opd_status' => '3',
'opd_status_remark' => $statusRemark,
]);
$this->db->transComplete();
if (! $this->db->transStatus()) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Unable to cancel OPD. Please retry.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
return $this->response->setJSON([
'update' => 1,
'showcontent' => 'OPD cancelled' . ($refundPaymentId > 0 ? ' and refund posted' : ''),
'refund_payment_id' => $refundPaymentId,
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
/**
* Load scan/upload modal context for a specific OPD record.
*/

View File

@ -48,6 +48,341 @@ class BedManagement extends BaseController
return $builder->countAllResults() > 0;
}
private function wardCodeExists(string $wardCode, int $excludeId = 0): bool
{
$builder = $this->db->table('ward_master')->where('ward_code', $wardCode);
if ($excludeId > 0) {
$builder->where('id !=', $excludeId);
}
return $builder->countAllResults() > 0;
}
public function wards(): string
{
$departments = [(object) [
'iId' => 0,
'vName' => 'All Department',
]];
if ($this->db->tableExists('hc_department')) {
$departmentRows = $this->db->table('hc_department')
->orderBy('vName', 'ASC')
->get()
->getResult();
$departments = array_merge($departments, $departmentRows);
}
$wardBuilder = $this->db->table('ward_master w')
->orderBy('w.ward_name', 'ASC');
if ($this->db->tableExists('hc_department')) {
$wardBuilder->select('w.*, COALESCE(d.vName, "All Department") AS department_name', false)
->join('hc_department d', 'd.iId = w.department_id', 'left');
} else {
$wardBuilder->select('w.*, "All Department" AS department_name', false);
}
$wardRows = $wardBuilder->get()->getResult();
return view('Setting/Bed/ward_list', [
'wards' => $wardRows,
'departments' => $departments,
]);
}
public function saveWard()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$id = (int) $this->request->getPost('id');
$wardCode = trim((string) $this->request->getPost('ward_code'));
$wardName = trim((string) $this->request->getPost('ward_name'));
$departmentIdRaw = $this->request->getPost('department_id');
$departmentId = $departmentIdRaw === null || $departmentIdRaw === '' ? 0 : (int) $departmentIdRaw;
if ($wardCode === '' || $wardName === '') {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Ward code and ward name are required.']);
}
if ($this->wardCodeExists($wardCode, $id)) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Ward code already exists.']);
}
if ($departmentId > 0 && $this->db->tableExists('hc_department')) {
$departmentExists = $this->db->table('hc_department')
->where('iId', $departmentId)
->countAllResults();
if ($departmentExists <= 0) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid department selected.']);
}
}
$user = auth()->user();
$data = [
'ward_code' => $wardCode,
'ward_name' => $wardName,
'department_id' => $departmentId,
'building_id' => $this->request->getPost('building_id') ?: null,
'floor_number' => $this->request->getPost('floor_number') ?: null,
'ward_type' => $this->request->getPost('ward_type') ?: 'General',
'gender_type' => $this->request->getPost('gender_type') ?: 'unisex',
'ward_category' => $this->request->getPost('ward_category') ?: 'adult',
'total_capacity' => $this->request->getPost('total_capacity') ?: 0,
'nurse_station_location' => $this->request->getPost('nurse_station_location'),
'has_oxygen' => $this->request->getPost('has_oxygen') ? 1 : 0,
'has_suction' => $this->request->getPost('has_suction') ? 1 : 0,
'has_monitor' => $this->request->getPost('has_monitor') ? 1 : 0,
'status' => $this->request->getPost('status') ?: 'active',
'remarks' => $this->request->getPost('remarks'),
];
$model = new WardModel();
if ($id > 0) {
$updated = $model->update($id, $data);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$data['created_by'] = $user->id ?? null;
$insertId = $model->insert($data);
return $this->response->setJSON([
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function deleteWard()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$id = (int) $this->request->getPost('id');
if ($id <= 0) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid ward ID']);
}
$bedCount = $this->db->table('bed_master')
->where('ward_id', $id)
->countAllResults();
if ($bedCount > 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => $bedCount . ' bed(s) are mapped with this ward. Cannot delete.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$model = new WardModel();
$deleted = $model->delete($id);
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Delete failed. Please try again.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function departments(): string
{
if (! $this->db->tableExists('hc_department')) {
return view('Setting/Bed/department_list', [
'departments' => [],
'tableMissing' => true,
]);
}
$departmentRows = $this->db->table('hc_department')
->orderBy('vName', 'ASC')
->get()
->getResult();
return view('Setting/Bed/department_list', [
'departments' => $departmentRows,
'tableMissing' => false,
]);
}
public function saveDepartment()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
if (! $this->db->tableExists('hc_department')) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Department table not found.']);
}
$id = (int) $this->request->getPost('id');
$name = trim((string) $this->request->getPost('department_name'));
$typeId = $this->request->getPost('hc_type_id');
$typeId = $typeId === null || $typeId === '' ? null : (int) $typeId;
if ($name === '') {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Department name is required.']);
}
$duplicate = $this->db->table('hc_department')
->where('vName', $name);
if ($id > 0) {
$duplicate->where('iId !=', $id);
}
if ($duplicate->countAllResults() > 0) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Department name already exists.']);
}
$payload = [
'vName' => $name,
'hc_type_id' => $typeId,
];
if ($id > 0) {
$updated = $this->db->table('hc_department')
->where('iId', $id)
->update($payload);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$inserted = $this->db->table('hc_department')->insert($payload);
$insertId = $inserted ? (int) $this->db->insertID() : 0;
return $this->response->setJSON([
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function deleteDepartment()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
if (! $this->db->tableExists('hc_department')) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Department table not found.']);
}
$id = (int) $this->request->getPost('id');
if ($id <= 0) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid department ID']);
}
$wardCount = $this->db->table('ward_master')
->where('department_id', $id)
->countAllResults();
if ($wardCount > 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => $wardCount . ' ward(s) use this department. Cannot delete.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$this->db->table('hc_department')->where('iId', $id)->delete();
$deleted = $this->db->affectedRows() > 0;
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Delete failed. Please try again.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function bedStatus(): string
{
$departments = [(object) ['iId' => 0, 'vName' => 'All Department']];
if ($this->db->tableExists('hc_department')) {
$departments = array_merge(
$departments,
$this->db->table('hc_department')->orderBy('vName', 'ASC')->get()->getResult()
);
}
$wards = $this->db->table('ward_master')
->select('id, ward_name, department_id')
->orderBy('ward_name', 'ASC')
->get()
->getResult();
$builder = $this->db->table('ipd_master i')
->select('i.id as ipd_id, i.ipd_code, i.register_date, i.p_id')
->where('i.ipd_status', 0)
->orderBy('i.id', 'DESC');
if ($this->db->tableExists('patient_master')) {
$builder->select('p.p_code, p.p_fname, p.p_lname, p.mphone1, p.mphone2')
->join('patient_master p', 'p.id = i.p_id', 'left');
}
if ($this->db->tableExists('bed_assignment_history')) {
$builder->join(
'(select max(id) as id, ipd_id from bed_assignment_history group by ipd_id) bah_latest',
'bah_latest.ipd_id = i.id',
'left',
false
)->join('bed_assignment_history bah', 'bah.id = bah_latest.id', 'left');
if ($this->db->tableExists('bed_master')) {
$builder->join('bed_master b', 'b.id = bah.bed_id', 'left');
}
if ($this->db->tableExists('ward_master')) {
$builder->join('ward_master w', 'w.id = bah.ward_id', 'left');
}
} else {
if ($this->db->tableExists('bed_master')) {
$builder->join('bed_master b', 'b.current_ipd_id = i.id', 'left');
}
if ($this->db->tableExists('ward_master')) {
$builder->join('ward_master w', 'w.id = b.ward_id', 'left');
}
}
$builder->select('b.bed_number, b.bed_code, w.id as ward_id, w.ward_name, w.department_id');
if ($this->db->tableExists('hc_department')) {
$builder->select('d.vName as department_name')
->join('hc_department d', 'd.iId = w.department_id', 'left');
} else {
$builder->select('"All Department" as department_name', false);
}
$rows = $builder->get()->getResult();
return view('Setting/Bed/bed_status_list', [
'rows' => $rows,
'departments' => $departments,
'wards' => $wards,
]);
}
public function categories(): string
{
$model = new BedCategoryModel();
@ -72,34 +407,35 @@ class BedManagement extends BaseController
}
$data = [
'category_code' => $code,
'category_name' => $name,
'category_type' => $this->request->getPost('category_type'),
'base_charge_per_day' => $this->request->getPost('base_charge_per_day') ?? 0,
'category_code' => $code,
'category_name' => $name,
'category_type' => $this->request->getPost('category_type'),
'base_charge_per_day' => $this->request->getPost('base_charge_per_day') ?? 0,
'nursing_charge_per_day' => $this->request->getPost('nursing_charge_per_day') ?? 0,
'amenities' => $this->request->getPost('amenities'),
'description' => $this->request->getPost('description'),
'status' => $this->request->getPost('status') ?? 'active',
'amenities' => $this->request->getPost('amenities'),
'description' => $this->request->getPost('description'),
'status' => $this->request->getPost('status') ?? 'active',
];
$model = new BedCategoryModel();
if ($id > 0) {
$updated = $model->update($id, $data);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$insertId = $model->insert($data);
return $this->response->setJSON([
'insertid' => $insertId,
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
@ -114,26 +450,41 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid category ID']);
}
$model = new BedCategoryModel();
// Block deletion if any beds in this category are currently occupied by IPD patients
$inUseCount = $this->db->table('bed_master')
->where('bed_category_id', $id)
->where('current_ipd_id IS NOT NULL', null, false)
->countAllResults();
if ($inUseCount > 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => "{$inUseCount} bed(s) in this category are currently occupied by IPD patient(s). Cannot delete.",
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$model = new BedCategoryModel();
$deleted = $model->delete($id);
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Delete failed. Please try again.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function beds(): string
{
$bedModel = new BedMasterModel();
$wardModel = new WardModel();
$bedModel = new BedMasterModel();
$wardModel = new WardModel();
$categoryModel = new BedCategoryModel();
return view('Setting/Bed/bed_list', [
'beds' => $bedModel->getAllWithRelations(),
'wards' => $wardModel->getAllActive(),
'beds' => $bedModel->getAllWithRelations(),
'wards' => $wardModel->getAllActive(),
'categories' => $categoryModel->getAll(),
]);
}
@ -144,10 +495,10 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$id = (int) $this->request->getPost('id');
$bedCode = trim((string) $this->request->getPost('bed_code'));
$bedNumber = trim((string) $this->request->getPost('bed_number'));
$wardId = (int) $this->request->getPost('ward_id');
$id = (int) $this->request->getPost('id');
$bedCode = trim((string) $this->request->getPost('bed_code'));
$bedNumber = trim((string) $this->request->getPost('bed_number'));
$wardId = (int) $this->request->getPost('ward_id');
$categoryId = (int) $this->request->getPost('bed_category_id');
if ($bedCode === '' || $bedNumber === '' || $wardId <= 0 || $categoryId <= 0) {
@ -163,41 +514,42 @@ class BedManagement extends BaseController
}
$data = [
'bed_code' => $bedCode,
'bed_number' => $bedNumber,
'ward_id' => $wardId,
'bed_category_id' => $categoryId,
'bed_status' => $this->request->getPost('bed_status') ?? 'available',
'bed_position' => $this->request->getPost('bed_position'),
'has_oxygen' => $this->request->getPost('has_oxygen') ? 1 : 0,
'has_suction' => $this->request->getPost('has_suction') ? 1 : 0,
'has_monitor' => $this->request->getPost('has_monitor') ? 1 : 0,
'has_ventilator' => $this->request->getPost('has_ventilator') ? 1 : 0,
'is_isolation_bed' => $this->request->getPost('is_isolation_bed') ? 1 : 0,
'base_charge_override' => $this->request->getPost('base_charge_override') ?: null,
'bed_code' => $bedCode,
'bed_number' => $bedNumber,
'ward_id' => $wardId,
'bed_category_id' => $categoryId,
'bed_status' => $this->request->getPost('bed_status') ?? 'available',
'bed_position' => $this->request->getPost('bed_position'),
'has_oxygen' => $this->request->getPost('has_oxygen') ? 1 : 0,
'has_suction' => $this->request->getPost('has_suction') ? 1 : 0,
'has_monitor' => $this->request->getPost('has_monitor') ? 1 : 0,
'has_ventilator' => $this->request->getPost('has_ventilator') ? 1 : 0,
'is_isolation_bed' => $this->request->getPost('is_isolation_bed') ? 1 : 0,
'base_charge_override' => $this->request->getPost('base_charge_override') ?: null,
'nursing_charge_override' => $this->request->getPost('nursing_charge_override') ?: null,
'status' => $this->request->getPost('status') ?? 'active',
'remarks' => $this->request->getPost('remarks'),
'status' => $this->request->getPost('status') ?? 'active',
'remarks' => $this->request->getPost('remarks'),
];
$model = new BedMasterModel();
if ($id > 0) {
$updated = $model->update($id, $data);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$insertId = $model->insert($data);
return $this->response->setJSON([
'insertid' => $insertId,
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
@ -212,20 +564,20 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid bed ID']);
}
$model = new BedMasterModel();
$model = new BedMasterModel();
$deleted = $model->delete($id);
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function maintenance(): string
{
$bedModel = new BedMasterModel();
$bedModel = new BedMasterModel();
$maintenanceModel = new BedMaintenanceLogModel();
return view('Setting/Bed/maintenance_list', [
@ -240,45 +592,46 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$id = (int) $this->request->getPost('id');
$id = (int) $this->request->getPost('id');
$bedId = (int) $this->request->getPost('bed_id');
$type = trim((string) $this->request->getPost('maintenance_type'));
$type = trim((string) $this->request->getPost('maintenance_type'));
if ($bedId <= 0 || $type === '') {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Bed and maintenance type are required.']);
}
$data = [
'bed_id' => $bedId,
'maintenance_type' => $type,
'scheduled_date' => $this->request->getPost('scheduled_date') ?: null,
'completed_date' => $this->normalizeDateTime($this->request->getPost('completed_date')),
'performed_by' => $this->request->getPost('performed_by'),
'issue_description' => $this->request->getPost('issue_description'),
'action_taken' => $this->request->getPost('action_taken'),
'bed_id' => $bedId,
'maintenance_type' => $type,
'scheduled_date' => $this->request->getPost('scheduled_date') ?: null,
'completed_date' => $this->normalizeDateTime($this->request->getPost('completed_date')),
'performed_by' => $this->request->getPost('performed_by'),
'issue_description' => $this->request->getPost('issue_description'),
'action_taken' => $this->request->getPost('action_taken'),
'next_maintenance_date' => $this->request->getPost('next_maintenance_date') ?: null,
'cost' => $this->request->getPost('cost') ?: 0,
'status' => $this->request->getPost('status') ?? 'pending',
'cost' => $this->request->getPost('cost') ?: 0,
'status' => $this->request->getPost('status') ?? 'pending',
];
$model = new BedMaintenanceLogModel();
if ($id > 0) {
$updated = $model->update($id, $data);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$insertId = $model->insert($data);
return $this->response->setJSON([
'insertid' => $insertId,
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
@ -293,27 +646,27 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid maintenance ID']);
}
$model = new BedMaintenanceLogModel();
$model = new BedMaintenanceLogModel();
$deleted = $model->delete($id);
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function assignmentHistory(): string
{
$model = new BedAssignmentHistoryModel();
$bedModel = new BedMasterModel();
$model = new BedAssignmentHistoryModel();
$bedModel = new BedMasterModel();
$wardModel = new WardModel();
return view('Setting/Bed/assignment_history', [
'assignments' => $model->getAllWithRelations(),
'beds' => $bedModel->getAllWithRelations(),
'wards' => $wardModel->getAllActive(),
'beds' => $bedModel->getAllWithRelations(),
'wards' => $wardModel->getAllActive(),
]);
}
@ -323,10 +676,10 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$id = (int) $this->request->getPost('id');
$bedId = (int) $this->request->getPost('bed_id');
$wardId = (int) $this->request->getPost('ward_id');
$type = trim((string) $this->request->getPost('assignment_type'));
$id = (int) $this->request->getPost('id');
$bedId = (int) $this->request->getPost('bed_id');
$wardId = (int) $this->request->getPost('ward_id');
$type = trim((string) $this->request->getPost('assignment_type'));
$assignedDate = $this->normalizeDateTime($this->request->getPost('assigned_date'));
if ($bedId <= 0 || $wardId <= 0 || $type === '' || $assignedDate === null) {
@ -334,39 +687,40 @@ class BedManagement extends BaseController
}
$data = [
'ipd_id' => $this->request->getPost('ipd_id') ?: null,
'bed_id' => $bedId,
'assignment_type' => $type,
'assigned_date' => $assignedDate,
'discharged_date' => $this->normalizeDateTime($this->request->getPost('discharged_date')),
'assigned_by' => $this->request->getPost('assigned_by') ?: null,
'transfer_reason' => $this->request->getPost('transfer_reason'),
'ipd_id' => $this->request->getPost('ipd_id') ?: null,
'bed_id' => $bedId,
'assignment_type' => $type,
'assigned_date' => $assignedDate,
'discharged_date' => $this->normalizeDateTime($this->request->getPost('discharged_date')),
'assigned_by' => $this->request->getPost('assigned_by') ?: null,
'transfer_reason' => $this->request->getPost('transfer_reason'),
'transfer_from_bed_id' => $this->request->getPost('transfer_from_bed_id') ?: null,
'remarks' => $this->request->getPost('remarks'),
'ward_id' => $wardId,
'released_date' => $this->normalizeDateTime($this->request->getPost('released_date')),
'total_days' => $this->request->getPost('total_days') ?: 0,
'release_reason' => $this->request->getPost('release_reason'),
'remarks' => $this->request->getPost('remarks'),
'ward_id' => $wardId,
'released_date' => $this->normalizeDateTime($this->request->getPost('released_date')),
'total_days' => $this->request->getPost('total_days') ?: 0,
'release_reason' => $this->request->getPost('release_reason'),
];
$model = new BedAssignmentHistoryModel();
if ($id > 0) {
$updated = $model->update($id, $data);
return $this->response->setJSON([
'update' => $updated ? 1 : 0,
'update' => $updated ? 1 : 0,
'showcontent' => $updated ? 'Data Saved successfully' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$insertId = $model->insert($data);
return $this->response->setJSON([
'insertid' => $insertId,
'insertid' => $insertId,
'error_text' => $insertId > 0 ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
@ -381,14 +735,14 @@ class BedManagement extends BaseController
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid assignment ID']);
}
$model = new BedAssignmentHistoryModel();
$model = new BedAssignmentHistoryModel();
$deleted = $model->delete($id);
return $this->response->setJSON([
'update' => $deleted ? 1 : 0,
'update' => $deleted ? 1 : 0,
'error_text' => $deleted ? '' : 'Please check',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
}
}

View File

@ -1346,4 +1346,343 @@ HTML;
return redirect()->to(base_url('setting/template/discharge_templates'));
}
private function ensureIpdDocumentTemplateTable(): void
{
if ($this->db->tableExists('ipd_document_templates')) {
return;
}
$sql = "CREATE TABLE IF NOT EXISTS ipd_document_templates (
id INT NOT NULL AUTO_INCREMENT,
form_no INT NOT NULL,
template_name VARCHAR(160) NOT NULL,
template_html LONGTEXT NOT NULL,
status TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_form_no (form_no),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$this->db->query($sql);
}
private function defaultIpdDocumentTemplates(): array
{
return [
[
'form_no' => 1,
'template_name' => 'Legacy Face Form (IPD2)',
'template_html' => <<<'HTML'
<style>
.ipd-form { font-size: 12px; line-height: 1.35; }
.ipd-form h3 { margin: 0 0 10px 0; text-align: center; }
.ipd-form table { width: 100%; border-collapse: collapse; }
.ipd-form .meta td { border: 1px solid #444; padding: 6px; vertical-align: top; }
</style>
<div class="ipd-form">
<h3>BED HEAD TICKET</h3>
<table class="meta">
<tr>
<td style="width:50%;">
<b>Patient Information</b><br><br>
<b>Patient Name:</b> {{PATIENT_NAME}}<br>
<b>Age/Gender:</b> {{AGE_GENDER}}<br>
<b>UHID:</b> {{UHID}}<br>
<b>IPD Code:</b> {{IPD_CODE}}<br>
<b>Date And Time of Admission:</b> {{ADMIT_DATE}}
</td>
<td style="width:50%;">
<b>Hospital:</b> {{HOSPITAL_NAME}}<br>
<b>Address:</b> {{HOSPITAL_ADDRESS}}<br>
<b>Doctors:</b> {{DOCTORS}}<br>
<b>Insurance:</b> {{INSURANCE_NAME}}
</td>
</tr>
</table>
<h3 style="font-size:18px;margin-top:12px;">सहमति पत्र</h3>
<p style="font-size:15px;">
मैं इलाज कराने का/की इच्छुक हूँ। मैं {{HOSPITAL_NAME}} के चिकित्सक और उनके सहायकों को अपना उपचार,
परीक्षण, परामर्श, जांच, औषधि देने एवं आवश्यक चिकित्सा प्रक्रियाएं करने की स्वीकृति देता/देती हूँ।
मुझे बीमारी, संभावित जटिलताओं और उपचार में होने वाले खर्च के बारे में समझा दिया गया है।
</p>
<p style="margin-top:18px;">हस्ताक्षर / अंगूठा निशान: __________________________</p>
<p>नाम: __________________________ &nbsp;&nbsp; मरीज से संबंध: __________________________</p>
<p>पता: __________________________ &nbsp;&nbsp; फ़ोन नंबर: __________________________</p>
</div>
HTML,
],
[
'form_no' => 3,
'template_name' => 'Legacy Self Declaration (IPD2)',
'template_html' => <<<'HTML'
<div style="font-size:12px;line-height:1.35;">
<h3 style="text-align:center;margin:0 0 10px 0;">SELF DECLARATION FROM HEALTH INSURANCE CARD HOLDER</h3>
<h3 style="text-align:center;margin:0 0 10px 0;">मेडिक्लेम बीमा कार्ड धारक द्वारा स्वघोषणा</h3>
<table border="1" cellpadding="6" cellspacing="0" style="width:100%;border-collapse:collapse;">
<tr>
<td><b>Patient Name:</b> {{PATIENT_NAME}}</td>
<td><b>UHID:</b> {{UHID}}</td>
<td><b>IPD:</b> {{IPD_CODE}}</td>
</tr>
<tr>
<td><b>Age/Gender:</b> {{AGE_GENDER}}</td>
<td><b>Admission:</b> {{ADMIT_DATE}}</td>
<td><b>Insurance:</b> {{INSURANCE_NAME}}</td>
</tr>
</table>
<p style="margin-top:12px;">
मैं __________________________ यह घोषणा करता/करती हूँ कि मरीज {{PATIENT_NAME}} का मेडिक्लेम {{INSURANCE_NAME}} में है,
और भर्ती दिनांक {{ADMIT_DATE}} को हुई है। मैंने आवश्यक दस्तावेज जमा कर दिए हैं और यदि क्लेम पूर्ण/आंशिक स्वीकृत नहीं होता
है तो शेष देय राशि का भुगतान स्वयं करूँगा/करूँगी।
</p>
<p>हस्ताक्षर / अंगूठा निशान: __________________________</p>
<p>नाम: __________________________ &nbsp;&nbsp; मरीज से संबंध: __________________________</p>
<p>पता: __________________________ &nbsp;&nbsp; फ़ोन नंबर: __________________________</p>
<p>दिनांक: {{CURRENT_DATE}}</p>
</div>
HTML,
],
[
'form_no' => 5,
'template_name' => 'Legacy Admission History & Physical Assessment',
'template_html' => <<<'HTML'
<div style="font-size:11px;line-height:1.3;">
<h3 style="text-align:center;margin:0 0 8px 0;">ADMISSION HISTORY AND PHYSICAL ASSESSMENT FORM</h3>
<p><b>Patient:</b> {{PATIENT_NAME}} &nbsp; <b>Age/Gender:</b> {{AGE_GENDER}} &nbsp; <b>UHID:</b> {{UHID}} &nbsp; <b>IPD:</b> {{IPD_CODE}} &nbsp; <b>Admit Date:</b> {{ADMIT_DATE}}</p>
<p><b>Diagnosis:</b> ______________________________________________</p>
<p><b>Time of Patient Arrival:</b> ____________ &nbsp;&nbsp; <b>Time of Doctor Assessment:</b> ____________</p>
<p><b>Consciousness:</b> [ ] Awake [ ] Alert [ ] In pain [ ] Response to verbal commands [ ] Unresponsive</p>
<p><b>GCS:</b> E___ V___ M___ &nbsp;&nbsp; <b>Airway/Breathing:</b> [ ] Clear [ ] Noisy [ ] Stridor [ ] Obstruction</p>
<p><b>Allergic To:</b> ______________________________________________</p>
<p><b>Present Complaints:</b></p>
<div style="border:1px solid #666;height:52px;"></div>
<p style="margin-top:8px;"><b>Past Medical History:</b></p>
<table border="1" cellpadding="4" cellspacing="0" style="width:100%;border-collapse:collapse;">
<tr><td style="width:25%;">Diabetes / Metabolic</td><td style="width:25%;"></td><td style="width:25%;">Jaundice / CLD</td><td style="width:25%;"></td></tr>
<tr><td>Hypertension / IHD</td><td></td><td>Osteoarthritis</td><td></td></tr>
<tr><td>Asthma / COPD / TB</td><td></td><td>Tuberculosis</td><td></td></tr>
<tr><td colspan="4">Others:</td></tr>
</table>
<p><b>Drug History:</b></p>
<div style="border:1px solid #666;height:42px;"></div>
<p><b>Past Surgical History:</b></p>
<div style="border:1px solid #666;height:42px;"></div>
<p><b>Declaration:</b> I hereby declare that the facts recorded above are accurate to the best of my knowledge.</p>
<p>Name of Patient/Relative: ________________________ &nbsp; Signature: ________________________</p>
<p>Date: {{CURRENT_DATE}}</p>
</div>
HTML,
],
[
'form_no' => 8,
'template_name' => 'Legacy Progress Notes and Doctor Order',
'template_html' => <<<'HTML'
<div style="font-size:11px;line-height:1.3;">
<h3 style="text-align:center;margin:0 0 8px 0;">PROGRESS NOTES AND DOCTOR'S ORDER</h3>
<p><b>Name:</b> {{PATIENT_NAME}} &nbsp; <b>IPD:</b> {{IPD_CODE}} &nbsp; <b>UHID:</b> {{UHID}} &nbsp; <b>Age/Gender:</b> {{AGE_GENDER}}</p>
<hr>
<table border="1" cellpadding="5" cellspacing="0" style="width:100%;border-collapse:collapse;">
<tr>
<th style="width:18%;text-align:center;">DATE</th>
<th style="text-align:center;">PROGRESS NOTES AND DOCTOR'S ORDER</th>
</tr>
<tr>
<td style="height:420px;"></td>
<td></td>
</tr>
</table>
</div>
HTML,
],
[
'form_no' => 9,
'template_name' => 'Legacy Oral / Enteral Intake Output Chart',
'template_html' => <<<'HTML'
<div style="font-size:11px;line-height:1.25;">
<h3 style="text-align:center;margin:0 0 8px 0;">ORAL / ENTERAL INTAKE & OUTPUT CHART</h3>
<p><b>Name:</b> {{PATIENT_NAME}} &nbsp; <b>IPD:</b> {{IPD_CODE}} &nbsp; <b>UHID:</b> {{UHID}} &nbsp; <b>Age/Gender:</b> {{AGE_GENDER}}</p>
<hr>
<table border="1" cellpadding="4" cellspacing="0" style="width:100%;border-collapse:collapse;">
<tr>
<th style="width:6%;">SR.No.</th>
<th style="width:10%;">Date</th>
<th style="width:8%;">Time</th>
<th style="width:17%;">Description</th>
<th style="width:7%;">Vol.</th>
<th style="width:7%;">Vol.</th>
<th style="width:7%;">Total</th>
<th style="width:7%;">Urine</th>
<th style="width:7%;">Stool</th>
<th style="width:7%;">Other</th>
<th style="width:7%;">Other</th>
<th style="width:7%;">Other</th>
<th style="width:7%;">Total</th>
</tr>
<tr><td>1</td><td style="height:24px;"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>2</td><td style="height:24px;"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>3</td><td style="height:24px;"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>4</td><td style="height:24px;"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>5</td><td style="height:24px;"></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
</table>
</div>
HTML,
],
[
'form_no' => 10,
'template_name' => 'Legacy Sticker [2 x 6]',
'template_html' => <<<'HTML'
<div style="font-size:9px;border:1px dashed #444;padding:4px;line-height:1.25;">
<div><b>{{HOSPITAL_NAME}}</b></div>
<div><b>Patient Name:</b> {{PATIENT_NAME}}</div>
<div><b>Age / Gender:</b> {{AGE_GENDER}}</div>
<div><b>UHID / Patient ID:</b> {{UHID}}</div>
<div><b>IPD Code:</b> {{IPD_CODE}}</div>
<div><b>Dept./Doctor:</b> {{DOCTORS}}</div>
<div><b>Date of Admission:</b> {{ADMIT_DATE}}</div>
</div>
HTML,
],
[
'form_no' => 11,
'template_name' => 'Legacy Sticker [2 x 8]',
'template_html' => <<<'HTML'
<div style="font-size:9px;border:1px dashed #444;padding:4px;line-height:1.25;">
<div><b>{{HOSPITAL_NAME}}</b></div>
<div><b>{{HOSPITAL_ADDRESS}}</b></div>
<div><b>Patient Name:</b> {{PATIENT_NAME}}</div>
<div><b>Age / Gender:</b> {{AGE_GENDER}}</div>
<div><b>UHID / Patient ID:</b> {{UHID}}</div>
<div><b>IPD Code:</b> {{IPD_CODE}}</div>
<div><b>Dept./Doctor:</b> {{DOCTORS}}</div>
<div><b>Date of Admission:</b> {{ADMIT_DATE}}</div>
<div><b>Insurance:</b> {{INSURANCE_NAME}}</div>
</div>
HTML,
],
];
}
private function ensureDefaultIpdDocumentTemplatesSeeded(): void
{
$this->ensureIpdDocumentTemplateTable();
if (! $this->db->tableExists('ipd_document_templates')) {
return;
}
$table = $this->db->table('ipd_document_templates');
foreach ($this->defaultIpdDocumentTemplates() as $row) {
$formNo = (int) ($row['form_no'] ?? 0);
$templateName = (string) ($row['template_name'] ?? '');
if ($formNo <= 0 || $templateName === '') {
continue;
}
$exists = $this->db->table('ipd_document_templates')
->where('form_no', $formNo)
->where('template_name', $templateName)
->countAllResults();
if ($exists > 0) {
continue;
}
$table->insert([
'form_no' => $formNo,
'template_name' => $templateName,
'template_html' => (string) ($row['template_html'] ?? ''),
'status' => 1,
]);
}
}
public function ipd_document_templates()
{
if ($resp = $this->requireAnyPermission(['template.discharge'])) {
return $resp;
}
$this->ensureDefaultIpdDocumentTemplatesSeeded();
$notice = '';
$noticeType = 'success';
if (strtolower($this->request->getMethod()) === 'post') {
$id = (int) ($this->request->getPost('id') ?? 0);
$formNo = (int) ($this->request->getPost('form_no') ?? 0);
$templateName = trim((string) ($this->request->getPost('template_name') ?? ''));
$templateHtml = (string) ($this->request->getPost('template_html') ?? '');
$status = (int) ($this->request->getPost('status') ?? 1) === 1 ? 1 : 0;
if (! in_array($formNo, [1, 3, 5, 8, 9, 10, 11], true)) {
$notice = 'Invalid form number.';
$noticeType = 'danger';
} elseif ($templateName === '' || trim($templateHtml) === '') {
$notice = 'Form number, template name and HTML are required.';
$noticeType = 'danger';
} else {
$table = $this->db->table('ipd_document_templates');
$data = [
'form_no' => $formNo,
'template_name' => $templateName,
'template_html' => $templateHtml,
'status' => $status,
];
if ($id > 0) {
$table->where('id', $id)->update($data);
$notice = 'Template updated.';
} else {
$table->insert($data);
$notice = 'Template created.';
}
}
}
$editId = (int) ($this->request->getGet('edit') ?? 0);
$editRow = [];
if ($editId > 0) {
$editRow = $this->db->table('ipd_document_templates')
->where('id', $editId)
->get(1)
->getRowArray() ?? [];
}
$rows = $this->db->table('ipd_document_templates')
->orderBy('form_no', 'ASC')
->orderBy('id', 'ASC')
->get()
->getResultArray();
return view('Setting/Template/ipd_document_templates', [
'rows' => $rows,
'edit_row' => $editRow,
'notice' => $notice,
'notice_type' => $noticeType,
]);
}
public function ipd_document_template_delete(int $id)
{
if ($resp = $this->requireAnyPermission(['template.discharge'])) {
return $resp;
}
$id = (int) $id;
if ($id > 0 && $this->db->tableExists('ipd_document_templates')) {
$this->db->table('ipd_document_templates')->where('id', $id)->delete();
}
return redirect()->to(base_url('setting/template/ipd_document_templates'));
}
}

View File

@ -2,7 +2,13 @@
function str_to_MysqlDate(string $strDate): string
{
$strDate = trim($strDate);
if ($strDate !== '') {
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $strDate) === 1) {
return $strDate;
}
$date = explode('/', $strDate);
if (count($date) === 3) {
return $date[2] . '-' . $date[1] . '-' . $date[0];

View File

@ -6,8 +6,9 @@ use CodeIgniter\Model;
class BedCategoryModel extends Model
{
protected $table = 'bed_category_master';
protected $primaryKey = 'id';
protected $table = 'bed_category_master';
protected $primaryKey = 'id';
protected $returnType = 'object';
protected $allowedFields = [
'category_code',
'category_name',

View File

@ -6,8 +6,9 @@ use CodeIgniter\Model;
class BedMasterModel extends Model
{
protected $table = 'bed_master';
protected $table = 'bed_master';
protected $primaryKey = 'id';
protected $returnType = 'object';
protected $allowedFields = [
'bed_code',
'bed_number',

View File

@ -6,8 +6,9 @@ use CodeIgniter\Model;
class WardModel extends Model
{
protected $table = 'ward_master';
protected $table = 'ward_master';
protected $primaryKey = 'id';
protected $returnType = 'object';
protected $allowedFields = [
'ward_code',
'ward_name',
@ -27,6 +28,11 @@ class WardModel extends Model
'created_by',
];
public function getAll(): array
{
return $this->orderBy('ward_name', 'ASC')->findAll();
}
public function getAllActive(): array
{
return $this->where('status', 'active')

View File

@ -0,0 +1,69 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">Charges Invoice List</h3>
</div>
<div class="card-body">
<form class="row g-2 mb-3" onsubmit="event.preventDefault(); chargesInvoiceSearch();">
<div class="col-md-6">
<input type="text" class="form-control" id="charges_invoice_search" value="<?= esc($search ?? '') ?>" placeholder="Search by Invoice Code, Patient Code, Name, Mobile, Email">
</div>
<div class="col-md-6 d-flex gap-2">
<button type="button" class="btn btn-primary" onclick="chargesInvoiceSearch()">Search</button>
<button type="button" class="btn btn-light" onclick="load_form('<?= base_url('Invoice/chargeslist') ?>','Charges Invoice')">Reset</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="chargesInvoiceTable">
<thead class="table-light">
<tr>
<th>Invoice Code</th>
<th>Patient</th>
<th>Patient Code</th>
<th>Date</th>
<th>Mobile</th>
<th>Type</th>
<th>Amount</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach (($rows ?? []) as $row): ?>
<?php
$invType = ((int) ($row->insurance_id ?? 0) > 1) ? 'Org.' : 'Direct';
$invDate = ! empty($row->inv_date) ? date('d-m-Y', strtotime((string) $row->inv_date)) : '-';
?>
<tr>
<td><?= esc((string) ($row->invoice_code ?? '-')) ?></td>
<td><?= esc((string) ($row->p_fname ?? '-')) ?></td>
<td><?= esc((string) ($row->p_code ?? '-')) ?></td>
<td><?= esc($invDate) ?></td>
<td><?= esc((string) ($row->mphone1 ?? '-')) ?></td>
<td><?= esc($invType) ?></td>
<td><?= esc(number_format((float) ($row->net_amount ?? 0), 2, '.', '')) ?></td>
<td>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="load_form('<?= base_url('billing/charges/show') ?>/<?= (int) ($row->inv_id ?? 0) ?>','Charges Invoice');">Open</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
function chargesInvoiceSearch() {
var q = (document.getElementById('charges_invoice_search') || {}).value || '';
load_form('<?= base_url('Invoice/chargeslist') ?>?q=' + encodeURIComponent(q), 'Charges Invoice');
}
(function() {
if (window.jQuery && $.fn && $.fn.DataTable) {
$('#chargesInvoiceTable').DataTable({
pageLength: 25,
order: [[0, 'desc']]
});
}
})();
</script>

View File

@ -0,0 +1,69 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">OPD Invoice List</h3>
</div>
<div class="card-body">
<form class="row g-2 mb-3" onsubmit="event.preventDefault(); opdInvoiceSearch();">
<div class="col-md-6">
<input type="text" class="form-control" id="opd_invoice_search" value="<?= esc($search ?? '') ?>" placeholder="Search by OPD Code, Patient Code, Name, Mobile, Email">
</div>
<div class="col-md-6 d-flex gap-2">
<button type="button" class="btn btn-primary" onclick="opdInvoiceSearch()">Search</button>
<button type="button" class="btn btn-light" onclick="load_form('<?= base_url('Invoice/opdlist') ?>','OPD Invoice')">Reset</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="opdInvoiceTable">
<thead class="table-light">
<tr>
<th>OPD Code</th>
<th>Patient</th>
<th>Patient Code</th>
<th>Date</th>
<th>Doctor</th>
<th>Mobile</th>
<th>Type</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach (($rows ?? []) as $row): ?>
<?php
$invType = ((int) ($row->insurance_id ?? 0) > 1) ? 'Org.' : 'Direct';
$opdDate = ! empty($row->apointment_date) ? date('d-m-Y', strtotime((string) $row->apointment_date)) : '-';
?>
<tr>
<td><?= esc((string) ($row->opd_code ?? '-')) ?></td>
<td><?= esc((string) ($row->P_name ?? '-')) ?></td>
<td><?= esc((string) ($row->p_code ?? '-')) ?></td>
<td><?= esc($opdDate) ?></td>
<td><?= esc((string) ($row->doc_name ?? '-')) ?></td>
<td><?= esc((string) ($row->mphone1 ?? '-')) ?></td>
<td><?= esc($invType) ?></td>
<td>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="load_form('<?= base_url('Opd/invoice') ?>/<?= (int) ($row->opd_id ?? 0) ?>','OPD Invoice');">Open</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
function opdInvoiceSearch() {
var q = (document.getElementById('opd_invoice_search') || {}).value || '';
load_form('<?= base_url('Invoice/opdlist') ?>?q=' + encodeURIComponent(q), 'OPD Invoice');
}
(function() {
if (window.jQuery && $.fn && $.fn.DataTable) {
$('#opdInvoiceTable').DataTable({
pageLength: 25,
order: [[0, 'desc']]
});
}
})();
</script>

View File

@ -1,10 +1,16 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">Bed Master</h3>
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/bed-management') ?>','maindiv','Bed Management');">
<i class="bi bi-arrow-left"></i>
Back
</button>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-primary" type="button" onclick="load_form_div('<?= base_url('setting/admin/bed-status') ?>','maindiv','Bed Status');">
<i class="bi bi-geo-alt"></i>
Bed Status
</button>
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/bed-management') ?>','maindiv','Bed Management');">
<i class="bi bi-arrow-left"></i>
Back
</button>
</div>
</div>
<div class="card-body">
<div id="bedAlert" class="alert alert-danger d-none"></div>
@ -28,6 +34,9 @@
<option value="<?= esc($ward->id ?? 0) ?>"><?= esc($ward->ward_name ?? '') ?></option>
<?php } ?>
</select>
<div class="mt-1">
<button type="button" class="btn btn-link btn-sm p-0" onclick="load_form_div('<?= base_url('setting/admin/wards') ?>','maindiv','Wards');">Manage Wards</button>
</div>
</div>
<div class="col-md-3">
<label class="form-label">Category</label>

View File

@ -0,0 +1,115 @@
<div class="card">
<div class="card-header d-flex align-items-center flex-wrap gap-2">
<h3 class="card-title mb-0">Bed Status (IPD Location Finder)</h3>
</div>
<div class="card-body">
<div class="row g-2 mb-2">
<div class="col-md-3">
<select class="form-select" id="filterDepartment">
<option value="">All Department</option>
<?php foreach (($departments ?? []) as $department): ?>
<?php $label = trim((string) ($department->vName ?? '')); ?>
<?php if ($label === '') {
continue;
} ?>
<option value="<?= esc($label) ?>"><?= esc($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="filterWard">
<option value="">All Wards</option>
<?php foreach (($wards ?? []) as $ward): ?>
<option value="<?= esc((string) ($ward->ward_name ?? '')) ?>"><?= esc((string) ($ward->ward_name ?? '')) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="bedStatusTable">
<thead class="table-light">
<tr>
<th>IPD Code</th>
<th>Patient Code</th>
<th>Patient Name</th>
<th>Mobile</th>
<th>Department</th>
<th>Ward</th>
<th>Bed No</th>
<th>Admit Date</th>
</tr>
</thead>
<tbody>
<?php foreach (($rows ?? []) as $row): ?>
<?php
$fullName = trim((string) (($row->p_fname ?? '') . ' ' . ($row->p_lname ?? '')));
if ($fullName === '') {
$fullName = '-';
}
$mobile = trim((string) ($row->mphone1 ?? ''));
if ($mobile === '' || $mobile === '0') {
$mobile = trim((string) ($row->mphone2 ?? ''));
}
if ($mobile === '' || $mobile === '0') {
$mobile = '-';
}
$bedNo = trim((string) ($row->bed_number ?? ''));
if ($bedNo === '' || $bedNo === '0') {
$bedNo = trim((string) ($row->bed_code ?? ''));
}
if ($bedNo === '') {
$bedNo = '-';
}
$departmentName = trim((string) ($row->department_name ?? ''));
if ($departmentName === '') {
$departmentName = 'All Department';
}
$wardName = trim((string) ($row->ward_name ?? ''));
if ($wardName === '') {
$wardName = '-';
}
$admitDate = trim((string) ($row->register_date ?? ''));
if ($admitDate === '') {
$admitDate = '-';
}
?>
<tr>
<td><?= esc((string) ($row->ipd_code ?? '-')) ?></td>
<td><?= esc((string) ($row->p_code ?? '-')) ?></td>
<td><?= esc($fullName) ?></td>
<td><?= esc($mobile) ?></td>
<td><?= esc($departmentName) ?></td>
<td><?= esc($wardName) ?></td>
<td><?= esc($bedNo) ?></td>
<td><?= esc($admitDate) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
(function() {
if (!window.jQuery || !$.fn || !$.fn.DataTable) {
return;
}
var table = $('#bedStatusTable').DataTable({
order: [[0, 'desc']],
pageLength: 25
});
function bindFilter(selector, columnIndex) {
var $select = $(selector);
$select.on('change', function() {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
table.column(columnIndex).search(val ? '^' + val + '$' : '', true, false).draw();
});
}
bindFilter('#filterDepartment', 4);
bindFilter('#filterWard', 5);
})();
</script>

View File

@ -0,0 +1,143 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">Department Master</h3>
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/bed-management') ?>','maindiv','Bed Management');">
<i class="bi bi-arrow-left"></i>
Back
</button>
</div>
<div class="card-body">
<?php if (($tableMissing ?? false) === true): ?>
<div class="alert alert-danger">Department table (hc_department) is not available in this database.</div>
<?php else: ?>
<div id="departmentAlert" class="alert alert-danger d-none"></div>
<form id="departmentForm">
<?= csrf_field() ?>
<input type="hidden" id="department_id" name="id" value="0">
<div class="row g-3">
<div class="col-md-5">
<label class="form-label">Department Name</label>
<input class="form-control" id="department_name" name="department_name" required>
</div>
<div class="col-md-3">
<label class="form-label">Department Type ID</label>
<input class="form-control" id="hc_type_id" name="hc_type_id" type="number" min="0">
</div>
<div class="col-md-4 d-flex align-items-end gap-2">
<button class="btn btn-primary" type="submit">Save Department</button>
<button class="btn btn-light" type="button" id="resetDepartment">Clear</button>
</div>
</div>
</form>
<hr/>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="departmentTable">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type ID</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach (($departments ?? []) as $row) {
$payload = json_encode($row);
?>
<tr>
<td><?= esc($row->iId ?? 0) ?></td>
<td><?= esc($row->vName ?? '') ?></td>
<td><?= esc($row->hc_type_id ?? '') ?></td>
<td>
<button class="btn btn-outline-secondary btn-sm edit-department" data-item="<?= esc($payload, 'attr') ?>">Edit</button>
<button class="btn btn-outline-danger btn-sm delete-department" data-id="<?= esc($row->iId ?? 0) ?>">Delete</button>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
<script>
(function() {
var form = document.getElementById('departmentForm');
var resetButton = document.getElementById('resetDepartment');
var alertBox = document.getElementById('departmentAlert');
if (!form || !window.jQuery) {
return;
}
function showAlert(message) {
alertBox.textContent = message;
alertBox.classList.remove('d-none');
}
function clearAlert() {
alertBox.textContent = '';
alertBox.classList.add('d-none');
}
function clearForm() {
form.reset();
$('#department_id').val(0);
clearAlert();
}
resetButton.addEventListener('click', clearForm);
$(form).on('submit', function(event) {
event.preventDefault();
clearAlert();
$.post('<?= base_url('setting/admin/departments/save') ?>', $(form).serialize())
.done(function(resp) {
if (resp && resp.error_text) {
showAlert(resp.error_text);
return;
}
load_form_div('<?= base_url('setting/admin/departments') ?>', 'maindiv', 'Departments');
})
.fail(function() {
showAlert('Request failed.');
});
});
$('.edit-department').on('click', function() {
var item = $(this).data('item');
if (!item) {
return;
}
$('#department_id').val(item.iId || 0);
$('#department_name').val(item.vName || '');
$('#hc_type_id').val(item.hc_type_id || '');
});
$('.delete-department').on('click', function() {
if (!confirm('Delete this department?')) {
return;
}
clearAlert();
$.post('<?= base_url('setting/admin/departments/delete') ?>', {
id: $(this).data('id'),
'<?= csrf_token() ?>': $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>'
}).done(function(resp) {
if (resp && resp.error_text) {
showAlert(resp.error_text);
return;
}
load_form_div('<?= base_url('setting/admin/departments') ?>', 'maindiv', 'Departments');
});
});
if (window.jQuery && $.fn && $.fn.DataTable) {
$('#departmentTable').DataTable();
}
})();
</script>

View File

@ -10,6 +10,24 @@
</div>
<div class="row g-3">
<div class="col-6 col-md-3">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('setting/admin/bed-status') ?>','maindiv','Bed Status');">
<i class="bi bi-geo-alt"></i>
<span>Bed Status</span>
</a>
</div>
<div class="col-6 col-md-3">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('setting/admin/departments') ?>','maindiv','Departments');">
<i class="bi bi-diagram-2"></i>
<span>Departments</span>
</a>
</div>
<div class="col-6 col-md-3">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('setting/admin/wards') ?>','maindiv','Wards');">
<i class="bi bi-diagram-3"></i>
<span>Wards</span>
</a>
</div>
<div class="col-6 col-md-3">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('setting/admin/bed-categories') ?>','maindiv','Bed Categories');">
<i class="bi bi-layers"></i>

View File

@ -0,0 +1,271 @@
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">Ward Master</h3>
<button class="btn btn-light" type="button" onclick="load_form_div('<?= base_url('setting/admin/bed-management') ?>','maindiv','Bed Management');">
<i class="bi bi-arrow-left"></i>
Back
</button>
</div>
<div class="card-body">
<div id="wardAlert" class="alert alert-danger d-none"></div>
<form id="wardForm">
<?= csrf_field() ?>
<input type="hidden" id="ward_id" name="id" value="0">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Ward Code</label>
<input class="form-control" id="ward_code" name="ward_code" required>
</div>
<div class="col-md-3">
<label class="form-label">Ward Name</label>
<input class="form-control" id="ward_name" name="ward_name" required>
</div>
<div class="col-md-2">
<label class="form-label">Department</label>
<select class="form-select" id="department_id" name="department_id">
<?php foreach (($departments ?? []) as $department) { ?>
<option value="<?= esc($department->iId ?? 0) ?>"><?= esc($department->vName ?? '') ?></option>
<?php } ?>
</select>
<div class="mt-1">
<button type="button" class="btn btn-link btn-sm p-0" onclick="load_form_div('<?= base_url('setting/admin/departments') ?>','maindiv','Departments');">Manage Departments</button>
</div>
</div>
<div class="col-md-2">
<label class="form-label">Floor</label>
<input class="form-control" id="floor_number" name="floor_number" type="number">
</div>
<div class="col-md-2">
<label class="form-label">Capacity</label>
<input class="form-control" id="total_capacity" name="total_capacity" type="number" min="0" value="0">
</div>
<div class="col-md-3">
<label class="form-label">Ward Type</label>
<select class="form-select" id="ward_type" name="ward_type">
<option value="General">General</option>
<option value="Private">Private</option>
<option value="Semi-Private">Semi-Private</option>
<option value="Deluxe">Deluxe</option>
<option value="ICU">ICU</option>
<option value="NICU">NICU</option>
<option value="PICU">PICU</option>
<option value="HDU">HDU</option>
<option value="ICCU">ICCU</option>
<option value="CCU">CCU</option>
<option value="Emergency">Emergency</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Gender Type</label>
<select class="form-select" id="gender_type" name="gender_type">
<option value="unisex">Unisex</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Ward Category</label>
<select class="form-select" id="ward_category" name="ward_category">
<option value="adult">Adult</option>
<option value="pediatric">Pediatric</option>
<option value="maternity">Maternity</option>
<option value="isolation">Isolation</option>
<option value="surgical">Surgical</option>
<option value="medical">Medical</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="under_maintenance">Under Maintenance</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Remarks</label>
<input class="form-control" id="remarks" name="remarks">
</div>
<div class="col-md-6 d-flex align-items-end gap-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="has_oxygen" name="has_oxygen">
<label class="form-check-label" for="has_oxygen">Oxygen</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="has_suction" name="has_suction">
<label class="form-check-label" for="has_suction">Suction</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="has_monitor" name="has_monitor">
<label class="form-check-label" for="has_monitor">Monitor</label>
</div>
</div>
<div class="col-md-12 d-flex gap-2">
<button class="btn btn-primary" type="submit">Save Ward</button>
<button class="btn btn-light" type="button" id="resetWard">Clear</button>
</div>
</div>
</form>
<hr/>
<div class="row g-2 mb-2">
<div class="col-md-3">
<select class="form-select" id="filterWardType">
<option value="">All Types</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="filterWardStatus">
<option value="">All Status</option>
</select>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="wardTable">
<thead class="table-light">
<tr>
<th>Code</th>
<th>Name</th>
<th>Department</th>
<th>Type</th>
<th>Capacity</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach (($wards ?? []) as $row) {
$payload = json_encode($row);
?>
<tr>
<td><?= esc($row->ward_code ?? '') ?></td>
<td><?= esc($row->ward_name ?? '') ?></td>
<td><?= esc($row->department_name ?? 'All Department') ?></td>
<td><?= esc($row->ward_type ?? '') ?></td>
<td><?= esc($row->total_capacity ?? 0) ?></td>
<td><?= esc($row->status ?? '') ?></td>
<td>
<button class="btn btn-outline-secondary btn-sm edit-ward" data-item="<?= esc($payload, 'attr') ?>">Edit</button>
<button class="btn btn-outline-danger btn-sm delete-ward" data-id="<?= esc($row->id ?? 0) ?>">Delete</button>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
(function() {
var form = document.getElementById('wardForm');
var resetButton = document.getElementById('resetWard');
var alertBox = document.getElementById('wardAlert');
if (!form || !window.jQuery) {
return;
}
function showAlert(message) {
alertBox.textContent = message;
alertBox.classList.remove('d-none');
}
function clearAlert() {
alertBox.textContent = '';
alertBox.classList.add('d-none');
}
function clearForm() {
form.reset();
$('#ward_id').val(0);
$('#total_capacity').val(0);
clearAlert();
}
resetButton.addEventListener('click', clearForm);
$(form).on('submit', function(event) {
event.preventDefault();
clearAlert();
$.post('<?= base_url('setting/admin/wards/save') ?>', $(form).serialize())
.done(function(resp) {
if (resp && resp.error_text) {
showAlert(resp.error_text);
return;
}
load_form_div('<?= base_url('setting/admin/wards') ?>', 'maindiv', 'Wards');
})
.fail(function() {
showAlert('Request failed.');
});
});
$('.edit-ward').on('click', function() {
var item = $(this).data('item');
if (!item) {
return;
}
$('#ward_id').val(item.id || 0);
$('#ward_code').val(item.ward_code || '');
$('#ward_name').val(item.ward_name || '');
$('#department_id').val(item.department_id !== undefined ? item.department_id : 0);
$('#floor_number').val(item.floor_number || '');
$('#total_capacity').val(item.total_capacity || 0);
$('#ward_type').val(item.ward_type || 'General');
$('#gender_type').val(item.gender_type || 'unisex');
$('#ward_category').val(item.ward_category || 'adult');
$('#status').val(item.status || 'active');
$('#remarks').val(item.remarks || '');
$('#has_oxygen').prop('checked', String(item.has_oxygen) === '1');
$('#has_suction').prop('checked', String(item.has_suction) === '1');
$('#has_monitor').prop('checked', String(item.has_monitor) === '1');
});
$('.delete-ward').on('click', function() {
if (!confirm('Delete this ward?')) {
return;
}
clearAlert();
$.post('<?= base_url('setting/admin/wards/delete') ?>', {
id: $(this).data('id'),
'<?= csrf_token() ?>': $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>'
}).done(function(resp) {
if (resp && resp.error_text) {
showAlert(resp.error_text);
return;
}
load_form_div('<?= base_url('setting/admin/wards') ?>', 'maindiv', 'Wards');
});
});
if (window.jQuery && $.fn && $.fn.DataTable) {
var table = $('#wardTable').DataTable();
function initFilter(selectId, columnIndex) {
var select = $(selectId);
if (!select.length) {
return;
}
var column = table.column(columnIndex);
var values = column.data().unique().sort();
values.each(function(d) {
if (d !== null && d !== '') {
select.append('<option value="' + d + '">' + d + '</option>');
}
});
select.on('change', function() {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
}
initFilter('#filterWardType', 3);
initFilter('#filterWardStatus', 5);
}
})();
</script>

View File

@ -11,6 +11,13 @@
<div class="card-body">
<?php $errors = $errors ?? session('errors'); ?>
<?php $formData = $formData ?? []; ?>
<?php
$doctorDobValue = trim((string) ($formData['datepicker_dob'] ?? ''));
if ($doctorDobValue !== '' && preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $doctorDobValue) === 1) {
[$dobDay, $dobMonth, $dobYear] = explode('/', $doctorDobValue);
$doctorDobValue = $dobYear . '-' . $dobMonth . '-' . $dobDay;
}
?>
<?php if (! empty($errors)) : ?>
<div class="alert alert-danger">
<?php foreach ((array) $errors as $error) : ?>
@ -61,7 +68,7 @@
</div>
<div class="col-md-4">
<label class="form-label">Date of Birth</label>
<input class="form-control datepicker" name="datepicker_dob" type="text" value="<?= esc($formData['datepicker_dob'] ?? '') ?>">
<input class="form-control" name="datepicker_dob" type="date" value="<?= esc($doctorDobValue) ?>">
</div>
</div>
<?php

View File

@ -21,6 +21,12 @@ if (!empty($data) && !empty($data[0])) {
}
}
?>
<?php
$doctorDobValue = '';
if (!empty($data) && !empty($data[0]) && !empty($data[0]->dob)) {
$doctorDobValue = trim((string) $data[0]->dob);
}
?>
<?php if (! empty($data)) : ?>
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
@ -77,7 +83,7 @@ if (!empty($data) && !empty($data[0])) {
</div>
<div class="col-md-3">
<label class="form-label">Date of Birth</label>
<input class="form-control datepicker" name="datepicker_dob" value="<?= esc(MysqlDate_to_str($data[0]->dob ?? '')) ?>" type="text" />
<input class="form-control" name="datepicker_dob" value="<?= esc($doctorDobValue) ?>" type="date" />
</div>
</div>
<div class="row g-3 mt-1">

View File

@ -206,6 +206,12 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<span>IPD Discharge Template</span>
</a>
</div>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('setting/template/ipd_document_templates') ?>','maindiv','IPD Document Master');">
<span class="tile-icon"><i class="bi bi-file-earmark-richtext"></i></span>
<span>IPD Document Master</span>
</a>
</div>
<?php } ?>
<?php if ($canDiagnosisPrintTemplate) { ?>

View File

@ -0,0 +1,173 @@
<?php
$rows = $rows ?? [];
$edit = $edit_row ?? [];
$notice = (string) ($notice ?? '');
$noticeType = (string) ($notice_type ?? 'success');
$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>');
$status = (int) ($edit['status'] ?? 1);
$formMap = [
1 => 'Print Face Form',
3 => 'Self Declaration Form',
5 => 'Admission Form',
8 => 'Progress Notes',
9 => 'Fluid In / Out',
10 => 'Sticker [2 x 6]',
11 => 'Sticker [2 x 8]',
];
?>
<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">IPD Document Master</h6>
<a class="btn btn-sm btn-outline-secondary" href="javascript:load_form_div('<?= base_url('setting/template/ipd_document_templates') ?>','maindiv','IPD Document Master');">Reset</a>
</div>
<div class="card-body">
<?php if ($notice !== ''): ?>
<div class="alert alert-<?= esc($noticeType) ?> py-2 mb-3"><?= esc($notice) ?></div>
<?php endif; ?>
<div class="alert alert-info py-2 small">
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>.
</div>
<div class="alert alert-warning py-2 small">
Local-language design supported. You can copy HTML blocks from old HMS files under <strong>old_code_ci3/views/IPD_Format</strong> and adapt them here.
</div>
<form method="post" action="<?= base_url('setting/template/ipd_document_templates') ?>" class="mb-4" id="ipd_document_template_form">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= $editId ?>">
<div class="row g-2">
<div class="col-md-3">
<label class="form-label small">Form</label>
<select name="form_no" class="form-select form-select-sm" required>
<?php foreach ($formMap as $id => $label): ?>
<option value="<?= (int) $id ?>" <?= $formNo === (int) $id ? 'selected' : '' ?>><?= (int) $id ?> - <?= esc($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label small">Template Name</label>
<input type="text" name="template_name" class="form-control form-control-sm" value="<?= esc($templateName) ?>" required>
</div>
<div class="col-md-3">
<label class="form-label small">Status</label>
<select name="status" class="form-select form-select-sm">
<option value="1" <?= $status === 1 ? 'selected' : '' ?>>Active</option>
<option value="0" <?= $status === 0 ? 'selected' : '' ?>>Inactive</option>
</select>
</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>
<small class="text-muted">Use Source mode for raw HTML from legacy forms.</small>
</div>
</div>
<div class="mt-3 d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm"><?= $editId > 0 ? 'Update Template' : 'Create Template' ?></button>
<?php if ($editId > 0): ?>
<a class="btn btn-outline-secondary btn-sm" href="javascript:load_form_div('<?= base_url('setting/template/ipd_document_templates') ?>','maindiv','IPD Document Master');">Cancel Edit</a>
<?php endif; ?>
</div>
</form>
<div class="table-responsive">
<table class="table table-sm table-bordered align-middle">
<thead class="table-light">
<tr>
<th style="width:60px;">ID</th>
<th style="width:140px;">Form</th>
<th>Template Name</th>
<th style="width:90px;">Status</th>
<th style="width:180px;">Action</th>
</tr>
</thead>
<tbody>
<?php if (empty($rows)): ?>
<tr><td colspan="5" class="text-center text-muted">No templates found.</td></tr>
<?php else: ?>
<?php foreach ($rows as $row): ?>
<?php $fNo = (int) ($row['form_no'] ?? 0); ?>
<tr>
<td><?= (int) ($row['id'] ?? 0) ?></td>
<td><?= $fNo ?> - <?= esc($formMap[$fNo] ?? 'Unknown') ?></td>
<td><?= esc((string) ($row['template_name'] ?? '')) ?></td>
<td><?= (int) ($row['status'] ?? 0) === 1 ? 'Active' : 'Inactive' ?></td>
<td>
<a class="btn btn-outline-primary btn-sm" href="javascript:load_form_div('<?= base_url('setting/template/ipd_document_templates?edit=' . (int) ($row['id'] ?? 0)) ?>','maindiv','IPD Document Master');">Edit</a>
<a class="btn btn-outline-danger btn-sm" href="javascript:if(confirm('Delete this template?')) load_form_div('<?= base_url('setting/template/ipd_document_templates/delete/' . (int) ($row['id'] ?? 0)) ?>','maindiv','IPD Document Master');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</section>
<script>
(function () {
var form = document.getElementById('ipd_document_template_form');
var editorFieldId = 'ipd_document_template_editor';
function initEditor() {
if (!window.CKEDITOR) {
return;
}
CKEDITOR.config.versionCheck = false;
CKEDITOR.config.removePlugins = '';
if (CKEDITOR.instances[editorFieldId]) {
CKEDITOR.instances[editorFieldId].destroy(true);
}
if (document.getElementById(editorFieldId)) {
CKEDITOR.replace(editorFieldId, {
height: 420,
toolbar: [
{ name: 'document', items: ['Source'] },
{ name: 'clipboard', items: ['Undo', 'Redo'] },
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'RemoveFormat'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', 'Outdent', 'Indent', 'Blockquote'] },
{ name: 'links', items: ['Link', 'Unlink'] },
{ name: 'insert', items: ['Table', 'HorizontalRule', 'SpecialChar'] },
{ name: 'styles', items: ['Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] },
{ name: 'tools', items: ['Maximize'] }
]
});
}
}
function syncEditor() {
if (!window.CKEDITOR) {
return;
}
if (CKEDITOR.instances[editorFieldId]) {
CKEDITOR.instances[editorFieldId].updateElement();
}
}
initEditor();
if (form) {
form.addEventListener('submit', function () {
syncEditor();
});
}
})();
</script>

View File

@ -0,0 +1,264 @@
<?php
$formNo = (int) ($form_no ?? 1);
$formMeta = is_array($form_meta ?? null) ? $form_meta : ['title' => 'IPD Form'];
$ipd = $ipd_info ?? null;
$person = $person_info ?? null;
$doctorList = is_array($doctor_names ?? null) ? $doctor_names : [];
$doctorText = ! empty($doctorList) ? implode(', ', $doctorList) : '-';
$insuranceText = trim((string) ($insurance_name ?? ''));
$hospitalName = trim((string) ($hospital_name ?? 'Hospital'));
$hospitalAddress = trim((string) ($hospital_address ?? ''));
$generatedAt = (string) ($generated_at ?? date('d-m-Y h:i A'));
$patientName = trim((string) (($person->title ?? '') . ' ' . ($person->p_fname ?? '')));
if ($patientName === '') {
$patientName = '-';
}
$patientAge = (string) ($person->age ?? '-');
$patientGender = (string) ($person->xgender ?? '-');
$patientCode = (string) ($person->p_code ?? '-');
$ipdCode = (string) ($ipd->ipd_code ?? '-');
$admitDate = (string) ($ipd->str_register_date ?? '');
if ($admitDate === '') {
$admitDate = (string) ($ipd->register_date ?? '-');
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: DejaVu Sans, sans-serif;
font-size: 11px;
color: #111;
line-height: 1.35;
}
.doc-header {
border-bottom: 1px solid #999;
margin-bottom: 8px;
padding-bottom: 6px;
}
.doc-title {
font-size: 16px;
font-weight: 700;
margin: 0 0 2px;
}
.doc-subtitle {
margin: 0;
font-size: 11px;
}
.meta-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 8px;
}
.meta-table td {
border: 1px solid #bbb;
padding: 4px 6px;
vertical-align: top;
}
.section-title {
font-size: 12px;
font-weight: 700;
margin: 10px 0 6px;
text-transform: uppercase;
}
.grid-table {
width: 100%;
border-collapse: collapse;
}
.grid-table th,
.grid-table td {
border: 1px solid #888;
padding: 4px;
vertical-align: top;
}
.spacer-row td {
height: 24px;
}
.sign-row {
margin-top: 20px;
width: 100%;
border-collapse: collapse;
}
.sign-row td {
width: 50%;
padding-top: 20px;
font-size: 10px;
}
.sticker-wrap {
border: 1px dashed #444;
padding: 4px;
margin: 0;
page-break-inside: avoid;
}
.sticker-title {
margin: 0 0 3px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.tiny {
font-size: 9px;
}
</style>
</head>
<body>
<?php if ($formNo === 10 || $formNo === 11) : ?>
<div class="sticker-wrap">
<p class="sticker-title"><?= esc($hospitalName) ?></p>
<div class="tiny">
<?php if ($hospitalAddress !== '') : ?>
<div><?= esc($hospitalAddress) ?></div>
<?php endif; ?>
<div><strong>Patient:</strong> <?= esc($patientName) ?></div>
<div><strong>Age/Gender:</strong> <?= esc($patientAge) ?> / <?= esc($patientGender) ?></div>
<div><strong>UHID:</strong> <?= esc($patientCode) ?></div>
<div><strong>IPD:</strong> <?= esc($ipdCode) ?></div>
<div><strong>Doctor:</strong> <?= esc($doctorText) ?></div>
<div><strong>Admit:</strong> <?= esc($admitDate) ?></div>
<?php if ($insuranceText !== '') : ?>
<div><strong>Insurance:</strong> <?= esc($insuranceText) ?></div>
<?php endif; ?>
</div>
</div>
<?php else : ?>
<div class="doc-header">
<p class="doc-title"><?= esc($hospitalName) ?></p>
<?php if ($hospitalAddress !== '') : ?>
<p class="doc-subtitle"><?= esc($hospitalAddress) ?></p>
<?php endif; ?>
<p class="doc-subtitle"><strong><?= esc((string) ($formMeta['title'] ?? 'IPD Form')) ?></strong></p>
</div>
<table class="meta-table">
<tr>
<td><strong>Patient Name:</strong> <?= esc($patientName) ?></td>
<td><strong>Age / Gender:</strong> <?= esc($patientAge) ?> / <?= esc($patientGender) ?></td>
<td><strong>UHID:</strong> <?= esc($patientCode) ?></td>
</tr>
<tr>
<td><strong>IPD Code:</strong> <?= esc($ipdCode) ?></td>
<td><strong>Date of Admission:</strong> <?= esc($admitDate) ?></td>
<td><strong>Doctor:</strong> <?= esc($doctorText) ?></td>
</tr>
<tr>
<td colspan="3">
<strong>Insurance:</strong> <?= esc($insuranceText !== '' ? $insuranceText : 'Direct') ?>
<span style="float:right;"><strong>Generated:</strong> <?= esc($generatedAt) ?></span>
</td>
</tr>
</table>
<?php if ($formNo === 1) : ?>
<div class="section-title">Patient Consent</div>
<p>
I willingly agree for treatment, examination, consultation, investigations, medication, and medically indicated procedures during this IPD admission.
I confirm that the information shared with the treating team is true and complete to the best of my knowledge.
</p>
<p>
I understand that the hospital may take emergency decisions in the patient's best interest when immediate care is required.
I also agree to follow hospital rules regarding safety, infection control, and billing process.
</p>
<table class="sign-row">
<tr>
<td>Patient / Attendant Signature: ____________________</td>
<td style="text-align:right;">Hospital Representative: ____________________</td>
</tr>
</table>
<?php elseif ($formNo === 3) : ?>
<div class="section-title">Self Declaration</div>
<p>
I, ____________________, declare that patient <?= esc($patientName) ?> is admitted under IPD code <?= esc($ipdCode) ?>
on date <?= esc($admitDate) ?>.
<?php if ($insuranceText !== '') : ?>
The patient is covered under insurance provider <?= esc($insuranceText) ?>.
<?php else : ?>
This admission is under self / direct payment category.
<?php endif; ?>
</p>
<p>
I confirm that all submitted details and documents are genuine and I will remain responsible for any differences in authorization, exclusions, or final bill settlement.
</p>
<table class="sign-row">
<tr>
<td>Declarant Signature: ____________________</td>
<td style="text-align:right;">Date: ____________________</td>
</tr>
</table>
<?php elseif ($formNo === 5) : ?>
<div class="section-title">Admission Assessment</div>
<table class="grid-table">
<tr><th style="width:35%;">Assessment Item</th><th>Observation</th></tr>
<tr class="spacer-row"><td>Chief Complaint</td><td></td></tr>
<tr class="spacer-row"><td>History of Present Illness</td><td></td></tr>
<tr class="spacer-row"><td>Past Medical / Surgical History</td><td></td></tr>
<tr class="spacer-row"><td>Allergy History</td><td></td></tr>
<tr class="spacer-row"><td>Medication History</td><td></td></tr>
<tr class="spacer-row"><td>Physical Examination</td><td></td></tr>
<tr class="spacer-row"><td>Provisional Diagnosis</td><td></td></tr>
<tr class="spacer-row"><td>Initial Plan</td><td></td></tr>
</table>
<?php elseif ($formNo === 8) : ?>
<div class="section-title">Progress Notes and Doctor Order</div>
<table class="grid-table">
<tr>
<th style="width:14%;">Date</th>
<th style="width:14%;">Time</th>
<th>Progress Notes / Orders</th>
<th style="width:18%;">Doctor Sign</th>
</tr>
<?php for ($i = 0; $i < 18; $i++) : ?>
<tr class="spacer-row">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<?php endfor; ?>
</table>
<?php elseif ($formNo === 9) : ?>
<div class="section-title">Fluid In / Out</div>
<table class="grid-table">
<tr>
<th style="width:10%;">S.No.</th>
<th style="width:20%;">Date / Time</th>
<th style="width:20%;">Fluid In (ml)</th>
<th style="width:20%;">Fluid Out (ml)</th>
<th>Remark</th>
</tr>
<?php for ($i = 1; $i <= 24; $i++) : ?>
<tr class="spacer-row">
<td style="text-align:center;"><?= $i ?></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<?php endfor; ?>
<tr>
<td colspan="2" style="text-align:right;"><strong>Total</strong></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<?php endif; ?>
<?php endif; ?>
</body>
</html>

View File

@ -180,6 +180,9 @@ if ($person) {
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab_discharge" data-url="<?= site_url('billing/ipd/panel/' . (int) ($ipd->id ?? 0) . '/tab/discharge-process') ?>" type="button" role="tab">Discharge Process</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab_documents" data-url="<?= site_url('billing/ipd/panel/' . (int) ($ipd->id ?? 0) . '/tab/documents') ?>" type="button" role="tab">Documents</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="tab_admission" role="tabpanel">
@ -209,6 +212,9 @@ if ($person) {
<div class="tab-pane fade" id="tab_discharge" role="tabpanel">
<div class="ipd-tab-panel" id="tab_discharge_content">Loading...</div>
</div>
<div class="tab-pane fade" id="tab_documents" role="tabpanel">
<div class="ipd-tab-panel" id="tab_documents_content">Loading...</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,53 @@
<?php
$ipdId = (int) ($ipd_id ?? 0);
$ipdCode = (string) ($ipd_info->ipd_code ?? ('IPD-' . $ipdId));
$documents = [
['id' => 1, 'label' => 'Print Face Form'],
['id' => 5, 'label' => 'Admission Form'],
['id' => 10, 'label' => 'Sticker [2 x 6]'],
['id' => 11, 'label' => 'Sticker [2 x 8]'],
['id' => 8, 'label' => 'Progress Notes'],
['id' => 9, 'label' => 'Fluid In / Out'],
['id' => 3, 'label' => 'Self Declaration Form'],
];
?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
<h6 class="mb-0">Pre-Print Documents</h6>
<div class="d-flex align-items-center gap-2">
<small class="text-muted">IPD: <?= esc($ipdCode) ?></small>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="load_form_div('<?= base_url('setting/template/ipd_document_templates') ?>','maindiv','IPD Document Master');">
<i class="bi bi-pencil-square"></i> Document Master
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm table-striped align-middle mb-0">
<thead>
<tr>
<th style="width: 80px;">Form ID</th>
<th>Document</th>
<th style="width: 170px;" class="text-end">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($documents as $document) : ?>
<tr>
<td><?= (int) $document['id'] ?></td>
<td><?= esc($document['label']) ?></td>
<td class="text-end">
<a class="btn btn-sm btn-outline-primary" target="_blank" href="<?= site_url('IpdNew/show_ipd_form/' . $ipdId . '/' . (int) $document['id']) ?>">
<i class="bi bi-printer"></i> Print
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -1,9 +1,16 @@
<?php
$canEdit = false;
$user = auth()->user();
if ($user && method_exists($user, 'inGroup')) {
$canEdit = $user->inGroup('OPDEdit');
if ($user) {
if (method_exists($user, 'can') && $user->can('billing.opd.edit')) {
$canEdit = true;
}
if (! $canEdit && method_exists($user, 'inGroup')) {
$canEdit = $user->inGroup('OPDEdit');
}
}
$paymentHistoryRows = $payment_history_rows ?? [];
?>
<section class="content-header">
<h1>OPD Invoice</h1>
@ -223,6 +230,50 @@ if ($user && method_exists($user, 'inGroup')) {
</div>
<?php endif; ?>
<hr />
<div class="row g-3">
<div class="col-12">
<h5 class="mb-2">Payment History</h5>
<div class="table-responsive">
<table class="table table-sm table-bordered align-middle">
<thead class="table-light">
<tr>
<th>Date</th>
<th>Type</th>
<th>Mode</th>
<th class="text-end">Amount</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<?php if (!empty($paymentHistoryRows)) : ?>
<?php foreach ($paymentHistoryRows as $historyRow) : ?>
<?php
$isRefund = (int) ($historyRow->credit_debit ?? 0) === 1;
$historyDate = trim((string) ($historyRow->payment_date ?? ''));
if ($historyDate === '') {
$historyDate = trim((string) ($historyRow->insert_time ?? ''));
}
?>
<tr>
<td><?= esc($historyDate) ?></td>
<td><?= $isRefund ? '<span class="text-danger">Refund</span>' : '<span class="text-success">Received</span>' ?></td>
<td><?= esc($historyRow->payment_mode_str ?? '') ?></td>
<td class="text-end"><?= esc(number_format((float) ($historyRow->amount ?? 0), 2, '.', '')) ?></td>
<td><?= esc($historyRow->remark ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="5" class="text-center text-muted">No payment history entries.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php if ($canEdit && ((int) ($opd_master[0]->opd_status ?? 0) === 1 || (int) ($opd_master[0]->opd_status ?? 0) === 2)) : ?>
<hr />
<div class="row g-3">
@ -419,7 +470,8 @@ if ($user && method_exists($user, 'inGroup')) {
"oid": $('#oid').val(),
"input_remark": $('#input_remark').val(),
[csrf.name]: csrf.value
}, function() {
}, function(data) {
updateCsrf(data);
load_form('<?= base_url('Opd/invoice') ?>/' + $('#oid').val());
});
}

View File

@ -39,8 +39,24 @@
}
}
}
$serverTimeZoneId = 'Asia/Kolkata';
$serverNow = new DateTimeImmutable('now', new DateTimeZone($serverTimeZoneId));
$serverTimeZoneLabel = trim((string) $serverNow->format('T'));
if ($serverTimeZoneLabel === '' || $serverTimeZoneLabel === 'GMT') {
$serverTimeZoneLabel = $serverTimeZoneId;
}
$serverEpochMs = (int) round(microtime(true) * 1000);
$serverDisplayTime = $serverNow->format('d-m-Y h:i A') . ' (' . $serverTimeZoneLabel . ')';
?>
<ul class="d-flex align-items-center">
<li class="nav-item pe-3 d-none d-md-flex align-items-center text-nowrap" title="Server time">
<i class="bi bi-clock me-1"></i>
<span id="header-server-datetime"
data-server-epoch-ms="<?= esc((string) $serverEpochMs) ?>"
data-server-timezone-id="<?= esc($serverTimeZoneId) ?>"
data-server-timezone-label="<?= esc($serverTimeZoneLabel) ?>"><?= esc($serverDisplayTime) ?></span>
</li>
<li class="nav-item dropdown pe-3">
<a class="nav-link nav-profile d-flex align-items-center pe-0" href="#" data-bs-toggle="dropdown">
<img src="<?= base_url('assets/img/profile-img.jpg') ?>" alt="Profile" class="rounded-circle">
@ -75,4 +91,95 @@
</ul>
</li>
</ul>
</nav>
</nav>
<script>
(function () {
var el = document.getElementById('header-server-datetime');
if (!el) {
return;
}
var serverEpochMs = Number(el.getAttribute('data-server-epoch-ms') || '0');
if (!Number.isFinite(serverEpochMs) || serverEpochMs <= 0) {
return;
}
var tzId = String(el.getAttribute('data-server-timezone-id') || '');
var tzLabel = String(el.getAttribute('data-server-timezone-label') || tzId || 'UTC');
var dateCtor = window['Date'];
var intlObj = window['Intl'];
if (!dateCtor) {
return;
}
var clientBaseMs = dateCtor.now();
function pad2(value) {
return String(value).padStart(2, '0');
}
function formatServerDateTime(epochMs) {
var dateObj = new dateCtor(epochMs);
var parts = null;
try {
if (!intlObj || !intlObj.DateTimeFormat) {
throw new Error('Intl unavailable');
}
parts = new intlObj.DateTimeFormat('en-GB', {
timeZone: tzId || undefined,
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
}).formatToParts(dateObj);
} catch (e) {
parts = null;
}
if (parts) {
var map = {};
parts.forEach(function (part) {
if (part.type !== 'literal') {
map[part.type] = part.value;
}
});
var dd = map.day || pad2(dateObj.getDate());
var mm = map.month || pad2(dateObj.getMonth() + 1);
var yyyy = map.year || String(dateObj.getFullYear());
var hh = map.hour || '12';
var min = map.minute || pad2(dateObj.getMinutes());
var period = String(map.dayPeriod || '').toUpperCase();
if (period !== 'AM' && period !== 'PM') {
period = dateObj.getHours() >= 12 ? 'PM' : 'AM';
}
return dd + '-' + mm + '-' + yyyy + ' ' + hh + ':' + min + ' ' + period + ' (' + tzLabel + ')';
}
var fallbackHours = dateObj.getHours();
var fallbackPeriod = fallbackHours >= 12 ? 'PM' : 'AM';
var fallbackHour12 = fallbackHours % 12;
if (fallbackHour12 === 0) {
fallbackHour12 = 12;
}
return pad2(dateObj.getDate()) + '-' + pad2(dateObj.getMonth() + 1) + '-' + dateObj.getFullYear()
+ ' ' + pad2(fallbackHour12) + ':' + pad2(dateObj.getMinutes()) + ' ' + fallbackPeriod + ' (' + tzLabel + ')';
}
function refreshClock() {
var nowMs = dateCtor.now();
var currentServerMs = serverEpochMs + (nowMs - clientBaseMs);
el.textContent = formatServerDateTime(currentServerMs);
}
refreshClock();
setInterval(refreshClock, 1000);
})();
</script>

View File

@ -107,6 +107,12 @@ if (! $canDiagnosis && $user && method_exists($user, 'inGroup')) {
<span>Dashboard</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('setting/admin/bed-status') ?>','Bed Status');">
<i class="bi bi-geo-alt"></i>
<span>Bed Status</span>
</a>
</li>
<?php if ($canBilling) { ?>
<li class="nav-heading">Billing</li>
@ -115,6 +121,18 @@ if (! $canDiagnosis && $user && method_exists($user, 'inGroup')) {
<i class="bi bi-person-lines-fill"></i>
<span>Patient List</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/opdlist') ?>','OPD Invoice')">
<i class="bi bi-receipt"></i>
<span>OPD Invoice</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Invoice/chargeslist') ?>','Charges Invoice')">
<i class="bi bi-journal-text"></i>
<span>Charges Invoice</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd') ?>','IPD List')">