/** * Process new batch files for csv data and html output * * The html output is only a file summary, no claim detail is created * * * @uses csv_verify_file() * @uses ibr_batch_csv_data() * @uses csv_write_record() * @uses csv_newfile_list() * @param array $file_array optional default is NULL * @param boolean $html_out whether to generate html files summary table * @return string html output only for files table */ function ibr_batch_process_new($file_array = NULL, $html_out = TRUE) { // // 'mtime', 'dirname', 'fname', 'trace' 'payer' 'claims' $html_str = ""; $chars1 = 0; $chars2 = 0; //$chars2 = 0; $idx = 0; $ar_batchf = array(); //$need_dir = TRUE; // get the list of new files if ($file_array === NULL || !is_array($file_array) || count($file_array) == 0) { $ar_newfiles = csv_newfile_list("batch"); } else { $ar_newfiles = $file_array; //$need_dir = FALSE; } // if (count($ar_newfiles) == 0) { if ($html_out) { $html_str .= "<p>ibr_batch_process_new: no new batch files <br />"; return $html_str; } else { return false; } } else { $btfcount = count($ar_newfiles); } // we have some new ones // verify and get complete path foreach ($ar_newfiles as $fbt) { $fp = csv_verify_file($fbt, 'batch', false); if ($fp) { $ar_batchf[] = $fp; } } $p = csv_parameters("batch"); $b_dir = $p['directory']; // if ($html_out) { $html_str .= "<table cols=5 class=\"batch\">\n\t\t <caption>Batch Files CSV</caption>\n\t\t <thead>\n\t\t\t<tr>\n\t\t\t <th>File Time</th><th>File Name</th>\n\t\t\t <th>Control</th><th>Claims</th><th>x12_Partner</th>\n\t\t\t</tr>\n\t\t </thead>\n\t\t <tbody>"; } foreach ($ar_batchf as $f_batch) { // // increment counter for alternating html backgrounds $bgc = $idx % 2 == 1 ? 'odd' : 'even'; $idx++; // //$full_path = ($need_dir) ? $b_dir.DIRECTORY_SEPARATOR.$f_batch : $f_batch; // get the file data for csv output //$ar_csv_data = ibr_batch_csv_data($full_path); $ar_csv_data = ibr_batch_csv_data($f_batch); // // write to the files_batch csv record $chars1 += csv_write_record($ar_csv_data['file'], 'batch', 'file'); $chars2 += csv_write_record($ar_csv_data['claim'], 'batch', 'claim'); // // link for viewing file <a href=\"edi_view_file.php?\'fvkey\'=$dta\" target=\"_blank\">$dta</a></td>"; if ($html_out) { $html_str .= "<tr class=\"{$bgc}\">\n <td>{$ar_csv_data['file'][0]}</td>\n <td><a target='_blank' href='edi_history_main.php?fvkey={$ar_csv_data['file'][1]}'>{$ar_csv_data['file'][1]}</a></td>\n <td>{$ar_csv_data['file'][2]}</td>\n <td>{$ar_csv_data['file'][3]}</td>\n <td>{$ar_csv_data['file'][4]}</td>\n </tr>"; } } if ($html_out) { $html_str .= "</tbody>\n\t\t </table>\n\t\t <p></p>"; } // csv_edihist_log("ibr_batch_process_new: {$chars1} characters written to files_batch.csv"); csv_edihist_log("ibr_batch_process_new: {$chars2} characters written to claims_batch.csv"); // if ($html_out) { return $html_str; } else { return "<p>Batch files: processed {$btfcount} files </p>"; } }
/** * Parse x12 file into array of segments. * * This function relies on csv_x12_delimiters() to get the delimiters array * and on csv_verify_file() to supply the next_segment value. * There are some basic verifications and failures are noted in the log. * <pre> * 'path'=>pathtofile * 'delimiters'=>array from x12_delimiters() * 'segments'=>segments[i]=>segment text * if $seg_array is true then segments[i]=>array of elements * </pre> * * @uses csv_x12_delimiters() * @uses csv_verify_file() * @param string $file_path the full path for the file * @param string $type one of batch|837|era|835|997|999|277|271 * @param bool $seg_array whether each segment should be made into an array of elements * @return array|bool array['delimiters']['segments']['path'], or false on error */ function csv_x12_segments($file_path, $type, $seg_array = FALSE) { // do verifications $fp_ar = csv_verify_file($file_path, $type, TRUE); // if (!$fp_ar || !$fp_ar[0]) { csv_edihist_log("csv_x12_segments: verification failed for {$file_path}"); return FALSE; } // if ($fp_ar) { // $fp = $fp_ar[0]; $next_segment = $fp_ar[1]; $f_str = file_get_contents($fp); // // verify $delimiters $delimiters = csv_x12_delimiters(substr($f_str, 0, 126), $next_segment); $dlm_ok = FALSE; // here, expect $delimiters['t'] ['e'] ['s'] ['r'] if (is_array($delimiters) && array_keys($delimiters) == array('t', 'e', 's', 'r')) { $dlm_ok = TRUE; $seg_d = $delimiters['t']; $elem_d = $delimiters['e']; $subelem_d = $delimiters['s']; $rep_d = $delimiters['r']; } else { csv_edihist_log("csv_x12_segments: invalid delimiters or delimiters wrong for {$fp}"); return FALSE; } } // OK, now initialize variables $fn = basename($fp); $ar_seg = array(); $ar_seg['path'] = $fp; $ar_seg['delimiters'] = $delimiters; $ar_seg['segments'] = array(); $seg_pos = 0; // position where segment begins $st_segs_ct = 0; // segments in ST-SE envelope $se_seg_ct = ""; // segment count from SE segment $isa_segs_ct = 0; // segments in ISA envelope $isa_ct = 0; // ISA envelope count $iea_ct = 0; // IEA count $trnset_seg_ct = 0; // segments by sum of isa segment count // $isa_str = "ISA" . $elem_d; // to reduce evaluations $iea_str = "IEA" . $elem_d; $st_str = "ST" . $elem_d; $se_str = "SE" . $elem_d; // $idx = -1; $moresegs = true; while ($moresegs) { $idx++; // extract each segment from the file text $seg_end = strpos($f_str, $seg_d, $seg_pos); $moresegs = strpos($f_str, $seg_d, $seg_end + 1); // $seg_text = substr($f_str, $seg_pos, $seg_end - $seg_pos); $seg_pos = $seg_end + 1; // // we trim in case there are line or carriage returns $seg_text = trim($seg_text); // // check for non ASCII basic characters. Note reg_ex '/[^\x20-\xFF]/' allows extended ASCII characters // this is partly file syntax and partly protection -- we don't want to process an imposter // We are mostly concerned with \x00 to \x19, the "control" characters, but apparently some are allowed // if (preg_match_all('/[^\\x20-\\x7E]/', $seg_text, $matches)) { csv_edihist_log("csv_x12_segments: Non-basic ASCII character in segment ({$idx}) in file {$fn}"); // quit here? return false; -- actually files have probably been scanned before in upload.php // also x12 files have more allowed characters than these } if ($seg_array) { $ar_seg['segments'][] = explode($elem_d, $seg_text); } else { $ar_seg['segments'][] = $seg_text; } $st_segs_ct++; $isa_segs_ct++; // // some checks, if wanted if (substr($seg_text, 0, 4) == $isa_str) { $isa_seg = explode($elem_d, $seg_text); $isa_id = $isa_seg[13]; $isa_segs_ct = 1; $isa_ct++; continue; } // if (substr($seg_text, 0, 3) == $st_str) { // $e = strpos($seg_text, $elem_d, 8); // ST*835* is 7 characters // $st02 = substr($seg_text, 7, $e-7); $st_ar = explode($elem_d, $seg_text); $st_num = $st_ar[2]; $st_segs_ct = 1; continue; } if (substr($seg_text, 0, 3) == $se_str) { $se_ar = explode($elem_d, $seg_text); $se_seg_ct = $se_ar[1]; $se_num = $se_ar[2]; if ($se_num != $st_num) { csv_edihist_log("csv_x12_segments: ST-SE number mismatch {$st_num} {$se_num} in {$fn}"); } if (intval($se_seg_ct) != $st_segs_ct) { csv_edihist_log("csv_x12_segments: ST-SE segment count mismatch {$st_segs_ct} {$se_seg_ct} in {$fn}"); } continue; } if (substr($seg_text, 0, 4) == $iea_str) { $iea_seg = explode($elem_d, $seg_text); $iea_id = $iea_seg[2]; $iea_ct++; // if ($isa_id != $iea_id) { csv_edihist_log("csv_x12_segments: ISA-IEA identifier mismatch set {$iea_ct} in {$fn}"); } if ($iea_ct == $isa_ct) { $trnset_seg_ct += $isa_segs_ct; if ($idx + 1 != $trnset_seg_ct) { // csv_edihist_log("csv_x12_segments: IEA segment count error ({idx+1}:{$trnset_seg_ct} set) {$iea_ct} in {$fn}"); } } else { csv_edihist_log("csv_x12_segments: ISA-IEA count mismatch set {$isa_ct} {$iea_ct} in {$fn}"); } } // } // return $ar_seg; }
/** * copy the substring of text containing claim payment information for a claim * * The filename must be found in the files.csv table, i.e. previously processed * * @uses csv_verify_file() * @uses csv_x12_delimiters() * @param string $clp_clm_num the pid-encounter value * @param string $era_file the filename * @return string newline characters are added to each segment end */ function ibr_era_get_clp_text($clp_clm_num, $era_file, $html_out = true) { // @param string $clp_clm_num -- CLP01 value, pid-encounter // @param string $era_file path to 835 file containing the $clp_clm_num // segment block CLP to CLP, SE, LX // get the substring of the era file containing the ST number // $fp = csv_verify_file($era_file, "era"); if (!$fp) { csv_edihist_log("ibr_era_get_clp_text: failed to read {$era_file}"); $str_clp .= "failed to read {$era_file}"; return $str_clp; } else { // $bstr = file_get_contents($fp); if (!$bstr) { csv_edihist_log("ibr_era_get_clp_text: failed to get contents of {$era_file}"); $str_clp .= "failed to read {$era_file}"; return $str_clp; } // get the delimiters $str_isa = substr($bstr, 0, 126); // $ar_delim = csv_x12_delimiters($str_isa, "GS"); $seg_delim = $ar_delim['t']; // $seg_clp = "CLP*" . $clp_clm_num; // CLP segment that begins remittance detail // $clp_pos = strpos($bstr, $seg_clp, 0); // break it off if $st_pos is not found if ($clp_pos == FALSE) { csv_edihist_log("Error: {$clp_clm_num} not found in {$era_file}"); $str_clp .= "Error: {$clp_clm_num} not found in {$era_file}"; return $str_clp; } // $seg_se = "SE*"; $seg_lx = "LX*"; $seg_clpx = "CLP*"; // see if we can find a closing segment $pos_ar[] = strpos($bstr, $seg_se, $clp_pos); // $se_pos = $pos_ar[] = strpos($bstr, $seg_lx, $clp_pos); // $lx_pos = $pos_ar[] = strpos($bstr, $seg_clpx, $clp_pos + 10); //$clpx_pos = // // choose the best closing position, closest to $clp_pos asort($pos_ar); foreach ($pos_ar as $p) { // echo "clp_pos $clp_pos pos_ar $p". PHP_EOL; if ($p > $clp_pos) { $end_pos = $p; break; } } // $str_clp = substr($bstr, $clp_pos, $end_pos - $clp_pos); // // add newlines so each segment is on its own line if (strpos($str_clp, $seg_delim . PHP_EOL)) { // if true, assume the file has newlines ending segments } else { // we could get fancy and make an html table or add line numbers $str_clp = str_replace($seg_delim, $seg_delim . PHP_EOL, $str_clp); } } // if ($html_out) { $str_html = "<div class=\"filetext\">"; $str_html .= "<p>{$pe} " . basename($fp) . " </p>" . PHP_EOL . "<pre><code>"; $str_html .= $str_clp; $str_html .= PHP_EOL . "</code></pre>" . PHP_EOL . "</div>" . PHP_EOL; return $str_html; } else { return $str_clp; } }
/** * Create html output for a 277 file, same as the process new output * * @uses csv_verify_file() * @uses csv_x12_segments() * @uses ibr_277_parse() * @uses ibr_277_csv_claim_data() * @uses ibr_277_html() * @param string $filepath -- filename or full path to file * @return string */ function ibr_277_filetohtml($filepath, $err_only = false) { // // simply create an html output for the file like processing new // $html_str = ""; // $fp = csv_verify_file($filepath, "f277"); if ($fp) { $ar_277_seg = csv_x12_segments($fp, "f277", false); if (is_array($ar_277_seg) && count($ar_277_seg['segments'])) { $ar_277_vals = ibr_277_parse($ar_277_segs); } else { $html_str .= "failed to get segments for {$fp} <br />" . PHP_EOL; continue; } $idx = 0; foreach ($ar_277_vals as $isa) { $ar_csvf = $isa['file']; $ar_csvc = ibr_277_csv_claim_data($isa['claim']); // $ar_h[$idx]['file'] = $ar_csvf; $ar_h[$idx]['claim'] = $ar_csvc; $idx++; } $html_str .= ibr_277_html($ar_h, $err_only); } else { csv_edihist_log("ibr_277_filetohtml: verification failed {$filepath}"); $html_str .= "ibr_277_filetohtml: Error, validation failed {$filepath} <br />"; } // return $html_str; }
/** * process new 997/999 files * * @uses csv_newfile_list() * @uses csv_verify_file() * @uses csv_parameters() * @param array $file_array -- optional, this array is sent from the ibr_io.php script * @param bool $html_out -- whether to produce and return html output * @param bool $err_only -- whether to generate claim information only for errors (ignored) * @return string */ function ibr_997_process_new($file_array = NULL, $html_out = TRUE, $err_only = TRUE) { // $ret_str = ""; $chr_ct1 = 0; $chr_ct2 = 0; $new_997 = array(); $need_dir = TRUE; // if (is_null($file_array) || empty($file_array)) { // directory will need to be prepended to name $new_997 = csv_newfile_list('f997'); } elseif (is_array($file_array) && count($file_array)) { // files that are not verified will just be ignored foreach ($file_array as $finp) { $fp = csv_verify_file($finp, 'f997'); if ($fp) { $new_997[] = $fp; } } $need_dir = FALSE; } // if (count($new_997) == 0) { $ret_str = "<p>ibr_997_process_new: no new 997/999 files. </p>"; return $ret_str; } else { $f997count = count($new_997); } // $ar_htm = array(); // $params = csv_parameters("f997"); $tdir = dirname(__FILE__) . $params['directory']; // // get batch files parameters, we need the batch_dir $bp = csv_parameters("batch"); $batch_dir = dirname(__FILE__) . $bp['directory']; // foreach ($new_997 as $f997) { // make the file path $fpath = $need_dir ? $tdir . DIRECTORY_SEPARATOR . $f997 : $f997; // get file m-time //$ftime = date('Ymd:His', filemtime($fpath)); // read file into string $f_str = file_get_contents($fpath); if ($f_str) { // transform file contents into segment arrays $ar_seg = csv_x12_segments($fpath, "f997", FALSE); // if (!$ar_seg) { // file was rejected csv_edihist_log("ibr_997_process_new: failed to get segments for {$fpath}"); $ret_str .= "ibr_997_process_new: failed to get segments for {$fpath}</p>" . PHP_EOL; continue; } // parse arrays into data arrays $ar_data = ibr_997_parse($ar_seg); // $ar_data = array("file" => $ar_997file, "rejects" => $ar_reject); // $csv_claims = ibr_997_data_csv($ar_data['file'], 'file'); if ($csv_claims) { $chr_ct1 += csv_write_record($csv_claims, "f997", "file"); } else { csv_edihist_log("ibr_997_process_new: error with files csv array"); } // if (isset($ar_data['claims']) && count($ar_data['claims'])) { // only add to claims_997.csv if there are rejected claims $csv_claims = ibr_997_data_csv($ar_data['claims'], 'claim'); if ($csv_claims) { $chr_ct2 += csv_write_record($csv_claims, "f997", "claim"); } else { csv_edihist_log("ibr_997_process_new: error with claims csv array"); } // } // // save all the ar_datas for html output if ($html_out) { $ar_htm[] = $ar_data; } // } else { $ret_str .= "<p>ibr_997_process_new: failed to read {$fpath} </p>" . PHP_EOL; } } // csv_edihist_log("ibr_997_process_new: {$chr_ct1} characters written to files_997.csv"); csv_edihist_log("ibr_997_process_new: {$chr_ct2} characters written to claims_997.csv"); // if ($html_out) { // generate html output and return that $ret_str .= ibr_997_file_data_html($ar_htm, $err_only); } else { $ret_str .= "x12_999 files: processed {$f997count} x12-999 files <br />"; } // return $ret_str; }
/** * process new .ta1 files * * * @uses ibr_process_ta1() * @uses ibr_ack_html() * @uses csv_newfile_list() * @uses csv_verify_file() * @param array|string -- optional array of filenames or filename * @param bool --whether to produce html table * @return string */ function ibr_ta1_process_new($ta1_files, $html_out = TRUE) { // $str_html = ""; // if (is_array($ta1_files)) { $new_files = $ta1_files; } elseif (is_string($ta1_files) && strlen($ta1_files)) { $new_files[] = $ta1_files; } else { $new_files = csv_newfile_list("ta1"); } // if (is_array($new_files) && count($new_files) > 0) { $fcount = count($new_files); foreach ($new_files as $ta) { $fp = csv_verify_file($ta, 'ta1'); if ($fp) { $ar_d[] = ibr_process_ta1($fp); } else { $str_html .= "TA1 failed to verify file: {$ta} <br />" . PHP_EOL; csv_edihist_log("ibr_ta1_process_new: failed to verify file: {$ta}"); } } } // write a line to csv file -- put in file_997.csv if (is_array($ar_d) && count($ar_d)) { foreach ($ar_d as $arec) { // do not put message, batch, or text file in csv record $ar_csv[] = array_slice($arec, 0, count($arec) - 3); } $rslt = csv_write_record($ar_csv, "ta1", "file"); csv_edihist_log("ibr_ta1_process_new: {$rslt} characters written to files_997.csv"); if ($html_out) { $str_html .= ibr_ack_html($ar_d); } else { $str_html .= "TA1 files: processed {$fcount} TA1 files <br />" . PHP_EOL; } } else { $str_html .= "No new TA1 files found. <br />" . PHP_EOL; } // return $str_html; }
/** * generate output as if file is being processed * * @param string filename * @param bool display errors only * @return string */ function ibr_ebr_filetohtml($filepath, $err_only = false) { // simply create an html output for the file $html_str = ""; $data_ar = array(); $fp = false; $ext = substr($filepath, -3); if (strpos('|ibr|ebr', $ext)) { $fp = csv_verify_file($filepath, "ebr"); } if ($fp) { if ($ext == 'ebr') { $data_ar = ibr_ebr_values($fp); } elseif ($ext == 'ibr') { $data_ar = ibr_ibr_values($fp); } else { csv_edihist_log("ibr_ebr_filetohtml: invalid extension {$ext} " . basename($fp)); return "<p>invalid extension {$ext} </p>" . PHP_EOL; } // $html_str .= ibr_ebr_html($data_ar, $err_only); } else { csv_edihist_log("ibr_ebr_filetohtml: verification failed {$filepath}"); $html_str .= "Error, validation failed {$filepath} <br />"; } // return $html_str; }
/** * Categorize and check uploaded files * * Files are typed and scanned, if they pass scan, then the file is moved * to the IBR_UPLOADS_DIR and an array('type', 'name') is returned * If an error occurs, FALSE is returned * * @uses csv_verify_file() * @param array $param_ar -- the csv_parameters("ALL") array (so we don't have to keep creating it) * @param array $fidx -- individual file array from $files[$i] array * @param string &$html_str -- the html output that is passed around * @return array|bool */ function ibr_upload_match_file($param_ar, $fidx, &$html_str) { // if (is_array($fidx) && isset($fidx['name'])) { $fn = basename($fidx['name']); $ftmp = $fidx['tmp_name']; } else { $html_str .= "Error: invalid file argument <br />" . PHP_EOL; return FALSE; } // if (is_array($param_ar) && count($param_ar)) { $p = $param_ar; // csv_parameters("ALL"); } else { $html_str .= "Error: invalid parameters <br />" . PHP_EOL; return FALSE; } // $ibr_upldir = csv_edih_tmpdir(); // $ar_fn = array(); // foreach ($p as $ky => $par) { // if (!$p[$ky]['regex']) { continue; } if (!preg_match($p[$ky]['regex'], $fn)) { continue; } // file name has matched an allowed extension; now, we scan the file //$is_tp = TRUE; // $fstr = file_get_contents($ftmp); if (!$fstr) { $html_str .= "Error: could not read {$fn} <br />" . PHP_EOL; return FALSE; } // check for Non-basic ASCII character and <%, <asp:, <?, ${, #!, <?, <scr (any other evil script indicators?) // basically allows A-Z a-z 0-9 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and newline carriage_return // this may need to be adapted to the allowed x12 characters, but <%, <asp:, <?, ${, and #! should never occur // any other bad stuff indicators should be added // //$fltr = filter_var ( $fstr, FILTER_SANITIZE_STRING ); FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH, if (preg_match('/[^\\x20-\\x7E\\x0A\\x0D]|(<\\?)|(<%)|(<asp)|(<ASP)|(#!)|(\\$\\{)|(<scr)|(<SCR)/', $fstr, $matches, PREG_OFFSET_CAPTURE)) { // $html_str .= "Filtered character in file {$fn} -- not accepted <br />" . PHP_EOL; $html_str .= " character: " . $matches[0][0] . " position: " . $matches[0][1] . "<br />" . PHP_EOL; // return FALSE; } else { // file matches extension and contains only allowed characters $newname = $ibr_upldir . DIRECTORY_SEPARATOR . $fn; // $is_mv = rename($ftmp, $newname); if (!$is_mv) { // moving file failed, check permissions for directory, since this may be a problem at start if (!is_writable($ibr_upldir)) { $html_str .= "write permission denied for {$ibr_upldir}<br />" . PHP_EOL; } $html_str .= "Error: unable to move {$fn} to uploads directory<br />" . PHP_EOL; return FALSE; } else { // file was moved, now set permissions to be -rw------- $iscm = chmod($newname, 0600); if (!$iscm) { $html_str .= "Error: failed to set permissions for {$fa['name']} -- trying to remove<br />" . PHP_EOL; unlink($newname); return FALSE; } } if ($is_mv && $iscm) { // verify file by checking for expected parts -- not extensive // function csv_verify_file() is in csv_record_include.php if (csv_verify_file($newname, $ky)) { // $ar_fn['type'] = (string) $ky; $ar_fn['name'] = $newname; } else { // //$html_str .= "verify failed for $newname <br />" .PHP_EOL; return FALSE; } } } // quit the extension check loop break; } return $ar_fn; }