Support for optional logging of print actions.
[openemr.git] / interface / orders / single_order_results.inc.php
blob115f0bf8129e48967d28c7b08554ba12070ada49
1 <?php
2 /**
3 * Script to display results for a given procedure order.
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>.
18 * @package OpenEMR
19 * @author Rod Roark <rod@sunsetsystems.com>
22 require_once($GLOBALS["srcdir"] . "/acl.inc");
23 require_once($GLOBALS["srcdir"] . "/formdata.inc.php");
24 require_once($GLOBALS["srcdir"] . "/options.inc.php");
25 require_once($GLOBALS["srcdir"] . "/formatting.inc.php");
26 require_once($GLOBALS["srcdir"] . "/classes/Document.class.php");
28 function getListItem($listid, $value) {
29 $lrow = sqlQuery("SELECT title FROM list_options " .
30 "WHERE list_id = ? AND option_id = ?",
31 array($listid, $value));
32 $tmp = xl_list_label($lrow['title']);
33 if (empty($tmp)) $tmp = (($value === '') ? '' : "($value)");
34 return $tmp;
37 function myCellText($s) {
38 $s = trim($s);
39 if ($s === '') return '&nbsp;';
40 return text($s);
43 // Check if the given string already exists in the $aNotes array.
44 // If not, stores it as a new entry.
45 // Either way, returns the corresponding key which is a small integer.
46 function storeNote($s) {
47 global $aNotes;
48 $key = array_search($s, $aNotes);
49 if ($key !== FALSE) return $key;
50 $key = count($aNotes);
51 $aNotes[$key] = $s;
52 return $key;
55 // Display a single row of output including order, report and result information.
57 function generate_result_row(&$ctx, &$row, &$rrow, $priors_omitted=false) {
58 $lab_id = empty($row['lab_id' ]) ? 0 : ($row['lab_id' ] + 0);
59 $order_type_id = empty($row['order_type_id' ]) ? 0 : ($row['order_type_id' ] + 0);
60 $order_seq = empty($row['procedure_order_seq']) ? 0 : ($row['procedure_order_seq'] + 0);
61 $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0);
62 $procedure_code = empty($row['procedure_code' ]) ? '' : $row['procedure_code'];
63 $procedure_name = empty($row['procedure_name' ]) ? '' : $row['procedure_name'];
64 $date_report = empty($row['date_report' ]) ? '' : $row['date_report'];
65 $date_collected = empty($row['date_collected' ]) ? '' : substr($row['date_collected'], 0, 16);
66 $specimen_num = empty($row['specimen_num' ]) ? '' : $row['specimen_num'];
67 $report_status = empty($row['report_status' ]) ? '' : $row['report_status'];
68 $review_status = empty($row['review_status' ]) ? 'received' : $row['review_status'];
70 $report_noteid ='';
71 if ($report_id && !isset($ctx['seen_report_ids'][$report_id])) {
72 $ctx['seen_report_ids'][$report_id] = true;
73 if ($review_status != 'reviewed') {
74 if ($ctx['sign_list']) $ctx['sign_list'] .= ',';
75 $ctx['sign_list'] .= $report_id;
77 if (!empty($row['report_notes'])) {
78 $report_noteid = 1 + storeNote($row['report_notes']);
82 $result_code = empty($rrow['result_code' ]) ? '' : $rrow['result_code'];
83 $result_text = empty($rrow['result_text' ]) ? '' : $rrow['result_text'];
84 $result_abnormal = empty($rrow['abnormal' ]) ? '' : $rrow['abnormal'];
85 $result_result = empty($rrow['result' ]) ? '' : $rrow['result'];
86 $result_units = empty($rrow['units' ]) ? '' : $rrow['units'];
87 $result_facility = empty($rrow['facility' ]) ? '' : $rrow['facility'];
88 $result_comments = empty($rrow['comments' ]) ? '' : $rrow['comments'];
89 $result_range = empty($rrow['range' ]) ? '' : $rrow['range'];
90 $result_status = empty($rrow['result_status' ]) ? '' : $rrow['result_status'];
91 $result_document_id = empty($rrow['document_id' ]) ? '' : $rrow['document_id'];
93 if ($i = strpos($result_comments, "\n")) { // "=" is not a mistake!
94 // If the first line of comments is not empty, then it is actually a long textual
95 // result value with lines delimited by "~" characters.
96 $result_comments = str_replace("~", "\n", substr($result_comments, 0, $i)) .
97 substr($result_comments, $i);
99 $result_comments = trim($result_comments);
101 $result_noteid = '';
102 if (!empty($result_comments)) {
103 $result_noteid = 1 + storeNote($result_comments);
105 if ($priors_omitted) {
106 if ($result_noteid) $result_noteid .= ', ';
107 $result_noteid .= 1 + storeNote(xl('This is the latest of multiple result values.'));
108 $ctx['priors_omitted'] = true;
111 if ($ctx['lastpcid'] != $order_seq) {
112 ++$ctx['encount'];
114 $bgcolor = "#" . (($ctx['encount'] & 1) ? "ddddff" : "ffdddd");
116 echo " <tr class='detail' bgcolor='$bgcolor'>\n";
118 if ($ctx['lastpcid'] != $order_seq) {
119 $ctx['lastprid'] = -1; // force report fields on first line of each procedure
120 $tmp = text("$procedure_code: $procedure_name");
121 // Get the LOINC code if one exists in the compendium for this order type.
122 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) {
123 $trow = sqlQuery("SELECT standard_code FROM procedure_type WHERE " .
124 "lab_id = ? AND procedure_code = ? AND procedure_type = 'ord' " .
125 "ORDER BY procedure_type_id LIMIT 1",
126 array($lab_id, $procedure_code));
127 if (!empty($trow['standard_code'])) {
128 $tmp = "<a href='javascript:educlick(\"LOINC\",\"" . attr($trow['standard_code']) .
129 "\")'>$tmp</a>";
132 echo " <td>$tmp</td>\n";
134 else {
135 echo " <td style='background-color:transparent'>&nbsp;</td>";
138 // If this starts a new report or a new order, generate the report fields.
139 if ($report_id != $ctx['lastprid']) {
140 echo " <td>";
141 echo myCellText(oeFormatShortDate($date_report));
142 echo "</td>\n";
144 echo " <td>";
145 echo myCellText($specimen_num);
146 echo "</td>\n";
148 echo " <td title='" . xla('Check mark indicates reviewed') . "'>";
149 echo myCellText(getListItem('proc_rep_status', $report_status));
150 if ($row['review_status'] == 'reviewed') {
151 echo " &#x2713;"; // unicode check mark character
153 echo "</td>\n";
155 echo " <td align='center'>";
156 echo myCellText($report_noteid);
157 echo "</td>\n";
159 else {
160 echo " <td colspan='4' style='background-color:transparent'>&nbsp;</td>\n";
163 if ($result_code !== '' || $result_document_id) {
164 $tmp = myCellText($result_code);
165 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE']) && !empty($result_code)) {
166 $tmp = "<a href='javascript:educlick(\"LOINC\",\"" . attr($result_code) .
167 "\")'>$tmp</a>";
169 echo " <td>$tmp</td>\n";
170 echo " <td>";
171 echo myCellText($result_text);
172 echo "</td>\n";
173 echo " <td>";
174 $tmp = myCellText(getListItem('proc_res_abnormal', $result_abnormal));
175 if ($result_abnormal && strtolower($result_abnormal) != 'no') {
176 echo "<b><font color='red'>$tmp</font></b>";
178 else {
179 echo $tmp;
181 echo "</td>\n";
183 if ($result_document_id) {
184 $d = new Document($result_document_id);
185 echo " <td colspan='3'>";
186 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) {
187 echo "<a href='" . $GLOBALS['webroot'] . "/controller.php?document";
188 echo "&retrieve&patient_id=$patient_id&document_id=$result_document_id' ";
189 echo "onclick='top.restoreSession()'>";
191 echo $d->get_url_file();
192 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) {
193 echo "</a>";
195 echo "</td>\n";
197 else {
198 echo " <td>";
199 echo myCellText($result_result);
200 echo "</td>\n";
201 echo " <td>";
202 echo myCellText($result_range);
203 echo "</td>\n";
204 echo " <td>";
205 echo myCellText($result_units);
206 echo "</td>\n";
208 echo " <td align='center'>";
209 echo myCellText($result_noteid);
210 echo "</td>\n";
212 else {
213 echo " <td colspan='7' style='background-color:transparent'>&nbsp;</td>\n";
216 echo " </tr>\n";
218 $ctx['lastpcid'] = $order_seq;
219 $ctx['lastprid'] = $report_id;
220 ++$ctx['lino'];
223 function generate_order_report($orderid, $input_form=false, $genstyles=true, $finals_only=false) {
224 global $aNotes;
226 // Check authorization.
227 $thisauth = acl_check('patients', 'med');
228 if (!$thisauth) return xl('Not authorized');
230 $orow = sqlQuery("SELECT " .
231 "po.procedure_order_id, po.date_ordered, po.control_id, " .
232 "po.order_status, po.specimen_type, po.patient_id, " .
233 "pd.pubpid, pd.lname, pd.fname, pd.mname, pd.cmsportal_login, pd.language, " .
234 "fe.date, " .
235 "pp.name AS labname, " .
236 "u.lname AS ulname, u.fname AS ufname, u.mname AS umname " .
237 "FROM procedure_order AS po " .
238 "LEFT JOIN patient_data AS pd ON pd.pid = po.patient_id " .
239 "LEFT JOIN procedure_providers AS pp ON pp.ppid = po.lab_id " .
240 "LEFT JOIN users AS u ON u.id = po.provider_id " .
241 "LEFT JOIN form_encounter AS fe ON fe.pid = po.patient_id AND fe.encounter = po.encounter_id " .
242 "WHERE po.procedure_order_id = ?",
243 array($orderid));
245 $patient_id = $orow['patient_id'];
246 $language = $orow['language'];
249 <?php if ($genstyles) { ?>
250 <style>
251 .labres tr.head { font-size:10pt; background-color:#cccccc; text-align:center; }
252 .labres tr.detail { font-size:10pt; }
253 .labres a, .labres a:visited, .labres a:hover { color:#0000cc; }
254 .labres table {
255 border-style: solid;
256 border-width: 1px 0px 0px 1px;
257 border-color: black;
259 .labres td, .labres th {
260 border-style: solid;
261 border-width: 0px 1px 1px 0px;
262 border-color: black;
264 </style>
265 <?php } ?>
267 <?php if ($input_form) { ?>
268 <script type="text/javascript" src="<?php echo $GLOBALS['webroot']; ?>/library/textformat.js"></script>
269 <?php } // end if input form ?>
271 <?php if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) { ?>
273 <script type="text/javascript" src="<?php echo $GLOBALS['webroot']; ?>/library/dialog.js"></script>
274 <script language="JavaScript">
276 var mypcc = '<?php echo $GLOBALS['phone_country_code'] ?>';
278 // Called to show patient notes related to this order in the "other" frame.
279 // This works even if we are in a separate window.
280 function showpnotes(orderid) {
281 // Find the top or bottom frame that contains or opened this page; return if none.
282 var w = window.opener ? window.opener : window;
283 for (; w.name != 'RTop' && w.name != 'RBot'; w = w.parent) {
284 if (w.parent == w) {
285 // This message is not translated because a developer will need to find it.
286 alert('Internal error locating target frame in ' + (window.opener ? 'opener' : 'window'));
287 return false;
290 var othername = (w.name == 'RTop') ? 'RBot' : 'RTop';
291 w.parent.left_nav.forceDual();
292 w.parent.left_nav.setRadio(othername, 'pno');
293 w.parent.left_nav.loadFrame('pno1', othername, 'patient_file/summary/pnotes_full.php?orderid=' + orderid);
294 return false;
297 // Process click on LOINC code for patient education popup.
298 function educlick(codetype, codevalue) {
299 dlgopen('<?php echo $GLOBALS['webroot']; ?>/interface/patient_file/education.php' +
300 '?type=' + encodeURIComponent(codetype) +
301 '&code=' + encodeURIComponent(codevalue) +
302 '&language=<?php echo urlencode($language); ?>',
303 '_blank', 1024, 750);
306 </script>
308 <?php } // end if not patient report ?>
310 <?php if ($input_form) { ?>
311 <form method='post' action='single_order_results.php?orderid=<?php echo $orderid; ?>'>
312 <?php } // end if input form ?>
314 <div class='labres'>
316 <table width='100%' cellpadding='2' cellspacing='0'>
317 <tr bgcolor='#cccccc'>
318 <td width='5%' nowrap><?php echo xlt('Patient ID'); ?></td>
319 <td width='45%'><?php echo myCellText($orow['pubpid']); ?></td>
320 <td width='5%' nowrap><?php echo xlt('Order ID'); ?></td>
321 <td width='45%'>
322 <?php
323 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) {
324 echo " <a href='" . $GLOBALS['webroot'];
325 echo "/interface/orders/order_manifest.php?orderid=";
326 echo attr($orow['procedure_order_id']);
327 echo "' target='_blank' onclick='top.restoreSession()'>";
329 echo myCellText($orow['procedure_order_id']);
330 if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) {
331 echo "</a>\n";
333 if ($orow['control_id']) {
334 echo myCellText(' ' . xl('Lab') . ': ' . $orow['control_id']);
337 </td>
338 </tr>
339 <tr bgcolor='#cccccc'>
340 <td nowrap><?php echo xlt('Patient Name'); ?></td>
341 <td><?php echo myCellText($orow['lname'] . ', ' . $orow['fname'] . ' ' . $orow['mname']); ?></td>
342 <td nowrap><?php echo xlt('Ordered By'); ?></td>
343 <td><?php echo myCellText($orow['ulname'] . ', ' . $orow['ufname'] . ' ' . $orow['umname']); ?></td>
344 </tr>
345 <tr bgcolor='#cccccc'>
346 <td nowrap><?php echo xlt('Order Date'); ?></td>
347 <td><?php echo myCellText(oeFormatShortDate($orow['date_ordered'])); ?></td>
348 <td nowrap><?php echo xlt('Print Date'); ?></td>
349 <td><?php echo oeFormatShortDate(date('Y-m-d')); ?></td>
350 </tr>
351 <tr bgcolor='#cccccc'>
352 <td nowrap><?php echo xlt('Order Status'); ?></td>
353 <td><?php echo myCellText($orow['order_status']); ?></td>
354 <td nowrap><?php echo xlt('Encounter Date'); ?></td>
355 <td><?php echo myCellText(oeFormatShortDate(substr($orow['date'], 0, 10))); ?></td>
356 </tr>
357 <tr bgcolor='#cccccc'>
358 <td nowrap><?php echo xlt('Lab'); ?></td>
359 <td><?php echo myCellText($orow['labname']); ?></td>
360 <td nowrap><?php echo xlt('Specimen Type'); ?></td>
361 <td><?php echo myCellText($orow['specimen_type']); ?></td>
362 </tr>
363 </table>
365 &nbsp;<br />
367 <table width='100%' cellpadding='2' cellspacing='0'>
369 <tr class='head'>
370 <td rowspan='2' valign='middle'><?php echo xlt('Ordered Procedure'); ?></td>
371 <td colspan='4'><?php echo xlt('Report'); ?></td>
372 <td colspan='7'><?php echo xlt('Results'); ?></td>
373 </tr>
375 <tr class='head'>
376 <td><?php echo xlt('Reported'); ?></td>
377 <td><?php echo xlt('Specimen'); ?></td>
378 <td><?php echo xlt('Status'); ?></td>
379 <td><?php echo xlt('Note'); ?></td>
380 <td><?php echo xlt('Code'); ?></td>
381 <td><?php echo xlt('Name'); ?></td>
382 <td><?php echo xlt('Abn'); ?></td>
383 <td><?php echo xlt('Value'); ?></td>
384 <td><?php echo xlt('Range'); ?></td>
385 <td><?php echo xlt('Units'); ?></td>
386 <td><?php echo xlt('Note'); ?></td>
387 </tr>
389 <?php
390 $query = "SELECT " .
391 "po.lab_id, po.date_ordered, pc.procedure_order_seq, pc.procedure_code, " .
392 "pc.procedure_name, " .
393 "pr.procedure_report_id, pr.date_report, pr.date_collected, pr.specimen_num, " .
394 "pr.report_status, pr.review_status, pr.report_notes " .
395 "FROM procedure_order AS po " .
396 "JOIN procedure_order_code AS pc ON pc.procedure_order_id = po.procedure_order_id " .
397 "LEFT JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id AND " .
398 "pr.procedure_order_seq = pc.procedure_order_seq " .
399 "WHERE po.procedure_order_id = ? " .
400 "ORDER BY pc.procedure_order_seq, pr.date_report, pr.procedure_report_id";
402 $res = sqlStatement($query, array($orderid));
403 $aNotes = array();
404 $finals = array();
405 $empty_results = array('result_code' => '');
407 // Context for this call that may be used in other functions.
408 $ctx = array(
409 'lastpcid' => -1,
410 'lastprid' => -1,
411 'encount' => 0,
412 'lino' => 0,
413 'sign_list' => '',
414 'seen_report_ids' => array(),
417 while ($row = sqlFetchArray($res)) {
418 $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0);
420 $query = "SELECT " .
421 "ps.result_code, ps.result_text, ps.abnormal, ps.result, ps.range, " .
422 "ps.result_status, ps.facility, ps.units, ps.comments, ps.document_id, ps.date " .
423 "FROM procedure_result AS ps " .
424 "WHERE ps.procedure_report_id = ? " .
425 "ORDER BY ps.result_code, ps.procedure_result_id";
427 $rres = sqlStatement($query, array($report_id));
429 if ($finals_only) {
430 // We are consolidating reports.
431 $rrowsets = array();
432 // First pass creates a $rrowsets[$key] for each unique result code in *this* report, with
433 // the value being an array of the corresponding result rows. This caters to multiple
434 // occurrences of the same result code in the same report.
435 while ($rrow = sqlFetchArray($rres)) {
436 $result_code = empty($rrow['result_code']) ? '' : $rrow['result_code'];
437 $key = sprintf('%05d/', $row['procedure_order_seq']) . $result_code;
438 if (!isset($rrowsets[$key])) $rrowsets[$key] = array();
439 $rrowsets[$key][] = $rrow;
441 // Second pass builds onto the array of final results for *all* reports, where each final
442 // result for a given result code is its *array* of result rows from *one* of the reports.
443 foreach ($rrowsets as $key => $rrowset) {
444 // When two reports have the same date, use the result date to decide which is "latest".
445 if (isset($finals[$key]) &&
446 $row['date_report'] == $finals[$key][0]['date_report'] &&
447 !empty($rrow['date']) && !empty($finals[$key][1]['date']) &&
448 $rrow['date'] < $finals[$key][1]['date'])
450 $finals[$key][2] = true;
451 continue;
453 $finals[$key] = array($row, $rrowset, isset($finals[$key]));
456 else {
457 // We are showing all results for all reports.
458 if (sqlNumRows($rres)) {
459 while ($rrow = sqlFetchArray($rres)) {
460 generate_result_row($ctx, $row, $rrow, false);
463 else {
464 generate_result_row($ctx, $row, $empty_results, false);
469 if ($finals_only) {
470 if (empty($finals) && !empty($row)) {
471 $finals[''] = array($row, array($empty_results), false);
473 ksort($finals);
474 foreach ($finals as $final) {
475 foreach ($final[1] as $rrow) {
476 generate_result_row($ctx, $final[0], $rrow, $final[2]);
483 </table>
485 &nbsp;<br />
486 <table width='100%' style='border-width:0px;'>
487 <tr>
488 <td style='border-width:0px;'>
489 <?php
490 if (!empty($aNotes)) {
491 echo "<table cellpadding='3' cellspacing='0'>\n";
492 echo " <tr bgcolor='#cccccc'>\n";
493 echo " <th align='center' colspan='2'>" . xlt('Notes') . "</th>\n";
494 echo " </tr>\n";
495 foreach ($aNotes as $key => $value) {
496 echo " <tr>\n";
497 echo " <td valign='top'>" . ($key + 1) . "</td>\n";
498 echo " <td>" . nl2br(text($value)) . "</td>\n";
499 echo " </tr>\n";
501 echo "</table>\n";
504 </td>
505 <td style='border-width:0px;' align='right' valign='top'>
506 <?php if ($input_form && !empty($ctx['priors_omitted']) /* empty($_POST['form_showall']) */ ) { ?>
507 <input type='submit' name='form_showall' value='<?php echo xla('Show All Results'); ?>'
508 title='<?php echo xla('Include all values reported for each result code'); ?>' />
509 <?php } else if ($input_form && !empty($_POST['form_showall'])) { ?>
510 <input type='submit' name='form_latest' value='<?php echo xla('Latest Results Only'); ?>'
511 title='<?php echo xla('Show only latest values reported for each result code'); ?>' />
512 <?php } ?>
513 <?php if (empty($GLOBALS['PATIENT_REPORT_ACTIVE'])) { ?>
514 &nbsp;
515 <input type='button' value='<?php echo xla('Related Patient Notes'); ?>'
516 onclick='showpnotes(<?php echo $orderid; ?>)' />
517 <?php } ?>
518 <?php if ($input_form && $ctx['sign_list']) { ?>
519 &nbsp;
520 <input type='hidden' name='form_sign_list' value='<?php echo attr($ctx['sign_list']); ?>' />
521 <input type='submit' name='form_sign' value='<?php echo xla('Sign Results'); ?>'
522 title='<?php echo xla('Mark these reports as reviewed'); ?>' />
523 <?php
524 // If this is a portal patient, sending them a copy is an option.
525 if ($GLOBALS['gbl_portal_cms_enable'] && $orow['cmsportal_login'] !== '') {
526 echo "&nbsp;";
527 echo "<input type='checkbox' name='form_send_to_portal' value='" .
528 attr($orow['cmsportal_login']) . "' checked />\n";
529 echo xlt('Send to portal');
532 <?php } ?>
533 <?php if ($input_form) { ?>
534 &nbsp;
535 <input type='button' value='<?php echo xla('Close'); ?>' onclick='window.close()' />
536 <?php } ?>
537 </td>
538 </tr>
539 </table>
541 </div>
543 <?php if ($input_form) { ?>
544 </form>
545 <?php } // end if input form ?>
547 <?php
548 } // end function generate_order_report