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
This commit is contained in:
DevSoftTech Server 2026-06-10 12:55:47 +05:30
parent 15458715a8
commit c437652b79
4 changed files with 107 additions and 15 deletions

View File

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

View File

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

View File

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

View File

@ -22,6 +22,25 @@ if (strlen($dischargeTime) > 5) {
}
?>
<?php
$currentIpdStatus = (int) ($ipd->ipd_status ?? 0);
$canManageIpdStatus = auth()->user()?->can('billing.ipd.status.manage') ?? false;
?>
<?php if ($canManageIpdStatus) : ?>
<div class="alert alert-warning border-warning d-flex align-items-center gap-3 mb-3">
<div class="flex-grow-1">
<strong>IPD Status Control:</strong>
Current status is <strong><?= $currentIpdStatus === 0 ? 'Active (Bills can be edited)' : 'Discharged (Bills are locked)' ?></strong>
</div>
<div>
<button type="button" class="btn btn-sm <?= $currentIpdStatus === 0 ? 'btn-danger' : 'btn-success' ?> js-toggle-ipd-status" data-current="<?= $currentIpdStatus ?>">
<?= $currentIpdStatus === 0 ? 'Lock Bills (Set Discharged)' : 'Unlock Bills (Set Active)' ?>
</button>
</div>
</div>
<?php endif; ?>
<div class="row g-3">
<div class="col-lg-6">
<div class="card h-100">
@ -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('<?= site_url('billing/ipd/panel/' . $ipdId . '/discharge/toggle-status') ?>', {
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);
});
});
})();
</script>
<?php else : ?>