consolidate attr_url function (#2143)
[openemr.git] / interface / main / finder / patient_select.php
blobd7353e10fdb6069c2bf70f5bd1f80c7da4673de2
1 <?php
2 /**
3 * Patient selector screen.
5 * @package OpenEMR
6 * @author Brady Miller <brady.g.miller@gmail.com>
7 * @copyright (C) 2017 Brady Miller <brady.g.miller@gmail.com>
8 * @link http://www.open-emr.org
9 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
13 require_once("../../globals.php");
14 require_once("$srcdir/patient.inc");
15 require_once("$srcdir/options.inc.php");
16 require_once("$srcdir/report_database.inc");
18 if (!empty($_REQUEST)) {
19 if (!verifyCsrfToken($_REQUEST["csrf_token_form"])) {
20 csrfNotVerified();
24 $fstart = isset($_REQUEST['fstart']) ? $_REQUEST['fstart'] : 0;
25 $popup = empty($_REQUEST['popup']) ? 0 : 1;
26 $message = isset($_GET['message']) ? $_GET['message'] : "";
27 $from_page = isset($_REQUEST['from_page']) ? $_REQUEST['from_page'] : "";
30 <!DOCTYPE html>
31 <html>
32 <head>
33 <?php html_header_show();?>
34 <script type="text/javascript" src="<?php echo $webroot ?>/interface/main/tabs/js/include_opener.js"></script>
36 <link rel=stylesheet href="<?php echo $css_header;?>" type="text/css">
37 <style>
38 form {
39 padding: 0px;
40 margin: 0px;
42 #searchCriteria {
43 text-align: center;
44 width: 100%;
45 font-size: 0.8em;
46 background-color: #ddddff;
47 font-weight: bold;
48 padding: 3px;
50 #searchResultsHeader {
51 width: 100%;
52 background-color: lightgrey;
54 #searchResultsHeader table {
55 width: 96%; /* not 100% because the 'searchResults' table has a scrollbar */
56 border-collapse: collapse;
58 #searchResultsHeader th {
59 font-size: 0.7em;
61 #searchResults {
62 width: 100%;
63 height: 80%;
64 overflow: auto;
67 .srName { width: 12%; }
68 .srGender { width: 5%; }
69 .srPhone { width: 11%; }
70 .srSS { width: 11%; }
71 .srDOB { width: 8%; }
72 .srID { width: 7%; }
73 .srPID { width: 7%; }
74 .srNumEnc { width: 11%; }
75 .srNumDays { width: 11%; }
76 .srDateLast { width: 11%; }
77 .srDateNext { width: 11%; }
78 .srMisc { width: 10%; }
80 #searchResults table {
81 width: 100%;
82 border-collapse: collapse;
83 background-color: white;
85 #searchResults tr {
86 cursor: hand;
87 cursor: pointer;
89 #searchResults td {
90 font-size: 0.7em;
91 border-bottom: 1px solid #eee;
93 .oneResult { }
94 .billing { color: red; font-weight: bold; }
95 .highlight {
96 background-color: #336699;
97 color: white;
99 </style>
101 <script type="text/javascript" src="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery/dist/jquery.min.js"></script>
103 <?php if ($popup) { ?>
104 <script type="text/javascript" src="../../../library/topdialog.js"></script>
105 <?php } ?>
107 <script language="JavaScript">
108 <?php if ($popup) {
109 require($GLOBALS['srcdir'] . "/restoreSession.php");
110 } ?>
111 // This is called when forward or backward paging is done.
113 function submitList(offset) {
114 var f = document.forms[0];
115 var i = parseInt(f.fstart.value) + offset;
116 if (i < 0) i = 0;
117 f.fstart.value = i;
118 top.restoreSession();
119 f.submit();
122 </script>
124 </head>
125 <body class="body_top">
127 <form method='post' action='patient_select.php' name='theform' onsubmit='return top.restoreSession()'>
128 <input type="hidden" name="csrf_token_form" value="<?php echo attr(collectCsrfToken()); ?>" />
130 <input type='hidden' name='fstart' value='<?php echo attr($fstart); ?>' />
132 <?php
133 $MAXSHOW = 100; // maximum number of results to display at once
135 //the maximum number of patient records to display:
136 $sqllimit = $MAXSHOW;
137 $given = "*";
138 $orderby = "lname ASC, fname ASC";
140 $search_service_code = trim($_POST['search_service_code']);
141 echo "<input type='hidden' name='search_service_code' value='" .
142 attr($search_service_code) . "' />\n";
144 if ($popup) {
145 echo "<input type='hidden' name='popup' value='1' />\n";
147 // Construct WHERE clause and save search parameters as form fields.
148 $sqlBindArray = array();
149 $where = "1 = 1";
150 $fres = sqlStatement("SELECT * FROM layout_options " .
151 "WHERE form_id = 'DEM' AND uor > 0 AND field_id != '' " .
152 "ORDER BY group_id, seq");
153 while ($frow = sqlFetchArray($fres)) {
154 $field_id = $frow['field_id'];
155 if (strpos($field_id, 'em_') === 0) {
156 continue;
159 $data_type = $frow['data_type'];
160 if (!empty($_REQUEST[$field_id])) {
161 $value = trim($_REQUEST[$field_id]);
162 if ($field_id == 'pid') {
163 $where .= " AND " . escape_sql_column_name($field_id, array('patient_data')) . " = ?";
164 array_push($sqlBindArray, $value);
165 } else if ($field_id == 'pubpid') {
166 $where .= " AND " . escape_sql_column_name($field_id, array('patient_data')) . " LIKE ?";
167 array_push($sqlBindArray, $value);
168 //for 'date' field
169 } else if ($data_type == 4) {
170 $where .= " AND " . escape_sql_column_name($field_id, array('patient_data')) . " LIKE ?";
171 array_push($sqlBindArray, DateToYYYYMMDD($value));
172 } else {
173 $where .= " AND " . escape_sql_column_name($field_id, array('patient_data')) . " LIKE ?";
174 array_push($sqlBindArray, $value."%");
177 echo "<input type='hidden' name='" . attr($field_id) .
178 "' value='" . attr($value) . "' />\n";
182 // If a non-empty service code was given, then restrict to patients who
183 // have been provided that service. Since the code is used in a LIKE
184 // clause, % and _ wildcards are supported.
185 if ($search_service_code) {
186 $where .=
187 " AND ( SELECT COUNT(*) FROM billing AS b WHERE " .
188 "b.pid = patient_data.pid AND " .
189 "b.activity = 1 AND " .
190 "b.code_type != 'COPAY' AND " .
191 "b.code LIKE ? " .
192 ") > 0";
193 array_push($sqlBindArray, $search_service_code);
196 $sql = "SELECT $given FROM patient_data " .
197 "WHERE $where ORDER BY $orderby LIMIT " . escape_limit($fstart) . ", " . escape_limit($sqllimit);
199 $rez = sqlStatement($sql, $sqlBindArray);
200 $result = array();
201 while ($row = sqlFetchArray($rez)) {
202 $result[] = $row;
205 _set_patient_inc_count($sqllimit, count($result), $where, $sqlBindArray);
206 } else if ($from_page == "cdr_report") {
207 // Collect setting from cdr report
208 echo "<input type='hidden' name='from_page' value='" . attr($from_page) . "' />\n";
209 $report_id = isset($_REQUEST['report_id']) ? $_REQUEST['report_id'] : 0;
210 echo "<input type='hidden' name='report_id' value='" . attr($report_id) . "' />\n";
211 $itemized_test_id = isset($_REQUEST['itemized_test_id']) ? $_REQUEST['itemized_test_id'] : 0;
212 echo "<input type='hidden' name='itemized_test_id' value='" . attr($itemized_test_id) . "' />\n";
213 $numerator_label = isset($_REQUEST['numerator_label']) ? $_REQUEST['numerator_label'] : '';
214 echo "<input type='hidden' name='numerator_label' value='" . attr($numerator_label) . "' />\n";
215 $pass_id = isset($_REQUEST['pass_id']) ? $_REQUEST['pass_id'] : "all";
216 echo "<input type='hidden' name='pass_id' value='" . attr($pass_id) . "' />\n";
217 $print_patients = isset($_REQUEST['print_patients'])? $_REQUEST['print_patients'] : 0;
218 echo "<input type='hidden' name='print_patients' value='" . attr($print_patients) . "' />\n";
220 // Collect patient listing from cdr report
221 if ($print_patients) {
222 // collect entire listing for printing
223 $result = collectItemizedPatientsCdrReport($report_id, $itemized_test_id, $pass_id, $numerator_label);
224 $GLOBALS['PATIENT_INC_COUNT'] = count($result);
225 $MAXSHOW = $GLOBALS['PATIENT_INC_COUNT'];
226 } else {
227 // collect the total listing count
228 $GLOBALS['PATIENT_INC_COUNT'] = collectItemizedPatientsCdrReport($report_id, $itemized_test_id, $pass_id, $numerator_label, true);
229 // then just collect applicable list for pagination
230 $result = collectItemizedPatientsCdrReport($report_id, $itemized_test_id, $pass_id, $numerator_label, false, $sqllimit, $fstart);
232 } else {
233 $patient = $_REQUEST['patient'];
234 $findBy = $_REQUEST['findBy'];
235 $searchFields = $_REQUEST['searchFields'];
237 echo "<input type='hidden' name='patient' value='" . attr($patient) . "' />\n";
238 echo "<input type='hidden' name='findBy' value='" . attr($findBy) . "' />\n";
240 if ($findBy == "Last") {
241 $result = getPatientLnames($patient, $given, $orderby, $sqllimit, $fstart);
242 } else if ($findBy == "ID") {
243 $result = getPatientId($patient, $given, "id ASC, ".$orderby, $sqllimit, $fstart);
244 } else if ($findBy == "DOB") {
245 $result = getPatientDOB(DateToYYYYMMDD($patient), $given, "DOB ASC, ".$orderby, $sqllimit, $fstart);
246 } else if ($findBy == "SSN") {
247 $result = getPatientSSN($patient, $given, "ss ASC, ".$orderby, $sqllimit, $fstart);
248 } elseif ($findBy == "Phone") { //(CHEMED) Search by phone number
249 $result = getPatientPhone($patient, $given, $orderby, $sqllimit, $fstart);
250 } else if ($findBy == "Any") {
251 $result = getByPatientDemographics($patient, $given, $orderby, $sqllimit, $fstart);
252 } else if ($findBy == "Filter") {
253 $result = getByPatientDemographicsFilter(
254 $searchFields,
255 $patient,
256 $given,
257 $orderby,
258 $sqllimit,
259 $fstart,
260 $search_service_code
266 </form>
268 <table border='0' cellpadding='5' cellspacing='0' width='100%'>
269 <tr>
270 <td class='text'>
271 <?php if ($from_page == "cdr_report") { ?>
272 <a href='../../reports/cqm.php?report_id=<?php echo attr_url($report_id); ?>' class='css_button' onclick='top.restoreSession()'><span><?php echo xlt("Return To Report Results"); ?></span></a>
273 <?php } else { ?>
274 <a href="./patient_select_help.php" target=_new onclick='top.restoreSession()'>[<?php echo xlt('Help'); ?>]&nbsp</a>
275 <?php } ?>
276 </td>
277 <td class='text' align='center'>
278 <?php if ($message) {
279 echo "<font color='red'><b>" . text($message) . "</b></font>\n";
280 } ?>
281 </td>
282 <td>
283 <?php if ($from_page == "cdr_report") { ?>
284 <?php echo "<a href='patient_select.php?from_page=cdr_report&pass_id=" . attr_url($pass_id) . "&report_id=" . attr_url($report_id) . "&itemized_test_id=" . attr_url($itemized_test_id) . "&numerator_label=" . attr_url($row['numerator_label']) . "&print_patients=1&csrf_token_form=" . attr_url(collectCsrfToken()) . "' class='css_button' onclick='top.restoreSession()'><span>" . xlt("Print Entire Listing") . "</span></a>"; ?>
285 <?php } ?> &nbsp;
286 </td>
287 <td class='text' align='right'>
288 <?php
289 // Show start and end row number, and number of rows, with paging links.
291 // $count = $fstart + $GLOBALS['PATIENT_INC_COUNT']; // Why did I do that???
292 $count = $GLOBALS['PATIENT_INC_COUNT'];
293 $fend = $fstart + $MAXSHOW;
294 if ($fend > $count) {
295 $fend = $count;
298 <?php if ($fstart) { ?>
299 <a href="javascript:submitList(-<?php echo attr(addslashes($MAXSHOW)); ?>)">
300 &lt;&lt;
301 </a>
302 &nbsp;&nbsp;
303 <?php } ?>
304 <?php
305 $countStatement = " - " . $fend . " " . xl('of') . " " . $count;
306 echo ($fstart + 1) . text($countStatement);
308 <?php if ($count > $fend) { ?>
309 &nbsp;&nbsp;
310 <a href="javascript:submitList(<?php echo attr(addslashes($MAXSHOW)); ?>)">
311 &gt;&gt;
312 </a>
313 <?php } ?>
314 </td>
315 </tr>
316 <tr>
317 <?php if ($from_page == "cdr_report") {
318 echo "<td colspan='6' class='text'>";
319 echo "<b>";
320 if ($pass_id == "fail") {
321 echo xlt("Failed Patients");
322 } else if ($pass_id == "pass") {
323 echo xlt("Passed Patients");
324 } else if ($pass_id == "exclude") {
325 echo xlt("Excluded Patients");
326 } else { // $pass_id == "all"
327 echo xlt("All Patients");
330 echo "</b>";
331 echo " - ";
332 echo collectItemizedRuleDisplayTitle($report_id, $itemized_test_id, $numerator_label);
333 echo "</td>";
334 } ?>
335 </tr>
336 </table>
338 <div id="searchResultsHeader" class="head">
339 <table>
340 <tr>
341 <th class="srName"><?php echo xlt('Name'); ?></th>
342 <th class="srGender"><?php echo xlt('Sex'); ?></th>
343 <th class="srPhone"><?php echo xlt('Phone'); ?></th>
344 <th class="srSS"><?php echo xlt('SS'); ?></th>
345 <th class="srDOB"><?php echo xlt('DOB'); ?></th>
346 <th class="srID"><?php echo xlt('ID'); ?></th>
348 <?php if (empty($GLOBALS['patient_search_results_style'])) { ?>
349 <th class="srPID"><?php echo xlt('PID'); ?></th>
350 <th class="srNumEnc"><?php echo xlt('[Number Of Encounters]'); ?></th>
351 <th class="srNumDays"><?php echo xlt('[Days Since Last Encounter]'); ?></th>
352 <th class="srDateLast"><?php echo xlt('[Date of Last Encounter]'); ?></th>
353 <th class="srDateNext">
354 <?php
355 $add_days = 90;
356 if (!$popup && preg_match('/^(\d+)\s*(.*)/', $patient, $matches) > 0) {
357 $add_days = $matches[1];
358 $patient = $matches[2];
361 [<?php echo attr($add_days);?> <?php echo xlt('Days From Last Encounter'); ?>]
362 </th>
364 <?php
365 } else {
366 // Alternate patient search results style; this gets address plus other
367 // fields that are mandatory, up to a limit of 5.
368 $extracols = array();
369 $tres = sqlStatement("SELECT * FROM layout_options " .
370 "WHERE form_id = 'DEM' AND ( uor > 1 AND field_id != '' " .
371 "OR uor > 0 AND field_id = 'street' ) AND " .
372 "field_id NOT LIKE '_name' AND " .
373 "field_id NOT LIKE 'phone%' AND " .
374 "field_id NOT LIKE 'title' AND " .
375 "field_id NOT LIKE 'ss' AND " .
376 "field_id NOT LIKE 'DOB' AND " .
377 "field_id NOT LIKE 'pubpid' " .
378 "ORDER BY group_id, seq LIMIT 5");
379 while ($trow = sqlFetchArray($tres)) {
380 $extracols[$trow['field_id']] = $trow;
381 echo "<th class='srMisc'>" . xlt($trow['title']) . "</th>\n";
386 </tr>
387 </table>
388 </div>
390 <div id="searchResults">
392 <table>
393 <tr>
394 <?php
395 if ($result) {
396 foreach ($result as $iter) {
397 echo "<tr class='oneresult' id='" . attr($iter['pid']) . "'>";
398 echo "<td class='srName'>" . text($iter['lname'] . ", " . $iter['fname']) . "</td>\n";
399 echo "<td class='srGender'>" . text(getListItemTitle("sex", $iter['sex'])) . "</td>\n";
400 //other phone number display setup for tooltip
401 $phone_biz = '';
402 if ($iter{"phone_biz"} != "") {
403 $phone_biz = " [business phone ".$iter{"phone_biz"}."] ";
406 $phone_contact = '';
407 if ($iter{"phone_contact"} != "") {
408 $phone_contact = " [contact phone ".$iter{"phone_contact"}."] ";
411 $phone_cell = '';
412 if ($iter{"phone_cell"} != "") {
413 $phone_cell = " [cell phone ".$iter{"phone_cell"}."] ";
416 $all_other_phones = $phone_biz.$phone_contact.$phone_cell;
417 if ($all_other_phones == '') {
418 $all_other_phones = xl('No other phone numbers listed');
421 //end of phone number display setup, now display the phone number(s)
422 echo "<td class='srPhone' title='" . attr($all_other_phones) . "'>" .
423 text($iter['phone_home']) . "</td>\n";
425 echo "<td class='srSS'>" . text($iter['ss']) . "</td>";
426 if ($iter{"DOB"} != "0000-00-00 00:00:00") {
427 echo "<td class='srDOB'>" . text(oeFormatShortDate($iter['DOB'])) . "</td>";
428 } else {
429 echo "<td class='srDOB'>&nbsp;</td>";
432 echo "<td class='srID'>" . text($iter['pubpid']) . "</td>";
434 if (empty($GLOBALS['patient_search_results_style'])) {
435 echo "<td class='srPID'>" . text($iter['pid']) . "</td>";
437 //setup for display of encounter date info
438 $encounter_count = 0;
439 $day_diff = '';
440 $last_date_seen = '';
441 $next_appt_date= '';
442 $pid = '';
444 // calculate date differences based on date of last encounter with billing entries
445 $query = "select max(form_encounter.date) as mydate," .
446 " (to_days(current_date())-to_days(max(form_encounter.date))) as day_diff," .
447 " (max(form_encounter.date) + interval " .
448 escape_limit($add_days) .
449 " day) as next_appt, dayname(max(form_encounter.date) + interval " .
450 escape_limit($add_days) .
451 " day) as next_appt_day from form_encounter " .
452 "join billing on billing.encounter = form_encounter.encounter and " .
453 "billing.pid = form_encounter.pid and billing.activity = 1 and " .
454 "billing.code_type not like 'COPAY' where ".
455 "form_encounter.pid = ?";
456 $statement= sqlStatement($query, array($iter{"pid"}));
457 if ($results = sqlFetchArray($statement)) {
458 $last_date_seen = $results['mydate'];
459 $day_diff = $results['day_diff'];
460 $next_appt_date= xl($results['next_appt_day']).', '.oeFormatShortDate($results['next_appt']);
463 // calculate date differences based on date of last encounter regardless of billing
464 $query = "select max(form_encounter.date) as mydate," .
465 " (to_days(current_date())-to_days(max(form_encounter.date))) as day_diff," .
466 " (max(form_encounter.date) + interval " .
467 escape_limit($add_days) .
468 " day) as next_appt, dayname(max(form_encounter.date) + interval " .
469 escape_limit($add_days) .
470 " day) as next_appt_day from form_encounter " .
471 " where form_encounter.pid = ?";
472 $statement= sqlStatement($query, array($iter{"pid"}));
473 if ($results = sqlFetchArray($statement)) {
474 $last_date_seen = $results['mydate'];
475 $day_diff = $results['day_diff'];
476 $next_appt_date= xl($results['next_appt_day']).', '.oeFormatShortDate($results['next_appt']);
479 //calculate count of encounters by distinct billing dates with cpt4
480 //entries
481 $query = "select count(distinct date) as encounter_count " .
482 " from billing ".
483 " where code_type not like 'COPAY' and activity = 1 " .
484 " and pid = ?";
485 $statement= sqlStatement($query, array($iter{"pid"}));
486 if ($results = sqlFetchArray($statement)) {
487 $encounter_count_billed = $results['encounter_count'];
490 // calculate count of encounters, regardless of billing
491 $query = "select count(date) as encounter_count ".
492 " from form_encounter where ".
493 " pid = ?";
494 $statement= sqlStatement($query, array($iter{"pid"}));
495 if ($results = sqlFetchArray($statement)) {
496 $encounter_count = $results['encounter_count'];
499 echo "<td class='srNumEnc'>" . text($encounter_count) . "</td>\n";
500 echo "<td class='srNumDay'>" . text($day_diff) . "</td>\n";
501 echo "<td class='srDateLast'>" . text(oeFormatShortDate($last_date_seen)) . "</td>\n";
502 echo "<td class='srDateNext'>" . text($next_appt_date) . "</td>\n";
503 } else { // alternate search results style
504 foreach ($extracols as $field_id => $frow) {
505 echo "<td class='srMisc'>";
506 echo generate_display_field($frow, $iter[$field_id]);
508 echo"</td>\n";
514 </table>
515 </div> <!-- end searchResults DIV -->
517 <script language="javascript">
519 // jQuery stuff to make the page a little easier to use
521 $(document).ready(function(){
522 // $("#searchparm").focus();
523 $(".oneresult").mouseover(function() { $(this).addClass("highlight"); });
524 $(".oneresult").mouseout(function() { $(this).removeClass("highlight"); });
525 $(".oneresult").click(function() { SelectPatient(this); });
526 // $(".event").dblclick(function() { EditEvent(this); });
527 <?php if ($print_patients) { ?>
528 var win = top.printLogPrint ? top : opener.top;
529 win.printLogPrint(window);
530 <?php } ?>
533 var SelectPatient = function (eObj) {
535 // The layout loads just the demographics frame here, which in turn
536 // will set the pid and load all the other frames.
537 objID = eObj.id;
538 var parts = objID.split("~");
539 <?php if (!$popup) { ?>
540 top.restoreSession();
541 document.location.href = "../../patient_file/summary/demographics.php?set_pid=" + parts[0];
542 <?php } else if ($popup) { ?>
543 dlgclose("srchDone", parts[0]);
544 <?php } ?>
546 return true;
549 </script>
551 </body>
552 </html>