Updated to document new features, particularly multibox buttons.
[openemr.git] / interface / billing / billing_process.php
blob5dd53fd295a6a7635c3ecf2c7dc85571b7184988
1 <?php
2 include_once("../globals.php");
3 include_once("$srcdir/patient.inc");
4 include_once("$srcdir/billrep.inc");
5 include_once("$srcdir/billing.inc");
6 include_once("$srcdir/gen_x12_837.inc.php");
7 include_once(dirname(__FILE__) . "/../../library/classes/WSClaim.class.php");
9 $EXPORT_INC = "$webserver_root/custom/BillingExport.php";
10 if (file_exists($EXPORT_INC)) {
11 include_once($EXPORT_INC);
12 $BILLING_EXPORT = true;
15 // This is a kludge to enter some parameters that are not in the X12
16 // Partners table, but should be.
18 // The following works for Zirmed:
19 $ISA07 = 'ZZ'; // ZZ = mutually defined, 01 = Duns, etc.
20 $ISA14 = '0'; // 1 = Acknowledgment requested, else 0
21 $ISA15 = 'T'; // T = testing, P = production
22 $GS02 = ''; // Empty to use the sender ID from the ISA segment
23 $PER06 = ''; // The submitter's EDI Access Number, if any
24 /**** For Availity:
25 $ISA07 = '01'; // ZZ = mutually defined, 01 = Duns, etc.
26 $ISA14 = '1'; // 1 = Acknowledgment requested, else 0
27 $ISA15 = 'T'; // T = testing, P = production
28 $GS02 = 'AV01101957';
29 $PER06 = 'xxxxxx'; // The submitter's EDI Access Number
30 ****/
32 $fconfig = $GLOBALS['oer_config']['freeb'];
33 $bill_info = array();
35 $bat_type = ''; // will be edi or hcfa
36 $bat_sendid = '';
37 $bat_recvid = '';
38 $bat_content = '';
39 $bat_stcount = 0;
40 $bat_time = time();
41 $bat_hhmm = date('Hi' , $bat_time);
42 $bat_yymmdd = date('ymd', $bat_time);
43 $bat_yyyymmdd = date('Ymd', $bat_time);
44 // Minutes since 1/1/1970 00:00:00 GMT will be our interchange control number:
45 $bat_icn = sprintf('%09.0f', $bat_time/60);
46 $bat_filename = date("Y-m-d-Hi", $bat_time) . "-batch.txt";
48 function append_claim(&$segs) {
49 global $bat_content, $bat_sendid, $bat_recvid, $bat_sender, $bat_stcount;
50 global $bat_yymmdd, $bat_yyyymmdd, $bat_hhmm, $bat_icn;
51 global $GS02, $ISA07, $ISA14, $ISA15, $PER06;
53 foreach ($segs as $seg) {
54 if (!$seg) continue;
55 $elems = explode('*', $seg);
56 if ($elems[0] == 'ISA') {
57 if (!$bat_content) {
58 $bat_sendid = trim($elems[6]);
59 $bat_recvid = trim($elems[8]);
60 $bat_sender = $GS02 ? $GS02 : $bat_sendid;
61 $bat_content = substr($seg, 0, 51) .
62 $ISA07 . substr($seg, 53, 17) .
63 "$bat_yymmdd*$bat_hhmm*U*00401*$bat_icn*$ISA14*$ISA15*:~" .
64 "GS*HC*$bat_sender*$bat_recvid*$bat_yyyymmdd*$bat_hhmm*1*X*004010X098A1~";
66 continue;
67 } else if (!$bat_content) {
68 die("Error:<br>\nInput must begin with 'ISA'; " .
69 "found '" . htmlentities($elems[0]) . "' instead");
71 if ($elems[0] == 'ST') {
72 ++$bat_stcount;
73 $bat_content .= sprintf("ST*837*%04d~", $bat_stcount);
74 continue;
76 if ($elems[0] == 'SE') {
77 $bat_content .= sprintf("SE*%d*%04d~", $elems[1], $bat_stcount);
78 continue;
80 if ($elems[0] == 'GS' || $elems[0] == 'GE' || $elems[0] == 'IEA') continue;
81 if ($elems[0] == 'PER' && $PER06 && !$elems[5]) {
82 $seg .= "*ED*$PER06";
84 $bat_content .= $seg . '~';
88 function append_claim_close() {
89 global $bat_content, $bat_stcount, $bat_icn;
90 $bat_content .= "GE*$bat_stcount*1~IEA*1*$bat_icn~";
93 function send_batch() {
94 global $bat_content, $bat_filename, $webserver_root;
95 // If a writable edi directory exists, log the batch to it.
96 // I guarantee you'll be glad we did this. :-)
97 $fh = @fopen("$webserver_root/edi/$bat_filename", 'a');
98 if ($fh) {
99 fwrite($fh, $bat_content);
100 fclose($fh);
102 header("Pragma: public");
103 header("Expires: 0");
104 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
105 header("Content-Type: application/force-download");
106 header("Content-Disposition: attachment; filename=$bat_filename");
107 header("Content-Description: File Transfer");
108 header("Content-Length: " . strlen($bat_content));
109 echo $bat_content;
112 //////////////////////////////////////////////////////////////////////
114 if (isset($_POST['bn_electronic_file']) && !empty($_POST['claims'])) {
116 if (empty($_POST['claims'])) {
117 $bill_info[] = xl("No claims were selected for inclusion.");
120 foreach ($_POST['claims'] as $claim) {
121 if (isset($claim['bill'])) {
122 if (substr($claim['file'],strlen($claim['file']) -4) == '.pdf') {
123 $fname = substr($claim['file'],0,-4);
125 else {
126 $fname = $claim['file'];
129 $tmp = substr($fname, strrpos($fname, '.') + 1);
130 if (!$bat_type) {
131 $bat_type = $tmp;
132 } else if ($bat_type != $tmp) {
133 die("You cannot mix '$bat_type' and '$tmp' formats in the same batch!");
136 $fname = preg_replace("[/]","",$fname);
137 $fname = preg_replace("[\.\.]","",$fname);
138 $fname = preg_replace("[\\\\]","",$fname);
139 $fname = $fconfig['claim_file_dir'] . $fname;
141 if (file_exists($fname)) {
142 //less than 500 is almost definitely an error
143 if (filesize($fname) > 500) {
144 $bill_info[] = xl("Added: ") . $fname . "\n";
146 if ($bat_type != 'edi') {
147 $bat_content .= file_get_contents($fname);
148 continue;
151 // Begin obsolete section.
153 // If we get here, we are sending X12 837p data. Strip off the ISA,
154 // GS, GE, and IEA segments from the individual files and send just
155 // one set of these for the whole batch. We think this is what the
156 // partners are happiest with, and Availity for one requires
157 // (as of this writing) exactly one ISA/IEA pair per batch.
158 $segs = explode('~', file_get_contents($fname));
159 append_claim($segs);
161 // End obsolete section.
164 else {
165 $bill_info[] = xl("May have an error: ") . $fname . "\n";
168 else {
169 $bill_info[] = xl("Not found: ") . $fname . "\n";
174 // Begin obsolete section.
175 if ($bat_type == 'edi' && $bat_content) {
176 append_claim_close();
178 // End obsolete section.
180 if ($bat_content) {
181 $db = $GLOBALS['adodb']['db'];
182 $error = false;
183 foreach ($_POST['claims'] as $claimid => $claim) {
184 if (isset($claim['bill'])) {
185 $tmpvars = split("-",$claimid);
186 $pid = $tmpvars[0];
187 $encounter = $tmpvars[1];
188 if (!empty($encounter) && !empty($pid)) {
189 /****
190 $sql = "UPDATE billing set billed = 1 where encounter = '" . $encounter .
191 "' and pid = '" . $pid . "' and activity != 0";
192 $result = $db->execute($sql);
193 if(!$result) {
194 $error = true;
195 $bill_info[] = xl("Marking claim "). $claimid . xl(" had a db error: ") . $db->ErrorMsg() . "\n";
197 // send claim to the web services code to sync to external system if enabled
198 // remember that in openemr it is only the encounter and patient id that make a group of
199 // billing line items unique so they can be grouped or associated as 1 claim
200 $ws = new WSClaim($pid, $encounter);
201 ****/
202 if (!updateClaim(false, $pid, $encounter, -1, 2)) {
203 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
208 if (!$error) {
209 send_batch();
210 exit;
215 else {
216 process_form($_POST);
219 function process_form($ar) {
220 global $bill_info, $webserver_root, $bat_filename;
222 if (isset($ar['bn_x12'])) {
223 $hlog = fopen("$webserver_root/library/freeb/process_bills.log", 'w');
226 if (isset($ar['bn_external'])) {
227 // Open external billing file for output.
228 $be = new BillingExport();
231 $db = $GLOBALS['adodb']['db'];
233 if (empty($ar['claims'])) {
234 $ar['claims'] = array();
236 foreach ($ar['claims'] as $claimid => $claim_array) {
238 $ta = split("-",$claimid);
239 $patient_id = $ta[0];
240 $encounter = $ta[1];
241 $payer = $claim_array['payer'];
243 if (isset($claim_array['bill'])) {
245 if (isset($ar['bn_external'])) {
246 // Write external claim.
247 $be->addClaim($patient_id, $encounter);
249 else {
250 $sql = "SELECT x.processing_format from x12_partners as x where x.id =" .
251 $db->qstr($claim_array['partner']);
252 $result = $db->Execute($sql);
253 $target = "x12";
254 if ($result && !$result->EOF) {
255 $target = $result->fields['processing_format'];
259 // $sql = "UPDATE billing set bill_date = NOW(), ";
260 $tmp = 1;
262 if (isset($ar['bn_hcfa_print'])) {
263 // $sql .= " bill_process = 5, target = 'hcfa', ";
264 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 5, '', 'hcfa');
265 } else if (isset($ar['bn_hcfa'])) {
266 // $sql .= " bill_process = 1, target = 'hcfa', ";
267 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 1, '', 'hcfa');
268 } else if (isset($ar['bn_ub92_print'])) {
269 // $sql .= " bill_process = 5, target = 'ub92', ";
270 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 5, '', 'ub92');
271 } else if (isset($ar['bn_ub92'])) {
272 // $sql .= " bill_process = 1, target = 'ub92', ";
273 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 1, '', 'ub92');
274 } else if (isset($ar['bn_x12'])) {
275 // $sql .= " bill_process = 1, target = '" . $target . "', x12_partner_id = '" .
276 // mysql_real_escape_string($claim_array['partner']) . "', ";
277 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 1, '', $target, $claim_array['partner']);
278 } else if (isset($ar['bn_mark'])) {
279 // $sql .= " billed = 1, ";
280 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 2);
281 } else if (isset($ar['bn_reopen'])) {
282 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 1, 0);
283 } else if (isset($ar['bn_external'])) {
284 // $sql .= " billed = 1, ";
285 $tmp = updateClaim(true, $patient_id, $encounter, $payer, 2);
288 // $sql .= " payer_id = '$payer' where encounter = " . $encounter . " and pid = " . $patient_id;
289 // $result = $db->Execute($sql);
291 // if(!$result) {
292 // die(xl("Claim ") . $claimid . xl(" could not be queued due to error: ") . $db->ErrorMsg());
293 // }
295 if (!$tmp) {
296 die(xl("Claim ") . $claimid . xl(" update failed, not in database?"));
298 else {
299 if(isset($ar['bn_mark'])) {
300 $bill_info[] = xl("Claim ") . $claimid . xl(" was marked as billed only.") . "\n";
303 else if (isset($ar['bn_reopen'])) {
304 $bill_info[] = xl("Claim ") . $claimid . xl(" has been re-opened.") . "\n";
307 else if (isset($ar['bn_x12'])) {
308 $log = '';
309 $segs = explode("~\n", gen_x12_837($patient_id, $encounter, $log));
310 fwrite($hlog, $log);
311 append_claim($segs);
313 /****
314 $db->execute("UPDATE billing SET billed = 1, bill_process = 2, " .
315 "process_date = NOW(), process_file = '' WHERE " .
316 "encounter = '$encounter' AND pid = '$patient_id' AND activity != 0");
317 $ws = new WSClaim($patient_id, $encounter); // requires billed=1
318 ****/
319 if (!updateClaim(false, $patient_id, $encounter, -1, 2, 2, $bat_filename)) {
320 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
324 else {
325 $bill_info[] = xl("Claim ") . $claimid . xl(" was queued successfully.") . "\n";
329 /****
330 if ($mark_only) {
331 //send claim to the web services code to sync to external system if enabled
332 //remeber that in openemr it is only the encounter and patient id that make a group of
333 //billing line items unique so they can be grouped or associated as 1 claim
334 $ws = new WSClaim($patient_id,$encounter);
336 ****/
342 if (isset($ar['bn_x12'])) {
343 append_claim_close();
344 fclose($hlog);
345 send_batch();
346 exit;
349 if (isset($ar['bn_external'])) {
350 // Close external billing file.
351 $be->close();
355 <html>
356 <head>
358 <link rel=stylesheet href="<?echo $css_header;?>" type="text/css">
360 </head>
361 <body <?echo $top_bg_line;?> topmargin=0 rightmargin=0 leftmargin=2 bottommargin=0 marginwidth=2 marginheight=0>
362 <br><p><h3><?php xl('Billing queue results:','e'); ?></h3><a href="billing_report.php">back</a><ul>
363 <?php
364 foreach ($bill_info as $infoline) {
365 echo nl2br($infoline);
368 </ul></p>
369 </body>
370 </html>