2 // Copyright (C) 2006-2010 Rod Roark <rod@sunsetsystems.com>
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']) {
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:
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';
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(
66 '2' => xl('SNOMED (RF1)'),
67 '3' => xl('SNOMED (RF2)'),
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() {
96 foreach ($code_types as $value) { if ($value['fee']) return true; }
100 function modifiers_are_used($fee_sheet=false) {
102 foreach ($code_types as $value) {
103 if ($fee_sheet && !empty($value['nofs'])) continue;
104 if ($value['mod']) return true;
109 function related_codes_are_used() {
111 foreach ($code_types as $value) { if ($value['rel']) return true; }
115 // Convert a code type id to a key
116 function convert_type_id_to_key($id) {
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) {
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
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 " .
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' " .
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
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 " .
181 if ($return_only_one) {
182 $query .= "WHERE codes.code = ? ";
183 array_push($sql_bind_array,$search_term);
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
197 // Only filter for active codes
198 $active_query=" AND (codes.active = 1 || codes.active = NULL) ";
200 // Ensure the icd10_dx_order_code sql table exists
201 $check_table = sqlQuery("SHOW TABLES LIKE 'icd10_dx_order_code'");
202 if ( !(empty($check_table)) ) {
203 $sql_bind_array = array();
204 $query = "SELECT icd10_dx_order_code.dx_code as code, icd10_dx_order_code.long_desc as code_text, " .
205 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
206 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
207 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
208 "FROM `icd10_dx_order_code` " .
209 "LEFT OUTER JOIN `codes` " .
210 "ON icd10_dx_order_code.dx_code = codes.code AND codes.code_type = ? ";
211 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
212 if ($return_only_one) {
213 $query .= "WHERE `dx_code` = ? $active_query ";
214 array_push($sql_bind_array,$search_term);
217 $query .= "WHERE (`long_desc` LIKE ? OR `dx_code` LIKE ?) $active_query ";
218 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
220 $query .= "ORDER BY `dx_code`+0, `dx_code` $limit_query";
221 $res = sqlStatement($query,$sql_bind_array);
224 else if ($code_types[$form_code_type]['external'] == 2 ) { // Search from SNOMED (RF1) codeset tables
226 // Only filter for active codes
227 $active_query=" AND (codes.active = 1 || codes.active = NULL) ";
229 // Ensure the sct_concepts sql table exists
230 $check_table = sqlQuery("SHOW TABLES LIKE 'sct_concepts'");
231 if ( !(empty($check_table)) ) {
232 $sql_bind_array = array();
233 $query = "SELECT `ConceptId` as code, `FullySpecifiedName` as code_text, " .
234 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
235 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
236 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
237 "FROM `sct_concepts` " .
238 "LEFT OUTER JOIN `codes` " .
239 "ON sct_concepts.ConceptId = codes.code AND codes.code_type = ? ";
240 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
241 if ($return_only_one) {
242 $query .= "WHERE (`ConceptId` = ? AND `FullySpecifiedName` LIKE '%(disorder)') $active_query ";
243 array_push($sql_bind_array,$search_term);
246 $query .= "WHERE ((`FullySpecifiedName` LIKE ? OR `ConceptId` LIKE ?) AND `FullySpecifiedName` LIKE '%(disorder)') $active_query ";
247 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
249 $query .= "AND `ConceptStatus` = 0 " .
250 "ORDER BY `ConceptId` $limit_query";
251 $res = sqlStatement($query,$sql_bind_array);
254 else if ($code_types[$form_code_type]['external'] == 3 ) { // Search from SNOMED (RF2) codeset tables
257 else if ($code_types[$form_code_type]['external'] == 4 ) { // Search from ICD9 codeset tables
259 // Only filter for active codes
260 $active_query=" AND (codes.active = 1 || codes.active = NULL) ";
262 // Ensure the icd9_dx_code sql table exists
263 $check_table = sqlQuery("SHOW TABLES LIKE 'icd9_dx_code'");
264 if ( !(empty($check_table)) ) {
265 $sql_bind_array = array();
266 $query = "SELECT icd9_dx_code.dx_code as code, icd9_dx_code.long_desc as code_text, " .
267 "codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " .
268 "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, codes.active, codes.reportable, " .
269 "'" . add_escape_custom($form_code_type) . "' as code_type_name " .
270 "FROM `icd9_dx_code` " .
271 "LEFT OUTER JOIN `codes` " .
272 "ON icd9_dx_code.dx_code = codes.code AND codes.code_type = ? ";
273 array_push($sql_bind_array,$code_types[$form_code_type]['id']);
274 if ($return_only_one) {
275 $query .= "WHERE icd9_dx_code.dx_code = ? $active_query ";
276 array_push($sql_bind_array,$search_term);
279 $query .= "WHERE (icd9_dx_code.long_desc LIKE ? OR icd9_dx_code.dx_code LIKE ?) $active_query ";
280 array_push($sql_bind_array,"%".$search_term."%","%".$search_term."%");
282 $query .= "ORDER BY `dx_code`+0, `dx_code` $limit_query";
283 $res = sqlStatement($query,$sql_bind_array);
287 //using an external code that is not yet supported, so skip.
291 // just return the count
292 return sqlNumRows($res);
301 // Look up descriptions for one or more billing codes. Input is of the
302 // form "type:code;type:code; etc.".
304 function lookup_code_descriptions($codes) {
307 if (!empty($codes)) {
308 $relcodes = explode(';', $codes);
309 foreach ($relcodes as $codestring) {
310 if ($codestring === '') continue;
311 list($codetype, $code) = explode(':', $codestring);
312 if ( !($code_types[$codetype]['external']) ) { // Collect from default codes table
318 $wheretype = "code_type = ? AND ";
319 array_push($sqlArray,$code_types[$codetype]['id']);
321 $sql = "SELECT code_text FROM codes WHERE " .
322 "$wheretype code = ? ORDER BY id LIMIT 1";
323 array_push($sqlArray,$code);
324 $crow = sqlQuery($sql,$sqlArray);
325 if (!empty($crow['code_text'])) {
326 if ($code_text) $code_text .= '; ';
327 $code_text .= $crow['code_text'];
330 else if ($code_types[$codetype]['external'] == 1) { // Collect from ICD10 codeset tables
331 // Ensure the icd10_dx_order_code sql table exists
332 $check_table = sqlQuery("SHOW TABLES LIKE 'icd10_dx_order_code'");
333 if ( !(empty($check_table)) ) {
334 if ( !(empty($code)) ) {
335 $sql = "SELECT `long_desc` FROM `icd10_dx_order_code` " .
336 "WHERE `dx_code` = ? LIMIT 1";
337 $crow = sqlQuery($sql, array($code) );
338 if (!empty($crow['long_desc'])) {
339 if ($code_text) $code_text .= '; ';
340 $code_text .= $crow['long_desc'];
345 else if ($code_types[$codetype]['external'] == 2) { // Collect from SNOMED (RF1) codeset tables
346 // Ensure the sct_concepts sql table exists
347 $check_table = sqlQuery("SHOW TABLES LIKE 'sct_concepts'");
348 if ( !(empty($check_table)) ) {
349 if ( !(empty($code)) ) {
350 $sql = "SELECT `FullySpecifiedName` FROM `sct_concepts` " .
351 "WHERE `ConceptId` = ? AND `ConceptStatus` = 0 LIMIT 1";
352 $crow = sqlQuery($sql, array($code) );
353 if (!empty($crow['FullySpecifiedName'])) {
354 if ($code_text) $code_text .= '; ';
355 $code_text .= $crow['FullySpecifiedName'];
360 else if ($code_types[$codetype]['external'] == 3) { // Collect from SNOMED (RF2) codeset tables
363 else if ($code_types[$codetype]['external'] == 4) { // Collect from ICD9 codeset tables
364 // Ensure the icd9_dx_code sql table exists
365 $check_table = sqlQuery("SHOW TABLES LIKE 'icd9_dx_code'");
366 if ( !(empty($check_table)) ) {
367 if ( !(empty($code)) ) {
368 $sql = "SELECT `long_desc` FROM `icd9_dx_code` " .
369 "WHERE `dx_code` = ? LIMIT 1";
370 $crow = sqlQuery($sql, array($code) );
371 if (!empty($crow['long_desc'])) {
372 if ($code_text) $code_text .= '; ';
373 $code_text .= $crow['long_desc'];
379 //using an external code that is not yet supported, so skip.