From c437652b790735a435583347a8aee6bc00a0ca9a Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 10 Jun 2026 12:55:47 +0530 Subject: [PATCH] Add IPD status management with strict ipd_status check - Update isIpdDischargedInvoice to check ONLY ipd_status field (remove fallback checks) - Add IPD status toggle UI in discharge panel (Active/Discharged control) - Create toggleIpdStatus controller method with permission check - Add billing.ipd.status.manage permission for controlling bill edit lock - Allows editing bills when needed by toggling status independently of discharge_date - Auto-releases bed when setting status to discharged --- app/Config/AuthGroups.php | 1 + app/Controllers/Billing/Ipd.php | 51 +++++++++++++++++++++++ app/Controllers/Medical.php | 19 ++------- app/Views/billing/ipd/panel_discharge.php | 51 +++++++++++++++++++++++ 4 files changed, 107 insertions(+), 15 deletions(-) diff --git a/app/Config/AuthGroups.php b/app/Config/AuthGroups.php index 367e071..011bbbf 100644 --- a/app/Config/AuthGroups.php +++ b/app/Config/AuthGroups.php @@ -131,6 +131,7 @@ class AuthGroups extends ShieldAuthGroups 'billing.ipd.admission.edit' => 'Can edit IPD admission details (date, mode, department, refer, doctors, contact, clinical notes)', 'billing.ipd.bill.print' => 'Can print IPD bill variants', 'billing.ipd.discharge.edit' => 'Can edit IPD discharge process details (discount, extra charges, status, date, time)', + 'billing.ipd.status.manage' => 'Can toggle IPD status between Active and Discharged (controls bill edit lock)', 'settings.bed_status.view' => 'Can view bed status and bed management screens', 'settings.charges.access' => 'Can access charges settings', 'reports.access' => 'Can access reports section', diff --git a/app/Controllers/Billing/Ipd.php b/app/Controllers/Billing/Ipd.php index 84b1454..4195bb9 100644 --- a/app/Controllers/Billing/Ipd.php +++ b/app/Controllers/Billing/Ipd.php @@ -879,6 +879,57 @@ class Ipd extends BaseController return $this->buildDischargeProcessResponse($ipdId, 'Discharge status updated.'); } + public function toggleIpdStatus(int $ipdId) + { + $permission = $this->requireAnyPermission([ + 'billing.ipd.status.manage', + ]); + if ($permission) { + return $permission; + } + + if (! $this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON(['update' => 0, 'message' => 'Invalid request']); + } + + $fieldMap = $this->getIpdFieldMap(); + if (! isset($fieldMap['ipd_status'])) { + return $this->response->setStatusCode(422)->setJSON([ + 'update' => 0, + 'message' => 'ipd_status field not available in schema' + ]); + } + + $newStatus = (int) ($this->request->getPost('ipd_status') ?? -1); + if ($newStatus !== 0 && $newStatus !== 1) { + return $this->response->setStatusCode(422)->setJSON([ + 'update' => 0, + 'message' => 'Invalid status value (must be 0 or 1)' + ]); + } + + // Get current status + $currentIpd = $this->ipdModel->getById($ipdId); + if (! $currentIpd) { + return $this->response->setStatusCode(404)->setJSON(['update' => 0, 'message' => 'IPD not found']); + } + + $currentStatus = (int) ($currentIpd['ipd_status'] ?? 0); + + // Update status + $update = ['ipd_status' => $newStatus]; + $this->ipdEditModel->updateIpd($update, $ipdId); + + // Release bed when setting to discharged (status = 1) + if ($newStatus === 1 && $currentStatus === 0) { + $this->ipdEditModel->releaseBed($ipdId); + } + + $statusText = $newStatus === 0 ? 'Active (Bills unlocked)' : 'Discharged (Bills locked)'; + + return $this->buildDischargeProcessResponse($ipdId, 'IPD status set to: ' . $statusText); + } + public function billPrint(int $ipdId, int $mode = 1) { $permission = $this->requireAnyPermission([ diff --git a/app/Controllers/Medical.php b/app/Controllers/Medical.php index 23c3991..1b2be6d 100644 --- a/app/Controllers/Medical.php +++ b/app/Controllers/Medical.php @@ -3511,21 +3511,10 @@ class Medical extends BaseController return false; } - foreach (['ipd_status', 'discharge_status', 'discharg_status', 'is_discharged'] as $statusCol) { - if (in_array($statusCol, $ipdFields, true) && (int) ($ipd[$statusCol] ?? 0) !== 0) { - return true; - } - } - - foreach (['discharge_date', 'discharge_datetime', 'discharged_at'] as $dateCol) { - if (! in_array($dateCol, $ipdFields, true)) { - continue; - } - - $value = trim((string) ($ipd[$dateCol] ?? '')); - if ($value !== '' && $value !== '0000-00-00' && $value !== '0000-00-00 00:00:00') { - return true; - } + // PRIMARY CHECK ONLY: ipd_status = 1 means discharged + // No fallback checks - this gives flexibility to edit bills even if discharge_date is set + if (in_array('ipd_status', $ipdFields, true)) { + return (int) ($ipd['ipd_status'] ?? 0) === 1; } return false; diff --git a/app/Views/billing/ipd/panel_discharge.php b/app/Views/billing/ipd/panel_discharge.php index ea6ba42..a848ecf 100644 --- a/app/Views/billing/ipd/panel_discharge.php +++ b/app/Views/billing/ipd/panel_discharge.php @@ -22,6 +22,25 @@ if (strlen($dischargeTime) > 5) { } ?> +ipd_status ?? 0); +$canManageIpdStatus = auth()->user()?->can('billing.ipd.status.manage') ?? false; +?> + + +
+
+ IPD Status Control: + Current status is +
+
+ +
+
+ +
@@ -228,6 +247,38 @@ if (strlen($dischargeTime) > 5) { alert(msg); }); }); + + $(document).on('click', '#tab_discharge_content .js-toggle-ipd-status', function() { + var currentStatus = Number($(this).data('current') || 0); + var newStatus = currentStatus === 0 ? 1 : 0; + var action = newStatus === 1 ? 'lock bills (set discharged)' : 'unlock bills (set active)'; + + if (!confirm('Are you sure you want to ' + action + '?\n\nThis will ' + (newStatus === 1 ? 'PREVENT' : 'ALLOW') + ' editing of IPD charges and medical bills.')) { + return; + } + + post('', { + ipd_status: newStatus + }).done(function(response) { + if (response && Number(response.update || 0) === 1) { + refreshDischargeTab(response); + if (typeof window.notify === 'function') { + window.notify('success', 'IPD Status', response.message || 'Status updated successfully'); + } + // Refresh entire panel to update other tabs + setTimeout(function() { + if (typeof window.loadIpdPanel === 'function') { + window.loadIpdPanel(ipdId); + } + }, 500); + } else { + alert(response.message || 'Unable to update IPD status'); + } + }).fail(function(xhr) { + var msg = (xhr.responseJSON && xhr.responseJSON.message) ? xhr.responseJSON.message : 'Unable to update IPD status'; + alert(msg); + }); + }); })();