Interim autoloaded library/classes via composer classmap, take 4. (#422)
[openemr.git] / interface / orders / list_reports.php
blob98f944d391c087b17d1e7ae3e903620b9af42174
1 <?php
2 /**
3 * List procedure orders and reports, and fetch new reports and their results.
5 * Copyright (C) 2013-2016 Rod Roark <rod@sunsetsystems.com>
7 * LICENSE: This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
18 * @package OpenEMR
19 * @author Rod Roark <rod@sunsetsystems.com>
22 $sanitize_all_escapes = true;
23 $fake_register_globals = false;
25 require_once("../globals.php");
26 require_once("$srcdir/log.inc");
27 require_once("$srcdir/acl.inc");
28 require_once("$srcdir/patient.inc");
29 require_once("$srcdir/options.inc.php");
30 require_once("$srcdir/formatting.inc.php");
31 require_once("./receive_hl7_results.inc.php");
32 require_once("./gen_hl7_order.inc.php");
34 /**
35 * Get a list item title, translating if required.
37 * @param string $listid List identifier.
38 * @param string $value List item identifier.
39 * @return string The item's title.
41 function getListItem($listid, $value) {
42 $lrow = sqlQuery("SELECT title FROM list_options " .
43 "WHERE list_id = ? AND option_id = ? AND activity = 1",
44 array($listid, $value));
45 $tmp = xl_list_label($lrow['title']);
46 if (empty($tmp)) $tmp = (($value === '') ? '' : "($value)");
47 return $tmp;
50 /**
51 * Adapt text to be suitable as the contents of a table cell.
53 * @param string $s Input text.
54 * @return string Output text.
56 function myCellText($s) {
57 if ($s === '') return '&nbsp;';
58 return text($s);
61 // Check authorization.
62 $thisauth = acl_check('patients', 'med');
63 if (!$thisauth) die(xlt('Not authorized'));
65 $errmsg = '';
67 // Send selected unsent orders if requested. This does not support downloading
68 // very well as it will only send the first of those.
69 if ($_POST['form_xmit']) {
70 foreach ($_POST['form_cb'] as $formid) {
71 $row = sqlQuery("SELECT lab_id FROM procedure_order WHERE " .
72 "procedure_order_id = ?", array($formid));
73 $ppid = intval($row['lab_id']);
74 $hl7 = '';
75 $errmsg = gen_hl7_order($formid, $hl7);
76 if (empty($errmsg)) {
77 $errmsg = send_hl7_order($ppid, $hl7);
79 if ($errmsg) break;
80 sqlStatement("UPDATE procedure_order SET date_transmitted = NOW() WHERE " .
81 "procedure_order_id = ?", array($formid));
85 <html>
86 <head>
87 <?php html_header_show();?>
89 <link rel="stylesheet" href='<?php echo $css_header ?>' type='text/css'>
90 <title><?php echo xlt('Procedure Orders and Reports'); ?></title>
92 <style>
94 tr.head { font-size:10pt; background-color:#cccccc; text-align:center; }
95 tr.detail { font-size:10pt; }
96 a, a:visited, a:hover { color:#0000cc; }
98 </style>
100 <style type="text/css">@import url(<?php echo $GLOBALS['webroot'] ?>/library/dynarch_calendar.css);</style>
101 <script type="text/javascript" src="<?php echo $GLOBALS['webroot'] ?>/library/dynarch_calendar.js"></script>
102 <?php include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?>
103 <script type="text/javascript" src="<?php echo $GLOBALS['webroot'] ?>/library/dynarch_calendar_setup.js"></script>
105 <script type="text/javascript" src="../../library/dialog.js?v=<?php echo $v_js_includes; ?>"></script>
106 <script type="text/javascript" src="../../library/textformat.js"></script>
108 <script language="JavaScript">
110 var mypcc = '<?php echo $GLOBALS['phone_country_code'] ?>';
112 function openResults(orderid) {
113 top.restoreSession();
114 // Open results in a new window. The options parameter serves to defeat Firefox's
115 // "open windows in a new tab", which is what we want because the doc may want to
116 // see the results concurrently with other stuff like related patient notes.
117 // Opening in the other frame is not good enough because if they then do related
118 // patients notes it will wipe out this script's list. We need 3 viewports.
119 window.open('single_order_results.php?orderid=' + orderid, '_blank', 'toolbar=0,location=0,menubar=0,scrollbars=yes');
121 // To open results in the same frame:
122 // document.location.href = 'single_order_results.php?orderid=' + orderid;
124 // To open results in the "other" frame:
125 // var w = window;
126 // var othername = (w.name == 'RTop') ? 'RBot' : 'RTop';
127 // w.parent.left_nav.forceDual();
128 // w.parent.left_nav.loadFrame('ore1', othername, 'orders/single_order_results.php?orderid=' + orderid);
131 // Invokes the patient matching dialog.
132 // args is a PHP-serialized array of patient attributes.
133 // The dialog script will directly insert the selected pid value, or 0,
134 // into the value of the form field named "[select][$key]".
136 function openPtMatch(args) {
137 top.restoreSession();
138 window.open('patient_match_dialog.php?key=' + encodeURIComponent(args), '_blank', 'toolbar=0,location=0,menubar=0,scrollbars=yes');
141 function openPatient(pid) {
142 top.restoreSession();
143 document.location.href = "../patient_file/summary/demographics.php?set_pid=" + pid;
146 </script>
148 </head>
150 <body class="body_top">
151 <form method='post' action='list_reports.php' enctype='multipart/form-data'>
153 <!-- This might be set by the results window: -->
154 <input type='hidden' name='form_external_refresh' value='' />
156 <?php
157 if ($errmsg) {
158 echo "<font color='red'>" . text($errmsg) . "</font><br />\n";
161 $info = array('select' => array());
163 // We skip match/delete processing if this is just a refresh, because that
164 // might be a nasty surprise.
165 if (empty($_POST['form_external_refresh'])) {
166 // Get patient matching selections from this form if there are any.
167 if (is_array($_POST['select'])) {
168 foreach ($_POST['select'] as $selkey => $selval) {
169 $info['select'][$selkey] = $selval;
172 // Get file delete requests from this form if there are any.
173 if (is_array($_POST['delete'])) {
174 foreach ($_POST['delete'] as $delkey => $dummy) {
175 $info[$delkey] = array('delete' => true);
180 // Attempt to post any incoming results.
181 $errmsg = poll_hl7_results($info);
183 // echo "<!--\n"; // debugging
184 // print_r($info); // debugging
185 // echo "-->\n"; // debugging
187 // Display a row for each required patient matching decision or message.
188 $s = '';
189 $matchreqs = false;
190 $errors = false;
192 // Generate HTML to request patient matching.
193 if (is_array($info['match'])) {
194 foreach ($info['match'] as $matchkey => $matchval) {
195 $matchreqs = true;
196 $s .= " <tr class='detail' bgcolor='#ccccff'>\n";
197 $s .= " <td>&nbsp;</td>\n";
198 $s .= " <td>&nbsp;</td>\n";
199 $s .= " <td><a href='javascript:openPtMatch(\"" . addslashes($matchkey) . "\")'>";
200 $tmp = unserialize($matchkey);
201 $s .= xlt('Click to match patient') . ' "' . text($tmp['lname']) .
202 ', ' . text($tmp['fname']) . '"';
203 $s .= "</a>";
204 $s .= "</td>\n";
205 $s .= " <td style='width:1%'><input type='text' name='select[" .
206 attr($matchkey) . "]' size='3' value='' " .
207 "style='background-color:transparent' readonly /></td>\n";
208 $s .= " </tr>\n";
212 foreach ($info as $infokey => $infoval) {
213 if ($infokey == 'match' || $infokey == 'select') continue;
214 $count = 0;
215 if (is_array($infoval['mssgs'])) {
216 foreach ($infoval['mssgs'] as $message) {
217 $s .= " <tr class='detail' bgcolor='#ccccff'>\n";
218 if (substr($message, 0, 1) == '*') {
219 $errors = true;
220 // Error message starts with '*'
221 if (!$count++) {
222 $s .= " <td><input type='checkbox' name='delete[" . attr($infokey) . "]' value='1' /></td>\n";
223 $s .= " <td>" . text($infokey) . "</td>\n";
225 else {
226 $s .= " <td>&nbsp;</td>\n";
227 $s .= " <td>&nbsp;</td>\n";
229 $s .= " <td colspan='2' style='color:red'>". text(substr($message, 1)) . "</td>\n";
231 else {
232 // Informational message starts with '>'
233 $s .= " <td>&nbsp;</td>\n";
234 $s .= " <td>" . text($infokey) . "</td>\n";
235 $s .= " <td colspan='2' style='color:green'>". text(substr($message, 1)) . "</td>\n";
237 $s .= " </tr>\n";
241 if ($s) {
242 if ($matchreqs || $errors) {
243 echo "<p class='bold' style='color:#008800'>";
244 echo xlt('Incoming results requiring attention:');
245 echo "</p>\n";
247 echo "<table width='100%'>\n";
248 echo " <tr class='head'>\n";
249 echo " <td>" . xlt('Delete' ) . "</th>\n";
250 echo " <td>" . xlt('Lab/File') . "</th>\n";
251 echo " <td>" . xlt('Message' ) . "</th>\n";
252 echo " <td>" . xlt('Match' ) . "</th>\n";
253 echo " </tr>\n";
254 echo $s;
255 echo "</table>\n";
256 if ($matchreqs || $errors) {
257 echo "<p class='bold' style='color:#008800'>";
258 if ($matchreqs) {
259 echo xlt('Click where indicated above to match the patient.') . ' ';
260 echo xlt('After that the Match column will show the selected patient ID, or 0 to create.') . ' ';
261 echo xlt('If you do not select a match the patient will be created.') . ' ';
263 echo xlt('Checkboxes above indicate if you want to reject and delete the HL7 file.') . ' ';
264 echo xlt('When done, click Submit (below) to apply your choices.');
265 echo "</p>\n";
269 // If there was a fatal error display that.
270 if ($errmsg) {
271 echo "<font color='red'>" . text($errmsg) . "</font><br />\n";
274 // Upload support removed because it is awkward to handle and of dubious
275 // value. Note we can now get results from the local filesystem.
277 /*********************************************************************
278 // Process uploaded file if there is one.
279 if (!empty($_FILES['userfile']['name'])) { // if upload was attempted
280 if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
281 $hl7 = file_get_contents($_FILES['userfile']['tmp_name']);
282 $msg = receive_hl7_results($hl7);
283 $message = xl('Upload processed successfully');
284 if ($msg) {
285 $message = xl('Error processing upload') . ": " . $msg;
287 echo text($message) . "<br />\n";
289 else {
290 echo "<font color='red'>" . xlt('Upload failed!') . "</font><br />\n";
293 *********************************************************************/
295 $form_from_date = empty($_POST['form_from_date']) ? '' : trim($_POST['form_from_date']);
296 $form_to_date = empty($_POST['form_to_date']) ? '' : trim($_POST['form_to_date']);
297 // if (empty($form_to_date)) $form_to_date = $form_from_date;
299 $form_reviewed = empty($_POST['form_reviewed']) ? 3 : intval($_POST['form_reviewed']);
301 $form_patient = !empty($_POST['form_patient']);
303 $form_provider = empty($_POST['form_provider']) ? '' : intval($_POST['form_provider']);
306 <table width='100%'>
307 <tr>
308 <td class='text' align='center'>
309 &nbsp;<?php echo xlt('From'); ?>:
310 <input type='text' size='6' name='form_from_date' id='form_from_date'
311 value='<?php echo attr($form_from_date); ?>'
312 title='<?php echo xla('yyyy-mm-dd'); ?>'
313 onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' />
314 <img src='../pic/show_calendar.gif' align='absbottom' width='24' height='22'
315 id='img_from_date' border='0' alt='[?]' style='cursor:pointer'
316 title='<?php echo xla('Click here to choose a date'); ?>' />
318 &nbsp;<?php echo xlt('To'); ?>:
319 <input type='text' size='6' name='form_to_date' id='form_to_date'
320 value='<?php echo attr($form_to_date); ?>'
321 title='<?php echo xla('yyyy-mm-dd'); ?>'
322 onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' />
323 <img src='../pic/show_calendar.gif' align='absbottom' width='24' height='22'
324 id='img_to_date' border='0' alt='[?]' style='cursor:pointer'
325 title='<?php echo xla('Click here to choose a date'); ?>' />
327 &nbsp;
328 <input type='checkbox' name='form_patient' value='1'
329 <?php if ($form_patient) echo 'checked '; ?>/><?php echo xlt('Current Pt Only'); ?>
331 &nbsp;
332 <select name='form_reviewed'>
333 <?php
334 foreach (array(
335 '1' => xl('All'),
336 '2' => xl('Reviewed'),
337 '3' => xl('Received, unreviewed'),
338 '4' => xl('Sent, not received'),
339 '5' => xl('Not sent'),
340 ) as $key => $value) {
341 echo "<option value='$key'";
342 if ($key == $form_reviewed) echo " selected";
343 echo ">" . text($value) . "</option>\n";
346 </select>
348 &nbsp;
349 <?php
350 generate_form_field(array('data_type' => 10, 'field_id' => 'provider',
351 'empty_title' => '-- All Providers --'), $form_provider);
354 &nbsp;
355 <input type='submit' name='form_refresh' value=<?php echo xla('Submit'); ?>>
356 </td>
357 </tr>
358 </table>
360 <table width='100%' cellpadding='1' cellspacing='2'>
362 <tr class='head'>
363 <td colspan='2'><?php echo xlt('Patient' ); ?></td>
364 <td colspan='2'><?php echo xlt('Order' ); ?></td>
365 <td colspan='2'><?php echo xlt('Procedure'); ?></td>
366 <td colspan='2'><?php echo xlt('Report' ); ?></td>
367 </tr>
369 <tr class='head'>
370 <td><?php echo xlt('Name' ); ?></td>
371 <td><?php echo xlt('ID' ); ?></td>
372 <td><?php echo xlt('Date' ); ?></td>
373 <td><?php echo xlt('ID' ); ?></td>
374 <td><?php echo xlt('Code' ); ?></td>
375 <td><?php echo xlt('Description'); ?></td>
376 <td><?php echo xlt('Date' ); ?></td>
377 <td><?php echo xlt('Status' ); ?></td>
378 <!-- <td><?php echo xlt('Reviewed' ); ?></td> -->
379 </tr>
381 <?php
382 $selects =
383 "po.patient_id, po.procedure_order_id, po.date_ordered, po.date_transmitted, " .
384 "pc.procedure_order_seq, pc.procedure_code, pc.procedure_name, pc.do_not_send, " .
385 "pr.procedure_report_id, pr.date_report, pr.date_report_tz, pr.report_status, pr.review_status";
387 $joins =
388 "LEFT JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id AND " .
389 "pr.procedure_order_seq = pc.procedure_order_seq";
391 $orderby =
392 "po.date_ordered, po.procedure_order_id, " .
393 "pc.do_not_send, pc.procedure_order_seq, pr.procedure_report_id";
395 $where = "1 = 1";
396 $sqlBindArray = array();
398 if (!empty($form_from_date)) {
399 $where .= " AND po.date_ordered >= ?";
400 $sqlBindArray[] = $form_from_date;
402 if (!empty($form_to_date)) {
403 $where .= " AND po.date_ordered <= ?";
404 $sqlBindArray[] = $form_to_date;
407 if ($form_patient) {
408 $where .= " AND po.patient_id = ?";
409 $sqlBindArray[] = $pid;
412 if ($form_provider) {
413 $where .= " AND po.provider_id = ?";
414 $sqlBindArray[] = $form_provider;
417 if ($form_reviewed == 2) {
418 $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status = 'reviewed'";
420 else if ($form_reviewed == 3) {
421 $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status != 'reviewed'";
423 else if ($form_reviewed == 4) {
424 $where .= " AND po.date_transmitted IS NOT NULL AND pr.procedure_report_id IS NULL";
426 else if ($form_reviewed == 5) {
427 $where .= " AND po.date_transmitted IS NULL AND pr.procedure_report_id IS NULL";
430 $query = "SELECT " .
431 "pd.fname, pd.mname, pd.lname, pd.pubpid, $selects " .
432 "FROM procedure_order AS po " .
433 "LEFT JOIN procedure_order_code AS pc ON pc.procedure_order_id = po.procedure_order_id " .
434 "LEFT JOIN patient_data AS pd ON pd.pid = po.patient_id $joins " .
435 "WHERE $where " .
436 "ORDER BY pd.lname, pd.fname, pd.mname, po.patient_id, $orderby";
438 $res = sqlStatement($query, $sqlBindArray);
440 $lastptid = -1;
441 $lastpoid = -1;
442 $lastpcid = -1;
443 $encount = 0;
444 $lino = 0;
445 $extra_html = '';
446 $num_checkboxes = 0;
448 while ($row = sqlFetchArray($res)) {
449 $patient_id = empty($row['patient_id' ]) ? 0 : ($row['patient_id' ] + 0);
450 $order_id = empty($row['procedure_order_id' ]) ? 0 : ($row['procedure_order_id' ] + 0);
451 $order_seq = empty($row['procedure_order_seq']) ? 0 : ($row['procedure_order_seq'] + 0);
452 $date_ordered = empty($row['date_ordered' ]) ? '' : $row['date_ordered'];
453 $date_transmitted = empty($row['date_transmitted' ]) ? '' : $row['date_transmitted'];
454 $procedure_code = empty($row['procedure_code' ]) ? '' : $row['procedure_code'];
455 $procedure_name = empty($row['procedure_name' ]) ? '' : $row['procedure_name'];
456 $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0);
457 $date_report = empty($row['date_report' ]) ? '' : substr($row['date_report'], 0, 16);
458 $date_report_suf = empty($row['date_report_tz' ]) ? '' : (' ' . $row['date_report_tz' ]);
459 $report_status = empty($row['report_status' ]) ? '' : $row['report_status'];
460 $review_status = empty($row['review_status' ]) ? '' : $row['review_status'];
462 // Sendable procedures sort first, so this also applies to the order on an order ID change.
463 $sendable = isset($row['procedure_order_seq']) && $row['do_not_send'] == 0;
465 $ptname = $row['lname'];
466 if ($row['fname'] || $row['mname'])
467 $ptname .= ', ' . $row['fname'] . ' ' . $row['mname'];
469 if ($lastpoid != $order_id || $lastpcid != $order_seq) {
470 ++$encount;
472 $bgcolor = "#" . (($encount & 1) ? "ddddff" : "ffdddd");
474 echo " <tr class='detail' bgcolor='$bgcolor'>\n";
476 // Generate patient columns.
477 if ($lastptid != $patient_id) {
478 $lastpoid = -1;
479 echo " <td onclick='openPatient($patient_id)' style='cursor:pointer;color:blue'>";
480 echo text($ptname);
481 echo "</td>\n";
482 echo " <td>" . text($row['pubpid']) . "</td>\n";
484 else {
485 echo " <td colspan='2' style='background-color:transparent'>&nbsp;</td>";
488 // Generate order columns.
489 if ($lastpoid != $order_id) {
490 $lastpcid = -1;
491 echo " <td>";
492 // Checkbox to support sending unsent orders, disabled if sent.
493 echo "<input type='checkbox' name='form_cb[$order_id]' value='$order_id' ";
494 if ($date_transmitted || !$sendable) {
495 echo "disabled";
496 } else {
497 echo "checked";
498 ++$num_checkboxes;
500 echo " />";
501 // Order date comes with a link to open results in the same frame.
502 echo "<a href='javascript:openResults($order_id)' ";
503 echo "title='" . xla('Click for results') . "'>";
504 echo text($date_ordered);
505 echo "</a></td>\n";
506 echo " <td>";
507 // Order ID comes with a link to open the manifest in a new window/tab.
508 echo "<a href='" . $GLOBALS['webroot'];
509 echo "/interface/orders/order_manifest.php?orderid=";
510 echo attr($order_id);
511 echo "' target='_blank' onclick='top.restoreSession()' ";
512 echo "title='" . xla('Click for order summary') . "'>";
513 echo text($order_id);
514 echo "</a></td>\n";
516 else {
517 echo " <td colspan='2' style='background-color:transparent'>&nbsp;</td>";
520 // Generate procedure columns.
521 if ($order_seq && $lastpcid != $order_seq) {
522 if ($sendable) {
523 echo " <td>" . text($procedure_code) . "</td>\n";
524 echo " <td>" . text($procedure_name) . "</td>\n";
526 else {
527 echo " <td><strike>" . text($procedure_code) . "</strike></td>\n";
528 echo " <td><strike>" . text($procedure_name) . "</strike></td>\n";
531 else {
532 echo " <td colspan='2' style='background-color:transparent'>&nbsp;</td>";
535 // Generate report columns.
536 if ($report_id) {
537 echo " <td>" . text($date_report . $date_report_suf) . "</td>\n";
538 echo " <td title='" . xla('Check mark indicates reviewed') . "'>";
539 echo myCellText(getListItem('proc_rep_status', $report_status));
540 if ($review_status == 'reviewed') {
541 echo " &#x2713;"; // unicode check mark character
543 echo "</td>\n";
545 else {
546 echo " <td colspan='2' style='background-color:transparent'>&nbsp;</td>";
549 echo " </tr>\n";
551 $lastptid = $patient_id;
552 $lastpoid = $order_id;
553 $lastpcid = $order_seq;
554 ++$lino;
558 </table>
560 <?php if ($num_checkboxes) { ?>
561 <center><p>
562 <input type='submit' name='form_xmit' value='<?php echo xla('Transmit Selected Orders'); ?>' />
563 </p></center>
564 <?php } ?>
566 <script language='JavaScript'>
568 // Initialize calendar widgets for "from" and "to" dates.
569 Calendar.setup({inputField:'form_from_date', ifFormat:'%Y-%m-%d',
570 button:'img_from_date'});
571 Calendar.setup({inputField:'form_to_date', ifFormat:'%Y-%m-%d',
572 button:'img_to_date'});
574 </script>
576 </form>
577 </body>
578 </html>