Code type module improvements:
authorbradymiller <bradymiller@users.sourceforge.net>
Sat, 9 Feb 2013 08:52:36 +0000 (9 00:52 -0800)
committerbradymiller <bradymiller@users.sourceforge.net>
Tue, 12 Feb 2013 07:00:31 +0000 (11 23:00 -0800)
 -Added wrappers to the code set search module functions to ease use.
 -Full support for multiple code type searching.
 -Support of short descriptions.
 -Support for new functionality in Administration->Services.
 -Added test calls in the Testing scripts
 -Consolidated the internal/external code search methods in code_set_search() function
 -Added ability to search code sets by category

Tests/code_types/CodesTests.php
Tests/code_types/DescriptionTests.php
Tests/code_types/MainCodesTests.php [new file with mode: 0644]
Tests/code_types/MultipleTests.php [new file with mode: 0644]
Tests/code_types/SequentialTests.php
custom/code_types.inc.php
interface/forms/fee_sheet/new.php
interface/patient_file/encounter/find_code_popup.php
interface/patient_file/encounter/superbill_custom_full.php

index 5e9d402..82b580b 100644 (file)
@@ -6,13 +6,13 @@ require_once("library/htmlspecialchars.inc.php");
 require_once("library/translation.inc.php");
 require_once("custom/code_types.inc.php");
 
-function search_test($type,$string,$mode='default')
+function search_test($type,$string,$mode='default',$return_only_one=false)
 {
     echo "<ol>";
-    $res=code_set_search($type,$string,false,true,false,0,10,array(),null,$mode);
-    foreach($res->GetArray() as $code)
+    $res=code_set_search($type,$string,false,true,$return_only_one,0,10,array(),null,$mode);
+    while ($code = sqlFetchArray($res))
     {
-        echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text']."</li>";
+        echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."</li>";
     }
     echo "</ol>";
 }
@@ -21,11 +21,6 @@ function search_test($type,$string,$mode='default')
 <?php
 search_test("SNOMED-CT","","description");
 
-search_test("--ALL--","100");
-search_test("--ALL--","100","code");
-search_test("--ALL--","colon benign","description");
-
-
 search_test("CVX","1","code");
 search_test("CVX","Hep ped","description");
 
@@ -47,5 +42,10 @@ search_test("SNOMED","Hypertension","description");
 search_test("SNOMED-CT","Hypertension","description");
 search_test("SNOMED-PR","Incision Drai","description");
 
+search_test("SNOMED-PR","Incision and","default");
+
+search_test("ICD10","A01.01","default",true);
+search_test("ICD10","A01.01","code",true);
+search_test("ICD10","A01.01","description",true);
 
 ?>
index b32bfeb..9d63fcf 100644 (file)
@@ -6,20 +6,30 @@ require_once("library/htmlspecialchars.inc.php");
 require_once("library/translation.inc.php");
 require_once("custom/code_types.inc.php");
 
-function description_test($codes)
+function description_test($codes,$mode)
 {
-    $descriptions=lookup_code_descriptions($codes);
+    $descriptions=lookup_code_descriptions($codes,$mode);
     echo $descriptions."    <br>".PHP_EOL;
 }
 echo PHP_EOL;
 description_test("ICD9:401.1");
+description_test("ICD9:401.1","code_text_short");
+
 description_test("CVX:1");
+description_test("CVX:1","code_text_short");
+
 description_test("ICD10:I10");
+description_test("ICD10:I10","code_text_short");
+
 description_test("SNOMED:1201005");
-description_test("SNOMED-CT:1201005");
-description_test("SNOMED-PR:285008");
+description_test("SNOMED:1201005","code_text_short");
 
+description_test("SNOMED-CT:1201005");
+description_test("SNOMED-CT:1201005","code_text_short");
 
+description_test("SNOMED-PR:285008");
+description_test("SNOMED-PR:285008","code_text_short");
 
 description_test("ICD9:401.1;CVX:1;ICD10:I10;SNOMED:1201005;SNOMED-CT:1201005;SNOMED-PR:285008");
+description_test("ICD9:401.1;CVX:1;ICD10:I10;SNOMED:1201005;SNOMED-CT:1201005;SNOMED-PR:285008","code_text_short");
 ?>
diff --git a/Tests/code_types/MainCodesTests.php b/Tests/code_types/MainCodesTests.php
new file mode 100644 (file)
index 0000000..9bf9094
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+$GLOBALS['OE_SITE_DIR']="/var/www/openemr/sites/default";
+set_include_path(get_include_path() . PATH_SEPARATOR . "/var/www/openemr");
+require_once("library/sql.inc");
+require_once("library/htmlspecialchars.inc.php");
+require_once("library/translation.inc.php");
+require_once("custom/code_types.inc.php");
+
+function main_search_test($type,$string,$limit=20,$modes=NULL,$count=false,$category=NULL)
+{
+    echo "<ol>";
+    $res=main_code_set_search($type,$string,$limit,$category,true,$modes,$count);
+    if ($count) {
+        echo "<li>" . $res . "</li>";
+    }
+    else {
+        while ($code = sqlFetchArray($res))
+        {
+            echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."</li>";
+        }
+    }
+    echo "</ol>";
+}
+
+function return_code_info_test($type,$string,$limit=20,$modes=NULL,$count=false)
+{
+    echo "<ol>";
+    $res=return_code_information($type,$string);
+    while ($code = sqlFetchArray($res))
+    {
+        echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."</li>";
+    }
+    echo "</ol>";
+}
+
+main_search_test("ICD9","hyperchol");
+main_search_test("ICD9","401");
+main_search_test(array("ICD9"),"401");
+
+main_search_test("ICD10","hypert");
+main_search_test("ICD10","I1");
+main_search_test(array("ICD10"),"I1");
+
+main_search_test("CPT4","99");
+
+main_search_test("SNOMED","hypert");
+main_search_test("SNOMED","1201005");
+
+main_search_test("SNOMED-CT","hypert");
+main_search_test("SNOMED-CT","1201005");
+
+main_search_test("SNOMED-PR","Incision Drai");
+
+main_search_test(array("ICD9","CVX","ICD10"),"100");
+main_search_test(array("ICD9","CVX"),"100");
+main_search_test(array("CVX","ICD10"),"100");
+
+main_search_test(array("ICD9","ICD10","SNOMED"),"colon benign");
+main_search_test(array("ICD9","SNOMED"),"colon benign");
+
+main_search_test("","polio",40,NULL,false,"active");
+main_search_test("","polio",40,NULL,false,"diagnosis");
+main_search_test("","polio",40,NULL,false,"procedure");
+main_search_test("","polio",40,NULL,false,"clinical_term");
+
+main_search_test("ICD9","401",NULL,array('code','description'),true);
+main_search_test("ICD9","401",NULL,array('description','code'),true);
+main_search_test("ICD9","401",NULL,array('code'),true);
+main_search_test("ICD9","401",NULL,array('description'),true);
+main_search_test("ICD9","chol",NULL,array('code','description'),true);
+main_search_test("ICD9","chol",NULL,array('description','code'),true);
+main_search_test("ICD9","chol",NULL,array('code'),true);
+main_search_test("ICD9","chol",NULL,array('description'),true);
+
+return_code_info_test("ICD9","045.10");
+return_code_info_test("CVX","2");
+return_code_info_test("ICD10","A80.2");
+return_code_info_test("SNOMED","172672006");
+return_code_info_test("SNOMED-CT","14535005");
+return_code_info_test("SNOMED-PR","170420002");
+
+?>
diff --git a/Tests/code_types/MultipleTests.php b/Tests/code_types/MultipleTests.php
new file mode 100644 (file)
index 0000000..5a38fa4
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+$GLOBALS['OE_SITE_DIR']="/var/www/openemr/sites/default";
+set_include_path(get_include_path() . PATH_SEPARATOR . "/var/www/openemr");
+require_once("library/sql.inc");
+require_once("library/htmlspecialchars.inc.php");
+require_once("library/translation.inc.php");
+require_once("custom/code_types.inc.php");
+
+function mult_search_test($type,$string,$limit=20,$modes=NULL,$count=false)
+{
+    echo "<ol>";
+    $res=multiple_code_set_search($type,$string,$limit,$modes,$count);
+    if ($count) {
+        echo "<li>" . $res . "</li>";
+    }
+    else {
+        while ($code = sqlFetchArray($res))
+        {
+            echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."</li>";
+        }
+    }
+    echo "</ol>";
+}
+
+
+mult_search_test(array("ICD9"),"hyperchol");
+mult_search_test(array("ICD9"),"401");
+
+mult_search_test(array("ICD10"),"hypert");
+mult_search_test(array("ICD10"),"I1");
+
+mult_search_test(array("CPT4"),"99");
+
+mult_search_test(array("SNOMED"),"hypert");
+mult_search_test(array("SNOMED"),"1201005");
+
+mult_search_test(array("SNOMED-CT"),"hypert");
+mult_search_test(array("SNOMED-CT"),"1201005");
+
+mult_search_test(array("SNOMED-PR"),"Incision Drai");
+
+mult_search_test(array(),"100");
+mult_search_test(array("ICD9","CVX"),"100");
+mult_search_test(array("CVX","ICD10"),"100");
+
+mult_search_test(array(),"colon benign");
+mult_search_test(array("ICD9","SNOMED"),"colon benign");
+
+mult_search_test(array("ICD9"),"401",NULL,array('code','description'),true);
+mult_search_test(array("ICD9"),"401",NULL,array('description','code'),true);
+mult_search_test(array("ICD9"),"401",NULL,array('code'),true);
+mult_search_test(array("ICD9"),"401",NULL,array('description'),true);
+mult_search_test(array("ICD9"),"chol",NULL,array('code','description'),true);
+mult_search_test(array("ICD9"),"chol",NULL,array('description','code'),true);
+mult_search_test(array("ICD9"),"chol",NULL,array('code'),true);
+mult_search_test(array("ICD9"),"chol",NULL,array('description'),true);
+
+?>
index fd9fa4e..f1967af 100644 (file)
@@ -16,7 +16,7 @@ function seq_search_test($type,$string,$limit=20,$modes=NULL,$count=false)
     else {
         while ($code = sqlFetchArray($res))
         {
-            echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text']."</li>";
+            echo "<li>". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."</li>";
         }
     }
     echo "</ol>";
@@ -41,7 +41,6 @@ seq_search_test("SNOMED-CT","1201005");
 
 seq_search_test("SNOMED-PR","Incision Drai");
 
-
 seq_search_test("ICD9","401",NULL,array('code','description'),true);
 seq_search_test("ICD9","401",NULL,array('description','code'),true);
 seq_search_test("ICD9","401",NULL,array('code'),true);
index 152b3eb..4122952 100644 (file)
@@ -85,6 +85,7 @@ while ($ctrow = sqlFetchArray($ctres)) {
 $code_external_tables=array();
 define('EXT_COL_CODE','code');
 define('EXT_COL_DESCRIPTION','description');
+define('EXT_COL_DESCRIPTION_BRIEF','description_brief');
 define('EXT_TABLE_NAME','table');
 define('EXT_FILTER_CLAUSES','filter_clause');
 define('EXT_VERSION_ORDER','filter_version_order');
@@ -101,15 +102,17 @@ define('DISPLAY_DESCRIPTION',"display_description");
  * @param type $table_name          The name of the table which stores the code informattion (e.g. icd9_dx_code
  * @param type $col_code            The name of the column which is the code
  * @param type $col_description     The name of the column which is the description
+ * @param type $col_description_brief The name of the column which is the brief description
  * @param type $filter_clauses      An array of clauses to be included in the search "WHERE" clause that limits results
  * @param type $version_order       How to choose between different revisions of codes
  * @param type $joins               An array which describes additional tables to join as part of a code search.  
  */
-function define_external_table(&$results, $index, $table_name,$col_code, $col_description,$filter_clauses=array(),$version_order="",$joins=array(),$display_desc="")
+function define_external_table(&$results, $index, $table_name,$col_code, $col_description,$col_description_brief,$filter_clauses=array(),$version_order="",$joins=array(),$display_desc="")
 {
     $results[$index]=array(EXT_TABLE_NAME=>$table_name,
                            EXT_COL_CODE=>$col_code,
                            EXT_COL_DESCRIPTION=>$col_description,
+                           EXT_COL_DESCRIPTION_BRIEF=>$col_description_brief,
                            EXT_FILTER_CLAUSES=>$filter_clauses,
                            EXT_JOINS=>$joins,
                            EXT_VERSION_ORDER=>$version_order,
@@ -117,18 +120,18 @@ function define_external_table(&$results, $index, $table_name,$col_code, $col_de
                            );
 }
 // In order to treat all the code types the same for lookup_code_descriptions, we include metadata for the original codes table
-define_external_table($code_external_tables,0,'codes','code','code_text',array(),'id');
+define_external_table($code_external_tables,0,'codes','code','code_text','code_text_short',array(),'id');
 
 // ICD9 External Definitions
-define_external_table($code_external_tables,4,'icd9_dx_code','formatted_dx_code','long_desc',array("active='1'"),'revision DESC');
-define_external_table($code_external_tables,5,'icd9_sg_code','formatted_sg_code','long_desc',array("active='1'"),'revision DESC');
+define_external_table($code_external_tables,4,'icd9_dx_code','formatted_dx_code','long_desc','short_desc',array("active='1'"),'revision DESC');
+define_external_table($code_external_tables,5,'icd9_sg_code','formatted_sg_code','long_desc','short_desc',array("active='1'"),'revision DESC');
 //**** End ICD9 External Definitions
 
 // SNOMED Definitions
 // For generic SNOMED-CT, there is no need to join with the descriptions table to get a specific description Type
 
 // For generic concepts, use the fully specified description (DescriptionType=3) so we can tell the difference between them.
-define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=3"),"");
+define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=3"),"");
 
 
 // To determine codes, we need to evaluate data in both the sct_descriptions table, and the sct_concepts table.
@@ -136,12 +139,12 @@ define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Te
 $SNOMED_joins=array(JOIN_TABLE=>"sct_concepts",JOIN_FIELDS=>array("sct_descriptions.ConceptId=sct_concepts.ConceptId"));
 
 // For disorders, use the preferred term (DescriptionType=1)
-define_external_table($code_external_tables,2,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins));
+define_external_table($code_external_tables,2,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins));
 // Add the filter to choose only disorders. This filter happens as part of the join with the sct_concepts table
 array_push($code_external_tables[2][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedName like '%(disorder)'");
 
 // SNOMED-PR definition
-define_external_table($code_external_tables,9,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins));
+define_external_table($code_external_tables,9,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins));
 // Add the filter to choose only procedures. This filter happens as part of the join with the sct_concepts table
 array_push($code_external_tables[9][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedName like '%(procedure)'");
 
@@ -149,8 +152,8 @@ array_push($code_external_tables[9][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedNa
 //**** End SNOMED Definitions
 
 // ICD 10 Definitions
-define_external_table($code_external_tables,1,'icd10_dx_order_code','formatted_dx_code','long_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC');
-define_external_table($code_external_tables,6,'icd10_pcs_order_code','pcs_code','long_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC');
+define_external_table($code_external_tables,1,'icd10_dx_order_code','formatted_dx_code','long_desc','short_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC');
+define_external_table($code_external_tables,6,'icd10_pcs_order_code','pcs_code','long_desc','short_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC');
 //**** End ICD 10 Definitions
 
 /**
@@ -241,7 +244,7 @@ function convert_type_id_to_key($id) {
  * as 1) an array and as 2) a comma-separated lists that has been
  * process by urlencode() in order to place into URL  address safely.
  *
- * @param  string       $category       category of code types('diagnosis', 'procedure' or 'clinical_term')
+ * @param  string       $category       category of code types('diagnosis', 'procedure', 'clinical_term' or 'active')
  * @param  string       $return_format  format or returned code types ('array' or 'csv')
  * @return string/array
  */
@@ -268,6 +271,11 @@ function collect_codetypes($category,$return_format="array") {
     array_push($return,$ct_key);
    }
   }
+  else if ($category == "active") {
+   if ($ct_arr['active']) {
+    array_push($return,$ct_key);
+   }
+  }
   else {
    //return nothing since no supported category was chosen
   }
@@ -284,13 +292,69 @@ function collect_codetypes($category,$return_format="array") {
 }
 
 /**
- * Main code set searching function.
+ * Return the code information for a specific code.
+ *
+ * Function is able to search a variety of code sets. See the code type items in the comments at top
+ * of this page for a listing of the code sets supported.
+ *
+ * @param  string    $form_code_type  code set key
+ * @param  string    $code            code
+ * @param  boolean   $active          if true, then will only return active entries (not pertinent for PROD code sets)
+ * @return recordset                  will contain only one item (row).
+ */
+function return_code_information($form_code_type,$code,$active=true) {
+  return code_set_search($form_code_type,$code,false,$active,true);  
+}
+
+/**
+* The main code set searching function.
+*
+* It will work for searching one or numerous code sets simultaneously.
+* Note that when searching numerous code sets, you CAN NOT search the PROD
+* codes; the PROD codes can only be searched by itself. 
+*
+* @param string/array  $form_code_type   code set key(s) (can either be one key in a string or multiple/one key(s) in an array
+* @param string        $search_term      search term
+* @param integer       $limit            Number of results to return (NULL means return all)
+* @param string        $category         Category of code sets. This WILL OVERRIDE the $form_code_type setting (category options can be found in the collect_codetypes() function above)      
+* @param boolean       $active           if true, then will only return active entries
+* @param array         $modes            Holds the search modes to process along with the order of processing (if NULL, then default behavior is sequential code then description search)
+* @param boolean       $count            if true, then will only return the number of entries
+* @param integer       $start            Query start limit (for pagination) (Note this setting will override the above $limit parameter)
+* @param integer       $number           Query number returned (for pagination) (Note this setting will override the above $limit parameter)
+* @param array         $filter_elements  Array that contains elements to filter
+* @return recordset/integer              Will contain either a integer(if counting) or the results (recordset)
+*/
+function main_code_set_search($form_code_type,$search_term,$limit=NULL,$category=NULL,$active=true,$modes=NULL,$count=false,$start=NULL,$number=NULL,$filter_elements=array()) {
+
+  // check for a category
+  if (!empty($category)) {
+    $form_code_type = collect_codetypes($category,"array");
+  }
+
+  // do the search
+  if (!empty($form_code_type)) {
+    if ( is_array($form_code_type) && (count($form_code_type) > 1) ) {
+      // run the multiple code set search
+      return multiple_code_set_search($form_code_type,$search_term,$limit,$modes,$count,$active,$start,$number,$filter_elements);
+    }
+    if ( is_array($form_code_type) && (count($form_code_type) == 1) ) {
+      // prepare the variable (ie. convert the one array item to a string) for the non-multiple code set search
+      $form_code_type = $form_code_type[0];
+    }
+    // run the non-multiple code set search
+    return sequential_code_set_search($form_code_type,$search_term,$limit,$modes,$count,$active,$start,$number,$filter_elements);
+  }
+}
+
+/**
+ * Main "internal" code set searching function.
  *
  * Function is able to search a variety of code sets. See the 'external' items in the comments at top
  * of this page for a listing of the code sets supported. Also note that Products (using PROD as code type)
- * is also supported.
+ * is also supported. (This function is not meant to be called directly)
  *
- * @param  string    $form_code_type  code set key (special keywords are PROD and --ALL--)
+ * @param  string    $form_code_type  code set key (special keywords are PROD) (Note --ALL-- has been deprecated and should be run through the multiple_code_set_search() function instead)
  * @param  string    $search_term     search term
  * @param  boolean   $count           if true, then will only return the number of entries
  * @param  boolean   $active          if true, then will only return active entries (not pertinent for PROD code sets)
@@ -298,38 +362,22 @@ function collect_codetypes($category,$return_format="array") {
  * @param  integer   $start           Query start limit
  * @param  integer   $number          Query number returned
  * @param  array     $filter_elements Array that contains elements to filter
- * @param array      $limit           Number of results to return (NULL means return all); note this is ignored if set $start/number
- * @param array      $mode            'default' mode searches code and description, 'code' mode only searches code, 'description' mode searches description (and separates words); note this is ignored if set $return_only_one to TRUE
- * @return recordset/integer 
+ * @param  integer   $limit           Number of results to return (NULL means return all); note this is ignored if set $start/number
+ * @param  array     $mode            'default' mode searches code and description, 'code' mode only searches code, 'description' mode searches description (and separates words); note this is ignored if set $return_only_one to TRUE
+ * @param  array     $return_query    This is a mode that will only return the query (everything except for the LIMIT is included) (returned as an array to include the query string and binding array)
+ * @return recordset/integer/array
  */
-function code_set_search($form_code_type,$search_term="",$count=false,$active=true,$return_only_one=false,$start=NULL,$number=NULL,$filter_elements=array(),$limit=NULL,$mode='default') {
+function code_set_search($form_code_type,$search_term="",$count=false,$active=true,$return_only_one=false,$start=NULL,$number=NULL,$filter_elements=array(),$limit=NULL,$mode='default',$return_query=false) {
   global $code_types,$code_external_tables;
 
   // Figure out the appropriate limit clause
-  if ( !is_null($start) && !is_null($number) ) {
-    // For pagination of results
-    $limit_query = " LIMIT $start, $number ";
-  }
-  else if (!is_null($limit)) {
-    $limit_query = " LIMIT $limit ";
-  }
-  else {
-    // No pagination and no limit
-    $limit_query = '';
-  }
-  
-  if ($return_only_one) {
-     // Only return one result (this is where only matching for exact code match)
-     // Note this overrides the above limit settings
-     $limit_query = " LIMIT 1 ";
-  }
-  // End determining limit clause
+  $limit_query = limit_query_string($limit,$start,$number,$return_only_one);
   
   // build the filter_elements sql code
   $query_filter_elements="";
   if (!empty($filter_elements)) {
    foreach ($filter_elements as $key => $element) {
-    $query_filter_elements .= " AND c." . add_escape_custom($key) . "=" . "'" . add_escape_custom($element)  . "' ";
+    $query_filter_elements .= " AND codes." . add_escape_custom($key) . "=" . "'" . add_escape_custom($element)  . "' ";
    }
   }
 
@@ -350,56 +398,12 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
   else { // Start a codes search
       // We are looking up the external table id here.  An "unset" value gets treated as 0(zero) without this test.  This way we can differentiate between "unset" and explicitly zero.
       $table_id=isset($code_types[$form_code_type]['external']) ? intval(($code_types[$form_code_type]['external'])) : -9999 ;  
-      if(($table_id>=0) || ($form_code_type == '--ALL--')) // Either we found a definition for the given code search or we are doing an "--ALL--" search, so start building the query
+      if($table_id>=0) // We found a definition for the given code search, so start building the query
       {
-
-        // Place the common columns variable here since all check codes table
-        $common_columns=" c.id, c.code_type, c.modifier, c.units, c.fee, " .
-                        "c.superbill, c.related_code, c.taxrates, c.cyp_factor, c.active, c.reportable, c.financial_reporting, ";
-
-        if ( $table_id==0  || ($form_code_type == '--ALL--') ) { // Search from default codes table.  --ALL-- only means all codes in the default tables
-          if($table_id==0){ $table_info[EXT_FILTER_CLAUSES]=array("code_type=".$code_types[$form_code_type]['id']); } // Add a filter for the code type
-          else {$table_info[EXT_FILTER_CLAUSES]=array("ct.ct_external=0");} // define filter array for "--ALL--"
-          $table_dot="c.";              // $table_dot is used to prevent awkward looking concatenations when referring to columns in
-          $code_col="code";
-          $code_text_col="code_text";
-
-          $active_query = '';
-             if ($active) {
-              // Only filter for active codes
-          $active_query=" AND c.active = 1 ";
-          }
-          $sql_bind_array = array();
-          $columns = " c.code_text, c.code_text_short, c.code, ".$common_columns;
-          if($table_id==0)
-          {
-              // If code type is specified, then include the "constant" as part of the query results for consistency
-              $columns .= " '" . add_escape_custom($form_code_type) . "' as code_type_name ";
-              if ($count) {
-                  $query = "SELECT count(c.code) as count ";
-              }
-              else {
-                  $query = "SELECT ".$columns;
-              }
-              $query .= " FROM `codes` as c ";
-
-          }
-          else if($form_code_type=='--ALL--')
-          {
-             // For an "--ALL--" search we need to join with the code_types table to get the string representation of each returned code.
-             $columns .= " ct.ct_key as code_type_name ";
-              if ($count) {
-                  $query = "SELECT count(c.code) as count ";
-              }
-              else {
-                  $query = "SELECT ".$columns;
-              }
-             $query .=  " FROM `codes` as c " .
-                        " LEFT OUTER JOIN `code_types` as ct " .
-                        " ON c.code_type = ct.ct_id ";
-          }
-        }
-        else if ($code_types[$form_code_type]['external'] > 0 ) { // Search from an external table with defined metadata
+            // Place the common columns variable here since all check codes table
+            $common_columns=" codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
+                            "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, " .
+                            "codes.active, codes.reportable, codes.financial_reporting, ";
             $columns .= $common_columns . "'" . add_escape_custom($form_code_type) . "' as code_type_name ";
 
             $active_query = '';
@@ -411,25 +415,39 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
                 // - Kevin Yeh
                 // If there is no entry in codes sql table, then default to active
                 //  (this is reason for including NULL below)
-                $active_query=" AND (c.active = 1 || c.active IS NULL) ";
+                if ($table_id==0) {
+                    // Search from default codes table
+                    $active_query=" AND codes.active = 1 ";
+                }
+                else {
+                    // Search from external tables
+                    $active_query=" AND (codes.active = 1 || codes.active IS NULL) ";
+                }
             }
       
-            // Get the basic metadata information
+            // Get/set the basic metadata information
             $table_info=$code_external_tables[$table_id];
             $table=$table_info[EXT_TABLE_NAME];
             $table_dot=$table.".";
             $code_col=$table_info[EXT_COL_CODE];
             $code_text_col=$table_info[EXT_COL_DESCRIPTION];
-            
+            $code_text_short_col=$table_info[EXT_COL_DESCRIPTION_BRIEF];
+            if ($table_id==0) {
+                $table_info[EXT_FILTER_CLAUSES]=array("code_type=".$code_types[$form_code_type]['id']); // Add a filter for the code type
+            }
+            $code_external = $code_types[$form_code_type]['external'];
+
             // If the description is supposed to come from "joined" table instead of the "main", 
             // the metadata defines a DISPLAY_DESCRIPTION element, and we use that to build up the query
             if($table_info[DISPLAY_DESCRIPTION]!="")
             {
                 $display_description=$table_info[DISPLAY_DESCRIPTION];
+                $display_description_brief=$table_info[DISPLAY_DESCRIPTION];
             }
             else
             {
                 $display_description=$table_dot.$code_text_col;
+                $display_description_brief=$table_dot.$code_text_short_col;
             }
             // Ensure the external table exists
             $check_table = sqlQuery("SHOW TABLES LIKE '".$table."'");
@@ -441,14 +459,23 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
                 $query = "SELECT count(".$table_dot.$code_col . ") as count ";
             }
             else {
-                $query = "SELECT ".$table_dot.$code_col . " as code, " . 
+                $query = "SELECT '" . $code_external ."' as code_external, " .
+                         $table_dot.$code_col . " as code, " . 
                          $display_description . " as code_text, " .        
+                         $display_description_brief . " as code_text_short, " .
                          $columns . " ";
             }
-            $query .= " FROM ".$table.
-                      " LEFT OUTER JOIN `codes` as c " .
-                      " ON ".$table_dot.$code_col." = c.code AND c.code_type = ? ";
-            array_push($sql_bind_array,$code_types[$form_code_type]['id']);
+            if ($table_id==0) {
+                // Search from default codes table
+                $query .= " FROM ".$table." ";
+            }
+            else {
+                // Search from external tables
+                $query .= " FROM ".$table.
+                          " LEFT OUTER JOIN `codes` " .
+                          " ON ".$table_dot.$code_col." = codes.code AND codes.code_type = ? ";
+                array_push($sql_bind_array,$code_types[$form_code_type]['id']);
+            }
 
             foreach($table_info[EXT_JOINS] as $join_info)
             {
@@ -468,7 +495,6 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
                   $not_first=true;                
               }
             }
-      } // End of block for handling external_id>0
       
       // Setup the where clause based on MODE
       $query.= " WHERE ";
@@ -513,6 +539,15 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
         $query .=$active_query . $query_filter_elements;
 
         $query .= " ORDER BY ".$table_dot.$code_col."+0,".$table_dot.$code_col; 
+
+        if ($return_query) {
+          // Just returning the actual query without the LIMIT information in it. This
+          // information can then be used to combine queries of different code types
+          // via the mysql UNION command. Returning an array to contain the query string
+          // and the binding parameters.
+          return array('query'=>$query,'binds'=>$sql_bind_array);
+        }
+
         $query .= $limit_query;
         
         $res = sqlStatement($query,$sql_bind_array);         
@@ -543,10 +578,17 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr
  * items in the comments at top of this page for a listing of the code sets supported.
  *
  * @param  string $codes  Is of the form "type:code;type:code; etc.".
+ * @param  string $desc_detail Can choose either the normal description('code_text') or the brief description('code_text_short').
  * @return string         Is of the form "description;description; etc.".
  */
-function lookup_code_descriptions($codes) {
+function lookup_code_descriptions($codes,$desc_detail="code_text") {
   global $code_types, $code_external_tables;
+
+  // ensure $desc_detail is set properly
+  if ( ($desc_detail != "code_text") && ($desc_detail != "code_text_short") ) {
+    $desc_detail="code_text";
+  }
+
   $code_text = '';
   if (!empty($codes)) {
     $relcodes = explode(';', $codes);
@@ -560,8 +602,9 @@ function lookup_code_descriptions($codes) {
         $table_name=$table_info[EXT_TABLE_NAME];
         $code_col=$table_info[EXT_COL_CODE];
         $desc_col= $table_info[DISPLAY_DESCRIPTION]=="" ? $table_info[EXT_COL_DESCRIPTION] : $table_info[DISPLAY_DESCRIPTION];
+        $desc_col_short= $table_info[DISPLAY_DESCRIPTION]=="" ? $table_info[EXT_COL_DESCRIPTION_BRIEF] : $table_info[DISPLAY_DESCRIPTION];
         $sqlArray = array();
-        $sql = "SELECT ".$desc_col." as code_text FROM ".$table_name;
+        $sql = "SELECT ".$desc_col." as code_text,".$desc_col_short." as code_text_short FROM ".$table_name;
         
         // include the "JOINS" so that we get the preferred term instead of the FullySpecifiedName when appropriate.
         foreach($table_info[EXT_JOINS] as $join_info)
@@ -609,9 +652,9 @@ function lookup_code_descriptions($codes) {
 
         $sql .= " LIMIT 1";
         $crow = sqlQuery($sql,$sqlArray);
-        if (!empty($crow["code_text"])) {
+        if (!empty($crow[$desc_detail])) {
           if ($code_text) $code_text .= '; ';
-          $code_text .= $crow["code_text"];
+          $code_text .= $crow[$desc_detail];
         }
       }
 
@@ -624,25 +667,27 @@ function lookup_code_descriptions($codes) {
 }
 
 /**
-* Sequential code set searching function
+* Sequential code set "internal" searching function
 *
 * Function is basically a wrapper of the code_set_search() function to support
 * a optimized searching models. The default mode will:
 * Searches codes first; then if no hits, it will then search the descriptions
 * (which are separated by each word in the code_set_search() function).
+* (This function is not meant to be called directly)
 *
-* @param string $form_code_type code set key (special keywords are PROD and --ALL--)
+* @param string $form_code_type code set key (special keyword is PROD) (Note --ALL-- has been deprecated and should be run through the multiple_code_set_search() function instead)
 * @param string $search_term search term
-* @param array $limit Number of results to return (NULL means return all)
+* @param integer $limit Number of results to return (NULL means return all)
 * @param array $modes Holds the search modes to process along with the order of processing (default behavior is described in above function comment)
 * @param boolean $count if true, then will only return the number of entries
 * @param boolean $active if true, then will only return active entries
 * @param integer $start Query start limit (for pagination)
 * @param integer $number Query number returned (for pagination)
 * @param array $filter_elements Array that contains elements to filter
-* @return recordset/integer
+* @param string $is_hit_mode This is a mode that simply returns the name of the mode if results were found
+* @return recordset/integer/string
 */
-function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array()) {
+function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array(),$is_hit_mode=false) {
   // Set the default behavior that is described in above function comments
   if (empty($modes)) {
     $modes=array('code','description');
@@ -652,9 +697,132 @@ function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$mo
   foreach ($modes as $mode) {
     $res = code_set_search($form_code_type,$search_term,$count,$active,false,$start,$number,$filter_elements,$limit,$mode);
     if ( ($count && $res>0) || (!$count && sqlNumRows($res)>0) ) {
-      // returns the count number if count is true or returns the data if count is false
-      return $res;
+      if ($is_hit_mode) {
+        // just return the mode
+        return $mode;
+      }
+      else {
+        // returns the count number if count is true or returns the data if count is false
+        return $res;
+      }
+    }
+  }
+}
+
+/**
+* Code set searching "internal" function for when searching multiple code sets.
+*
+* It will also work for one code set search, although not meant for this.
+* (This function is not meant to be called directly)
+*
+* @param array $form_code_types code set keys (will default to checking all active code types if blank)
+* @param string $search_term search term
+* @param integer $limit Number of results to return (NULL means return all)
+* @param array $modes Holds the search modes to process along with the order of processing (default behavior is described in above function comment)
+* @param boolean $count if true, then will only return the number of entries
+* @param boolean $active if true, then will only return active entries
+* @param integer $start Query start limit (for pagination)
+* @param integer $number Query number returned (for pagination)
+* @param array $filter_elements Array that contains elements to filter
+* @return recordset/integer
+*/
+function multiple_code_set_search($form_code_types=array(),$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array()) {
+
+  if (empty($form_code_types)) {
+    // Collect the active code types
+    $form_code_types = collect_codetypes("active","array");
+  }
+
+  if ($count) {
+    //start the counter
+    $counter = 0;
+  }
+  else {
+    // Figure out the appropriate limit clause
+    $limit_query = limit_query_string($limit,$start,$number); 
+
+    // Prepare the sql bind array
+    $sql_bind_array = array();
+
+    // Start the query string
+    $query = "SELECT * FROM ((";
+  }
+
+  // Loop through each code type
+  $flag_first = true;
+  $flag_hit = false; //ensure there is a hit to avoid trying an empty query
+  foreach ($form_code_types as $form_code_type) {
+    // see if there is a hit
+    $mode_hit = NULL;
+    // only use the count method here, since it's much more efficient than doing the actual query
+    $mode_hit = sequential_code_set_search($form_code_type,$search_term,NULL,$modes,true,$active,NULL,NULL,$filter_elements,true);
+    if ($mode_hit) {
+      if ($count) {
+        // count the hits
+        $count_hits = code_set_search($form_code_type,$search_term,$count,$active,false,NULL,NULL,$filter_elements,NULL,$mode_hit);
+        // increment the counter
+        $counter += $count_hits;
+      }
+      else {
+        $flag_hit = true;
+        // build the query
+        $return_query = code_set_search($form_code_type,$search_term,$count,$active,false,NULL,NULL,$filter_elements,NULL,$mode_hit,true);
+        if (!empty($sql_bind_array)) {
+          $sql_bind_array = array_merge($sql_bind_array,$return_query['binds']);
+        }
+        else {
+          $sql_bind_array = $return_query['binds'];
+        }
+        if (!$flag_first) {
+          $query .= ") UNION ALL (";
+        }
+        $query .= $return_query['query'];
+      }
+      $flag_first = false;
+    }
+  }
+
+  if ($count) {
+    //return the count
+    return $counter;
+  }
+  else {
+    // Finish the query string
+    $query .= ")) as atari $limit_query";
+    
+    // Process and return the query (if there was a hit)
+    if ($flag_hit) {
+      return sqlStatement($query,$sql_bind_array);
     }
   }
 }
+
+/**
+* Returns the limit to be used in the sql query for code set searches.
+*
+* @param  integer  $limit            Number of results to return (NULL means return all)
+* @param  integer  $start            Query start limit (for pagination)
+* @param  integer  $number           Query number returned (for pagination)
+* @param  boolean  $return_only_one  if true, then will only return one perfect matching item 
+* @return recordset/integer
+*/
+function limit_query_string($limit=NULL,$start=NULL,$number=NULL,$return_only_one=false) {
+  if ( !is_null($start) && !is_null($number) ) {
+    // For pagination of results
+    $limit_query = " LIMIT $start, $number ";
+  }
+  else if (!is_null($limit)) {
+    $limit_query = " LIMIT $limit ";
+  }
+  else {
+    // No pagination and no limit
+    $limit_query = '';
+  }
+  if ($return_only_one) {
+     // Only return one result (this is where only matching for exact code match)
+     // Note this overrides the above limit settings
+     $limit_query = " LIMIT 1 ";
+  }
+  return $limit_query;
+}
 ?>
index c26ee89..cc52fc8 100644 (file)
@@ -860,7 +860,7 @@ echo "  <td colspan='" . attr($FEE_SHEET_COLUMNS) . "' align='center' nowrap>\n"
 //
 $numrows = 0;
 if ($_POST['bn_search'] && $_POST['search_term']) {
-  $res = sequential_code_set_search($search_type,$_POST['search_term']);
+  $res = main_code_set_search($search_type,$_POST['search_term']);
   if (!empty($res)) {
     $numrows = sqlNumRows($res);
   }
index 8183739..f67a7e7 100644 (file)
@@ -159,7 +159,7 @@ else {
  </tr>
 <?php
   $search_term = $_REQUEST['search_term'];
-  $res = sequential_code_set_search($form_code_type,$search_term);
+  $res = main_code_set_search($form_code_type,$search_term);
   if ($form_code_type == 'PROD') { // Special case that displays search for products/drugs
     while ($row = sqlFetchArray($res)) {
       $drug_id = $row['drug_id'];
index a01d6da..8d58ed0 100644 (file)
@@ -137,7 +137,7 @@ if (isset($mode)) {
     $code_type_name_external = $_POST['code_type_name_external'];
     $code_external = $_POST['code_external'];
     $code_id = $_POST['code_id'];
-    $results = code_set_search($code_type_name_external,$code_external,false,false,true); // only will return one item
+    $results = return_code_information($code_type_name_external,$code_external,false); // only will return one item
     while ($row = sqlFetchArray($results)) {
       $code         = $row['code'];
       $code_text    = $row['code_text'];
@@ -161,9 +161,15 @@ if (!empty($related_code)) {
 }
 
 $fstart = $_REQUEST['fstart'] + 0;
-$filter = $_REQUEST['filter'] + 0;
-if ($filter) {
- $filter_key = convert_type_id_to_key($filter);
+if (isset($_REQUEST['filter'])) {
+  $filter = array();
+  $filter_key = array();
+  foreach ($_REQUEST['filter'] as $var) {
+    $var = $var+0;
+    array_push($filter,$var);
+    $var_key = convert_type_id_to_key($var);
+    array_push($filter_key,$var_key);
+  }
 }
 $search = $_REQUEST['search'];
 $search_reportable = $_REQUEST['search_reportable'];
@@ -178,12 +184,10 @@ if (!empty($search_financial_reporting)) {
  $filter_elements['financial_reporting'] = $search_financial_reporting;
 }
 
-if ($filter) {
- $count = sequential_code_set_search($filter_key,$search,NULL,NULL,true,false,NULL,NULL,$filter_elements);
-}
-else {
- $count = sequential_code_set_search("--ALL--",$search,NULL,NULL,true,false,NULL,NULL,$filter_elements);
+if (isset($_REQUEST['filter'])) {
+ $count = main_code_set_search($filter_key,$search,NULL,NULL,false,NULL,true,NULL,NULL,$filter_elements);
 }
+
 if ($fstart >= $count) $fstart -= $pagesize;
 if ($fstart < 0) $fstart = 0;
 $fend = $fstart + $pagesize;
@@ -518,31 +522,11 @@ if ($taxline) {
  <tr>
 
   <td class='text'>
-   <select name='filter' onchange='submitList(0)'>
-    <option value='0'>
-    <?php $all_string = xlt("All");
-          if ( !(empty($external_sets) )) {
-           // Show the external code sets that will not work with All selection
-           $all_string .= " (" . xlt("Except") . " ";
-           $first_flag = true;
-           foreach ($external_sets as $set) {
-            if ($first_flag) { //deal with the comma
-             $first_flag = false;
-            }
-            else {
-             $all_string .= ",";
-            }
-            $all_string .= xlt($code_types[$set]['label']);
-           }
-           $all_string .= ")";
-          }
-          echo $all_string;
-    ?>
-    </option>
+   <select name='filter[]' multiple='multiple'>
 <?php
 foreach ($code_types as $key => $value) {
   echo "<option value='" . attr($value['id']) . "'";
-  if ($value['id'] == $filter) echo " selected";
+  if (in_array($value['id'],$filter)) echo " selected";
   echo ">" . xlt($value['label']) . "</option>\n";
 }
 ?>
@@ -587,6 +571,7 @@ foreach ($code_types as $key => $value) {
   <td><span class='bold'><?php echo xlt('Serv Rep'); ?></span></td>
   <td><span class='bold'><?php echo xlt('Type'); ?></span></td>
   <td><span class='bold'><?php echo xlt('Description'); ?></span></td>
+  <td><span class='bold'><?php echo xlt('Short Description'); ?></span></td>
 <?php if (related_codes_are_used()) { ?>
   <td><span class='bold'><?php echo xlt('Related'); ?></span></td>
 <?php } ?>
@@ -601,17 +586,9 @@ while ($prow = sqlFetchArray($pres)) {
   <td></td>
  </tr>
 <?php
-// Flag is this is from an external set
-$is_external_set=false;
-if (in_array($filter_key,$external_sets)) {
-  $is_external_set=true;
-}
 
-if ($filter) {
- $res = sequential_code_set_search($filter_key,$search,NULL,NULL,false,false,$fstart,($fend - $fstart),$filter_elements);
-}
-else {
- $res = sequential_code_set_search("--ALL--",$search,NULL,NULL,false,false,$fstart,($fend - $fstart),$filter_elements);
+if (isset($_REQUEST['filter'])) {
+  $res = main_code_set_search($filter_key,$search,NULL,NULL,false,NULL,false,$fstart,($fend - $fstart),$filter_elements);
 }
 
 for ($i = 0; $row = sqlFetchArray($res); $i++) $all[$i] = $row;
@@ -632,7 +609,7 @@ if (!empty($all)) {
     echo " <tr>\n";
     echo "  <td class='text'>" . text($iter["code"]) . "</td>\n";
     echo "  <td class='text'>" . text($iter["modifier"]) . "</td>\n";
-    if ($is_external_set) {
+    if ($iter["code_external"] > 0) {
       // If there is no entry in codes sql table, then default to active
       //  (this is reason for including NULL below)
       echo "  <td class='text'>" . ( ($iter["active"] || $iter["active"]==NULL) ? xlt('Yes') : xlt('No')) . "</td>\n";
@@ -644,6 +621,7 @@ if (!empty($all)) {
     echo "  <td class='text'>" . ($iter["financial_reporting"] ? xlt('Yes') : xlt('No')) . "</td>\n";
     echo "  <td class='text'>" . text($iter['code_type_name']) . "</td>\n";
     echo "  <td class='text'>" . text($iter['code_text']) . "</td>\n";
+    echo "  <td class='text'>" . text($iter['code_text_short']) . "</td>\n";
 
     if (related_codes_are_used()) {
       // Show related codes.
@@ -665,7 +643,7 @@ if (!empty($all)) {
       echo "<td class='text' align='right'>" . text(bucks($prow['pr_price'])) . "</td>\n";
     }
 
-    if ($is_external_set) {
+    if ($iter["code_external"] > 0) {
       echo "  <td align='right'><a class='link' href='javascript:submitModify(\"" . attr($iter['code_type_name']) . "\",\"" . attr($iter['code']) . "\",\"" . attr($iter['id']) . "\")'>[" . xlt('Modify') . "]</a></td>\n";
     }
     else {