From: Rod Roark Date: Fri, 28 Sep 2012 02:32:17 +0000 (-0700) Subject: Changes to support electronic lab ordering and results. X-Git-Tag: whats-been-changed~440 X-Git-Url: https://repo.or.cz/w/openemr.git/commitdiff_plain/9d9b3d0c16003857fd1d8b390060f609381bdf07 Changes to support electronic lab ordering and results. --- diff --git a/interface/forms/procedure_order/new.php b/interface/forms/procedure_order/new.php index 4755f3f86..c2737ed3d 100644 --- a/interface/forms/procedure_order/new.php +++ b/interface/forms/procedure_order/new.php @@ -1,10 +1,23 @@ -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. +/** +* Encounter form for entering procedure orders. +* +* Copyright (C) 2010-2013 Rod Roark +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ require_once("../../globals.php"); require_once("$srcdir/api.inc"); @@ -12,6 +25,8 @@ require_once("$srcdir/forms.inc"); require_once("$srcdir/options.inc.php"); require_once("$srcdir/formdata.inc.php"); require_once("$srcdir/formatting.inc.php"); +require_once("../../orders/qoe.inc.php"); +require_once("../../orders/gen_hl7_order.inc.php"); // Defaults for new orders. $row = array( @@ -47,17 +62,19 @@ function QuotedOrNull($fld) { $formid = formData('id', 'G') + 0; -// If Save was clicked, save the info. +// If Save or Transmit was clicked, save the info. // -if ($_POST['bn_save']) { +if ($_POST['bn_save'] || $_POST['bn_xmit']) { + $ppid = formData('form_lab_id') + 0; $sets = - "procedure_type_id = " . (formData('form_proc_type') + 0) . ", " . "date_ordered = " . QuotedOrNull(formData('form_date_ordered')) . ", " . "provider_id = " . (formData('form_provider_id') + 0) . ", " . + "lab_id = " . $ppid . ", " . "date_collected = " . QuotedOrNull(formData('form_date_collected')) . ", " . "order_priority = '" . formData('form_order_priority') . "', " . "order_status = '" . formData('form_order_status') . "', " . + "diagnoses = '" . formData('form_diagnoses') . "', " . "patient_instructions = '" . formData('form_patient_instructions') . "', " . "patient_id = '" . $pid . "', " . "encounter_id = '" . $encounter . "'"; @@ -74,11 +91,83 @@ if ($_POST['bn_save']) { // else { $query = "INSERT INTO procedure_order SET $sets"; - $newid = sqlInsert($query); - addForm($encounter, "Procedure Order", $newid, "procedure_order", $pid, $userauthorized); + $formid = sqlInsert($query); + addForm($encounter, "Procedure Order", $formid, "procedure_order", $pid, $userauthorized); + } + + // Remove any existing procedures and their answers for this order and + // replace them from the form. + + sqlStatement("DELETE FROM procedure_answers WHERE procedure_order_id = ?", + array($formid)); + sqlStatement("DELETE FROM procedure_order_code WHERE procedure_order_id = ?", + array($formid)); + + for ($i = 0; isset($_POST['form_proc_type'][$i]); ++$i) { + $ptid = $_POST['form_proc_type'][$i] + 0; + if ($ptid <= 0) continue; + + $prefix = "ans$i" . "_"; + + $poseq = sqlInsert("INSERT INTO procedure_order_code SET ". + "procedure_order_id = ?, " . + "procedure_code = (SELECT procedure_code FROM procedure_type WHERE procedure_type_id = ?), " . + "procedure_name = (SELECT name FROM procedure_type WHERE procedure_type_id = ?)", + array($formid, $ptid, $ptid)); + + $qres = sqlStatement("SELECT " . + "q.procedure_code, q.question_code, q.options, q.fldtype " . + "FROM procedure_type AS t " . + "JOIN procedure_questions AS q ON q.lab_id = t.lab_id " . + "AND q.procedure_code = t.procedure_code AND q.activity = 1 " . + "WHERE t.procedure_type_id = ? " . + "ORDER BY q.seq, q.question_text", array($ptid)); + + while ($qrow = sqlFetchArray($qres)) { + $options = trim($qrow['options']); + $qcode = trim($qrow['question_code']); + $fldtype = $qrow['fldtype']; + $data = ''; + if ($fldtype == 'G') { + if ($_POST["G1_$prefix$qcode"]) { + $data = $_POST["G1_$prefix$qcode"] * 7 + $_POST["G2_$prefix$qcode"]; + } + } + else { + $data = $_POST["$prefix$qcode"]; + } + if (!isset($data) || $data === '') continue; + if (!is_array($data)) $data = array($data); + foreach ($data as $datum) { + // Note this will auto-assign the seq value. + sqlStatement("INSERT INTO procedure_answers SET ". + "procedure_order_id = ?, " . + "procedure_order_seq = ?, " . + "question_code = ?, " . + "answer = ?", + array($formid, $poseq, $qcode, strip_escape_custom($datum))); + } + } + } + + $alertmsg = ''; + if ($_POST['bn_xmit']) { + $hl7 = ''; + $alertmsg = gen_hl7_order($formid, $hl7); + if (empty($alertmsg)) { + $alertmsg = send_hl7_order($ppid, $hl7); + } } + // TBD: Implement and set a transmit date in the order. + // Add code elsewhere to show a warning if a previously transmitted order is opened. + formHeader("Redirecting...."); + if ($alertmsg) { + echo "\n\n"; + } formJump(); formFooter(); exit; @@ -86,14 +175,16 @@ if ($_POST['bn_save']) { if ($formid) { $row = sqlQuery ("SELECT * FROM procedure_order WHERE " . - "procedure_order_id = '$formid' AND activity = '1'") ; + "procedure_order_id = ?", + array($formid)) ; } $enrow = sqlQuery("SELECT p.fname, p.mname, p.lname, fe.date FROM " . "form_encounter AS fe, forms AS f, patient_data AS p WHERE " . - "p.pid = '$pid' AND f.pid = '$pid' AND f.encounter = '$encounter' AND " . + "p.pid = ? AND f.pid = p.pid AND f.encounter = ? AND " . "f.formdir = 'newpatient' AND f.deleted = 0 AND " . - "fe.id = f.form_id LIMIT 1"); + "fe.id = f.form_id LIMIT 1", + array($pid, $encounter)); ?> @@ -124,20 +215,102 @@ td { @@ -159,42 +332,38 @@ function set_proc_type(typeid, typename) {

- +
+ + + - - - - + - - + - - + - - + - - + - - + + + + + + + ''); + + $i = 0; + foreach ($oparr as $oprow) { + $ptid = -1; // -1 means no procedure is selected yet + if (!empty($oprow['procedure_type_id'])) { + $ptid = $oprow['procedure_type_id']; + } +?> + + + + + +
: ''); -if (!empty($row['procedure_type_id'])) { - $ptid = $row['procedure_type_id']; - $ptrow = sqlQuery("SELECT name FROM procedure_type WHERE " . - "procedure_type_id = '$ptid'"); -} +generate_form_field(array('data_type'=>10,'field_id'=>'provider_id'), + $row['provider_id']); ?> -
: - ' - onclick='sel_proc_type()' onfocus='this.blur()' - title='' - style='width:100%;cursor:pointer;cursor:hand' readonly /> -
: -10,'field_id'=>'provider_id'), - $row['provider_id']); + : +
: + : 10,'field_id'=>'provider_id'),
: + : " . @@ -225,8 +394,8 @@ generate_form_field(array('data_type'=>10,'field_id'=>'provider_id'),
: + : 1,'field_id'=>'order_priority', 'list_id'=>'ord_priority'), $row['order_priority']); @@ -235,8 +404,8 @@ generate_form_field(array('data_type'=>1,'field_id'=>'order_priority',
: + : 1,'field_id'=>'order_status', 'list_id'=>'ord_status'), $row['order_status']); @@ -245,19 +414,103 @@ generate_form_field(array('data_type'=>1,'field_id'=>'order_status',
: + : + ' onclick='sel_related(this.name)' + title='' + onfocus='this.blur()' + style='width:100%;cursor:pointer;cursor:hand' readonly /> +
:
: + ' + onclick="sel_proc_type()" + onfocus='this.blur()' + title='' + style='width:100%;cursor:pointer;cursor:hand' readonly /> + + +
+$qoe_init_javascript"; +?> +
+

-' /> +' onclick="addProcLine()" /> +  +' /> +  +' />   -' onclick="top.restoreSession();location=''" /> +' onclick="top.restoreSession();location=''" />

diff --git a/interface/forms/procedure_order/report.php b/interface/forms/procedure_order/report.php dissimilarity index 75% index c4c625f4a..9808d7e9f 100644 --- a/interface/forms/procedure_order/report.php +++ b/interface/forms/procedure_order/report.php @@ -1,51 +1,58 @@ - -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. - -include_once("../../globals.php"); -include_once($GLOBALS["srcdir"] . "/api.inc"); -include_once($GLOBALS["srcdir"] . "/options.inc.php"); - -function procedure_order_report($pid, $encounter, $cols, $id) { - $cols = 1; // force always 1 column - $count = 0; - $data = sqlQuery("SELECT pt.name as Procedure_Order,po.* " . - "FROM procedure_order as po, procedure_type as pt WHERE " . - "po.procedure_type_id = pt.procedure_type_id AND po.procedure_order_id = '$id' AND po.activity = '1'"); - if ($data) { - print "\n\n"; - foreach($data as $key => $value) { - if ($key == "procedure_order_id" || $key == "pid" || $key == "user" || $key == "groupname" || - $key == "authorized" || $key == "activity" || $key == "date" || - $value == "" || $value == "0" || $value == "0.00") { - continue; - } - - $key=ucwords(str_replace("_"," ",$key)); - if ($key == "Order Priority") { - print "\n"; - } - else if ($key == "Order Status") { - print "\n"; - } - else { - print "\n"; - } - $count++; - if ($count == $cols) { - $count = 0; - print "\n\n"; - } - } - print "\n
" . xl($key). ": " . - generate_display_field(array('data_type'=>'1','list_id'=>'ord_priority'),$value) . - "  " . xl($key). ": " . - generate_display_field(array('data_type'=>'1','list_id'=>'ord_status'),$value) . - "  " . xl($key). ": $value  
\n"; - } -} -?> + +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +include_once("../../globals.php"); +include_once($GLOBALS["srcdir"] . "/api.inc"); +include_once($GLOBALS["srcdir"] . "/options.inc.php"); + +function procedure_order_report($pid, $encounter, $cols, $id) { + $data = sqlQuery("SELECT " . + "po.procedure_order_id, po.date_ordered, po.diagnoses, " . + "po.order_status, po.specimen_type, " . + "pp.name AS labname, pr.procedure_report_id, " . + "u.lname AS ulname, u.fname AS ufname, u.mname AS umname " . + "FROM procedure_order AS po " . + "LEFT JOIN procedure_providers AS pp ON pp.ppid = po.lab_id " . + "LEFT JOIN users AS u ON u.id = po.provider_id " . + "LEFT JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id " . + "WHERE po.procedure_order_id = ? " . + "ORDER BY pr.procedure_report_id LIMIT 1", + array($id)); + + if ($data) { + echo "\n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + if (!empty($data['procedure_report_id'])) { + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + } + echo "
" . xlt('Order ID') . ": " . xlt($data['procedure_order_id']) . "
" . xlt('Order Date') . ": " . xlt(oeFormatShortDate($data['date_ordered'])) . "
" . xlt('Ordered By') . ": " . xlt($data['ulname'] . ', ' . $data['ufname'] . ' ' . $data['umname']) . "
" . xlt('Lab') . ": " . xlt($data['labname']) . "
 [" . xlt('View Results') . "]
\n"; + } +} +?> diff --git a/interface/main/left_nav.php b/interface/main/left_nav.php index 8703d0840..897ea8024 100644 --- a/interface/main/left_nav.php +++ b/interface/main/left_nav.php @@ -109,8 +109,11 @@ 'ono' => array(xl('Ofc Notes') , 0, 'main/onotes/office_comments.php'), 'fax' => array(xl('Fax/Scan') , 0, 'fax/faxq.php'), 'adb' => array(xl('Addr Bk') , 0, 'usergroup/addrbook_list.php'), + 'orl' => array(xl('Proc Prov') , 0, 'orders/procedure_provider_list.php'), 'ort' => array(xl('Proc Cat') , 0, 'orders/types.php'), + 'orc' => array(xl('Proc Load') , 0, 'orders/load_compendium.php'), 'orb' => array(xl('Proc Bat') , 0, 'orders/orders_results.php?batch=1'), + 'ore' => array(xl('E-Reports') , 0, 'orders/list_reports.php'), 'cht' => array(xl('Chart Trk') , 0, '../custom/chart_tracker.php'), 'imp' => array(xl('Import') , 0, '../custom/import.php'), 'bil' => array(xl('Billing') , 0, 'billing/billing_report.php'), @@ -1210,10 +1213,13 @@ if (!empty($reg)) {
    • + + +
  • +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +/* +* A bit of documentation that will need to go into the manual: +* +* The lab may want a list of your insurances for mapping into their system. +* To produce it, go into phpmyadmin and run this query: +* +* SELECT i.id, i.name, a.line1, a.line2, a.city, a.state, a.zip, p.area_code, +* p.prefix, p.number FROM insurance_companies AS i +* LEFT JOIN addresses AS a ON a.foreign_id = i.id +* LEFT JOIN phone_numbers AS p ON p.type = 2 AND p.foreign_id = i.id +* ORDER BY i.name, i.id; +* +* Then export as a CSV file and read it into your favorite spreadsheet app. +*/ + +require_once("$srcdir/classes/Address.class.php"); +require_once("$srcdir/classes/InsuranceCompany.class.php"); + +function hl7Text($s) { + // See http://www.interfaceware.com/hl7_escape_protocol.html: + $s = str_replace('\\', '\\E\\' , $s); + $s = str_replace('^' , '\\S\\' , $s); + $s = str_replace('|' , '\\F\\' , $s); + $s = str_replace('~' , '\\R\\' , $s); + $s = str_replace('&' , '\\T\\' , $s); + $s = str_replace("\r", '\\X0d\\', $s); + return $s; +} + +function hl7Zip($s) { + return hl7Text(preg_replace('/[-\s]*/','',$s)); +} + +function hl7Date($s) { + return preg_replace('/[^\d]/','',$s); +} + +function hl7Time($s) { + if (empty($s)) return ''; + return date('YmdHis', strtotime($s)); +} + +function hl7Sex($s) { + $s = strtoupper(substr($s, 0, 1)); + if ($s !== 'M' && $s !== 'F') $s = 'U'; + return $s; +} + +function hl7Phone($s) { + if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)\D*$/", $s, $tmp)) { + return '(' . $tmp[1] . ')' . $tmp[2] . '-' . $tmp[3]; + } + if (preg_match("/(\d\d\d)\D*(\d\d\d\d)\D*$/", $s, $tmp)) { + return $tmp[1] . '-' . $tmp[2]; + } + return ''; +} + +function hl7SSN($s) { + if (preg_match("/(\d\d\d)\D*(\d\d)\D*(\d\d\d\d)\D*$/", $s, $tmp)) { + return $tmp[1] . '-' . $tmp[2] . '-' . $tmp[3]; + } + return ''; +} + +function hl7Priority($s) { + return strtoupper(substr($s, 0, 1)) == 'H' ? 'S' : 'R'; +} + +function hl7Relation($s) { + $tmp = strtolower($s); + if ($tmp == 'self' || $tmp == '') return 'self'; + else if ($tmp == 'spouse') return 'spouse'; + else if ($tmp == 'child' ) return 'child'; + else if ($tmp == 'other' ) return 'other'; + // Should not get here so this will probably get noticed if we do. + return $s; +} + +/** + * Get array of insurance payers for the specified patient as of the specified + * date. If no date is passed then the current date is used. + * + * @param integer $pid Patient ID. + * @param date $encounter_date YYYY-MM-DD date. + * @return array Array containing an array of data for each payer. + */ +function loadPayerInfo($pid, $date='') { + if (empty($date)) $date = date('Y-m-d'); + $payers = array(); + $dres = sqlStatement("SELECT * FROM insurance_data WHERE " . + "pid = ? AND date <= ? ORDER BY type ASC, date DESC", + array($pid, $date)); + $prevtype = ''; // type is primary, secondary or tertiary + while ($drow = sqlFetchArray($dres)) { + if (strcmp($prevtype, $drow['type']) == 0) continue; + $prevtype = $drow['type']; + // Very important to check for a missing provider because + // that indicates no insurance as of the given date. + if (empty($drow['provider'])) continue; + $ins = count($payers); + $crow = sqlQuery("SELECT * FROM insurance_companies WHERE id = ?", + array($drow['provider'])); + $orow = new InsuranceCompany($drow['provider']); + $payers[$ins] = array(); + $payers[$ins]['data'] = $drow; + $payers[$ins]['company'] = $crow; + $payers[$ins]['object'] = $orow; + } + return $payers; +} + +/** + * Generate HL7 for the specified procedure order. + * + * @param integer $orderid Procedure order ID. + * @param string &$out Container for target HL7 text. + * @return string Error text, or empty if no errors. + */ +function gen_hl7_order($orderid, &$out) { + + // Delimiters + $d0 = "\r"; + $d1 = '|'; + $d2 = '^'; + + $today = time(); + $out = ''; + + $porow = sqlQuery("SELECT " . + "po.date_collected, po.date_ordered, po.order_priority, po.diagnoses, " . + "pp.*, " . + "pd.pid, pd.pubpid, pd.fname, pd.lname, pd.mname, pd.DOB, pd.ss, " . + "pd.phone_home, pd.phone_biz, pd.sex, pd.street, pd.city, pd.state, pd.postal_code, " . + "f.encounter, u.fname AS docfname, u.lname AS doclname, u.npi AS docnpi " . + "FROM procedure_order AS po, procedure_providers AS pp, " . + "forms AS f, patient_data AS pd, users AS u " . + "WHERE " . + "po.procedure_order_id = ? AND " . + "pp.ppid = po.lab_id AND " . + "f.formdir = 'procedure_order' AND " . + "f.form_id = po.procedure_order_id AND " . + "pd.pid = f.pid AND " . + "u.id = po.provider_id", + array($orderid)); + if (empty($porow)) return "Procedure order or lab is missing for order ID '$orderid'"; + + $pcres = sqlStatement("SELECT " . + "pc.procedure_code, pc.procedure_name, pc.procedure_order_seq " . + "FROM procedure_order_code AS pc " . + "WHERE " . + "pc.procedure_order_id = ? " . + "ORDER BY pc.procedure_order_seq", + array($orderid)); + + // Message Header + $out .= "MSH" . + $d1 . "$d2~\\&" . // Encoding Characters (delimiters) + $d1 . $porow['send_app_id'] . // Sending Application ID + $d1 . $porow['send_fac_id'] . // Sending Facility ID + $d1 . $porow['recv_app_id'] . // Receiving Application ID + $d1 . $porow['recv_fac_id'] . // Receiving Facility ID + $d1 . date('YmdHis', $today) . // Date and time of this message + $d1 . + $d1 . 'ORM' . $d2 . 'O01' . // Message Type + $d1 . $orderid . // Unique Message Number + $d1 . $porow['DorP'] . // D=Debugging, P=Production + $d1 . '2.3' . // HL7 Version ID + $d0; + + // Patient Identification + $out .= "PID" . + $d1 . "1" . // Set ID (always just 1 of these) + $d1 . $porow['pid'] . // Patient ID (not required) + $d1 . $porow['pid'] . // Patient ID (required) + $d1 . // Alternate Patient ID (not required) + $d1 . hl7Text($porow['lname']) . + $d2 . hl7Text($porow['fname']); + if ($porow['mname']) $out .= $d2 . hl7Text($porow['mname']); + $out .= + $d1 . + $d1 . hl7Date($porow['DOB']) . // DOB + $d1 . hl7Sex($porow['sex']) . // Sex: M, F or U + $d1 . $d1 . + $d1 . hl7Text($porow['street']) . + $d2 . + $d2 . hl7Text($porow['city']) . + $d2 . hl7Text($porow['state']) . + $d2 . hl7Zip($porow['zip']) . + $d1 . + $d1 . hl7Phone($porow['phone_home']) . + $d1 . hl7Phone($porow['phone_biz']) . + $d1 . $d1 . $d1 . + $d1 . $porow['encounter'] . + $d1 . hl7SSN($porow['ss']) . + $d1 . $d1 . $d1 . + $d0; + + // NTE segment(s) omitted. + + // Patient Visit. + $out .= "PV1" . + $d1 . "1" . // Set ID (always just 1 of these) + $d1 . // Patient Class (if required, O for Outpatient) + $d1 . // Patient Location (for inpatient only?) + $d1 . $d1 . $d1 . + $d1 . hl7Text($porow['docnpi']) . // Attending Doctor ID + $d2 . hl7Text($porow['doclname']) . // Last Name + $d2 . hl7Text($porow['docfname']) . // First Name + str_repeat($d1, 11) . // PV1 8 to 18 all empty + $d1 . $porow['encounter'] . // Encounter Number + str_repeat($d1, 13) . // PV1 20 to 32 all empty + $d0; + + // Insurance stuff. + $payers = loadPayerInfo($porow['pid'], $porow['date_ordered']); + $setid = 0; + foreach ($payers as $payer) { + $payer_object = $payer['object']; + $payer_address = $payer_object->get_address(); + $out .= "IN1" . + $d1 . ++$setid . // Set ID + $d1 . // Insurance Plan Identifier ?? + $d1 . hl7Text($payer['company']['id']) . // Insurance Company ID + $d1 . hl7Text($payer['company']['name']) . // Insurance Company Name + $d1 . hl7Text($payer_address->get_line1()) . // Street Address + $d2 . + $d2 . hl7Text($payer_address->get_city()) . // City + $d2 . hl7Text($payer_address->get_state()) . // State + $d2 . hl7Zip($payer_address->get_zip()) . // Zip Code + $d1 . + $d1 . hl7Phone($payer_object->get_phone()) . // Phone Number + $d1 . hl7Text($payer['data']['group_number']) . // Insurance Company Group Number + str_repeat($d1, 7) . // IN1 9-15 all empty + $d1 . hl7Text($payer['data']['subscriber_lname']) . // Insured last name + $d2 . hl7Text($payer['data']['subscriber_fname']) . // Insured first name + $d2 . hl7Text($payer['data']['subscriber_mname']) . // Insured middle name + $d1 . hl7Relation($payer['data']['subscriber_relationship']) . + $d1 . hl7Date($payer['data']['subscriber_DOB']) . // Insured DOB + $d1 . hl7Date($payer['data']['subscriber_street']) . // Insured Street Address + $d2 . + $d2 . hl7Text($payer['data']['subscriber_city']) . // City + $d2 . hl7Text($payer['data']['subscriber_state']) . // State + $d2 . hl7Zip($payer['data']['subscriber_postal_code']) . // Zip + $d1 . + $d1 . + $d1 . $setid . // 1=Primary, 2=Secondary, 3=Tertiary + str_repeat($d1, 13) . // IN1-23 to 35 all empty + $d1 . hl7Text($payer['data']['policy_number']) . // Policy Number + str_repeat($d1, 12) . // IN1-37 to 48 all empty + $d0; + + // IN2 segment omitted. + } + + // Guarantor. OpenEMR doesn't have these so use the patient. + $out .= "GT1" . + $d1 . "1" . // Set ID (always just 1 of these) + $d1 . + $d1 . hl7Text($porow['lname']) . + $d2 . hl7Text($porow['fname']); + if ($porow['mname']) $out .= $d2 . hl7Text($porow['mname']); + $out .= + $d1 . + $d1 . hl7Text($porow['street']) . + $d2 . + $d2 . hl7Text($porow['city']) . + $d2 . hl7Text($porow['state']) . + $d2 . hl7Zip($porow['zip']) . + $d1 . hl7Phone($porow['phone_home']) . + $d1 . hl7Phone($porow['phone_biz']) . + $d1 . hl7Date($porow['DOB']) . // DOB + $d1 . hl7Sex($porow['sex']) . // Sex: M, F or U + $d1 . + $d1 . 'self' . // Relationship + $d1 . hl7SSN($porow['ss']) . + $d0; + + // Common Order. + $out .= "ORC" . + $d1 . "NW" . // New Order + $d1 . $orderid . // Placer Order Number + str_repeat($d1, 6) . // ORC 3-8 not used + $d1 . date('YmdHis') . // Transaction date/time + $d1 . $d1 . + $d1 . hl7Text($porow['docnpi']) . // Ordering Provider + $d2 . hl7Text($porow['doclname']) . // Last Name + $d2 . hl7Text($porow['docfname']) . // First Name + str_repeat($d1, 7) . // ORC 13-19 not used + $d1 . "2" . // ABN Status: 2 = Notified & Signed, 4 = Unsigned + $d0; + + $setid = 0; + while ($pcrow = sqlFetchArray($pcres)) { + // Observation Request. + $out .= "OBR" . + $d1 . ++$setid . // Set ID + $d1 . $orderid . // Placer Order Number + $d1 . + $d1 . hl7Text($pcrow['procedure_code']) . + $d2 . hl7Text($pcrow['procedure_name']) . + $d1 . hl7Priority($porow['order_priority']) . // S=Stat, R=Routine + $d1 . + $d1 . hl7Time($porow['date_collected']) . // Observation Date/Time + str_repeat($d1, 8) . // OBR 8-15 not used + $d1 . hl7Text($porow['docnpi']) . // Physician ID + $d2 . hl7Text($porow['doclname']) . // Last Name + $d2 . hl7Text($porow['docfname']) . // First Name + $d1 . + $d1 . (count($payers) ? 'I' : 'P') . // I=Insurance, C=Client, P=Self Pay + str_repeat($d1, 8) . // OBR 19-26 not used + $d1 . '0' . // ? + $d0; + + // Diagnoses. Currently hard-coded for ICD9 and we'll surely want to make + // this more flexible. + $setid2 = 0; + if (!empty($porow['diagnoses'])) { + $relcodes = explode(';', $porow['diagnoses']); + foreach ($relcodes as $codestring) { + if ($codestring === '') continue; + list($codetype, $code) = explode(':', $codestring); + if ($codetype !== 'ICD9') continue; + $dcrow = sqlQuery("SELECT c.code_text FROM " . + "code_types AS ct, codes AS c WHERE " . + "ct.ct_key = ? AND c.code_type = ct.ct_id AND " . + "c.code = ? " . + "ORDER BY c.id LIMIT 1", + array($codetype, $code)); + $desc = empty($dcrow['code_text']) ? '' : $dcrow['code_text']; + $out .= "DG1" . + $d1 . ++$setid2 . // Set ID + $d1 . // Diagnosis Coding Method + $d1 . $code . // Diagnosis Code + $d1 . hl7Text($desc) . // Diagnosis Description + $d0; + } + } + + // Order entry questions and answers. + $qres = sqlStatement("SELECT " . + "a.question_code, a.answer, q.fldtype " . + "FROM procedure_answers AS a " . + "LEFT JOIN procedure_questions AS q ON " . + "q.lab_id = ? " . + "AND q.procedure_code = ? AND " . + "q.question_code = a.question_code " . + "WHERE " . + "a.procedure_order_id = ? AND " . + "a.procedure_order_seq = ? " . + "ORDER BY q.seq, a.answer_seq", + array($porow['ppid'], $pcrow['procedure_code'], $orderid, $pcrow['procedure_order_seq'])); + $setid2 = 0; + while ($qrow = sqlFetchArray($qres)) { + // Formatting of these answer values may be lab-specific and we'll figure + // out how to deal with that as more labs are supported. + $answer = trim($qrow['answer']); + $fldtype = $qrow['fldtype']; + $datatype = 'ST'; + if ($fldtype == 'N') { + $datatype = "NM"; + } else if ($fldtype == 'D') { + $answer = hl7Date($answer); + } else if ($fldtype == 'G') { + $weeks = intval($answer / 7); + $days = $answer % 7; + $answer = $weeks . 'wks ' . $days . 'days'; + } + $out .= "OBX" . + $d1 . ++$setid2 . // Set ID + $d1 . $datatype . // Structure of observation value + $d1 . hl7Text($qrow['question_code']) . // Clinical question code + $d1 . + $d1 . hl7Text($answer) . // Clinical question answer + $d0; + } + } + + return ''; +} + +/** + * Transmit HL7 for the specified lab. + * + * @param integer $ppid Procedure provider ID. + * @param string $out The HL7 text to be sent. + * @return string Error text, or empty if no errors. + */ +function send_hl7_order($ppid, $out) { + global $srcdir; + + $d0 = "\r"; + + $pprow = sqlQuery("SELECT * FROM procedure_providers " . + "WHERE ppid = ?", array($ppid)); + if (empty($pprow)) return xl('Procedure provider') . " $ppid " . xl('not found'); + + $protocol = $pprow['protocol']; + $remote_host = $pprow['remote_host']; + + // Extract MSH-10 which is the message control ID. + $segmsh = explode(substr($out, 3, 1), substr($out, 0, strpos($out, $d0))); + $msgid = $segmsh[9]; + if (empty($msgid)) return xl('Internal error: Cannot find MSH-10'); + + if ($protocol == 'SFTP') { + ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . "$srcdir/phpseclib"); + require_once("$srcdir/phpseclib/Net/SFTP.php"); + + // Compute the target path/file name. + $filename = $msgid . '.txt'; + if ($pprow['orders_path']) $filename = $pprow['orders_path'] . '/' . $filename; + + // Connect to the server and write the file. + $sftp = new Net_SFTP($remote_host); + if (!$sftp->login($pprow['login'], $pprow['password'])) { + return xl('Login to remote host') . " '$remote_host' " . xl('failed'); + } + if (!$sftp->put($filename, $out)) { + return xl('Creating file') . " '$filename' " . xl('on remote host failed'); + } + } + + // TBD: Insert "else if ($protocol == '???') {...}" to support other protocols. + + else { + return xl('Protocol') . " '$protocol' " . xl('not implemented'); + } + + // Falling through to here indicates success. + newEvent("proc_order_xmit", $_SESSION['authUser'], $_SESSION['authProvider'], 1, + "ID: $msgid Protocol: $protocol Host: $remote_host"); + return ''; +} +?> diff --git a/interface/orders/list_reports.php b/interface/orders/list_reports.php new file mode 100644 index 000000000..1b60d44d2 --- /dev/null +++ b/interface/orders/list_reports.php @@ -0,0 +1,320 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +require_once("../globals.php"); +require_once("$srcdir/acl.inc"); +require_once("$srcdir/formdata.inc.php"); +require_once("$srcdir/options.inc.php"); +require_once("$srcdir/formatting.inc.php"); +require_once("./receive_hl7_results.inc.php"); + +function getListItem($listid, $value) { + $lrow = sqlQuery("SELECT title FROM list_options " . + "WHERE list_id = ? AND option_id = ?", + array($listid, $value)); + $tmp = xl_list_label($lrow['title']); + if (empty($tmp)) $tmp = "($report_status)"; + return $tmp; +} + +function myCellText($s) { + if ($s === '') return ' '; + return text($s); +} + +// Check authorization. +$thisauth = acl_check('patients', 'med'); +if (!$thisauth) die(xl('Not authorized')); +?> + + + + + +<?php xl('Procedure Orders and Reports','e'); ?> + + + + + + + + + + + + + + + + +
    + +\n"; +} +if ($errmsg) { + echo "" . text($errmsg) . "
    \n"; +} + +$form_from_date = formData('form_from_date','P',true); +$form_to_date = formData('form_to_date','P',true); +if (empty($form_to_date)) $form_to_date = $form_from_date; + +$form_reviewed = 0 + formData('form_reviewed'); +if (!$form_reviewed) $form_reviewed = 3; + +$form_patient = !empty($_POST['form_patient']); +?> + + + + + +
    +  : + ' + onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' /> + [?]' /> + +  : + ' + onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' /> + [?]' /> + +   + + +   + />Current Patient Only + +   + > +
    + + + + + + + + + + + + + + + + + + + + + + += '$form_from_date'"; +} +if (!empty($form_to_date)) { + $where .= " AND po.date_ordered <= '$form_to_date'"; +} + +if ($form_patient) { + $where .= " AND po.patient_id = '$pid'"; +} + +if ($form_reviewed == 2) { + $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status = 'reviewed'"; +} +else if ($form_reviewed == 3) { + $where .= " AND pr.procedure_report_id IS NOT NULL AND pr.review_status != 'reviewed'"; +} +else if ($form_reviewed == 4) { + $where .= " AND pr.procedure_report_id IS NULL"; +} + +$query = "SELECT po.patient_id, " . + "pd.fname, pd.mname, pd.lname, pd.pubpid, $selects " . + "FROM procedure_order AS po " . + "LEFT JOIN procedure_order_code AS pc ON pc.procedure_order_id = po.procedure_order_id " . + "LEFT JOIN patient_data AS pd ON pd.pid = po.patient_id $joins " . + "WHERE $where " . + "ORDER BY pd.lname, pd.fname, pd.mname, po.patient_id, $orderby"; + +$res = sqlStatement($query); + +$lastptid = -1; +$lastpoid = -1; +$lastpcid = -1; +$encount = 0; +$lino = 0; +$extra_html = ''; + +while ($row = sqlFetchArray($res)) { + $patient_id = empty($row['patient_id' ]) ? 0 : ($row['patient_id' ] + 0); + $order_id = empty($row['procedure_order_id' ]) ? 0 : ($row['procedure_order_id' ] + 0); + $order_seq = empty($row['procedure_order_seq']) ? 0 : ($row['procedure_order_seq'] + 0); + $date_ordered = empty($row['date_ordered']) ? '' : $row['date_ordered']; + $procedure_code = empty($row['procedure_code']) ? '' : $row['procedure_code']; + $procedure_name = empty($row['procedure_name']) ? '' : $row['procedure_name']; + $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0); + $date_report = empty($row['date_report']) ? '' : $row['date_report']; + $report_status = empty($row['report_status']) ? '' : $row['report_status']; + $review_status = empty($row['review_status']) ? '' : $row['review_status']; + + $ptname = $row['lname']; + if ($row['fname'] || $row['mname']) + $ptname .= ', ' . $row['fname'] . ' ' . $row['mname']; + + if ($lastpoid != $order_id || $lastpcid != $order_seq) { + ++$encount; + } + $bgcolor = "#" . (($encount & 1) ? "ddddff" : "ffdddd"); + + echo " \n"; + + // Generate patient columns. + if ($lastptid != $patient_id) { + $lastpoid = -1; + echo " \n"; + echo " \n"; + } + else { + echo " "; + } + + // Generate order columns. + if ($lastpoid != $order_id) { + $lastpcid = -1; + echo " \n"; + echo " \n"; + } + else { + echo " "; + } + + // Generate procedure columns. + if ($order_seq && $lastpcid != $order_seq) { + echo " \n"; + echo " \n"; + } + else { + echo " "; + } + + // Generate report columns. + if ($report_id) { + echo " \n"; + + // echo " \n"; + // echo " \n"; + + echo " \n"; + + } + else { + echo " "; + } + + echo " \n"; + + $lastptid = $patient_id; + $lastpoid = $order_id; + $lastpcid = $order_seq; + ++$lino; +} +?> + +
    " . text($ptname) . "" . text($row['pubpid']) . " "; + echo text($date_ordered); + echo "" . text($order_id) . " " . text($procedure_code) . "" . text($procedure_name) . " " . text($date_report) . "" . text($report_status) . "" . text($review_status) . ""; + echo myCellText(getListItem('proc_rep_status', $report_status)); + if ($review_status == 'reviewed') { + echo " ✓"; // unicode check mark character + } + echo " 
    + + + +
    + + diff --git a/interface/orders/load_compendium.php b/interface/orders/load_compendium.php new file mode 100644 index 000000000..a0d3dded0 --- /dev/null +++ b/interface/orders/load_compendium.php @@ -0,0 +1,462 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +set_time_limit(0); + +$sanitize_all_escapes = true; +$fake_register_globals = false; + +require_once("../globals.php"); +require_once("$srcdir/acl.inc"); + +// This array is an important reference for the supported labs and their NPI +// numbers as known to this program. The clinic must define at least one +// address book entry for a lab that has a supported NPI number. +// +$lab_npi = array( + '1235186800' => 'Pathgroup Labs LLC', + '1598760985' => 'Yosemite Pathology Medical Group', +); + +/** + * Get lab's ID from the users table given its NPI. If none return 0. + * + * @param string $npi The lab's NPI number as known to the system + * @return integer The numeric value of the lab's address book entry + */ +function getLabID($npi) { + $lrow = sqlQuery("SELECT ppid FROM procedure_providers WHERE " . + "npi = ? ORDER BY ppid LIMIT 1", + array($npi)); + if (empty($lrow['ppid'])) return 0; + return intval($lrow['ppid']); +} + +if (!acl_check('admin', 'super')) die(xlt('Not authorized','','','!')); + +$form_step = isset($_POST['form_step']) ? trim($_POST['form_step']) : '0'; +$form_status = isset($_POST['form_status' ]) ? trim($_POST['form_status' ]) : ''; + +if (!empty($_POST['form_import'])) $form_step = 1; + +// When true the current form will submit itself after a brief pause. +$auto_continue = false; + +// Set up main paths. +$EXPORT_FILE = $GLOBALS['temporary_files_dir'] . "/openemr_config.sql"; +?> + + + + +<?php echo xlt('Load Lab Configuration'); ?> + + + +

    +
    + + + +\n"; + echo " \n"; + echo " \n"; + echo " \n"; + + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + + echo " \n"; + echo " \n"; + echo "\n"; + echo " \n"; + + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; +} + +echo " \n"; +echo " + +
    " . xlt('Vendor') . "
    " . xlt('Action') . "
    " . xlt('Container Group Name') . "
    " . xlt('File to Upload') . ""; + echo "
     
    \n"; + +if ($form_step == 1) { + // Process uploaded config file. + if (is_uploaded_file($_FILES['userfile']['tmp_name'])) { + $form_vendor = $_POST['vendor']; + $form_action = intval($_POST['action']); + $form_group = intval($_POST['group']); + $lab_id = getLabID($form_vendor); + + $form_status .= xlt('Applying') . "...
    "; + echo nl2br($form_status); + + $fhcsv = fopen($_FILES['userfile']['tmp_name'], "r"); + + if ($fhcsv) { + + // Vendor = Pathgroup + // + if ($form_vendor == '1235186800') { + + if ($form_action == 1) { // load compendium + // Mark all "ord" rows having the indicated parent as inactive. + sqlStatement("UPDATE procedure_type SET activity = 0 WHERE " . + "parent = ? AND procedure_type = 'ord'", + array($form_group)); + + // What should be uploaded is the "Compendium" spreadsheet provided by + // PathGroup, saved in "Text CSV" format from OpenOffice, using its + // default settings. Values for each row are: + // 0: Order Code : mapped to procedure_code of order type + // 1: Order Name : mapped to name of order type + // 2: Result Code : mapped to procedure_code of result type + // 3: Result Name : mapped to name of result type + // + while (!feof($fhcsv)) { + $acsv = fgetcsv($fhcsv, 4096); + if (count($acsv) < 4 || $acsv[0] == "Order Code") continue; + $standard_code = empty($acsv[2]) ? '' : ('CPT4:' . $acsv[2]); + + // Update or insert the order row, if not already done. + $trow = sqlQuery("SELECT * FROM procedure_type WHERE " . + "parent = ? AND procedure_code = ? AND procedure_type = 'ord' " . + "ORDER BY procedure_type_id DESC LIMIT 1", + array($form_group, $acsv[0])); + if (empty($trow['procedure_type_id']) || $trow['activity'] == 0) { + if (empty($trow['procedure_type_id'])) { + $ptid = sqlInsert("INSERT INTO procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?", + array($form_group, $acsv[1], $lab_id, $acsv[0], 'ord')); + } + else { + $ptid = $trow['procedure_type_id']; + sqlStatement("UPDATE procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " . + "activity = 1 WHERE procedure_type_id = ?", + array($form_group, $acsv[1], $lab_id, $acsv[0], 'ord', $ptid)); + } + sqlStatement("UPDATE procedure_type SET activity = 0 WHERE " . + "parent = ? AND procedure_type = 'res'", + array($ptid)); + } + + // Update or insert the result row. + // Not sure we need this, but what the hell. + $trow = sqlQuery("SELECT * FROM procedure_type WHERE " . + "parent = ? AND procedure_code = ? AND procedure_type = 'res' " . + "ORDER BY procedure_type_id DESC LIMIT 1", + array($ptid, $acsv[2])); + // The following should always be true, otherwise duplicate input row. + if (empty($trow['procedure_type_id']) || $trow['activity'] == 0) { + if (empty($trow['procedure_type_id'])) { + sqlInsert("INSERT INTO procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?", + array($ptid, $acsv[3], $lab_id, $acsv[2], 'res')); + } + else { + $resid = $trow['procedure_type_id']; + sqlStatement("UPDATE procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " . + "activity = 1 WHERE procedure_type_id = ?", + array($ptid, $acsv[3], $lab_id, $acsv[2], 'res', $resid)); + } + } // end if + } // end while + } // end load compendium + + else if ($form_action == 2) { // load questions + // Mark the vendor's current questions inactive. + sqlStatement("UPDATE procedure_questions SET activity = 0 WHERE lab_id = ?", + array($lab_id)); + + // What should be uploaded is the "AOE Questions" spreadsheet provided by + // PathGroup, saved in "Text CSV" format from OpenOffice, using its + // default settings. Values for each row are: + // 0: OBRCode (order code) + // 1: Question Code + // 2: Question + // 3: "Tips" + // 4: Required (0 = No, 1 = Yes) + // 5: Maxchar (integer length) + // 6: FieldType (FT = free text, DD = dropdown, ST = string) + // + $seq = 0; + $last_code = ''; + while (!feof($fhcsv)) { + $acsv = fgetcsv($fhcsv, 4096); + if (count($acsv) < 7 || $acsv[4] == "Required") continue; + $code = trim($acsv[0]); + if (empty($code)) continue; + + if ($code != $last_code) { + $seq = 0; + $last_code = $code; + } + ++$seq; + + $required = 0 + $acsv[4]; + $maxsize = 0 + $acsv[5]; + $fldtype = 'T'; + + // Figure out field type. + if ($acsv[6] == 'DD') $fldtype = 'S'; + else if (stristr($acsv[3], 'mm/dd/yy') !== FALSE) $fldtype = 'D'; + else if (stristr($acsv[3], 'wks_days') !== FALSE) $fldtype = 'G'; + else if ($acsv[6] == 'FT') $fldtype = 'T'; + else $fldtype = 'N'; + + $qrow = sqlQuery("SELECT * FROM procedure_questions WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array($lab_id, $code, $acsv[1])); + + if (empty($qrow['question_code'])) { + sqlStatement("INSERT INTO procedure_questions SET " . + "lab_id = ?, procedure_code = ?, question_code = ?, question_text = ?, " . + "required = ?, maxsize = ?, fldtype = ?, options = '', tips = ?, + activity = 1, seq = ?", + array($lab_id, $code, $acsv[1], $acsv[2], $required, $maxsize, $fldtype, $acsv[3], $seq)); + } + else { + sqlStatement("UPDATE procedure_questions SET " . + "question_text = ?, required = ?, maxsize = ?, fldtype = ?, " . + "options = '', tips = ?, activity = 1, seq = ? WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array($acsv[2], $required, $maxsize, $fldtype, $acsv[3], $seq, + $lab_id, $code, $acsv[1])); + } + + } // end while + } // end load questions + + else if ($form_action == 3) { // load question options + // What should be uploaded is the "AOE Options" spreadsheet provided + // by YPMG, saved in "Text CSV" format from OpenOffice, using its + // default settings. Values for each row are: + // 0: OBXCode (question code) + // 1: OBRCode (procedure code) + // 2: Option1 (option text) + // 3: Optioncode (the row is duplicated for each possible value) + // + while (!feof($fhcsv)) { + $acsv = fgetcsv($fhcsv, 4096); + if (count($acsv) < 4 || ($acsv[0] == "OBXCode")) continue; + $pcode = trim($acsv[1]); + $qcode = trim($acsv[0]); + $options = trim($acsv[2]) . ':' . trim($acsv[3]); + if (empty($pcode) || empty($qcode)) continue; + $qrow = sqlQuery("SELECT * FROM procedure_questions WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array($lab_id, $pcode, $qcode)); + if (empty($qrow['procedure_code'])) { + continue; // should not happen + } + else { + if ($qrow['activity'] == '1' && $qrow['options'] !== '') { + $options = $qrow['options'] . ';' . $options; + } + sqlStatement("UPDATE procedure_questions SET " . + "options = ? WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array($options, $lab_id, $pcode, $qcode)); + } + } // end while + } // end load questions + } // End Pathgroup + + // Vendor = Yosemite Pathology Medical Group + // + if ($form_vendor == '1598760985') { + if ($form_action == 1) { // load compendium + // Mark all "ord" rows having the indicated parent as inactive. + sqlStatement("UPDATE procedure_type SET activity = 0 WHERE " . + "parent = ? AND procedure_type = 'ord'", + array($form_group)); + // What should be uploaded is the Order Compendium spreadsheet provided + // by YPMG, saved in "Text CSV" format from OpenOffice, using its + // default settings. Values for each row are: + // 0: Order code : mapped to procedure_code + // 1: Order Name : mapped to name + // 2: Result Code : ignored (will cause multiple occurrences of the same order code) + // 3: Result Name : ignored + // + while (!feof($fhcsv)) { + $acsv = fgetcsv($fhcsv, 4096); + $ordercode = trim($acsv[0]); + if (count($acsv) < 2 || $ordercode == "Order Code") continue; + $trow = sqlQuery("SELECT * FROM procedure_type WHERE " . + "parent = ? AND procedure_code = ? AND procedure_type = 'ord' " . + "ORDER BY procedure_type_id DESC LIMIT 1", + array($form_group, $ordercode)); + + if (empty($trow['procedure_type_id'])) { + sqlStatement("INSERT INTO procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " . + "activity = 1", + array($form_group, trim($acsv[1]), $lab_id, $ordercode, 'ord')); + } + else { + sqlStatement("UPDATE procedure_type SET " . + "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " . + "activity = 1 " . + "WHERE procedure_type_id = ?", + array($form_group, trim($acsv[1]), $lab_id, $ordercode, 'ord', + $trow['procedure_type_id'])); + } + } + } + else if ($form_action == 2) { // load questions + // Mark the vendor's current questions inactive. + sqlStatement("UPDATE procedure_questions SET activity = 0 WHERE lab_id = ?", + array($lab_id)); + + // What should be uploaded is the "AOE Questions" spreadsheet provided + // by YPMG, saved in "Text CSV" format from OpenOffice, using its + // default settings. Values for each row are: + // 0: Order Code + // 1: Question Code + // 2: Question + // 3: Is Required (always "false") + // 4: Field Type ("Free Text", "Pre-Defined Text" or "Multiselect Pre-Defined Text") + // 5: Response (just one; the row is duplicated for each possible value) + // + while (!feof($fhcsv)) { + $acsv = fgetcsv($fhcsv, 4096); + if (count($acsv) < 5 || ($acsv[3] !== "false" && $acsv[3] !== "true")) continue; + + $pcode = trim($acsv[0]); + $qcode = trim($acsv[1]); + $options = trim($acsv[5]); + if (empty($pcode) || empty($qcode)) continue; + + $qrow = sqlQuery("SELECT * FROM procedure_questions WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array($lab_id, $pcode, $qcode)); + + // If this is the first option value and it's Multiselect, then + // prepend '+;' to indicate start of a multi-select list. + if (!empty($options) && + (empty($qrow['options']) || empty($qrow['activity'])) && + strpos($acsv[4], 'Multiselect') !== FALSE) + { + $options = '+;' . $options; + } + + if (empty($qrow['procedure_code'])) { + sqlStatement("INSERT INTO procedure_questions SET " . + "lab_id = ?, procedure_code = ?, question_code = ?, question_text = ?, " . + "options = ?, activity = 1", + array($lab_id, $pcode, $qcode, trim($acsv[2]), $options)); + } + else { + if ($qrow['activity'] == '1' && $qrow['options'] !== '' && $options !== '') { + $options = $qrow['options'] . ';' . $options; + } + sqlStatement("UPDATE procedure_questions SET " . + "question_text = ?, options = ?, activity = 1 WHERE " . + "lab_id = ? AND procedure_code = ? AND question_code = ?", + array(trim($acsv[2]), $options, $lab_id, $pcode, $qcode)); + } + } // end while + } // end load questions + } // End YPMG + + fclose($fhcsv); + } + else { + echo xlt('Internal error accessing uploaded file!'); + $form_step = -1; + } + } + else { + echo xlt('Upload failed!'); + $form_step = -1; + } + $auto_continue = true; +} + +if ($form_step == 2) { + $form_status .= xlt('Done') . "."; + echo nl2br($form_status); +} + +++$form_step; +?> + +
    + + + + +
    + + + +
    + + + + + + + + diff --git a/interface/orders/orders_results.php b/interface/orders/orders_results.php index c58915b15..d0836a0de 100644 --- a/interface/orders/orders_results.php +++ b/interface/orders/orders_results.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2013 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,11 +23,11 @@ $form_review = empty($_GET['review']) ? 0 : 1; $thisauth = acl_check('patients', 'med'); if (!$thisauth) die(xl('Not authorized')); -// Check authorization for panding review. +// Check authorization for pending review. $reviewauth = acl_check('patients', 'sign'); if ($form_review and !$reviewauth and !$thisauth) die(xl('Not authorized')); -// Set pid for panding review. +// Set pid for pending review. if ($_GET['set_pid'] && $form_review) { require_once("$srcdir/pid.inc"); require_once("$srcdir/patient.inc"); @@ -44,6 +44,11 @@ if ($_GET['set_pid'] && $form_review) { if (!$form_batch && !$pid && !$form_review) die(xl('There is no current patient')); +function oresRawData($name, $index) { + $s = isset($_POST[$name][$index]) ? $_POST[$name][$index] : ''; + return trim(strip_escape_custom($s)); +} + function oresData($name, $index) { $s = isset($_POST[$name][$index]) ? $_POST[$name][$index] : ''; return formDataCore($s, true); @@ -56,14 +61,12 @@ function QuotedOrNull($fld) { $current_report_id = 0; -if ($_POST['form_submit']) { - if($_POST['form_line']!='') { - foreach ($_POST['form_line'] as $lino => $line_value) { - list($order_id, $restyp_id, $report_id, $result_id) = explode(':', $line_value); +if ($_POST['form_submit'] && !empty($_POST['form_line'])) { + foreach ($_POST['form_line'] as $lino => $line_value) { + list($order_id, $order_seq, $report_id, $result_id) = explode(':', $line_value); - // Not using xl() here because these errors are for debugging only. + // Not using xl() here because this is for debugging only. if (empty($order_id)) die("Order ID is missing from line $lino."); - if (empty($restyp_id)) die("Result type ID is missing from line $lino."); // If report data exists for this line, save it. $date_report = oresData("form_date_report", $lino); @@ -71,6 +74,7 @@ if ($_POST['form_submit']) { if (!empty($date_report)) { $sets = "procedure_order_id = '$order_id', " . + "procedure_order_seq = '$order_seq', " . "date_report = '$date_report', " . "date_collected = " . QuotedOrNull(oresData("form_date_collected", $lino)) . ", " . "specimen_num = '" . oresData("form_specimen_num", $lino) . "', " . @@ -95,14 +99,24 @@ if ($_POST['form_submit']) { // If there's a report, save corresponding results. if ($current_report_id) { + // Comments and notes will be combined into one comments field. + $form_comments = oresRawData("form_comments", $lino); + $form_comments = str_replace("\n" ,'~' , $form_comments); + $form_comments = str_replace("\r" ,'' , $form_comments); + $form_notes = oresRawData("form_notes", $lino); + if ($form_notes !== '') { + $form_comments .= "\n" . $form_notes; + } $sets = "procedure_report_id = '$current_report_id', " . - "procedure_type_id = '$restyp_id', " . + "result_code = '" . oresData("form_result_code", $lino) . "', " . + "result_text = '" . oresData("form_result_text", $lino) . "', " . "abnormal = '" . oresData("form_result_abnormal", $lino) . "', " . "result = '" . oresData("form_result_result", $lino) . "', " . "`range` = '" . oresData("form_result_range", $lino) . "', " . + "units = '" . oresData("form_result_units", $lino) . "', " . "facility = '" . oresData("form_facility", $lino) . "', " . - "comments = '" . oresData("form_comments", $lino) . "', " . + "comments = '" . add_escape_custom($form_comments) . "', " . "result_status = '" . oresData("form_result_status", $lino) . "'"; if ($result_id) { // result already exists sqlStatement("UPDATE procedure_result SET $sets " . @@ -112,9 +126,7 @@ if ($_POST['form_submit']) { $result_id = sqlInsert("INSERT INTO procedure_result SET $sets"); } } - } // end foreach - } } ?> @@ -265,15 +277,6 @@ function validate(f) { } var abnstat = f['form_result_abnormal['+lino+']'].selectedIndex > 0; if (abnstat && !prDateRequired(rlino)) return false; - /******************************************************************* - var resstat = f['form_result_status['+lino+']'].selectedIndex > 0; - if (resstat != abnstat) { - alert(''); - if (f['form_result_abnormal['+lino+']'].focus) - f['form_result_abnormal['+lino+']'].focus(); - return false; - } - *******************************************************************/ } top.restoreSession(); return true; @@ -349,90 +352,72 @@ if ($form_batch) { - + - + - - + + + = '$form_from_date' AND po.date_ordered <= '$form_to_date' " . "AND $where " . - "ORDER BY pd.lname, pd.fname, pd.mname, po.patient_id, $orderby"); + "ORDER BY pd.lname, pd.fname, pd.mname, po.patient_id, $orderby"; } else { - $res = sqlStatement("SELECT $selects " . - "FROM procedure_order AS po $joins " . + $query = "SELECT $selects " . + "FROM procedure_order AS po " . + "$joins " . "WHERE po.patient_id = '$pid' AND $where " . - "ORDER BY $orderby"); + "ORDER BY $orderby"; } +$res = sqlStatement($query); + $lastpoid = -1; +$lastpcid = -1; $lastprid = -1; $encount = 0; $lino = 0; @@ -441,40 +426,15 @@ $lastrcn = ''; $facilities = array(); while ($row = sqlFetchArray($res)) { - $order_id = empty($row['procedure_order_id' ]) ? 0 : ($row['procedure_order_id' ] + 0); - $restyp_id = empty($row['result_type_id']) ? 0 : ($row['result_type_id' ] + 0); - $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0); - $result_id = empty($row['procedure_result_id']) ? 0 : ($row['procedure_result_id'] + 0); - - /******************************************************************* - $result_name = ''; - if (!empty($row['result_category_name'])) $result_name = $row['result_category_name'] . ' / '; - if (!empty($row['result_name'])) $result_name .= $row['result_name']; - *******************************************************************/ - $result_category_name = empty($row['result_category_name']) ? '--' : $row['result_category_name']; - $result_name = empty($row['result_name' ]) ? '' : $row['result_name']; - - $date_report = empty($row['date_report' ]) ? '' : $row['date_report']; - $date_collected = empty($row['date_collected' ]) ? '' : substr($row['date_collected'], 0, 16); - $specimen_num = empty($row['specimen_num' ]) ? '' : $row['specimen_num']; - $report_status = empty($row['report_status' ]) ? '' : $row['report_status']; - $result_abnormal = empty($row['abnormal' ]) ? '' : $row['abnormal']; - $result_result = empty($row['result' ]) ? '' : $row['result']; - $result_unit = empty($row['units' ]) ? '' : $row['units']; - $facility = empty($row['facility' ]) ? '' : $row['facility']; - $comments = empty($row['comments' ]) ? '' : $row['comments']; - $result_range = empty($row['range' ]) ? $row['result_def_range'] : $row['range']; - $result_status = empty($row['result_status' ]) ? '' : $row['result_status']; - $result_def_units = empty($row['result_def_units']) ? '' : $row['result_def_units']; - $units_name = empty($row['units_name' ]) ? xl('Units not defined') : $row['units_name']; - - $review_status = empty($row['review_status' ]) ? 'received' : $row['review_status']; - - //echo $facility. "
    "; - if($facility <> "" && !in_array($facility, $facilities)) - { - $facilities[] = $facility; - } + $order_type_id = empty($row['order_type_id' ]) ? 0 : ($row['order_type_id' ] + 0); + $order_id = empty($row['procedure_order_id' ]) ? 0 : ($row['procedure_order_id' ] + 0); + $order_seq = empty($row['procedure_order_seq']) ? 0 : ($row['procedure_order_seq'] + 0); + $report_id = empty($row['procedure_report_id']) ? 0 : ($row['procedure_report_id'] + 0); + $date_report = empty($row['date_report' ]) ? '' : $row['date_report']; + $date_collected = empty($row['date_collected' ]) ? '' : substr($row['date_collected'], 0, 16); + $specimen_num = empty($row['specimen_num' ]) ? '' : $row['specimen_num']; + $report_status = empty($row['report_status' ]) ? '' : $row['report_status']; + $review_status = empty($row['review_status' ]) ? 'received' : $row['review_status']; // skip report_status = receive to make sure do not show the report before it reviewed and sign off by Physicians if ($form_review) { @@ -484,192 +444,264 @@ while ($row = sqlFetchArray($res)) { if ($review_status == "received") continue; } - if ($lastpoid != $order_id) { - ++$encount; - $lastrcn = ''; - } - $bgcolor = "#" . (($encount & 1) ? "ddddff" : "ffdddd"); - - echo " \n"; - - // If this starts a new order, display its date and procedure name, - // otherwise empty space. - // - if ($lastpoid != $order_id) { - if ($form_batch) { - $tmp = $row['lname']; - if ($row['fname'] || $row['mname']) - $tmp .= ', ' . $row['fname'] . ' ' . $row['mname']; - echo " " . htmlentities($tmp) . "\n"; - echo " " . htmlentities($row['pubpid']) . "\n"; + $selects = "pt2.procedure_type, pt2.procedure_code, pt2.units AS pt2_units, " . + "pt2.range AS pt2_range, pt2.procedure_type_id AS procedure_type_id, " . + "pt2.name AS name, pt2.description, pt2.seq AS seq, " . + "ps.procedure_result_id, ps.result_code AS result_code, ps.result_text, ps.abnormal, ps.result, " . + "ps.range, ps.result_status, ps.facility, ps.comments, ps.units, ps.comments"; + + // procedure_type_id for order: + $pt2cond = "pt2.parent = $order_type_id AND " . + "(pt2.procedure_type LIKE 'res%' OR pt2.procedure_type LIKE 'rec%')"; + + // pr.procedure_report_id or 0 if none: + $pscond = "ps.procedure_report_id = $report_id"; + + $joincond = "ps.result_code = pt2.procedure_code"; + + // This union emulates a full outer join. The idea is to pick up all + // result types defined for this order type, as well as any actual + // results that do not have a matching result type. + $query = "(SELECT $selects FROM procedure_type AS pt2 " . + "LEFT JOIN procedure_result AS ps ON $pscond AND $joincond " . + "WHERE $pt2cond" . + ") UNION (" . + "SELECT $selects FROM procedure_result AS ps " . + "LEFT JOIN procedure_type AS pt2 ON $pt2cond AND $joincond " . + "WHERE $pscond) " . + "ORDER BY seq, name, procedure_type_id, result_code"; + + $rres = sqlStatement($query); + while ($rrow = sqlFetchArray($rres)) { + $restyp_code = empty($rrow['procedure_code' ]) ? '' : $rrow['procedure_code']; + $restyp_type = empty($rrow['procedure_type' ]) ? '' : $rrow['procedure_type']; + $restyp_name = empty($rrow['name' ]) ? '' : $rrow['name']; + $restyp_units = empty($rrow['pt2_units' ]) ? '' : $rrow['pt2_units']; + $restyp_range = empty($rrow['pt2_range' ]) ? '' : $rrow['pt2_range']; + + $result_id = empty($rrow['procedure_result_id']) ? 0 : ($rrow['procedure_result_id'] + 0); + $result_code = empty($rrow['result_code' ]) ? $restyp_code : $rrow['result_code']; + $result_text = empty($rrow['result_text' ]) ? $restyp_name : $rrow['result_text']; + $result_abnormal = empty($rrow['abnormal' ]) ? '' : $rrow['abnormal']; + $result_result = empty($rrow['result' ]) ? '' : $rrow['result']; + $result_units = empty($rrow['units' ]) ? $restyp_units : $rrow['units']; + $result_facility = empty($rrow['facility' ]) ? '' : $rrow['facility']; + $result_comments = empty($rrow['comments' ]) ? '' : $rrow['comments']; + $result_range = empty($rrow['range' ]) ? $restyp_range : $rrow['range']; + $result_status = empty($rrow['result_status' ]) ? '' : $rrow['result_status']; + + // If there is more than one line of comments, everything after that is "notes". + $result_notes = ''; + $i = strpos($result_comments, "\n"); + if ($i !== FALSE) { + $result_notes = trim(substr($result_comments, $i + 1)); + $result_comments = substr($result_comments, 0, $i); + } + $result_comments = trim($result_comments); + + if($result_facility <> "" && !in_array($result_facility, $facilities)) { + $facilities[] = $result_facility; + } + + if ($lastpoid != $order_id || $lastpcid != $order_seq) { + ++$encount; + $lastrcn = ''; + } + $bgcolor = "#" . (($encount & 1) ? "ddddff" : "ffdddd"); + + echo " \n"; + + // Generate first 2 columns. + if ($lastpoid != $order_id || $lastpcid != $order_seq) { + $lastprid = -1; // force report fields on first line of each procedure + if ($form_batch) { + if ($lastpoid != $order_id) { + $tmp = $row['lname']; + if ($row['fname'] || $row['mname']) + $tmp .= ', ' . $row['fname'] . ' ' . $row['mname']; + echo " " . htmlentities($tmp) . "\n"; + echo " " . htmlentities($row['pubpid']) . "\n"; + } + else { + echo "  "; + } + } + else { + if ($lastpoid != $order_id) { + echo " " . $row['date_ordered'] . "\n"; + } + else { + echo "  "; + } + echo " " . htmlentities($row['procedure_name']) . "\n"; + } } else { - echo " " . $row['date_ordered'] . "\n"; - echo " " . htmlentities($row['procedure_name']) . "\n"; + echo "  "; } - $lastprid = -1; // force report fields on first line of each order - } else { - echo "  "; - } - // Include a hidden form field containing all IDs for this line. - echo ""; - echo "\n"; - - // If this starts a new report or a new order, generate the report form - // fields. In the case of a new order with no report yet, the fields will - // have their blank/default values, and form_line (above) will indicate a - // report ID of 0. - // - // TBD: Also generate default report fields and another set of results if - // the previous report is marked "Preliminary". - // - if ($report_id != $lastprid) { + + // If this starts a new report or a new order, generate the report form + // fields. In the case of a new order with no report yet, the fields will + // have their blank/default values, and form_line (above) will indicate a + // report ID of 0. + // + // TBD: Also generate default report fields and another set of results if + // the previous report is marked "Preliminary". + // + if ($report_id != $lastprid) { + echo " "; + echo ""; + echo "?"; + echo "\n"; + + echo " "; + echo ""; + echo "?"; + echo "\n"; + + echo " "; + echo ""; + echo "\n"; + + echo " "; + echo generate_select_list("form_report_status[$lino]", 'proc_rep_status', + $report_status, xl('Report Status'), ' ', 'cellselect'); + echo "\n"; + } + else { + echo "  \n"; + } + echo " "; - echo ""; - echo "?"; + echo "" . + "\n"; + + echo " " . + ""; + "\n"; + + echo " "; + echo generate_select_list("form_result_abnormal[$lino]", 'proc_res_abnormal', + $result_abnormal, xl('Indicates abnormality'), ' ', 'cellselect'); echo "\n"; - echo " "; - echo ""; - echo "?"; + echo " "; + if ($result_units == 'bool') { + echo " --"; + } + else { + echo ""; + } echo "\n"; echo " "; - echo ""; echo "\n"; echo " "; - echo generate_select_list("form_report_status[$lino]", 'proc_rep_status', - $report_status, xl('Report Status'), ' ', 'cellselect'); + echo ""; + // Include a hidden form field containing all IDs for this line. + echo ""; echo "\n"; - } - else { - echo "  \n"; - } - if ($result_category_name != $lastrcn) { - echo " "; - echo htmlentities($result_category_name); + echo " "; + echo " ? "; echo "\n"; - $lastrcn = $result_category_name; - } - else { - echo "  \n"; - } - echo " " . - htmlentities($result_name) . "\n"; - - echo " "; - echo generate_select_list("form_result_abnormal[$lino]", 'proc_res_abnormal', - $result_abnormal, xl('Indicates abnormality'), ' ', 'cellselect'); - echo "\n"; - - echo " "; - if ($result_def_units == 'bool') { - // echo generate_select_list("form_result_result[$lino]", 'proc_res_bool', - // $result_result, $units_name, ' ', 'cellselect'); - echo " --"; - } - else { - echo ""; + echo " \n"; + + // Create a floating div for additional attributes of this result. + $extra_html .= "
    \n" . + "\n" . + "\n" . + "" . + "\n" . + "" . + "\n" . + "" . + "\n" . + "" . + "\n" . + "
    " . + htmlspecialchars($result_text) . + "
    " . xlt('Status') . ": " . generate_select_list("form_result_status[$lino]", 'proc_res_status', + $result_status, xl('Result Status'), '') . "
    " . xlt('Facility') . ":
    " . xlt('Comments') . ":
    " . xlt('Notes') . ":
    \n" . + "

    \n". + "
    "; + + $lastpoid = $order_id; + $lastpcid = $order_seq; + $lastprid = $report_id; + ++$lino; } - echo "\n"; - - echo ""; - echo $result_unit; - echo ""; - - - echo " "; - echo ""; - echo "\n"; - - echo " \n"; - - // Create a floating div for additional attributes of this result. - $extra_html .= "
    \n" . - "\n" . - "\n" . - "" . - "\n" . - "" . - "\n" . - "" . - "\n" . - "
    " . - // xl('Additional Attributes') . - htmlspecialchars($result_name) . - "
    " . xl('Status') . ": " . generate_select_list("form_result_status[$lino]", 'proc_res_status', - $result_status, xl('Result Status'), '') . "
    " . xl('Facility') . ":
    " . xl('Comments') . ":
    \n" . - "

    \n". - "
    "; - - $lastpoid = $order_id; - $lastprid = $report_id; - ++$lino; - } -// display facility information -$extra_html .= ""; -$extra_html .= ""; -foreach($facilities as $facilityID) -{ - foreach(explode(":", $facilityID) as $lab_facility) - { - - $facility_array = getFacilityInfo($lab_facility); - if($facility_array) - { - $extra_html .= - "" . - "" . - "" . - "" . - ""; - } + +if (!empty($facilities)) { + // display facility information + $extra_html .= "
    ". xl('Performing Laboratory Facility') . "

    ". htmlspecialchars($facility_array['fname']) . " " . htmlspecialchars($facility_array['lname']) . ", " . htmlspecialchars($facility_array['title']). "
    ". htmlspecialchars($facility_array['organization']) . "
    ". htmlspecialchars($facility_array['street']) . " " .htmlspecialchars($facility_array['city']) . " " . htmlspecialchars($facility_array['state']) . "
    ". htmlspecialchars(formatPhone($facility_array['phone'])) . "
    "; + $extra_html .= ""; + foreach($facilities as $facilityID) { + foreach(explode(":", $facilityID) as $lab_facility) { + $facility_array = getFacilityInfo($lab_facility); + if($facility_array) { + $extra_html .= + "" . + "" . + "" . + "" . + ""; + } } + } + $extra_html .= "
    ". xl('Performing Laboratory Facility') . "

    ". htmlspecialchars($facility_array['fname']) . " " . htmlspecialchars($facility_array['lname']) . ", " . htmlspecialchars($facility_array['title']). "
    ". htmlspecialchars($facility_array['organization']) . "
    ". htmlspecialchars($facility_array['street']) . " " .htmlspecialchars($facility_array['city']) . " " . htmlspecialchars($facility_array['state']) . "
    ". htmlspecialchars(formatPhone($facility_array['phone'])) . "
    \n"; } - -$extra_html .= "\n"; ?> +

    diff --git a/interface/orders/pending_orders.php b/interface/orders/pending_orders.php index a355532fa..4fa6856f6 100644 --- a/interface/orders/pending_orders.php +++ b/interface/orders/pending_orders.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2013 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,7 +27,6 @@ function thisLineItem($row) { echo '"' . addslashes($row['pubpid' ]) . '",'; echo '"' . addslashes(oeFormatShortDate($row['date_ordered' ])) . '",'; echo '"' . addslashes($row['organization' ]) . '",'; - echo '"' . addslashes($row['procedure_name']) . '",'; echo '"' . addslashes($provname ) . '",'; echo '"' . addslashes($row['priority_name' ]) . '",'; echo '"' . addslashes($row['status_name' ]) . '"' . "\n"; @@ -39,7 +38,6 @@ function thisLineItem($row) { - @@ -125,7 +123,6 @@ else { // not export - @@ -142,9 +139,8 @@ if ($_POST['form_refresh'] || $_POST['form_csvexport']) { $query = "SELECT po.patient_id, po.date_ordered, " . "pd.pubpid, " . "CONCAT(pd.lname, ', ', pd.fname, ' ', pd.mname) AS patient_name, " . - "pt1.name AS procedure_name, " . "u1.lname AS provider_lname, u1.fname AS provider_fname, u1.mname AS provider_mname, " . - "u2.organization, " . + "pp.name AS organization, " . "lop.title AS priority_name, " . "los.title AS status_name, " . "pr.procedure_report_id, pr.date_report, pr.report_status " . @@ -152,8 +148,7 @@ if ($_POST['form_refresh'] || $_POST['form_csvexport']) { "JOIN form_encounter AS fe ON fe.pid = po.patient_id AND fe.encounter = po.encounter_id " . "JOIN patient_data AS pd ON pd.pid = po.patient_id " . "LEFT JOIN users AS u1 ON u1.id = po.provider_id " . - "LEFT JOIN procedure_type AS pt1 ON pt1.procedure_type_id = po.procedure_type_id " . - "LEFT JOIN users AS u2 ON u2.id = pt1.lab_id " . + "LEFT JOIN procedure_providers AS pp ON pp.ppid = po.lab_id " . "LEFT JOIN list_options AS lop ON lop.list_id = 'ord_priority' AND lop.option_id = po.order_priority " . "LEFT JOIN list_options AS los ON los.list_id = 'ord_status' AND los.option_id = po.order_status " . "LEFT JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id " . diff --git a/interface/orders/procedure_provider_edit.php b/interface/orders/procedure_provider_edit.php new file mode 100644 index 000000000..4b9d51327 --- /dev/null +++ b/interface/orders/procedure_provider_edit.php @@ -0,0 +1,277 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +$sanitize_all_escapes = true; +$fake_register_globals =false; + +require_once("../globals.php"); +require_once("$srcdir/acl.inc"); +require_once("$srcdir/options.inc.php"); +require_once("$srcdir/formdata.inc.php"); +require_once("$srcdir/htmlspecialchars.inc.php"); + +// Collect user id if editing entry +$ppid = $_REQUEST['ppid']; + +$info_msg = ""; + +function invalue($name) { + $fld = add_escape_custom(trim($_POST[$name])); + return "'$fld'"; +} + +?> + + +<?php echo $ppid ? xlt('Edit') : xlt('Add New') ?> <?php echo xlt('Procedure Provider'); ?> + + + + + + + + + + +\n"; + if ($info_msg) echo " alert('" . addslashes($info_msg) . "');\n"; + echo " window.close();\n"; + echo " if (opener.refreshme) opener.refreshme();\n"; + echo "\n"; + exit(); +} + +if ($ppid) { + $row = sqlQuery("SELECT * FROM procedure_providers WHERE ppid = ?", array($ppid)); +} +?> + +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    : + ' + style='width:100%' class='inputtext' /> +
    : + ' class='inputtext' /> +
    : + : + ' + title='' + class='inputtext' /> +  : + ' + title='' + class='inputtext' /> +
    : + : + ' + title='' + class='inputtext' /> +  : + ' + title='' + class='inputtext' /> +
    : + +
    : + +
    : + ' class='inputtext' /> +
    : + ' class='inputtext' /> +
    : + ' class='inputtext' /> +
    : + ' class='inputtext' /> +
    : + ' class='inputtext' /> +
    : + +
    + +
    + +' /> + + +  +' style='color:red' /> + + +  +' onclick='window.close()' /> +

    + +
    +
    + + diff --git a/interface/orders/procedure_provider_list.php b/interface/orders/procedure_provider_list.php new file mode 100644 index 000000000..a806d7fa1 --- /dev/null +++ b/interface/orders/procedure_provider_list.php @@ -0,0 +1,122 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +$sanitize_all_escapes = true; +$fake_register_globals = false; + +require_once("../globals.php"); +require_once("$srcdir/acl.inc"); +require_once("$srcdir/formdata.inc.php"); +require_once("$srcdir/options.inc.php"); +require_once("$srcdir/htmlspecialchars.inc.php"); + +$popup = empty($_GET['popup']) ? 0 : 1; + +$form_name = trim($_POST['form_name']); + +$query = "SELECT pp.* FROM procedure_providers AS pp"; +$query .= " ORDER BY pp.name"; +$res = sqlStatement($query); +?> + + + + + +<?php echo xlt('Procedure Providers'); ?> + + + + + + + + + + + + +
    +
    + + + + + +
    + + + + + + + + +\n"; + } + else { + $trTitle = $displayName . " (" . xl("Not Allowed to Edit") . ")"; + echo " \n"; + } + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + } +?> +
    '>
    " . text($row['name'] ) . "" . text($row['npi'] ) . "" . text($row['protocol']) . "
    + + + diff --git a/interface/orders/procedure_stats.php b/interface/orders/procedure_stats.php index 5bddebc91..2776054dd 100644 --- a/interface/orders/procedure_stats.php +++ b/interface/orders/procedure_stats.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2013 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -395,24 +395,27 @@ foreach (array(1 => 'Screen', 2 => 'Printer', 3 => 'Export File') as $key => $va "po.provider_id, pd.regdate, " . "pd.sex, pd.DOB, pd.lname, pd.fname, pd.mname, " . "pd.contrastart, pd.referral_source$pd_fields, " . - "ps.abnormal, ps.procedure_type_id AS result_type, " . - "pto.name AS order_name, ptr.name AS result_name, ptr.related_code " . + "ps.abnormal, " . + // "pto.name AS order_name, ptr.name AS result_name, ptr.related_code " . + "pc.procedure_name AS order_name, ptr.name AS result_name, ptr.related_code " . "FROM procedure_order AS po " . "JOIN form_encounter AS fe ON fe.pid = po.patient_id AND fe.encounter = po.encounter_id " . "JOIN patient_data AS pd ON pd.pid = po.patient_id $sexcond" . + "JOIN procedure_order_code AS pc ON pc.procedure_order_id = po.procedure_order_id " . "JOIN procedure_report AS pr ON pr.procedure_order_id = po.procedure_order_id " . + "AND pr.procedure_order_seq = pc.procedure_order_seq " . "AND pr.date_report IS NOT NULL " . "JOIN procedure_result AS ps ON ps.procedure_report_id = pr.procedure_report_id " . "AND ps.result_status = 'final' " . - "JOIN procedure_type AS pto ON pto.procedure_type_id = po.procedure_type_id " . - "JOIN procedure_type AS ptr ON ptr.procedure_type_id = ps.procedure_type_id " . - "AND ptr.procedure_type NOT LIKE 'rec' " . + // "JOIN procedure_type AS pto ON pto.procedure_type_id = pc.procedure_type_id " . + "JOIN procedure_type AS ptr ON ptr.lab_id = po.lab_id AND ptr.procedure_code = ps.result_code " . + "AND ptr.procedure_type LIKE 'res%' " . "WHERE po.date_ordered IS NOT NULL AND po.date_ordered >= '$from_date' " . "AND po.date_ordered <= '$to_date' "; if ($form_facility) { $query .= "AND fe.facility_id = '$form_facility' "; } - $query .= "ORDER BY fe.pid, fe.encounter, ps.procedure_type_id"; // needed? + $query .= "ORDER BY fe.pid, fe.encounter, ps.result_code"; // needed? $res = sqlStatement($query); diff --git a/interface/orders/qoe.inc.php b/interface/orders/qoe.inc.php new file mode 100644 index 000000000..6fb629ceb --- /dev/null +++ b/interface/orders/qoe.inc.php @@ -0,0 +1,195 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +/** + * Generate HTML for the QOE form suitable for insertion into a
    . + * This HTML may contain single quotes but not unescaped double quotes. + * + * @param integer $ptid Value matching a procedure_type_id in the procedure_types table. + * @param integer $orderid Procedure order ID, if there is an existing order. + * @param integer $dbseq Procedure order item sequence number, if there is an existing procedure. + * @param string $formseq Zero-relative occurrence number in the form. + * @return string The generated HTML. + */ +function generate_qoe_html($ptid=0, $orderid=0, $dbseq=0, $formseq=0) { + global $rootdir, $qoe_init_javascript; + + $s = ""; + $qoe_init_javascript = ''; + $prefix = 'ans' . $formseq . '_'; + + if (empty($ptid)) return $s; + + $s .= ""; + + // Get all the questions for the given procedure order type. + $qres = sqlStatement("SELECT " . + "q.question_code, q.question_text, q.options, q.required, q.maxsize, " . + "q.fldtype, q.tips " . + "FROM procedure_type AS t " . + "JOIN procedure_questions AS q ON q.lab_id = t.lab_id " . + "AND q.procedure_code = t.procedure_code AND q.activity = 1 " . + "WHERE t.procedure_type_id = ? " . + "ORDER BY q.seq, q.question_text", array($ptid)); + + while ($qrow = sqlFetchArray($qres)) { + $options = trim($qrow['options']); + $qfieldid = $prefix . attr(trim($qrow['question_code'])); + $fldtype = $qrow['fldtype']; + $maxsize = 0 + $qrow['maxsize']; + + // Get answer value(s) to this question, if any. + $answers = array(); + if ($orderid && $dbseq > 0) { + $ares = sqlStatement("SELECT answer FROM procedure_answers WHERE " . + "procedure_order_id = ? AND procedure_order_seq = ? AND question_code = ? " . + "ORDER BY answer_seq", array($orderid, $dbseq, $qrow['question_code'])); + while ($arow = sqlFetchArray($ares)) { + $answers[] = $arow['answer']; + } + } + + $s .= ""; + $s .= " + + +
    "; + $s .= ""; + + if ($fldtype == 'T') { + // Text Field. + $s .= ""; + $qoe_init_javascript .= " Calendar.setup({inputField:'$qfieldid', ifFormat:'%Y-%m-%d', button:'img_$qfieldid'});"; + } + + else if ($fldtype == 'G') { + // Gestational age in weeks and days. + $currweeks = -1; + $currdays = -1; + if (!empty($answers)) { + $currweeks = intval($answers[0] / 7); + $currdays = $answers[0] % 7; + } + $s .= ""; + } + *****************************************************************/ + + else if ($fldtype == 'M') { + // List of checkboxes. + $a = explode(';', $qrow['options']); + $i = 0; + foreach ($a as $aval) { + list($desc, $code) = explode(':', $aval); + if (empty($code)) $code = $desc; + if ($i) $s .= "
    "; + $s .= " diff --git a/interface/orders/receive_hl7_results.inc.php b/interface/orders/receive_hl7_results.inc.php new file mode 100644 index 000000000..b45a80b6b --- /dev/null +++ b/interface/orders/receive_hl7_results.inc.php @@ -0,0 +1,369 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +function rhl7InsertRow(&$arr, $tablename) { + if (empty($arr)) return; + + // echo "\n"; + + $query = "INSERT INTO $tablename SET"; + $binds = array(); + $sep = ''; + foreach ($arr as $key => $value) { + $query .= "$sep `$key` = ?"; + $sep = ','; + $binds[] = $value; + } + $arr = array(); + return sqlInsert($query, $binds); +} + +function rhl7FlushResult(&$ares) { + return rhl7InsertRow($ares, 'procedure_result'); +} + +function rhl7FlushReport(&$arep) { + return rhl7InsertRow($arep, 'procedure_report'); +} + +function rhl7Text($s) { + $s = str_replace('\\S\\' ,'^' , $s); + $s = str_replace('\\F\\' ,'|' , $s); + $s = str_replace('\\R\\' ,'~' , $s); + $s = str_replace('\\T\\' ,'&' , $s); + $s = str_replace('\\X0d\\',"\r", $s); + $s = str_replace('\\E\\' ,'\\', $s); + return $s; +} + +function rhl7DateTime($s) { + if (empty($s)) return '0000-00-00 00:00:00'; + $ret = substr($s, 0, 4) . '-' . substr($s, 4, 2) . '-' . substr($s, 6, 2); + if (strlen($s) > 8) $ret .= ' ' . substr($s, 8, 2) . ':' . substr($s, 10, 2) . ':'; + if (strlen($s) > 12) { + $ret .= substr($s, 12, 2); + } else { + $ret .= '00'; + } + return $ret; +} + +function rhl7Abnormal($s) { + if ($s == '' ) return 'no'; + if ($s == 'A' ) return 'yes'; + if ($s == 'H' ) return 'high'; + if ($s == 'L' ) return 'low'; + if ($s == 'HH') return 'vhigh'; + if ($s == 'LL') return 'vlow'; + return rhl7Text($s); +} + +function rhl7ReportStatus($s) { + if ($s == 'F') return 'final'; + if ($s == 'P') return 'prelim'; + if ($s == 'C') return 'correct'; + return rhl7Text($s); +} + +/** + * Parse and save. + * + * @param string &$pprow A row from the procedure_providers table. + * @param string &$hl7 The input HL7 text. + * @return string Error text, or empty if no errors. + */ +function receive_hl7_results(&$pprow, &$hl7) { + if (substr($hl7, 0, 3) != 'MSH') { + return xl('Input does not begin with a MSH segment'); + } + + // End-of-line delimiter for text in procedure_result.comments + $commentdelim = "\n"; + + $today = time(); + + $in_message_id = ''; + $in_ssn = ''; + $in_dob = ''; + $in_lname = ''; + $in_fname = ''; + $in_orderid = 0; + $in_procedure_code = ''; + $in_report_status = ''; + $in_encounter = 0; + + $porow = false; + $pcrow = false; + $procedure_report_id = 0; + $arep = array(); // holding area for OBR and its NTE data + $ares = array(); // holding area for OBX and its NTE data + + // This is so we know where we are if a segment like NTE that can appear in + // different places is encountered. + $context = ''; + + // Delimiters + $d0 = "\r"; + $d1 = substr($hl7, 3, 1); // typically | + $d2 = substr($hl7, 4, 1); // typically ^ + $d3 = substr($hl7, 5, 1); // typically ~ + + $segs = explode($d0, $hl7); + + foreach ($segs as $seg) { + if (empty($seg)) continue; + + $a = explode($d1, $seg); + + if ($a[0] == 'MSH') { + $context = $a[0]; + if ($a[8] != 'ORU^R01') { + return xl('Message type') . " '${a[8]}' " . xl('does not seem valid'); + } + $in_message_id = $a[9]; + } + + else if ($a[0] == 'PID') { + $context = $a[0]; + rhl7FlushResult($ares); + // Next line will do something only if there was a report with no results. + rhl7FlushReport($arep); + $in_ssn = $a[4]; + $in_dob = $a[7]; // yyyymmdd format + $tmp = explode($d2, $a[5]); + $in_lname = $tmp[0]; + $in_fname = $tmp[1]; + } + + else if ($a[0] == 'PV1') { + // Save placer encounter number if present. + if (!empty($a[19])) { + $tmp = explode($d2, $a[19]); + $in_encounter = intval($tmp[0]); + } + } + + else if ($a[0] == 'ORC') { + $context = $a[0]; + rhl7FlushResult($ares); + // Next line will do something only if there was a report with no results. + rhl7FlushReport($arep); + $porow = false; + $pcrow = false; + if ($a[2]) $in_orderid = intval($a[2]); + } + + else if ($a[0] == 'NTE' && $context == 'ORC') { + // TBD? Is this ever used? + } + + else if ($a[0] == 'OBR') { + $context = $a[0]; + rhl7FlushResult($ares); + // Next line will do something only if there was a report with no results. + rhl7FlushReport($arep); + $procedure_report_id = 0; + if ($a[2]) $in_orderid = intval($a[2]); + $tmp = explode($d2, $a[4]); + $in_procedure_code = $tmp[0]; + $in_procedure_name = $tmp[1]; + $in_report_status = rhl7ReportStatus($a[25]); + if (empty($porow)) { + $porow = sqlQuery("SELECT * FROM procedure_order WHERE " . + "procedure_order_id = ?", array($in_orderid)); + // The order must already exist. Currently we do not handle electronic + // results returned for manual orders. + if (empty($porow)) { + return xl('Procedure order') . " '$in_orderid' " . xl('was not found'); + } + if ($in_encounter) { + if ($porow['encounter_id'] != $in_encounter) { + return xl('Encounter ID') . + " '" . $porow['encounter_id'] . "' " . + xl('for OBR placer order number') . + " '$in_orderid' " . + xl('does not match the PV1 encounter number') . + " '$in_encounter'"; + } + } + else { + // They did not return an encounter number to verify, so more checking + // might be done here to make sure the patient seems to match. + } + } + // Find the order line item (procedure code) that matches this result. + $pcquery = "SELECT pc.* FROM procedure_order_code AS pc " . + "WHERE pc.procedure_order_id = ? AND pc.procedure_code = ? " . + "ORDER BY procedure_order_seq LIMIT 1"; + $pcrow = sqlQuery($pcquery, array($in_orderid, $in_procedure_code)); + if (empty($pcrow)) { + // There is no matching procedure in the order, so it must have been + // added after the original order was sent, either as a manual request + // from the physician or as a "reflex" from the lab. + // procedure_source = '2' indicates this. + sqlInsert("INSERT INTO procedure_order_code SET " . + "procedure_order_id = ?, " . + "procedure_code = ?, " . + "procedure_name = ?, " . + "procedure_source = '2'", + array($in_orderid, $in_procedure_code, $in_procedure_name)); + $pcrow = sqlQuery($pcquery, array($in_orderid, $in_procedure_code)); + } + $arep = array(); + $arep['procedure_order_id'] = $in_orderid; + $arep['procedure_order_seq'] = $pcrow['procedure_order_seq']; + $arep['date_collected'] = rhl7DateTime($a[7]); + $arep['date_report'] = substr(rhl7DateTime($a[22]), 0, 10); + $arep['report_status'] = $in_report_status; + $arep['report_notes'] = ''; + } + + else if ($a[0] == 'NTE' && $context == 'OBR') { + $arep['report_notes'] .= rhl7Text($a[3]) . "\n"; + } + + else if ($a[0] == 'OBX') { + $context = $a[0]; + rhl7FlushResult($ares); + if (!$procedure_report_id) { + $procedure_report_id = rhl7FlushReport($arep); + } + $ares = array(); + $ares['procedure_report_id'] = $procedure_report_id; + // OBX-5 can be a very long string of text with "~" as line separators. + // The first line of comments is reserved for such things. + if (strlen($a[5]) > 200) { + $ares['result_data_type'] = 'L'; + $ares['result'] = ''; + $ares['comments'] = rhl7Text($a[5]) . $commentdelim; + } + else { + $ares['result_data_type'] = substr($a[2], 0, 1); // N, S or F + $ares['result'] = rhl7Text($a[5]); + $ares['comments'] = $commentdelim; + } + $tmp = explode($d2, $a[3]); + $ares['result_code'] = rhl7Text($tmp[0]); + $ares['result_text'] = rhl7Text($tmp[1]); + $ares['date'] = rhl7DateTime($a[14]); + $ares['facility'] = rhl7Text($a[15]); + $ares['units'] = rhl7Text($a[6]); + $ares['range'] = rhl7Text($a[7]); + $ares['abnormal'] = rhl7Abnormal($a[8]); // values are lab dependent + $ares['result_status'] = rhl7ReportStatus($a[11]); + } + + else if ($a[0] == 'NTE' && $context == 'OBX') { + $ares['comments'] .= rhl7Text($a[3]) . $commentdelim; + } + + // Add code here for any other segment types that may be present. + + else { + return xl('Segment name') . " '${a[0]}' " . xl('is misplaced or unknown'); + } + } + + rhl7FlushResult($ares); + // Next line will do something only if there was a report with no results. + rhl7FlushReport($arep); + return ''; +} + +/** + * Poll all eligible labs for new results and store them in the database. + * + * @param array &$messages Receives messages of interest. + * @return string Error text, or empty if no errors. + */ +function poll_hl7_results(&$messages) { + global $srcdir; + + $messages = array(); + $filecount = 0; + $badcount = 0; + + $ppres = sqlStatement("SELECT * FROM procedure_providers ORDER BY name"); + + while ($pprow = sqlFetchArray($ppres)) { + $protocol = $pprow['protocol']; + $remote_host = $pprow['remote_host']; + $hl7 = ''; + + if ($protocol == 'SFTP') { + ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . "$srcdir/phpseclib"); + require_once("$srcdir/phpseclib/Net/SFTP.php"); + // Compute the target path name. + $pathname = '.'; + if ($pprow['results_path']) $pathname = $pprow['results_path'] . '/' . $pathname; + // Connect to the server and enumerate files to process. + $sftp = new Net_SFTP($remote_host); + if (!$sftp->login($pprow['login'], $pprow['password'])) { + return xl('Login to remote host') . " '$remote_host' " . xl('failed'); + } + $files = $sftp->nlist($pathname); + foreach ($files as $file) { + if (substr($file, 0, 1) == '.') continue; + ++$filecount; + $hl7 = $sftp->get("$pathname/$file"); + // Archive the results file. + $prpath = $GLOBALS['OE_SITE_DIR'] . "/procedure_results"; + if (!file_exists($prpath)) mkdir($prpath); + $prpath .= '/' . $pprow['ppid']; + if (!file_exists($prpath)) mkdir($prpath); + $fh = fopen("$prpath/$file", 'w'); + if ($fh) { + fwrite($fh, $hl7); + fclose($fh); + } + else { + $messages[] = xl('File') . " '$file' " . xl('cannot be archived, ignored'); + ++$badcount; + continue; + } + // Now delete it from its ftp directory. + if (!$sftp->delete("$pathname/$file")) { + $messages[] = xl('File') . " '$file' " . xl('cannot be deleted, ignored'); + ++$badcount; + continue; + } + // Parse and process its contents. + $msg = receive_hl7_results($pprow, $hl7); + if ($msg) { + $messages[] = xl('Error processing file') . " '$file':" . $msg; + ++$badcount; + continue; + } + $messages[] = xl('New file') . " '$file' " . xl('processed successfully'); + } + } + + // TBD: Insert "else if ($protocol == '???') {...}" to support other protocols. + + } + + if ($badcount) return "$badcount " . xl('error(s) encountered from new results'); + + return ''; +} +?> diff --git a/interface/orders/single_order_results.php b/interface/orders/single_order_results.php new file mode 100644 index 000000000..ed0360beb --- /dev/null +++ b/interface/orders/single_order_results.php @@ -0,0 +1,385 @@ + +* +* LICENSE: This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* @package OpenEMR +* @author Rod Roark +*/ + +require_once("../globals.php"); +require_once("$srcdir/acl.inc"); +require_once("$srcdir/formdata.inc.php"); +require_once("$srcdir/options.inc.php"); +require_once("$srcdir/formatting.inc.php"); +require_once("../orders/lab_exchange_tools.php"); + +// Check authorization. +$thisauth = acl_check('patients', 'med'); +if (!$thisauth) die(xl('Not authorized')); + +$orderid = intval($_GET['orderid']); + +function getListItem($listid, $value) { + $lrow = sqlQuery("SELECT title FROM list_options " . + "WHERE list_id = ? AND option_id = ?", + array($listid, $value)); + $tmp = xl_list_label($lrow['title']); + if (empty($tmp)) $tmp = "($report_status)"; + return $tmp; +} + +function myCellText($s) { + if ($s === '') return ' '; + return text($s); +} + +// Check if the given string already exists in the $aNotes array. +// If not, stores it as a new entry. +// Either way, returns the corresponding key which is a small integer. +function storeNote($s) { + global $aNotes; + $key = array_search($s, $aNotes); + if ($key !== FALSE) return $key; + $key = count($aNotes); + $aNotes[$key] = $s; + return $key; +} + +if (!empty($_POST['form_sign_list'])) { + if (!acl_check('patients', 'sign')) { + die(xl('Not authorized to sign results')); + } + // When signing results we are careful to sign only those reports that were + // in the sending form. While this will usually be all the reports linked to + // the order it's possible for a new report to come in while viewing these, + // and it would be very bad to sign results that nobody has seen! + $arrSign = explode(',', $_POST['form_sign_list']); + foreach ($arrSign as $id) { + sqlStatement("UPDATE procedure_report SET " . + "review_status = 'reviewed' WHERE " . + "procedure_report_id = ?", array($id)); + } +} + +$orow = sqlQuery("SELECT " . + "po.procedure_order_id, po.date_ordered, po.diagnoses, " . + "po.order_status, po.specimen_type, " . + "pd.pubpid, pd.lname, pd.fname, pd.mname, " . + "pp.name AS labname, " . + "u.lname AS ulname, u.fname AS ufname, u.mname AS umname " . + "FROM procedure_order AS po " . + "LEFT JOIN patient_data AS pd ON pd.pid = po.patient_id " . + "LEFT JOIN procedure_providers AS pp ON pp.ppid = po.lab_id " . + "LEFT JOIN users AS u ON u.id = po.provider_id " . + "WHERE po.procedure_order_id = ?", + array($orderid)); +?> + + + + + + +<?php xl('Order Results','e'); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + 
    + + + + + + + + + + + + + + + + + + + + + + + + ''); + } + + foreach ($rrows as $rrow) { + $result_code = empty($rrow['result_code' ]) ? '' : $rrow['result_code']; + $result_text = empty($rrow['result_text' ]) ? '' : $rrow['result_text']; + $result_abnormal = empty($rrow['abnormal' ]) ? '' : $rrow['abnormal']; + $result_result = empty($rrow['result' ]) ? '' : $rrow['result']; + $result_units = empty($rrow['units' ]) ? '' : $rrow['units']; + $result_facility = empty($rrow['facility' ]) ? '' : $rrow['facility']; + $result_comments = empty($rrow['comments' ]) ? '' : $rrow['comments']; + $result_range = empty($rrow['range' ]) ? '' : $rrow['range']; + $result_status = empty($rrow['result_status' ]) ? '' : $rrow['result_status']; + + $result_comments = trim($result_comments); + $result_noteid = ' '; + if (!empty($result_comments)) { + $result_noteid = 1 + storeNote($result_comments); + } + + if ($lastpoid != $order_id || $lastpcid != $order_seq) { + ++$encount; + } + $bgcolor = "#" . (($encount & 1) ? "ddddff" : "ffdddd"); + + echo " \n"; + + if ($lastpcid != $order_seq) { + $lastprid = -1; // force report fields on first line of each procedure + echo " \n"; + } + else { + echo " "; + } + + // If this starts a new report or a new order, generate the report fields. + if ($report_id != $lastprid) { + echo " \n"; + + echo " \n"; + + echo " \n"; + + echo " \n"; + } + else { + echo " \n"; + } + + if ($result_code !== '') { + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + } + else { + echo " \n"; + } + + echo " \n"; + + $lastpoid = $order_id; + $lastpcid = $order_seq; + $lastprid = $report_id; + ++$lino; + } +} +?> + +
    " . htmlentities("$procedure_code: $procedure_name") . " "; + echo myCellText(oeFormatShortDate($date_report)); + echo ""; + echo myCellText($specimen_num); + echo ""; + echo myCellText(getListItem('proc_rep_status', $report_status)); + if ($row['review_status'] == 'reviewed') { + echo " ✓"; // unicode check mark character + } + echo ""; + echo $report_noteid; + echo " "; + echo myCellText($result_code); + echo ""; + echo myCellText($result_text); + echo ""; + echo myCellText(getListItem('proc_res_abnormal', $result_abnormal)); + echo ""; + echo myCellText($result_result); + echo ""; + echo myCellText($result_range); + echo ""; + echo myCellText($result_units); + echo ""; + echo $result_noteid; + echo " 
    + + 
    + + + \n"; + echo " \n"; + echo " \n"; + foreach ($aNotes as $key => $value) { + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + } + echo "
    +\n"; + echo "
    " . xlt('Notes') . "
    " . ($key + 1) . "" . nl2br(text($value)) . "
    \n"; +} +?> +
    + + + ' + title='' /> + +
    + + + + diff --git a/interface/orders/types.php b/interface/orders/types.php index ae536f1e1..1a4f7f96a 100644 --- a/interface/orders/types.php +++ b/interface/orders/types.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2012 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -17,6 +17,7 @@ require_once("$srcdir/formdata.inc.php"); $popup = empty($_GET['popup']) ? 0 : 1; $order = formData('order', 'G') + 0; +$labid = formData('labid', 'G') + 0; // If Save was clicked, set the result, close the window and exit. // @@ -27,11 +28,24 @@ if ($popup && $_POST['form_save']) { $name = addslashes($ptrow['name']); ?> 0) toggle(thisid); else - $.getScript('types_ajax.php?id=' + thisid + '&order='); + $.getScript('types_ajax.php?id=' + thisid + '&order=' + '&labid='); } else { recolor(); @@ -160,7 +174,7 @@ function toggle(id) { td1.parent().after('Loading...'); td1.addClass('isExpanded'); swapsign(td1, '+', '-'); - $.getScript('types_ajax.php?id=' + id + '&order='); + $.getScript('types_ajax.php?id=' + id + '&order=' + '&labid='); } } @@ -191,7 +205,7 @@ function refreshFamily(id, haskids) { } } if (haskids) - $.getScript('types_ajax.php?id=' + id + '&order='); + $.getScript('types_ajax.php?id=' + id + '&order=' + '&labid='); else recolor(); } @@ -225,7 +239,11 @@ function recolor() {

    -
    +'> diff --git a/interface/orders/types_ajax.php b/interface/orders/types_ajax.php index c9d3cd975..eee53cd7b 100644 --- a/interface/orders/types_ajax.php +++ b/interface/orders/types_ajax.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2012 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -11,6 +11,7 @@ require_once("$srcdir/formdata.inc.php"); $id = formData('id','G') + 0; $order = formData('order','G') + 0; +$labid = formData('labid','G') + 0; echo "$('#con$id').html('
    "; @@ -45,11 +46,11 @@ while ($row = sqlFetchArray($res)) { echo ""; echo $iscontainer ? "+" : '|'; echo ""; - echo $row['name'] . ""; + echo htmlspecialchars($row['name'], ENT_QUOTES) . ""; // echo ""; // - echo ""; - echo ""; + echo ""; + echo ""; echo ""; } -echo "
    "; if (substr($row['procedure_type'], 0, 3) == 'ord') { - if ($order) { + if ($order && ($labid == 0 || $row['lab_id'] == $labid)) { echo ""; @@ -63,8 +64,8 @@ while ($row = sqlFetchArray($res)) { } echo "" . $row['procedure_code'] . "" . $row['description'] . "" . htmlspecialchars($row['procedure_code'], ENT_QUOTES) . "" . htmlspecialchars($row['description'], ENT_QUOTES) . ""; echo "[" . xl('Edit') . "]"; echo " [" . xl('Add') . "]"; @@ -72,7 +73,6 @@ while ($row = sqlFetchArray($res)) { echo "
    ');\n"; // end of html argument - +echo "');\n"; echo "nextOpen();\n"; ?> diff --git a/interface/orders/types_edit.php b/interface/orders/types_edit.php index 7d1208b85..21b5a33b8 100644 --- a/interface/orders/types_edit.php +++ b/interface/orders/types_edit.php @@ -1,5 +1,5 @@ +// Copyright (C) 2010-2012 Rod Roark // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ function recursiveDelete($typeid) { ?> -<?php echo $typeid ? xl('Edit') : xl('Add New') ?> <?php xl('Order/Result Type','e'); ?> +<?php echo $typeid ? xlt('Edit') : xlt('Add New'); ?> <?php echo xlt('Order/Result Type'); ?>