edihistory -- just keep current, minor edit
[openemr.git] / library / edihistory / ibr_ack_read.php
blob28eecbc3c07999ce24b292736977797998c9864c
1 <?PHP
2 /**
3 * ibr_ack_read.php, read and process ack files
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 // 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!');
33 /**
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) {
40 $ar_ta1code = array(
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.',
44 '000' => 'No error',
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];
79 } else {
80 $ta1_msg = "Code $code not found";
82 return $ta1_msg;
86 function ibr_ack_error($filename, $errcode) {
88 $errval = '<p>';
89 $ext = strtolower(substr($filename, -3));
90 if ($ext == 'ack') {
91 if (!$errcode) { $errcode = '1E'; }
92 $fp = csv_verify_file($filename, $ext);
93 if ($fp) {
94 $fh = fopen($fp, 'r');
95 if ($fh) {
96 while (($buffer = fgets($fh, 4096)) !== false) {
97 if (substr($buffer, 0, 2) == $errcode) {
98 $errval = substr($buffer, 3) . "</p>".PHP_EOL;
99 break;
102 } else {
103 $errval .= "Failed to read file $filename <p>".PHP_EOL;
105 } else {
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;
124 break;
127 } else {
128 $errval = "Failed to read file $filename <br />".PHP_EOL;
129 csv_edihist_log ( "ibr_ack_error Error: failed to read " . $filename);
133 return $errval;
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
142 * @return array
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);
155 } else {
156 // error, unable to read file
157 csv_edihist_log ("ibr_process_ack: Error, unable to read file $filepath");
158 return FALSE;
160 // read the file
161 $fh = fopen($filepath, 'r');
162 if ($fh) {
163 while (($buffer = fgets($fh, 4096)) !== false) {
164 $ar_ack[] = trim($buffer);
166 } else {
167 csv_edihist_log ( "ibr_process_ack Error: failed to read " . $file_path );
169 fclose($fh);
171 $ar_ackfile = array();
172 $batch_file ="";
173 $ack_msg = "";
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) ) {
181 $ack_mtime = $dtstr;
183 $ack_date = trim($ln[1]);
184 $ack_ip = trim($ln[2]);
185 $ack_isa13 = strval($ln[4]);
186 $ack_ctlnum = strval($ln[5]);
187 continue;
189 if (substr($strln, 0 ,2) == '1E') {
190 $ln = explode(IBR_DELIMITER, $strln);
191 $ack_msg .= '1E: '.trim(substr($ln[1],0, 80)) . "... ";
192 $ack_code = '1E';
193 continue;
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"
209 return $ar_ackfile;
213 * process TA1 acknowledgment files
215 * @return array
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);
231 } else {
232 // error getting segments
233 csv_edihist_log("ibr_process_ta1: error getting segments for $filepath");
234 return false;
237 $batch_file = '';
238 $ack_msg = '';
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];
251 $ack_code = $seg[5];
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 : "";
268 return $ar_ackfile;
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 ""; }
281 $idf = 0;
283 // array('Date', 'FileName', 'isa13', 'ta1ctrl', 'code');
284 $str_html = "<table class=\"f997\" cols=5><caption>ACK/TA1 File Report</caption>
285 <thead>
286 <tr>
287 <th>Date</th><th>File Name</th><th>Batch ICN</th><th>Batch</th><th>Code</th>
288 </tr>
289 </thead>
290 <tbody>";
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';
294 $idf++;
296 $str_html .= "
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>&nbsp;&nbsp; <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>
303 </tr>
304 <tr>
305 <td span=4>{$ack['ack_msg']}</td>
306 </tr>";
309 $str_html .= "</tbody>".PHP_EOL."</table>".PHP_EOL;
310 return $str_html;
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
324 * @return string
326 function ibr_ack_process_new($ack_files, $html_out = TRUE ) {
328 $str_html = "";
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;
334 } else {
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');
342 if ($fp) {
343 $ar_d[] = ibr_process_ack($fp);
344 } else {
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");
359 if ($html_out) {
360 $str_html .= ibr_ack_html($ar_d);
361 } else {
362 $str_html .= "ACK files: processed $fcount ACK files <br />".PHP_EOL;
364 } else {
365 $str_html .= "No new ACK files found. <br />" .PHP_EOL;
368 return $str_html;
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
381 * @return string
383 function ibr_ta1_process_new($ta1_files, $html_out = TRUE ) {
385 $str_html = "";
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;
391 } else {
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');
399 if ($fp) {
400 $ar_d[] = ibr_process_ta1($fp);
401 } else {
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");
415 if ($html_out) {
416 $str_html .= ibr_ack_html($ar_d);
417 } else {
418 $str_html .= "TA1 files: processed $fcount TA1 files <br />".PHP_EOL;
420 } else {
421 $str_html .= "No new TA1 files found. <br />" .PHP_EOL;
424 return $str_html;