Added standard jquery library for eye form
[openemr.git] / library / edihistory / csv_record_include.php
blob7d79c1e4c9185bfd208fe81df92a5b3f237ca69c
1 <?php
2 /**
3 * csv_record_include.php
4 *
5 * Copyright 2012 Kevin McCormick
6 *
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 * **************************
43 * </pre>
45 * The claim_history x12 files are batch (837) claim status (277 also TA1) and era (835)
46 * (Eligibility status files (271) would probably work in this scheme as well.)
47 * There are also Availity clearinghouse specific files .ibr, .ebr, .dpr (nothing for .dpr files)
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. Read the parameters array and choose the parameters using 'type'
55 * 2. Search the 'directory' for files matching the 'regex' regular expressions and
56 * compare the results to the files listed in the 'files_csv' files.csv record -- unmatched files are "new"
57 * 3. Each "new" x12 file should be read by csv_x12_segments -- returns array('path', 'delimiters', 'segments')
58 * ibr, ebr, ack -- basically Availity formats have their own read functions
59 * 4. Pass the array to various functions which parse for claims information
60 * 5. Write the results to files.csv or claims.csv and create html output for display
62 * 6. Other outputs as called for in ibr_history.php -- from user input from claim_history.html
63 * </pre>
65 * Key usability issue is the "new" files are in the users home directory -- downloaded there
66 * while the OpenEMR is on the server -- so there is a basic issue of access to the files
68 * The ibr_uploads.php script handles uploads of zip archives or multiple file uploads
70 * The csv data files are just php written .csv files, so anything different may cause errors
71 * You can open and edit them in OpenOffice, but you must save them in "original format"
73 * TO DO script to read 271 eligibility response files -- add type to csv_verfy_file, csv_parameters, csv_files_header, csv_setup
75 * TO_DO Some type of "find in files" search would be helpful for locating all references to a claim, patient, etc.
76 * [ grep -nHIrF 'findtext']
78 * TO_DO functions to zip old files, put them aside, and remove them from csv tables
79 */
81 ///**
82 // * a security measure to prevent direct web access to this file
83 // */
84 // if (!defined('SITE_IN')) die('Direct access not allowed!');
86 /**
87 * Log messages to the log file
89 * @param string $msg_str the log message
90 * @return int number of characters written
92 function csv_edihist_log ( $msg_str ) {
94 $logfile = csv_edih_basedir();
95 //$logfile = ($logfile) ? $logfile."/edi_history_log.txt" : $GLOBALS['OE_SITE_DIR']."/edi_history_log.txt";
96 $logfile = ($logfile) ? $logfile."/edi_history_log.txt" : false;
97 if (!$logfile) {
98 echo "EDI History log file not available <br />";
99 return false;
101 $rslt = FALSE;
102 if ( is_string($msg_str) ) {
103 $tm = date( 'Ymd:Hms' );
104 $tm .= " " . $msg_str . PHP_EOL;
105 $fh = fopen( $logfile, 'a');
106 if ($fh !== FALSE) {
107 $rslt = fwrite($fh, $tm); // number of characters written
109 fclose($fh);
111 return $rslt;
116 * read the edi_history_log.txt file into an
117 * html formatted ordered list
119 * @return string
121 function csv_log_html() {
122 $html_str = "<div class=\"filetext\">".PHP_EOL."<ol class='logview'>".PHP_EOL;
123 $fp = csv_edih_basedir();
124 $fp = $fp."/edi_history_log.txt";
125 $fh = fopen( $fp, 'r');
126 if ($fh !== FALSE) {
127 while (($buffer = fgets($fh)) !== false) {
128 $html_str .= "<li>$buffer</li>".PHP_EOL;
130 $html_str .= "</ol>".PHP_EOL."</div>".PHP_EOL;
131 if (!feof($fh)) {
132 $html_str .= "<p>Error: unexpected file ending error</p>".PHP_EOL;
134 fclose($fh);
135 } else {
136 $html_str = "<p>Error: unable to open log file</p>".PHP_EOL;
138 return $html_str;
141 function csv_log_archive() {
142 $dte = date('Ymds');
144 $str_out = '';
145 $bdir = csv_edih_basedir();
146 $zname = $bdir.DIRECTORY_SEPARATOR.$dte.'_edihistory_log.txt';
147 $logname = $bdir.DIRECTORY_SEPARATOR.'edi_history_log.txt';
148 $archname = $bdir.DIRECTORY_SEPARATOR.'edihistory_archive.zip';
150 $fs = filesize($logname);
151 if ($fs == false && $fs < 512) {
152 $str_out = "$tm error accessng log file.<br />".PHP_EOL;
153 return $str_out;
154 } elseif ($fs < 512) {
155 $str_out = "$tm log too small to archive.<br />".PHP_EOL;
156 return $str_out;
159 $zip = new ZipArchive;
160 if ($zip->open($archname, ZIPARCHIVE::CREATE | ZIPARCHIVE::CHECKCONS)!==TRUE) {
161 exit("cannot open <$archname>\n");
162 } else {
163 $a = $zip->addFile($logname, $zname);
164 $c = $zip->close();
165 if ($c) {
166 $fh = fopen($logname, "w+b");
167 if ($fh !== FALSE) {
168 $tm = date('Ymd:Hms');
169 $rslt = fwrite($fh, "$tm log file archive created.".PHP_EOL);
171 fclose($fh);
172 $str_out = "$tm log file archive created.<br />".PHP_EOL;
173 } else {
174 $str_out = "$tm error in archive of log file.<br />".PHP_EOL;
177 return $str_out;
181 * open or save a user notes file
183 * @param string
184 * @param bool
185 * @return string
187 function csv_notes_file($content='', $open=true) {
189 $str_html = '';
190 //$fp = dirname(__FILE__) . "/edi_notes.txt";
191 $fp = csv_edih_basedir();
192 $fp = $fp."/edi_notes.txt";
193 if (! is_writable($fp) ) {
194 $fh = fopen( $fp, 'a+b');
195 fclose($fh);
197 if ($open) {
198 // if contents were previously deleted by user and file is empty,
199 // the text 'empty' is put in content in save operation
200 $ftxt = file_get_contents($fp);
201 if ($ftxt === false) { echo "csv_notes_file: file error<br />".PHP_EOL; }
202 if (substr($ftxt, 0, 5) == 'empty' && strlen($ftxt) == 5) {
203 $ftxt = '## '. date("F j, Y, g:i a");
204 } elseif (!$ftxt) {
205 $ftxt = '## '. date("F j, Y, g:i a");
207 $str_html .= $ftxt;
208 } elseif (strlen($content)) {
209 //echo "csv_notes_file: we have content<br />".PHP_EOL;
210 if (preg_match('/[^\x20-\x7E\x0A\x0D]|(<\?)|(<%)|(<asp)|(<ASP)|(#!)|(\$\{)|(<scr)|(<SCR)/', $content, $matches, PREG_OFFSET_CAPTURE)) {
211 $str_html .= "Filtered character in file content not accepted <br />" . PHP_EOL;
212 $str_html .= " character: " . $matches[0][0] . " position: " . $matches[0][1] . "<br />" . PHP_EOL;
213 $saved = false;
214 } else {
215 //echo "csv_notes_file: we are trying to save in $fp<br />".PHP_EOL;
216 $saved = file_put_contents($fp, $content);
218 $str_html .= ($saved) ? "Notes saved<br />" : "Save Error<br />";
219 } else {
220 $ftxt = 'empty';
221 $saved = file_put_contents($fp, $ftxt);
222 $str_html .= ($saved) ? "No content in notes.<br />" : "Save Error with empty file<br />";
225 return $str_html;
229 * set the base path for most file operations
231 * @return string|boolean
233 function csv_edih_basedir() {
234 //$GLOBALS['OE_SITE_DIR']
235 // should be something like /var/www/htdocs/openemr/sites/default
236 if (isset($GLOBALS['OE_SITE_DIR'])) {
237 return $GLOBALS['OE_SITE_DIR'].'/edi/history';
238 } else {
239 csv_edihist_log("csv_edih_basedir: failed to obtain OpenEMR Site directory");
240 return false;
245 * set the temporary directory used for uploads, etc.
247 * @return string
249 function csv_edih_tmpdir() {
250 //define("IBR_UPLOAD_DIR", "/tmp/edihist");
251 $systmp = sys_get_temp_dir();
252 if (substr($systmp, -1) == DIRECTORY_SEPARATOR) {
253 return $systmp."edihist";
254 } else {
255 return $systmp.DIRECTORY_SEPARATOR."edihist";
261 * Initial setup function
263 * Create the directory tree and write the column headers into the csv files
264 * This function will accept a directory argument and it appends the value
265 * from IBR_HISTORY_DIR to the path. Then a directory for each type of file
266 * and the csv files are created under that.
268 * @uses csv_parameters()
269 * @uses csv_files_header()
270 * @param string $dir
271 * @param string &$out_str referenced, should be created in calling function
272 * @return boolean
274 function csv_setup(&$out_str) {
276 $isOK = FALSE;
277 $chr = 0;
278 //$basedir = dirname(__FILE__);
279 $edihist_dir = csv_edih_basedir();
280 if ($edihist_dir) {
281 $basedir = $GLOBALS['OE_SITE_DIR'].DIRECTORY_SEPARATOR.'edi';
282 $csv_dir = $edihist_dir.DIRECTORY_SEPARATOR.'csv';
283 $archive_dir = $edihist_dir.DIRECTORY_SEPARATOR.'archive';
284 } else {
285 //csv_edihist_log("setup: failed to obtain OpenEMR Site directory");
286 $out_str .= "setup: failed to obtain OpenEMR Site directory <br />";
287 return false;
290 if (is_writable($basedir) ) {
291 $isOK = TRUE;
292 $out_str .= "setup: directory $basedir <br />";
293 //csv_edihist_log("setup: directory $basedir");
296 if ($isOK) {
298 if (!mkdir($edihist_dir, 0755, true)) {
299 //csv_edihist_log("Setup: Failed to create folder... $edihist_dir");
300 $out_str .= "Setup: Failed to create folder... $edihist_dir<br />".PHP_EOL;
301 $isOK = FALSE;
302 return false;
303 } else {
304 $p_ar = csv_parameters("ALL");
306 if (!mkdir($csv_dir, 0755, true) ) {
307 $isOK = FALSE;
308 //csv_edihist_log("Setup: Failed to create csv folder...$csv_dir");
309 return false;
311 if (!mkdir($archive_dir, 0755, true) ) {
312 $isOK = FALSE;
313 //csv_edihist_log("Setup: Failed to create archive folder...$archive_dir");
314 return false;
317 foreach ($p_ar as $key=>$val) {
318 // make the file storage subdirs; like /history/era /history/f997, etc.
319 $type_dir = $p_ar[$key]['directory'];
321 if (!is_dir($type_dir) && !mkdir($type_dir, 0755, false) ) {
322 //csv_edihist_log("Setup: failed to create storage directory $key");
323 $out_str .= "Setup: failed to create storage directory $key<br />".PHP_EOL;
324 return false;
326 $out_str .= "created directory for $key<br />" .PHP_EOL;
327 $chr = 0;
329 $hdr_f = csv_files_header($p_ar[$key]['type'], 'file');
330 $hdr_c = csv_files_header($p_ar[$key]['type'], 'claim');
332 $fpath = $p_ar[$key]['files_csv'];
333 $cpath = $p_ar[$key]['claims_csv'];
335 if (is_array($hdr_f) ) {
336 // create the files_type.csv files and insert header row
337 if ($fpath) {
338 //csv_edihist_log("Creating file $fpath for $key");
339 $fh = fopen($fpath, 'x');
340 if ($fh !== FALSE) {
341 $chr = fputcsv($fh, $hdr_f);
342 $out_str .= ($chr) ? "created $fpath <br />" .PHP_EOL : "failed to create $fpath<br />" .PHP_EOL;
343 $isOK = ($chr) ? TRUE : FALSE;
344 $chr = 0;
345 } else {
346 $isOK = FALSE;
347 //csv_edihist_log("Creating file failed for $key");
348 $out_str .= "Creating file failed for $key<br />" .PHP_EOL;
350 fclose($fh);
352 } else {
353 //csv_edihist_log("Did not get header row for $key file");
354 $out_str .= "Did not get header row for $key file<br />".PHP_EOL;
357 if (is_array($hdr_c) ) {
358 // // create the claims_type.csv files and insert header row
359 if ($cpath) {
360 //csv_edihist_log("Creating file $cpath for $key");
361 $fh = fopen($cpath, 'x');
362 if ($fh !== FALSE) {
363 $chr = fputcsv($fh, $hdr_c);
364 $out_str .= ($chr) ? "created $cpath <br />" .PHP_EOL : "failed to write heading row for $cpath<br />" .PHP_EOL;
365 $isOK = ($chr) ? TRUE : FALSE;
366 $chr = 0;
367 } else {
368 $isOK = FALSE;
369 //csv_edihist_log("Creating file failed for $key");
370 $out_str .= "Creating file failed for $key<br />" .PHP_EOL;
372 fclose($fh);
374 } else {
375 $isOK = FALSE;
376 //csv_edihist_log("Did not get header row for $key claims table");
377 $out_str .= "Did not get header row for $key claims table<br />".PHP_EOL;
381 //$GLOBALS['OE_SITE_DIR']."/edi_history_log.txt";
382 //if (is_file($GLOBALS['OE_SITE_DIR']."/edi_history_log.txt")) {
383 // rename ($GLOBALS['OE_SITE_DIR']."/edi_history_log.txt", $edihist_dir."/edi_history_log.txt" );
386 } else {
387 $out_str .= "Setup failed: Can not create directories <br />" . PHP_EOL;
389 return $isOK;
390 //return $out_str;
395 * Empty all contents of tmp dir IBR_UPLOAD_DIR
397 * @param none
398 * @return bool
400 function csv_clear_tmpdir() {
402 $edih_tmpdir = csv_edih_tmpdir();
403 $tmp_files = scandir($edih_tmpdir);
404 if (count($tmp_files)) {
405 foreach($tmp_files as $idx=>$tmpf) {
406 if ($tmpf == "." || $tmpf == "..") {
407 // can't delete . and ..
408 continue;
410 if (is_file($edih_tmpdir.DIRECTORY_SEPARATOR.$tmpf) ) {
411 unlink($edih_tmpdir.DIRECTORY_SEPARATOR.$tmpf);
412 unset($tmp_files[$idx]);
416 if (count($tmp_files) > 2) {
418 csv_edihist_log ( "tmp dir contents remain in $edih_tmpdir");
419 return FALSE;
420 } else {
421 return TRUE;
426 * The array that holds the various parameters used in dealing with files
428 * A key function since it holds the paths, columns, etc. This function relies on
429 * the IBR_HISTORY_DIR constant. Unfortunately, there is an issue with matching the type in
430 * the case of the values '997', '277', '999', etc, becasue these strings may be recast
431 * from strings to integers, so the 'type' originally supplied is lost.
432 * This introduces an inconsistency when the 'type' is used in comparison tests.
433 * The workaround is to say the "type" should have an 'f' prepended to the x12 type number.
434 * The 'datecolumn' and 'fncolumn' entries are used in csv_to_html() to filter by date
435 * or place links to files.
437 * @param string $type -- default = ALL or one of batch, ibr, ebr, dpr, f997, f277, era, ack, ta1, text
438 * @return array
440 function csv_parameters($type="ALL") {
442 $edihist_dir = csv_edih_basedir(); // $GLOBALS['OE_SITES_BASE'].'/edi/history'
443 $p_ar = array();
444 // the batch file directory is a special case - decision is to use OpenEMR batch files so users will not have to
445 // upload these. If they are accidentally uploaded, they will be matched and the extra copy will be discarded
446 // OpenEMR copies each batch file to sites/default/edi and this project never reads from or writes to that directory
447 // 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$/'
449 //$p_ar['csv'] = array("type"=>'csv', "directory"=>$edihist_dir.'/csv', "claims_csv"=>'ibr_parameters.csv',
450 // "files_csv"=>'', "column"=>'', "regex"=>'/\.csv$/');
452 $p_ar['batch'] = array("type"=>'batch', "directory"=>$GLOBALS['OE_SITE_DIR'].'/edi', "claims_csv"=>$edihist_dir."/csv/claims_batch.csv",
453 "files_csv"=>$edihist_dir."/csv/files_batch.csv", "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\-batch(.*)\.txt$/');
454 $p_ar['ta1'] = array("type"=>'ta1', "directory"=>$edihist_dir.'/f997', "claims_csv"=>'',
455 "files_csv"=>$edihist_dir.'/csv/files_997.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.ta1$/i');
456 $p_ar['ack'] = array("type"=>'ack', "directory"=>$edihist_dir.'/f997', "claims_csv"=>'',
457 "files_csv"=>$edihist_dir.'/csv/files_997.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.ack$/i');
458 $p_ar['f997'] = array("type"=>'f997', "directory"=>$edihist_dir.'/f997', "claims_csv"=>$edihist_dir.'/csv/claims_997.csv',
459 "files_csv"=>$edihist_dir.'/csv/files_997.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.99[79]$/');
461 $p_ar['ibr'] = array("type"=>'ibr', "directory"=>$edihist_dir.'/ibr', "claims_csv"=>$edihist_dir.'/csv/claims_ibr.csv',
462 "files_csv"=>$edihist_dir.'/csv/files_ibr.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.ibr$/');
463 $p_ar['ebr'] = array("type"=>'ebr', "directory"=>$edihist_dir.'/ebr', "claims_csv"=>$edihist_dir.'/csv/claims_ebr.csv',
464 "files_csv"=>$edihist_dir.'/csv/files_ebr.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.ebr$/');
465 $p_ar['dpr'] = array("type"=>'dpr', "directory"=>$edihist_dir.'/dpr', "claims_csv"=>$edihist_dir.'/csv/claims_dpr.csv',
466 "files_csv"=>'', "datecolumn"=>'1', "fncolumn"=>'5', "regex"=>'/\.dpr$/');
467 $p_ar['f277'] = array("type"=>'f277', "directory"=>$edihist_dir.'/f277', "claims_csv"=>$edihist_dir.'/csv/claims_277.csv',
468 "files_csv"=>$edihist_dir.'/csv/files_277.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/\.277([ei]br)?$/');
469 // OpenEMR stores era files, but the naming scheme is confusing, so we will just use our own directory for them
470 $p_ar['era'] = array("type"=>'era', "directory"=>$edihist_dir.'/era', "claims_csv"=>$edihist_dir.'/csv/claims_era.csv',
471 "files_csv"=>$edihist_dir.'/csv/files_era.csv', "datecolumn"=>'0', "fncolumn"=>'1', "regex"=>'/835[0-9]{5}\.835*|\.(era|ERA)$/');
472 $p_ar['text'] = array("type"=>'text', "directory"=>$edihist_dir.'/text', "claims_csv"=>'',
473 "files_csv"=>'', "column"=>'', "regex"=>'/\.(EB)|(IB)|(DP)|(AC)|(TA)|(99)|(97)T$/i');
475 $tp = strpos('|f837', (string)$type) ? 'batch' : $type;
476 $tp = strpos('|f999', (string)$type) ? 'f997' : $tp;
477 $tp = strpos('|f997', (string)$type) ? 'f997' : $tp;
478 $tp = strpos('|f835', (string)$type) ? 'era' : $tp;
479 $tp = strpos('|f277', (string)$type) ? 'f277' : $tp;
481 if ( array_key_exists($tp, $p_ar) ) {
482 return $p_ar[$tp];
483 } else {
484 return $p_ar;
489 * determine if a csv table has data for select dropdown
491 * @param string default 'json'
492 * @return array json if argument is 'json'
494 function csv_table_select_list($outtp='json') {
495 $optlist = array();
497 $edihist_dir = csv_edih_basedir(); // $GLOBALS['OE_SITE_DIR'].'/edi/history'
498 $thisdir = $edihist_dir.'/csv';
499 $tbllist = scandir($thisdir);
500 $idx = 0;
501 foreach($tbllist as $csvf) {
502 if ($csvf == "." || $csvf == ".." ) { continue; }
503 if (filesize($thisdir.DIRECTORY_SEPARATOR.$csvf) < 90) { continue; }
504 if (substr($csvf, -1) == '~') { continue; }
505 $finfo = pathinfo($thisdir.DIRECTORY_SEPARATOR.$csvf);
506 $fn = $finfo['filename'];
507 $tp = explode('_', $fn);
508 $optlist[$idx]['fname'] = $fn;
509 $optlist[$idx]['desc'] = $tp[1] .' '.$tp[0];
510 $idx++;
512 if ($outtp == 'json') {
513 return json_encode($optlist);
514 } else {
515 return $optlist;
521 * List files in the directory for the given type
523 * Write an entry in the log if an file is in the directory
524 * that does not match the type.
526 * @uses csv_parameters()
527 * @param string $type a type from our list
528 * @return array
530 function csv_dirfile_list ($type) {
531 // return false if location is not appropriate
532 // use regular expressions to select desired files from directory
533 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ack|ta1", $type) ) {
534 if ($type != 'text') {
535 // do not log text type, but do not search either
536 csv_edihist_log("csv_dirfile_list error: incorrect type $type");
538 return FALSE;
540 $params = csv_parameters($type);
541 $search_dir = $params['directory'].DIRECTORY_SEPARATOR;
542 $typedir = basename($params['directory']);
543 $ext_re = $params['regex'];
544 $dirfiles = array();
546 if (is_dir($search_dir)) {
547 if ($dh = opendir($search_dir)) {
548 while (($file = readdir($dh)) !== false) {
549 if (is_file($search_dir.$file) ) {
550 if (preg_match($ext_re, $file) ) {
551 $dirfiles[] = $file;
552 } elseif ($typedir == 'f997') {
553 $ext = substr($file, -3);
554 // no error, since ack|ta1 files are put there
555 if ($type == 'f997') {
556 if ($ext == 'ack' || $ext == 'ta1') { continue; }
557 } elseif ($type == 'ack') {
558 if (ext == '999' || ext == '997' || $ext == 'ta1') { continue; }
559 } elseif ($type == 'ta1') {
560 if (ext == '999' || ext == '997' || $ext == 'ack') { continue; }
562 } else {
563 //if ($file == '.' || $file == '..') { continue; } // . and .. are not files
564 csv_edihist_log("csv_dirfile_list: $type wrong type $file");
568 closedir($dh);
570 } else {
571 csv_edihist_log("csv_dirfile_list: Error: $typedir directory seems to be missing!");
574 return $dirfiles;
575 } // end function
579 * List files that are in the csv record
581 * @uses csv_parameters()
582 * @param string $type -- one of our types
583 * @return array
585 function csv_processed_files_list ($type) {
588 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ack|ta1", $type) ) {
589 if ($type != 'text') {
590 csv_edihist_log("csv_processed_files_list error: incorrect type $type");
592 return FALSE;
594 $processed_files = array();
595 $param = csv_parameters($type);
596 $csv_col = $param['fncolumn'];
597 if ($type == 'dpr') {
598 $csv_file = $param['claims_csv'];
599 //$csv_col = '5';
600 } else {
601 $csv_file = $param['files_csv'];
604 $idx = 0;
605 if (($fh1 = fopen( $csv_file, "r" )) !== FALSE) {
606 while (($data = fgetcsv($fh1, 1024, ",")) !== FALSE) {
607 if ($idx) { $processed_files[] = $data[$csv_col]; }
608 // skip the header row
609 $idx++;
611 fclose($fh1);
612 } else {
613 csv_edihist_log ("csv_list_processed_files: failed to access $csv_file" );
614 return false;
616 // consider array_shift($processed_files) to drop the header row (too slow)
617 // consider array_unique($processed_files) becasue files may be listed several times
618 return $processed_files;
619 } // end function
623 * Give an array of files in the storage directories that are not in the csv record
625 * @param string $type -- one of our types
626 * @return array
628 function csv_newfile_list($type) {
630 //f277 f997 ack batch csv ebr era ibr text
631 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ack|ta1", $type) ) {
632 if ($type != 'text') {
633 csv_edihist_log("csv_newfile_list: incorrect type $type");
635 return FALSE;
638 $dir_files = csv_dirfile_list ($type);
639 $csv_files = csv_processed_files_list ($type);
640 // $dir_files should come first in array_diff()
641 if (empty($dir_files)) {
642 // logic error -- fixed; if dir_files is empty, there are no files of that type
643 //$ar_new = $csv_files;
644 $ar_new = $dir_files;
645 } else {
646 $ar_new = array_diff($dir_files, $csv_files);
649 return $ar_new;
654 * Give the column headings for the csv files
656 * @param string $file_type -- one of our types batch|era|ibr|ebr|dpr|f277|f997, etc.
657 * @param string $csv_type -- one of 'file' or 'claim'
658 * @return array
660 function csv_files_header($file_type, $csv_type) {
662 if (! strpos("|era|835|f997|999|ibr|ebr|dpr|f277|batch|837|ta1|ack", $file_type) ) {
663 csv_edihist_log("csv_files_header error: incorrect file type $file_type");
664 return FALSE;
666 if (!strpos('|file|claim', $csv_type) ) {
667 csv_edihist_log("csv_files_header error: incorrect csv type $csv_type");
668 return FALSE;
671 $ft = strpos('|277', $file_type) ? 'f277' : $file_type;
672 $ft = strpos('|835', $file_type) ? 'era' : $ft;
673 $ft = strpos('|837', $file_type) ? 'batch' : $ft;
674 $ft = strpos('|999|997|ack|ta1', $file_type) ? 'f997' : $ft;
676 $csv_hd_ar = array();
677 // actually, 'ack' and 'ta1' are probably redundant, since they are interpreted as 'f997'
678 $csv_hd_ar['ack']['file'] = array('Date', 'FileName', 'isa13', 'ta1ctrl', 'Code');
679 $csv_hd_ar['ebr']['file'] = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch');
680 $csv_hd_ar['ibr']['file'] = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch');
682 $csv_hd_ar['batch']['file'] = array('Date', 'FileName', 'Ctn_837', 'claim_ct', 'x12_partner');
683 $csv_hd_ar['ta1']['file'] = array('Date', 'FileName', 'Ctn_ta1', 'ta1ctrl', 'Code');
684 $csv_hd_ar['f997']['file'] = array('Date', 'FileName', 'Ctn_999', 'ta1ctrl', 'RejCt');
685 $csv_hd_ar['f277']['file'] = array('Date', 'FileName', 'Ctn_277', 'Accept', 'AccAmt', 'Reject', 'RejAmt');
686 $csv_hd_ar['era']['file'] = array('Date', 'FileName', 'Trace', 'claim_ct', 'Denied', 'Payer');
688 $csv_hd_ar['ebr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
689 $csv_hd_ar['ibr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
690 $csv_hd_ar['dpr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
692 $csv_hd_ar['batch']['claim'] = array('PtName', 'SvcDate', 'clm01', 'InsLevel', 'Ctn_837', 'File_837', 'Fee', 'PtPaid', 'Provider' );
693 $csv_hd_ar['f997']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'ak_num', 'File_997', 'Ctn_837', 'err_seg');
694 $csv_hd_ar['f277']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'st_277', 'File_277', 'payer_name', 'claim_id', 'bht03_837');
695 $csv_hd_ar['era']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'trace', 'File_835', 'claimID', 'Pmt', 'PtResp', 'Payer');
697 return $csv_hd_ar[$ft][$csv_type];
702 * Determine whether an array is multidimensional
704 * @param array
705 * @return bool false if arrayis multidimensional
707 function csv_singlerecord_test ( $array ) {
708 // the two versions of count() are compared
709 // if the array has a sub-array, count recursive is greater
710 $is_sngl = count($array, COUNT_RECURSIVE ) == count( $array, COUNT_NORMAL);
712 return $is_sngl;
716 * A multidimensional array will be flattened to a single row.
718 * @param array $array array to be flattened
719 * @return array
721 function csv_array_flatten($array) {
723 if (!is_array($array)) {return FALSE;}
724 $result = array();
725 foreach ($array as $key => $value) {
726 if (is_array($value)) {
727 $result = array_merge($result, csv_array_flatten($value));
728 } else {
729 $result[$key] = $value;
732 return $result;
736 * Append rows to one of the csv record files.
738 * @uses csv_singlerecord_test()
739 * @uses csv_parameters()
740 * @uses csv_files_header()
741 * @param array $csv_data the data array, either file data or claim data
742 * @param string $file_type which of our file types to use
743 * @param string $csv_type either 'claim' or 'file'
744 * @return int number of characters written per fputcsv()
746 function csv_write_record($csv_data, $file_type, $csv_type) {
748 if (!is_array($csv_data)) { return FALSE;}
749 // use CSV_RECORD class to write ibr or ebr claims data to the csv file
750 // csv, batch, ibr, ebr, f997, or era
751 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ta1|ack", $file_type) ) {
752 csv_edihist_log("csv_write_record error: incorrect file type $file_type");
753 return FALSE;
756 $ft = $file_type;
757 $ft = strpos("|835", $file_type) ? 'era' : $ft;
758 $ft = strpos("|837", $file_type) ? 'batch' : $ft;
759 $ft = strpos("|999|ack|ta1", $file_type) ? 'f997' : $ft;
761 $params = csv_parameters($ft);
763 if ($csv_type == "claim") {
764 $fp = $params['claims_csv'];
765 } elseif ($csv_type == "file") {
766 $fp = $params['files_csv'];
767 } else {
768 csv_edihist_log("csv_writedata_csv error: incorrect csv type $csv_type");
769 return FALSE;
772 $fh = fopen( $fp, 'a');
773 // count characters written -- returned by fputcsv
774 $indc = 0;
775 // if we fail to open the file, return the result, expect FALSE
776 if (!$fh) { return FALSE; }
777 // test for a new file
778 if ( filesize($fp) === 0 ) {
779 $ar_h = csv_files_header($file_type, $csv_type);
780 $td = fgetcsv($fh);
781 if ($td === FALSE || $td === NULL ) {
782 // assume we have an empty file
783 // write header row if this is a new csv file
784 if (count($ar_h) ) {
785 $indc += fputcsv ( $fh, $ar_h );
790 // test array for dimension counts
791 $is_sngl = csv_singlerecord_test($csv_data) ;
792 if ( $is_sngl ) {
793 $indc += fputcsv ( $fh, $csv_data );
794 } else {
795 // multi-dimensional array -- we rely on array_flatten to
796 // assure us that the array depth is 1
797 foreach ($csv_data as $row) {
798 $wr = csv_array_flatten($row);
799 // $wr is false if $row is not an array
800 if ($wr) {
801 $indc += fputcsv ( $fh , $wr );
802 } else {
803 continue;
807 fclose($fh);
809 return $indc;
813 * Search a csv record file and return the row or values from selected columns
815 * This function requires that the $search_ar parameter be an array
816 * with keys ['s_val']['s_col']['r_cols'], and 'r_cols' is an array
817 * 's_val' is the search value, s_col is the column to check, r_cols is an array
818 * of column numbers from which values are returned. If r_cols is not an array,
819 * then the entire row will be returned. If the 'expect' parameter is 1, then
820 * the search will stop after the first success and return the result. Otherwise, the
821 * entire file will be searched.
822 * ex: csv_search_record('batch', 'claim', array('s_val'=>'20120115', 's_col'=>1, 'r_cols'=>array(0, 1, 2, 8)), "2" )
824 * @uses csv_parameters()
825 * @param string $file_type
826 * @param string $csv_type
827 * @param array $search_ar
828 * @param mixed $expect
829 * @return array
831 function csv_search_record($file_type, $csv_type, $search_ar, $expect="1") {
833 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ack", $file_type) ) {
834 csv_edihist_log("csv_search_record: incorrect file type $file_type");
835 return FALSE;
838 $params = csv_parameters($file_type);
840 if ($csv_type == "claim") {
841 $fp = $params['claims_csv'];
842 } elseif ($csv_type == "file") {
843 $fp = $params['files_csv'];
844 } else {
845 csv_edihist_log("csv_search_record: incorrect csv type $csv_type");
846 return FALSE;
849 if (!is_array($search_ar) || array_keys($search_ar) != array('s_val', 's_col', 'r_cols')) {
850 csv_edihist_log("csv_search_record: invalid search criteria");
851 return FALSE;
853 $sv = $search_ar['s_val'];
854 $sc = $search_ar['s_col'];
855 $rv = (is_array($search_ar['r_cols']) && count($search_ar['r_cols'])) ? $search_ar['r_cols'] : 'all';
856 $ret_ar = array();
857 $idx = 0;
859 if (($fh1 = fopen($fp, "r")) !== FALSE) {
860 while (($data = fgetcsv($fh1)) !== FALSE) {
861 // check for a match
862 if ($data[$sc] == $sv) {
863 if ($rv == 'all') {
864 $ret_ar[$idx] = $data;
865 } else {
866 // now loop through the 'r_cols' array for data index
867 $dct = count($data);
868 foreach($rv as $c) {
869 // make sure we don't access a non-existing index
870 if ($c >= $dct) { continue; }
872 $ret_ar[$idx][] = $data[$c];
875 $idx++;
876 if ($expect == '1') { break; }
879 fclose($fh1);
880 } else {
881 csv_edihist_log("csv_search_record: failed to open $fp");
882 return false;
884 if (empty($ret_ar) ) {
885 return false;
886 } else {
887 return $ret_ar;
892 * Search the 'claims' csv table for the patient control and find the associated file name
894 * In 'claims' csv tables, clm01 is position 2 number is pos 4, and filename is pos 5;
895 * except in ebr, ibr, and dpr files which have batch name in pos 4. See the
896 * csv files column headings for more information.
898 * @uses csv_parameters()
899 * @uses csv_pid_enctr_parse()
900 * @see csv_files_header()
901 * @param string patient control-- pid-encounter, pid, or encounter
902 * @param string filetype batch, era, f277, f997, ibr, ebr, dpr
903 * @param string search type encounter, pid, or ptctln
904 * @return array|bool [i](pid_encounter, number, filename) or false on error
906 function csv_file_with_pid_enctr ($ptctln, $filetype='batch', $srchtype='encounter' ) {
908 // return array of [i](pid_encounter, filename), there may be more than one file
910 if (!$ptctln) {
911 csv_edihist_log("csv_file_with_pid_enctr: missing encounter data");
912 //return "invalid encounter data<br />" . PHP_EOL;
913 return false;
915 // IBR_FTYPES
916 if (! strpos('|era|835|f997|999|ibr|ebr|dpr|f277|batch|837|ta1|ack', $filetype) ) {
917 csv_edihist_log("csv_file_with_pid_enctr: incorrect file type $filetype");
918 return false;
919 } else {
920 $params = csv_parameters($filetype);
921 //$fp = isset($params['claims_csv']) ? dirname(__FILE__).$params['claims_csv'] : false;
922 $fp = isset($params['claims_csv']) ? $params['claims_csv'] : false;
923 if (!$fp) {
924 csv_edihist_log("csv_file_with_pid_enctr: incorrect file type $filetype");
925 return false;
929 $enctr = trim($ptctln);
931 preg_match('/\D/', $enctr, $match2, PREG_OFFSET_CAPTURE);
933 if (count($match2)) {
935 if ($srchtype != 'ptctln') {
936 $idar = csv_pid_enctr_parse($enctr);
937 if (is_array($idar) && count($idar)) {
938 $p = strval($idar['pid']);
939 $plen = strlen($p);
940 $e = strval($idar['enctr']);
941 $elen = strlen($e);
942 } else {
943 csv_edihist_log("csv_file_with_pid_enctr: error parsing pid_encounter $pid_enctr");
944 return false;
946 } else {
947 $pe = $enctr;
949 } else {
950 // no match from preg_match, so $enctr has no non-digit characer like '-'
951 if ($srchtype == 'ptctln') {
952 if (strlen($enctr) > IBR_ENCOUNTER_DIGIT_LENGTH) {
953 $pe = substr($enctr, 0, strlen($enctr)-IBR_ENCOUNTER_DIGIT_LENGTH) .'-'.substr($enctr, -IBR_ENCOUNTER_DIGIT_LENGTH);
954 } else {
955 // no pid, so change search type to encounter only
956 $srchtype = 'encounter';
959 $p = strval($enctr);
960 $e = strval($enctr);
961 $plen = strlen($p);
962 $elen = strlen($e);
965 $ret_ar = array();
966 // in 'claims' csv tables, clm01 is position 2 and filename is position 5
967 if (($fh1 = fopen($fp, "r")) !== FALSE) {
968 if ($srchtype == 'encounter') {
969 while (($data = fgetcsv($fh1, 1024, ",")) !== FALSE) {
970 // check for a match
971 if (substr($data[2], -$elen) == $e) {
972 // since e=123 will match 1123 and 123
973 $peval = csv_pid_enctr_parse($data[2]);
974 if (is_array($peval) && count($peval)) {
975 if ($peval['enctr'] == $e) {
976 $ret_ar[] = array($data[2], $data[4], $data[5]);
981 } elseif ($srchtype == 'pid') {
982 while (($data = fgetcsv($fh1, 1024, ",")) !== FALSE) {
983 // check for a match
984 if (substr($data[2], 0, $plen) == $p) {
985 // since p=123 will match 1123 and 123
986 $peval = csv_pid_enctr_parse($data[2]);
987 if (is_array($peval) && count($peval)) {
988 if ($peval['pid'] == $p) {
989 $ret_ar[] = array($data[2], $data[4], $data[5]);
994 } else {
995 while (($data = fgetcsv($fh1, 1024, ",")) !== FALSE) {
996 // check for a match
997 if ($data[2] == $pe) {
998 $ret_ar[] = array($data[2], $data[4], $data[5]);
1002 fclose($fh1);
1003 } else {
1004 csv_edihist_log("csv_file_with_pid_enctr: failed to open csv file ");
1005 return false;
1007 return $ret_ar;
1011 * get the x12 file containing the control_num ISA13
1013 * The csv for x12 files 999, 277, 835, 837 has the control number in pos 2
1014 * and the filename in pos 1. This is a convenience function, since the actual
1015 * work is done by csv_search_record()
1017 * @uses csv_search_record()
1018 * @param string $control_num the interchange control number, isa13
1019 * @return string the file name
1021 function csv_file_by_controlnum($type, $control_num) {
1022 // get the batch file containing the control_num
1024 if (! strpos("|era|f997|f277|batch|ta1", $type) ) {
1025 csv_edihist_log("csv_file_by_controlnum: incorrect file type $type");
1026 return FALSE;
1028 // $search_ar should have keys ['s_val']['s_col'] array(['r_cols'][])
1029 // like "batch', 'claim, array(9, '0024', array(1, 2, 7))
1030 $fn = '';
1031 if ($type == 'era') {
1032 $ctln = trim(strval($control_num));
1033 } else {
1034 $ctln = (strlen($control_num) >= 9) ? substr($control_num, 0, 9) : trim(strval($control_num));
1036 $search = array('s_val'=>$ctln, 's_col'=>2, 'r_cols'=>array(1));
1037 $result = csv_search_record($type, 'file', $search, "1");
1038 if (is_array($result) && count($result[0]) == 1) {
1039 $fn = $result[0][0];
1041 return $fn;
1045 * A function to try and assure the pid-encounter is correctly parsed
1047 * assume a format of pid-encounter, since that is sent in the OpenEMR x12 837
1048 * however, in case payer mangles the pid-encounter by dropping the separator,
1049 * check value and use IBR_ENCOUNTER_DIGIT_LENGTH constant
1051 * @param string $pid_enctr the value from element CPL01
1052 * return array array('pid' => $pid, 'enctr' => $enc)
1054 function csv_pid_enctr_parse( $pid_enctr ) {
1055 // evaluate the patient account field
1057 if (!$pid_enctr || !is_string($pid_enctr) ) {
1058 csv_edihist_log("csv_pid_enctr_parse: invalid argument");
1059 return false;
1061 $pval = trim($pid_enctr);
1062 preg_match('/\D/', $pval, $match2, PREG_OFFSET_CAPTURE);
1063 $inv_split = (count($match2)) ? preg_split('/\D/', $pval, 2, PREG_SPLIT_NO_EMPTY) : false;
1064 if ($inv_split) {
1065 $pid = $inv_split[0];
1066 $enc = $inv_split[1];
1067 } elseif ( preg_match('/20[01]{1}[0-9]{1}(0[0-9]{1}|1[0-2]{1})[0-3]{1}[0-9]{1}/', $pval) ) {
1068 // encounter numbers can also be Ymd like 20110412
1069 $enc = $pval;
1070 $pid = '';
1071 } else {
1072 $enc = (strlen($pval) >= IBR_ENCOUNTER_DIGIT_LENGTH) ? substr($pval, -IBR_ENCOUNTER_DIGIT_LENGTH) : $pval;
1073 $pid = (strlen($pval) > IBR_ENCOUNTER_DIGIT_LENGTH) ? substr($pval, 0, (strlen($pval)-IBR_ENCOUNTER_DIGIT_LENGTH)) : '';
1075 return array('pid' => $pid, 'enctr' => $enc);
1080 * This function is supposed to allow the downloading of a file.
1082 * Not used or tested -- do not use. Since the users cannot scan the directories,
1083 * the file to be downloaded would have to be selected from a csv table display
1084 * or some other listing produced by reading the directories.
1086 * @todo implement this function
1087 * @param string $filename
1088 * @return void --save file dialogue
1090 function csv_download_file( $filename ){
1091 // adapted from http://php.net/manual/en/function.header.php
1092 // phpnet at holodyn dot com 31-Jan-2011 01:01
1093 // Must be fresh start
1094 // ///////////////////// this function not used as of now and probably doesn't work
1095 //////////////////////////////////////////////////////////////////////////////////////
1096 // but a "view file" function will be made, probably as a separate page
1097 // links in csv file
1098 // <a href='edi_view_file.php?key=filename' target='_blank'>filename</a>
1099 // OpenEMR open log link: <a href='../../library/freeb/process_bills.log' target='_blank' class='link_submit' title=''>[View Log]</a>
1101 if( headers_sent() ) {
1102 csv_edihist_log("csv_download_file: error headers already sent");
1103 return FALSE;
1105 //FILTER_SANITIZE_URL
1106 //$filename = $_GET['dlkey'];
1108 $filename = filter_input(INPUT_GET,'dlkey',FILTER_SANITIZE_STRING);
1109 $fp = csv_check_filepath($filename);
1110 if (!fp) {
1111 csv_edihist_log("csv_download_file: invalid filename for download $filename");
1112 //echo "csv_download_file: invalid filename for download $filename <br />" . PHP_EOL;
1113 return FALSE; // no -- httpd error code 504
1116 $file_html = csv_filetohtml($fp);
1118 if ($file_html) {
1119 $bn = basename($filename) . '.html';
1120 $ctype="text/html";
1121 $host = $_SERVER['HTTP_HOST'];
1122 $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
1123 header("Location: http://$host$uri/$extra");
1124 file_put_contents($host . $uri .DIRECTROY_SEPARATOR. $bn, $file_html);
1125 $fsize = filesize($host . $uri .DIRECTROY_SEPARATOR. $bn);
1128 } else {
1129 csv_edihist_log("csv_download_file: file was not converted to html $filename");
1130 //echo "csv_download_file: file was not converted to html $filename <br />" . PHP_EOL;
1131 return FALSE;
1134 // Required for some browsers
1135 if(ini_get('zlib.output_compression'))
1136 ini_set('zlib.output_compression', 'Off');
1139 $ctype="application/pdf";
1141 header("Pragma: public"); // required
1142 header("Expires: 0");
1143 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
1144 header("Cache-Control: private",false); // required for certain browsers
1145 header("Content-Type: $ctype");
1146 header("Content-Disposition: attachment; filename=\"".basename($fp)."\";" );
1147 header("Content-Transfer-Encoding: binary");
1148 header("Content-Length: ".$fsize);
1149 ob_clean();
1150 flush();
1151 readfile( $fp );
1153 exit();
1159 * check the csv claims tables and return rows for a particular encounter
1161 * @uses csv_pid_enctr_parse()
1162 * @uses csv_file_with_pid_enctr()
1163 * @uses csv_table_select_list()
1164 * @uses csv_search_record()
1165 * @param string encounter number
1166 * @return string
1168 function csv_claim_history($encounter) {
1170 if ($encounter) {
1171 $enct = csv_pid_enctr_parse(strval($encounter));
1172 $e = ($enct) ? $enct['enctr'] : false;
1175 if (!$e) {
1176 return "invalid encounter value $encounter <br />".PHP_EOL;
1178 // get complete pid-encounter from the batch claims record
1179 $efp = csv_file_with_pid_enctr($e);
1180 if (is_array($efp) && count($efp)) {
1181 $pe = $efp[0][0];
1182 } else {
1183 csv_edihist_log("csv_claim_history: failed to locate $e in batch claims record");
1184 return "failed to locate $e in batch claims record";
1186 // use function csv_table_select_list() so that only
1187 // existing csv tables are queried
1188 $tbl2 = csv_table_select_list('array');
1189 $rtypes = array();
1190 if (is_array($tbl2) && count($tbl2) ) {
1191 foreach($tbl2 as $tbl) {
1192 $tp1 = explode(' ', $tbl['desc']);
1193 if ($tp1[1] == 'files') { continue; }
1194 if ($tp1[0] == '999' || $tp1[0] == '997' || $tp1[0] == '277') {
1195 $k = 'f'.$tp1[0];
1196 $rtypes[$k] = $k;
1197 } elseif ($tp1[0] == 'ibr' || $tp1[0] == 'ebr' || $tp1[0] == 'dpr') {
1198 $k = $tp1[0];
1199 $rtypes['prop'][] = $k;
1200 } else {
1201 $k = $tp1[0];
1202 $rtypes[$k] = $k;
1205 } else {
1206 csv_edihist_log("csv_claim_history: failed to get csv table names");
1207 return "failed to get csv table names";
1210 $ch_html .= "<table class='clmhist' columns=4><caption>Encounter Record for $pe</caption>";
1211 $ch_html .= "<tbody>".PHP_EOL;
1213 if (isset($rtypes['batch'])) {
1214 $tp = 'batch';
1215 $srchar = array('s_val'=>$pe, 's_col'=>2, 'r_cols'=>'all');
1216 $btar = csv_search_record($tp, 'claim', $srchar, '2');
1218 $ch_html .= "<tr class='chhead'>".PHP_EOL;
1219 $ch_html .= "<td>Name</td><td>SvcDate</td><td>CLM01</td><td>File</td>".PHP_EOL;
1220 $ch_html .= "</tr>".PHP_EOL;
1221 if (is_array($btar) && count($btar)) {
1222 foreach($btar as $ch) {
1223 $dt = substr($ch[1], 0, 4).'-'.substr($ch[1], 4, 2).'-'.substr($ch[1], 6, 2);
1224 //array('PtName', 'SvcDate', 'clm01', 'InsLevel', 'Ctn_837', 'File_837', 'Fee', 'PtPaid', 'Provider' );
1225 $ch_html .= "<tr class='chbatch'>".PHP_EOL;
1227 $ch_html .= "<td>{$ch[0]}</td>".PHP_EOL;
1228 $ch_html .= "<td>$dt</td>".PHP_EOL;
1229 $ch_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch={$ch[5]}&btpid={$ch[2]}'>{$ch[2]}</a></td>".PHP_EOL;
1230 $ch_html .= "<td title='{$ch[4]}'><a target='_blank' href='edi_history_main.php?fvkey={$ch[5]}'>{$ch[5]}</a></td>".PHP_EOL;
1232 $ch_html .= "</tr>".PHP_EOL;
1234 } else {
1235 $ch_html .= "<tr class='chbatch'>".PHP_EOL;
1236 $ch_html .= "<td colspan=4>Batch -- Nothing found for $pe in $tp record</td>".PHP_EOL;
1237 $ch_html .= "</tr>".PHP_EOL;
1241 if (isset($rtypes['f997'])) {
1242 $tp = 'f997';
1243 $srchar = array('s_val'=>$pe, 's_col'=>2, 'r_cols'=>'all');
1244 $f997ar = csv_search_record($tp, 'claim', $srchar, '2');
1246 $ch_html .= "<tr class='chhead'>".PHP_EOL;
1247 $ch_html .= "<td>Response</td><td>Status</td><td>File</td><td>Notes</td>".PHP_EOL;
1248 $ch_html .= "</tr>".PHP_EOL;
1249 if (is_array($f997ar) && count($f997ar)) {
1250 foreach($f997ar as $ch) {
1252 $msg = strlen($ch[7]) ? $ch[7] : 'ST Number';
1253 //array('PtName', 'SvcDate', 'clm01', 'Status', 'ak_num', 'File_997', 'Ctn_837', 'err_seg');
1254 $ch_html .= "<tr class='chf997'>";
1255 $ch_html .= "<td>997/999</td>".PHP_EOL;
1256 $ch_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?fv997={$ch[5]}&aknum={$ch[4]}'>{$ch[3]}</a></td>".PHP_EOL;
1257 $ch_html .= "<td><a target='_blank' href='edi_history_main.php?fvkey={$ch[5]}'>{$ch[5]}</a></td>".PHP_EOL;
1258 $ch_html .= "<td title='$msg'>{$ch[6]} {$ch[4]}</td>".PHP_EOL;
1259 $ch_html .= "</tr>".PHP_EOL;
1261 } else {
1262 $ch_html .= "<tr class='chf997'>";
1263 $ch_html .= "<td colspan=4>x12 999 -- Nothing found for $pe</td>".PHP_EOL;
1264 $ch_html .= "</tr>".PHP_EOL;
1268 if (isset($rtypes['f277'])) {
1269 $tp = 'f277';
1271 $srchar = array('s_val'=>$pe, 's_col'=>2, 'r_cols'=>'all');
1272 $f277ar = csv_search_record($tp, 'claim', $srchar, '2');
1274 $ch_html .= "<tr class='chhead'>".PHP_EOL;
1275 $ch_html .= "<td>Response</td><td>Status</td><td>File</td><td>ClaimID</td>".PHP_EOL;
1276 $ch_html .= "</tr>".PHP_EOL;
1277 if (is_array($f277ar) && count($f277ar)) {
1278 foreach($f277ar as $ch) {
1279 // array('PtName', 'SvcDate', 'clm01', 'Status', 'st_277', 'File_277', 'payer_name', 'claim_id', 'bht03_837');
1280 $ch_html .= "<tr class='chf277'>";
1282 $ch_html .= "<td>x12 277</td>".PHP_EOL;
1283 $ch_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?rspfile={$ch[5]}&pidenc={$ch[2]}&rspstnum={$ch[4]}'>{$ch[3]}</a></td>".PHP_EOL;
1284 $ch_html .= "<td title='{$ch[5]}'><a target='_blank' href='edi_history_main.php?fvkey={$ch[5]}'>File</a></td>".PHP_EOL;
1285 $ch_html .= "<td title='{$ch[6]}'>{$ch[7]}</td>".PHP_EOL;
1287 $ch_html .= "</tr>".PHP_EOL;
1289 } else {
1290 $ch_html .= "<tr class='chf277'>";
1291 $ch_html .= "<td colspan=4>x12 277 -- Nothing found for $pe</td>".PHP_EOL;
1292 $ch_html .= "</tr>".PHP_EOL;
1296 if (is_array($rtypes['prop']) && count($rtypes['prop']) ) {
1297 foreach($rtypes['prop'] as $tp) {
1299 $rspnm = strtoupper($tp);
1300 $srchar = array('s_val'=>$pe, 's_col'=>2, 'r_cols'=>'all');
1301 $ibrar = csv_search_record($tp, 'claim', $srchar, '2');
1303 $ch_html .= "<tr class='chhead'>".PHP_EOL;
1304 $ch_html .= "<td>Response</td><td>Status</td><td>File</td><td>Payer</td>".PHP_EOL;
1305 $ch_html .= "</tr>".PHP_EOL;
1306 if (is_array($ibrar) && count($ibrar)) {
1307 foreach($ibrar as $ch) {
1308 //array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1309 $ch_html .= "<tr class='ch$tp'>";
1311 $ch_html .= "<td>$rspnm</td>".PHP_EOL;
1312 if ($tp == 'dpr') {
1313 $ch_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?dprfile={$ch[5]}&dprclm={$ch[2]}'>{$ch[3]}</a></td>".PHP_EOL;
1314 } else {
1315 $ch_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?ebrfile={$ch[5]}&ebrclm={$ch[2]}&batchfile={$ch[4]}'>{$ch[3]}</a></td>".PHP_EOL;
1317 $ch_html .= "<td title='{$ch[5]}'><a target='_blank' href='edi_history_main.php?fvkey={$ch[5]}'>File</a></td>".PHP_EOL;
1318 $ch_html .= "<td>{$ch[6]}</td>".PHP_EOL;
1320 $ch_html .= "</tr>".PHP_EOL;
1322 } else {
1323 $ch_html .= "<tr class='ch$tp'>";
1324 $ch_html .= "<td colspan=4>$rspnm -- Nothing found for $pe</td>".PHP_EOL;
1325 $ch_html .= "</tr>".PHP_EOL;
1330 if (isset($rtypes['era'])) {
1331 $tp = 'era';
1333 $srchar = array('s_val'=>$pe, 's_col'=>2, 'r_cols'=>'all');
1334 $eraar = csv_search_record($tp, 'claim', $srchar, '2');
1336 $ch_html .= "<tr class='chhead'>".PHP_EOL;
1337 $ch_html .= "<td>Response</td><td>Status</td><td>Trace</td><td>Payer</td>".PHP_EOL;
1338 $ch_html .= "</tr>".PHP_EOL;
1339 if (is_array($eraar) && count($eraar)) {
1340 foreach($eraar as $ch) {
1342 $msg = $ch[6] .' '.$ch[7].' '.$ch[8];
1343 // array('PtName', 'SvcDate', 'clm01', 'Status', 'trace', 'File_835', 'claimID', 'Pmt', 'PtResp', 'Payer');
1344 $ch_html .= "<tr class='ch835'>";
1346 $ch_html .= "<td>x12 ERA</td>".PHP_EOL;
1347 $ch_html .= "<td>{$ch[3]} <a class='clmstatus' target='_blank' href='edi_history_main.php?erafn={$ch[5]}&pidenc={$ch[2]}&summary=yes'>S</a>&nbsp;&nbsp;<a target='_blank' href='edi_history_main.php?erafn={$ch[5]}&pidenc={$ch[2]}&srchtp=encounter'>RA</a></td>".PHP_EOL;
1348 $ch_html .= "<td><a target='_blank' href='edi_history_main.php?erafn={$ch[5]}&trace={$ch[4]}&srchtp=trace'>{$ch[4]}</a>&nbsp;&nbsp;<a target='_blank' href='edi_history_main.php?fvkey={$ch[5]}'>x12</a></td>".PHP_EOL;
1349 $ch_html .= "<td title=$msg>{$ch[9]}</td>".PHP_EOL;
1351 $ch_html .= "</tr>".PHP_EOL;
1353 } else {
1354 $ch_html .= "<tr class='ch835'>";
1355 $ch_html .= "<td colspan=4>x12 835 ERA -- Nothing found for $pe</td>".PHP_EOL;
1356 $ch_html .= "</tr>".PHP_EOL;
1359 } // end if($tp ...
1360 // -- this is where a query on the payments datatable could be used to show if payment
1361 // has been received, even if no era file shows the payment.
1363 $ch_html .= "</tbody>".PHP_EOL;
1364 $ch_html .= "</table>".PHP_EOL;
1366 return $ch_html;
1371 * Render one of our csv record files as an html table
1373 * This function determines the actual csv file from the file_type and the
1374 * csv_type. A percentage (default=100) of the csv file rows is selected
1375 * or the date field of each row is checked against the optional date parameters.
1377 * @uses csv_parameters()
1378 * @param string $file_type -- one of "|era|f997|ibr|ebr|dpr|f277|batch"
1379 * @param string $csv_type -- either "file" or "claim"
1380 * @param float $row_pct the percentage of the table to return (most recent)
1381 * @param string $datestart
1382 * @param string $dateend
1383 * @return string
1385 function csv_to_html($file_type, $csv_type, $row_pct = 1, $datestart='', $dateend='') {
1387 // read a csv file into an html table, using predefined stylesheet and javascript
1389 $csv_html = "";
1390 $is_date = FALSE;
1392 if (! strpos("|era|f997|ibr|ebr|dpr|f277|batch|ack|ta1", $file_type) ) {
1393 csv_edihist_log("csv_to_html error: incorrect file type $file_type");
1394 $csv_html .= "csv_to_html error: incorrect file type $file_type <br />".PHP_EOL;
1395 return FALSE;
1398 $params = csv_parameters($file_type);
1400 // csv tables date is given date or mtime in col 0 for file, date is service date in col 1 for claim
1401 $dtcol = ($csv_type == "file") ? $params['datecolumn'] : '1';
1402 $fncol = ($csv_type == "file") ? $params['fncolumn'] : '1';
1403 // but dpr files are only in the claims table, a special case
1404 if ($file_type == "dpr") { $fncol = $params['fncolumn']; }
1406 if ($csv_type == "claim") {
1407 $fp = $params['claims_csv'];
1408 } elseif ($csv_type == "file") {
1409 $fp = $params['files_csv'];
1410 } else {
1411 csv_edihist_log("csv_to_html error: incorrect csv type $csv_type");
1412 $csv_html .= "csv_to_html error: incorrect csv type $csv_type <br />".PHP_EOL;
1413 return FALSE;
1415 // for using OpenEMR dynarch calendar, assume format of CCYY-MM-DD
1416 if (preg_match('/\d{4}-\d{2}-\d{2}/', $datestart) && preg_match('/\d{4}-\d{2}-\d{2}/', $dateend) ) {
1417 $ds = str_replace('-', '', $datestart);
1418 $de = str_replace('-', '', $dateend);
1419 if ( $de <= $ds) { $de = date("Ymd", time()); }
1420 $is_date = TRUE;
1421 $row_pct = 1;
1424 $f_name = basename($fp);
1425 // open the file for read and read it into an array
1426 $fh = fopen($fp, "r");
1427 if ($is_date) {
1428 $isok = FALSE;
1429 $idx = 0;
1431 if ($fh !== FALSE) {
1432 while (($data = fgetcsv($fh, 1024, ",")) !== FALSE) {
1434 if ($idx == 0) {
1435 $csv_d[] = $data;
1436 } else {
1437 $isok = (substr($data[$dtcol], 0, 8) >= $ds) ? TRUE : FALSE;
1438 $isok = (substr($data[$dtcol], 0, 8) > $de) ? FALSE : $isok;
1440 if ($isok) { $csv_d[] = $data; }
1442 $idx++;
1444 fclose($fh);
1445 } else {
1446 $csv_html .= "csv_to_html: failed to open $fp <br />".PHP_EOL;
1447 return $csv_html;
1449 } else {
1450 // get the entire table
1451 if ($fh !== FALSE) {
1452 while (($data = fgetcsv($fh)) !== FALSE) {
1453 $csv_d[] = $data;
1455 fclose($fh);
1456 } else {
1457 $csv_html .= "csv_to_html: failed to open $fp <br />".PHP_EOL;
1458 return $csv_html;
1462 $ln_ct = count($csv_d);
1463 // make sure row_pct is between 0 and 1
1464 if ($row_pct > 1 || $row_pct <= 0) { $row_pct = 1; }
1465 // only return the number of desired rows
1466 $rwct = (int)($ln_ct * $row_pct) + 1;
1467 $rwst = $ln_ct - $rwct;
1468 if ($rwst < 1) { $rwst = 1; $rwct = $ln_ct; }
1470 if ($is_date) {
1471 $csv_html .= "<div id='dttl'>Table: $f_name &nbsp;&nbsp; Start Date: $datestart &nbsp; End Date: $dateend &nbsp;Rows: $rwct</div>".PHP_EOL;
1472 } else {
1473 $csv_html .= "<div id='dttl'>Table: $f_name &nbsp;&nbsp; Rows: $ln_ct &nbsp;&nbsp; Shown: $rwct</div>".PHP_EOL;
1476 $csv_html .= "<table id=\"csvTable\" class=\"csvDisplay\">".PHP_EOL;
1477 // this is the body of the table
1479 if ($csv_type == 'file') {
1480 //['era']['file'] = array('Date', 'FileName', 'Trace', 'claim_ct', 'Denied', 'Payer');
1482 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1483 foreach ($csv_d[0] as $h) { $csv_html .= "<th>$h</th>"; }
1484 $csv_html .= PHP_EOL.'</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1486 if ($file_type == 'era') {
1487 for ($i=$rwst; $i<$ln_ct; $i++) {
1488 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1489 $csv_html .= "<tr class='{$bgc}'>".PHP_EOL;
1490 foreach($csv_d[$i] as $idx=>$dta) {
1491 if ($idx == 1) {
1492 $csv_html .= "<td><a href='edi_history_main.php?fvkey=$dta' target='_blank'>$dta</a></td>".PHP_EOL;
1493 } elseif ($idx == 2) {
1494 $fnm = $csv_d[$i][1];
1495 $csv_html .= "<td><a href='edi_history_main.php?erafn=$fnm&trace=$dta' target='_blank'>$dta</a> &nbsp;&nbsp;<a class=\"clmstatus\" target='_blank' href='edi_history_main.php?tracecheck=$dta&ckprocessed=yes'>(a)</td>".PHP_EOL;
1496 } else {
1497 $csv_html .= "<td>$dta</td>".PHP_EOL;
1500 $csv_html .= "</tr>".PHP_EOL;
1502 } elseif ($file_type == 'f997') {
1504 // array('Date', 'FileName', 'Ctn_999', 'ta1ctrl', 'RejCt');
1505 for ($i=$rwst; $i<$ln_ct; $i++) {
1506 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1507 $csv_html .= "<tr class=\"$bgc\">".PHP_EOL;
1509 foreach($csv_d[$i] as $idx => $dta) {
1510 if ($idx == 1) {
1511 $fnm = $dta;
1512 $ext = strtolower(substr($dta, -3));
1513 $csv_html .= "<td><a target=\"_blank\" href=\"edi_history_main.php?fvkey=$dta\">$dta</a>&nbsp;&nbsp;<a target=\"_blank\" href=\"edi_history_main.php?fvkey=$dta&readable=yes\">Text</a></td>".PHP_EOL;
1514 } elseif ($idx == 3) {
1515 $csv_html .= "<td><a target=\"_blank\" href=\"edi_history_main.php?btctln=$dta\">$dta</a></td>".PHP_EOL;
1516 } elseif ($idx == 4) {
1517 if ($ext == '999' || $ext == '997') {
1518 $csv_html .= "<td><a class=\"codeval\" target=\"_blank\" href=\"edi_history_main.php?fv997=$fnm&err997=$dta\">$dta</a></td>".PHP_EOL;
1519 } elseif ($ext == 'ta1' || $ext == 'ack') {
1520 $csv_html .= "<td><a class=\"codeval\" target=\"_blank\" href=\"edi_history_main.php?ackfile=$fnm&ackcode=$dta\">$dta</a></td>".PHP_EOL;
1521 } else {
1522 $csv_html .= "<td>$dta</td>".PHP_EOL;
1524 } else {
1525 $csv_html .= "<td>$dta</td>".PHP_EOL;
1528 $csv_html .= "</tr>".PHP_EOL;
1530 } elseif ($file_type == 'ebr' || $file_type == 'ibr' ) {
1531 //['ibr']['file'] = array('Date', 'FileName', 'clrhsid', 'claim_ct', 'reject_ct', 'Batch');
1532 for ($i=$rwst; $i<$ln_ct; $i++) {
1533 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1534 $fnm = $csv_d[$i][1];
1535 $btfile = $csv_d[$i][5];
1536 if (intval($csv_d[$i][4]) > 0) {
1537 $rejlink = "<td><a class=\"clmstatus\" target=\"_blank\" href=\"edi_history_main.php?ebrfile=$fnm&ebrclm=any\">{$csv_d[$i][4]}</a></td>";
1538 } else {
1539 $rejlink = "<td>{$csv_d[$i][4]}</td>";
1542 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1543 $csv_html .= "<td><a target=\"_blank\" href=\"edi_history_main.php?fvkey={$csv_d[$i][1]}\">{$csv_d[$i][1]}</a>&nbsp;&nbsp;<a target=\"_blank\" href=\"edi_history_main.php?fvkey={$csv_d[$i][1]}&readable=yes\">Text</a></td>".PHP_EOL;
1544 $csv_html .= "<td>{$csv_d[$i][2]}</td>".PHP_EOL;
1545 $csv_html .= "<td>{$csv_d[$i][3]}</td>".PHP_EOL;
1546 $csv_html .= $rejlink.PHP_EOL;
1547 $csv_html .= "<td><a target=\"_blank\" href=\"edi_history_main.php?fvkey=$btfile\">$btfile</a></td>".PHP_EOL;
1549 $csv_html .= "</tr>".PHP_EOL;
1551 } elseif ($file_type == 'batch') {
1552 //['batch']['file'] = array('Date', 'FileName', 'Ctn_837', 'claim_ct', 'x12_partner');
1553 for ($i=$rwst; $i<$ln_ct; $i++) {
1554 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1555 $csv_html .= "<tr class='{$bgc}'>";
1556 foreach($csv_d[$i] as $idx=>$dta) {
1557 $fnm = $csv_d[$i][$fncol];
1558 if ($idx == $fncol) {
1559 $csv_html .= "<td><a href='edi_history_main.php?fvkey=$dta' target='_blank'>$dta</a></td>".PHP_EOL;
1560 } elseif ($idx == 2) {
1561 // batch control number
1562 $csv_html .= "<td>$dta &nbsp;&nbsp;<a class=\"clmstatus\" target=\"_blank\" href=\"edi_history_main.php?batchicn=$dta\">(r)</a></td>";
1563 } else {
1564 $csv_html .= "<td>$dta</td>".PHP_EOL;
1567 $csv_html .= "</tr>".PHP_EOL;
1569 } else {
1570 // the generic case -- for 'file' type tables, the filename is in column 1, as set in the parameters array
1571 // see csv_parameters()
1572 for ($i=$rwst; $i<$ln_ct; $i++) {
1573 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1574 $csv_html .= "<tr class='{$bgc}'>";
1575 foreach($csv_d[$i] as $idx=>$dta) {
1576 if ($idx == $fncol) {
1577 $csv_html .= "<td><a href='edi_history_main.php?fvkey=$dta' target='_blank'>$dta</a></td>".PHP_EOL;
1578 } else {
1579 $csv_html .= "<td>$dta</td>".PHP_EOL;
1582 $csv_html .= "</tr>".PHP_EOL;
1585 } elseif ($csv_type == 'claim') {
1586 // a 'claim' type table $csv_type == 'claim' there is more variation
1587 if ($file_type == 'era') {
1588 // era csv_type is claim col 2 is pid, 3 encounter, 8 is trace
1589 //['era']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'trace', 'File_835', 'claimID', 'Pmt', 'PtResp', 'Payer');
1590 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1591 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Status</th><th>Trace</th><th>File</th><th>Payer</th>'.PHP_EOL;
1592 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1593 for ($i=$rwst; $i<$ln_ct; $i++) {
1594 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1595 $nm = $csv_d[$i][0];
1596 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1597 $clm = $csv_d[$i][2];
1598 $sts = $csv_d[$i][3];
1599 $trc = $csv_d[$i][4];
1600 $fnm = $csv_d[$i][5];
1601 $clmid = $csv_d[$i][6];
1602 $msg = $csv_d[$i][6] .' '.$csv_d[$i][7].' '.$csv_d[$i][8];
1603 $pr = $csv_d[$i][9];
1604 $csv_html .= "<tr class='{$bgc}'>";
1605 //Name
1606 $csv_html .= "<td>$nm</td>".PHP_EOL;
1607 //SvcDate
1608 $csv_html .= "<td>$dt</td>".PHP_EOL;
1609 //CLM01
1610 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch=&btpid=$clm'>$clm</a></td>".PHP_EOL;
1611 //Status
1612 $csv_html .= "<td>$sts <a class='clmstatus' target='_blank' href='edi_history_main.php?erafn=$fnm&pidenc=$clm&summary=yes'>S</a> &nbsp;&nbsp;<a target='_blank' href='edi_history_main.php?erafn=$fnm&pidenc=$clm&srchtp=encounter'>RA</a></td>".PHP_EOL;
1613 //Trace
1614 $csv_html .= "<td><a target='_blank' href='edi_history_main.php?erafn=$fnm&trace=$trc&srchtp=trace'>$trc</a></td>".PHP_EOL;
1615 //File_835
1616 $csv_html .= "<td title=$fnm><a target='_blank' href='edi_history_main.php?fvkey=$fnm'>x12</a></td>".PHP_EOL;
1617 //ClaimID
1618 $csv_html .= "<td title=$msg>$pr</td>".PHP_EOL;
1620 $csv_html .= "</tr>".PHP_EOL;
1622 } elseif ($file_type == 'dpr') {
1623 // dpr case, only file type is claim,
1624 //['dpr']['claim'] = array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1625 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1626 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Status</th><th>Batch</th><th>File</th><th>Payer</th>'.PHP_EOL;
1627 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1628 for ($i=$rwst; $i<$ln_ct; $i++) {
1629 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1630 $pidenc = $csv_d[$i][2];
1631 $btfile = $csv_d[$i][4];
1632 $fnm = $csv_d[$i][5];
1633 //$msg = $csv_d[$i][8];
1634 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1635 $csv_html .= "<tr class='{$bgc}'>";
1636 //Name
1637 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1638 //SvcDate
1639 $csv_html .= "<td>$dt</td>".PHP_EOL;
1640 //CLM01
1641 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch=$btfile&btpid=$pidenc'>$pidenc</a></td>".PHP_EOL;
1642 //Status
1643 $csv_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?dprfile=$fnm&dprclm=$pidenc'>{$csv_d[$i][3]}</a></td>".PHP_EOL;
1644 //Batch
1645 $csv_html .= "<td title=$btfile><a target='_blank'href='edi_history_main.php?fvkey=$btfile'>Batch</a></td>".PHP_EOL;
1646 //File
1647 $csv_html .= "<td title=$fnm><a target='_blank'href='edi_history_main.php?fvkey=$fnm'>dpr File</a> <a target='_blank'href='edi_history_main.php?fvkey=$fnm&readable=yes'>Text</a></td>".PHP_EOL;
1648 //Payer
1649 $csv_html .= "<td>{$csv_d[$i][6]}</td>".PHP_EOL;
1651 $csv_html .= "</tr>".PHP_EOL;
1654 } elseif ($file_type == 'ebr' || $file_type == 'ibr') {
1655 //array('PtName','SvcDate', 'clm01', 'Status', 'Batch', 'FileName', 'Payer');
1657 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1658 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Status</th><th>Batch</th><th>File</th><th>Payer</th>'.PHP_EOL;
1659 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1660 for ($i=$rwst; $i<$ln_ct; $i++) {
1661 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1663 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1664 $pidenc = $csv_d[$i][2];
1665 $btfile = $csv_d[$i][4];
1666 $fnm = $csv_d[$i][5];
1667 //$msg = $csv_d[$i][8];
1668 $csv_html .= "<tr class='{$bgc}'>";
1669 //Name
1670 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1671 //SvcDate
1672 $csv_html .= "<td>$dt</td>".PHP_EOL;
1673 //CLM01
1674 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch=$btfile&btpid=$pidenc'>$pidenc</a></td>".PHP_EOL;
1675 //Status
1676 $csv_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?ebrfile=$fnm&ebrclm=$pidenc'>{$csv_d[$i][3]}</a></td>".PHP_EOL;
1677 //Batch
1678 $csv_html .= "<td title=$btfile><a target='_blank' href='edi_history_main.php?fvkey=$btfile'>Batch</a></td>".PHP_EOL;
1679 //File
1680 $csv_html .= "<td title=$fnm><a target='_blank'href='edi_history_main.php?fvkey=$fnm'>File</a> <a target='_blank'href='edi_history_main.php?fvkey=$fnm&readable=yes'>R</a></td>".PHP_EOL;
1681 //Payer
1682 $csv_html .= "<td title=$msg>{$csv_d[$i][6]}</td>".PHP_EOL;
1684 $csv_html .= "</tr>".PHP_EOL;
1686 } elseif ($file_type == 'f997') {
1688 //['f997']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'ak_num', 'File_997', 'Ctn_837', 'err_seg');
1689 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1690 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Status</th><th>ST</th><th>File</th><th>Batch</th>'.PHP_EOL;
1691 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1692 for ($i=$rwst; $i<$ln_ct; $i++) {
1693 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1695 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1696 $pidenc = $csv_d[$i][2];
1697 $akn = $csv_d[$i][4];
1698 $fnm = $csv_d[$i][5];
1699 $msg = strlen($csv_d[$i][7]) ? $csv_d[$i][7] : 'ST Number';
1701 $csv_html .= "<tr class='{$bgc}'>";
1702 //Name
1703 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1704 //SvcDate
1705 $csv_html .= "<td>$dt</td>".PHP_EOL;
1706 //CLM01
1707 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch={$csv_d[$i][6]}&btpid=$pidenc'>$pidenc</a></td>".PHP_EOL;
1708 //Status
1709 $csv_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?fv997=$fnm&aknum=$akn'>{$csv_d[$i][3]}</a></td>".PHP_EOL;
1710 //ST
1711 $csv_html .= "<td title='$msg'>$akn</td>".PHP_EOL;
1712 //File
1713 $csv_html .= "<td><a target='_blank' href='edi_history_main.php?fvkey=$fnm'>$fnm</a></td>".PHP_EOL;
1714 //Batch
1715 $csv_html .= "<td><a target='_blank' href='edi_history_main.php?btctln={$csv_d[$i][6]}'>{$csv_d[$i][6]}</a></td>".PHP_EOL;
1717 $csv_html .= "</tr>".PHP_EOL;
1719 } elseif ($file_type == 'f277') {
1721 //['f277']['claim'] = array('PtName', 'SvcDate', 'clm01', 'Status', 'st_277', 'File_277', 'payer_name', 'claim_id', 'bht03_837');
1722 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1723 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Status</th><th>File_277</th><th>ClaimID</th>'.PHP_EOL; //<th>Batch</th>
1724 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1725 for ($i=$rwst; $i<$ln_ct; $i++) {
1726 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1727 $csv_html .= "<tr class='{$bgc}'>";
1729 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1730 $btpid = $csv_d[$i][2];
1731 $f277file = $csv_d[$i][5];
1732 $clmid = $csv_d[$i][7];
1733 $bt_bht03 = $csv_d[$i][8];
1734 //$msg277 = (strlen($csv_d[$i][9])) ? $csv_d[$i][9] : '';
1735 //Name
1736 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1737 //SvcDate
1738 $csv_html .= "<td>$dt</td>".PHP_EOL;
1739 //CLM01
1740 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch=$bt_bht03&btpid=$btpid'>$btpid</a></td>".PHP_EOL;
1741 //Status
1742 $csv_html .= "<td><a class='clmstatus' target='_blank' href='edi_history_main.php?rspfile=$f277file&pidenc=$btpid&rspstnum={$csv_d[$i][4]}'>{$csv_d[$i][3]}</a></td>".PHP_EOL;
1743 //File
1744 $csv_html .= "<td title='$f277file'><a target='_blank' href='edi_history_main.php?fvkey=$f277file'>File</a></td>".PHP_EOL;
1745 //ClaimID
1746 $csv_html .= "<td>$clmid</td>".PHP_EOL;
1747 //Batch
1748 //$csv_html .= "<td title='$bt_bht03'><a target='_blank' href='edi_history_main.php?btctln=$bt_bht03'>Batch</a></td>".PHP_EOL;
1750 $csv_html .= "</tr>".PHP_EOL;
1752 } elseif ($file_type == 'batch') {
1754 //['batch']['claim'] = array('PtName', 'SvcDate', 'clm01', 'InsLevel', 'Ctn_837', 'File_837', 'Fee', 'PtPaid', 'Provider' );
1755 $csv_html .= '<thead>'.PHP_EOL.'<tr>'.PHP_EOL;
1756 $csv_html .= '<th>Name</th><th>SvcDate</th><th>CLM01</th><th>Ins</th><th>Fee/PtPd</th><th>File</th><th>Provider</th>'.PHP_EOL;
1757 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1758 for ($i=$rwst; $i<$ln_ct; $i++) {
1759 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1761 $dt = substr($csv_d[$i][1], 0, 4).'-'.substr($csv_d[$i][1], 4, 2).'-'.substr($csv_d[$i][1], 6, 2);
1762 $msg = $csv_d[$i][6].' ('.$csv_d[$i][7].')';
1763 $btfile = $csv_d[$i][5];
1765 $csv_html .= "<tr class='{$bgc}'>";
1766 //Name
1767 $csv_html .= "<td>{$csv_d[$i][0]}</td>".PHP_EOL;
1768 //SvcDate
1769 $csv_html .= "<td>$dt</td>".PHP_EOL;
1770 //CLM01
1771 $csv_html .= "<td><a class='btclm' target='_blank' href='edi_history_main.php?fvbatch=$btfile&btpid={$csv_d[$i][2]}'>{$csv_d[$i][2]}</a></td>".PHP_EOL;
1772 //Ins
1773 $csv_html .= "<td>{$csv_d[$i][3]}</td>".PHP_EOL;
1774 //Fee/PtPd
1775 $csv_html .= "<td>$msg</td>".PHP_EOL;
1776 //File
1777 $csv_html .= "<td><a target='_blank' href='edi_history_main.php?fvkey=$btfile'>$btfile</a></td>".PHP_EOL;
1778 //Provider
1779 $csv_html .= "<td>{$csv_d[$i][8]}</td>".PHP_EOL;
1781 $csv_html .= "</tr>".PHP_EOL;
1783 } else {
1785 $csv_html .= '<thead>'.PHP_EOL.'<tr>';
1786 foreach ($csv_d[0] as $h) { $csv_html .= "<th>$h</th>"; }
1787 $csv_html .= '</tr>'.PHP_EOL.'</thead>'.PHP_EOL.'<tbody>'.PHP_EOL;
1789 for ($i=$rwst; $i<$ln_ct; $i++) {
1790 $bgc = ($i % 2 == 1 ) ? 'odd' : 'even';
1791 $csv_html .= "<tr class='{$bgc}'>";
1792 foreach($csv_d[$i] as $idx=>$dta) {
1793 $csv_html .= "<td>$dta</td>".PHP_EOL;
1795 $csv_html .= "</tr>".PHP_EOL;
1798 } // end body of the table
1799 //$csv_html .= "</tbody>".PHP_EOL."</table>".PHP_EOL."</div>".PHP_EOL;
1800 $csv_html .= "</tbody>".PHP_EOL."</table>".PHP_EOL;
1801 return $csv_html;
1805 * Produce an html rendition of one of our files
1807 * this function accepts a file name array from uploads.php, one file only,
1808 * or a string file path as the filepath argument
1810 * @uses csv_check_filepath()
1811 * @uses csv_parameters()
1812 * @uses csv_x12_segments()
1813 * @param mixed $filepath string or array path to or name of one of our files
1814 * @return string html formatted
1816 function csv_filetohtml ($filepath) {
1818 if (is_array($filepath) && count($filepath) > 0) {
1819 $ftkey = array_keys($filepath);
1820 $type = $ftkey[0];
1821 $ftestpath = $filepath[$type][0];
1822 $bn = basename($ftestpath);
1823 } else {
1824 $bn = basename($filepath);
1825 $params = csv_parameters("ALL");
1827 foreach($params as $ky=>$val) {
1828 if ( !$params[$ky]['regex'] ) { continue; }
1829 if (!preg_match($params[$ky]['regex'], $bn) ) {
1830 continue;
1831 } else {
1832 $type = $params[$ky]['type'];
1833 //$ftestpath = dirname(__FILE__) . $params[$ky]['directory'].DIRECTORY_SEPARATOR.$bn;
1834 $ftestpath = $params[$ky]['directory'].DIRECTORY_SEPARATOR.$bn;
1835 break;
1841 if (!isset($type) ) {
1842 csv_edihist_log("csv_filetohtml: failed to type $bn");
1843 $out_str = "csv_filetohtml: failed to classify $bn <br />". PHP_EOL;
1844 return $out_str;
1847 $fp = csv_check_filepath($ftestpath, $type);
1848 if (!$fp) {
1849 csv_edihist_log("csv_filetohtml: could not get good path for $bn");
1850 $out_str = "csv_filetohtml: could not get good path for $bn <br />". PHP_EOL;
1851 return $out_str;
1853 // different file types
1854 if (strpos("|batch|era|f277|f997|999|837|835|ta1", (string)$type) ) {
1855 // x12 file types
1856 $seg_ar = csv_x12_segments($fp, $type);
1857 if (is_array($seg_ar) && count($seg_ar['segments']) ) {
1858 $txt_ar = $seg_ar['segments'];
1859 $seg_d = $seg_ar['delimiters']['t'];
1860 $fltp = 'x12';
1861 } else {
1862 csv_edihist_log("csv_filetohtml: did not get segments for $bn");
1863 $out_str = "csv_filetohtml: did not get segments for $bn <br />". PHP_EOL;
1864 return $out_str;
1867 } elseif (strpos("|ibr|ebr|dpr|ack", $type) ) {
1868 // clearinghouse file types (newlines)
1869 $fh = fopen($fp, 'r');
1870 if ($fh) {
1871 while (($buffer = fgets($fh, 1024)) !== false) {
1872 $txt_ar[] = $buffer;
1874 if (!feof($fh)) {
1875 csv_edihist_log("csv_filetohtml: failed to open $bn <br />");
1877 fclose($fh);
1878 $fltp = 'ibr';
1879 } else {
1880 csv_edihist_log("csv_filetohtml: did not get lines for $bn");
1881 $out_str = "csv_filetohtml: did not get lines for $bn <br />". PHP_EOL;
1882 return $out_str;
1884 } elseif (strpos("|text", $type) ) {
1885 // clearinghouse readable versions
1886 $txt_ar = file_get_contents($fp);
1887 $fltp = 'text';
1888 if (!$txt_ar) {
1889 csv_edihist_log("csv_filetohtml: did not get contents for $bn");
1890 $out_str = "csv_filetohtml: did not get contents for $bn <br />". PHP_EOL;
1891 return $out_str;
1894 } else {
1895 csv_edihist_log("csv_filetohtml: $type was not matched for $bn");
1896 $out_str = "csv_filetohtml: $type was not matched for $bn <br />". PHP_EOL;
1897 return $out_str;
1899 // we have navigated our checks and read the file
1900 $out_str = "";
1902 // now prepare the html page
1903 $out_str = '';
1904 // use an ordered list format for x12 and ebr/ibr, regular text for text type
1905 if ($fltp == "text") {
1906 $out_str .= "<h4>$bn</h4>
1907 <div class=\"filetext\">
1908 <pre>
1910 $out_str .= $txt_ar;
1911 $out_str .= "
1912 </pre>";
1913 } elseif ($fltp == 'x12') {
1914 $out_str .= "<h4>$bn</h4>
1915 <div class=\"filetext\">
1916 <ol>
1918 foreach($txt_ar as $line) {
1919 $out_str .= "
1920 <li>
1922 $line$seg_d
1923 </p>
1924 </li>
1927 $out_str .= "</ol>
1929 } else {
1930 $out_str .= "<h4>$bn</h4>
1931 <div class=\"filetext\">
1932 <ol>
1934 foreach($txt_ar as $line) {
1935 $out_str .= "
1936 <li>
1938 $line
1939 </p>
1940 </li>
1943 $out_str .= "</ol>
1947 $out_str .= "
1948 </div>
1949 </body>
1950 </html>";
1952 return $out_str;
1956 * Check that the file path we are working with is a readable file.
1958 * If it is a file we have uploaded and we have only the file name
1959 * this function will type the file and find it in the uploaded files directories
1960 * and return the complete path.
1962 * @uses csv_parameters()
1963 * @param string $filename name of a file that is one of our types
1964 * @param string $type optional; one of our file types
1965 * @return string either an empty string or a readable filepath
1967 function csv_check_filepath($filename, $type = "ALL") {
1969 // if file is readable, just return it
1970 if ( is_file($filename) && is_readable($filename) ) {
1971 return $filename;
1974 $goodpath = "";
1975 $fn = basename($filename);
1977 if ($type != "ALL") {
1978 $p = csv_parameters($type);
1979 if (is_array($p) && array_key_exists('type', $p) ) {
1980 // type was found
1981 $fp = $p['directory'].DIRECTORY_SEPARATOR.$fn;
1982 if ( is_file($fp) && is_readable($fp) ) {
1983 $goodpath = $fp;
1985 } else {
1986 csv_edihist_log("csv_check_filepath: invalid type $type");
1988 } else {
1989 $p_ar = csv_parameters("ALL");
1990 foreach ($p_ar as $tp=>$par) {
1992 if ( !$p_ar[$tp]['regex'] || !preg_match($p_ar[$tp]['regex'], $fn) ) {
1993 continue;
1994 } else {
1996 $fp = $p_ar[$tp]['directory'].DIRECTORY_SEPARATOR.$fn;
1997 if ( is_file($fp) && is_readable($fp) ) {
1998 $goodpath = $fp;
2000 break;
2005 return $goodpath;
2009 * Verify readibility and check for some expected content.
2011 * @uses csv_check_filepath()
2012 * @param string $file_path full path to file
2013 * @param string $type one of our file types
2014 * @param bool $val_array optional; default is false, whether to return filepath only
2015 * or array(filepath, next_segment)
2016 * @return string|array string file path or array if valid, otherwise 'false'
2018 function csv_verify_file( $file_path, $type, $val_array=FALSE ) {
2019 // check whether $file_path actually leads to a plausible x12 file
2020 // and supply proper directory if needed
2021 $fp = csv_check_filepath($file_path, $type);
2023 // verify that the file is correct format by checking the first ST segment
2024 if ($fp) {
2026 $type = strtolower($type);
2028 if ($type == "batch" || $type == "837") { $st_str = "ST*837"; $next_segment = "GS"; $slen = 250; }
2029 if ($type == "era" || $type == "835") { $st_str = "ST*835"; $next_segment = "GS"; $slen = 250; }
2030 if ($type == "997" || $type == "f997") { $st_str = "ST*997"; $next_segment = "TA1"; $slen = 250; }
2031 if ($type == "999" || $type == "f999") { $st_str = "ST*999"; $next_segment = "TA1"; $slen = 250; }
2032 if ($type == "277" || $type == "f277") { $st_str = "ST*277"; $next_segment = "GS"; $slen = 250; }
2033 if ($type == "271" || $type == "f271") { $st_str = "ST*271"; $next_segment = "GS"; $slen = 250; }
2034 if ($type == "ta1") { $st_str = "TA1*"; $next_segment = "TA1"; $slen = 200; }
2035 // note the ebr, ibr, dpr ack checks will not be valid for years < 2010 or > 2019
2036 if ($type == "ebr") { $st_str = "1|201"; $slen = 10; }
2037 if ($type == "ibr") { $st_str = "1|201"; $slen = 10; }
2038 if ($type == "dpr") { $st_str = "DPR|201"; $slen = 10; }
2039 if ($type == "ack") { $st_str = "1|201"; $slen = 10; }
2040 if ($type == "text") { $st_str = "Date Received"; $slen = 800; }
2041 // for proprietary file formats, if we have a list of clearinghouses,
2042 // the $st_str could be "Availity|Emdeon|ABC " etc. and name probably found in the first 100 characters
2043 // instead of Date Received, which I just guess will be standard and within the first 500 characters
2044 $f_str = file_get_contents($fp, FALSE, NULL, 0, $slen);
2045 $st_pos = strpos($f_str, $st_str);
2047 // special check for 997/999 types, since 999 may be type 997
2048 if (($type == "997" || $type == "f997") && preg_match('/\.999$/', $fp) ) {
2049 $st_pos = strpos($f_str, "ST*999");
2050 $next_segment = "TA1";
2052 //$f_str = file_get_contents($fp);
2053 //$st_pos = strpos(substr($f_str, 0, $slen), $st_str);
2054 if ( $st_pos === FALSE ) {
2055 // did not find the magic word
2056 csv_edihist_log ("csv_verify_file: Error, not a valid $type file: $file_path");
2057 //echo "ibr_era_check_path Error, not a valid $type file: $file_path <br />"
2058 $fp = FALSE;
2059 $next_segment = FALSE;
2061 } else {
2062 $next_segment = '';
2064 if ($val_array) {
2065 return array($fp, $next_segment);
2066 } else {
2067 return $fp;
2072 * Parse Availity ebr, ibr, or dpr file to an array
2074 * This function will return a multidimensional array that is indexed per line of the file
2075 * and another array of each field or element in the line. It uses the
2076 * constant IBR_DELIMITER as the element delimiter
2078 * @param string $file_path complete path to file
2079 * @return array multidimensional array
2081 function csv_ebr_filetoarray ($file_path ) {
2082 // read the ibr, ebr, dpr file into an array of arrays
2083 // since the file is multi-line, use fgets()
2084 //$delim = "|";
2085 $ext = substr($file_path, -3);
2086 $fp = csv_verify_file( $file_path, $ext);
2087 if (!$fp) {
2088 csv_edihist_log("csv_ebr_filetoarray: failed to verify $file_path");
2089 return false;
2091 $ar_ibr = array();
2092 $fh = fopen($fp, 'r');
2093 if ($fh) {
2094 while (($buffer = fgets($fh, 4096)) !== false) {
2095 $ar_ibr[] = explode ( IBR_DELIMITER, trim($buffer) );
2097 } else {
2099 csv_edihist_log( "csv_ebr_filetoarray Error: failed to read " . $file_path );
2101 fclose($fh);
2102 return $ar_ibr;
2107 * Parse an x12 segment text into an array.
2109 * @param string $sgmt_str -- a substring of all or part of a segment
2110 * @param string $seg_term -- segment delimiter default is "~"
2111 * @param string $elem_delim -- element delimiter default is "*"
2112 * @return array -- exploded $sgmt_str by $element_d
2114 function csv_x12_segment_to_array($sgmt_str, $seg_term = "~", $elem_delim = "*") {
2116 // allow for imprecise selecting of segment strings
2118 $slen = strlen($sgmt_str);
2119 if (!$slen) { return FALSE; }
2120 if (!strpos($sgmt_str, $elem_delim)) { return FALSE; }
2121 // find segment delimiters (up to two)
2122 $p1 = strpos($sgmt_str, $seg_term);
2123 $p2 = ($p1) ? strpos($sgmt_str, $seg_term, $p1+1 ) : FALSE;
2124 if ($p1===FALSE) {
2125 // assume we have just the segment text
2126 $seg1 = trim($sgmt_str);
2127 } elseif ($p1 && $p2 && ($p2 > $p1) && ($p2-$p1 < $slen) ) {
2128 // assume we have end of segment ~ segment ~ begining of next segment
2129 $seg1 = substr($sgmt_str, $p1+1, $p2-$p1-1);
2130 } elseif ($p1 !== FALSE && $p1 == 0) {
2131 // assume we have ~segment
2132 $seg1 = substr($sgmt_str, $p1+1);
2133 } elseif ($p1 !== FALSE && $p1+1 == $slen ) {
2134 // assume we have segment~
2135 $seg1 = substr($sgmt_str, 0, $p1-1);
2136 } elseif ($p1 !== FALSE && $p1+1 < $slen ) {
2137 // assume we have segment~ start of segment
2138 $seg1 = substr($sgmt_str, 0, $p1-1);
2139 } else {
2140 // no conjecture matched, just use it
2141 $seg1 = trim($sgmt_str);
2144 $ar_seg = explode ($elem_delim, $seg1 );
2146 return $ar_seg;
2151 * Extract x12 delimiters from the ISA segment
2153 * There are obviously easier/faster ways of doing this, but I wanted to be able
2154 * to possibly extract these needed values from malformed or mishandled
2155 * files, so we go character by character. The array returned is empty on error, otherwise:
2156 * <pre>
2157 * array('t'=>segment terminator, 'e'=>element delimiter,
2158 * 's'=>sub-element delimiter, 'r'=>repetition delimiter)
2159 * </pre>
2161 * @param string $isa_str126 first 126 characters of x12 file
2162 * @param string $next_segid the segment ID immediately following ISA segment
2163 * @return mixed array or false on error
2165 function csv_x12_delimiters($isa_str126, $next_segid) {
2166 // this function reads the ISA segment and into the next segment of
2167 // an x12 file, to determine the delimiters,
2168 // ISA segment is 106 characters, 16 elements, so the $next_segid could be dispensed with
2170 if (substr($isa_str126, 0, 3) != "ISA") {
2171 // not the starting 126 characters
2172 csv_edihist_log("csv_x12_delimiters Error: isa_str126 does not begin with ISA");
2173 return FALSE;
2175 if (! strpos($isa_str126, $next_segid) ) {
2176 // not the starting 126 characters
2177 csv_edihist_log("csv_x12_delimiters Error: next_segment $next_segid not in isa_str126 ");
2178 return FALSE;
2180 // $ns_pos = strpos($isa_str126, $next_segid);
2181 // $elem_pos = strrpos (substr($isa_str126, 0, $ns_pos-1), $elem_delim);
2182 // $dstr = substr($isa_str126, $elem_pos, $ns_pos-1);
2184 $ret_ar = array();
2185 $delim_ct = 0;
2186 $seg2_len = strlen($next_segid); // usually "GS" but could be "TA1" or whatever the specification says
2187 $rep_d = ""; // repetition delimiter ISA11 5010A1 - per Availity EDI guide
2189 $elem_delim = substr($isa_str126, 3, 1); // ISA*
2190 $s = '';
2191 $chars = strlen($isa_str126);
2193 for ($i = 0; $i < $chars; $i++) {
2194 if (strlen($s) >= 3 && substr($s, -$seg2_len) == $next_segid) { break; }
2195 $c = substr($isa_str126, $i, 1);
2196 if ($c == $elem_delim) {
2197 if ($delim_ct == 11) { $rep_d = substr($s, 1, 1); }
2198 $s = $elem_delim;
2199 $delim_ct++;
2200 } else {
2201 $s .= $c;
2203 // there are 16 elements in ISA segment
2204 if ($delim_ct > 16) {
2205 // incorrect $next_segid argument
2206 csv_edihist_log("csv_x12_delimiters: incorrect next_segment $next_segid does not follow ISA");
2207 return FALSE;
2210 if (strlen($s) >= 5) {
2211 $ret_ar["t"] = $s[2];
2212 $ret_ar["e"] = $s[0];
2213 $ret_ar["s"] = $s[1];
2214 $ret_ar["r"] = $rep_d;
2215 } else {
2216 csv_edihist_log("csv_x12_delimiters: Invalid delimiters $s");
2218 return $ret_ar;
2222 * from php help: tleffler [AT] gmail [DOT] com 12-May-2011 08:55
2224 * Not used, but possibly interesting. Thought is to pass a file handle as an argument
2225 * @param mixed $possibleResource
2226 * @return bool
2228 function isResource ($possibleResource) { return !is_null(@get_resource_type($possibleResource)); }
2231 * Parse x12 file into array of segments.
2233 * This function relies on csv_x12_delimiters() to get the delimiters array
2234 * and on csv_verify_file() to supply the next_segment value.
2235 * There are some basic verifications and failures are noted in the log.
2236 * <pre>
2237 * 'path'=>pathtofile
2238 * 'delimiters'=>array from x12_delimiters()
2239 * 'segments'=>segments[i]=>segment text
2240 * if $seg_array is true then segments[i]=>array of elements
2241 * </pre>
2243 * @uses csv_x12_delimiters()
2244 * @uses csv_verify_file()
2245 * @param string $file_path the full path for the file
2246 * @param string $type one of batch|837|era|835|997|999|277|271
2247 * @param bool $seg_array whether each segment should be made into an array of elements
2248 * @return array|bool array['delimiters']['segments']['path'], or false on error
2250 function csv_x12_segments($file_path, $type, $seg_array = FALSE) {
2251 // do verifications
2252 $fp_ar = csv_verify_file($file_path, $type, TRUE );
2254 if (!$fp_ar || !$fp_ar[0]) {
2255 csv_edihist_log ("csv_x12_segments: verification failed for $file_path");
2256 return FALSE;
2259 if ($fp_ar) {
2261 $fp =$fp_ar[0];
2262 $next_segment = $fp_ar[1];
2263 $f_str = file_get_contents($fp);
2265 // verify $delimiters
2266 $delimiters = csv_x12_delimiters(substr($f_str,0,126), $next_segment);
2267 $dlm_ok = FALSE;
2268 // here, expect $delimiters['t'] ['e'] ['s'] ['r']
2269 if (is_array($delimiters) && array_keys($delimiters) == array('t', 'e', 's', 'r') ) {
2270 $dlm_ok = TRUE;
2271 $seg_d = $delimiters['t'];
2272 $elem_d = $delimiters['e'];
2273 $subelem_d = $delimiters['s'];
2274 $rep_d = $delimiters['r'];
2275 } else {
2276 csv_edihist_log ("csv_x12_segments: invalid delimiters or delimiters wrong for $fp");
2277 return FALSE;
2280 // OK, now initialize variables
2281 $fn = basename($fp);
2282 $ar_seg = array();
2283 $ar_seg['path'] = $fp;
2284 $ar_seg['delimiters'] = $delimiters;
2285 $ar_seg['segments'] = array();
2286 $seg_pos = 0; // position where segment begins
2287 $st_segs_ct = 0; // segments in ST-SE envelope
2288 $se_seg_ct = ""; // segment count from SE segment
2289 $isa_segs_ct = 0; // segments in ISA envelope
2290 $isa_ct = 0; // ISA envelope count
2291 $iea_ct = 0; // IEA count
2292 $trnset_seg_ct = 0; // segments by sum of isa segment count
2294 $isa_str = "ISA".$elem_d; // to reduce evaluations
2295 $iea_str = "IEA".$elem_d;
2296 $st_str = "ST".$elem_d;
2297 $se_str = "SE".$elem_d;
2299 $idx = -1;
2300 $moresegs = true;
2301 while ($moresegs) {
2302 $idx++;
2303 // extract each segment from the file text
2304 $seg_end = strpos($f_str, $seg_d, $seg_pos);
2305 $moresegs = strpos($f_str, $seg_d, $seg_end+1);
2307 $seg_text = substr($f_str, $seg_pos, $seg_end-$seg_pos);
2308 $seg_pos = $seg_end + 1;
2310 // we trim in case there are line or carriage returns
2311 $seg_text = trim($seg_text);
2313 // check for non ASCII basic characters. Note reg_ex '/[^\x20-\xFF]/' allows extended ASCII characters
2314 // this is partly file syntax and partly protection -- we don't want to process an imposter
2315 // We are mostly concerned with \x00 to \x19, the "control" characters, but apparently some are allowed
2317 if (preg_match_all('/[^\x20-\x7E]/', $seg_text, $matches)) {
2318 csv_edihist_log ("csv_x12_segments: Non-basic ASCII character in segment ($idx) in file $fn");
2319 // quit here? return false; -- actually files have probably been scanned before in upload.php
2320 // also x12 files have more allowed characters than these
2322 if ($seg_array) {
2323 $ar_seg['segments'][] = explode($elem_d, $seg_text);
2324 } else {
2325 $ar_seg['segments'][] = $seg_text;
2327 $st_segs_ct++;
2328 $isa_segs_ct++;
2330 // some checks, if wanted
2331 if (substr($seg_text, 0, 4) == $isa_str) {
2332 $isa_seg = explode($elem_d, $seg_text);
2333 $isa_id = $isa_seg[13];
2334 $isa_segs_ct = 1;
2335 $isa_ct++;
2336 continue;
2339 if (substr($seg_text, 0, 3) == $st_str) {
2340 // $e = strpos($seg_text, $elem_d, 8); // ST*835* is 7 characters
2341 // $st02 = substr($seg_text, 7, $e-7);
2342 $st_ar = explode($elem_d, $seg_text);
2343 $st_num = $st_ar[2];
2344 $st_segs_ct = 1;
2345 continue;
2347 if (substr($seg_text, 0, 3) == $se_str) {
2348 $se_ar = explode($elem_d, $seg_text);
2349 $se_seg_ct = $se_ar[1];
2350 $se_num = $se_ar[2];
2351 if ($se_num != $st_num) {
2352 csv_edihist_log ("csv_x12_segments: ST-SE number mismatch $st_num $se_num in $fn");
2354 if (intval($se_seg_ct) != $st_segs_ct) {
2355 csv_edihist_log ("csv_x12_segments: ST-SE segment count mismatch $st_segs_ct $se_seg_ct in $fn");
2357 continue;
2359 if (substr($seg_text, 0, 4) == $iea_str) {
2360 $iea_seg = explode($elem_d, $seg_text);
2361 $iea_id = $iea_seg[2];
2362 $iea_ct++;
2364 if ($isa_id != $iea_id) {
2365 csv_edihist_log ("csv_x12_segments: ISA-IEA identifier mismatch set $iea_ct in $fn");
2367 if ($iea_ct == $isa_ct) {
2368 $trnset_seg_ct += $isa_segs_ct;
2369 if ($idx+1 != $trnset_seg_ct ) { //
2370 csv_edihist_log ("csv_x12_segments: IEA segment count error ({idx+1}:$trnset_seg_ct set) $iea_ct in $fn");
2372 } else {
2373 csv_edihist_log ("csv_x12_segments: ISA-IEA count mismatch set $isa_ct $iea_ct in $fn");
2379 return $ar_seg;