diff --git a/app/Controllers/Opd.php b/app/Controllers/Opd.php
index 7ff4ad1..3857d1e 100644
--- a/app/Controllers/Opd.php
+++ b/app/Controllers/Opd.php
@@ -4201,12 +4201,15 @@ class Opd extends BaseController
$insert['session_id'] = 0;
}
if (in_array('queue_no', $fields, true)) {
- $maxQueue = $this->db->table('opd_prescription')
- ->selectMax('queue_no', 'max_queue')
- ->where('date_opd_visit', date('Y-m-d'))
- ->where('doc_id', (int) ($opdRow->doc_id ?? 0))
- ->get()
- ->getRow();
+ // JOIN through opd_master to guarantee per-doctor isolation
+ // (opd_prescription.doc_id may be stale on re-assignments)
+ $maxQueue = $this->db->query(
+ 'SELECT COALESCE(MAX(pr.queue_no), 0) AS max_queue'
+ . ' FROM opd_prescription pr'
+ . ' INNER JOIN opd_master om ON om.opd_id = pr.opd_id'
+ . ' WHERE pr.date_opd_visit = ? AND om.doc_id = ?',
+ [date('Y-m-d'), (int) ($opdRow->doc_id ?? 0)]
+ )->getRow();
$insert['queue_no'] = (int) ($maxQueue->max_queue ?? 0) + 1;
}
diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php
index cb92e21..a65156a 100644
--- a/app/Controllers/Opd_prescription.php
+++ b/app/Controllers/Opd_prescription.php
@@ -482,12 +482,14 @@ class Opd_prescription extends BaseController
}
if (in_array('queue_no', $fields, true) && !isset($allowedData['queue_no'])) {
- $maxQueue = $this->db->table('opd_prescription')
- ->selectMax('queue_no', 'max_queue')
- ->where('date_opd_visit', date('Y-m-d'))
- ->where('doc_id', (int) ($opdRow->doc_id ?? 0))
- ->get()
- ->getRow();
+ // JOIN through opd_master to guarantee per-doctor isolation
+ $maxQueue = $this->db->query(
+ 'SELECT COALESCE(MAX(pr.queue_no), 0) AS max_queue'
+ . ' FROM opd_prescription pr'
+ . ' INNER JOIN opd_master om ON om.opd_id = pr.opd_id'
+ . ' WHERE pr.date_opd_visit = ? AND om.doc_id = ?',
+ [date('Y-m-d'), (int) ($opdRow->doc_id ?? 0)]
+ )->getRow();
$allowedData['queue_no'] = (int) ($maxQueue->max_queue ?? 0) + 1;
}
@@ -6008,12 +6010,14 @@ class Opd_prescription extends BaseController
$insert['session_id'] = 0;
}
if (in_array('queue_no', $fields, true)) {
- $maxQueue = $this->db->table('opd_prescription')
- ->selectMax('queue_no', 'max_queue')
- ->where('date_opd_visit', date('Y-m-d'))
- ->where('doc_id', (int) ($opdRow->doc_id ?? 0))
- ->get()
- ->getRow();
+ // JOIN through opd_master to guarantee per-doctor isolation
+ $maxQueue = $this->db->query(
+ 'SELECT COALESCE(MAX(pr.queue_no), 0) AS max_queue'
+ . ' FROM opd_prescription pr'
+ . ' INNER JOIN opd_master om ON om.opd_id = pr.opd_id'
+ . ' WHERE pr.date_opd_visit = ? AND om.doc_id = ?',
+ [date('Y-m-d'), (int) ($opdRow->doc_id ?? 0)]
+ )->getRow();
$insert['queue_no'] = (int) ($maxQueue->max_queue ?? 0) + 1;
}
diff --git a/app/Views/billing/opd_appointment_list.php b/app/Views/billing/opd_appointment_list.php
index a4dacf8..685bba2 100644
--- a/app/Views/billing/opd_appointment_list.php
+++ b/app/Views/billing/opd_appointment_list.php
@@ -57,9 +57,14 @@ $mergedGroups = [
Cancelled = $countCancelled ?>
-
@@ -471,14 +476,28 @@ $mergedGroups = [
}
if (liveDigest !== String(resp.digest || '')) {
- mergeLiveRowsOrReload();
- return;
+ // Instead of auto-reloading, show notification on the Refresh button
+ signalRefreshAvailable();
}
}).always(function() {
livePollInFlight = false;
});
}
+ function signalRefreshAvailable() {
+ var $btn = $('#btn_manual_refresh');
+ if (!$btn.hasClass('btn-warning')) {
+ $btn.removeClass('btn-outline-secondary').addClass('btn-warning text-dark');
+ $btn.html('↻ Refresh New');
+ }
+ }
+
+ function clearRefreshSignal() {
+ var $btn = $('#btn_manual_refresh');
+ $btn.removeClass('btn-warning text-dark').addClass('btn-outline-secondary');
+ $btn.html('↻ Refresh');
+ }
+
function updateQueueCell($row, queueNo) {
var qNo = parseInt(queueNo || '0', 10);
var $queueCell = $row.find('td').eq(3);
@@ -501,87 +520,6 @@ $mergedGroups = [
}
}
- function mergeLiveRowsOrReload() {
- $.getJSON(liveRowsUrl, function(resp) {
- if (!resp || parseInt(resp.ok || 0, 10) !== 1 || !Array.isArray(resp.rows)) {
- reloadAppointmentList();
- return;
- }
-
- var $domRows = $('#opdAllTable tbody tr[data-opd-id]');
- if ($domRows.length !== resp.rows.length) {
- reloadAppointmentList();
- return;
- }
-
- var domMap = {};
- $domRows.each(function() {
- var id = parseInt($(this).attr('data-opd-id') || '0', 10);
- if (id > 0) {
- domMap[id] = $(this);
- }
- });
-
- var requiresFullReload = false;
-
- for (var i = 0; i < resp.rows.length; i++) {
- var r = resp.rows[i] || {};
- var opdId = parseInt(r.opd_id || 0, 10);
- if (!opdId || !domMap[opdId]) {
- requiresFullReload = true;
- break;
- }
-
- var $row = domMap[opdId];
- var nextStatus = (r.status || 'booked').toString().toLowerCase();
- var prevStatus = (($row.attr('data-opd-status') || '').toLowerCase());
-
- // Booked transitions usually change action layout significantly, keep a safe fallback.
- if (nextStatus === 'booked' && prevStatus !== 'booked') {
- requiresFullReload = true;
- break;
- }
-
- $row.attr('data-opd-status', nextStatus);
- $row.attr('data-has-prescription', parseInt(r.has_prescription || 0, 10) === 1 ? '1' : '0');
- $row.attr('data-has-vitals', parseInt(r.has_vitals || 0, 10) === 1 ? '1' : '0');
-
- applyStatusVisual($row, nextStatus);
- updateQueueCell($row, r.queue_no || 0);
-
- if (nextStatus === 'waiting') {
- ensureWaitingActionButtons($row, opdId);
- } else if (nextStatus === 'visited') {
- $row.find('.btn-opd-create-queue').remove();
- var $statusBtn = $row.find('.btn-opd-status').first();
- if ($statusBtn.length) {
- $statusBtn.removeClass('btn-outline-success').addClass('btn-success').attr('data-opd-status', '2').text('Visit Done');
- }
- } else if (nextStatus === 'cancelled') {
- $row.find('.btn-opd-status').remove();
- $row.find('.btn-opd-create-queue').remove();
- }
-
- updateVitalsButtonState($row, r.has_prescription || 0, r.has_vitals || 0);
- }
-
- if (requiresFullReload) {
- reloadAppointmentList();
- return;
- }
-
- if (opdDataTable) {
- opdDataTable.rows().invalidate().draw(false);
- }
-
- recalcStatusCounts();
- applyFilters();
- liveDigest = String(resp.digest || '');
- }).fail(function() {
- // Keep screen usable even if live sync fails intermittently.
- });
- }
-
function startLiveUpdates() {
if (window.opdLiveMonitorTimer) {
clearInterval(window.opdLiveMonitorTimer);
@@ -920,12 +858,18 @@ $mergedGroups = [
});
});
+ // Manual refresh button
+ $(document).off('click.opdrefresh', '#btn_manual_refresh').on('click.opdrefresh', '#btn_manual_refresh', function() {
+ clearRefreshSignal();
+ reloadAppointmentList();
+ });
+
// Initialize
registerOpdTableFilter();
opdDataTable = initializeOpdTable();
recalcStatusCounts();
syncWaitingHintAndBadge();
- startLiveUpdates();
+ startLiveUpdates(); // lightweight digest poll — shows "Refresh" badge instead of auto-reloading
})();