Make IPD forms dynamic and stop stale OPD refresh polling

This commit is contained in:
DevSoftTech Server 2026-04-08 11:41:33 +05:30
parent 8bc9158e14
commit 2977192017
5 changed files with 158 additions and 50 deletions

View File

@ -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

View File

@ -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.';

View File

@ -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);
?>
<section class="content">
@ -65,12 +78,17 @@ $formMap = [
<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>
<label class="form-label small">Form ID</label>
<input type="number" name="form_no" list="ipd_form_suggestions" class="form-control form-control-sm" value="<?= (int) $formNo ?>" min="1" step="1" required>
<datalist id="ipd_form_suggestions">
<?php foreach ($dynamicFormHints as $id => $label): ?>
<option value="<?= (int) $id ?>"><?= (int) $id ?> - <?= esc($label) ?></option>
<?php endforeach; ?>
</select>
<?php foreach ($formMap as $id => $label): ?>
<option value="<?= (int) $id ?>"><?= (int) $id ?> - <?= esc($label) ?></option>
<?php endforeach; ?>
</datalist>
<small class="text-muted">Use any positive number (example: 12 for Treatment Chart, 13 for Vitals).</small>
</div>
<div class="col-md-6">
<label class="form-label small">Template Name</label>

View File

@ -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 : [];
?>
<div class="card border-0 shadow-sm">
@ -35,6 +27,11 @@ $documents = [
</tr>
</thead>
<tbody>
<?php if (empty($documents)) : ?>
<tr>
<td colspan="3" class="text-center text-muted py-3">No active document template found. Add templates from Document Master.</td>
</tr>
<?php endif; ?>
<?php foreach ($documents as $document) : ?>
<tr>
<td><?= (int) $document['id'] ?></td>

View File

@ -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();