From 99ec71677a9a32d48d6ff82c51ae9fcd3132e7dd Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Tue, 24 Mar 2026 04:24:19 +0530 Subject: [PATCH] Restore legacy HMS flows and add IPD document master templates --- app/Config/App.php | 2 +- app/Config/Routes.php | 18 + app/Controllers/Billing/Ipd.php | 219 +++++++ app/Controllers/Invoice.php | 58 ++ app/Controllers/Opd.php | 95 +++ app/Controllers/Setting/BedManagement.php | 560 ++++++++++++++---- app/Controllers/Setting/Template.php | 339 +++++++++++ app/Helpers/common_helper.php | 6 + app/Models/BedCategoryModel.php | 5 +- app/Models/BedMasterModel.php | 3 +- app/Models/WardModel.php | 8 +- app/Views/Invoice/charges_invoice_list.php | 69 +++ app/Views/Invoice/opd_invoice_list.php | 69 +++ app/Views/Setting/Bed/bed_list.php | 17 +- app/Views/Setting/Bed/bed_status_list.php | 115 ++++ app/Views/Setting/Bed/department_list.php | 143 +++++ app/Views/Setting/Bed/index.php | 18 + app/Views/Setting/Bed/ward_list.php | 271 +++++++++ app/Views/Setting/Doctor/Doctor_V.php | 9 +- app/Views/Setting/Doctor/Doctor_profile_V.php | 8 +- app/Views/Setting/Template/index.php | 6 + .../Template/ipd_document_templates.php | 173 ++++++ app/Views/billing/ipd/ipd_form_print.php | 264 +++++++++ app/Views/billing/ipd/panel.php | 6 + app/Views/billing/ipd/panel_documents.php | 53 ++ app/Views/billing/opd_invoice_V.php | 58 +- app/Views/partials/header.php | 109 +++- app/Views/partials/sidebar.php | 18 + 28 files changed, 2601 insertions(+), 118 deletions(-) create mode 100644 app/Views/Invoice/charges_invoice_list.php create mode 100644 app/Views/Invoice/opd_invoice_list.php create mode 100644 app/Views/Setting/Bed/bed_status_list.php create mode 100644 app/Views/Setting/Bed/department_list.php create mode 100644 app/Views/Setting/Bed/ward_list.php create mode 100644 app/Views/Setting/Template/ipd_document_templates.php create mode 100644 app/Views/billing/ipd/ipd_form_print.php create mode 100644 app/Views/billing/ipd/panel_documents.php diff --git a/app/Config/App.php b/app/Config/App.php index 99d7deb..15b9768 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -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'; /** * -------------------------------------------------------------------------- diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 9446a07..7dc2b67 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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'); diff --git a/app/Controllers/Billing/Ipd.php b/app/Controllers/Billing/Ipd.php index 5010093..ccd9ce8 100644 --- a/app/Controllers/Billing/Ipd.php +++ b/app/Controllers/Billing/Ipd.php @@ -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; diff --git a/app/Controllers/Invoice.php b/app/Controllers/Invoice.php index c122f83..02921de 100644 --- a/app/Controllers/Invoice.php +++ b/app/Controllers/Invoice.php @@ -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()) { diff --git a/app/Controllers/Opd.php b/app/Controllers/Opd.php index 65417f6..9780bb5 100644 --- a/app/Controllers/Opd.php +++ b/app/Controllers/Opd.php @@ -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. */ diff --git a/app/Controllers/Setting/BedManagement.php b/app/Controllers/Setting/BedManagement.php index c94a517..033a372 100644 --- a/app/Controllers/Setting/BedManagement.php +++ b/app/Controllers/Setting/BedManagement.php @@ -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(), ]); } -} +} \ No newline at end of file diff --git a/app/Controllers/Setting/Template.php b/app/Controllers/Setting/Template.php index 248cdb9..cd25deb 100644 --- a/app/Controllers/Setting/Template.php +++ b/app/Controllers/Setting/Template.php @@ -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' + +
+

BED HEAD TICKET

+ + + + + +
+ Patient Information

+ Patient Name: {{PATIENT_NAME}}
+ Age/Gender: {{AGE_GENDER}}
+ UHID: {{UHID}}
+ IPD Code: {{IPD_CODE}}
+ Date And Time of Admission: {{ADMIT_DATE}} +
+ Hospital: {{HOSPITAL_NAME}}
+ Address: {{HOSPITAL_ADDRESS}}
+ Doctors: {{DOCTORS}}
+ Insurance: {{INSURANCE_NAME}} +
+ +

सहमति पत्र

+

+ मैं इलाज कराने का/की इच्छुक हूँ। मैं {{HOSPITAL_NAME}} के चिकित्सक और उनके सहायकों को अपना उपचार, + परीक्षण, परामर्श, जांच, औषधि देने एवं आवश्यक चिकित्सा प्रक्रियाएं करने की स्वीकृति देता/देती हूँ। + मुझे बीमारी, संभावित जटिलताओं और उपचार में होने वाले खर्च के बारे में समझा दिया गया है। +

+ +

हस्ताक्षर / अंगूठा निशान: __________________________

+

नाम: __________________________    मरीज से संबंध: __________________________

+

पता: __________________________    फ़ोन नंबर: __________________________

+
+HTML, + ], + [ + 'form_no' => 3, + 'template_name' => 'Legacy Self Declaration (IPD2)', + 'template_html' => <<<'HTML' +
+

SELF DECLARATION FROM HEALTH INSURANCE CARD HOLDER

+

मेडिक्लेम बीमा कार्ड धारक द्वारा स्वघोषणा

+ + + + + + + + + + + + +
Patient Name: {{PATIENT_NAME}}UHID: {{UHID}}IPD: {{IPD_CODE}}
Age/Gender: {{AGE_GENDER}}Admission: {{ADMIT_DATE}}Insurance: {{INSURANCE_NAME}}
+ +

+ मैं __________________________ यह घोषणा करता/करती हूँ कि मरीज {{PATIENT_NAME}} का मेडिक्लेम {{INSURANCE_NAME}} में है, + और भर्ती दिनांक {{ADMIT_DATE}} को हुई है। मैंने आवश्यक दस्तावेज जमा कर दिए हैं और यदि क्लेम पूर्ण/आंशिक स्वीकृत नहीं होता + है तो शेष देय राशि का भुगतान स्वयं करूँगा/करूँगी। +

+ +

हस्ताक्षर / अंगूठा निशान: __________________________

+

नाम: __________________________    मरीज से संबंध: __________________________

+

पता: __________________________    फ़ोन नंबर: __________________________

+

दिनांक: {{CURRENT_DATE}}

+
+HTML, + ], + [ + 'form_no' => 5, + 'template_name' => 'Legacy Admission History & Physical Assessment', + 'template_html' => <<<'HTML' +
+

ADMISSION HISTORY AND PHYSICAL ASSESSMENT FORM

+

Patient: {{PATIENT_NAME}}   Age/Gender: {{AGE_GENDER}}   UHID: {{UHID}}   IPD: {{IPD_CODE}}   Admit Date: {{ADMIT_DATE}}

+

Diagnosis: ______________________________________________

+

Time of Patient Arrival: ____________    Time of Doctor Assessment: ____________

+

Consciousness: [ ] Awake [ ] Alert [ ] In pain [ ] Response to verbal commands [ ] Unresponsive

+

GCS: E___ V___ M___    Airway/Breathing: [ ] Clear [ ] Noisy [ ] Stridor [ ] Obstruction

+

Allergic To: ______________________________________________

+

Present Complaints:

+
+ +

Past Medical History:

+ + + + + +
Diabetes / MetabolicJaundice / CLD
Hypertension / IHDOsteoarthritis
Asthma / COPD / TBTuberculosis
Others:
+ +

Drug History:

+
+

Past Surgical History:

+
+ +

Declaration: I hereby declare that the facts recorded above are accurate to the best of my knowledge.

+

Name of Patient/Relative: ________________________   Signature: ________________________

+

Date: {{CURRENT_DATE}}

+
+HTML, + ], + [ + 'form_no' => 8, + 'template_name' => 'Legacy Progress Notes and Doctor Order', + 'template_html' => <<<'HTML' +
+

PROGRESS NOTES AND DOCTOR'S ORDER

+

Name: {{PATIENT_NAME}}   IPD: {{IPD_CODE}}   UHID: {{UHID}}   Age/Gender: {{AGE_GENDER}}

+
+ + + + + + + + + +
DATEPROGRESS NOTES AND DOCTOR'S ORDER
+
+HTML, + ], + [ + 'form_no' => 9, + 'template_name' => 'Legacy Oral / Enteral Intake Output Chart', + 'template_html' => <<<'HTML' +
+

ORAL / ENTERAL INTAKE & OUTPUT CHART

+

Name: {{PATIENT_NAME}}   IPD: {{IPD_CODE}}   UHID: {{UHID}}   Age/Gender: {{AGE_GENDER}}

+
+ + + + + + + + + + + + + + + + + + + + + +
SR.No.DateTimeDescriptionVol.Vol.TotalUrineStoolOtherOtherOtherTotal
1
2
3
4
5
+
+HTML, + ], + [ + 'form_no' => 10, + 'template_name' => 'Legacy Sticker [2 x 6]', + 'template_html' => <<<'HTML' +
+
{{HOSPITAL_NAME}}
+
Patient Name: {{PATIENT_NAME}}
+
Age / Gender: {{AGE_GENDER}}
+
UHID / Patient ID: {{UHID}}
+
IPD Code: {{IPD_CODE}}
+
Dept./Doctor: {{DOCTORS}}
+
Date of Admission: {{ADMIT_DATE}}
+
+HTML, + ], + [ + 'form_no' => 11, + 'template_name' => 'Legacy Sticker [2 x 8]', + 'template_html' => <<<'HTML' +
+
{{HOSPITAL_NAME}}
+
{{HOSPITAL_ADDRESS}}
+
Patient Name: {{PATIENT_NAME}}
+
Age / Gender: {{AGE_GENDER}}
+
UHID / Patient ID: {{UHID}}
+
IPD Code: {{IPD_CODE}}
+
Dept./Doctor: {{DOCTORS}}
+
Date of Admission: {{ADMIT_DATE}}
+
Insurance: {{INSURANCE_NAME}}
+
+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')); + } } diff --git a/app/Helpers/common_helper.php b/app/Helpers/common_helper.php index ab8acbe..f811ce6 100644 --- a/app/Helpers/common_helper.php +++ b/app/Helpers/common_helper.php @@ -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]; diff --git a/app/Models/BedCategoryModel.php b/app/Models/BedCategoryModel.php index c452a58..896e775 100644 --- a/app/Models/BedCategoryModel.php +++ b/app/Models/BedCategoryModel.php @@ -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', diff --git a/app/Models/BedMasterModel.php b/app/Models/BedMasterModel.php index 4da2e45..10be772 100644 --- a/app/Models/BedMasterModel.php +++ b/app/Models/BedMasterModel.php @@ -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', diff --git a/app/Models/WardModel.php b/app/Models/WardModel.php index cd72de2..d740d07 100644 --- a/app/Models/WardModel.php +++ b/app/Models/WardModel.php @@ -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') diff --git a/app/Views/Invoice/charges_invoice_list.php b/app/Views/Invoice/charges_invoice_list.php new file mode 100644 index 0000000..c474ada --- /dev/null +++ b/app/Views/Invoice/charges_invoice_list.php @@ -0,0 +1,69 @@ +
+
+

Charges Invoice List

+
+
+
+
+ +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + insurance_id ?? 0) > 1) ? 'Org.' : 'Direct'; + $invDate = ! empty($row->inv_date) ? date('d-m-Y', strtotime((string) $row->inv_date)) : '-'; + ?> + + + + + + + + + + + + +
Invoice CodePatientPatient CodeDateMobileTypeAmountAction
invoice_code ?? '-')) ?>p_fname ?? '-')) ?>p_code ?? '-')) ?>mphone1 ?? '-')) ?>net_amount ?? 0), 2, '.', '')) ?> + +
+
+
+
+ + diff --git a/app/Views/Invoice/opd_invoice_list.php b/app/Views/Invoice/opd_invoice_list.php new file mode 100644 index 0000000..e1724d5 --- /dev/null +++ b/app/Views/Invoice/opd_invoice_list.php @@ -0,0 +1,69 @@ +
+
+

OPD Invoice List

+
+
+
+
+ +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + insurance_id ?? 0) > 1) ? 'Org.' : 'Direct'; + $opdDate = ! empty($row->apointment_date) ? date('d-m-Y', strtotime((string) $row->apointment_date)) : '-'; + ?> + + + + + + + + + + + + +
OPD CodePatientPatient CodeDateDoctorMobileTypeAction
opd_code ?? '-')) ?>P_name ?? '-')) ?>p_code ?? '-')) ?>doc_name ?? '-')) ?>mphone1 ?? '-')) ?> + +
+
+
+
+ + diff --git a/app/Views/Setting/Bed/bed_list.php b/app/Views/Setting/Bed/bed_list.php index 4f7ab56..d50192b 100644 --- a/app/Views/Setting/Bed/bed_list.php +++ b/app/Views/Setting/Bed/bed_list.php @@ -1,10 +1,16 @@

Bed Master

- +
+ + +
@@ -28,6 +34,9 @@ +
+ +
diff --git a/app/Views/Setting/Bed/bed_status_list.php b/app/Views/Setting/Bed/bed_status_list.php new file mode 100644 index 0000000..3256438 --- /dev/null +++ b/app/Views/Setting/Bed/bed_status_list.php @@ -0,0 +1,115 @@ +
+
+

Bed Status (IPD Location Finder)

+
+
+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + 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 = '-'; + } + ?> + + + + + + + + + + + + +
IPD CodePatient CodePatient NameMobileDepartmentWardBed NoAdmit Date
ipd_code ?? '-')) ?>p_code ?? '-')) ?>
+
+
+
+ diff --git a/app/Views/Setting/Bed/department_list.php b/app/Views/Setting/Bed/department_list.php new file mode 100644 index 0000000..e14aedb --- /dev/null +++ b/app/Views/Setting/Bed/department_list.php @@ -0,0 +1,143 @@ +
+
+

Department Master

+ +
+
+ +
Department table (hc_department) is not available in this database.
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + +
IDNameType IDAction
iId ?? 0) ?>vName ?? '') ?>hc_type_id ?? '') ?> + + +
+
+ +
+
+ diff --git a/app/Views/Setting/Bed/index.php b/app/Views/Setting/Bed/index.php index 807e943..deb2bdb 100644 --- a/app/Views/Setting/Bed/index.php +++ b/app/Views/Setting/Bed/index.php @@ -10,6 +10,24 @@
+ + +
diff --git a/app/Views/Setting/Bed/ward_list.php b/app/Views/Setting/Bed/ward_list.php new file mode 100644 index 0000000..7a5104d --- /dev/null +++ b/app/Views/Setting/Bed/ward_list.php @@ -0,0 +1,271 @@ +
+
+

Ward Master

+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
CodeNameDepartmentTypeCapacityStatusAction
ward_code ?? '') ?>ward_name ?? '') ?>department_name ?? 'All Department') ?>ward_type ?? '') ?>total_capacity ?? 0) ?>status ?? '') ?> + + +
+
+
+
+ diff --git a/app/Views/Setting/Doctor/Doctor_V.php b/app/Views/Setting/Doctor/Doctor_V.php index 780f325..f311024 100644 --- a/app/Views/Setting/Doctor/Doctor_V.php +++ b/app/Views/Setting/Doctor/Doctor_V.php @@ -11,6 +11,13 @@
+
@@ -61,7 +68,7 @@
- +
+dob)) { + $doctorDobValue = trim((string) $data[0]->dob); +} +?>
@@ -77,7 +83,7 @@ if (!empty($data) && !empty($data[0])) {
- +
+ diff --git a/app/Views/Setting/Template/ipd_document_templates.php b/app/Views/Setting/Template/ipd_document_templates.php new file mode 100644 index 0000000..fa07f61 --- /dev/null +++ b/app/Views/Setting/Template/ipd_document_templates.php @@ -0,0 +1,173 @@ +{{FORM_TITLE}}

{{PATIENT_NAME}}

'); +$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]', +]; +?> + +
+
+
+
IPD Document Master
+ Reset +
+
+ +
+ + +
+ Placeholders: {{FORM_TITLE}}, {{HOSPITAL_NAME}}, {{HOSPITAL_ADDRESS}}, + {{PATIENT_NAME}}, {{AGE}}, {{GENDER}}, {{AGE_GENDER}}, {{UHID}}, + {{IPD_CODE}}, {{ADMIT_DATE}}, {{DOCTORS}}, {{INSURANCE_NAME}}, + {{CURRENT_DATE}}, {{CURRENT_DATETIME}}. +
+ +
+ Local-language design supported. You can copy HTML blocks from old HMS files under old_code_ci3/views/IPD_Format and adapt them here. +
+ +
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + Use Source mode for raw HTML from legacy forms. +
+
+ +
+ + 0): ?> + Cancel Edit + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
IDFormTemplate NameStatusAction
No templates found.
- + Edit + Delete +
+
+
+
+
+ + diff --git a/app/Views/billing/ipd/ipd_form_print.php b/app/Views/billing/ipd/ipd_form_print.php new file mode 100644 index 0000000..a1ca00e --- /dev/null +++ b/app/Views/billing/ipd/ipd_form_print.php @@ -0,0 +1,264 @@ + '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 ?? '-'); +} +?> + + + + + + + + +
+

+
+ +
+ +
Patient:
+
Age/Gender: /
+
UHID:
+
IPD:
+
Doctor:
+
Admit:
+ +
Insurance:
+ +
+
+ +
+

+ +

+ +

+
+ + + + + + + + + + + + + + + +
Patient Name: Age / Gender: / UHID:
IPD Code: Date of Admission: Doctor:
+ Insurance: + Generated: +
+ + +
Patient Consent
+

+ 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. +

+

+ 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. +

+ + + + + +
Patient / Attendant Signature: ____________________Hospital Representative: ____________________
+ +
Self Declaration
+

+ I, ____________________, declare that patient is admitted under IPD code + on date . + + The patient is covered under insurance provider . + + This admission is under self / direct payment category. + +

+

+ 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. +

+ + + + + +
Declarant Signature: ____________________Date: ____________________
+ +
Admission Assessment
+ + + + + + + + + + +
Assessment ItemObservation
Chief Complaint
History of Present Illness
Past Medical / Surgical History
Allergy History
Medication History
Physical Examination
Provisional Diagnosis
Initial Plan
+ +
Progress Notes and Doctor Order
+ + + + + + + + + + + + + + + +
DateTimeProgress Notes / OrdersDoctor Sign
+ +
Fluid In / Out
+ + + + + + + + + + + + + + + + + + + + + + + +
S.No.Date / TimeFluid In (ml)Fluid Out (ml)Remark
Total
+ + + + diff --git a/app/Views/billing/ipd/panel.php b/app/Views/billing/ipd/panel.php index 76df154..b2b1966 100644 --- a/app/Views/billing/ipd/panel.php +++ b/app/Views/billing/ipd/panel.php @@ -180,6 +180,9 @@ if ($person) { +
@@ -209,6 +212,9 @@ if ($person) {
Loading...
+
+
Loading...
+
diff --git a/app/Views/billing/ipd/panel_documents.php b/app/Views/billing/ipd/panel_documents.php new file mode 100644 index 0000000..e9ea5ae --- /dev/null +++ b/app/Views/billing/ipd/panel_documents.php @@ -0,0 +1,53 @@ +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'], +]; +?> + +
+
+
+
Pre-Print Documents
+
+ IPD: + +
+
+ +
+ + + + + + + + + + + + + + + + + +
Form IDDocumentAction
+ + Print + +
+
+
+
diff --git a/app/Views/billing/opd_invoice_V.php b/app/Views/billing/opd_invoice_V.php index 8bb1ca6..da32477 100644 --- a/app/Views/billing/opd_invoice_V.php +++ b/app/Views/billing/opd_invoice_V.php @@ -1,9 +1,16 @@ 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 ?? []; ?>

OPD Invoice

@@ -223,6 +230,50 @@ if ($user && method_exists($user, 'inGroup')) {
+
+
+
+
Payment History
+
+ + + + + + + + + + + + + + credit_debit ?? 0) === 1; + $historyDate = trim((string) ($historyRow->payment_date ?? '')); + if ($historyDate === '') { + $historyDate = trim((string) ($historyRow->insert_time ?? '')); + } + ?> + + + + + + + + + + + + + + +
DateTypeModeAmountRemark
Refund' : 'Received' ?>payment_mode_str ?? '') ?>amount ?? 0), 2, '.', '')) ?>remark ?? '') ?>
No payment history entries.
+
+
+
+ opd_status ?? 0) === 1 || (int) ($opd_master[0]->opd_status ?? 0) === 2)) : ?>
@@ -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('/' + $('#oid').val()); }); } diff --git a/app/Views/partials/header.php b/app/Views/partials/header.php index a091ba8..171b337 100644 --- a/app/Views/partials/header.php +++ b/app/Views/partials/header.php @@ -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 . ')'; ?> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/Views/partials/sidebar.php b/app/Views/partials/sidebar.php index 0876adf..db1a5b2 100644 --- a/app/Views/partials/sidebar.php +++ b/app/Views/partials/sidebar.php @@ -107,6 +107,12 @@ if (! $canDiagnosis && $user && method_exists($user, 'inGroup')) { Dashboard + @@ -115,6 +121,18 @@ if (! $canDiagnosis && $user && method_exists($user, 'inGroup')) { Patient List + + +