3 * ibr_ack_read.php, read and process ack files
5 * Copyright 2012 Kevin McCormick
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 3 or later. You should have
15 * received a copy of the GNU General Public License along with this program;
16 * if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * <http://opensource.org/licenses/gpl-license.php>
20 * @author Kevin McCormick
21 * @link: http://www.open-emr.org
23 * @subpackage ediHistory
27 // a security measure to prevent direct web access to this file
28 // must be accessed through the main calling script ibr_history.php
29 // from admin at rune-city dot com; found in php manual
30 // if (!defined('SITE_IN')) die('Direct access not allowed!');
34 * get error code test for TA1 error
36 * @param string $code - the error code
37 * @return string error test
39 function ibr_ta1_code($code) {
41 'A' => 'Interchange accepted with no errors.',
42 'R' => 'Interchange rejected because of errors. Sender must resubmit file.',
43 'E' => 'Interchange accepted, but errors are noted. Sender must not resubmit file.',
45 '001' => 'The Interchange Control Number in the header and trailer do not match.',
46 '002' => 'This Standard as noted in the Control Standards Identifier is not supported.',
47 '003' => 'This Version of the controls is not supported',
48 '004' => 'The Segment Terminator is invalid',
49 '005' => 'Invalid Interchange ID Qualifier for sender',
50 '006' => 'Invalid Interchange Sender ID',
51 '007' => 'Invalid Interchange ID Qualifier for receiver',
52 '008' => 'Invalid Interchange Receiver ID',
53 '009' => 'Unknown Interchange Receiver ID',
54 '010' => 'Invalid Authorization Information Qualifier value',
55 '011' => 'Invalid Authorization Information value',
56 '012' => 'Invalid Security Information Qualifier value',
57 '013' => 'Invalid Security Information value',
58 '014' => 'Invalid Interchange Date value',
59 '015' => 'Invalid Interchange Time value',
60 '016' => 'Invalid Interchange Standards Identifier value',
61 '017' => 'Invalid Interchange Version ID value',
62 '018' => 'Invalid Interchange Control Number',
63 '019' => 'Invalid Acknowledgment Requested value',
64 '020' => 'Invalid Test Indicator value',
65 '021' => 'Invalid Number of Included Group value',
66 '022' => 'Invalid control structure',
67 '023' => 'Improper (Premature) end-of-file (Transmission)',
68 '024' => 'Invalid Interchange Content (e.g., invalid GS Segment)',
69 '025' => 'Duplicate Interchange Control Number',
70 '026' => 'Invalid Data Element Separator',
71 '027' => 'Invalid Component Element Separator',
72 '028' => 'Invalid delivery date in Deferred Delivery Request',
73 '029' => 'Invalid delivery time in Deferred Delivery Request',
74 '030' => 'Invalid delivery time Code in Deferred Delivery Request',
75 '031' => 'Invalid grade of Service Code'
77 if (array_key_exists($code, $ar_ta1code)) {
78 $ta1_msg = $ar_ta1code[$code];
80 $ta1_msg = "Code $code not found";
86 function ibr_ack_error($filename, $errcode) {
89 $ext = strtolower(substr($filename, -3));
91 if (!$errcode) { $errcode = '1E'; }
92 $fp = csv_verify_file($filename, $ext);
94 $fh = fopen($fp, 'r');
96 while (($buffer = fgets($fh, 4096)) !== false) {
97 if (substr($buffer, 0, 2) == $errcode) {
98 $errval = substr($buffer, 3) . "</p>".PHP_EOL
;
103 $errval .= "Failed to read file $filename <p>".PHP_EOL
;
106 $errval .= "Verification failed for file $filename <p>".PHP_EOL
;
107 csv_edihist_log ( "ibr_ack_error Error: failed to read " . $filename);
109 } elseif ($ext == 'ta1') {
110 $seg_ar = csv_x12_segments($filename, 'ta1', false);
111 if (is_array($seg_ar) && isset($seg_ar['segments']) ) {
112 $elem_d = $seg_ar['delimiters']['e'];
113 foreach($seg_ar['segments'] as $segstr) {
114 $seg = explode($elem_d, $segstr);
115 if ($seg[0] =='TA1') {
116 $ack_code = isset($seg[5]) ?
strval($seg[5]) : '';
117 if (strlen($errcode) && $errcode != $ack_code) {
118 $errval .= "Given code: $errcode <br />".PHP_EOL
;
119 $errval .= "Message: " . ibr_ta1_code($errcode) . "</p>".PHP_EOL
;
120 $errval .= "Found Code: <br />".PHP_EOL
;
122 $errstr = ($ack_code) ?
$seg[5].' '. ibr_ta1_code($seg[5]) : '';
123 $errval .= "$errstr</p>".PHP_EOL
;
128 $errval = "Failed to read file $filename <br />".PHP_EOL
;
129 csv_edihist_log ( "ibr_ack_error Error: failed to read " . $filename);
138 * to parse ACK files -- usually a batch reject due to errors in ISA segment
140 * @uses ibr_batch_find_file_with_controlnum()
141 * @param string $filepath -- full path to .ack file
144 function ibr_process_ack($filepath) {
145 // to parse ACK files -- usually a batch reject due to errors in ISA segment
147 if (is_readable($filepath)) {
148 // string setlocale ( int $category , array $locale ) // may be needed
149 $path_parts = pathinfo($filepath);
150 $ack_dir = $path_parts['dirname'];
151 $ack_fname = $path_parts['basename'];
152 $ack_ext = $path_parts['extension'];
153 $ack_mtime = date ("Ymd", filemtime($filepath));
154 $act_txt = str_replace ( ".ACK", ".ACT", $ack_fname);
156 // error, unable to read file
157 csv_edihist_log ("ibr_process_ack: Error, unable to read file $filepath");
161 $fh = fopen($filepath, 'r');
163 while (($buffer = fgets($fh, 4096)) !== false) {
164 $ar_ack[] = trim($buffer);
167 csv_edihist_log ( "ibr_process_ack Error: failed to read " . $file_path );
171 $ar_ackfile = array();
175 foreach($ar_ack as $strln) {
176 if (substr($strln, 0, 2) == '1'.IBR_DELIMITER
) {
177 $ln = explode(IBR_DELIMITER
, $strln);
178 // use the given date for the file date, otherwise filemtime
179 $dtstr = str_replace('-', '', $ln[1]);
180 if (strlen($dtstr) == 8 && substr($dtstr, 0, 4) == substr($ln[1], 0, 4) ) {
183 $ack_date = trim($ln[1]);
184 $ack_ip = trim($ln[2]);
185 $ack_isa13 = strval($ln[4]);
186 $ack_ctlnum = strval($ln[5]);
189 if (substr($strln, 0 ,2) == '1E') {
190 $ln = explode(IBR_DELIMITER
, $strln);
191 $ack_msg .= '1E: '.trim(substr($ln[1],0, 80)) . "... ";
196 if ( isset($ack_ctlnum) && strlen($ack_ctlnum) ) {
197 $batch_file = csv_file_by_controlnum('batch', $ack_ctlnum);
200 $ar_ackfile["ack_time"] = isset($ack_mtime) ?
trim($ack_mtime) : ""; //"date"
201 $ar_ackfile["ack_file"] = isset($ack_fname) ?
trim($ack_fname) : ""; //"filename"
202 $ar_ackfile["ack_isa13"] = isset($ack_isa13) ?
trim($ack_isa13) : ""; // file id?
203 $ar_ackfile["ack_ctrl"] = isset($ack_ctlnum) ?
trim($ack_ctlnum) : ""; //"batch icn "
204 $ar_ackfile["ack_code"] = isset($ack_code) ?
trim($ack_code) : ""; //"error code ? 1E "
205 $ar_ackfile["ack_msg"] = isset($ack_msg) ?
trim($ack_msg) : "";
206 $ar_ackfile["ack_batch"] = ($batch_file) ?
trim($batch_file) : "batch not identified"; //"batch"
207 $ar_ackfile["ack_ftxt"] = ($ack_txt) ?
$ack_txt : ""; //"readable filename"
213 * process TA1 acknowledgment files
217 function ibr_process_ta1($filepath) {
219 $ar_ackfile = array();
221 $seg_ar = csv_x12_segments($filepath, 'ta1', false);
222 if (is_array($seg_ar) && isset($seg_ar['segments']) ) {
223 $ar_ta1_segments = $seg_ar['segments'];
224 $ack_mtime = date('Ymd', filemtime($seg_ar['path']));
225 $elem_d = $seg_ar['delimiters']['e'];
226 $sub_d = $seg_ar['delimiters']['s'];
227 $rep_d = $seg_ar['delimiters']['r'];
229 $ack_fname = basename($seg_ar['path']);
230 $ack_txt = str_replace('.TA1', '.TAT', $ack_fname);
232 // error getting segments
233 csv_edihist_log("ibr_process_ta1: error getting segments for $filepath");
240 foreach($ar_ta1_segments as $segstr) {
241 $seg = explode($elem_d, $segstr);
242 if ($seg[0] == 'ISA') {
243 $ack_mtime = $seg[9];
244 if (strlen($seg[9]) == 6) { $ack_mtime = '20' . $seg[9]; }
245 $ack_isa13 = strval($seg[13]);
247 if ($seg[0] == 'TA1') {
248 $ack_ctlnum = strval($seg[1]);
249 $ack_date = '20' . $seg[2]; // date is 6 digits, prepend century
250 $ack_status = $seg[4];
252 $ack_msg = ($ack_code) ?
$seg[5].": ". ibr_ta1_code($seg[5]) : '';
255 if ( isset($ack_ctlnum) && strlen($ack_ctlnum) ) {
256 $batch_file = csv_file_by_controlnum('batch', $ack_ctlnum);
259 $ar_ackfile["ack_time"] = isset($ack_mtime) ?
$ack_mtime : ""; //"date"
260 $ar_ackfile["ack_file"] = isset($ack_fname) ?
$ack_fname : ""; //"file"
261 $ar_ackfile["ack_isa13"] = isset($ack_isa13) ?
$ack_isa13 : ""; // $ta1 isa13
262 $ar_ackfile["ack_ctrl"] = isset($ack_ctlnum) ?
$ack_ctlnum : ""; //"batch_ctrl"
263 $ar_ackfile["ack_code"] = isset($ack_code) ?
$ack_code : ""; // error code
264 $ar_ackfile["ack_msg"] = isset($ack_msg) ?
$ack_msg : "";
265 $ar_ackfile["ack_batch"] = ($batch_file) ?
$batch_file : "batch not identified";
266 $ar_ackfile["ack_ftxt"] = ($ack_txt) ?
$ack_txt : "";
272 * creates an html table listing .ack files and rejected claims
274 * @param array -- the data array from ibr_process_ack()
275 * @return string -- tml table
277 function ibr_ack_html($ar_data) {
279 if (!count($ar_data) ) { return ""; }
283 // array('Date', 'FileName', 'isa13', 'ta1ctrl', 'code');
284 $str_html = "<table class=\"f997\" cols=5><caption>ACK/TA1 File Report</caption>
287 <th>Date</th><th>File Name</th><th>Batch ICN</th><th>Batch</th><th>Code</th>
291 // file information row ack_time ack_file ack_batch ack_msg
292 foreach ($ar_data as $ack) {
293 $bgf = ($idf %
2 == 1) ?
'odd' : 'even';
297 <tr class=\"{$bgf}\">
298 <td>{$ack['ack_time']}</td>
299 <td><a target='_blank' href='edi_history_main.php?fvkey={$ack['ack_file']}'>{$ack['ack_file']}</a> <a target='_blank' href='edi_history_main.php?fvkey={$ack['ack_ftxt']}&readable=yes'>R</a></td>
300 <td><a href='edi_history_main.php?btctln={$ack['ack_ctrl']}' target='_blank'>{$ack['ack_ctrl']}</a></td>
301 <td><a href='edi_history_main.php?fvkey={$ack['ack_batch']}' target='_blank'>{$ack['ack_batch']}</a></td>
302 <td>{$ack['ack_code']}</td>
305 <td span=4>{$ack['ack_msg']}</td>
309 $str_html .= "</tbody>".PHP_EOL
."</table>".PHP_EOL
;
314 * process new .ack files
316 * This is the main function in this script
318 * @uses ibr_process_ack()
319 * @uses ibr_ack_html()
320 * @uses csv_newfile_list()
321 * @uses csv_verify_file()
322 * @param array|string -- optional array of filenames or filename
323 * @param bool --whether to produce html table
326 function ibr_ack_process_new($ack_files, $html_out = TRUE ) {
330 if ( is_array($ack_files) ) {
331 $new_ack = $ack_files;
332 } elseif (is_string($ack_files) && strlen($ack_files) ) {
333 $new_ack[] = $ack_files;
335 $new_ack = csv_newfile_list("ack");
338 if ( is_array($new_ack) && count($new_ack) > 0 ) {
339 $fcount = count($new_ack);
340 foreach($new_ack as $ack) {
341 $fp = csv_verify_file($ack, 'ack');
343 $ar_d[] = ibr_process_ack($fp);
345 $str_html .= "ACK failed to verify file: $ack <br />" .PHP_EOL
;
346 csv_edihist_log("ibr_ack_process_new: failed to verify file: $ack");
351 // write a line to csv file -- put in file_997.csv
352 if ( is_array($ar_d) && count($ar_d) ) {
353 foreach($ar_d as $arec) {
354 // do not put message, batch file, or text file in csv record
355 $ar_csv[] = array_slice($arec, 0, count($arec)-3);
357 $rslt = csv_write_record($ar_csv, "ack", "file");
358 csv_edihist_log("ibr_ack_process_new: $rslt characters written to files_997.csv");
360 $str_html .= ibr_ack_html($ar_d);
362 $str_html .= "ACK files: processed $fcount ACK files <br />".PHP_EOL
;
365 $str_html .= "No new ACK files found. <br />" .PHP_EOL
;
372 * process new .ta1 files
375 * @uses ibr_process_ta1()
376 * @uses ibr_ack_html()
377 * @uses csv_newfile_list()
378 * @uses csv_verify_file()
379 * @param array|string -- optional array of filenames or filename
380 * @param bool --whether to produce html table
383 function ibr_ta1_process_new($ta1_files, $html_out = TRUE ) {
387 if ( is_array($ta1_files) ) {
388 $new_files = $ta1_files;
389 } elseif (is_string($ta1_files) && strlen($ta1_files)) {
390 $new_files[] = $ta1_files;
392 $new_files = csv_newfile_list("ta1");
395 if ( is_array($new_files) && count($new_files) > 0 ) {
396 $fcount = count($new_files);
397 foreach($new_files as $ta) {
398 $fp = csv_verify_file($ta, 'ta1');
400 $ar_d[] = ibr_process_ta1($fp);
402 $str_html .= "TA1 failed to verify file: $ta <br />" .PHP_EOL
;
403 csv_edihist_log("ibr_ta1_process_new: failed to verify file: $ta");
407 // write a line to csv file -- put in file_997.csv
408 if ( is_array($ar_d) && count($ar_d) ) {
409 foreach($ar_d as $arec) {
410 // do not put message, batch, or text file in csv record
411 $ar_csv[] = array_slice($arec, 0, count($arec)-3);
413 $rslt = csv_write_record($ar_csv, "ta1", "file");
414 csv_edihist_log("ibr_ta1_process_new: $rslt characters written to files_997.csv");
416 $str_html .= ibr_ack_html($ar_d);
418 $str_html .= "TA1 files: processed $fcount TA1 files <br />".PHP_EOL
;
421 $str_html .= "No new TA1 files found. <br />" .PHP_EOL
;