minor comment fix in library/sql.inc
[openemr.git] / custom / code_types.inc.php
blobadb82f399e7b9ce41214f8f8f8150166ed009501
1 <?php
2 // Copyright (C) 2006-2010 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // This array provides abstraction of billing code types. This is desirable
10 // because different countries or fields of practice use different methods for
11 // coding diagnoses, procedures and supplies. Fees will not be relevant where
12 // medical care is socialized. Attribues are:
14 // id - the numeric identifier of this code type in the codes table
15 // fee - 1 if fees are used, else 0
16 // mod - the maximum length of a modifier, 0 if modifiers are not used
17 // just - the code type used for justification, empty if none
18 // rel - 1 if other billing codes may be "related" to this code type
19 // nofs - 1 if this code type should NOT appear in the Fee Sheet
20 // diag - 1 if this code type is for diagnosis
21 // active - 1 if this code type is activated
22 // label - label used for code type
23 // external - 0 for storing codes in the code table
24 // 1 for storing codes in external ICD10 tables
25 // 2 for storing codes in external SNOMED (RF1) tables
26 // 3 for storing codes in external SNOMED (RF2) tables
27 // 4 for storing codes in external ICD9 tables
30 /*********************************************************************
31 if ($GLOBALS['ippf_specific']) {
32 // IPPF:
33 $code_types = array(
34 'ICD9' => array('id' => 2, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 0, 'diag' => TRUE),
35 'MA' => array('id' => 12, 'fee' => 1, 'mod' => 0, 'just' => '', 'rel' => 1, 'nofs' => 0),
36 'IPPF' => array('id' => 11, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 1),
37 'ACCT' => array('id' => 13, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 1),
39 $default_search_type = 'MA';
41 else if ($GLOBALS['athletic_team']) {
42 // UK Sports Medicine:
43 $code_types = array(
44 'OSICS10' => array('id' => 9, 'fee' => 0, 'mod' => 4, 'just' => '', 'rel' => 0, 'nofs' => 0, 'diag' => TRUE),
45 'OPCS' => array('id' => 6, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 0),
46 'PTCJ' => array('id' => 7, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 0),
47 'CPT4' => array('id' => 1, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 0),
48 'SMPC' => array('id' => 10, 'fee' => 0, 'mod' => 0, 'just' => '', 'rel' => 0, 'nofs' => 0),
50 $default_search_type = 'OSICS10';
52 else {
53 // USA Clinics:
54 $code_types = array(
55 'ICD9' => array('id' => 2, 'fee' => 0, 'mod' => 2, 'just' => '' , 'rel' => 0, 'nofs' => 0, 'diag' => TRUE),
56 'CPT4' => array('id' => 1, 'fee' => 1, 'mod' => 2, 'just' => 'ICD9', 'rel' => 0, 'nofs' => 0),
57 'HCPCS' => array('id' => 3, 'fee' => 1, 'mod' => 2, 'just' => 'ICD9', 'rel' => 0, 'nofs' => 0),
59 $default_search_type = 'ICD9';
61 *********************************************************************/
63 $cd_external_options = array(
64 '0' => xl('No'),
65 '1' => xl('ICD10'),
66 '2' => xl('SNOMED (RF1)'),
67 '3' => xl('SNOMED (RF2)'),
68 '4' => xl('ICD9')
71 // Code types are now stored in the database.
73 $code_types = array();
74 $default_search_type = '';
75 $ctres = sqlStatement("SELECT * FROM code_types WHERE ct_active=1 ORDER BY ct_seq, ct_key");
76 while ($ctrow = sqlFetchArray($ctres)) {
77 $code_types[$ctrow['ct_key']] = array(
78 'id' => $ctrow['ct_id' ],
79 'fee' => $ctrow['ct_fee' ],
80 'mod' => $ctrow['ct_mod' ],
81 'just' => $ctrow['ct_just'],
82 'rel' => $ctrow['ct_rel' ],
83 'nofs' => $ctrow['ct_nofs'],
84 'diag' => $ctrow['ct_diag'],
85 'mask' => $ctrow['ct_mask'],
86 'label'=> ( (empty($ctrow['ct_label'])) ? $ctrow['ct_key'] : $ctrow['ct_label'] ),
87 'external'=> $ctrow['ct_external']
89 if ($default_search_type === '') $default_search_type = $ctrow['ct_key'];
92 /********************************************************************/
94 function fees_are_used() {
95 global $code_types;
96 foreach ($code_types as $value) { if ($value['fee']) return true; }
97 return false;
100 function modifiers_are_used($fee_sheet=false) {
101 global $code_types;
102 foreach ($code_types as $value) {
103 if ($fee_sheet && !empty($value['nofs'])) continue;
104 if ($value['mod']) return true;
106 return false;
109 function related_codes_are_used() {
110 global $code_types;
111 foreach ($code_types as $value) { if ($value['rel']) return true; }
112 return false;
115 // Convert a code type id to a key
116 function convert_type_id_to_key($id) {
117 global $code_types;
118 foreach ($code_types as $key => $value) {
119 if ($value['id'] == $id) return $key;
123 // Main code set searching function
124 // $form_code_type - code set key (special keywords are PROD and --ALL--)
125 // $search_term - search term
126 // $count - if true, then will only return the number of entries
127 // $active - if true, then will only return active entries (not pertinent for PROD or external code sets)
128 // $return_only_one - if true, then will only return one perfect matching item
129 // $start - Query start limit
130 // $end - Query end limit
131 function code_set_search($form_code_type,$search_term="",$count=false,$active=true,$return_only_one=false,$start=NULL,$number=NULL) {
132 global $code_types;
134 $limit_query = '';
135 if ( !is_null($start) && !is_null($number) ) {
136 $limit_query = " LIMIT $start, $number ";
138 if ($return_only_one) {
139 $limit_query = " LIMIT 1 ";
142 if ($form_code_type == 'PROD') { // Search for products/drugs
143 $query = "SELECT dt.drug_id, dt.selector, d.name " .
144 "FROM drug_templates AS dt, drugs AS d WHERE " .
145 "( d.name LIKE ? OR " .
146 "dt.selector LIKE ? ) " .
147 "AND d.drug_id = dt.drug_id " .
148 "ORDER BY d.name, dt.selector, dt.drug_id $limit_query";
149 $res = sqlStatement($query, array("%".$search_term."%", "%".$search_term."%") );
151 else if ($form_code_type == '--ALL--') { // Search all codes from the default codes table
152 // Note this will not search the external code sets
153 $active_query = '';
154 if ($active) {
155 // Only filter for active codes
156 $active_query=" AND codes.active = 1 ";
158 $query = "SELECT `id`, `code_text`, `code_text_short`, `code`, `code_type`, `modifier`, `units`, `fee`, " .
159 "`superbill`, `related_code`, `taxrates`, `cyp_factor`, `active`, `reportable`, " .
160 "code_types.ct_key as code_type_name " .
161 "FROM `codes` " .
162 "LEFT OUTER JOIN `code_types` " .
163 "ON codes.code_type = code_types.ct_id " .
164 "WHERE (codes.code_text LIKE ? OR " .
165 "codes.code LIKE ?) AND code_types.ct_external = '0' " .
166 " $active_query " .
167 "ORDER BY code_type,code+0,code $limit_query";
168 $res = sqlStatement($query, array("%".$search_term."%", "%".$search_term."%") );
170 else if ( !($code_types[$form_code_type]['external']) ) { // Search from default codes table
171 $active_query = '';
172 if ($active) {
173 // Only filter for active codes
174 $active_query=" AND codes.active = 1 ";
176 $sql_bind_array = array();
177 $query = "SELECT `id`, `code_text`, `code_text_short`, `code`, `code_type`, `modifier`, `units`, `fee`, " .
178 "`superbill`, `related_code`, `taxrates`, `cyp_factor`, `active`, `reportable`, " .
179 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
180 "FROM `codes` ";
181 if ($return_only_one) {
182 $query .= "WHERE codes.code = ? ";
183 array_push($sql_bind_array,$search_term);
185 else {
186 $query .= "WHERE (codes.code_text LIKE ? OR codes.code LIKE ?) ";
187 array_push($sql_bind_array,"%".$search_term."%", "%".$search_term."%");
189 $query .= "AND code_type = ? $active_query " .
190 "ORDER BY code+0,code $limit_query";
191 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
192 $res = sqlStatement($query,$sql_bind_array);
194 else if ($code_types[$form_code_type]['external'] == 1 ) { // Search from ICD10 codeset tables
195 $active_query = '';
196 if ($active) {
197 // Only filter for active codes
198 // If there is no entry in codes sql table, then default to active
199 // (this is reason for including NULL below)
200 $active_query=" AND (codes.active = 1 || codes.active IS NULL) ";
202 // Ensure the icd10_dx_order_code sql table exists
203 $check_table = sqlQuery("SHOW TABLES LIKE 'icd10_dx_order_code'");
204 if ( !(empty($check_table)) ) {
205 $sql_bind_array = array();
206 $query = "SELECT icd10_dx_order_code.formatted_dx_code as code, icd10_dx_order_code.long_desc as code_text, " .
207 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
208 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
209 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
210 "FROM `icd10_dx_order_code` " .
211 "LEFT OUTER JOIN `codes` " .
212 "ON icd10_dx_order_code.formatted_dx_code = codes.code AND codes.code_type = ? ";
213 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
214 if ($return_only_one) {
215 $query .= "WHERE icd10_dx_order_code.formatted_dx_code = ? AND icd10_dx_order_code.valid_for_coding = '1' $active_query ";
216 array_push($sql_bind_array,$search_term);
218 else {
219 $query .= "WHERE (icd10_dx_order_code.long_desc LIKE ? OR icd10_dx_order_code.formatted_dx_code LIKE ?) AND icd10_dx_order_code.valid_for_coding = '1' $active_query ";
220 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
222 $query .= "ORDER BY `formatted_dx_code`+0, `formatted_dx_code` $limit_query";
223 $res = sqlStatement($query,$sql_bind_array);
226 else if ($code_types[$form_code_type]['external'] == 2 ) { // Search from SNOMED (RF1) codeset tables
227 if ($active) {
228 // Only filter for active codes
229 // If there is no entry in codes sql table, then default to active
230 // (this is reason for including NULL below)
231 $active_query=" AND (codes.active = 1 || codes.active IS NULL) ";
233 // Ensure the sct_concepts sql table exists
234 $check_table = sqlQuery("SHOW TABLES LIKE 'sct_concepts'");
235 if ( !(empty($check_table)) ) {
236 $sql_bind_array = array();
237 $query = "SELECT `ConceptId` as code, `FullySpecifiedName` as code_text, " .
238 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
239 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
240 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
241 "FROM `sct_concepts` " .
242 "LEFT OUTER JOIN `codes` " .
243 "ON sct_concepts.ConceptId = codes.code AND codes.code_type = ? ";
244 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
245 if ($return_only_one) {
246 $query .= "WHERE (`ConceptId` = ? AND `FullySpecifiedName` LIKE '%(disorder)') $active_query ";
247 array_push($sql_bind_array,$search_term);
249 else {
250 $query .= "WHERE ((`FullySpecifiedName` LIKE ? OR `ConceptId` LIKE ?) AND `FullySpecifiedName` LIKE '%(disorder)') $active_query ";
251 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
253 $query .= "AND `ConceptStatus` = 0 " .
254 "ORDER BY `ConceptId` $limit_query";
255 $res = sqlStatement($query,$sql_bind_array);
258 else if ($code_types[$form_code_type]['external'] == 3 ) { // Search from SNOMED (RF2) codeset tables
259 //placeholder
261 else if ($code_types[$form_code_type]['external'] == 4 ) { // Search from ICD9 codeset tables
262 if ($active) {
263 // Only filter for active codes
264 // If there is no entry in codes sql table, then default to active
265 // (this is reason for including NULL below)
266 $active_query=" AND (codes.active = 1 || codes.active IS NULL) ";
268 // Ensure the icd9_dx_code sql table exists
269 $check_table = sqlQuery("SHOW TABLES LIKE 'icd9_dx_code'");
270 if ( !(empty($check_table)) ) {
271 $sql_bind_array = array();
272 $query = "SELECT icd9_dx_code.formatted_dx_code as code, icd9_dx_code.long_desc as code_text, " .
273 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
274 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
275 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
276 "FROM `icd9_dx_code` " .
277 "LEFT OUTER JOIN `codes` " .
278 "ON icd9_dx_code.formatted_dx_code = codes.code AND codes.code_type = ? ";
279 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
280 if ($return_only_one) {
281 $query .= "WHERE icd9_dx_code.formatted_dx_code = ? $active_query ";
282 array_push($sql_bind_array,$search_term);
284 else {
285 $query .= "WHERE (icd9_dx_code.long_desc LIKE ? OR icd9_dx_code.formatted_dx_code LIKE ?) $active_query ";
286 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
288 $query .= "ORDER BY `formatted_dx_code`+0, `formatted_dx_code` $limit_query";
289 $res = sqlStatement($query,$sql_bind_array);
292 else {
293 //using an external code that is not yet supported, so skip.
295 if (isset($res)) {
296 if ($count) {
297 // just return the count
298 return sqlNumRows($res);
300 else {
301 // return the data
302 return $res;
307 // Look up descriptions for one or more billing codes. Input is of the
308 // form "type:code;type:code; etc.".
310 function lookup_code_descriptions($codes) {
311 global $code_types;
312 $code_text = '';
313 if (!empty($codes)) {
314 $relcodes = explode(';', $codes);
315 foreach ($relcodes as $codestring) {
316 if ($codestring === '') continue;
317 list($codetype, $code) = explode(':', $codestring);
318 if ( !($code_types[$codetype]['external']) ) { // Collect from default codes table
319 $wheretype = "";
320 $sqlArray = array();
321 if (empty($code)) {
322 $code = $codetype;
323 } else {
324 $wheretype = "code_type = ? AND ";
325 array_push($sqlArray,$code_types[$codetype]['id']);
327 $sql = "SELECT code_text FROM codes WHERE " .
328 "$wheretype code = ? ORDER BY id LIMIT 1";
329 array_push($sqlArray,$code);
330 $crow = sqlQuery($sql,$sqlArray);
331 if (!empty($crow['code_text'])) {
332 if ($code_text) $code_text .= '; ';
333 $code_text .= $crow['code_text'];
336 else if ($code_types[$codetype]['external'] == 1) { // Collect from ICD10 codeset tables
337 // Ensure the icd10_dx_order_code sql table exists
338 $check_table = sqlQuery("SHOW TABLES LIKE 'icd10_dx_order_code'");
339 if ( !(empty($check_table)) ) {
340 if ( !(empty($code)) ) {
341 $sql = "SELECT `long_desc` FROM `icd10_dx_order_code` " .
342 "WHERE `formatted_dx_code` = ? LIMIT 1";
343 $crow = sqlQuery($sql, array($code) );
344 if (!empty($crow['long_desc'])) {
345 if ($code_text) $code_text .= '; ';
346 $code_text .= $crow['long_desc'];
351 else if ($code_types[$codetype]['external'] == 2) { // Collect from SNOMED (RF1) codeset tables
352 // Ensure the sct_concepts sql table exists
353 $check_table = sqlQuery("SHOW TABLES LIKE 'sct_concepts'");
354 if ( !(empty($check_table)) ) {
355 if ( !(empty($code)) ) {
356 $sql = "SELECT `FullySpecifiedName` FROM `sct_concepts` " .
357 "WHERE `ConceptId` = ? AND `ConceptStatus` = 0 LIMIT 1";
358 $crow = sqlQuery($sql, array($code) );
359 if (!empty($crow['FullySpecifiedName'])) {
360 if ($code_text) $code_text .= '; ';
361 $code_text .= $crow['FullySpecifiedName'];
366 else if ($code_types[$codetype]['external'] == 3) { // Collect from SNOMED (RF2) codeset tables
367 //placeholder
369 else if ($code_types[$codetype]['external'] == 4) { // Collect from ICD9 codeset tables
370 // Ensure the icd9_dx_code sql table exists
371 $check_table = sqlQuery("SHOW TABLES LIKE 'icd9_dx_code'");
372 if ( !(empty($check_table)) ) {
373 if ( !(empty($code)) ) {
374 $sql = "SELECT `long_desc` FROM `icd9_dx_code` " .
375 "WHERE `formatted_dx_code` = ? LIMIT 1";
376 $crow = sqlQuery($sql, array($code) );
377 if (!empty($crow['long_desc'])) {
378 if ($code_text) $code_text .= '; ';
379 $code_text .= $crow['long_desc'];
384 else {
385 //using an external code that is not yet supported, so skip.
389 return $code_text;