New Http Rest Client (#2023)
[openemr.git] / library / edihistory / edih_csv_inc.php
blob8ce9adc559a5963c68a3abc5931f8fc6591d769f
1 <?php
2 /**
3 * edih_csv_inc.php
5 * Copyright 2012 Kevin McCormick
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 3 or later. You should have
15 * received a copy of the GNU General Public License along with this program;
16 * if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * <http://opensource.org/licenses/gpl-license.php>
20 * @author Kevin McCormick
21 * @link: http://www.open-emr.org
22 * @package OpenEMR
23 * @subpackage ediHistory
27 * The purpose of this file is to hold functions of general utility for
28 * my edi_claim_history project. It began as a php "class" but I am now
29 * thinking that instantiating the class is too much bother and probably
30 * a waste of memory, since the contents of the file have to be read into
31 * memory anyway.
33 * <pre>
34 * ******* important *********
35 * function csv_parameters($type="ALL")
36 * This function must have the correct values or nothing will work
37 * function csv_verify_file( $file_path, $type, $val_array=FALSE )
38 * critical for file verification and x12 parsing
39 * function (in ibr_uploads.php) ibr_upload_match_file($param_ar, $fidx, &$html_str)
40 * contains a regular expression that must be correct
42 * Also, the constant IBR_HISTORY_DIR must be correct
43 * **************************
44 * </pre>
46 * The claim_history x12 files are claim (837) acknowledgement (997/999) claim status (277) and claim payment (835)
47 * Also eligibility request (270) and eligibility response (271)
49 * <pre>
50 * Basic workflow:
51 * Each file type has a row in the array from csv_paramaters()
52 * type directory files_csv claims_csv column regex
54 * 1. open submitted file in edih_x12_class to verify and produce properties
55 * 2. Read the parameters array and choose the parameters using 'type'
56 * 2. Search the matched type 'directory' for the filename files matching the 'regex' regular expressions and
57 * compare the results to the files listed in the 'files_csv' files.csv record -- unmatched files are "new"
58 * 3. Each "new" x12 file should be read by csv_x12_segments -- returns array('path', 'delimiters', 'segments')
59 * ibr, ebr, ack -- basically Availity formats have their own read functions
60 * 4. Pass the array to various functions which parse for claims information
61 * 5. Write the results to files.csv or claims.csv and create html output for display
63 * 6. Other outputs as called for in ibr_history.php -- from user input from claim_history.html
64 * </pre>
66 * Key usability issue is the "new" files are in the users home directory -- downloaded there
67 * while the OpenEMR is on the server -- so there is a basic issue of access to the files
69 * The ibr_uploads.php script handles uploads of zip archives or multiple file uploads
71 * The csv data files are just php written .csv files, so anything different may cause errors
72 * You can open and edit them in OpenOffice, but you must save them in "original format"
74 * TO_DO Some type of "find in files" search would be helpful for locating all references to a claim, patient, etc.
75 * [ grep -nHIrF 'findtext']
77 * TO_DO functions to zip old files, put them aside, and remove them from csv tables
80 ///**
81 // * a security measure to prevent direct web access to this file
82 // */
83 // if (!defined('SITE_IN')) die('Direct access not allowed!');
84 // $GLOBALS['OE_EDIH_DIR'] $GLOBALS['OE_SITE_DIR']
86 /* *********** GLOBALS used for testing only **********
88 // //$GLOBALS['OE_SITE_DIR'].'/edi/history';
89 //$OE_SITES_BASE = $GLOBALS['OE_SITE_DIR'];
90 //$OE_SITE_DIR = $OE_SITES_BASE.'/testing';
91 //$OE_EDIH_DIR = $OE_SITE_DIR.'/edi/history';
93 /* ***********
95 /**
96 * Constant that is checked in included files to prevent direct access.
97 * concept taken from Joomla
99 define('_EDIH', 1);
100 //DIRECTORY_SEPARATOR;
101 if (!defined('DS')) {
102 define('DS', DIRECTORY_SEPARATOR);
106 * Log messages to the log file
108 * @param string $msg_str the log message
109 * @return int number of characters written
111 function csv_edihist_log($msg_str)
114 //$dir = dirname(__FILE__).DS.'log';
115 //$dir = $GLOBALS['OE_EDIH_DIR'].DS.'log';
116 //$logfile = $GLOBALS['OE_EDIH_DIR'] . "/log/edi_history_log.txt";
117 $logfile = 'edih_log_'.date('Y-m-d').'.txt';
118 $dir = csv_edih_basedir().DS.'log';
119 $rslt = 0;
120 if (is_string($msg_str) && strlen($msg_str)) {
121 $tm = date('Ymd:Hms') . ' ' . $msg_str . PHP_EOL;
123 $rslt = file_put_contents($dir.DS.$logfile, $tm, FILE_APPEND);
124 } else {
126 $fnctn = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
127 csv_edihist_log('invalid message string '.$fnctn);
131 return $rslt; // number of characters written
135 * read the edi_history_log.txt file into an
136 * html formatted ordered list
138 * @return string
140 function csv_log_html($logname = '')
142 check_file_dir_name($logname);
143 $html_str = "<div class='filetext'>".PHP_EOL."<ol class='logview'>".PHP_EOL;
144 $fp = csv_edih_basedir().DS.'log'.DS.$logname;
145 if (is_file($fp)) {
146 $fh = fopen($fp, 'r');
147 if ($fh !== false) {
148 while (($buffer = fgets($fh)) !== false) {
149 $html_str .= "<li>" . text($buffer) . "</li>".PHP_EOL;
152 $html_str .= "</ol>".PHP_EOL."</div>".PHP_EOL;
153 if (!feof($fh)) {
154 $html_str .= "<p>Error in logfile: unexpected file ending</p>".PHP_EOL;
157 fclose($fh);
158 } else {
159 $html_str = "<p>Error: unable to open log file</p>".PHP_EOL;
163 return $html_str;
168 * list log files and store old logs in an archive
170 * @param bool
171 * @return array (json)
173 function csv_log_manage($list = true)
176 //$dir = dirname(__FILE__).DS.'log';
177 $dir = csv_edih_basedir().DS.'log';
178 $list_ar = array();
179 $old_ar = array();
180 $lognames = scandir($dir);
181 if ($list) {
182 foreach ($lognames as $log) {
183 if (!strpos($log, '_log_')) {
184 continue;
187 $list_ar[] = $log;
190 $s = (count($list_ar)) ? rsort($list_ar) : false;
192 return json_encode($list_ar);
194 } else {
195 // list is false, must be archive
196 $datetime1 = date_create(date('Y-m-d'));
198 foreach ($lognames as $log) {
199 if ($log == '.' || $log == '..') {
200 continue;
204 $pos1 = strrpos($log, '_');
205 if ($pos1) {
206 $ldate = substr($log, $pos1+1, 10);
207 $datetime2 = date_create($ldate);
208 $interval = date_diff($datetime1, $datetime2);
209 //echo '== date difference '.$ldate.' '.$interval->format('%R%a days').PHP_EOL;
210 if ($interval->format('%R%a') < -7) {
211 // older log files are put in zip archive
212 if (is_file($dir.DS.$log)) {
213 $old_ar[] = $log;
221 $ok = false;
222 $archname = $dir.DS.'edih-log-archive.zip';
223 $filelimit = 200;
225 if (count($old_ar)) {
226 $zip = new ZipArchive;
227 if (is_file($archname)) {
228 $ok = $zip->open($archname, ZipArchive::CHECKCONS);
229 } else {
230 $ok = $zip->open($archname, ZipArchive::CREATE);
234 if ($ok) {
235 if ($zip->numFiles >= $filelimit) {
236 $zip->close();
237 $dte = $datetime1->format('Ymd');
238 $ok = rename($dir.DS.$archname, $dir.DS.$dte.'_'.$archname);
239 csv_edihist_log('csv_log_archive: rename full archive '.$dte.'_'.$archname);
240 if ($ok) {
241 $ok = $zip->open($archname, ZipArchive::CREATE);
242 if (!$ok) {
243 csv_edihist_log('csv_log_archive: cannot create '.$archname);
245 } else {
246 csv_edihist_log('csv_log_archive: cannot rename '.$archname);
251 if ($ok) {
252 foreach ($old_ar as $lg) {
253 if (is_file($dir.DS.$lg)) {
254 $a = $zip->addFile($dir.DS.$lg, $lg);
255 if ($a) {
256 csv_edihist_log('csv_log_archive: add to archive '.$lg);
257 } else {
258 csv_edihist_log('csv_log_archive: error archiving '.$lg);
263 $c = $zip->close();
264 if ($c) {
265 foreach ($old_ar as $lg) {
266 $u = unlink($dir.DS.$lg);
267 if ($u) {
268 continue;
269 } else {
270 csv_edihist_log('csv_log_archive: error removing '.$dir.DS.$lg);
273 } else {
274 csv_edihist_log('csv_log_archive: error closing log file archive');
276 } else {
277 csv_edihist_log('csv_log_manage: error failed to open '.$archname);
283 return json_encode($old_ar);
288 * open or save a user notes file
290 * @param string
291 * @param bool
292 * @return string
294 function csv_notes_file($content = '', $open = true)
297 $str_html = '';
298 //$fp = $GLOBALS['OE_EDIH_DIR'].'/edi_notes.txt';
299 $fp = csv_edih_basedir().DS.'archive'.DS.'edi_notes.txt';
300 if (! is_writable($fp)) {
301 $fh = fopen($fp, 'a+b');
302 fclose($fh);
305 // for retrieving notes
306 if ($open) {
307 // if contents were previously deleted by user and file is empty,
308 // the text 'empty' is put in content in save operation
309 $ftxt = file_get_contents($fp);
310 if ($ftxt === false) {
311 $str_html .= 'csv_notes_file: file error <br>'.PHP_EOL;
312 csv_edihist_log('csv_notes_file: file error');
315 if (substr($ftxt, 0, 5) == 'empty' && strlen($ftxt) == 5) {
316 $ftxt = '## '. date("F j, Y, g:i a");
317 } elseif (!$ftxt) {
318 $ftxt = '## '. date("F j, Y, g:i a");
321 $str_html .= PHP_EOL . text($ftxt) . PHP_EOL;
322 // next stanza for saving content
323 } elseif (strlen($content)) {
324 //echo "csv_notes_file: we have content<br>".PHP_EOL;
325 // use finfo php class
326 if (class_exists('finfo')) {
327 $finfo = new finfo(FILEINFO_MIME);
328 $mimeinfo = $finfo->buffer($content);
329 if (strncmp($mimeinfo, 'text/plain; charset=us-ascii', 28) !== 0) {
330 csv_edihist_log('csv_notes_file: invalid mime-type '.$mimeinfo);
331 $str_html = 'csv_notes_file: invalid mime-type <br>' . text($mimeinfo);
333 return $str_html;
335 } elseif (preg_match('/[^\x20-\x7E\x0A\x0D]|(<\?)|(<%)|(<asp)|(<ASP)|(#!)|(\$\{)|(<scr)|(<SCR)/', $content, $matches, PREG_OFFSET_CAPTURE)) {
336 csv_edihist_log('csv_notes_file: Filtered character in file content -- character: '.$matches[0][0].' position: '.$matches[0][1]);
337 $str_html .= 'Filtered character in file content not accepted <br>'. PHP_EOL;
338 $str_html .= ' character: ' . text($matches[0][0]) . ' position: ' . text($matches[0][1]) . '<br>' . PHP_EOL;
340 return $str_html;
342 } else {
343 $ftxt = ($content) ? $content : 'empty';
344 $saved = file_put_contents($fp, $ftxt);
345 $str_html .= ($saved) ? '<p>Save Error with notes file</p>' : '<p>Notes content saved</p>';
349 return $str_html;
353 * generates path to edi history files
355 * @return string|bool directory path
357 function csv_edih_basedir()
359 // should be something like /var/www/htdocs/openemr/sites/default
360 if (isset($GLOBALS['OE_SITE_DIR'])) {
361 // debug
362 //echo 'csv_edih_basedir OE_SITE_DIR '.$GLOBALS['OE_SITE_DIR'].'<br>'.PHP_EOL;
363 return $GLOBALS['OE_SITE_DIR'].DS.'edi'.DS.'history';
364 } else {
365 csv_edihist_log('csv_edih_basedir: failed to obtain OpenEMR Site directory');
366 return false;
371 * generates path to edi_history tmp dir for file upload operations
373 * @uses csv_edih_basedir()
374 * @return string directory path
376 function csv_edih_tmpdir()
379 $bdir = csv_edih_basedir();
380 $tdir = ($bdir) ? $bdir.DS.'tmp' : false;
381 //$systmp = sys_get_temp_dir();
382 //$systmp = stripcslashes($systmp);
383 //$systdir = $systmp.DS.'edihist';
384 //if ( $tdir && (is_dir($tdir) || mkdir($tdir, 0755) ) ) {
385 if ($tdir) {
386 return $tdir;
387 } else {
388 return false;
395 * Initial setup function
397 * Create the directory tree and write the column headers into the csv files
398 * This function will accept a directory argument and it appends the value
399 * from IBR_HISTORY_DIR to the path. Then a directory for each type of file
400 * and the csv files are created under that.
402 * @uses csv_parameters()
403 * @uses csv_table_header()
404 * @uses csv_edih_basedir()
406 * @param string &$out_str referenced, should be created in calling function
407 * @return boolean
409 function csv_setup()
412 $isOK = false;
413 $out_str = '';
414 $chr = 0;
415 // $GLOBALS['OE_SITE_DIR'] should be like /var/www/htdocs/openemr/sites/default
416 $sitedir = $GLOBALS['OE_SITE_DIR'];
417 //$sitedir = csv_edih_basedir();
419 if (is_readable($sitedir)) {
420 $basedir = $sitedir.DS.'edi';
421 $edihist_dir = $basedir.DS.'history';
422 $csv_dir = $edihist_dir.DS.'csv';
423 $archive_dir = $edihist_dir.DS.'archive';
424 $log_dir = $edihist_dir.DS.'log';
425 $tmp_dir = $edihist_dir.DS.'tmp';
426 } else {
427 //csv_edihist_log('setup: failed to obtain OpenEMR Site directory');
428 echo 'setup: failed to obtain OpenEMR Site directory<br>'.PHP_EOL;
429 return false;
433 if (is_writable($basedir)) {
434 $isOK = true;
435 //csv_edihist_log('setup: directory '.$basedir);
436 $out_str .= 'EDI_History Setup should not overwrite existing data.<br>'.PHP_EOL;
437 $out_str .= 'Setup: directory ' . text($basedir) . '<br>'.PHP_EOL;
439 if (is_dir($edihist_dir) || mkdir($edihist_dir, 0755)) {
440 $out_str .= 'created folder ' . text($edihist_dir) . '<br>'.PHP_EOL;
441 $isOK = true;
442 if (is_dir($csv_dir) || mkdir($csv_dir, 0755)) {
443 $out_str .= 'created folder ' . text($csv_dir) . '<br>'.PHP_EOL;
444 $isOK = true;
445 } else {
446 $isOK = false;
447 $out_str .= 'Setup: Failed to create csv folder... '.'<br>'.PHP_EOL;
448 die('Failed to create csv folder... ' . text($archive_dir));
451 if (is_dir($archive_dir) || mkdir($archive_dir, 0755)) {
452 $out_str .= 'created folder ' . text($archive_dir) . '<br>'.PHP_EOL;
453 $isOK = true;
454 } else {
455 $isOK = false;
456 $out_str .= 'Setup: Failed to create archive folder... '.'<br>'.PHP_EOL;
457 die('Failed to create archive folder... ');
460 if (is_dir($log_dir) || mkdir($log_dir, 0755)) {
461 $out_str .= 'created folder ' . text($log_dir) . '<br>'.PHP_EOL;
462 $isOK = true;
463 } else {
464 $isOK = false;
465 $out_str .= 'Setup: Failed to create log folder... '.'<br>'.PHP_EOL;
466 die('Failed to create log folder... ');
469 if (is_dir($tmp_dir) || mkdir($tmp_dir, 0755)) {
470 $out_str .= 'created folder ' . text($tmp_dir) . PHP_EOL;
471 $isOK = true;
472 } else {
473 $isOK = false;
474 $out_str .= 'Setup: Failed to create tmp folder... '.'<br>'.PHP_EOL;
475 die('Failed to create tmp folder... ');
477 } else {
478 $isOK = false;
479 $out_str .= 'Setup failed: cannot write to folder ' . text($basedir) . '<br>'.PHP_EOL;
480 die('Setup failed: cannot write to ' . text($basedir));
482 } else {
483 $isOK = false;
484 $out_str .= 'Setup: Failed to create history folder... '.'<br>'.PHP_EOL;
485 die('Failed to create history folder... ' . text($edihist_dir));
488 if ($isOK) {
489 $p_ar = csv_parameters('ALL');
490 $old_csv = array('f837'=>'batch', 'f835'=>'era');
491 foreach ($p_ar as $key => $val) {
492 // rename existing csv files to old_filename
493 if (is_dir($csv_dir)) {
494 if ($dh = opendir($csv_dir)) {
495 while (($file = readdir($dh)) !== false) {
496 if (is_file($csv_dir.DS.$file) && strpos($file, 'csv')) {
497 $rn = rename($csv_dir.DS.$file, $csv_dir.DS.'old_'.$file);
498 if ($rn) {
499 $out_str .= 'renamed csv/' . text($file) . ' to old_' . text($file) . '<br />'.PHP_EOL;
500 } else {
501 $out_str .= 'attempt to rename csv/' . text($file) . ' failed<br />'.PHP_EOL;
509 // make the edi files storage subdirs
510 $tp = $p_ar[$key]['type'];
511 $type_dir = $p_ar[$key]['directory'];
513 if (is_dir($type_dir)) {
514 $out_str .= 'folder for ' . text($tp) . ' exists ' . text($type_dir) . '<br>'.PHP_EOL;
515 } elseif (mkdir($type_dir, 0755)) {
516 if ($tp == 'f835') {
517 // in upgrade case the f835 directory should not exist
518 // move 'era' files from /era to /f835
519 if (is_dir($edihist_dir.DS.'era')) {
520 $fct = 0;
521 $rct = 0;
522 if ($dh = opendir($edihist_dir.DS.'era')) {
523 while (($file = readdir($dh)) !== false) {
524 if (is_file($edihist_dir.DS.'era'.DS.$file)) {
525 $rct++;
526 $rn = rename($edihist_dir.DS.'era'.DS.$file, $type_dir.DS.$file);
527 $fct = ($rn) ? $fct + 1 : $fct;
532 $out_str .= 'created type folder ' . text($type_dir) . ' and moved ' . text($fct) . ' of ' . text($rct) . ' files from /era<br>'.PHP_EOL;
534 } else {
535 $out_str .= 'created type folder ' . text($type_dir) . '<br>'.PHP_EOL;
537 } else {
538 $out_str .= 'Setup failed to create directory for ' . text($tp) . '<br>'.PHP_EOL;
541 } else {
542 $out_str .= 'Setup failed: Can not create directories <br>'.PHP_EOL;
545 if ($isOK) {
546 csv_edihist_log($out_str);
547 return true;
548 } else {
549 return $out_str;
555 * Empty all contents of tmp dir /edi/history/tmp
557 * @uses csv_edih_tmpdir()
558 * @param none
559 * @return bool
561 function csv_clear_tmpdir()
564 $tmpdir = csv_edih_tmpdir();
565 if (basename($tmpdir) != 'tmp') {
566 csv_edihist_log('tmp dir not /edi/history/tmp');
567 return false;
570 $tmp_files = scandir($tmpdir);
571 if (count($tmp_files) > 2) {
572 foreach ($tmp_files as $idx => $tmpf) {
573 if ($tmpf == "." || $tmpf == "..") {
574 // can't delete . and ..
575 continue;
576 } elseif (is_file($tmpdir.DS.$tmpf)) {
577 unlink($tmpdir.DS.$tmpf);
578 } elseif (is_dir($tmpdir.DS.$tmpf)) {
579 $tdir_ar = scandir($tmpdir.DS.$tmpf);
580 foreach ($tdir_ar as $tfn) {
581 if ($tfn == "." || $tfn == "..") {
582 continue;
583 } elseif (is_file($tmpdir.DS.$tmpf.DS.$tfn)) {
584 unlink($tmpdir.DS.$tmpf.DS.$tfn);
588 rmdir($tmpdir.DS.$tmpf);
593 $tmp_files = scandir($tmpdir);
594 if (count($tmp_files) > 2) {
595 csv_edihist_log('tmp dir contents remain in ... /edi/history/tmp');
596 return false;
597 } else {
598 return true;
603 * open and verify a default edih_x12_file object
605 * @uses csv_check_filepath()
607 * @param string filepath or filename
608 * @parm string file x12 type
609 * @return object edih_x12_file class
611 function csv_check_x12_obj($filepath, $type = '')
614 $x12obj = false;
615 $ok = false;
617 $fp = csv_check_filepath($filepath, $type);
619 if ($fp) {
620 $x12obj = new edih_x12_file($fp);
621 if ('edih_x12_file' == get_class($x12obj)) {
622 if ($x12obj->edih_valid() == 'ovigs') {
623 $ok = count($x12obj->edih_segments());
624 $ok = ($ok) ? count($x12obj->edih_envelopes()) : false;
625 $ok = ($ok) ? count($x12obj->edih_delimiters()) : false;
626 if (!$ok) {
627 csv_edihist_log("csv_check_x12_obj: object missing properties [$filepath]");
628 csv_edihist_log($x12obj->edih_message());
629 return false;
631 } else {
632 csv_edihist_log("csv_check_x12_obj: invalid object $filepath");
633 return false;
635 } else {
636 csv_edihist_log("csv_check_x12_obj: object not edih_x12_file $filepath");
637 return false;
639 } else {
640 csv_edihist_log("csv_check_x12_obj: invalid file path $filepath");
641 return false;
645 return $x12obj;
649 * Check that the file path we are working with is a readable file.
651 * If it is a file we have uploaded and we have only the file name
652 * this function will type the file and find it in the uploaded files directories
653 * and return the complete path.
655 * @uses csv_parameters()
656 * @param string $filename name of a file that is one of our types
657 * @param string $type optional; one of our file types
658 * @return string either an empty string or a readable filepath
660 function csv_check_filepath($filename, $type = 'ALL')
663 // if file is readable, just return it
664 if (is_file($filename) && is_readable($filename)) {
665 return $filename;
669 $goodpath = '';
670 $fp = '';
671 $fn = basename($filename);
673 if ($type && $type != 'ALL') {
674 $p = csv_parameters($type);
675 if (is_array($p) && array_key_exists('type', $p)) {
676 $fp = $p['directory'].DS.$fn;
678 } else {
679 $p_ar = csv_parameters("ALL");
680 foreach ($p_ar as $tp => $par) {
681 if (!$p_ar[$tp]['regex'] || !preg_match($p_ar[$tp]['regex'], $fn)) {
682 continue;
683 } else {
684 $fp = $p_ar[$tp]['directory'].DS.$fn;
685 break;
690 if (is_file($fp) && is_readable($fp)) {
691 $goodpath = realpath($fp);
695 return $goodpath;
699 * verify file type parameter
701 * @param string file type
702 * @param bool return GS02 code or fXXX
703 * @return string file type or empty
705 function csv_file_type($type, $gs_code = false)
708 if (!$type) {
709 csv_edihist_log('csv_file_type: invalid or missing type argument '.$type);
710 return false;
711 } else {
712 $tp_type = (string)$type;
716 if (strpos('|f837|batch|HC', $tp_type)) {
717 $tp = ($gs_code) ? 'HC' : 'f837';
718 } elseif (strpos('|f835|era|HP', $tp_type)) {
719 $tp = ($gs_code) ? 'HP' : 'f835';
720 } elseif (strpos('|f999|f997|ack|ta1|FA', $tp_type)) {
721 $tp = ($gs_code) ? 'FA' : 'f997';
722 } elseif (strpos('|f277|HN', $tp_type)) {
723 $tp = ($gs_code) ? 'HN' : 'f277';
724 } elseif (strpos('|f276|HR', $tp_type)) {
725 $tp = ($gs_code) ? 'HR' : 'f276';
726 } elseif (strpos('|f271|HB', $tp_type)) {
727 $tp = ($gs_code) ? 'HB' : 'f271';
728 } elseif (strpos('|f270|HS', $tp_type)) {
729 $tp = ($gs_code) ? 'HS' : 'f270';
730 } elseif (strpos('|f278|HI', $tp_type)) {
731 $tp = ($gs_code) ? 'HI' : 'f278';
732 } else {
733 $tp = '';
737 if (!$tp) {
738 csv_edihist_log('csv_file_type error: incorrect type '.$tp_type);
741 return $tp;
746 * The array that holds the various parameters used in dealing with files
748 * A key function since it holds the paths, columns, etc.
749 * Unfortunately, there is an issue with matching the type in * the case of the
750 * values '997', '277', '999', etc, becasue these strings may be recast
751 * from strings to integers, so the 'type' originally supplied is lost.
752 * This introduces an inconsistency when the 'type' is used in comparison tests.
753 * We call the csv_file_type() function to return a usable file type identifier.
754 * The 'datecolumn' and 'fncolumn' entries are used in csv_to_html() to filter by date
755 * or place links to files.
757 * @param string $type -- default = ALL or one of batch, ibr, ebr, dpr, f997, f277, era, ack, text
758 * @return array
760 function csv_parameters($type = 'ALL')
763 // This will need the OpenEMR 'oe_site_dir' to replace global
765 $p_ar = array();
767 $tp = ($type === 'ALL') ? $type : csv_file_type($type);
768 if (!$tp) {
769 csv_edihist_log('csv_parameters() error: incorrect type '.$type);
770 return $p_ar;
773 //$edihist_dir = $GLOBALS['OE_SITE_DIR'].'/edi/history';
774 $edihist_dir = csv_edih_basedir();
776 // the batch file directory is a special case - decide whether to use OpenEMR batch files or make our own copies
777 // OpenEMR copies each batch file to sites/default/edi and this project never writes to that directory
778 // batch reg ex -- '/20[01][0-9]-[01][0-9]-[0-3][0-9]-[0-9]{4}-batch*\.txt/' '/\d{4}-\d{2}-\d{2}-batch*\.txt$/'
780 $p_ar['f837'] = array('type'=>'f837', 'directory'=>$GLOBALS['OE_SITE_DIR'].DS.'edi', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f837.csv',
781 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f837.csv', 'filedate'=>'Date', 'claimdate'=>'SvcDate', 'regex'=>'/\-batch(.*)\.txt$/');
783 //$p_ar['csv'] = array("type"=>'csv', "directory"=>$edihist_dir.'/csv', "claims_csv"=>'ibr_parameters.csv',
784 // "files_csv"=>'', "column"=>'', "regex"=>'/\.csv$/');
785 $p_ar['f997'] = array('type'=>'f997', 'directory'=>$edihist_dir.DS.'f997', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f997.csv',
786 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f997.csv', 'filedate'=>'Date', 'claimdate'=>'RspDate', 'regex'=>'/\.(99[79]|ta1|ack)$/i');
787 $p_ar['f276'] = array('type'=>'f276', 'directory'=>$edihist_dir.DS.'f276', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f276.csv',
788 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f276.csv', 'filedate'=>'Date', 'claimdate'=>'ReqDate', 'regex'=>'/\.276([ei]br)?$/');
789 $p_ar['f277'] = array('type'=>'f277', 'directory'=>$edihist_dir.DS.'f277', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f277.csv',
790 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f277.csv', 'filedate'=>'Date', 'claimdate'=>'SvcDate', 'regex'=>'/\.277([ei]br)?$/i');
791 $p_ar['f270'] = array('type'=>'f270', 'directory'=>$edihist_dir.DS.'f270', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f270.csv',
792 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f270.csv', 'filedate'=>'Date', 'claimdate'=>'ReqDate', 'regex'=>'/\.270([ei]br)?$/i');
793 $p_ar['f271'] = array('type'=>'f271', 'directory'=>$edihist_dir.DS.'f271', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f271.csv',
794 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f271.csv', 'filedate'=>'Date', 'claimdate'=>'RspDate', 'regex'=>'/\.271([ei]br)?$/i');
795 $p_ar['f278'] = array('type'=>'f278', 'directory'=>$edihist_dir.DS.'f278', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f278.csv',
796 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f278.csv', 'filedate'=>'Date', 'claimdate'=>'FileDate', 'regex'=>'/\.278/');
797 // OpenEMR stores era files, but the naming scheme is confusing, so we will just use our own directory for them
798 $p_ar['f835'] = array('type'=>'f835', 'directory'=>$edihist_dir.DS.'f835', 'claims_csv'=>$edihist_dir.DS.'csv'.DS.'claims_f835.csv',
799 'files_csv'=>$edihist_dir.DS.'csv'.DS.'files_f835.csv', 'filedate'=>'Date', 'claimdate'=>'SvcDate', 'regex'=>'/835[0-9]{5}\.835*|\.(era|ERA|835)$/i');
801 if (array_key_exists($tp, $p_ar)) {
802 return $p_ar[$tp];
803 } else {
804 return $p_ar;
809 * determine if a csv table has data for select dropdown
811 * @param string default 'json'
812 * @return array json if argument is 'json'
814 function csv_table_select_list($outtp = 'json')
816 $optlist = array();
817 $labels = array('f835'=>'Payments', 'f837'=>'Claims', 'batch'=>'Claims', 'f277'=>'Status', 'f276'=>'Status Req',
818 'f997'=>'Ack','f271'=>'Benefit', 'f270'=>'Benefit Req', 'f278'=>'Auth');
820 $edihist_dir = csv_edih_basedir(); // $GLOBALS['OE_SITE_DIR'].'/edi/history'
821 $csvdir = $edihist_dir.DS.'csv';
822 $tbllist = scandir($csvdir);
823 $idx = 0;
824 foreach ($tbllist as $csvf) {
825 if ($csvf == "." || $csvf == "..") {
826 continue;
829 if (strpos($csvf, 'old') === 0) {
830 continue;
833 if (filesize($csvdir.DS.$csvf) < 70) {
834 continue;
837 if (substr($csvf, -1) == '~') {
838 continue;
841 $finfo = pathinfo($csvdir.DS.$csvf);
842 $fn = $finfo['filename'];
843 // e.g. files_f997
844 $tp = explode('_', $fn);
845 //$lblkey = $labels[$tp[1]];
846 $optlist[$tp[0]][$tp[1]]['fname'] = $fn;
847 $optlist[$tp[0]][$tp[1]]['desc'] = $tp[0].'-'.$labels[$tp[1]]; //$tp[1] .' '.$tp[0];
848 $idx++;
851 if ($outtp == 'json') {
852 return json_encode($optlist);
853 } else {
854 return $optlist;
859 * list existing archive files
861 * @param string default 'json'
862 * @return array json if argument is 'json'
864 function csv_archive_select_list($outtp = 'json')
867 $flist = array();
868 $archdir = csv_edih_basedir().DS.'archive';
870 // debug
871 csv_edihist_log("csv_archive_select_list: using $archdir");
873 $scan = scandir($archdir);
874 if (is_array($scan) && count($scan)) {
875 foreach ($scan as $s) {
876 if ($s == '.' || $s == '..') {
877 continue;
878 } elseif (strpos($s, 'note')) {
879 continue;
880 } else {
881 $flist[] = $s;
886 if ($outtp == 'json') {
887 return json_encode($flist);
888 } else {
889 return $flist;
894 * List files in the directory for the given type
896 * Write an entry in the log if an file is in the directory
897 * that does not match the type
899 * @uses csv_parameters()
900 * @param string $type a type from our list
901 * @return array
903 function csv_dirfile_list($type)
905 // return false if location is not appropriate
906 $tp = csv_file_type($type);
907 if (!$tp) {
908 csv_edihist_log("csv_dirfile_list error: incorrect type $type");
909 return false;
912 $params = csv_parameters($tp);
913 if (empty($params) || csv_singlerecord_test($params) == false) {
914 csv_edihist_log("csv_dirfile_list() error: incorrect type $type");
915 return false;
918 $search_dir = $params['directory'];
919 $ext_re = $params['regex'];
920 $dirfiles = array();
922 if (is_dir($search_dir)) {
923 if ($dh = opendir($search_dir)) {
924 while (($file = readdir($dh)) !== false) {
925 if ($file == '.' || $file == '..') {
926 continue;
927 } elseif ($tp == 'f837' && ($file == 'history' || $file == 'README.txt')) {
928 continue;
929 } elseif (is_file($search_dir.DS.$file)) {
930 $dirfiles[] = $file;
931 } else {
932 if ($tp == 'f837' && $file == 'history') {
933 continue;
936 csv_edihist_log("csv_dirfile_list $type : not a file $file");
939 } else {
940 csv_edihist_log("csv_dirfile_list $type : error in scan $search_dir");
942 } else {
943 csv_edihist_log("csv_dirfile_list $type : not a directory $search_dir");
947 return $dirfiles;
948 } // end function
952 * List files that are in the csv record
954 * @uses csv_parameters()
955 * @uses csv_table_header()
957 * @param string $type -- one of our types
958 * @return array
960 function csv_processed_files_list($type)
963 $tp = csv_file_type($type);
964 if (!$tp) {
965 csv_edihist_log("csv_processed_files_list: incorrect type $type");
966 return false;
969 $processed_files = array();
970 $param = csv_parameters($tp);
971 $hdr_ar = csv_table_header($tp, 'file');
972 if (is_array($hdr_ar)) {
973 foreach ($hdr_ar as $k => $hd) {
974 if ($hd == 'FileName') {
975 $csv_col = $k;
976 break;
981 $csv_col = (isset($csv_col)) ? $csv_col : 1;
982 $csv_file = $param['files_csv'];
983 //if ($tp == 'dpr') {
984 //$csv_file = $param['claims_csv'];
985 //$csv_col = '5';
986 //} else {
987 //$csv_file = $param['files_csv'];
990 //$idx = 0;
991 if (is_file($csv_file)) {
992 if (($fh1 = fopen($csv_file, "r")) !== false) {
993 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
994 $processed_files[] = $data[$csv_col];
996 //if ($idx) { $processed_files[] = $data[$csv_col]; }
997 // skip the header row
998 //$idx++;
1001 fclose($fh1);
1002 } else {
1003 csv_edihist_log("csv_list_processed_files: failed to access $csv_file");
1004 return false;
1006 } else {
1007 // first run - no file exists
1008 csv_edihist_log("csv_processed_files_list: csv file does not exist ".basename($csv_file));
1011 // remove the header row, but avoid NULL or false
1012 $ret_ar = (empty($processed_files)) ? $processed_files : array_slice($processed_files, 1);
1013 return $ret_ar;
1014 } // end function
1018 * Give an array of files in the storage directories that are not in the csv record
1020 * @param string $type -- one of our types
1021 * @return array
1023 function csv_newfile_list($type)
1026 $ar_new = array();
1027 $tp = csv_file_type($type);
1028 if (!$tp) {
1029 csv_edihist_log('csv_newfile_list: incorrect type '.$type);
1030 return false;
1034 $dir_files = csv_dirfile_list($tp);
1035 $csv_files = csv_processed_files_list($tp);
1037 // $dir_files should come first in array_diff()
1038 if (empty($dir_files)) {
1039 $ar_new = array();
1040 } elseif (empty($csv_files) || is_null($csv_files)) {
1041 $ar_new = $dir_files;
1042 } else {
1043 $ar_new = array_diff($dir_files, $csv_files);
1047 return $ar_new;
1051 * Parse 997 IK3 error segment to identify segment causing rejection
1052 * The error segment string is specially created in edih_997_csv_data()
1053 * Simple analysis, but the idea is just to identify the bad segment
1055 * @param string error segment from edih_997_csv_data()
1056 * @param bool true if only the 1st segmentID is wanted
1057 * return array|string
1059 function edih_errseg_parse($err_seg, $id = false)
1061 // ['err_seg'] = '|IK3*segID*segpos*loop*errcode*bht03syn|CTX-IK3*transID*segID*segpos*elempos
1062 // |IK4*elempos*errcode*elem*|CTX-IK4*segID*segpos*elempos
1064 // note: multiple IK3 segments are allowed in 997/999 x12
1066 $ret_ar = array();
1067 if (!$err_seg || strpos($err_seg, 'IK3') === false) {
1068 csv_edihist_log('edih_errseg_parse: invalid argument');
1069 return $ret_ar;
1072 //'|IK3*segID*segpos*loop*errcode*bht03syn|CTX-IK3*segID*segPos*loopLS*elemPos:compositePos:repPos
1073 // revised: 123456789004*IK3*segID*segpos[*segID*segpos*segID*segpos]
1074 $ik = explode('*', $err_seg);
1075 foreach ($ik as $i => $k) {
1076 switch ((int)$i) {
1077 case 0:
1078 $ret_ar['trace'] = $k;
1079 break;
1080 case 1:
1081 break; // IK3
1082 case 2:
1083 $ret_ar['id'][] = $k;
1084 break; // segment ID
1085 case 3:
1086 $ret_ar['err'][] = $k;
1087 break; // segment position
1088 case 4:
1089 $ret_ar['id'][] = $k;
1090 break;
1091 case 5:
1092 $ret_ar['err'][] = $k;
1093 break;
1094 case 6:
1095 $ret_ar['id'][] = $k;
1096 break;
1097 case 7:
1098 $ret_ar['err'][] = $k;
1099 break;
1104 return $ret_ar;
1108 * Order the csv data array according to the csv table heading row
1109 * so the data to be added to csv table rows are correctly ordered
1110 * the supplied data should be in an array with thie structure
1111 * array['icn'] ['file'][i]['key'] ['claim'][i]['key'] ['type']['type']
1113 * @uses csv_table_header()
1115 * @param array data_ar data array from edih_XXX_csv_data()
1116 * @return array|bool ordered array or false on error
1118 function edih_csv_order($csvdata)
1121 $wrcsv = array();
1122 $order_ar = array();
1124 foreach ($csvdata as $icn => $data) {
1125 // [icn]['type']['file']['claim']
1126 $ft = $data['type'];
1127 $wrcsv[$icn]['type'] = $ft;
1129 foreach ($data as $key => $val) {
1130 if ($key == 'type') {
1131 continue;
1134 $order_ar[$icn][$key] = csv_table_header($ft, $key);
1135 $ct = count($order_ar[$icn][$key]);
1136 foreach ($val as $k => $rcrd) {
1138 foreach ($order_ar[$icn][$key] as $ky => $vl) {
1139 $wrcsv[$icn][$key][$k][$ky] = $rcrd[$vl];
1145 return $wrcsv;
1149 * insert dashes in ten-digit telephone numbers
1151 * @param string $str_val the telephone number
1152 * @return string the telephone number with dashes
1154 function edih_format_telephone($str_val)
1156 $strtel = (string)$str_val;
1157 $strtel = preg_replace('/\D/', '', $strtel);
1158 if (strlen($strtel) != 10) {
1159 csv_edihist_log('edih_format_telephone: invalid argument: '.$str_val);
1160 return $str_val;
1161 } else {
1162 $tel = substr($strtel, 0, 3) . "-" . substr($strtel, 3, 3) . "-" . substr($strtel, 6);
1165 return $tel;
1169 * order MM DD YYYY values and insert slashes in eight-digit dates
1171 * US MM/DD/YYYY or general YYYY-MM-DD
1173 * @param string $str_val the eight-digit date
1174 * @param string $pref if 'US' (default) anything else means YYYY-MM-DD
1175 * @return string the date with slashes
1177 function edih_format_date($str_val, $pref = "Y-m-d")
1179 $strdt = (string)$str_val;
1180 $strdt = preg_replace('/\D/', '', $strdt);
1181 $dt = '';
1182 if (strlen($strdt) == 6) {
1183 $tdy = date('Ymd');
1184 if ($pref == "US") {
1185 // assume mmddyy
1186 $strdt = substr($tdy, 0, 2).substr($strdt, -2).substr($strdt, 0, 4);
1187 } else {
1188 // assume yymmdd
1189 $strdt = substr($tdy, 0, 2).$strdt;
1193 if ($pref == "US") {
1194 $dt = substr($strdt, 4, 2) . "/" . substr($strdt, 6) . "/" . substr($strdt, 0, 4);
1195 } else {
1196 $dt = substr($strdt, 0, 4) . "-" . substr($strdt, 4, 2) . "-" . substr($strdt, 6);
1199 return $dt;
1203 * format monetary amounts with two digits after the decimal place
1205 * @todo add other formats
1206 * @param string $str_val the amount string
1207 * @return string the telephone number with dashes
1209 function edih_format_money($str_val)
1212 if ($str_val || $str_val === '0') {
1213 $mny = sprintf("$%01.2f", $str_val);
1214 } else {
1215 $mny = $str_val;
1218 return $mny;
1222 * format percentage amounts with % sign
1223 * typical example ".50" from x12 edi segment element
1225 * @param string $str_val the amount string
1226 * @return string the value as a percentage
1228 function edih_format_percent($str_val)
1230 $val = (float)$str_val;
1231 if (is_float($val)) {
1232 $pct = $val*100 . '%';
1233 } else {
1234 $pct = $str_val.'%';
1237 return $pct;
1241 * HTML string for table thead element
1243 * @uses csv_table_header()
1244 * @param string
1245 * @param string
1246 * @return string
1248 function csv_thead_html($file_type, $csv_type, $tblhd = null)
1251 if (is_array($tblhd) & count($tblhd)) {
1252 $hvals = $tblhd;
1253 } else {
1254 $hvals = csv_table_header($file_type, $csv_type);
1257 if (is_array($hvals) && count($hvals)) {
1258 $str_html = '';
1259 } else {
1260 return false;
1263 $str_html .= "<thead>".PHP_EOL."<tr>".PHP_EOL;
1264 foreach ($hvals as $val) {
1265 $str_html .="<th>" . text($val) . "</th>";
1268 $str_html .= PHP_EOL."</tr>".PHP_EOL."</thead>".PHP_EOL;
1270 return $str_html;
1275 * Give the column headings for the csv files
1277 * @uses csv_file_type()
1278 * @param string $file_type one of our edi types
1279 * @param string $csv_type either 'file' or 'claim'
1280 * @return array
1282 function csv_table_header($file_type, $csv_type)
1285 $ft = csv_file_type($file_type);
1286 $ct = strpos('|file', $csv_type) ? 'file' : $csv_type;
1287 $ct = strpos('|claim', $ct) ? 'claim' : $ct;
1289 $hdr = array();
1290 if (!$ft || !$ct) {
1291 csv_edihist_log('csv_table_header error: incorrect file ['.$file_type.']or csv ['.$csv_type.'] type');
1292 return $hdr;
1296 if ($ct === 'file') {
1297 switch ((string)$ft) {
1298 //case 'ack': $hdr = array('Date', 'FileName', 'isa13', 'ta1ctrl', 'Code'); break;
1299 //case 'ebr': $hdr = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch'); break;
1300 //case 'ibr': $hdr = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch'); break;
1302 case 'f837':
1303 $hdr = array('Date', 'FileName', 'Control', 'Claim_ct', 'x12Partner');
1304 break;
1305 case 'ta1':
1306 $hdr = array('Date', 'FileName', 'Control', 'Trace', 'Code');
1307 break;
1308 case 'f997':
1309 $hdr = array('Date', 'FileName', 'Control', 'Trace', 'RspType', 'RejCt');
1310 break;
1311 case 'f276':
1312 $hdr = array('Date', 'FileName', 'Control', 'Claim_ct', 'x12Partner');
1313 break;
1314 case 'f277':
1315 $hdr = array('Date', 'FileName', 'Control', 'Accept', 'AccAmt', 'Reject', 'RejAmt');
1316 break;
1317 case 'f270':
1318 $hdr = array('Date', 'FileName', 'Control', 'Claim_ct', 'x12Partner');
1319 break;
1320 case 'f271':
1321 $hdr = array('Date', 'FileName', 'Control', 'Claim_ct', 'Reject', 'Payer');
1322 break;
1323 case 'f278':
1324 $hdr = array('Date', 'FileName', 'Control', 'TrnCount', 'Auth', 'Payer');
1325 break;
1326 case 'f835':
1327 $hdr = array('Date', 'FileName', 'Control', 'Trace', 'Claim_ct', 'Denied', 'Payer');
1328 break;
1330 } elseif ($ct === 'claim') {
1331 switch ((string)$ft) {
1332 //case 'ebr': $hdr = array('PtName','SvcDate', 'CLM01', 'Status', 'Batch', 'FileName', 'Payer'); break;
1333 //case 'ibr': $hdr = array('PtName','SvcDate', 'CLM01', 'Status', 'Batch', 'FileName', 'Payer'); break;
1334 //case 'dpr': $hdr = array('PtName','SvcDate', 'CLM01', 'Status', 'Batch', 'FileName', 'Payer'); break;
1336 case 'f837':
1337 $hdr = array('PtName', 'SvcDate', 'CLM01', 'InsLevel', 'BHT03', 'FileName', 'Fee', 'PtPaid', 'Provider' );
1338 break;
1339 case 'f997':
1340 $hdr = array('PtName', 'RspDate', 'Trace', 'Status', 'Control', 'FileName', 'RspType', 'err_seg');
1341 break;
1342 case 'f276':
1343 $hdr = array('PtName', 'SvcDate', 'CLM01', 'ClaimID', 'BHT03', 'FileName', 'Payer', 'Trace');
1344 break;
1345 case 'f277':
1346 $hdr = array('PtName', 'SvcDate', 'CLM01', 'Status', 'BHT03', 'FileName', 'Payer', 'Trace');
1347 break;
1348 case 'f270':
1349 $hdr = array('PtName', 'ReqDate', 'Trace', 'InsBnft', 'BHT03', 'FileName', 'Payer');
1350 break;
1351 case 'f271':
1352 $hdr = array('PtName', 'RspDate', 'Trace', 'Status', 'BHT03', 'FileName', 'Payer');
1353 break;
1354 case 'f278':
1355 $hdr = array('PtName', 'FileDate', 'Trace', 'Status', 'BHT03', 'FileName', 'Auth', 'Payer');
1356 break;
1357 case 'f835':
1358 $hdr = array('PtName', 'SvcDate', 'CLM01', 'Status', 'Trace', 'FileName', 'ClaimID', 'Pmt', 'PtResp', 'Payer');
1359 break;
1361 } else {
1362 // unexpected error
1363 csv_edihist_log('edih_csv_table_header() error: failed to match file type ['.$ft.'] or csv type ['.$ct.']');
1364 return false;
1367 if (count($hdr)) {
1368 return $hdr;
1369 } else {
1370 return false;
1375 function csv_files_header($file_type, $csv_type) {
1377 $tp = csv_file_type($type);
1378 if (!$tp) {
1379 csv_edihist_log('csv_files_header: incorrect type '.$file_type);
1380 return false;
1382 if (!strpos('|file|claim', $csv_type) ) {
1383 csv_edihist_log('csv_files_header error: incorrect csv type '.$csv_type);
1384 return false;
1387 $ft = strpos('|277', $file_type) ? 'f277' : $file_type;
1388 $ft = strpos('|835', $file_type) ? 'era' : $ft;
1389 $ft = strpos('|837', $file_type) ? 'batch' : $ft;
1390 $ft = strpos('|999|997|ack|ta1', $file_type) ? 'f997' : $ft;
1392 $csv_hd_ar = array();
1393 // dataTables: | 'date' | 'file_name (link)' | 'file_text (link fmt)' | 'claim_ct' | 'reject_ct' |
1394 $csv_hd_ar['ack']['file'] = array('Date', 'FileName', 'isa13', 'ta1ctrl', 'Code');
1395 $csv_hd_ar['ebr']['file'] = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch');
1396 $csv_hd_ar['ibr']['file'] = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch');
1398 // dataTables: | 'date' | 'file_name (link)' | 'file_text (link fmt)' | 'claim_ct' | 'partner' |
1399 $csv_hd_ar['batch']['file'] = array('Date', 'FileName', 'Ctn_837', 'claim_ct', 'x12_partner');
1400 $csv_hd_ar['ta1']['file'] = array('Date', 'FileName', 'Ctn_ta1', 'ta1ctrl', 'Code');
1401 $csv_hd_ar['f997']['file'] = array('Date', 'FileName', 'Ctn_999', 'ta1ctrl', 'RejCt');
1402 $csv_hd_ar['f277']['file'] = array('Date', 'FileName', 'Ctn_277', 'Accept', 'AccAmt', 'Reject', 'RejAmt');
1403 $csv_hd_ar['f270']['file'] = array('Date', 'FileName', 'Ctn_270', 'claim_ct', 'x12_partner');
1404 $csv_hd_ar['f271']['file'] = array('Date', 'FileName', 'Ctn_271', 'claim_ct', 'Denied', 'Payer');
1405 $csv_hd_ar['era']['file'] = array('Date', 'FileName', 'Trace', 'claim_ct', 'Denied', 'Payer');
1407 // dataTables: | 'pt_name' | 'svc_date' | 'clm01 (link clm)' | 'status (mouseover)' | b f t (links to files) | message (mouseover) |
1408 $csv_hd_ar['ebr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1409 $csv_hd_ar['ibr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1410 $csv_hd_ar['dpr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1412 // dataTables: | 'pt_name' | 'svc_date' | 'clm01 (link clm)' | 'status (mouseover)' | 'bht03_837 (link rsp)' | message (mouseover) |
1413 $csv_hd_ar['batch']['claim'] = array('PtName', 'SvcDate', 'clm01', 'InsLevel', 'Ctn_837', 'File_837', 'Fee', 'PtPaid', 'Provider' );
1414 $csv_hd_ar['f997']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'ak_num', 'File_997', 'Ctn_837', 'err_seg');
1415 $csv_hd_ar['f277']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'st_277', 'File_277', 'payer_name', 'claim_id', 'bht03_837');
1416 $csv_hd_ar['f270']['claim'] = array('PtName', 'SvcDate', 'clm01', 'InsLevel', 'st_270', 'File_270', 'payer_name', 'bht03_270');
1417 $csv_hd_ar['f271']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'st_271', 'File_271', 'payer_name', 'bht03_270');
1418 $csv_hd_ar['era']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'trace', 'File_835', 'claimID', 'Pmt', 'PtResp', 'Payer');
1420 return $csv_hd_ar[$ft][$csv_type];
1425 * adapted from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/
1427 * @param int
1429 * @return string
1431 function csv_convert_bytes($bytes)
1433 $sizes = array('Bytes', 'KB', 'MB', 'GB', 'TB');
1434 if ($bytes == 0) {
1435 return 'n/a';
1438 $i = floor(log($bytes) / log(1024));
1439 //$i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
1440 if ($i == 0) {
1441 return $bytes.' '.$sizes[$i];
1442 } else {
1443 return round($bytes / pow(1024, $i), 1).' '.$sizes[$i];
1448 * Determine whether an array is multidimensional
1450 * @param array
1451 * @return bool false if arrayis multidimensional
1453 function csv_singlerecord_test($array)
1455 // the two versions of count() are compared
1456 // if the array has a sub-array, count recursive is greater
1457 if (is_array($array)) {
1458 $is_sngl = count($array, COUNT_RECURSIVE) == count($array, COUNT_NORMAL);
1459 } else {
1460 $is_sngl = false;
1464 return $is_sngl;
1468 * give first and last index keys for an array
1470 * @param array
1471 * @return array
1473 function csv_array_bounds($array)
1475 // get the segment array bounds
1476 $ret_ar = array();
1477 if (is_array($array) && count($array)) {
1478 if (reset($array) !== false) {
1479 $ret_ar[0] = key($array);
1482 if (end($array) !== false) {
1483 $ret_ar[1] = key($array);
1487 return $ret_ar;
1491 * return a csv file as an associative array
1492 * the first row is the header or array keys for the row
1493 * array structure:
1494 * array[i]=>array(hdr0=>csvrow[0], hdr1=>csvrow[1], hdr2=>csvrow[2], ...)
1496 * @param string file type e.g. f837
1497 * @param string csv type claim or file
1498 * @return array
1500 function csv_assoc_array($file_type, $csv_type)
1503 if (!$file_type || !$csv_type) {
1504 csv_edihist_log('csv_assoc_array; invalid arguments ft: '.$file_type.' csvt: '.$csv_type);
1505 return false;
1508 $csv_ar = array();
1509 $h = array();
1510 $fp = '';
1512 $param = csv_parameters($file_type);
1513 $fcsv = (strpos($csv_type, 'aim')) ? 'claims_csv' : 'files_csv';
1515 $fp = (isset($param[$fcsv])) ? $param[$fcsv] : '';
1516 if (!is_file($fp)) {
1517 csv_edihist_log('csv_assoc_array; invalid csv file '.basename($fp));
1518 return $csv_ar;
1521 $ct = 0;
1522 $row = 0;
1523 $ky = -1;
1524 if (($fh = fopen($fp, "rb")) !== false) {
1525 while (($data = fgetcsv($fh, 2048, ",")) !== false) {
1526 if (is_null($data)) {
1527 continue;
1530 if ($row) {
1531 for ($i=0; $i<$ct; $i++) {
1532 $csv_ar[$ky][$h[$i]] = $data[$i];
1534 } else {
1535 $ct = count($data);
1536 $h = $data;
1539 $row++;
1540 $ky++;
1543 fclose($fh);
1544 } else {
1545 // invalid file path
1546 csv_edihist_log('csv_assoc_array; invalid file path '.$fp);
1547 return false;
1551 return $csv_ar;
1556 * A multidimensional array will be flattened to a single row.
1558 * @param array $array array to be flattened
1559 * @return array
1561 function csv_array_flatten($array)
1564 if (!is_array($array)) {
1565 return false;
1568 $result = array();
1569 foreach ($array as $key => $value) {
1570 if (is_array($value)) {
1571 $result = array_merge($result, csv_array_flatten($value));
1572 } else {
1573 $result[$key] = $value;
1577 return $result;
1582 * Write parsed data from edi x12 files to csv file
1584 * @uses csv_parameters()
1585 * @usescsv_table_header()
1587 * @param array data array from parse functions
1588 * @return bool true if no error
1590 function edih_csv_write($csv_data)
1593 if (! (is_array($csv_data) && count($csv_data))) {
1594 csv_edihist_log('edih_csv_write(): invalid data array');
1595 return false;
1599 foreach ($csv_data as $icn => $isa) {
1600 // should be array[icn] => [file][j][key] [claim][j][key] [type]
1601 $ft = ( isset($isa['type']) ) ? $isa['type'] : '';
1602 if (!$ft) {
1603 csv_edihist_log('edih_csv_write(): invalid file type');
1604 continue;
1608 $param = csv_parameters($ft);
1609 $f_hdr = csv_table_header($ft, 'file');
1610 $c_hdr = csv_table_header($ft, 'claim');
1611 if (is_array($param)) {
1612 // if either csv files does not exist, create them both
1613 // all unlisted files in type directory will be processed on next process round
1614 if (is_file($param['files_csv']) && (filesize($param['files_csv']) > 20)) {
1615 csv_edihist_log('edih_csv_write: csv check for files csv '.$ft);
1616 } else {
1617 $nfcsv = $param['files_csv'];
1618 $fh = fopen($nfcsv, 'wb');
1619 if ($fh !== false) {
1620 fputcsv($fh, $f_hdr);
1621 fclose($fh);
1622 chmod($nfcsv, 0600);
1625 csv_edihist_log('edih_csv_write: created files_csv file for '.$ft);
1628 if (is_file($param['claims_csv']) && filesize($param['claims_csv'])) {
1629 csv_edihist_log('edih_csv_write: csv check for claims csv '.$ft);
1630 } else {
1631 $nfcsv = $param['claims_csv'];
1632 $fh = fopen($nfcsv, 'wb');
1633 if ($fh !== false) {
1634 fputcsv($fh, $c_hdr);
1635 fclose($fh);
1636 chmod($nfcsv, 0600);
1639 csv_edihist_log('edih_csv_write: created claims_csv file for '.$ft);
1641 } else {
1642 csv_edihist_log('edih_csv_write: parameters error for type '.$ft);
1643 return false;
1647 foreach ($isa as $key => $data) {
1648 if ($key == 'type') {
1649 continue;
1652 // get the csv file path from parameters
1653 $fp = ($key == 'file') ? $param['files_csv'] : $param['claims_csv'];
1654 // get the csv row header
1655 $order_ar = ($key == 'file') ? $f_hdr : $c_hdr;
1656 $ct = count($order_ar);
1657 $chrs = 0;
1658 $rws = 0;
1660 $fh = fopen($fp, 'ab');
1661 if (is_resource($fh)) {
1662 // to assure proper order of data in each row, the
1663 // csv row is assembled by matching keys to the header row
1664 foreach ($data as $ky => $row) {
1665 $csvrow = array();
1666 for ($i=0; $i<$ct; $i++) {
1667 $csvrow[$i] = $row[$order_ar[$i]];
1670 $chrs += fputcsv($fh, $csvrow);
1671 $rws++;
1673 } else {
1674 csv_edihist_log('edih_csv_write(): failed to open '.$fp);
1675 return false;
1679 csv_edihist_log('edih_csv_write() wrote '.$rws.' rows to '.basename($fp));
1684 return $rws;
1689 * Search a csv record file and return the row or values from selected columns
1691 * This function requires that the $search_ar parameter be an array
1692 * with keys ['s_val']['s_col']['r_cols'], and 'r_cols' is an array
1693 * 's_val' is the search value, s_col is the column to check, r_cols is an array
1694 * of column numbers from which values are returned. If r_cols is not an array,
1695 * then the entire row will be returned. If the 'expect' parameter is 1, then
1696 * the search will stop after the first success and return the result. Otherwise, the
1697 * entire file will be searched.
1698 * ex: csv_search_record('batch', 'claim', array('s_val'=>'0024', 's_col'=>9, 'r_cols'=>array(1, 2, 7)), "1" )
1700 * @uses csv_parameters()
1701 * @param string $file_type
1702 * @param string $csv_type
1703 * @param array $search_ar
1704 * @param mixed $expect
1705 * @return array
1707 function csv_search_record($file_type, $csv_type, $search_ar, $expect = '1')
1710 csv_edihist_log("csv_search_record: ".strval($file_type)." ".strval($csv_type)." ".strval($search_ar['s_val']));
1712 $tp = csv_file_type($file_type);
1713 if (!$tp) {
1714 csv_edihist_log("csv_search_record: incorrect type $file_type");
1715 return false;
1719 $params = csv_parameters($tp);
1721 if ($csv_type == 'claim') {
1722 $fp = $params['claims_csv'];
1723 } elseif ($csv_type == 'file') {
1724 $fp = $params['files_csv'];
1725 } else {
1726 csv_edihist_log('csv_search_record: incorrect csv type '.$csv_type);
1727 return false;
1731 if (!is_array($search_ar) || array_keys($search_ar) != array('s_val', 's_col', 'r_cols')) {
1732 csv_edihist_log('csv_search_record: invalid search criteria');
1733 return false;
1736 $sv = $search_ar['s_val'];
1737 $sc = $search_ar['s_col'];
1738 $rv = (is_array($search_ar['r_cols']) && count($search_ar['r_cols'])) ? $search_ar['r_cols'] : 'all';
1739 $ret_ar = array();
1740 $idx = 0;
1741 if (($fh1 = fopen($fp, "r")) !== false) {
1742 while (($data = fgetcsv($fh1)) !== false) {
1743 // check for a match
1744 if ($data[$sc] == $sv) {
1745 if ($rv == 'all') {
1746 $ret_ar[$idx] = $data;
1747 } else {
1748 // now loop through the 'r_cols' array for data index
1749 $dct = count($data);
1750 foreach ($rv as $c) {
1751 // make sure we don't access a non-existing index
1752 if ($c >= $dct) {
1753 continue;
1757 $ret_ar[$idx][] = $data[$c];
1761 $idx++;
1762 if ($expect == '1') {
1763 break;
1768 fclose($fh1);
1769 } else {
1770 csv_edihist_log('csv_search_record: failed to open '.$fp);
1771 return false;
1774 if (empty($ret_ar)) {
1775 return false;
1776 } else {
1777 return $ret_ar;
1782 * Search the 'claims' csv table for the patient control and find the associated file name
1784 * Searchtype
1785 * In 'claims' csv tables, clm01 is position 2, ISA13 number is pos 4, and filename is pos 5;
1786 * Since we are interested usually in the filename, ISA13 is irrelevant usually.
1788 * @uses csv_parameters()
1789 * @uses csv_pid_enctr_parse()
1790 * @param string patient control-- pid-encounter, encounter, or pid
1791 * @param string filetype -- x12 type or f837, f277, etc
1792 * @param string search type encounter, pid, or clm01
1793 * @return array|bool [i] data row array or empty on error
1795 function csv_file_by_enctr($clm01, $filetype = 'f837')
1798 // return array of [i](pid_encounter, filename), there may be more than one file
1800 if (!$clm01) {
1801 return 'invalid encounter data<br>' . PHP_EOL;
1805 $ret_ar = array();
1806 $ft = csv_file_type($filetype);
1808 if (!$ft) {
1809 csv_edihist_log('csv_file_by_enctr: incorrect file type '.$filetype);
1810 return $ret_ar;
1811 } else {
1812 $params = csv_parameters($ft);
1813 //$fp = isset($params['claims_csv']) ? dirname(__FILE__).$params['claims_csv'] : false;
1814 $fp = isset($params['claims_csv']) ? $params['claims_csv'] : false;
1815 $h_ar = csv_table_header($ft, 'claim');
1816 $hct = count($h_ar);
1817 if (!$fp) {
1818 csv_edihist_log('csv_file_by_enctr: incorrect file type '.$filetype);
1819 return $ret_ar;
1824 $enct = csv_pid_enctr_parse(strval($clm01));
1825 $p = (isset($enct['pid'])) ? $enct['pid'] : '';
1826 $e = (isset($enct['enctr'])) ? $enct['enctr'] : '';
1827 if ($p && $e) {
1828 $pe = $p.'-'.$e;
1829 $srchtype = '';
1830 } elseif ($e) {
1831 $srchtype = 'encounter';
1832 } elseif ($p) {
1833 $srchtype = 'pid';
1834 } else {
1835 csv_edihist_log('csv_file_by_enctr: unable to determine encounter value '.$clm01);
1836 return 'unable to determine encounter value ' . text($clm01) . '<br />'.PHP_EOL;
1839 // OpenEMR creates CLM01 as nnn-nnn in genX12 batch
1840 //$pm = preg_match('/\D/', $enctr, $match2, PREG_OFFSET_CAPTURE);
1841 $val = array();
1842 //array_combine ( array $keys , array $values )
1843 // in 'claims' csv tables, clm01 is position 2 and filename is position 5
1844 if (($fh1 = fopen($fp, "r")) !== false) {
1845 if ($srchtype == 'encounter') {
1846 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
1847 // check for a match
1848 if (strpos($data[2], $e)) {
1849 $te = substr($data[2], strpos($data[2], '-')+1);
1850 if (strcmp($te, $e) === 0) {
1851 for ($i=0; $i<$hct;
1852 $i++) {
1853 $val[$h_ar[$i]] = $data[$i];
1856 $ret_ar[] = $val; // array_combine($h_ar, $data);
1860 } elseif ($srchtype == 'pid') {
1861 while (($data = fgetcsv($fh1, 1024, ',')) !== false) {
1862 if (strpos($data[2], $p) !== false) {
1863 $te = (strpos($data[2], '-')) ? substr($data[2], 0, strpos($data[2], '-')) : '';
1864 if (strcmp($te, $p) === 0) {
1865 for ($i=0; $i<$hct;
1866 $i++) {
1867 $val[$h_ar[$i]] = $data[$i];
1870 $ret_ar[] = $val; // $ret_ar[] = array_combine($h_ar, $data);
1874 } else {
1875 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
1876 // check for a match
1877 if (strcmp($data[2], $pe) === 0) {
1878 for ($i=0; $i<$hct;
1879 $i++) {
1880 $val[$h_ar[$i]] = $data[$i];
1883 $ret_ar[] = $val; // $ret_ar[] = array_combine($h_ar, $data);
1888 fclose($fh1);
1889 } else {
1890 csv_edihist_log('csv_file_by_enctr: failed to open csv file '.basename($fp));
1891 return false;
1894 return $ret_ar;
1899 * get the x12 file containing the control_num ISA13
1901 * @todo the csv for x12 files 999, 277, 835, 837 must have the control number
1903 * @uses csv_search_record()
1904 * @param string $control_num the interchange control number, isa13
1905 * @return string the file name
1907 function csv_file_by_controlnum($type, $control_num)
1909 // get the batch file containing the control_num
1911 $tp = csv_file_type($type);
1913 $hdr = csv_table_header($tp, 'file');
1914 $scol = array_search('Control', $hdr);
1915 $rcol = array_search('FileName', $hdr);
1917 // $search_ar should have keys ['s_val']['s_col'] array(['r_cols'][])
1918 // like "batch', 'claim, array(9, '0024', array(1, 2, 7))
1919 //$csv_hd_ar['batch']['file'] = array('time', 'file_name', 'control_num', 'claims', 'x12_partner', 'x12_version');
1921 $fn = '';
1922 $ctln = (strlen($control_num) >= 9) ? substr($control_num, 0, 9) : $control_num;
1923 $search = array('s_val'=>$ctln, 's_col'=>$scol, 'r_cols'=>array($rcol));
1924 $result = csv_search_record($tp, 'file', $search, "1");
1925 if (is_array($result) && count($result[0]) == 1) {
1926 $fn = $result[0][0];
1929 return $fn;
1934 * Search the csv table to obtain the file name for a given
1935 * trace value (835 / 997 999 type only)
1937 * Note: the 997/999 trace is the ISA13 of a batch file
1940 * @param string trace value (TRN02, TA101, or BHT03)
1941 * @param string from type (default is f835)
1942 * @param string to type (default is f835)
1943 * @return string file name or empty string
1945 function csv_file_by_trace($trace, $from_type = 'f835', $to_type = 'f837')
1947 // get the file referenced by the trace value
1949 $ft = ($from_type) ? csv_file_type($from_type) : '';
1950 $tt = ($to_type) ? csv_file_type($to_type) : '';
1951 $fn = '';
1952 $csv_type = '';
1953 $type = '';
1954 $search = array();
1956 csv_edihist_log("csv_file_by_trace: $trace from $ft to $tt");
1958 // $search_ar should have keys ['s_val']['s_col'] array(['r_cols'])
1959 // like "f837', 'claim, array(9, '0024', array(1, 2, 7))
1961 if ($ft == 'f835') {
1962 // trace payment to status or claim
1963 $search = array('s_val'=>$trace, 's_col'=>3, 'r_cols'=>'All');
1964 $type = $tt;
1965 $csv_type = 'file';
1966 } elseif ($ft == 'f997') {
1967 // trace ACK to batch file
1968 $icn = (is_numeric($trace) && strlen($trace) >= 9) ? substr($trace, 0, 9) : $trace;
1969 $search = array('s_val'=>$icn, 's_col'=>2, 'r_cols'=>'All');
1970 $type = $tt;
1971 $csv_type = 'file';
1972 } elseif ($ft == 'f277') {
1973 // trace status to status req or claim
1974 if ($tt == 'f276') {
1975 $search = array('s_val'=>$trace, 's_col'=>7, 'r_cols'=>'All');
1976 $type = $tt;
1977 $csv_type = 'claim';
1978 } elseif ($tt == 'f837') {
1979 // expect CLM01 for trace value
1980 $search = array('s_val'=>$trace, 's_col'=>2, 'r_cols'=>'All');
1981 $type = $tt;
1982 $csv_type = 'claim';
1984 } elseif ($ft == 'f271') {
1985 // trace benefit to benefit req
1986 if ($tt == 'f270') {
1987 $search = array('s_val'=>$trace, 's_col'=>2, 'r_cols'=>'All');
1988 $type = $tt;
1989 $csv_type = 'claim';
1991 } elseif ($ft == 'f278') {
1992 // trace auth to auth req
1993 $search = array('s_val'=>$trace, 's_col'=>2, 'r_cols'=>'All');
1994 $type = 'f278';
1995 $csv_type = 'claim';
1996 } else {
1997 csv_edihist_log('csv_file_by_trace: incorrect file type '.$file_type);
1998 return $fn;
2002 if ($type && $csv_type && $search) {
2003 $result = csv_search_record($type, $csv_type, $search, false);
2004 if (is_array($result) && count($result)) {
2005 if ($ft == 'f278') {
2006 foreach ($result as $r) {
2007 if ($r[6] == 'Rsp' || $r[6] == 'Reply') {
2008 $fn = $result[0][5];
2009 break;
2012 } elseif ($csv_type == 'claim') {
2013 $fn = $result[0][5];
2014 } else {
2015 $fn = $result[0][1];
2017 } else {
2018 csv_edihist_log("csv_file_by_trace: search failed $type csv $csv_type for trace $trace $from_type $to_type");
2020 } else {
2021 csv_edihist_log("csv_file_by_trace: error type $type csv $csv_type for trace $trace $from_type $to_type");
2024 return $fn;
2028 * list claim records with Denied or Reject status in given file
2030 * @param string
2031 * @param string
2033 * @return array
2035 function csv_denied_by_file($filetype, $filename, $trace = '')
2038 $ret_ar = array();
2039 $ft = csv_file_type($filetype);
2040 if (strpos('|f997|f271|f277|f835', $ft)) {
2041 $param = csv_parameters($ft);
2042 $csv_file = $param['claims_csv'];
2043 } else {
2044 csv_edihist_log("csv_errors_by_file: incorrect file type $filetype");
2045 return $ret_ar;
2049 csv_edihist_log("csv_errors_by_file: $ft searching $filename with trace $trace");
2051 if (($fh1 = fopen($csv_file, "r")) !== false) {
2052 if ($ft == 'f835') {
2053 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
2054 // check filename, then status
2055 if ($trace) {
2056 if ($data[4] == $trace) {
2057 if (!in_array($data[3], array('1', '2', '3', '19', '20', '21'))) {
2058 $ret_ar[] = $data;
2061 } elseif ($data[5] == $filename) {
2062 if (!in_array($data[3], array('1', '2', '3', '19', '20', '21'))) {
2063 $ret_ar[] = $data;
2067 } elseif ($ft == 'f277') {
2068 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
2069 if ($data[5] == $filename) {
2070 if (!strpos('|A1|A2|A5', substr($data[3], 0, 2))) {
2071 $ret_ar[] = $data;
2075 } elseif (strpos('|f997|f999|f271', $ft)) {
2076 while (($data = fgetcsv($fh1, 1024, ",")) !== false) {
2077 if ($data[5] == $filename) {
2078 if ($data[3] !== 'A') {
2079 $ret_ar[] = $data;
2083 } else {
2084 csv_edihist_log("csv_errors_by_file: file type did not match $filetype");
2087 fclose($fh1);
2091 return $ret_ar;
2096 * A function to try and assure the pid-encounter is correctly parsed
2098 * assume a format of pid-encounter, since that is sent in the OpenEMR x12 837
2100 * @param string $pid_enctr the value from element CLM01
2101 * return array array('pid' => $pid, 'enctr' => $enc)
2103 function csv_pid_enctr_parse($pid_enctr)
2105 // evaluate the patient account field
2107 if (!$pid_enctr || !is_string($pid_enctr)) {
2108 csv_edihist_log("csv_pid_enctr_parse: invalid argument");
2109 return false;
2112 $pval = trim($pid_enctr);
2113 if (strpos($pval, '-')) {
2114 $pid = substr($pval, 0, strpos($pval, '-'));
2115 $enc = substr($pval, strpos($pval, '-')+1);
2116 } elseif (ctype_digit($pval)) {
2117 if (preg_match('/(19|20)\d{2}[01]\d{1}[0-3]\d{1}/', $pval)) {
2118 $enc = $pval;
2119 } else {
2120 $enc = ( strlen($pval) ) >= ENCOUNTER_MIN_DIGIT_LENGTH ? $pval : '';
2121 $pid = '';
2123 } elseif (preg_match('/\D/', $pval, $match2, PREG_OFFSET_CAPTURE)) {
2124 $inv_split = (count($match2)) ? preg_split('/\D/', $pval, 2, PREG_SPLIT_NO_EMPTY) : false;
2125 if ($inv_split) {
2126 $pid = $inv_split[0];
2127 $enc = $inv_split[1];
2129 } else {
2130 $enc = ( strlen($pval) ) >= ENCOUNTER_MIN_DIGIT_LENGTH ? $pval : '';
2131 $pid = '';
2134 return array('pid' => $pid, 'enctr' => $enc);