3 * List procedure orders and reports, and fetch new reports and their results.
5 * Copyright (C) 2013-2015 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>.
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/formdata.inc.php");
30 require_once("$srcdir/options.inc.php");
31 require_once("$srcdir/formatting.inc.php");
32 require_once("$srcdir/classes/Document.class.php");
33 require_once("./receive_hl7_results.inc.php");
34 require_once("./gen_hl7_order.inc.php");
37 * Get a list item title, translating if required.
39 * @param string $listid List identifier.
40 * @param string $value List item identifier.
41 * @return string The item's title.
43 function getListItem($listid, $value) {
44 $lrow = sqlQuery("SELECT title FROM list_options " .
45 "WHERE list_id = ? AND option_id = ?",
46 array($listid, $value));
47 $tmp = xl_list_label($lrow['title']);
48 if (empty($tmp)) $tmp = (($value === '') ?
'' : "($value)");
53 * Adapt text to be suitable as the contents of a table cell.
55 * @param string $s Input text.
56 * @return string Output text.
58 function myCellText($s) {
59 if ($s === '') return ' ';
63 // Check authorization.
64 $thisauth = acl_check('patients', 'med');
65 if (!$thisauth) die(xlt('Not authorized'));
69 // Send selected unsent orders if requested. This does not support downloading
70 // very well as it will only send the first of those.
71 if ($_POST['form_xmit']) {
72 foreach ($_POST['form_cb'] as $formid) {
73 $row = sqlQuery("SELECT lab_id FROM procedure_order WHERE " .
74 "procedure_order_id = ?", array($formid));
75 $ppid = intval($row['lab_id']);
77 $errmsg = gen_hl7_order($formid, $hl7);
79 $errmsg = send_hl7_order($ppid, $hl7);
82 sqlStatement("UPDATE procedure_order SET date_transmitted = NOW() WHERE " .
83 "procedure_order_id = ?", array($formid));
89 <?php
html_header_show();?
>
91 <link rel
="stylesheet" href
='<?php echo $css_header ?>' type
='text/css'>
92 <title
><?php
echo xlt('Procedure Orders and Reports'); ?
></title
>
96 tr
.head
{ font
-size
:10pt
; background
-color
:#cccccc; text-align:center; }
97 tr
.detail
{ font
-size
:10pt
; }
98 a
, a
:visited
, a
:hover
{ color
:#0000cc; }
102 <style type
="text/css">@import
url(<?php
echo $GLOBALS['webroot'] ?
>/library
/dynarch_calendar
.css
);</style
>
103 <script type
="text/javascript" src
="<?php echo $GLOBALS['webroot'] ?>/library/dynarch_calendar.js"></script
>
104 <?php
include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?
>
105 <script type
="text/javascript" src
="<?php echo $GLOBALS['webroot'] ?>/library/dynarch_calendar_setup.js"></script
>
107 <script type
="text/javascript" src
="../../library/dialog.js"></script
>
108 <script type
="text/javascript" src
="../../library/textformat.js"></script
>
110 <script language
="JavaScript">
112 var mypcc
= '<?php echo $GLOBALS['phone_country_code
'] ?>';
114 function openResults(orderid
) {
115 top
.restoreSession();
116 // Open results in a new window. The options parameter serves to defeat Firefox's
117 // "open windows in a new tab", which is what we want because the doc may want to
118 // see the results concurrently with other stuff like related patient notes.
119 // Opening in the other frame is not good enough because if they then do related
120 // patients notes it will wipe out this script's list. We need 3 viewports.
121 window
.open('single_order_results.php?orderid=' + orderid
, '_blank', 'toolbar=0,location=0,menubar=0,scrollbars=yes');
123 // To open results in the same frame:
124 // document.location.href = 'single_order_results.php?orderid=' + orderid;
126 // To open results in the "other" frame:
128 // var othername = (w.name == 'RTop') ? 'RBot' : 'RTop';
129 // w.parent.left_nav.forceDual();
130 // w.parent.left_nav.setRadio(othername, 'ore');
131 // w.parent.left_nav.loadFrame('ore1', othername, 'orders/single_order_results.php?orderid=' + orderid);
134 // Invokes the patient matching dialog.
135 // args is a PHP-serialized array of patient attributes.
136 // The dialog script will directly insert the selected pid value, or 0,
137 // into the value of the form field named "[select][$key]".
139 function openPtMatch(args
) {
140 top
.restoreSession();
141 window
.open('patient_match_dialog.php?key=' +
encodeURIComponent(args
), '_blank', 'toolbar=0,location=0,menubar=0,scrollbars=yes');
144 function openPatient(pid
) {
145 top
.restoreSession();
146 document
.location
.href
= "../patient_file/summary/demographics.php?set_pid=" + pid
;
153 <body
class="body_top">
154 <form method
='post' action
='list_reports.php' enctype
='multipart/form-data'>
156 <!-- This might be set by the results window
: -->
157 <input type
='hidden' name
='form_external_refresh' value
='' />
161 echo "<font color='red'>" . text($errmsg) . "</font><br />\n";
164 $info = array('select' => array());
166 // We skip match/delete processing if this is just a refresh, because that
167 // might be a nasty surprise.
168 if (empty($_POST['form_external_refresh'])) {
169 // Get patient matching selections from this form if there are any.
170 if (is_array($_POST['select'])) {
171 foreach ($_POST['select'] as $selkey => $selval) {
172 $info['select'][$selkey] = $selval;
175 // Get file delete requests from this form if there are any.
176 if (is_array($_POST['delete'])) {
177 foreach ($_POST['delete'] as $delkey => $dummy) {
178 $info[$delkey] = array('delete' => true);
183 // Attempt to post any incoming results.
184 $errmsg = poll_hl7_results($info);
186 // echo "<!--\n"; // debugging
187 // print_r($info); // debugging
188 // echo "-->\n"; // debugging
190 // Display a row for each required patient matching decision or message.
195 // Generate HTML to request patient matching.
196 if (is_array($info['match'])) {
197 foreach ($info['match'] as $matchkey => $matchval) {
199 $s .= " <tr class='detail' bgcolor='#ccccff'>\n";
200 $s .= " <td> </td>\n";
201 $s .= " <td> </td>\n";
202 $s .= " <td><a href='javascript:openPtMatch(\"" . addslashes($matchkey) . "\")'>";
203 $tmp = unserialize($matchkey);
204 $s .= xlt('Click to match patient') . ' "' . text($tmp['lname']) .
205 ', ' . text($tmp['fname']) . '"';
208 $s .= " <td style='width:1%'><input type='text' name='select[" .
209 attr($matchkey) . "]' size='3' value='' " .
210 "style='background-color:transparent' readonly /></td>\n";
215 foreach ($info as $infokey => $infoval) {
216 if ($infokey == 'match' ||
$infokey == 'select') continue;
218 if (is_array($infoval['mssgs'])) {
219 foreach ($infoval['mssgs'] as $message) {
220 $s .= " <tr class='detail' bgcolor='#ccccff'>\n";
221 if (substr($message, 0, 1) == '*') {
223 // Error message starts with '*'
225 $s .= " <td><input type='checkbox' name='delete[" . attr($infokey) . "]' value='1' /></td>\n";
226 $s .= " <td>" . text($infokey) . "</td>\n";
229 $s .= " <td> </td>\n";
230 $s .= " <td> </td>\n";
232 $s .= " <td colspan='2' style='color:red'>". text(substr($message, 1)) . "</td>\n";
235 // Informational message starts with '>'
236 $s .= " <td> </td>\n";
237 $s .= " <td>" . text($infokey) . "</td>\n";
238 $s .= " <td colspan='2' style='color:green'>". text(substr($message, 1)) . "</td>\n";
245 if ($matchreqs ||
$errors) {
246 echo "<p class='bold' style='color:#008800'>";
247 echo xlt('Incoming results requiring attention:');
250 echo "<table width='100%'>\n";
251 echo " <tr class='head'>\n";
252 echo " <td>" . xlt('Delete' ) . "</th>\n";
253 echo " <td>" . xlt('Lab/File') . "</th>\n";
254 echo " <td>" . xlt('Message' ) . "</th>\n";
255 echo " <td>" . xlt('Match' ) . "</th>\n";
259 if ($matchreqs ||
$errors) {
260 echo "<p class='bold' style='color:#008800'>";
262 echo xlt('Click where indicated above to match the patient.') . ' ';
263 echo xlt('After that the Match column will show the selected patient ID, or 0 to create.') . ' ';
264 echo xlt('If you do not select a match the patient will be created.') . ' ';
266 echo xlt('Checkboxes above indicate if you want to reject and delete the HL7 file.') . ' ';
267 echo xlt('When done, click Submit (below) to apply your choices.');
272 // If there was a fatal error display that.
274 echo "<font color='red'>" . text($errmsg) . "</font><br />\n";
277 // Upload support removed because it is awkward to handle and of dubious
278 // value. Note we can now get results from the local filesystem.
280 /*********************************************************************
281 // Process uploaded file if there is one.
282 if (!empty($_FILES['userfile']['name'])) { // if upload was attempted
283 if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
284 $hl7 = file_get_contents($_FILES['userfile']['tmp_name']);
285 $msg = receive_hl7_results($hl7);
286 $message = xl('Upload processed successfully');
288 $message = xl('Error processing upload') . ": " . $msg;
290 echo text($message) . "<br />\n";
293 echo "<font color='red'>" . xlt('Upload failed!') . "</font><br />\n";
296 *********************************************************************/
298 $form_from_date = empty($_POST['form_from_date']) ?
'' : trim($_POST['form_from_date']);
299 $form_to_date = empty($_POST['form_to_date']) ?
'' : trim($_POST['form_to_date']);
300 // if (empty($form_to_date)) $form_to_date = $form_from_date;
302 $form_reviewed = empty($_POST['form_reviewed']) ?
3 : intval($_POST['form_reviewed']);
304 $form_patient = !empty($_POST['form_patient']);
306 $form_provider = empty($_POST['form_provider']) ?
'' : intval($_POST['form_provider']);
311 <td
class='text' align
='center'>
312  
;<?php
echo xlt('From'); ?
>:
313 <input type
='text' size
='6' name
='form_from_date' id
='form_from_date'
314 value
='<?php echo attr($form_from_date); ?>'
315 title
='<?php echo xla('yyyy
-mm
-dd
'); ?>'
316 onkeyup
='datekeyup(this,mypcc)' onblur
='dateblur(this,mypcc)' />
317 <img src
='../pic/show_calendar.gif' align
='absbottom' width
='24' height
='22'
318 id
='img_from_date' border
='0' alt
='[?]' style
='cursor:pointer'
319 title
='<?php echo xla('Click here to choose a date
'); ?>' />
321  
;<?php
echo xlt('To'); ?
>:
322 <input type
='text' size
='6' name
='form_to_date' id
='form_to_date'
323 value
='<?php echo attr($form_to_date); ?>'
324 title
='<?php echo xla('yyyy
-mm
-dd
'); ?>'
325 onkeyup
='datekeyup(this,mypcc)' onblur
='dateblur(this,mypcc)' />
326 <img src
='../pic/show_calendar.gif' align
='absbottom' width
='24' height
='22'
327 id
='img_to_date' border
='0' alt
='[?]' style
='cursor:pointer'
328 title
='<?php echo xla('Click here to choose a date
'); ?>' />
331 <input type
='checkbox' name
='form_patient' value
='1'
332 <?php
if ($form_patient) echo 'checked '; ?
>/><?php
echo xlt('Current Pt Only'); ?
>
335 <select name
='form_reviewed'>
339 '2' => xl('Reviewed'),
340 '3' => xl('Received, unreviewed'),
341 '4' => xl('Sent, not received'),
342 '5' => xl('Not sent'),
343 ) as $key => $value) {
344 echo "<option value='$key'";
345 if ($key == $form_reviewed) echo " selected";
346 echo ">" . text($value) . "</option>\n";
353 generate_form_field(array('data_type' => 10, 'field_id' => 'provider',
354 'empty_title' => '-- All Providers --'), $form_provider);
358 <input type
='submit' name
='form_refresh' value
=<?php
echo xla('Submit'); ?
>>
363 <table width
='100%' cellpadding
='1' cellspacing
='2'>
366 <td colspan
='2'><?php
echo xlt('Patient' ); ?
></td
>
367 <td colspan
='2'><?php
echo xlt('Order' ); ?
></td
>
368 <td colspan
='2'><?php
echo xlt('Procedure'); ?
></td
>
369 <td colspan
='2'><?php
echo xlt('Report' ); ?
></td
>
373 <td
><?php
echo xlt('Name' ); ?
></td
>
374 <td
><?php
echo xlt('ID' ); ?
></td
>
375 <td
><?php
echo xlt('Date' ); ?
></td
>
376 <td
><?php
echo xlt('ID' ); ?
></td
>
377 <td
><?php
echo xlt('Code' ); ?
></td
>
378 <td
><?php
echo xlt('Description'); ?
></td
>
379 <td
><?php
echo xlt('Date' ); ?
></td
>
380 <td
><?php
echo xlt('Status' ); ?
></td
>
381 <!-- <td
><?php
echo xlt('Reviewed' ); ?
></td
> -->
386 "po.patient_id, po.procedure_order_id, po.date_ordered, po.date_transmitted, " .
387 "pc.procedure_order_seq, pc.procedure_code, pc.procedure_name, pc.do_not_send, " .
388 "pr.procedure_report_id, pr.date_report, pr.date_report_tz, pr.report_status, pr.review_status";
391 "LEFT JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id AND " .
392 "pr.procedure_order_seq = pc.procedure_order_seq";
395 "po.date_ordered, po.procedure_order_id, " .
396 "pc.do_not_send, pc.procedure_order_seq, pr.procedure_report_id";
399 $sqlBindArray = array();
401 if (!empty($form_from_date)) {
402 $where .= " AND po.date_ordered >= ?";
403 $sqlBindArray[] = $form_from_date;
405 if (!empty($form_to_date)) {
406 $where .= " AND po.date_ordered <= ?";
407 $sqlBindArray[] = $form_to_date;
411 $where .= " AND po.patient_id = ?";
412 $sqlBindArray[] = $pid;
415 if ($form_provider) {
416 $where .= " AND po.provider_id = ?";
417 $sqlBindArray[] = $form_provider;
420 if ($form_reviewed == 2) {
421 $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status = 'reviewed'";
423 else if ($form_reviewed == 3) {
424 $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status != 'reviewed'";
426 else if ($form_reviewed == 4) {
427 $where .= " AND po.date_transmitted IS NOT NULL AND pr.procedure_report_id IS NULL";
429 else if ($form_reviewed == 5) {
430 $where .= " AND po.date_transmitted IS NULL AND pr.procedure_report_id IS NULL";
434 "pd.fname, pd.mname, pd.lname, pd.pubpid, $selects " .
435 "FROM procedure_order AS po " .
436 "LEFT JOIN procedure_order_code AS pc ON pc.procedure_order_id = po.procedure_order_id " .
437 "LEFT JOIN patient_data AS pd ON pd.pid = po.patient_id $joins " .
439 "ORDER BY pd.lname, pd.fname, pd.mname, po.patient_id, $orderby";
441 $res = sqlStatement($query, $sqlBindArray);
451 while ($row = sqlFetchArray($res)) {
452 $patient_id = empty($row['patient_id' ]) ?
0 : ($row['patient_id' ] +
0);
453 $order_id = empty($row['procedure_order_id' ]) ?
0 : ($row['procedure_order_id' ] +
0);
454 $order_seq = empty($row['procedure_order_seq']) ?
0 : ($row['procedure_order_seq'] +
0);
455 $date_ordered = empty($row['date_ordered' ]) ?
'' : $row['date_ordered'];
456 $date_transmitted = empty($row['date_transmitted' ]) ?
'' : $row['date_transmitted'];
457 $procedure_code = empty($row['procedure_code' ]) ?
'' : $row['procedure_code'];
458 $procedure_name = empty($row['procedure_name' ]) ?
'' : $row['procedure_name'];
459 $report_id = empty($row['procedure_report_id']) ?
0 : ($row['procedure_report_id'] +
0);
460 $date_report = empty($row['date_report' ]) ?
'' : substr($row['date_report'], 0, 16);
461 $date_report_suf = empty($row['date_report_tz' ]) ?
'' : (' ' . $row['date_report_tz' ]);
462 $report_status = empty($row['report_status' ]) ?
'' : $row['report_status'];
463 $review_status = empty($row['review_status' ]) ?
'' : $row['review_status'];
465 // Sendable procedures sort first, so this also applies to the order on an order ID change.
466 $sendable = isset($row['procedure_order_seq']) && $row['do_not_send'] == 0;
468 $ptname = $row['lname'];
469 if ($row['fname'] ||
$row['mname'])
470 $ptname .= ', ' . $row['fname'] . ' ' . $row['mname'];
472 if ($lastpoid != $order_id ||
$lastpcid != $order_seq) {
475 $bgcolor = "#" . (($encount & 1) ?
"ddddff" : "ffdddd");
477 echo " <tr class='detail' bgcolor='$bgcolor'>\n";
479 // Generate patient columns.
480 if ($lastptid != $patient_id) {
482 echo " <td onclick='openPatient($patient_id)' style='cursor:pointer;color:blue'>";
485 echo " <td>" . text($row['pubpid']) . "</td>\n";
488 echo " <td colspan='2' style='background-color:transparent'> </td>";
491 // Generate order columns.
492 if ($lastpoid != $order_id) {
495 // Checkbox to support sending unsent orders, disabled if sent.
496 echo "<input type='checkbox' name='form_cb[$order_id]' value='$order_id' ";
497 if ($date_transmitted ||
!$sendable) {
504 // Order date comes with a link to open results in the same frame.
505 echo "<a href='javascript:openResults($order_id)' ";
506 echo "title='" . xla('Click for results') . "'>";
507 echo text($date_ordered);
510 // Order ID comes with a link to open the manifest in a new window/tab.
511 echo "<a href='" . $GLOBALS['webroot'];
512 echo "/interface/orders/order_manifest.php?orderid=";
513 echo attr($order_id);
514 echo "' target='_blank' onclick='top.restoreSession()' ";
515 echo "title='" . xla('Click for order summary') . "'>";
516 echo text($order_id);
520 echo " <td colspan='2' style='background-color:transparent'> </td>";
523 // Generate procedure columns.
524 if ($order_seq && $lastpcid != $order_seq) {
526 echo " <td>" . text($procedure_code) . "</td>\n";
527 echo " <td>" . text($procedure_name) . "</td>\n";
530 echo " <td><strike>" . text($procedure_code) . "</strike></td>\n";
531 echo " <td><strike>" . text($procedure_name) . "</strike></td>\n";
535 echo " <td colspan='2' style='background-color:transparent'> </td>";
538 // Generate report columns.
540 echo " <td>" . text($date_report . $date_report_suf) . "</td>\n";
541 echo " <td title='" . xla('Check mark indicates reviewed') . "'>";
542 echo myCellText(getListItem('proc_rep_status', $report_status));
543 if ($review_status == 'reviewed') {
544 echo " ✓"; // unicode check mark character
549 echo " <td colspan='2' style='background-color:transparent'> </td>";
554 $lastptid = $patient_id;
555 $lastpoid = $order_id;
556 $lastpcid = $order_seq;
563 <?php
if ($num_checkboxes) { ?
>
565 <input type
='submit' name
='form_xmit' value
='<?php echo xla('Transmit Selected Orders
'); ?>' />
569 <script language
='JavaScript'>
571 // Initialize calendar widgets for "from" and "to" dates.
572 Calendar
.setup({inputField
:'form_from_date', ifFormat
:'%Y-%m-%d',
573 button
:'img_from_date'});
574 Calendar
.setup({inputField
:'form_to_date', ifFormat
:'%Y-%m-%d',
575 button
:'img_to_date'});