chore: complete integration of flex-3.20 (alpine 3.20) into ci (#7538)
[openemr.git] / interface / billing / sl_eob_search.php
blobd8b1f31a53d44778e204566f37dbd33a2ba7beda
1 <?php
3 /**
4 * This the first of two pages to support posting of EOBs.
5 * The second is sl_eob_invoice.php.
7 * @package OpenEMR
8 * @link http://www.open-emr.org
9 * @author Rod Roark <rod@sunsetsystems.com>
10 * @author Bill Cernansky
11 * @author Tony McCormick
12 * @author Roberto Vasquez <robertogagliotta@gmail.com>
13 * @author Jerry Padgett <sjpadgett@gmail.com>
14 * @author Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2005-2020 Rod Roark <rod@sunsetsystems.com>
16 * @copyright Copyright (c) 2018-2020 Brady Miller <brady.g.miller@gmail.com>
17 * @copyright Copyright (c) 2018-2020 Jerry Padgett <sjpadgett@gmail.com>
18 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
21 // Updated by Growlingflea Software. now generates correct service and billing facility on statement.
22 // any questions contact Daniel Pflieger at daniel@growlingflea.com
24 require_once("../globals.php");
25 require_once("$srcdir/patient.inc.php");
26 require_once("$srcdir/appointments.inc.php");
27 require_once($GLOBALS['OE_SITE_DIR'] . "/statement.inc.php");
28 require_once("$srcdir/api.inc.php");
29 require_once("$srcdir/forms.inc.php");
30 require_once("$srcdir/../controllers/C_Document.class.php");
31 require_once("$srcdir/documents.php");
32 require_once("$srcdir/options.inc.php");
33 require_once "$srcdir/user.inc.php";
35 use Mpdf\Mpdf;
36 use OpenEMR\Billing\InvoiceSummary;
37 use OpenEMR\Billing\ParseERA;
38 use OpenEMR\Billing\SLEOB;
39 use OpenEMR\Common\Acl\AclMain;
40 use OpenEMR\Common\Csrf\CsrfUtils;
41 use OpenEMR\Common\Twig\TwigContainer;
42 use OpenEMR\Common\Utils\FormatMoney;
43 use OpenEMR\Core\Header;
44 use OpenEMR\OeUI\OemrUI;
45 use OpenEMR\Pdf\Config_Mpdf;
47 if (!AclMain::aclCheckCore('acct', 'eob', '', 'write')) {
48 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("EOB Posting - Search")]);
49 exit;
52 $DEBUG = 0; // set to 0 for production, 1 to test
53 $alertmsg = '';
54 $where = '';
55 $eraname = '';
56 $eracount = 0;
57 $g_posting_adj_disable = $GLOBALS['posting_adj_disable'] ? 'checked' : '';
58 $posting_adj_disable = prevSetting('sl_eob_search.', 'posting_adj_disable', 'posting_adj_disable', $g_posting_adj_disable);
59 $form_cb = false;
61 /* Load dependencies only if we need them */
62 if (!empty($GLOBALS['portal_onsite_two_enable'])) {
63 /* Addition of onsite portal patient notify of invoice and reformated invoice - sjpadgett 01/2017 */
64 require_once("../../portal/lib/portal_mail.inc.php");
65 require_once("../../portal/lib/appsql.class.php");
67 function is_auth_portal($pid = 0)
69 if ($pData = sqlQuery("SELECT id, allow_patient_portal, fname, lname FROM `patient_data` WHERE `pid` = ?", array($pid))) {
70 if ($pData['allow_patient_portal'] != "YES") {
71 return false;
72 } else {
73 $_SESSION['portalUser'] = strtolower($pData['fname']) . $pData['id'];
74 return true;
76 } else {
77 return false;
81 function notify_portal($thispid, array $invoices, $template, $invid)
83 $builddir = $GLOBALS['OE_SITE_DIR'] . '/documents/onsite_portal_documents/templates/' . $thispid;
84 if (!is_dir($builddir)) {
85 mkdir($builddir, 0755, true);
88 if (fixup_invoice($template, $builddir . '/invoice' . $invid . '.tpl') != true) {
89 return false;
92 if (SavePatientAudit($thispid, $invoices) != true) {
93 return false;
94 } // this is all the invoice data for portal auditing
95 $note = xl('You have an invoice due for payment in your Patient Documents. There you may pay, download or print the invoice. Thank you.');
96 if (sendMail($_SESSION['authUser'], $note, xlt('Bill/Collect'), '', '0', $_SESSION['authUser'], $_SESSION['authUser'], $_SESSION['portalUser'], $invoices[0]['patient'], "New", '0') == 1) { // remind admin this was sent
97 sendMail($_SESSION['portalUser'], $note, xlt('Bill/Collect'), '', '0', $_SESSION['authUser'], $_SESSION['authUser'], $_SESSION['portalUser'], $invoices[0]['patient'], "New", '0'); // notify patient
98 } else {
99 return false;
102 return true;
105 function fixup_invoice($template, $ifile)
107 $data = file_get_contents($template);
108 if ($data == "") {
109 return false;
112 if (!file_put_contents($ifile, $data)) {
113 return false;
116 return true;
119 function SavePatientAudit($pid, $invs)
121 $appsql = new ApplicationTable();
122 try {
123 $audit = array();
124 $audit['patient_id'] = $pid;
125 $audit['activity'] = "invoice";
126 $audit['require_audit'] = "0";
127 $audit['pending_action'] = "payment";
128 $audit['action_taken'] = "";
129 $audit['status'] = "waiting transaction";
130 $audit['narrative'] = "Request patient online payment.";
131 $audit['table_action'] = '';
132 $audit['table_args'] = json_encode($invs);
133 $audit['action_user'] = $pid;
134 $audit['action_taken_time'] = "";
135 $audit['checksum'] = "";
136 $edata = $appsql->getPortalAudit($pid, 'payment', 'invoice', "waiting transaction", 0);
137 if ($edata['id'] > 0) {
138 $appsql->portalAudit('update', $edata['id'], $audit);
139 } else {
140 $appsql->portalAudit('insert', '', $audit);
142 } catch (Exception $ex) {
143 return $ex;
146 return true;
150 // This is called back by ParseERA::parseERA() if we are processing X12 835's.
151 function era_callback(&$out)
153 global $where, $eracount, $eraname;
154 // print_r($out); // debugging
155 ++$eracount;
156 // $eraname = $out['isa_control_number'];
157 // since it's always sent we use isa_sender_id if payer_id is not provided
158 $eraname = $out['gs_date'] . '_' . ltrim($out['isa_control_number'], '0') .
159 '_' . ltrim($out['payer_id'] ? $out['payer_id'] : $out['isa_sender_id'], '0');
161 if (!empty($out['our_claim_id'])) {
162 list($pid, $encounter, $invnumber) = SLEOB::slInvoiceNumber($out);
163 if ($pid && $encounter) {
164 if ($where) {
165 $where .= ' OR ';
168 $where .= "( f.pid = '" . add_escape_custom($pid) . "' AND f.encounter = '" . add_escape_custom($encounter) . "' )";
173 function validEmail($email)
175 if (preg_match("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$^", $email)) {
176 return true;
179 return false;
182 function emailLogin($patient_id, $message)
184 $patientData = sqlQuery("SELECT * FROM `patient_data` WHERE `pid`=?", array($patient_id));
185 if ($patientData['hipaa_allowemail'] != "YES" || empty($patientData['email']) || empty($GLOBALS['patient_reminder_sender_email'])) {
186 return false;
189 if (!(validEmail($patientData['email']))) {
190 return false;
193 if (!(validEmail($GLOBALS['patient_reminder_sender_email']))) {
194 return false;
197 if ($_SESSION['pc_facility']) {
198 $sql = "select * from facility where id=?";
199 $facility = sqlQuery($sql, array($_SESSION['pc_facility']));
200 } else {
201 $sql = "SELECT * FROM facility ORDER BY billing_location DESC LIMIT 1";
202 $facility = sqlQuery($sql);
205 $mail = new MyMailer();
206 $pt_name = $patientData['fname'] . ' ' . $patientData['lname'];
207 $pt_email = $patientData['email'];
208 $email_subject = ($facility['name'] . ' ' . xl('Patient Statement Bill'));
209 $email_sender = $GLOBALS['patient_reminder_sender_email'];
210 $mail->AddReplyTo($email_sender, $email_sender);
211 $mail->SetFrom($email_sender, $email_sender);
212 $mail->AddAddress($pt_email, $pt_name);
213 $mail->Subject = $email_subject;
214 $mail->MsgHTML("<html><body><div class='wrapper'>" . $message . "</div></body></html>");
215 $mail->IsHTML(true);
216 $mail->AltBody = $message;
218 if ($mail->Send()) {
219 return true;
220 } else {
221 $email_status = $mail->ErrorInfo;
222 error_log("EMAIL ERROR: " . errorLogEscape($email_status), 0);
223 return false;
227 // Upload a file to the client's browser
229 function upload_file_to_client($file_to_send)
231 header("Pragma: public");
232 header("Expires: 0");
233 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
234 header("Content-Type: application/force-download");
235 header("Content-Length: " . filesize($file_to_send));
236 header("Content-Disposition: attachment; filename=" . basename($file_to_send));
237 header("Content-Description: File Transfer");
238 readfile($file_to_send);
239 // flush the content to the browser. If you don't do this, the text from the subsequent
240 // output from this script will be in the file instead of sent to the browser.
241 flush();
242 exit(); //added to exit from process properly in order to stop bad html code -ehrlive
243 // sleep one second to ensure there's no follow-on.
244 sleep(1);
247 function upload_file_to_client_email($ppid, $file_to_send)
249 $message = "";
250 global $STMT_TEMP_FILE_PDF;
251 $file = fopen($file_to_send, "r");//this file contains the text to be converted to pdf.
252 while (!feof($file)) {
253 $OneLine = fgets($file);//one line is read
255 $message = $message . $OneLine . '<br />';
257 $countline++;
260 emailLogin($ppid, $message);
263 function upload_file_to_client_pdf($file_to_send, $aPatFirstName = '', $aPatID = null, $flagCFN = false)
265 //modified for statement title name
266 //Function reads a HTML file and converts to pdf.
268 $aPatFName = convert_safe_file_dir_name($aPatFirstName); //modified for statement title name
269 if ($flagCFN) {
270 $STMT_TEMP_FILE_PDF = $GLOBALS['temporary_files_dir'] . "/Stmt_{$aPatFName}_{$aPatID}.pdf";
271 } else {
272 global $STMT_TEMP_FILE_PDF;
275 global $srcdir;
277 if ($GLOBALS['statement_appearance'] == '1') {
278 $config_mpdf = Config_Mpdf::getConfigMpdf();
279 $pdf2 = new mPDF($config_mpdf);
280 if ($_SESSION['language_direction'] == 'rtl') {
281 $pdf2->SetDirectionality('rtl');
283 ob_start();
284 // this file contains the HTML to be converted to pdf.
285 readfile($file_to_send, "r");
286 $content = ob_get_clean();
287 $pdf2->WriteHTML($content);
288 $temp_filename = $STMT_TEMP_FILE_PDF;
289 $pdf2->Output($temp_filename, 'F');
290 } else {
291 $pdf = new Cezpdf('LETTER');//pdf creation starts
292 $pdf->ezSetMargins(45, 9, 36, 10);
293 $pdf->selectFont('Courier');
294 $pdf->ezSetY($pdf->ez['pageHeight'] - $pdf->ez['topMargin']);
295 $countline = 1;
296 // this file contains the text to be converted to pdf.
297 $file = fopen($file_to_send, "r");
298 while (!feof($file)) {
299 // one line is read
300 $OneLine = fgets($file);
301 // form feed means we should start a new page.
302 if (stristr($OneLine, "\014") == true && !feof($file)) {
303 $pdf->ezNewPage();
304 $pdf->ezSetY($pdf->ez['pageHeight'] - $pdf->ez['topMargin']);
305 str_replace("\014", "", $OneLine);
308 if (
309 stristr($OneLine, 'REMIT TO') == true ||
310 stristr($OneLine, 'Visit Date') == true ||
311 stristr($OneLine, 'Future Appointments') == true ||
312 stristr($OneLine, 'Current') == true
314 // lines are made bold when 'REMIT TO' or 'Visit Date' is there.
315 $pdf->ezText('<b>' . $OneLine . '</b>', 12, array('justification' => 'left', 'leading' => 6));
316 } else {
317 $pdf->ezText($OneLine, 12, array('justification' => 'left', 'leading' => 6));
320 $countline++;
322 // stored to a pdf file
323 $fh = @fopen($STMT_TEMP_FILE_PDF, 'w');
324 if ($fh) {
325 fwrite($fh, $pdf->ezOutput());
326 fclose($fh);
329 // this section outputs the pdf file to browser
330 header("Pragma: public");
331 header("Expires: 0");
332 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
333 header("Content-Type: application/force-download");
334 header("Content-Length: " . filesize($STMT_TEMP_FILE_PDF));
335 header("Content-Disposition: attachment; filename=" . basename($STMT_TEMP_FILE_PDF));
336 header("Content-Description: File Transfer");
337 readfile($STMT_TEMP_FILE_PDF);
338 // flush the content to the browser. If you don't do this, the text from the subsequent
339 // output from this script will be in the file instead of sent to the browser.
340 flush();
341 // added to exit from process properly in order to stop bad html code -ehrlive
342 exit();
343 // sleep one second to ensure there's no follow-on.
344 sleep(1);
348 $today = date("Y-m-d");
350 // were any invoices selected?
351 if (!empty($_REQUEST['form_cb'])) {
352 $form_cb = true;
354 // Print or download statements if requested.
355 if (
358 !empty($_REQUEST['form_print']) ||
359 !empty($_REQUEST['form_download']) ||
360 !empty($_REQUEST['form_email']) ||
361 !empty($_REQUEST['form_pdf'])
362 ) || !empty($_REQUEST['form_portalnotify'])
363 ) && $form_cb
365 if (!CsrfUtils::verifyCsrfToken($_REQUEST["csrf_token_form"])) {
366 CsrfUtils::csrfNotVerified();
369 $fhprint = fopen($STMT_TEMP_FILE, 'w');
371 $sqlBindArray = array();
372 $where = "";
373 foreach ($_REQUEST['form_cb'] as $key => $value) {
374 $where .= " OR f.id = ?";
375 array_push($sqlBindArray, $key);
378 if (!empty($where)) {
379 $where = substr($where, 4);
380 $where = '( ' . $where . ' ) AND';
383 $res = sqlStatement("SELECT " .
384 "f.id, f.date, f.pid, f.encounter, f.stmt_count, f.last_stmt_date, f.last_level_closed, f.last_level_billed, f.billing_note as enc_billing_note, " .
385 "p.fname, p.mname, p.lname, p.street, p.city, p.state, p.postal_code, p.billing_note as pat_billing_note, f.provider_id " .
386 "FROM form_encounter AS f, patient_data AS p " .
387 "WHERE $where " .
388 "p.pid = f.pid " .
389 "ORDER BY p.lname, p.fname, f.pid, f.date, f.encounter", $sqlBindArray);
391 $stmt = array();
392 $stmt_count = 0;
394 $flagT = true;
395 $aPatientFirstName = '';
396 $aPatientID = null;
397 $multiplePatients = false;
398 $usePatientNamePdf = false;
400 // get pids for delimits
401 // need to only use summary invoice for multi visits
402 $inv_pid = array();
403 $inv_count = -1;
404 if (!empty($_REQUEST['form_portalnotify'])) {
405 foreach ($_REQUEST['form_invpids'] as $key => $v) {
406 if ($_REQUEST['form_cb'][$key]) {
407 array_push($inv_pid, key($v));
411 $rcnt = 0;
412 while ($row = sqlFetchArray($res)) {
413 $rows[] = $row;
414 if (empty($inv_pid[$rcnt])) {
415 array_push($inv_pid, $row['pid']);
417 $rcnt++;
419 // This loops once for each invoice/encounter.
421 for ($rcnt = 0; $row = $rows[$rcnt] ?? null; $rcnt++) {
422 $svcdate = substr($row['date'], 0, 10);
423 $duedate = $svcdate; // TBD?
424 $duncount = $row['stmt_count'];
425 $enc_note = $row['enc_billing_note'];
427 if ($flagT) {
428 $flagT = false;
429 $aPatientFirstName = $row['fname'];
430 $aPatientID = $row['pid'];
431 $usePatientNamePdf = true;
432 } elseif (!$multiplePatients) {
433 if ($aPatientID != $row['pid']) {
434 $multiplePatients = true;
435 $aPatientFirstName = '';
436 $aPatientID = null;
437 $usePatientNamePdf = false;
441 // If this is a new patient then print the pending statement
442 // and start a new one. This is an associative array:
444 // cid = same as pid
445 // pid = OpenEMR patient ID
446 // patient = patient name
447 // amount = total amount due
448 // adjust = adjustments (already applied to amount)
449 // duedate = due date of the oldest included invoice
450 // age = number of days from duedate to today
451 // to = array of addressee name/address lines
452 // lines = array of:
453 // dos = date of service "yyyy-mm-dd"
454 // desc = description
455 // amount = charge less adjustments
456 // paid = amount paid
457 // notice = 1 for first notice, 2 for second, etc.
458 // detail = array of details, see InvoiceSummary.php
460 if (empty($stmt['cid']) || ($stmt['cid'] != $row['pid'])) {
461 if (!empty($stmt)) {
462 ++$stmt_count;
464 $stmt['fid'] = $row['id'];
465 $stmt['cid'] = $row['pid'];
466 $stmt['pid'] = $row['pid'];
467 $stmt['dun_count'] = $row['stmt_count'];
468 $stmt['bill_note'] = $row['pat_billing_note'];
469 $stmt['enc_bill_note'] = $row['enc_billing_note'];
470 $stmt['bill_level'] = $row['last_level_billed'];
471 $stmt['level_closed'] = $row['last_level_closed'];
472 $stmt['patient'] = $row['fname'] . ' ' . $row['lname'];
473 $stmt['encounter'] = $row['encounter'];
474 $stmt['provider_id'] = $row['provider_id'];
475 #If you use the field in demographics layout called
476 #guardiansname this will allow you to send statements to the parent
477 #of a child or a guardian etc
478 if (empty($row['guardiansname'])) {
479 $stmt['to'] = array($row['fname'] . ' ' . $row['lname']);
480 } else {
481 $stmt['to'] = array($row['guardiansname']);
484 if ($row['street']) {
485 $stmt['to'][] = $row['street'];
488 $stmt['to'][] = $row['city'] . ", " . $row['state'] . " " . $row['postal_code'];
489 $stmt['lines'] = array();
490 $stmt['amount'] = '0.00';
491 $stmt['ins_paid'] = 0;
492 $stmt['today'] = $today;
493 $stmt['duedate'] = $duedate;
494 } else {
495 // Report the oldest due date.
496 if ($duedate < $stmt['duedate']) {
497 $stmt['duedate'] = $duedate;
501 // Recompute age at each invoice.
502 $stmt['age'] = round((strtotime($today) - strtotime($stmt['duedate'])) / (24 * 60 * 60));
503 // grab last bill date from billing
504 $bdrow = sqlQuery("select bill_date from billing where pid = ? AND encounter = ? limit 1", array($row['pid'], $row['encounter']));
506 $invlines = InvoiceSummary::arGetInvoiceSummary($row['pid'], $row['encounter'], true);
507 foreach ($invlines as $key => $value) {
508 $line = array();
509 $line['dos'] = $svcdate;
510 if ($GLOBALS['use_custom_statement']) {
511 $line['desc'] = ($key == 'CO-PAY') ? "Patient Payment" : $value['code_text'];
512 } else {
513 $line['desc'] = ($key == 'CO-PAY') ? "Patient Payment" : "Procedure $key";
516 $line['amount'] = sprintf("%.2f", $value['chg']);
517 $line['adjust'] = sprintf("%.2f", ($value['adj'] ?? null));
518 $line['paid'] = sprintf("%.2f", $value['chg'] - $value['bal']);
519 $line['notice'] = $duncount + 1;
520 $line['detail'] = $value['dtl'];
521 $line['bill_date'] = $bdrow['bill_date'];
522 $stmt['lines'][] = $line;
523 $stmt['amount'] = sprintf("%.2f", $stmt['amount'] + $value['bal']);
524 $stmt['ins_paid'] = $stmt['ins_paid'] + ($value['ins'] ?? null);
527 // Record that this statement was run.
528 if (!$DEBUG && empty($_REQUEST['form_without'])) {
529 sqlStatement("UPDATE form_encounter SET " .
530 "last_stmt_date = ?, stmt_count = stmt_count + 1 " .
531 "WHERE id = ?", array($today, $row['id']));
533 $inv_count += 1;
534 if (!empty($_REQUEST['form_portalnotify'])) {
535 if (!is_auth_portal($stmt['pid'])) {
536 $alertmsg = xlt('Notification FAILED: Not Portal Authorized');
537 break;
539 $pvoice[] = $stmt;
540 // we don't want to send the portal multiple invoices, thus this. Last invoice for pid is summary.
541 if ($inv_pid[$inv_count] != $inv_pid[$inv_count + 1]) {
542 fwrite($fhprint, make_statement($stmt));
543 if (!notify_portal($stmt['pid'], $pvoice, $STMT_TEMP_FILE, $stmt['pid'] . "-" . $stmt['encounter'])) {
544 $alertmsg = xlt('Notification FAILED');
545 break;
548 $pvoice = array();
549 flush();
550 ftruncate($fhprint, 0);
551 } else {
552 continue;
554 } else {
555 if ($inv_pid[$inv_count] != ($inv_pid[$inv_count + 1] ?? null)) {
556 if ($_REQUEST['form_category'] == 'Due Pt' && (get_patient_balance($stmt['pid']) < 0)) {
557 // not printing statement if patient balance is less than zero even though
558 // a single encounter may have a balance
559 unset($stmt);
560 } else {
561 $tmp = make_statement($stmt);
562 if (empty($tmp)) {
563 $tmp = xlt("This EOB item does not meet minimum print requirements setup in Globals or there is an unknown error.") . " " . xlt("EOB Id") . ":" . text($inv_pid[$inv_count]) . " " . xlt("Encounter") . ":" . text($stmt['encounter']) . "\n";
564 $tmp .= "<br />\n\014<br /><br />";
566 fwrite($fhprint, $tmp);
567 // now save it to pt documents
568 $d = new Document();
569 $doc_pid = $inv_pid[$inv_count];
570 $invoice_category_id = 0;
571 $catrow = sqlQuery("SELECT id FROM categories WHERE name = ?", ['Invoices']);
572 if (!empty($catrow['id'])) {
573 $invoice_category_id = $catrow['id'];
575 // even if click download pdf the file content in $tmp is text
576 // set mimetype and fileext based on statement appearance
577 $isPdf = ($GLOBALS['statement_appearance'] == 1);
578 $fileext = $isPdf ? '.pdf' : '.txt';
579 $inv_filename = 'Invoice-' . date('Y-m-d-H:i:s') . $fileext;
580 $mimetype = $isPdf ? 'pdf' : 'text/plain';
581 if ($isPdf) {
582 $pdf2 = new mPDF(Config_Mpdf::getConfigMpdf());
583 if ($_SESSION['language_direction'] == 'rtl') {
584 $pdf2->SetDirectionality('rtl');
586 $pdf2->WriteHTML($tmp);
587 $tmp = $pdf2->Output('', 'S');
589 $invoice = $d->createDocument(
590 $doc_pid,
591 $invoice_category_id, // TBD: Make sure not 0
592 $inv_filename,
593 $mimetype,
594 $tmp
599 } // end while
601 if (!empty($stmt)) {
602 ++$stmt_count;
605 fclose($fhprint);
606 sleep(1);
607 // Download or print the file, as selected
608 if (!empty($_REQUEST['form_download'])) {
609 upload_file_to_client($STMT_TEMP_FILE);
610 } elseif ($_REQUEST['form_pdf']) {
611 upload_file_to_client_pdf($STMT_TEMP_FILE, $aPatientFirstName, $aPatientID, $usePatientNamePdf);
612 } elseif ($_REQUEST['form_email']) {
613 upload_file_to_client_email($stmt['pid'], $STMT_TEMP_FILE);
614 } elseif ($_REQUEST['form_portalnotify']) {
615 if ($alertmsg == "") {
616 $alertmsg = xl('Sending Invoice to Patient Portal Completed');
618 } else { // Must be print!
619 if ($DEBUG) {
620 $alertmsg = xl("Printing skipped; see test output in") . ' ' . $STMT_TEMP_FILE;
621 } else {
622 exec(escapeshellcmd($STMT_PRINT_CMD) . " " . escapeshellarg($STMT_TEMP_FILE));
623 if ($_REQUEST['form_without']) {
624 $alertmsg = xl('Now printing') . ' ' . $stmt_count . ' ' . xl('statements; invoices will not be updated.');
625 } else {
626 $alertmsg = xl('Now printing') . ' ' . $stmt_count . ' ' . xl('statements and updating invoices.');
628 } // end not debug
629 } // end not form_download
630 } // end statements requested
632 // let biller know no why statement was not generated
633 if (
636 !empty($_REQUEST['form_print']) ||
637 !empty($_REQUEST['form_download']) ||
638 !empty($_REQUEST['form_email']) ||
639 !empty($_REQUEST['form_pdf'])
640 ) || !empty($_REQUEST['form_portalnotify'])
641 ) && !$form_cb
643 echo "<script> alert(" . xlj('No invoices were checked.') . ");\n</script>";
646 <html>
647 <head>
648 <?php Header::setupHeader(['datetime-picker']); ?>
649 <title><?php echo xlt('EOB Posting - Search'); ?></title>
650 <script>
651 var mypcc = '1';
652 function reSubmit() {
653 opener.$('#btn-inv-search').click();
655 function editInvoice(e, id) {
656 e.preventDefault();
657 let url = './sl_eob_invoice.php?isPosting=1&id=' + encodeURIComponent(id);
658 <?php if (isset($_FILES['form_erafile']['size']) && !$_FILES['form_erafile']['size']) { ?>
659 dlgopen(url,'','modal-full',700,false,'', {
660 sizeHeight: 'full',
661 onClosed: 'reSubmit'
662 }); <?php } else { // keep era page up so can check on other remits ?>
663 dlgopen(url,'','modal-full',700,false,'', {
664 sizeHeight: 'full',
665 onClosed: ''
666 }); <?php } ?>
669 function checkAll(checked) {
670 var f = document.forms[0];
671 for (var i = 0; i < f.elements.length; ++i) {
672 var ename = f.elements[i].name;
673 if (ename.indexOf('form_cb[') == 0)
674 f.elements[i].checked = checked;
678 function persistCriteria(el, e) {
679 e.preventDefault();
680 let target = "sl_eob_search.posting_adj_disable";
681 let val = el.checked ? 'checked' : '';
682 $.post("./../../library/ajax/user_settings.php",
684 target: target,
685 setting: val,
686 csrf_token_form: <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>
691 function npopup(e, pid) {
692 e.preventDefault();
693 let url = 'sl_eob_patient_note.php?patient_id=' + encodeURIComponent(pid);
694 dlgopen(url, 'billnote', 'modal-sm', 275, '');
697 function toEncSummary(e, pid) {
698 e.preventDefault();
699 // Tabs only
700 top.restoreSession();
701 let encurl = 'patient_file/history/encounters.php?billing=1&issue=0&pagesize=20&pagestart=0';
702 let paturl = "patient_file/summary/demographics.php?set_pid=" + encodeURIComponent(pid);
703 parent.left_nav.loadFrame('pat2', 'pat', paturl);
704 // need a little time so can force a billing view
705 setTimeout(function(){parent.left_nav.loadFrame('enc2', 'enc', encurl);}, 3000);
708 $(function () {
709 $('.datepicker').datetimepicker({
710 <?php $datetimepicker_timepicker = false; ?>
711 <?php $datetimepicker_showseconds = false; ?>
712 <?php $datetimepicker_formatInput = false; ?>
713 <?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
714 <?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
718 </script>
719 <style>
720 @media only screen and (max-width: 768px) {
721 [class*="col-"] {
722 width: 100%;
723 text-align: left !Important;
727 @media only screen and (max-width: 1004px) and (min-width: 641px) {
728 .oe-large {
729 display: none;
732 .oe-small {
733 display: inline-block;
737 @media print {
738 body * {
739 visibility: hidden;
742 .modal-body, .modal-body * {
743 visibility: visible;
746 .modal-body {
747 position: absolute;
748 left: 0;
749 top: 0;
750 width: 100%;
751 height: 10000px;
754 </style>
755 <?php
756 $arrOeUiSettings = array(
757 'heading_title' => xl('EOB Posting - Search'),
758 'include_patient_name' => false,
759 'expandable' => true,
760 'expandable_files' => array('sl_eob_search_xpd'),//all file names need suffix _xpd
761 'action' => "reset",
762 'action_title' => "",
763 'action_href' => "sl_eob_search.php",//only for actions - reset, link or back
764 'show_help_icon' => true,
765 'help_file_name' => "sl_eob_help.php"
767 $oemr_ui = new OemrUI($arrOeUiSettings);
770 </head>
772 <body>
773 <div id="container_div" class="<?php echo attr($oemr_ui->oeContainer()); ?> mt-3">
774 <div class="row">
775 <div class="col-12">
776 <?php echo $oemr_ui->pageHeading() . "\r\n"; ?>
777 </div>
778 </div>
779 <div class="row">
780 <div class="col-lg">
781 <form id="formSearch" action="" enctype='multipart/form-data' method='post'>
782 <input type="hidden" name="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>"/>
783 <fieldset id="payment-allocate" class="oe-show-hide px-2">
784 <legend>
785 &nbsp;<?php echo xlt('Post Item'); ?><i id="payment-info-do-not-remove"> </i>
786 </legend>
787 <div class="form-row p-2">
788 <div class="form-group col-lg">
789 <label class="control-label" for="form_payer_id"> <?php echo xlt('Payer'); ?>:</label>
790 <?php
791 $insurancei = getInsuranceProviders();
792 echo " <select name='form_payer_id'id='form_payer_id' class='form-control'>\n";
793 echo " <option value='0'>-- " . xlt('Patient') . " --</option>\n";
794 foreach ($insurancei as $iid => $iname) {
795 echo "<option value='" . attr($iid) . "'";
796 if (!empty($_REQUEST['form_payer_id']) && ($iid == $_REQUEST['form_payer_id'])) {
797 echo " selected";
799 echo ">" . text($iname) . "</option>\n";
801 echo " </select>\n";
803 </div>
804 <div class="form-group col-lg">
805 <label class="control-label" for="form_source"><?php echo xlt('Source'); ?>:</label>
806 <input type='text' name='form_source' id='form_source' class='form-control' value='<?php echo attr($_REQUEST['form_source'] ?? ''); ?>' title='<?php echo xla("A check number or claim number to identify the payment"); ?>' />
807 </div>
808 <div class="form-group col-lg">
809 <label class="control-label" for="form_paydate"><?php echo xlt('Pay Date'); ?>:</label>
810 <input type='text' name='form_paydate' id='form_paydate' class='form-control datepicker' value='<?php echo attr($_REQUEST['form_paydate'] ?? ''); ?>' onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' title='<?php echo xla("Date of payment yyyy-mm-dd"); ?>' />
811 </div>
812 <div class="form-group col-lg">
813 <label class="control-label oe-large" for="form_deposit_date"><?php echo xlt('Deposit Date'); ?>:</label>
814 <label class="control-label oe-small" for="form_deposit_date"><?php echo xlt('Dep Date'); ?>:</label>
815 <input type='text' name='form_deposit_date' id='form_deposit_date' class='form-control datepicker' value='<?php echo attr($_REQUEST['form_deposit_date'] ?? ''); ?>' onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' title='<?php echo xla("Date of bank deposit yyyy-mm-dd"); ?>' />
816 </div>
817 <div class="form-group col-lg">
818 <label class="control-label" for="form_amount"><?php echo xlt('Amount'); ?>:</label>
819 <input type='text' name='form_amount' id='form_amount' class='form-control' value='<?php echo attr($_REQUEST['form_amount'] ?? ''); ?>' title='<?php echo xla("Paid amount that you will allocate"); ?>' />
820 </div>
821 <div class="form-group col-lg">
822 <label class="control-label oe-large" for="only_with_debt"><?php echo xlt('Pt Debt'); ?>:</label>
823 <label class="control-label oe-small" for="only_with_debt"><?php echo xlt('Debt'); ?>:</label>
824 <input <?php echo (!empty($_REQUEST['only_with_debt'])) ? 'checked=checked' : ''; ?> type="checkbox" name="only_with_debt" id="only_with_debt" />
825 </div>
826 </div>
827 </fieldset>
829 <fieldset class="px-2" id="search-upload">
830 <legend>
831 &nbsp;<span><?php echo xlt('Select Method'); ?></span>&nbsp;<i id='select-method-tooltip'
832 class="fa fa-info-circle oe-superscript" aria-hidden="true"></i>
834 <div id="radio-div" class="form-check form-check-inline oe-legend-radio">
835 <label class="radio-inline btn btn-sm btn-secondary">
836 <input type="radio" id="invoice_search" name="radio-search" onclick="" value="inv-search" /><?php echo xlt('Invoice Search'); ?>
837 </label>
838 <label class="radio-inline btn btn-sm btn-secondary">
839 <input type="radio" id="era_upload" name="radio-search" onclick="" value="era-upld" /><?php echo xlt('ERA Upload'); ?>
840 </label>
841 </div>
843 <input type="hidden" id="hid1" value="<?php echo xla('Invoice Search'); ?>" />
844 <input type="hidden" id="hid2" value="<?php echo xla('ERA Upload'); ?>" />
845 <input type="hidden" id="hid3" value="<?php echo xla('Select Method'); ?>" />
846 </legend>
847 <div class="oe-show-hide" id='inv-search'>
848 <div class="form-row p-2">
849 <div class="form-group col-lg">
850 <label class="control-label" for="form_name"><?php echo xlt('Name'); ?>:</label>
851 <input type='text' name='form_name' id='form_name' class='form-control' value='<?php echo attr($_REQUEST['form_name'] ?? ''); ?>' title='<?php echo xla("Any part of the patient name, or \"last,first\", or \"X-Y\""); ?>' placeholder='<?php echo xla('Last name, First name'); ?>' />
852 </div>
853 <div class="form-group col-lg">
854 <label class="control-label" for="form_pid"><?php echo xlt('Chart ID'); ?>:</label>
855 <input type='text' name='form_pid' id='form_pid' class='form-control' value='<?php echo attr($_REQUEST['form_pid'] ?? ''); ?>' title='<?php echo xla("Patient chart ID"); ?>' />
856 </div>
857 <div class="form-group col-lg">
858 <label class="control-label" for="form_encounter"><?php echo xlt('Encounter'); ?>:</label>
859 <input type='text' name='form_encounter' id='form_encounter' class='form-control' value='<?php echo attr($_REQUEST['form_encounter'] ?? ''); ?>' title='<?php echo xla("Encounter number"); ?>' />
860 </div>
861 <div class="form-group col-lg">
862 <label class="control-label oe-large" for="form_date"><?php echo xlt('Service Date From'); ?>:</label>
863 <label class="control-label oe-small" for="form_date"><?php echo xlt('Svc Date'); ?>:</label>
864 <input type='text' name='form_date' id='form_date' class='form-control datepicker' value='<?php echo attr($_REQUEST['form_date'] ?? ''); ?>' title='<?php echo xla("Date of service mm/dd/yyyy"); ?>' />
865 </div>
866 <div class="form-group col-lg">
867 <label class="control-label" for="form_to_date"><?php echo xlt('Service Date To'); ?>:</label>
868 <input type='text' name='form_to_date' id='form_to_date' class='form-control datepicker' value='<?php echo attr($_REQUEST['form_to_date'] ?? ''); ?>' title='<?php echo xla("Ending DOS mm/dd/yyyy if you wish to enter a range"); ?>' />
869 </div>
870 <div class="form-group col-lg" style="padding-right:0px">
871 <label class="control-label" for="type_name"><?php echo xlt('Type'); ?>:</label>
872 <select name='form_category' id='form_category' class='form-control'>
873 <?php
874 foreach (array(xl('Open'), xl('All'), xl('Due Pt'), xl('Due Ins')) as $value) {
875 echo " <option value='" . attr($value) . "'";
876 if (!empty($_REQUEST['form_category']) && ($_REQUEST['form_category'] == $value)) {
877 echo " selected";
879 echo ">" . text($value) . "</option>\n";
882 </select>
883 </div>
884 </div>
885 </div>
886 <div class="form-row oe-show-hide" id='era-upld' style="display: none">
887 <div class="form-group col-lg oe-file-div">
888 <div class="input-group">
889 <label class="input-group-prepend">
890 <span class="btn btn-secondary">Browse&hellip;<input type="file" id="uploadedfile" name="form_erafile" style="display: none;" /><input name="MAX_FILE_SIZE" type="hidden" value="5000000" /></span>
891 </label>
892 <input type="text" class="form-control" placeholder="<?php echo xla('Click Browse and select one Electronic Remittance Advice (ERA) file...'); ?>" readonly />
893 </div>
894 </div>
895 </div>
896 </fieldset>
898 <?php //can change position of buttons by creating a class 'position-override' and adding rule text-alig:center or right as the case may be in individual stylesheets ?>
899 <div class="form-row p-2">
900 <div class="form-group position-override oe-show-hide" id="search-btn">
901 <div class="btn-group" role="group">
902 <button type='submit' class="btn btn-primary btn-search oe-show-hide" name='form_search' id="btn-inv-search" value='Search'><?php echo xlt("Search"); ?></button>
903 <button type='submit' class="btn btn-primary btn-save oe-show-hide" name='form_search' id="btn-era-upld" value='Upload'><?php echo xlt("Upload"); ?></button>
904 </div>
905 </div>
906 </div>
907 <fieldset id="search-results" class="oe-show-hide px-2">
908 <legend><span><?php echo xlt('Search Results'); ?></span>
909 <div class="oe-pull-away oe-legend-radio">
910 <label class="checkbox-inline">
911 <input type="checkbox" id="posting_adj_disable" name="posting_adj_disable" onchange='persistCriteria(this, event)' title="<?php echo xlt("Disable automatically calculating balance adjustments for invoice posting") ?>" value="<?php echo attr($posting_adj_disable); ?>" <?php echo ' ' . attr($posting_adj_disable); ?> /><?php echo xlt('Disable Auto Adjustments'); ?>
912 </label>
913 </div>
914 </legend>
915 <div class="table-responsive">
916 <?php
917 if (!empty($_REQUEST['form_search']) || !empty($_REQUEST['form_print'])) {
918 if (!CsrfUtils::verifyCsrfToken($_REQUEST["csrf_token_form"])) {
919 CsrfUtils::csrfNotVerified();
922 $form_name = trim($_REQUEST['form_name']);
923 $form_pid = trim($_REQUEST['form_pid']);
924 $form_encounter = trim($_REQUEST['form_encounter']);
925 $form_date = fixDate($_REQUEST['form_date'], "");
926 $form_to_date = fixDate($_REQUEST['form_to_date'], "");
928 $where = "";
930 // Handle X12 835 file upload.
932 if ($_FILES['form_erafile']['size']) {
933 $tmp_name = $_FILES['form_erafile']['tmp_name'];
935 // Handle .zip extension if present. Probably won't work on Windows.
936 if (strtolower(substr($_FILES['form_erafile']['name'], -4)) == '.zip') {
937 rename($tmp_name, "$tmp_name.zip");
938 exec("unzip -p " . escapeshellarg($tmp_name . ".zip") . " > " . escapeshellarg($tmp_name));
939 unlink("$tmp_name.zip");
942 echo "<!-- Notes from ERA upload processing:\n";
943 $alertmsg .= ParseERA::parseERA($tmp_name, 'era_callback');
944 echo "-->\n";
945 $erafullname = $GLOBALS['OE_SITE_DIR'] . "/documents/era/$eraname.edi";
946 $edihname = $GLOBALS['OE_SITE_DIR'] . "/documents/edi/history/f835/$eraname.835";
948 if (is_file($erafullname)) {
949 $alertmsg .= "Warning: Set $eraname was already uploaded ";
950 if (is_file($GLOBALS['OE_SITE_DIR'] . "/documents/era/$eraname.html")) {
951 $alertmsg .= "and processed. ";
952 } else {
953 $alertmsg .= "but not yet processed. ";
956 rename($tmp_name, $erafullname);
957 copy($erafullname, $edihname);
958 } // End 835 upload
960 if ($eracount) {
961 // Note that ParseERA::parseERA() modified $eracount and $where.
962 if (!$where) {
963 $where = '1 = 2';
965 } else {
966 if ($form_name) {
967 if ($where) {
968 $where .= " AND ";
970 // Allow the last name to be followed by a comma and some part of a first name.
971 if (preg_match('/^(.*\S)\s*,\s*(.*)/', $form_name, $matches)) {
972 $where .= "p.lname LIKE '" . add_escape_custom($matches[1]) . "%' AND p.fname LIKE '" . add_escape_custom($matches[2]) . "%'";
973 // Allow a filter like "A-C" on the first character of the last name.
974 } elseif (preg_match('/^(\S)\s*-\s*(\S)$/', $form_name, $matches)) {
975 $tmp = '1 = 2';
976 while (ord($matches[1]) <= ord($matches[2])) {
977 $tmp .= " OR p.lname LIKE '" . add_escape_custom($matches[1]) . "%'";
978 $matches[1] = chr(ord($matches[1]) + 1);
980 $where .= "( $tmp ) ";
981 } else {
982 $where .= "p.lname LIKE '%" . add_escape_custom($form_name) . "%'";
985 if ($form_pid) {
986 if ($where) {
987 $where .= " AND ";
989 $where .= "f.pid = '" . add_escape_custom($form_pid) . "'";
991 if ($form_encounter) {
992 if ($where) {
993 $where .= " AND ";
995 $where .= "f.encounter = '" . add_escape_custom($form_encounter) . "'";
997 if ($form_date) {
998 if ($where) {
999 $where .= " AND ";
1001 if ($form_to_date) {
1002 $where .= "f.date >= '" . add_escape_custom($form_date) . "' AND f.date <= '" . add_escape_custom($form_to_date) . "'";
1003 } else {
1004 $where .= "f.date = '" . add_escape_custom($form_date) . "'";
1007 if (!$where) {
1008 if ($_REQUEST['form_category'] == 'All') {
1009 $alertmsg .= xlt("At least one search parameter is required if you select All.");
1010 } else {
1011 $where = "1 = 1";
1016 // Notes that as of release 4.1.1 the copays are stored
1017 // in the ar_activity table marked with a PCP in the account_code column.
1018 $query = "SELECT f.id, f.pid, f.encounter, f.date, " .
1019 "f.last_level_billed, f.last_level_closed, f.last_stmt_date, f.stmt_count, f.in_collection, " .
1020 "p.fname, p.mname, p.lname, p.pubpid, p.billing_note, " .
1021 "( SELECT SUM(b.fee) FROM billing AS b WHERE " .
1022 "b.pid = f.pid AND b.encounter = f.encounter AND " .
1023 "b.activity = 1 AND b.code_type != 'COPAY' ) AS charges, " .
1024 "( SELECT SUM(a.pay_amount) FROM ar_activity AS a WHERE " .
1025 "a.pid = f.pid AND a.encounter = f.encounter AND a.deleted IS NULL AND a.payer_type = 0 AND a.account_code = 'PCP')*-1 AS copays, " .
1026 "( SELECT SUM(a.pay_amount) FROM ar_activity AS a WHERE " .
1027 "a.pid = f.pid AND a.encounter = f.encounter AND a.deleted IS NULL AND a.account_code != 'PCP') AS payments, " .
1028 "( SELECT SUM(a.adj_amount) FROM ar_activity AS a WHERE " .
1029 "a.pid = f.pid AND a.encounter = f.encounter AND a.deleted IS NULL ) AS adjustments " .
1030 "FROM form_encounter AS f " .
1031 "JOIN patient_data AS p ON p.pid = f.pid " .
1032 "WHERE $where " .
1033 "ORDER BY p.lname, p.fname, p.mname, f.pid, f.encounter";
1035 // Note that unlike the SQL-Ledger case, this query does not weed
1036 // out encounters that are paid up. Also the use of sub-selects
1037 // will require MySQL 4.1 or greater.
1039 $num_invoices = 0;
1041 // removed if condition on alert message so biller can see what's in the era
1042 $t_res = sqlStatement($query);
1043 $num_invoices = sqlNumRows($t_res);
1045 if ($eracount && $num_invoices != $eracount) {
1046 $alertmsg .= "Of $eracount remittances, there are $num_invoices " .
1047 "matching encounters in OpenEMR. ";
1050 <table class="table table-striped table-sm">
1051 <thead>
1052 <tr>
1053 <th class="id dehead"><?php echo xlt('Billing Note'); ?></th>
1054 <th class="dehead">&nbsp;<?php echo xlt('Patient'); ?></th>
1055 <th class="dehead">&nbsp;<?php echo xlt('Invoice'); ?></th>
1056 <th class="dehead">&nbsp;<?php echo xlt('Svc Date'); ?></th>
1057 <th class="dehead">&nbsp;<?php echo xlt('Last Stmt'); ?></th>
1058 <th class="dehead text-right"><?php echo xlt('Charge'); ?>&nbsp;</th>
1059 <th class="dehead text-right"><?php echo xlt('Adjust'); ?>&nbsp;</th>
1060 <th class="dehead text-right"><?php echo xlt('Paid'); ?>&nbsp;</th>
1061 <th class="dehead text-right"><?php echo xlt('Balance'); ?>&nbsp;</th>
1062 <th class="dehead text-center"><?php echo xlt('Prv'); ?></th>
1063 <?php
1064 if (!$eracount) { ?>
1065 <th class="dehead text-left"><?php echo xlt('Sel'); ?></th>
1066 <th class="dehead text-center"><?php echo xlt('Email'); ?></th>
1067 <?php
1068 } ?>
1069 </tr>
1070 </thead>
1071 <?php
1072 $orow = -1;
1074 while ($row = sqlFetchArray($t_res)) {
1075 $balance = sprintf("%.2f", $row['charges'] + $row['copays'] - $row['payments'] - $row['adjustments']);
1076 //new filter only patients with debt.
1077 if (!empty($_REQUEST['only_with_debt']) && $balance <= 0) {
1078 continue;
1082 if ($_REQUEST['form_category'] != 'All' && $eracount == 0 && $balance == 0) {
1083 continue;
1086 // Determine if customer is in collections.
1088 $billnote = $row['billing_note'];
1089 $in_collections = stristr($billnote, 'IN COLLECTIONS') !== false
1090 || $row['in_collection'] == 1;
1092 // $duncount was originally supposed to be the number of times that
1093 // the patient was sent a statement for this invoice.
1095 $duncount = $row['stmt_count'];
1097 // But if we have not yet billed the patient, then compute $duncount as a
1098 // negative count of the number of insurance plans for which we have not
1099 // yet closed out insurance.
1101 if (!$duncount) {
1102 for ($i = 1; $i <= 3 && SLEOB::arGetPayerID($row['pid'], $row['date'], $i); ++$i) {
1104 $duncount = $row['last_level_closed'] + 1 - $i;
1107 $isdueany = ($balance > 0);
1109 // An invoice is now due from the patient if money is owed and we are
1110 // not waiting for insurance to pay.
1112 $isduept = ($duncount >= 0 && $isdueany && !$in_collections) ? " checked" : "";
1114 // Skip invoices not in the desired "Due..." category.
1116 if (substr($_REQUEST['form_category'], 0, 3) == 'Due' && !$isdueany) {
1117 continue;
1119 if ($_REQUEST['form_category'] == 'Due Ins' && ($duncount >= 0 || !$isdueany)) {
1120 continue;
1122 if ($_REQUEST['form_category'] == 'Due Pt' && ($duncount < 0 || !$isdueany)) {
1123 continue;
1126 $bgcolor = ((++$orow & 1) ? "#ffdddd" : "#ddddff");
1128 $svcdate = substr($row['date'], 0, 10);
1129 $last_stmt_date = empty($row['last_stmt_date']) ? '' : $row['last_stmt_date'];
1132 <tr>
1133 <td class="detail">
1134 <a href="#" class="btn btn-secondary btn-sm" onclick="npopup(event, <?php echo attr_js($row['pid']); ?>)"><?php echo text($row['pid']); ?></a>
1135 </td>
1136 <td class="detail">&nbsp;
1137 <a href="#" class="btn btn-secondary btn-sm" onclick="toEncSummary(event, <?php echo attr_js($row['pid']); ?>)"><?php echo text($row['lname']) . ', ' . text($row['fname']); ?></a>
1138 </td>
1139 <td class="detail">&nbsp;
1140 <a href="#" class="btn btn-secondary btn-sm" onclick="editInvoice(event,<?php echo attr_js($row['id']); ?>)"><?php echo text($row['pid']) . '.' . text($row['encounter']); ?></a>
1141 </td>
1142 <td class="detail">&nbsp;<?php echo text(oeFormatShortDate($svcdate)); ?></td>
1143 <td class="detail">
1144 &nbsp;<?php echo text(oeFormatShortDate($last_stmt_date)); ?></td>
1145 <td class="detail text-right"><?php echo text(FormatMoney::getBucks($row['charges'])); ?>&nbsp;
1146 </td>
1147 <td class="detail text-right"><?php echo text(FormatMoney::getBucks($row['adjustments'])); ?>
1148 &nbsp;
1149 </td>
1150 <td class="detail text-right"><?php echo text(FormatMoney::getBucks($row['payments'] - $row['copays'])); ?>
1151 &nbsp;
1152 </td>
1153 <td class="detail text-right"><?php echo text(FormatMoney::getBucks($balance)); ?>&nbsp;</td>
1154 <td class="detail text-center"><?php echo $duncount ? text($duncount) : "&nbsp;" ?></td>
1155 <?php if (!$eracount) { ?>
1156 <td class="detail text-left">
1157 <input type='checkbox'
1158 name='form_cb[<?php echo attr($row['id']) ?>]'<?php echo text($isduept); ?> />
1159 <?php
1160 if ($in_collections) {
1161 echo "<span class='font-weight-bold text-danger'>IC</span>";
1162 } ?>
1163 <?php
1164 if (function_exists('is_auth_portal') ? is_auth_portal($row['pid']) : false) {
1165 echo(' PPt');
1166 echo("<input type='hidden' name='form_invpids[" . attr($row['id']) . "][" . attr($row['pid']) . "]' />");
1167 $is_portal = true;
1168 } ?>
1169 </td>
1170 <?php } ?>
1171 <td class="detail text-left">
1172 <?php
1173 $patientData = sqlQuery("SELECT * FROM `patient_data` WHERE `pid`=?", array($row['pid']));
1174 if ($patientData['hipaa_allowemail'] == "YES" && $patientData['allow_patient_portal'] == "YES" && $patientData['hipaa_notice'] == "YES" && validEmail($patientData['email'])) {
1175 echo xlt("YES");
1176 } else {
1177 echo xlt("NO");
1180 </td>
1181 </tr>
1182 <?php
1183 } // end while
1184 } // end search/print logic
1186 </table>
1187 </div><!--End of table-responsive div-->
1188 </fieldset>
1189 <?php //can change position of buttons by creating a class 'position-override' and adding rule text-alig:center or right as the case may be in individual stylesheets ?>
1190 <div class="form-group clearfix">
1191 <div class="form-row text-left position-override oe-show-hide" id="statement-download">
1192 <div class="btn-group" role="group">
1193 <?php
1194 if ($eracount) { ?>
1195 <button type="button" class="btn btn-secondary btn-save" name="Submit" onclick='processERA()' value="<?php echo xla('Process ERA File'); ?>">
1196 <?php echo xlt('Process ERA File'); ?></button>
1197 <?php
1198 } else { ?>
1199 <button type="button" class="btn btn-secondary btn-save" name="Submit1" onclick='checkAll(true)'><?php echo xlt('Select All'); ?></button>
1200 <button type="button" class="btn btn-secondary btn-undo" name="Submit2" onclick='checkAll(false)'><?php echo xlt('Clear All'); ?></button>
1201 <?php if ($GLOBALS['statement_appearance'] != '1') { ?>
1202 <button type="submit" class="btn btn-secondary btn-print" name='form_print' value="<?php echo xla('Print Selected Statements'); ?>"><?php echo xlt('Print Selected'); ?></button>
1203 <button type="submit" class="btn btn-secondary btn-download" name='form_download' value="<?php echo xla('Download Selected Statements'); ?>"><?php echo xlt('Download Selected'); ?></button>
1204 <?php } ?>
1205 <button type="submit" class="btn btn-secondary btn-download" name='form_pdf' value="<?php echo xla('PDF Download Selected Statements'); ?>"><?php echo xlt('PDF Download Selected'); ?></button>
1206 <button type="submit" class="btn btn-secondary btn-mail" name='form_email' value="<?php echo xla('Email Selected Statements'); ?>"><?php echo xlt('Email Selected'); ?></button>
1207 <?php
1208 if (!empty($is_portal)) { ?>
1209 <button type="submit" class="btn btn-secondary btn-save" name='form_portalnotify' value="<?php echo xla('Notify via Patient Portal'); ?>"><?php echo xlt('Notify Patients Portal'); ?></button>
1210 <?php
1214 <label class="radio-inline btn"><?php echo xlt('Without Update'); ?>
1215 <input type='checkbox' name='form_without' value='1'/>
1216 </label>
1217 </div>
1218 </div>
1219 </div>
1220 </form>
1221 </div>
1222 </div>
1223 </div> <!--End of Container div-->
1224 <?php $oemr_ui->oeBelowContainerDiv();?>
1226 <script>
1228 function processERA() {
1229 var f = document.forms[0];
1230 var debug = f.form_without.checked ? '1' : '0';
1231 var paydate = f.form_paydate.value;
1232 window.open('sl_eob_process.php?eraname=' + <?php echo js_url($eraname); ?> + '&debug=' + encodeURIComponent(debug) + '&paydate=' + encodeURIComponent(paydate) + '&original=original' + '&csrf_token_form=' + <?php echo js_url(CsrfUtils::collectCsrfToken()); ?>, '_blank');
1233 return false;
1236 $(function () {
1237 //https://www.abeautifulsite.net/whipping-file-inputs-into-shape-with-bootstrap-3
1238 // We can attach the `fileselect` event to all file inputs on the page
1239 $(document).on('change', ':file', function () {
1240 var input = $(this),
1241 numFiles = input.get(0).files ? input.get(0).files.length : 1,
1242 label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
1243 input.trigger('fileselect', [numFiles, label]);
1246 // We can watch for our custom `fileselect` event like this
1247 $(function () {
1248 $(':file').on('fileselect', function (event, numFiles, label) {
1249 var input = $(this).parents('.input-group').find(':text'),
1250 log = numFiles > 1 ? numFiles + ' files selected' : label;
1252 if (input.length) {
1253 input.val(log);
1254 document.querySelector('#btn-era-upld').disabled = false;
1256 else {
1257 if (log) alert(log);
1262 //to dynamically show /hide relevant divs and change Fieldset legends
1263 $(function () {
1264 $("input[name=radio-search]").on("change", function () {
1265 let flip = $(this).val();
1266 $(".oe-show-hide").hide();
1267 $("#" + flip).show();
1268 if (flip == 'inv-search') {
1269 $("#search-upload").insertAfter("#payment-allocate");
1270 $('#payment-allocate').show();
1271 $('#search-btn').show();
1272 $('#btn-inv-search').show();
1273 var legend_text = $('#hid1').val();
1274 $('#search-upload').find('legend').find('span').text(legend_text);
1275 $('#select-method-tooltip').hide();
1277 else if (flip == 'era-upld') {
1278 $('#payment-allocate').hide();
1279 $('#search-btn').show();
1280 $('#btn-era-upld').show();
1281 document.querySelector('#btn-era-upld').disabled = true;
1282 var legend_text = $('#hid2').val();
1283 $('#search-upload').find('legend').find('span').text(legend_text);
1284 $('#select-method-tooltip').hide();
1286 else {
1287 $('#payment-allocate').hide();
1288 $('#search-btn').hide();
1289 var legend_text = $('#hid3').val();
1290 $('#search-upload').find('legend').find('span').text(legend_text);
1291 $('#select-method-tooltip').show();
1295 <?php if (empty($_REQUEST['form_search'])) { ?>
1296 $("#invoice_search").click();
1297 <?php } ?>
1299 <?php
1300 if ($alertmsg) {
1301 echo "alert('" . addslashes($alertmsg) . "');\n";
1304 $(function () {
1305 $('#select-method-tooltip').attr({"title": <?php echo xlj('Click on either the Invoice Search button on the far right, for manual entry or ERA Upload button for uploading an entire electronic remittance advice ERA file'); ?>, "data-toggle":"tooltip", "data-placement":"bottom"}).tooltip();
1307 </script>
1308 <?php
1309 // not a good idea to do translate. it's a constant so pulling sjp.
1310 if (!empty($_REQUEST['form_search']) && ($_REQUEST['form_search'] == "Search")) { ?>
1311 <script>
1312 $("#payment-allocate").insertAfter("#search-upload");
1313 $('#payment-allocate').show();
1314 $("#search-results").show();
1315 $("#statement-download").show();
1316 </script>
1317 <?php
1319 if (!empty($_REQUEST['form_search']) && ($_REQUEST['form_search'] == "Upload")) { ?>
1320 <script>
1321 $('#era-upld').show();
1322 $('#search-results').show();
1323 $("#statement-download").show();
1324 </script>
1325 <?php
1330 </body>
1331 </html>