3 * Billing process Program
5 * This program processes data for claims generation
8 * @link http://www.open-emr.org
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @author Terry Hill <terry@lilysystems.com>
11 * @author Jerry Padgett <sjpadgett@gmail.com>
12 * @author Stephen Waite <stephen.waite@cmsvt.com>
13 * @copyright Copyright (c) 2014-2019 Brady Miller <brady.g.miller@gmail.com>
14 * @copyright Copyright (c) 2016 Terry Hill <terry@lillysystems.com>
15 * @copyright Copyright (c) 2017 Jerry Padgett <sjpadgett@gmail.com>
16 * @copyright Copyright (c) 2018-2019 Stephen Waite <stephen.waite@cmsvt.com>
17 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
20 require_once("../globals.php");
21 require_once("$srcdir/patient.inc");
22 require_once("$srcdir/billrep.inc");
24 use OpenEMR\Billing\BillingUtilities
;
25 use OpenEMR\Billing\HCFA_1500
;
26 use OpenEMR\Billing\X12_5010_837P
;
27 use OpenEMR\Common\Crypto\CryptoGen
;
29 if (!verifyCsrfToken($_POST["csrf_token_form"])) {
33 if ($GLOBALS['ub04_support']) {
34 require_once("./ub04_dispose.php");
36 $EXPORT_INC = "$webserver_root/custom/BillingExport.php";
37 if (file_exists($EXPORT_INC)) {
38 include_once($EXPORT_INC);
39 $BILLING_EXPORT = true;
44 $bat_type = ''; // will be edi or hcfa
51 $bat_hhmm = date('Hi', $bat_time);
52 $bat_yymmdd = date('ymd', $bat_time);
53 $bat_yyyymmdd = date('Ymd', $bat_time);
54 // Seconds since 1/1/1970 00:00:00 GMT will be our interchange control number
55 // but since limited to 9 char must be without leading 1
56 $bat_icn = substr((string)$bat_time, 1, 9);
57 $bat_filename = date("Y-m-d-Hi", $bat_time) . "-batch.";
58 $bat_filename .= (isset($_POST['bn_process_hcfa']) ||
isset($_POST['bn_process_hcfa_form']) ||
isset($_POST['bn_process_ub04']) ||
isset($_POST['bn_process_ub04_form'])) ?
'pdf' : 'txt';
62 if (isset($_POST['bn_process_hcfa']) ||
isset($_POST['bn_process_hcfa_form'])) {
63 $pdf = new Cezpdf('LETTER');
64 $pdf->ezSetMargins(trim($_POST['top_margin']) +
0, 0, trim($_POST['left_margin']) +
0, 0);
65 $pdf->selectFont('Courier');
68 function append_claim(&$segs)
70 global $bat_content, $bat_sendid, $bat_recvid, $bat_sender, $bat_stcount;
71 global $bat_gscount, $bat_yymmdd, $bat_yyyymmdd, $bat_hhmm, $bat_icn;
73 foreach ($segs as $seg) {
77 $elems = explode('*', $seg);
78 if ($elems[0] == 'ISA') {
80 $bat_sendid = trim($elems[6]);
81 $bat_recvid = trim($elems[8]);
82 $bat_sender = $GS02 ?
$GS02 : $bat_sendid;
83 $bat_content = substr($seg, 0, 70) . "$bat_yymmdd*$bat_hhmm*" . $elems[11] . "*" . $elems[12] . "*$bat_icn*" . $elems[14] . "*" . $elems[15] . "*:~";
86 } elseif (! $bat_content) {
87 die("Error:<br>\nInput must begin with 'ISA'; " . "found '" . text($elems[0]) . "' instead");
89 if ($elems[0] == 'GS') {
90 if ($bat_gscount == 0) {
92 $bat_content .= "GS*HC*" . $elems[2] . "*" . $elems[3] . "*$bat_yyyymmdd*$bat_hhmm*1*X*" . $elems[8] . "~";
96 if ($elems[0] == 'ST') {
98 $bat_st_02 = sprintf("%04d", $bat_stcount);
99 $bat_content .= "ST*837*" . $bat_st_02;
100 if (! empty($elems[3])) {
101 $bat_content .= "*" . $elems[3];
108 if ($elems[0] == 'BHT') {
109 // needle is set in OpenEMR\Billing\X12_5010_837P
110 $bat_content .= substr_replace($seg, '*'.$bat_icn.$bat_st_02.'*', strpos($seg, '*0123*'), 6);
115 if ($elems[0] == 'SE') {
116 $bat_content .= sprintf("SE*%d*%04d~", $elems[1], $bat_stcount);
120 if ($elems[0] == 'GE' ||
$elems[0] == 'IEA') {
124 $bat_content .= $seg . '~';
128 function append_claim_close()
130 global $bat_content, $bat_stcount, $bat_gscount, $bat_icn;
132 $bat_content .= "GE*$bat_stcount*1~";
135 $bat_content .= "IEA*$bat_gscount*$bat_icn~";
138 function send_batch()
140 global $bat_content, $bat_filename, $webserver_root;
141 // If a writable edi directory exists, log the batch to it.
142 // I guarantee you'll be glad we did this. :-)
143 $fh = @fopen
($GLOBALS['OE_SITE_DIR'] . "/documents/edi/$bat_filename", 'a');
145 fwrite($fh, $bat_content);
148 header("Pragma: public");
149 header("Expires: 0");
150 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
151 header("Content-Type: application/force-download");
152 header("Content-Disposition: attachment; filename=$bat_filename");
153 header("Content-Description: File Transfer");
154 header("Content-Length: " . strlen($bat_content));
158 process_form($_POST);
160 function process_form($ar)
162 global $bill_info, $webserver_root, $bat_filename, $pdf, $template;
165 // Set up crypto object
166 $cryptoGen = new CryptoGen();
168 if (isset($ar['bn_x12']) ||
isset($ar['bn_x12_encounter']) ||
isset($ar['bn_process_hcfa']) ||
isset($ar['bn_hcfa_txt_file']) ||
isset($ar['bn_process_hcfa_form'])
169 ||
isset($ar['bn_process_ub04_form']) ||
isset($ar['bn_process_ub04']) ||
isset($ar['bn_ub04_x12'])) {
170 if ($GLOBALS['billing_log_option'] == 1) {
171 $hlog = file_get_contents($GLOBALS['OE_SITE_DIR'] . "/documents/edi/process_bills.log");
172 if ($cryptoGen->cryptCheckStandard($hlog)) {
173 $hlog = $cryptoGen->decryptStandard($hlog, null, 'database');
175 } else { // ($GLOBALS['billing_log_option'] == 2)
180 if (isset($ar['bn_external'])) {
181 // Open external billing file for output.
182 $be = new BillingExport();
185 if (empty($ar['claims'])) {
186 $ar['claims'] = array();
189 foreach ($ar['claims'] as $claimid => $claim_array) {
190 $ta = explode("-", $claimid);
191 $patient_id = $ta[0];
193 $payer_id = substr($claim_array['payer'], 1);
194 $payer_type = substr($claim_array['payer'], 0, 1);
195 $payer_type = $payer_type == 'T' ?
3 : $payer_type == 'S' ?
2 : 1;
197 if (isset($claim_array['bill'])) {
198 if (isset($ar['bn_external'])) {
199 // Write external claim.
200 $be->addClaim($patient_id, $encounter);
202 $sql = "SELECT x.processing_format from x12_partners as x where x.id =?";
203 $result = sqlQuery($sql, [$claim_array['partner']]);
205 if (!empty($result['processing_format'])) {
206 $target = $result['processing_format'];
211 if (isset($ar['HiddenMarkAsCleared']) && $ar['HiddenMarkAsCleared'] == 'yes') {
212 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 2); // $sql .= " billed = 1, ";
214 if (isset($ar['bn_x12']) ||
isset($ar['bn_x12_encounter'])) {
215 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 1, 1, '', $target, $claim_array['partner']);
216 } elseif (isset($ar['bn_ub04_x12'])) {
217 $ub04id = get_ub04_array($patient_id, $encounter);
218 $ub_save = json_encode($ub04id);
219 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 1, 1, '', $target, $claim_array['partner'] . '-837I', 0, $ub_save);
220 } elseif (isset($ar['bn_process_ub04_form']) ||
isset($ar['bn_process_ub04'])) {
221 $ub04id = get_ub04_array($patient_id, $encounter);
222 $ub_save = json_encode($ub04id);
223 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 1, 1, '', 'ub04', - 1, 0, $ub_save);
224 } elseif (isset($ar['bn_process_hcfa']) ||
isset($ar['bn_hcfa_txt_file']) ||
isset($ar['bn_process_hcfa_form'])) {
225 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 1, 1, '', 'hcfa');
226 } elseif (isset($ar['bn_mark'])) {
227 // $sql .= " billed = 1, ";
228 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 2);
229 } elseif (isset($ar['bn_reopen'])) {
230 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 1, 0);
231 } elseif (isset($ar['bn_external'])) {
232 // $sql .= " billed = 1, ";
233 $tmp = BillingUtilities
::updateClaim(true, $patient_id, $encounter, $payer_id, $payer_type, 2);
237 die(xlt("Claim ") . text($claimid) . xlt(" update failed, not in database?"));
239 if (isset($ar['bn_mark'])) {
240 $bill_info[] = xl("Claim ") . $claimid . xl(" was marked as billed only.") . "\n";
241 } elseif (isset($ar['bn_reopen'])) {
242 $bill_info[] = xl("Claim ") . $claimid . xl(" has been re-opened.") . "\n";
243 } elseif (isset($ar['bn_x12']) ||
isset($ar['bn_x12_encounter'])) {
245 $segs = explode("~\n", X12_5010_837P
::gen_x12_837($patient_id, $encounter, $log, isset($ar['bn_x12_encounter'])));
248 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename)) {
249 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
251 } elseif (isset($ar['bn_ub04_x12'])) {
253 $segs = explode("~\n", generate_x12_837I($patient_id, $encounter, $log, $ub04id));
256 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename, 'X12-837I', - 1, 0, json_encode($ub04id))) {
257 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
259 } elseif (isset($ar['bn_process_hcfa'])) {
261 $hcfa = new HCFA_1500();
262 $lines = $hcfa->gen_hcfa_1500($patient_id, $encounter, $log);
264 $alines = explode("\014", $lines); // form feeds may separate pages
265 foreach ($alines as $tmplines) {
266 if ($claim_count ++
) {
269 $pdf->ezSetY($pdf->ez
['pageHeight'] - $pdf->ez
['topMargin']);
270 $pdf->ezText($tmplines, 12, array(
271 'justification' => 'left',
275 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename)) {
276 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
278 } elseif (isset($ar['bn_process_hcfa_form'])) {
280 $hcfa = new HCFA_1500();
281 $lines = $hcfa->gen_hcfa_1500($patient_id, $encounter, $log);
282 $hcfa_image = $GLOBALS['images_static_absolute'] . "/cms1500.png";
284 $alines = explode("\014", $lines); // form feeds may separate pages
285 foreach ($alines as $tmplines) {
286 if ($claim_count ++
) {
289 $pdf->ezSetY($pdf->ez
['pageHeight'] - $pdf->ez
['topMargin']);
290 $pdf->addPngFromFile("$hcfa_image", 0, 0, 612, 792);
291 $pdf->ezText($tmplines, 12, array(
292 'justification' => 'left',
296 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename)) {
297 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
299 } elseif (isset($ar['bn_process_ub04_form']) ||
isset($ar['bn_process_ub04'])) {
302 $template[] = buildTemplate($patient_id, $encounter, "", "", $log);
304 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename, 'ub04', - 1, 0, json_encode($ub04id))) {
305 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
307 } elseif (isset($ar['bn_hcfa_txt_file'])) {
309 $hcfa = new HCFA_1500();
310 $lines = $hcfa->gen_hcfa_1500($patient_id, $encounter, $log);
312 $bat_content .= $lines;
313 if (! BillingUtilities
::updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename)) {
314 $bill_info[] = xl("Internal error: claim ") . $claimid . xl(" not found!") . "\n";
317 $bill_info[] = xl("Claim ") . $claimid . xl(" was queued successfully.") . "\n";
320 } // end if this claim has billing
324 if ($GLOBALS['drive_encryption']) {
325 $hlog = $cryptoGen->encryptStandard($hlog, null, 'database');
327 file_put_contents($GLOBALS['OE_SITE_DIR'] . "/documents/edi/process_bills.log", $hlog);
330 if (isset($ar['bn_process_ub04_form']) ||
isset($ar['bn_process_ub04'])) {
331 if (isset($ar['bn_process_ub04'])) {
333 } elseif (isset($ar['bn_process_ub04_form'])) {
336 ub04Dispose('download', $template, $bat_filename, $action);
339 if (isset($ar['bn_x12']) ||
isset($ar['bn_x12_encounter']) ||
isset($ar['bn_ub04_x12'])) {
340 append_claim_close();
345 if (isset($ar['bn_process_hcfa'])) {
346 // If a writable edi directory exists (and it should), write the pdf to it.
347 $fh = @fopen
($GLOBALS['OE_SITE_DIR'] . "/documents/edi/$bat_filename", 'a');
349 fwrite($fh, $pdf->ezOutput());
352 // Send the PDF download.
353 $pdf->ezStream(array(
354 'Content-Disposition' => $bat_filename
358 if (isset($ar['bn_process_hcfa_form'])) {
359 // If a writable edi directory exists (and it should), write the pdf to it.
360 $fh = @fopen
($GLOBALS['OE_SITE_DIR'] . "/documents/edi/$bat_filename", 'a');
362 fwrite($fh, $pdf->ezOutput());
365 // Send the PDF download.
366 header("Pragma: public");
367 header("Expires: 0");
368 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
369 header("Content-Type: application/force-download");
370 header("Content-Disposition: attachment; filename=$bat_filename");
371 header("Content-Description: File Transfer");
372 // header("Content-Length: " . strlen($bat_content));
373 echo $pdf->ezOutput();
378 if (isset($ar['bn_hcfa_txt_file'])) {
379 $fh = @fopen
($GLOBALS['OE_SITE_DIR'] . "/documents/edi/$bat_filename", 'a');
381 fwrite($fh, $bat_content);
384 header("Pragma: public");
385 header("Expires: 0");
386 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
387 header("Content-Type: application/force-download");
388 header("Content-Disposition: attachment; filename=$bat_filename");
389 header("Content-Description: File Transfer");
390 header("Content-Length: " . strlen($bat_content));
395 if (isset($ar['bn_external'])) {
396 // Close external billing file.
404 <link rel
="stylesheet" href
="<?php echo $css_header;?>" type
="text/css">
405 <script type
="text/javascript"
406 src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-1-9-1/jquery.min.js"></script
>
409 $
("#close-link").click( function() {
416 <body
class="body_top">
417 <br
><p
><h3
><?php
echo xlt('Billing queue results'); ?
>:</h3
><a href
="#" id
="close-link"><?php
echo xlt('Close'); ?
></a
><ul
>
419 foreach ($bill_info as $infoline) {
420 echo nl2br($infoline);