3 * Patient Tracker (Patient Flow Board)
5 * This program displays the information entered in the Calendar program ,
6 * allowing the user to change status and view those changed here and in the Calendar
7 * Will allow the collection of length of time spent in each status
10 * @link http://www.open-emr.org
11 * @author Terry Hill <terry@lilysystems.com>
12 * @author Brady Miller <brady.g.miller@gmail.com>
13 * @copyright Copyright (c) 2015 Terry Hill <terry@lillysystems.com>
14 * @copyright Copyright (c) 2017 Brady Miller <brady.g.miller@gmail.com>
15 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
19 require_once("../globals.php");
20 require_once("$srcdir/patient.inc");
21 require_once("$srcdir/options.inc.php");
22 require_once("$srcdir/patient_tracker.inc.php");
23 require_once("$srcdir/user.inc");
25 // mdsupport - user_settings prefix
26 $uspfx = substr(__FILE__
, strlen($webserver_root)) . '.';
27 $setting_new_window = prevSetting($uspfx, 'setting_new_window', 'form_new_window', ' ');
29 #define variables, future enhancement allow changing the to_date and from_date
30 #to allow picking a date to review
32 if (!is_null($_POST['form_provider'])) {
33 $provider = $_POST['form_provider'];
34 } else if ($_SESSION['userauthorized']) {
35 $provider = $_SESSION['authUserID'];
40 $facility = !is_null($_POST['form_facility']) ?
$_POST['form_facility'] : null;
41 $form_apptstatus = !is_null($_POST['form_apptstatus']) ?
$_POST['form_apptstatus'] : null;
43 if (isset($_POST['form_apptcat'])) {
44 if ($form_apptcat!="ALL") {
45 $form_apptcat=intval($_POST['form_apptcat']);
49 $form_patient_name = !is_null($_POST['form_patient_name']) ?
$_POST['form_patient_name'] : null;
50 $form_patient_id = !is_null($_POST['form_patient_id']) ?
$_POST['form_patient_id'] : null;
52 $appointments = array();
53 $from_date = date("Y-m-d");
54 $to_date = date("Y-m-d");
55 $datetime = date("Y-m-d H:i:s");
57 # go get the information and process it
58 $appointments = fetch_Patient_Tracker_Events($from_date, $to_date, $provider, $facility, $form_apptstatus, $form_apptcat, $form_patient_name, $form_patient_id);
59 $appointments = sortAppointments($appointments, 'time');
61 //grouping of the count of every status
62 $appointments_status = getApptStatus($appointments);
64 // Below are new constants for the translation pipeline
66 // xl('Reminder done')
72 // xl('Left w/o visit')
73 // xl('Ins/fin issue')
77 // xl('Canceled < 24h')
78 $lres = sqlStatement("SELECT option_id, title FROM list_options WHERE list_id = ? AND activity=1", array('apptstat'));
79 while ($lrow = sqlFetchArray($lres)) {
80 // if exists, remove the legend character
81 if ($lrow['title'][1] == ' ') {
82 $splitTitle = explode(' ', $lrow['title']);
83 array_shift($splitTitle);
84 $title = implode(' ', $splitTitle);
86 $title = $lrow['title'];
89 $statuses_list[$lrow['option_id']] = $title;
92 $chk_prov = array(); // list of providers with appointments
94 // Scan appointments for additional info
95 foreach ($appointments as $apt) {
96 $chk_prov[$apt['uprovider_id']] = $apt['ulname'] . ', ' . $apt['ufname'] . ' ' . $apt['umname'];
101 <title
><?php
echo xlt("Flow Board") ?
></title
>
102 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/css/bootstrap.min.css" rel
="stylesheet" type
="text/css" />
103 <link rel
="stylesheet" href
="<?php echo $css_header;?>" type
="text/css">
104 <link rel
="stylesheet" href
="<?php echo $GLOBALS['assets_static_relative'];?>/font-awesome-4-6-3/css/font-awesome.css" type
="text/css">
106 <script type
="text/javascript" src
="../../library/dialog.js?v=<?php echo $v_js_includes; ?>"></script
>
107 <script type
="text/javascript" src
="../../library/js/common.js"></script
>
108 <script type
="text/javascript" src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-3-1-1/index.js"></script
>
110 <script language
="JavaScript">
112 function refreshme() {
113 top
.restoreSession();
114 document
.pattrk
.submit();
116 // popup for patient tracker status
117 function bpopup(tkid
) {
119 dlgopen('../patient_tracker/patient_tracker_status.php?tracker_id=' + tkid
, '_blank', 500, 250);
123 // popup for calendar add edit
124 function calendarpopup(eid
,date_squash
) {
126 dlgopen('../main/calendar/add_edit_event.php?eid=' + eid +
'&date=' + date_squash
, '_blank', 775, 500);
130 // auto refresh screen pat_trkr_timer is the timer variable
131 function refreshbegin(first
){
132 <?php
if ($GLOBALS['pat_trkr_timer'] != '0') { ?
>
133 var reftime
="<?php echo attr($GLOBALS['pat_trkr_timer']); ?>";
134 var parsetime
=reftime
.split(":");
135 parsetime
=(parsetime
[0]*60)+
(parsetime
[1]*1)*1000;
139 setTimeout("refreshbegin('0')",parsetime
);
145 // used to display the patient demographic and encounter screens
146 function topatient(newpid
, enc
) {
147 if (document
.pattrk
.form_new_window
.checked
) {
148 openNewTopWindow(newpid
,enc
);
151 top
.restoreSession();
153 top
.RTop
.location
= "<?php echo $GLOBALS['webroot']; ?>/interface/patient_file/summary/demographics.php?set_pid=" + newpid +
"&set_encounterid=" + enc
;
156 top
.RTop
.location
= "<?php echo $GLOBALS['webroot']; ?>/interface/patient_file/summary/demographics.php?set_pid=" + newpid
;
161 // opens the demographic and encounter screens in a new window
162 function openNewTopWindow(newpid
,newencounterid
) {
163 document
.fnew
.patientID
.value
= newpid
;
164 document
.fnew
.encounterID
.value
= newencounterid
;
165 top
.restoreSession();
166 document
.fnew
.submit();
174 if ($GLOBALS['pat_trkr_timer'] == '0') {
175 // if the screen is not set up for auto refresh, use standard page call
176 $action_page = "patient_tracker.php";
178 // if the screen is set up for auto refresh, this will allow it to be closed by auto logoff
179 $action_page = "patient_tracker.php?skip_timeout_reset=1";
183 <span
class="title"><?php
echo xlt("Flow Board") ?
></span
>
184 <body
class="body_top" >
185 <form method
='post' name
='theform' id
='theform' action
='<?php echo $action_page; ?>' onsubmit
='return top.restoreSession()'>
186 <div id
="flow_board_parameters">
189 <td
class='label_custom'><?php
echo xlt('Provider'); ?
>:</td
>
192 # Build a drop-down list of providers.
194 $query = "SELECT id, lname, fname FROM users WHERE ".
195 "authorized = 1 ORDER BY lname, fname"; #(CHEMED) facility filter
197 $ures = sqlStatement($query);
199 echo " <select name='form_provider'>\n";
200 echo " <option value='ALL'>-- " . xlt('All') . " --\n";
202 while ($urow = sqlFetchArray($ures)) {
203 $provid = $urow['id'];
204 echo " <option value='" . attr($provid) . "'";
205 if (isset($_POST['form_provider']) && $provid == $_POST['form_provider']) {
207 } elseif (!isset($_POST['form_provider'])&& $_SESSION['userauthorized'] && $provid == $_SESSION['authUserID']) {
211 echo ">" . text($urow['lname']) . ", " . text($urow['fname']) . "\n";
218 <td
class='label_custom'><?php
echo xlt('Status'); # status code drop down creation ?>:</td>
219 <td
><?php
generate_form_field(array('data_type'=>1,'field_id'=>'apptstatus','list_id'=>'apptstat','empty_title'=>'All'), $_POST['form_apptstatus']);?
></td
>
220 <td
><?php
echo xlt('Category') #category drop down creation ?>:</td>
222 <select id
="form_apptcat" name
="form_apptcat">
224 $categories=fetchAppointmentCategories();
225 echo "<option value='ALL'>".xlt("All")."</option>";
226 while ($cat=sqlFetchArray($categories)) {
227 echo "<option value='".attr($cat['id'])."'";
228 if ($cat['id']==$_POST['form_apptcat']) {
229 echo " selected='true' ";
232 echo ">".text(xl_appt_category($cat['category']))."</option>";
237 <td style
="border-left: 1px solid;" rowspan
="2">
238 <div style
='margin-left: 15px'>
239 <a href
='#' class='css_button' onclick
='$("#form_refresh").attr("value","true"); $("#theform").submit();'>
240 <span
> <?php
echo xlt('Filter'); ?
> </span
> </a
>
241 <?php
if ($_POST['form_refresh'] ||
$_POST['form_orderby']) { ?
>
242 <a href
='#' class='css_button' id
='printbutton'>
243 <span
> <?php
echo xlt('Print'); ?
> </span
> </a
>
249 <td
><?php
echo xlt('Patient ID') ?
>:</td
>
251 <input type
="text" id
="patient_id" name
="form_patient_id" value
="<?php echo ($form_patient_id) ? attr($form_patient_id) : ""; ?>">
253 <td
><?php
echo xlt('Patient Name') ?
>:</td
>
255 <input type
="text" id
="patient_name" name
="form_patient_name" value
="<?php echo ($form_patient_name) ? attr($form_patient_name) : ""; ?>">
262 <form name
='pattrk' id
='pattrk' method
='post' action
='<?php echo $action_page; ?>' onsubmit
='return top.restoreSession()' enctype
='multipart/form-data'>
265 <?php
if (count($chk_prov) == 1) {?
>
266 <h2
><span style
='float: left'><?php
echo xlt('Appointments for'). ' : '. text(reset($chk_prov)) ?
></span
></h2
>
268 <div id
= 'inanewwindow' class='inanewwindow'>
269 <span style
='float: right'>
270 <a id
='setting_cog'><i
class="fa fa-cog fa-2x fa-fw"> 
;</i
></a
>
271 <?php
// Note that are unable to html escape below $setting_new_window, or else will break the code, secondary to white space issues. ?>
272 <input type
='hidden' name
='setting_new_window' id
='setting_new_window' value
='<?php echo $setting_new_window ?>' />
273 <label id
='settings'><input type
='checkbox' name
='form_new_window' id
='form_new_window' value
='1'<?php
echo $setting_new_window ?
> >
274 <?php
echo xlt('Open Patient in New Window'); ?
></input
></label
>
275 <a id
='refreshme'><i
class="fa fa-refresh fa-2x fa-fw"> 
;</i
></a
>
279 <?php
if ($GLOBALS['pat_trkr_timer'] =='0') { ?
>
280 <table border
='0' cellpadding
='5' cellspacing
='0'>
282 <td align
='center'><br
>
283 <a href
='javascript:;' class='css_button_small' align
='center' onclick
="document.getElementById('pattrk').submit();"><span
><?php
echo xlt('Refresh Screen'); ?
></span
></a
>
289 <table border
='0' cellpadding
='1' cellspacing
='2' width
='100%'>
294 $statuses_output = xlt('Total patients') . ':' . text($appointments_status['count_all']);
295 unset($appointments_status['count_all']);
296 foreach ($appointments_status as $status_symbol => $count) {
297 $statuses_output .= " | " . text(xl_list_label($statuses_list[$status_symbol])) .":" . $count;
300 echo $statuses_output;
305 <tr bgcolor
="#cccff">
306 <?php
if ($GLOBALS['ptkr_show_pid']) { ?
>
307 <td
class="dehead" align
="center">
308 <?php
echo xlt('PID'); ?
>
311 <td
class="dehead" align
="center">
312 <?php
echo xlt('Patient'); ?
>
314 <?php
if ($GLOBALS['ptkr_visit_reason']) { ?
>
315 <td
class="dehead" align
="center">
316 <?php
echo xlt('Reason'); ?
>
319 <?php
if ($GLOBALS['ptkr_show_encounter']) { ?
>
320 <td
class="dehead" align
="center">
321 <?php
echo xlt('Encounter'); ?
>
324 <td
class="dehead" align
="center">
325 <?php
echo xlt('Exam Room #'); ?
>
327 <td
class="dehead" align
="center">
328 <?php
echo xlt('Appt Time'); ?
>
330 <td
class="dehead" align
="center">
331 <?php
echo xlt('Arrive Time'); ?
>
333 <td
class="dehead" align
="center">
334 <?php
echo xlt('Status'); ?
>
336 <td
class="dehead" align
="center">
337 <?php
echo xlt('Current Status Time'); ?
>
339 <td
class="dehead" align
="center">
340 <?php
echo xlt('Visit Type'); ?
>
342 <?php
if (count($chk_prov) > 1) { ?
>
343 <td
class="dehead" align
="center">
344 <?php
echo xlt('Provider'); ?
>
347 <td
class="dehead" align
="center">
348 <?php
echo xlt('Total Time'); ?
>
350 <td
class="dehead" align
="center">
351 <?php
echo xlt('Check Out Time'); ?
>
353 <td
class="dehead" align
="center">
354 <?php
echo xlt('Updated By'); ?
>
356 <?php
if ($GLOBALS['drug_screen']) { ?
>
357 <td
class="dehead" align
="center">
358 <?php
echo xlt('Random Drug Screen'); ?
>
360 <td
class="dehead" align
="center">
361 <?php
echo xlt('Drug Screen Completed'); ?
>
367 foreach ($appointments as $appointment) {
368 # Collect appt date and set up squashed date for use below
369 $date_appt = $appointment['pc_eventDate'];
370 $date_squash = str_replace("-", "", $date_appt);
372 # Collect variables and do some processing
373 $docname = $chk_prov[$appointment['uprovider_id']];
374 if (strlen($docname)<= 3) {
378 $ptname = $appointment['lname'] . ', ' . $appointment['fname'] . ' ' . $appointment['mname'];
379 $appt_enc = $appointment['encounter'];
380 $appt_eid = (!empty($appointment['eid'])) ?
$appointment['eid'] : $appointment['pc_eid'];
381 $appt_pid = (!empty($appointment['pid'])) ?
$appointment['pid'] : $appointment['pc_pid'];
383 continue; // skip when $appt_pid = 0, since this means it is not a patient specific appt slot
386 $status = (!empty($appointment['status'])) ?
$appointment['status'] : $appointment['pc_apptstatus'];
387 $appt_room = (!empty($appointment['room'])) ?
$appointment['room'] : $appointment['pc_room'];
388 $appt_time = (!empty($appointment['appttime'])) ?
$appointment['appttime'] : $appointment['pc_startTime'];
389 $tracker_id = $appointment['id'];
391 if ($GLOBALS['ptkr_visit_reason']) {
392 $reason_visit = $appointment['pc_hometext'];
395 $newarrive = collect_checkin($tracker_id);
396 $newend = collect_checkout($tracker_id);
397 $colorevents = (collectApptStatusSettings($status));
398 $bgcolor = $colorevents['color'];
399 $statalert = $colorevents['time_alert'];
400 # process the time to allow items with a check out status to be displayed
401 if (is_checkout($status) && ($GLOBALS['checkout_roll_off'] > 0)) {
402 $to_time = strtotime($newend);
403 $from_time = strtotime($datetime);
404 $display_check_out = round(abs($from_time - $to_time) / 60, 0);
405 if ($display_check_out >= $GLOBALS['checkout_roll_off']) {
410 <tr bgcolor
='<?php echo $bgcolor ?>'>
411 <?php
if ($GLOBALS['ptkr_show_pid']) { ?
>
412 <td
class="detail" align
="center">
413 <?php
echo text($appt_pid) ?
>
416 <td
class="detail" align
="center">
417 <a href
="#" onclick
="return topatient('<?php echo attr($appt_pid);?>','<?php echo attr($appt_enc);?>')" >
418 <?php
echo text($ptname); ?
></a
>
421 <?php
if ($GLOBALS['ptkr_visit_reason']) { ?
>
422 <td
class="detail" align
="center">
423 <?php
echo text($reason_visit) ?
>
426 <?php
if ($GLOBALS['ptkr_show_encounter']) { ?
>
427 <td
class="detail" align
="center">
428 <?php
if ($appt_enc != 0) {
429 echo text($appt_enc);
433 <td
class="detail" align
="center">
434 <?php
echo getListItemTitle('patient_flow_board_rooms', $appt_room);?
>
436 <td
class="detail" align
="center">
437 <?php
echo text(oeFormatTime($appt_time)); ?
>
439 <td
class="detail" align
="center">
440 <?php
echo ($newarrive ?
text(oeFormatTime($newarrive)) : ' ') ?
>
442 <td
class="detail" align
="center">
443 <?php
if (empty($tracker_id)) { #for appt not yet with tracker id and for recurring appt ?>
444 <a href
="" onclick
="return calendarpopup(<?php echo attr($appt_eid).",".attr($date_squash); # calls popup for add edit calendar event?>)">
446 <a href
="" onclick
="return bpopup(<?php echo attr($tracker_id); # calls popup for patient tracker status?>)">
448 <?php
echo text(getListItemTitle("apptstat", $status)); # drop down list for appointment status?>
453 #time in current status
454 $to_time = strtotime(date("Y-m-d H:i:s"));
456 if (strtotime($newend) != '') {
457 $from_time = strtotime($newarrive);
458 $to_time = strtotime($newend);
461 $from_time = strtotime($appointment['start_datetime']);
465 $timecheck = round(abs($to_time - $from_time) / 60, 0);
466 if ($timecheck >= $statalert && ($statalert != '0')) { # Determine if the time in status limit has been reached.
467 echo "<td align='center' class='js-blink-infinite'> "; # and if so blink
469 echo "<td align='center' class='detail'> "; # and if not do not blink
472 if (($yestime == '1') && ($timecheck >=1) && (strtotime($newarrive)!= '')) {
473 echo text($timecheck . ' ' .($timecheck >=2 ?
xl('minutes'): xl('minute')));
476 #end time in current status
479 <td
class="detail" align
="center">
480 <?php
echo text(xl_appt_category($appointment['pc_title'])) ?
>
482 <?php
if (count($chk_prov) > 1) { ?
>
483 <td
class="detail" align
="center">
484 <?php
echo text($docname); ?
>
487 <td
class="detail" align
="center">
490 # total time in practice
491 if (strtotime($newend) != '') {
492 $from_time = strtotime($newarrive);
493 $to_time = strtotime($newend);
495 $from_time = strtotime($newarrive);
496 $to_time = strtotime(date("Y-m-d H:i:s"));
499 $timecheck2 = round(abs($to_time - $from_time) / 60, 0);
500 if (strtotime($newarrive) != '' && ($timecheck2 >=1)) {
501 echo text($timecheck2 . ' ' .($timecheck2 >=2 ?
xl('minutes'): xl('minute')));
504 # end total time in practice
506 <?php
echo text($appointment['pc_time']); ?
>
508 <td
class="detail" align
="center">
510 if (strtotime($newend) != '') {
511 echo text(oeFormatTime($newend)) ;
515 <td
class="detail" align
="center">
516 <?php
echo text($appointment['user']) ?
>
518 <?php
if ($GLOBALS['drug_screen']) { ?
>
519 <?php
if (strtotime($newarrive) != '') { ?
>
520 <td
class="detail" align
="center">
521 <?php
if (text($appointment['random_drug_test']) == '1') {
530 <?php
if (strtotime($newarrive) != '' && $appointment['random_drug_test'] == '1') { ?
>
531 <td
class="detail" align
="center">
532 <?php
if (strtotime($newend) != '') { # the following block allows the check box for drug screens to be disabled once the status is check out ?>
533 <input type
=checkbox disabled
='disable' class="drug_screen_completed" id
="<?php echo htmlspecialchars($appointment['pt_tracker_id'], ENT_NOQUOTES) ?>" <?php
if ($appointment['drug_screen_completed'] == "1") {
537 <input type
=checkbox
class="drug_screen_completed" id
='<?php echo htmlspecialchars($appointment['pt_tracker_id
'], ENT_NOQUOTES) ?>' name
="drug_screen_completed" <?php
if ($appointment['drug_screen_completed'] == "1") {
552 //saving the filter for auto refresh
553 if (!is_null($_POST['form_provider'])) {
554 echo "<input type='hidden' name='form_provider' value='" . attr($_POST['form_provider']) . "'>";
557 if (!is_null($_POST['form_facility'])) {
558 echo "<input type='hidden' name='form_facility' value='" . attr($_POST['form_facility']) . "'>";
561 if (!is_null($_POST['form_apptstatus'])) {
562 echo "<input type='hidden' name='form_apptstatus' value='" . attr($_POST['form_apptstatus']) . "'>";
565 if (!is_null($_POST['form_apptcat'])) {
566 echo "<input type='hidden' name='form_apptcat' value='" . attr($_POST['form_apptcat']) . "'>";
569 if (!is_null($_POST['form_patient_id'])) {
570 echo "<input type='hidden' name='form_patient_id' value='" . attr($_POST['form_patient_id']) . "'>";
573 if (!is_null($_POST['form_patient_name'])) {
574 echo "<input type='hidden' name='form_patient_name' value='" . attr($_POST['form_patient_name']) . "'>";
581 <script type
="text/javascript">
582 $
(document
).ready(function() {
583 $
('#settings').css("display","none");
586 $
('.js-blink-infinite').each(function() {
587 // set up blinking text
589 setInterval(function() {
590 if (elem
.css('visibility') == 'hidden') {
591 elem
.css('visibility', 'visible');
593 elem
.css('visibility', 'hidden');
598 // toggle of the check box status for drug screen completed and ajax call to update the database
599 $
(".drug_screen_completed").change(function() {
600 top
.restoreSession();
602 testcomplete_toggle
="true";
604 testcomplete_toggle
="false";
606 $
.post( "../../library/ajax/drug_screen_completed.php", {
608 testcomplete
: testcomplete_toggle
613 // mdsupport - Immediately post changes to form_new_window
614 $
('#form_new_window').click(function () {
615 $
('#setting_new_window').val(this
.checked ?
' checked' : ' ');
616 $
.post( "<?php echo basename(__FILE__) ?>", {
617 data
: $
('form#pattrk').serialize(),
618 success
: function (data
) {}
622 $
('#setting_cog').click(function () {
623 $
(this
).css("display","none");
624 $
('#settings').css("display","inline");
627 $
('#refreshme').click(function () {
631 <!-- form used to open a
new top level window when a patient row is clicked
-->
632 <form name
='fnew' method
='post' target
='_blank' action
='../main/main_screen.php?auth=login&site=<?php echo attr($_SESSION['site_id
']); ?>'>
633 <input type
='hidden' name
='patientID' value
='0' />
634 <input type
='hidden' name
='encounterID' value
='0' />