Fee sheet and Codes revenue code (#7415)
[openemr.git] / interface / orders / load_compendium.php
blob944336f3392dc793eaa7bf5ff725ca3f3d65a0ac
1 <?php
3 /**
4 * Administrative loader for lab compendium data.
6 * Supports loading of lab order codes and related order entry questions from CSV
7 * format into the procedure_order and procedure_questions tables, respectively.
9 * Copyright (C) 2012-2013 Rod Roark <rod@sunsetsystems.com>
11 * LICENSE: This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
22 * @package OpenEMR
23 * @author Rod Roark <rod@sunsetsystems.com>
26 set_time_limit(0);
28 require_once("../globals.php");
30 use OpenEMR\Common\Acl\AclMain;
31 use OpenEMR\Common\Twig\TwigContainer;
32 use OpenEMR\Core\Header;
34 // This array is an important reference for the supported labs and their NPI
35 // numbers as known to this program. The clinic must define at least one
36 // procedure provider entry for a lab that has a supported NPI number.
38 $lab_npi = array(
39 '1235138868' => 'Diagnostic Pathology Medical Group',
40 '1235186800' => 'Pathgroup Labs LLC',
41 '1598760985' => 'Yosemite Pathology Medical Group',
44 /**
45 * Get lab's ID from the users table given its NPI. If none return 0.
47 * @param string $npi The lab's NPI number as known to the system
48 * @return integer The numeric value of the lab's address book entry
50 function getLabID($npi)
52 $lrow = sqlQuery(
53 "SELECT ppid FROM procedure_providers WHERE " .
54 "npi = ? ORDER BY ppid LIMIT 1",
55 array($npi)
57 if (empty($lrow['ppid'])) {
58 return 0;
61 return intval($lrow['ppid']);
64 if (!AclMain::aclCheckCore('admin', 'super')) {
65 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Load Compendium")]);
66 exit;
69 $form_step = isset($_POST['form_step']) ? trim($_POST['form_step']) : '0';
70 $form_status = isset($_POST['form_status' ]) ? trim($_POST['form_status' ]) : '';
72 if (!empty($_POST['form_import'])) {
73 $form_step = 1;
76 // When true the current form will submit itself after a brief pause.
77 $auto_continue = false;
79 // Set up main paths.
80 $EXPORT_FILE = $GLOBALS['temporary_files_dir'] . "/openemr_config.sql";
82 <html>
84 <head>
85 <?php Header::setupHeader(); ?>
86 <title><?php echo xlt('Load Compendium'); ?></title>
87 </head>
89 <body>
90 <div class="container mt-3">
91 <div class="row">
92 <div class="col-12">
93 <h2><?php echo xlt('Load Lab Compendium'); ?></h2>
94 </div>
95 </div>
96 <form class="jumbotron py-4" method='post' action='load_compendium.php' enctype='multipart/form-data'>
97 <table class="table table-borderless">
98 <?php if ($form_step == 0) { ?>
99 <tr>
100 <td class="text-nowrap">
101 <?php echo xlt('Vendor'); ?>
102 </td>
103 <td>
104 <select class='form-control' name='vendor'>
105 <?php foreach ($lab_npi as $key => $value) {
106 echo "<option value='" . attr($key) . "'";
107 if (!getLabID($key)) {
108 // Entries with no matching address book entry will be disabled.
109 echo " disabled";
111 echo ">" . text($key) . ": " . text($value) . "</option>";
112 } ?>
113 </select>
114 </td>
115 </tr>
116 <tr>
117 <td class="text-nowrap">
118 <?php echo xlt('Action'); ?>
119 </td>
120 <td>
121 <select class='form-control' name='action'>
122 <option value='1'><?php echo xlt('Load Order Definitions'); ?></option>
123 <option value='2'><?php echo xlt('Load Order Entry Questions'); ?></option>
124 <option value='3'><?php echo xlt('Load OE Question Options'); ?></option>
125 </select>
126 </td>
127 </tr>
128 <tr>
129 <td class="text-nowrap">
130 <?php echo xlt('Container Group Name'); ?>
131 </td>
132 <td>
133 <select class='form-control' name='group'>
134 <?php
135 $gres = sqlStatement("SELECT procedure_type_id, name FROM procedure_type " .
136 "WHERE procedure_type = 'grp' ORDER BY name, procedure_type_id");
137 while ($grow = sqlFetchArray($gres)) {
138 echo "<option value='" . attr($grow['procedure_type_id']) . "'>" .
139 text($grow['name']) . "</option>";
141 </select>
142 </td>
143 </tr>
144 <tr>
145 <td class="text-nowrap">
146 <?php echo xlt('File to Upload'); ?>
147 </td>
148 <td>
149 <div class="custom-file">
150 <label class="custom-file-label" for="userfile"><?php echo xlt('Choose file'); ?></label>
151 <input type='hidden' class="custom-file-input" name='MAX_FILE_SIZE' value='4000000' />
152 <input class='form-control' type='file' name='userfile' id='userfile' />
153 </div>
154 </td>
155 </tr>
156 <tr>
157 <td colspan="2">
158 <button type="submit" class="btn btn-primary btn-save" value='<?php echo xla('Submit'); ?>'>
159 <?php echo xlt('Submit'); ?>
160 </button>
161 </td>
162 </tr>
163 <?php }
165 echo " <tr>\n";
166 echo " <td colspan='2'>\n";
168 if ($form_step == 1) {
169 // Process uploaded config file.
170 if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
171 $form_vendor = $_POST['vendor'];
172 $form_action = intval($_POST['action']);
173 $form_group = intval($_POST['group']);
174 $lab_id = getLabID($form_vendor);
176 $form_status .= xlt('Applying') . "...<br />";
177 echo nl2br(text($form_status));
179 $fhcsv = fopen($_FILES['userfile']['tmp_name'], "r");
181 if ($fhcsv) {
182 // Vendor = Pathgroup
184 if ($form_vendor == '1235186800') {
185 if ($form_action == 1) { // load compendium
186 // Mark all "ord" rows having the indicated parent as inactive.
187 sqlStatement(
188 "UPDATE procedure_type SET activity = 0 WHERE " .
189 "parent = ? AND procedure_type = 'ord'",
190 array($form_group)
193 // What should be uploaded is the "Compendium" spreadsheet provided by
194 // PathGroup, saved in "Text CSV" format from OpenOffice, using its
195 // default settings. Values for each row are:
196 // 0: Order Code : mapped to procedure_code of order type
197 // 1: Order Name : mapped to name of order type
198 // 2: Result Code : mapped to procedure_code of result type
199 // 3: Result Name : mapped to name of result type
201 while (!feof($fhcsv)) {
202 $acsv = fgetcsv($fhcsv, 4096);
203 if (count($acsv) < 4 || $acsv[0] == "Order Code") {
204 continue;
207 $standard_code = empty($acsv[2]) ? '' : ('CPT4:' . $acsv[2]);
209 // Update or insert the order row, if not already done.
210 $trow = sqlQuery(
211 "SELECT * FROM procedure_type WHERE " .
212 "parent = ? AND procedure_code = ? AND procedure_type = 'ord' " .
213 "ORDER BY procedure_type_id DESC LIMIT 1",
214 array($form_group, $acsv[0])
216 if (empty($trow['procedure_type_id']) || $trow['activity'] == 0) {
217 if (empty($trow['procedure_type_id'])) {
218 $ptid = sqlInsert(
219 "INSERT INTO procedure_type SET " .
220 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?",
221 array($form_group, $acsv[1], $lab_id, $acsv[0], 'ord')
223 } else {
224 $ptid = $trow['procedure_type_id'];
225 sqlStatement(
226 "UPDATE procedure_type SET " .
227 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " .
228 "activity = 1 WHERE procedure_type_id = ?",
229 array($form_group, $acsv[1], $lab_id, $acsv[0], 'ord', $ptid)
233 sqlStatement(
234 "UPDATE procedure_type SET activity = 0 WHERE " .
235 "parent = ? AND procedure_type = 'res'",
236 array($ptid)
240 // Update or insert the result row.
241 // Not sure we need this, but what the hell.
242 $trow = sqlQuery(
243 "SELECT * FROM procedure_type WHERE " .
244 "parent = ? AND procedure_code = ? AND procedure_type = 'res' " .
245 "ORDER BY procedure_type_id DESC LIMIT 1",
246 array($ptid, $acsv[2])
248 // The following should always be true, otherwise duplicate input row.
249 if (empty($trow['procedure_type_id']) || $trow['activity'] == 0) {
250 if (empty($trow['procedure_type_id'])) {
251 sqlStatement(
252 "INSERT INTO procedure_type SET " .
253 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?",
254 array($ptid, $acsv[3], $lab_id, $acsv[2], 'res')
256 } else {
257 $resid = $trow['procedure_type_id'];
258 sqlStatement(
259 "UPDATE procedure_type SET " .
260 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " .
261 "activity = 1 WHERE procedure_type_id = ?",
262 array($ptid, $acsv[3], $lab_id, $acsv[2], 'res', $resid)
265 } // end if
266 } // end while
267 // end SFTP
268 } elseif ($form_action == 2) { // load questions
269 // Delete the vendor's current questions.
270 sqlStatement(
271 "DELETE FROM procedure_questions WHERE lab_id = ?",
272 array($lab_id)
275 // What should be uploaded is the "AOE Questions" spreadsheet provided by
276 // PathGroup, saved in "Text CSV" format from OpenOffice, using its
277 // default settings. Values for each row are:
278 // 0: OBRCode (order code)
279 // 1: Question Code
280 // 2: Question
281 // 3: "Tips"
282 // 4: Required (0 = No, 1 = Yes)
283 // 5: Maxchar (integer length)
284 // 6: FieldType (FT = free text, DD = dropdown, ST = string)
286 $seq = 0;
287 $last_code = '';
288 while (!feof($fhcsv)) {
289 $acsv = fgetcsv($fhcsv, 4096);
290 if (count($acsv) < 7 || $acsv[4] == "Required") {
291 continue;
294 $code = trim($acsv[0]);
295 if (empty($code)) {
296 continue;
299 if ($code != $last_code) {
300 $seq = 0;
301 $last_code = $code;
304 ++$seq;
306 $required = 0 + $acsv[4];
307 $maxsize = 0 + $acsv[5];
308 $fldtype = 'T';
310 // Figure out field type.
311 if ($acsv[6] == 'DD') {
312 $fldtype = 'S';
313 } elseif (stristr($acsv[3], 'mm/dd/yy') !== false) {
314 $fldtype = 'D';
315 } elseif (stristr($acsv[3], 'wks_days') !== false) {
316 $fldtype = 'G';
317 } elseif ($acsv[6] == 'FT') {
318 $fldtype = 'T';
319 } else {
320 $fldtype = 'N';
323 $qrow = sqlQuery(
324 "SELECT * FROM procedure_questions WHERE " .
325 "lab_id = ? AND procedure_code = ? AND question_code = ?",
326 array($lab_id, $code, $acsv[1])
329 if (empty($qrow['question_code'])) {
330 sqlStatement(
331 "INSERT INTO procedure_questions SET " .
332 "lab_id = ?, procedure_code = ?, question_code = ?, question_text = ?, " .
333 "required = ?, maxsize = ?, fldtype = ?, options = '', tips = ?,
334 activity = 1, seq = ?",
335 array($lab_id, $code, $acsv[1], $acsv[2], $required, $maxsize, $fldtype, $acsv[3], $seq)
337 } else {
338 sqlStatement(
339 "UPDATE procedure_questions SET " .
340 "question_text = ?, required = ?, maxsize = ?, fldtype = ?, " .
341 "options = '', tips = ?, activity = 1, seq = ? WHERE " .
342 "lab_id = ? AND procedure_code = ? AND question_code = ?",
343 array($acsv[2], $required, $maxsize, $fldtype, $acsv[3], $seq,
344 $lab_id,
345 $code,
346 $acsv[1])
349 } // end while
350 // end load questions
351 } elseif ($form_action == 3) { // load question options
352 // What should be uploaded is the "AOE Options" spreadsheet provided
353 // by YPMG, saved in "Text CSV" format from OpenOffice, using its
354 // default settings. Values for each row are:
355 // 0: OBXCode (question code)
356 // 1: OBRCode (procedure code)
357 // 2: Option1 (option text)
358 // 3: Optioncode (the row is duplicated for each possible value)
360 while (!feof($fhcsv)) {
361 $acsv = fgetcsv($fhcsv, 4096);
362 if (count($acsv) < 4 || ($acsv[0] == "OBXCode")) {
363 continue;
366 $pcode = trim($acsv[1]);
367 $qcode = trim($acsv[0]);
368 $options = trim($acsv[2]) . ':' . trim($acsv[3]);
369 if (empty($pcode) || empty($qcode)) {
370 continue;
373 $qrow = sqlQuery(
374 "SELECT * FROM procedure_questions WHERE " .
375 "lab_id = ? AND procedure_code = ? AND question_code = ?",
376 array($lab_id, $pcode, $qcode)
378 if (empty($qrow['procedure_code'])) {
379 continue; // should not happen
380 } else {
381 if ($qrow['activity'] == '1' && $qrow['options'] !== '') {
382 $options = $qrow['options'] . ';' . $options;
385 sqlStatement(
386 "UPDATE procedure_questions SET " .
387 "options = ? WHERE " .
388 "lab_id = ? AND procedure_code = ? AND question_code = ?",
389 array($options, $lab_id, $pcode, $qcode)
392 } // end while
393 } // end load questions
394 } // End Pathgroup
396 // Vendor = YPMG or DPMG
398 if ($form_vendor == '1598760985' || $form_vendor == '1235138868') {
399 if ($form_action == 1) { // load compendium
400 // Mark all "ord" rows having the indicated parent as inactive.
401 sqlStatement(
402 "UPDATE procedure_type SET activity = 0 WHERE " .
403 "parent = ? AND procedure_type = 'ord'",
404 array($form_group)
406 // What should be uploaded is the Order Compendium spreadsheet provided
407 // by YPMG, saved in "Text CSV" format from OpenOffice, using its
408 // default settings. Values for each row are:
409 // 0: Order code : mapped to procedure_code
410 // 1: Order Name : mapped to name
411 // 2: Result Code : ignored (will cause multiple occurrences of the same order code)
412 // 3: Result Name : ignored
414 while (!feof($fhcsv)) {
415 $acsv = fgetcsv($fhcsv, 4096);
416 $ordercode = trim($acsv[0]);
417 if (count($acsv) < 2 || $ordercode == "Order Code") {
418 continue;
421 $trow = sqlQuery(
422 "SELECT * FROM procedure_type WHERE " .
423 "parent = ? AND procedure_code = ? AND procedure_type = 'ord' " .
424 "ORDER BY procedure_type_id DESC LIMIT 1",
425 array($form_group, $ordercode)
428 if (empty($trow['procedure_type_id'])) {
429 sqlStatement(
430 "INSERT INTO procedure_type SET " .
431 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " .
432 "activity = 1",
433 array($form_group, trim($acsv[1]), $lab_id, $ordercode, 'ord')
435 } else {
436 sqlStatement(
437 "UPDATE procedure_type SET " .
438 "parent = ?, name = ?, lab_id = ?, procedure_code = ?, procedure_type = ?, " .
439 "activity = 1 " .
440 "WHERE procedure_type_id = ?",
441 array($form_group, trim($acsv[1]), $lab_id, $ordercode, 'ord',
442 $trow['procedure_type_id'])
446 } elseif ($form_action == 2) { // load questions
447 // Mark the vendor's current questions inactive.
448 sqlStatement(
449 "DELETE FROM procedure_questions WHERE lab_id = ?",
450 array($lab_id)
453 // What should be uploaded is the "AOE Questions" spreadsheet provided
454 // by YPMG, saved in "Text CSV" format from OpenOffice, using its
455 // default settings. Values for each row are:
456 // 0: Order Code
457 // 1: Question Code
458 // 2: Question
459 // 3: Is Required (always "false")
460 // 4: Field Type ("Free Text", "Pre-Defined Text" or "Drop Down";
461 // "Drop Down" was previously "Multiselect Pre-Defined Text" and
462 // indicates that more than one choice is allowed)
463 // 5: Response (just one; the row is duplicated for each possible value)
465 $seq = 0;
466 $last_code = '';
467 while (!feof($fhcsv)) {
468 $acsv = fgetcsv($fhcsv, 4096);
469 if (count($acsv) < 5 || ($acsv[3] !== "false" && $acsv[3] !== "true")) {
470 continue;
473 $pcode = trim($acsv[0]);
474 $qcode = trim($acsv[1]);
475 $required = strtolower(substr($acsv[3], 0, 1)) == 't' ? 1 : 0;
476 $options = trim($acsv[5]);
477 if (empty($pcode) || empty($qcode)) {
478 continue;
481 if ($pcode != $last_code) {
482 $seq = 0;
483 $last_code = $pcode;
486 ++$seq;
488 // Figure out field type.
489 $fldtype = 'T';
490 if (strpos($acsv[4], 'Drop') !== false) {
491 $fldtype = 'S';
492 } elseif (strpos($acsv[4], 'Multiselect') !== false) {
493 $fldtype = 'S';
496 $qrow = sqlQuery(
497 "SELECT * FROM procedure_questions WHERE " .
498 "lab_id = ? AND procedure_code = ? AND question_code = ?",
499 array($lab_id, $pcode, $qcode)
502 // If this is the first option value and it's a multi-select list,
503 // then prepend '+;' here to indicate that. YPMG does not use those
504 // but keep this note here for future reference.
506 if (empty($qrow['procedure_code'])) {
507 sqlStatement(
508 "INSERT INTO procedure_questions SET " .
509 "lab_id = ?, procedure_code = ?, question_code = ?, question_text = ?, " .
510 "fldtype = ?, required = ?, options = ?, seq = ?, activity = 1",
511 array($lab_id, $pcode, $qcode, trim($acsv[2]), $fldtype, $required, $options, $seq)
513 } else {
514 if ($qrow['activity'] == '1' && $qrow['options'] !== '' && $options !== '') {
515 $options = $qrow['options'] . ';' . $options;
518 sqlStatement(
519 "UPDATE procedure_questions SET " .
520 "question_text = ?, fldtype = ?, required = ?, options = ?, activity = 1 WHERE " .
521 "lab_id = ? AND procedure_code = ? AND question_code = ?",
522 array(trim($acsv[2]), $fldtype, $required, $options, $lab_id, $pcode, $qcode)
525 } // end while
526 } // end load questions
527 } // End YPMG
529 fclose($fhcsv);
530 } else {
531 echo "<p class='text-danger'>" . xlt('Internal error accessing uploaded file!') . "</p>";
532 $form_step = -1;
534 } else {
535 echo "<p class='text-danger'>" . xlt('Upload failed!') . "</p>";
536 $form_step = -1;
539 $auto_continue = true;
542 if ($form_step == 2) {
543 $form_status .= xlt('Done') . ".";
544 echo nl2br(text($form_status));
547 ++$form_step;
549 </td>
550 </tr>
551 </table>
552 <input type='hidden' name='form_step' value='<?php echo attr($form_step); ?>' />
553 <input type='hidden' name='form_status' value='<?php echo attr($form_status); ?>' />
554 </form>
556 <?php
557 ob_flush();
558 flush();
561 <?php if ($auto_continue) { ?>
562 <script>
563 setTimeout("document.forms[0].submit();", 500);
564 </script>
565 <?php } ?>
566 </div>
568 </body>
569 </html>