From 2977192017e59a1a918bfa7cffdaa24afd43423d Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 8 Apr 2026 11:41:33 +0530 Subject: [PATCH] Make IPD forms dynamic and stop stale OPD refresh polling --- app/Controllers/Billing/Ipd.php | 142 +++++++++++++----- app/Controllers/Setting/Template.php | 4 +- .../Template/ipd_document_templates.php | 28 +++- app/Views/billing/ipd/panel_documents.php | 15 +- app/Views/billing/opd_appointment_list.php | 19 +++ 5 files changed, 158 insertions(+), 50 deletions(-) diff --git a/app/Controllers/Billing/Ipd.php b/app/Controllers/Billing/Ipd.php index 26479ca..864a73f 100644 --- a/app/Controllers/Billing/Ipd.php +++ b/app/Controllers/Billing/Ipd.php @@ -400,36 +400,50 @@ class Ipd extends BaseController } if ($tab === 'ipd-charges') { - $this->syncNursingChargesToInvoice($ipdId); - $panelData['ipd_charges_grouped'] = $this->ipdModel->getIpdChargesGrouped($ipdId); - $panelData['ipd_charges_total'] = $this->ipdModel->getIpdChargesTotal($ipdId); - $panelData['ipd_packages'] = $this->ipdModel->getIpdPackages($ipdId); + try { + $this->syncNursingChargesToInvoice($ipdId); + $panelData['ipd_charges_grouped'] = $this->ipdModel->getIpdChargesGrouped($ipdId); + $panelData['ipd_charges_total'] = $this->ipdModel->getIpdChargesTotal($ipdId); + $panelData['ipd_packages'] = $this->ipdModel->getIpdPackages($ipdId); - $caseMeta = $this->ipdModel->getIpdCaseMeta($ipdId); - $insCompId = (int) ($caseMeta['insurance_id'] ?? 0); - if ($insCompId <= 0) { - $insCompId = 1; - } - - $panelData['bedside_items_by_category'] = $this->nursingBedsideItemModel->getBillableGroupedByCategory($insCompId); - - $panelData['ipd_insurance_id'] = $insCompId; - $panelData['doc_list'] = $this->ipdModel->getIpdDoctorList(); - $doctorIds = array_map(static fn ($doc) => (int) ($doc->id ?? 0), $panelData['doc_list']); - $doctorIds = array_values(array_filter($doctorIds, static fn ($id) => $id > 0)); - $panelData['doctor_visit_fee_types'] = $this->ipdModel->getDoctorVisitFeeTypes(); - $panelData['doctor_visit_fee_map'] = $this->ipdModel->getDoctorVisitFeeMap($doctorIds); - $panelData['item_types'] = $this->itemIpdModel->getItemTypes(); - - $itemLists = []; - foreach ($panelData['item_types'] as $type) { - $typeId = (int) ($type->itype_id ?? 0); - if ($typeId <= 0) { - continue; + $caseMeta = $this->ipdModel->getIpdCaseMeta($ipdId); + $insCompId = (int) ($caseMeta['insurance_id'] ?? 0); + if ($insCompId <= 0) { + $insCompId = 1; } - $itemLists[$typeId] = $this->itemIpdModel->getItemsByTypeWithInsurance($typeId, $insCompId); + + $panelData['bedside_items_by_category'] = $this->nursingBedsideItemModel->getBillableGroupedByCategory($insCompId); + + $panelData['ipd_insurance_id'] = $insCompId; + $panelData['doc_list'] = $this->ipdModel->getIpdDoctorList(); + $doctorIds = array_map(static fn ($doc) => (int) ($doc->id ?? 0), $panelData['doc_list']); + $doctorIds = array_values(array_filter($doctorIds, static fn ($id) => $id > 0)); + $panelData['doctor_visit_fee_types'] = $this->ipdModel->getDoctorVisitFeeTypes(); + $panelData['doctor_visit_fee_map'] = $this->ipdModel->getDoctorVisitFeeMap($doctorIds); + $panelData['item_types'] = $this->itemIpdModel->getItemTypes(); + + $itemLists = []; + foreach ($panelData['item_types'] as $type) { + $typeId = (int) ($type->itype_id ?? 0); + if ($typeId <= 0) { + continue; + } + $itemLists[$typeId] = $this->itemIpdModel->getItemsByTypeWithInsurance($typeId, $insCompId); + } + $panelData['item_lists'] = $itemLists; + } catch (\Throwable $e) { + log_message('error', 'IPD Charges tab failed for IPD #' . $ipdId . ': ' . $e->getMessage()); + $panelData['ipd_charges_grouped'] = []; + $panelData['ipd_charges_total'] = (object) ['total_amt' => 0, 'package_amt' => 0, 'patient_amt' => 0]; + $panelData['ipd_packages'] = []; + $panelData['bedside_items_by_category'] = []; + $panelData['ipd_insurance_id'] = 0; + $panelData['doc_list'] = []; + $panelData['doctor_visit_fee_types'] = []; + $panelData['doctor_visit_fee_map'] = []; + $panelData['item_types'] = []; + $panelData['item_lists'] = []; } - $panelData['item_lists'] = $itemLists; return view('billing/ipd/panel_ipd_charges', $panelData); } @@ -485,6 +499,7 @@ class Ipd extends BaseController return view('billing/ipd/panel_documents', [ 'ipd_id' => $ipdId, 'ipd_info' => $panelData['ipd_info'] ?? null, + 'documents' => $this->getAvailableIpdDocuments(), ]); } @@ -849,11 +864,6 @@ class Ipd extends BaseController 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'); @@ -2246,7 +2256,7 @@ class Ipd extends BaseController return [$start, $end]; } - private function getIpdFormMeta(int $formNo): ?array + private function getIpdFormMeta(int $formNo): array { $meta = [ 1 => ['title' => 'Face Form'], @@ -2258,7 +2268,71 @@ class Ipd extends BaseController 11 => ['title' => 'Sticker [2 x 8]'], ]; - return $meta[$formNo] ?? null; + if (isset($meta[$formNo])) { + return $meta[$formNo]; + } + + $template = $this->getActiveIpdDocumentTemplate($formNo); + if (is_array($template) && ! empty($template['template_name'])) { + return ['title' => (string) $template['template_name']]; + } + + return ['title' => 'IPD Form #' . $formNo]; + } + + private function getAvailableIpdDocuments(): array + { + $default = [ + ['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'], + ]; + + if (! $this->db->tableExists('ipd_document_templates')) { + return $default; + } + + $rows = $this->db->table('ipd_document_templates') + ->select('form_no, template_name, id') + ->where('status', 1) + ->orderBy('form_no', 'ASC') + ->orderBy('id', 'DESC') + ->get() + ->getResultArray(); + + if (empty($rows)) { + return $default; + } + + $documents = []; + foreach ($rows as $row) { + $id = (int) ($row['form_no'] ?? 0); + if ($id <= 0 || isset($documents[$id])) { + continue; + } + + $label = trim((string) ($row['template_name'] ?? '')); + if ($label === '') { + $label = (string) ($this->getIpdFormMeta($id)['title'] ?? ('IPD Form #' . $id)); + } + + $documents[$id] = [ + 'id' => $id, + 'label' => $label, + ]; + } + + if (empty($documents)) { + return $default; + } + + ksort($documents); + + return array_values($documents); } private function buildIpdPrintableFormData(int $ipdId, int $formNo): ?array diff --git a/app/Controllers/Setting/Template.php b/app/Controllers/Setting/Template.php index 839a6fe..037a3bb 100644 --- a/app/Controllers/Setting/Template.php +++ b/app/Controllers/Setting/Template.php @@ -1730,8 +1730,8 @@ HTML, $marginFooter = max(0, min(25, (float) ($this->request->getPost('margin_footer_cm') ?? 0.5))); $status = (int) ($this->request->getPost('status') ?? 1) === 1 ? 1 : 0; - if (! in_array($formNo, [1, 3, 5, 8, 9, 10, 11], true)) { - $notice = 'Invalid form number.'; + if ($formNo <= 0) { + $notice = 'Form number must be a positive integer.'; $noticeType = 'danger'; } elseif ($templateName === '' || trim($templateHtml) === '') { $notice = 'Form number, template name and HTML are required.'; diff --git a/app/Views/Setting/Template/ipd_document_templates.php b/app/Views/Setting/Template/ipd_document_templates.php index eb9ca61..86130df 100644 --- a/app/Views/Setting/Template/ipd_document_templates.php +++ b/app/Views/Setting/Template/ipd_document_templates.php @@ -31,6 +31,19 @@ $formMap = [ 10 => 'Sticker [2 x 6]', 11 => 'Sticker [2 x 8]', ]; + +$dynamicFormHints = []; +foreach ($rows as $_row) { + $fNo = (int) ($_row['form_no'] ?? 0); + $tName = trim((string) ($_row['template_name'] ?? '')); + if ($fNo <= 0 || $tName === '') { + continue; + } + if (!isset($dynamicFormHints[$fNo])) { + $dynamicFormHints[$fNo] = $tName; + } +} +ksort($dynamicFormHints); ?>
@@ -65,12 +78,17 @@ $formMap = [
- - + + $label): ?> + - + $label): ?> + + + + Use any positive number (example: 12 for Treatment Chart, 13 for Vitals).
diff --git a/app/Views/billing/ipd/panel_documents.php b/app/Views/billing/ipd/panel_documents.php index e9ea5ae..69e8675 100644 --- a/app/Views/billing/ipd/panel_documents.php +++ b/app/Views/billing/ipd/panel_documents.php @@ -2,15 +2,7 @@ $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'], -]; +$documents = is_array($documents ?? null) ? $documents : []; ?>
@@ -35,6 +27,11 @@ $documents = [ + + + No active document template found. Add templates from Document Master. + + diff --git a/app/Views/billing/opd_appointment_list.php b/app/Views/billing/opd_appointment_list.php index 685bba2..d2de3eb 100644 --- a/app/Views/billing/opd_appointment_list.php +++ b/app/Views/billing/opd_appointment_list.php @@ -460,6 +460,14 @@ $mergedGroups = [ } function checkLiveUpdates() { + if (!document.getElementById('opdAllTable')) { + if (window.opdLiveMonitorTimer) { + clearInterval(window.opdLiveMonitorTimer); + window.opdLiveMonitorTimer = null; + } + return; + } + if (livePollInFlight || shouldSkipLiveCheck()) { return; } @@ -530,6 +538,13 @@ $mergedGroups = [ window.opdLiveMonitorTimer = setInterval(checkLiveUpdates, livePollMs); } + function stopLiveUpdates() { + if (window.opdLiveMonitorTimer) { + clearInterval(window.opdLiveMonitorTimer); + window.opdLiveMonitorTimer = null; + } + } + function setVitalMessage(type, text) { var $msg = $('#vital_msg'); $msg.removeClass('text-muted text-success text-danger'); @@ -864,6 +879,10 @@ $mergedGroups = [ reloadAppointmentList(); }); + $(window).off('beforeunload.opdlive').on('beforeunload.opdlive', function() { + stopLiveUpdates(); + }); + // Initialize registerOpdTableFilter(); opdDataTable = initializeOpdTable();