2 // Copyright (C) 2005-2008 Rod Roark <rod@sunsetsystems.com>
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // This is the first of two pages to support posting of EOBs.
10 // The second is sl_eob_invoice.php.
12 include_once("../globals.php");
13 include_once("../../library/patient.inc");
14 include_once("../../library/sql-ledger.inc");
15 include_once("../../library/invoice_summary.inc.php");
16 include_once("../../custom/statement.inc.php");
17 include_once("../../library/parse_era.inc.php");
18 include_once("../../library/sl_eob.inc.php");
20 $DEBUG = 0; // set to 0 for production, 1 to test
22 $INTEGRATED_AR = $GLOBALS['oer_config']['ws_accounting']['enabled'] === 2;
29 // This is called back by parse_era() if we are processing X12 835's.
31 function era_callback(&$out) {
32 global $where, $eracount, $eraname;
33 // print_r($out); // debugging
35 // $eraname = $out['isa_control_number'];
36 $eraname = $out['gs_date'] . '_' . ltrim($out['isa_control_number'], '0') .
37 '_' . ltrim($out['payer_id'], '0');
38 list($pid, $encounter, $invnumber) = slInvoiceNumber($out);
40 if ($pid && $encounter) {
41 if ($where) $where .= ' OR ';
42 $where .= "invnumber = '$invnumber'";
46 function bucks($amount) {
48 printf("%.2f", $amount);
51 $today = date("Y-m-d");
55 // Print statements if requested.
57 if ($_POST['form_print'] && $_POST['form_cb']) {
59 $fhprint = fopen($STMT_TEMP_FILE, 'w');
62 foreach ($_POST['form_cb'] as $key => $value) $where .= " OR f.id = $key";
63 $where = substr($where, 4);
65 $res = sqlStatement("SELECT " .
66 "f.id, f.date, f.pid, f.encounter, f.stmt_count, f.last_stmt_date, " .
67 "p.fname, p.mname, p.lname, p.street, p.city, p.state, p.postal_code " .
68 "FROM form_encounter AS f, patient_data AS p " .
69 "WHERE ( $where ) AND " .
71 "ORDER BY p.lname, p.fname, f.pid, f.date, f.encounter");
76 // This loops once for each invoice/encounter.
78 while ($row = sqlFetchArray($res)) {
79 $svcdate = substr($row['date'], 0, 10);
80 $duedate = $svcdate; // TBD?
81 $duncount = $row['stmt_count'];
83 // If this is a new patient then print the pending statement
84 // and start a new one. This is an associative array:
87 // pid = OpenEMR patient ID
88 // patient = patient name
89 // amount = total amount due
90 // adjust = adjustments (already applied to amount)
91 // duedate = due date of the oldest included invoice
92 // age = number of days from duedate to today
93 // to = array of addressee name/address lines
95 // dos = date of service "yyyy-mm-dd"
97 // amount = charge less adjustments
99 // notice = 1 for first notice, 2 for second, etc.
100 // detail = array of details, see invoice_summary.inc.php
102 if ($stmt['cid'] != $row['pid']) {
103 if (!empty($stmt)) ++
$stmt_count;
104 fwrite($fhprint, create_statement($stmt));
105 $stmt['cid'] = $row['pid'];
106 $stmt['pid'] = $row['pid'];
107 $stmt['patient'] = $row['fname'] . ' ' . $row['lname'];
108 $stmt['to'] = array($row['fname'] . ' ' . $row['lname']);
109 if ($row['street']) $stmt['to'][] = $row['street'];
110 $stmt['to'][] = $row['city'] . ", " . $row['state'] . " " . $row['postal_code'];
111 $stmt['lines'] = array();
112 $stmt['amount'] = '0.00';
113 $stmt['today'] = $today;
114 $stmt['duedate'] = $duedate;
116 // Report the oldest due date.
117 if ($duedate < $stmt['duedate']) {
118 $stmt['duedate'] = $duedate;
122 // Recompute age at each invoice.
123 $stmt['age'] = round((strtotime($today) - strtotime($stmt['duedate'])) /
126 $invlines = ar_get_invoice_summary($row['pid'], $row['encounter'], true);
127 foreach ($invlines as $key => $value) {
129 $line['dos'] = $svcdate;
130 $line['desc'] = ($key == 'CO-PAY') ?
"Patient Payment" : "Procedure $key";
131 $line['amount'] = sprintf("%.2f", $value['chg']);
132 $line['adjust'] = sprintf("%.2f", $value['adj']);
133 $line['paid'] = sprintf("%.2f", $value['chg'] - $value['bal']);
134 $line['notice'] = $duncount +
1;
135 $line['detail'] = $value['dtl'];
136 $stmt['lines'][] = $line;
137 $stmt['amount'] = sprintf("%.2f", $stmt['amount'] +
$value['bal']);
140 // Record that this statement was run.
141 if (! $DEBUG && ! $_POST['form_without']) {
142 sqlStatement("UPDATE form_encounter SET " .
143 "last_stmt_date = '$today', stmt_count = stmt_count + 1 " .
144 "WHERE id = " . $row['id']);
148 if (!empty($stmt)) ++
$stmt_count;
149 fwrite($fhprint, create_statement($stmt));
152 $alertmsg = xl("Printing skipped; see test output in ") . $STMT_TEMP_FILE;
154 exec("$STMT_PRINT_CMD $STMT_TEMP_FILE");
155 if ($_POST['form_without']) {
156 $alertmsg = xl("Now printing $stmt_count statements; encounters will not be updated.");
158 $alertmsg = xl("Now printing $stmt_count statements and updating encounters.");
161 } // end statements requested
162 } // end $INTEGRATED_AR
166 // This will be true starting with SQL-Ledger 2.8.x:
167 $got_address_table = SLQueryValue("SELECT count(*) FROM pg_tables WHERE " .
168 "schemaname = 'public' AND tablename = 'address'");
170 // Print statements if requested.
172 if ($_POST['form_print'] && $_POST['form_cb']) {
174 $fhprint = fopen($STMT_TEMP_FILE, 'w');
177 foreach ($_POST['form_cb'] as $key => $value) $where .= " OR ar.id = $key";
178 $where = substr($where, 4);
180 // Sort by patient so that multiple invoices can be
181 // represented on a single statement.
182 if ($got_address_table) {
183 $res = SLQuery("SELECT ar.*, customer.name, " .
184 "address.address1, address.address2, " .
185 "address.city, address.state, address.zipcode, " .
186 "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS fname, " .
187 "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS lname " .
188 "FROM ar, customer, address WHERE ( $where ) AND " .
189 "customer.id = ar.customer_id AND " .
190 "address.trans_id = ar.customer_id " .
191 "ORDER BY lname, fname, ar.customer_id, ar.transdate");
194 $res = SLQuery("SELECT ar.*, customer.name, " .
195 "customer.address1, customer.address2, " .
196 "customer.city, customer.state, customer.zipcode, " .
197 "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS lname, " .
198 "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS fname " .
199 "FROM ar, customer WHERE ( $where ) AND " .
200 "customer.id = ar.customer_id " .
201 "ORDER BY lname, fname, ar.customer_id, ar.transdate");
203 if ($sl_err) die($sl_err);
208 for ($irow = 0; $irow < SLRowCount($res); ++
$irow) {
209 $row = SLGetRow($res, $irow);
211 // Determine the date of service. An 8-digit encounter number is
212 // presumed to be a date of service imported during conversion.
213 // Otherwise look it up in the form_encounter table.
216 list($pid, $encounter) = explode(".", $row['invnumber']);
217 if (strlen($encounter) == 8) {
218 $svcdate = substr($encounter, 0, 4) . "-" . substr($encounter, 4, 2) .
219 "-" . substr($encounter, 6, 2);
220 } else if ($encounter) {
221 $tmp = sqlQuery("SELECT date FROM form_encounter WHERE " .
222 "encounter = $encounter");
223 $svcdate = substr($tmp['date'], 0, 10);
226 // How many times have we dunned them for this invoice?
227 $intnotes = trim($row['intnotes']);
228 $duncount = substr_count(strtolower($intnotes), "statement sent");
230 // If this is a new patient then print the pending statement
231 // and start a new one. This is an associative array:
233 // cid = SQL-Ledger customer ID
234 // pid = OpenEMR patient ID
235 // patient = patient name
236 // amount = total amount due
237 // adjust = adjustments (already applied to amount)
238 // duedate = due date of the oldest included invoice
239 // age = number of days from duedate to today
240 // to = array of addressee name/address lines
242 // dos = date of service "yyyy-mm-dd"
243 // desc = description
244 // amount = charge less adjustments
245 // paid = amount paid
246 // notice = 1 for first notice, 2 for second, etc.
247 // detail = array of details, see invoice_summary.inc.php
249 if ($stmt['cid'] != $row['customer_id']) {
250 if (!empty($stmt)) ++
$stmt_count;
251 fwrite($fhprint, create_statement($stmt));
252 $stmt['cid'] = $row['customer_id'];
255 if ($got_address_table) {
256 $stmt['patient'] = $row['fname'] . ' ' . $row['lname'];
257 $stmt['to'] = array($row['fname'] . ' ' . $row['lname']);
259 $stmt['patient'] = $row['name'];
260 $stmt['to'] = array($row['name']);
263 if ($row['address1']) $stmt['to'][] = $row['address1'];
264 if ($row['address2']) $stmt['to'][] = $row['address2'];
265 $stmt['to'][] = $row['city'] . ", " . $row['state'] . " " . $row['zipcode'];
266 $stmt['lines'] = array();
267 $stmt['amount'] = '0.00';
268 $stmt['today'] = $today;
269 $stmt['duedate'] = $row['duedate'];
271 // Report the oldest due date.
272 if ($row['duedate'] < $stmt['duedate']) {
273 $stmt['duedate'] = $row['duedate'];
277 $stmt['age'] = round((strtotime($today) - strtotime($stmt['duedate'])) /
280 $invlines = get_invoice_summary($row['id'], true); // true added by Rod 2006-06-09
281 foreach ($invlines as $key => $value) {
283 $line['dos'] = $svcdate;
284 $line['desc'] = ($key == 'CO-PAY') ?
"Patient Payment" : "Procedure $key";
285 $line['amount'] = sprintf("%.2f", $value['chg']);
286 $line['adjust'] = sprintf("%.2f", $value['adj']);
287 $line['paid'] = sprintf("%.2f", $value['chg'] - $value['bal']);
288 $line['notice'] = $duncount +
1;
289 $line['detail'] = $value['dtl']; // Added by Rod 2006-06-09
290 $stmt['lines'][] = $line;
291 $stmt['amount'] = sprintf("%.2f", $stmt['amount'] +
$value['bal']);
294 // Record something in ar.intnotes about this statement run.
295 if ($intnotes) $intnotes .= "\n";
296 $intnotes = addslashes($intnotes . "Statement sent $today");
297 if (! $DEBUG && ! $_POST['form_without']) {
298 SLQuery("UPDATE ar SET intnotes = '$intnotes' WHERE id = " . $row['id']);
299 if ($sl_err) die($sl_err);
303 if (!empty($stmt)) ++
$stmt_count;
304 fwrite($fhprint, create_statement($stmt));
307 $alertmsg = xl("Printing skipped; see test output in ").$STMT_TEMP_FILE;
309 exec("$STMT_PRINT_CMD $STMT_TEMP_FILE");
310 if ($_POST['form_without']) {
311 $alertmsg = xl("Now printing $stmt_count statements; invoices will not be updated.");
313 $alertmsg = xl("Now printing $stmt_count statements and updating invoices.");
316 } // end statements requested
317 } // end not $INTEGRATED_AR
321 <?
html_header_show();?
>
322 <link rel
=stylesheet href
="<?echo $css_header;?>" type
="text/css">
323 <title
><?php
xl('EOB Posting - Search','e'); ?
></title
>
324 <script type
="text/javascript" src
="../../library/textformat.js"></script
>
326 <script language
="JavaScript">
330 function checkAll(checked
) {
331 var f
= document
.forms
[0];
332 for (var i
= 0; i
< f
.elements
.length
; ++i
) {
333 var ename
= f
.elements
[i
].name
;
334 if (ename
.indexOf('form_cb[') == 0)
335 f
.elements
[i
].checked
= checked
;
339 function npopup(pid
) {
340 window
.open('sl_eob_patient_note.php?patient_id=' + pid
, '_blank', 'width=500,height=250,resizable=1');
348 <body leftmargin
='0' topmargin
='0' marginwidth
='0' marginheight
='0'>
351 <form method
='post' action
='sl_eob_search.php' enctype
='multipart/form-data'>
353 <table border
='0' cellpadding
='5' cellspacing
='0'>
357 if ($INTEGRATED_AR) {
358 // Identify the payer to support resumable posting sessions.
360 echo " " . xl('Payer') . ":\n";
363 $insurancei = getInsuranceProviders();
364 echo " <select name='form_payer_id'>\n";
365 echo " <option value='0'>-- " . xl('Patient') . " --</option>\n";
366 foreach ($insurancei as $iid => $iname) {
367 echo "<option value='$iid'";
368 if ($iid == $_POST['form_payer_id']) echo " selected";
369 echo ">" . $iname . "</option>\n";
377 <?php
xl('Source:','e'); ?
>
380 <input type
='text' name
='form_source' size
='10' value
='<?php echo $_POST['form_source
']; ?>'
381 title
='<?php xl("A check number or claim number to identify the payment","e"); ?>'>
384 <?php
xl('Pay Date:','e'); ?
>
387 <input type
='text' name
='form_paydate' size
='10' value
='<?php echo $_POST['form_paydate
']; ?>'
388 onkeyup
='datekeyup(this,mypcc)' onblur
='dateblur(this,mypcc)'
389 title
='<?php xl("Date of payment yyyy-mm-dd","e"); ?>'>
392 <?php
if ($INTEGRATED_AR) { // include deposit date ?>
394 <?php
xl('Deposit Date:','e'); ?
>
397 <input type
='text' name
='form_deposit_date' size
='10' value
='<?php echo $_POST['form_deposit_date
']; ?>'
398 onkeyup
='datekeyup(this,mypcc)' onblur
='dateblur(this,mypcc)'
399 title
='<?php xl("Date of bank deposit yyyy-mm-dd","e"); ?>'>
404 <?php
xl('Amount:','e'); ?
>
407 <input type
='text' name
='form_amount' size
='10' value
='<?php echo $_POST['form_amount
']; ?>'
408 title
='<?php xl("Paid amount that you will allocate","e"); ?>'>
411 <a href
='sl_eob_help.php' target
='_blank'><?php
xl('Help','e'); ?
></a
>
417 <table border
='0' cellpadding
='5' cellspacing
='0'>
419 <tr bgcolor
='#ddddff'>
421 <?php
xl('Name:','e'); ?
>
424 <input type
='text' name
='form_name' size
='10' value
='<?php echo $_POST['form_name
']; ?>'
425 title
='<?php xl("Any part of the patient name, or \"last,first\", or \"X-Y\"","e"); ?>'>
428 <?php
xl('Chart ID:','e'); ?
>
431 <input type
='text' name
='form_pid' size
='10' value
='<?php echo $_POST['form_pid
']; ?>'
432 title
='<?php xl("Patient chart ID","e"); ?>'>
435 <?php
xl('Encounter:','e'); ?
>
438 <input type
='text' name
='form_encounter' size
='10' value
='<?php echo $_POST['form_encounter
']; ?>'
439 title
='<?php xl("Encounter number","e"); ?>'>
442 <?php
xl('Svc Date:','e'); ?
>
445 <input type
='text' name
='form_date' size
='10' value
='<?php echo $_POST['form_date
']; ?>'
446 title
='<?php xl("Date of service mm/dd/yyyy","e"); ?>'>
449 <?php
xl('To:','e'); ?
>
452 <input type
='text' name
='form_to_date' size
='10' value
='<?php echo $_POST['form_to_date
']; ?>'
453 title
='<?php xl("Ending DOS mm/dd/yyyy if you wish to enter a range","e"); ?>'>
456 <select name
='form_category'>
458 foreach (array(xl('Open'), xl('All'), xl('Due Pt'), xl('Due Ins')) as $value) {
459 echo " <option value='$value'";
460 if ($_POST['form_category'] == $value) echo " selected";
461 echo ">$value</option>\n";
467 <input type
='submit' name
='form_search' value
='<?php xl("Search","e"); ?>'>
471 <!-- Support
for X12
835 upload
-->
472 <tr bgcolor
='#ddddff'>
474 <?php
xl('Or upload ERA file:','e'); ?
>
475 <input type
="hidden" name
="MAX_FILE_SIZE" value
="5000000" />
476 <input name
="form_erafile" type
="file" />
481 <td height
="1" colspan
="10">
488 if ($_POST['form_search'] ||
$_POST['form_print']) {
489 $form_name = trim($_POST['form_name']);
490 $form_pid = trim($_POST['form_pid']);
491 $form_encounter = trim($_POST['form_encounter']);
492 $form_date = fixDate($_POST['form_date'], "");
493 $form_to_date = fixDate($_POST['form_to_date'], "");
497 // Handle X12 835 file upload.
499 if ($_FILES['form_erafile']['size']) {
500 $tmp_name = $_FILES['form_erafile']['tmp_name'];
502 // Handle .zip extension if present. Probably won't work on Windows.
503 if (strtolower(substr($_FILES['form_erafile']['name'], -4)) == '.zip') {
504 rename($tmp_name, "$tmp_name.zip");
505 exec("unzip -p $tmp_name.zip > $tmp_name");
506 unlink("$tmp_name.zip");
509 echo "<!-- Notes from ERA upload processing:\n";
510 $alertmsg .= parse_era($tmp_name, 'era_callback');
512 $erafullname = "$webserver_root/era/$eraname.edi";
514 if (is_file($erafullname)) {
515 $alertmsg .= "Warning: Set $eraname was already uploaded ";
516 if (is_file("$webserver_root/era/$eraname.html"))
517 $alertmsg .= "and processed. ";
519 $alertmsg .= "but not yet processed. ";
521 rename($tmp_name, $erafullname);
524 if ($INTEGRATED_AR) {
526 // Note that parse_era() modified $eracount and $where.
527 if (! $where) $where = '1 = 2';
531 if ($where) $where .= " AND ";
532 // Allow the last name to be followed by a comma and some part of a first name.
533 if (preg_match('/^(.*\S)\s*,\s*(.*)/', $form_name, $matches)) {
534 $where .= "p.lname LIKE '" . $matches[1] . "%' AND p.fname LIKE '" . $matches[1] . "%'";
535 // Allow a filter like "A-C" on the first character of the last name.
536 } else if (preg_match('/^(\S)\s*-\s*(\S)$/', $form_name, $matches)) {
538 while (ord($matches[1]) <= ord($matches[2])) {
539 $tmp .= " OR p.lname LIKE '" . $matches[1] . "%'";
540 $matches[1] = chr(ord($matches[1]) +
1);
542 $where .= "( $tmp ) ";
544 $where .= "p.lname LIKE '%$form_name%'";
548 if ($where) $where .= " AND ";
549 $where .= "f.pid = '$form_pid'";
551 if ($form_encounter) {
552 if ($where) $where .= " AND ";
553 $where .= "f.encounter = '$form_encounter'";
556 if ($where) $where .= " AND ";
558 $where .= "f.date >= '$form_date' AND f.date <= '$form_to_date'";
561 $where .= "f.date = '$form_date'";
565 if ($_POST['form_category'] == 'All') {
566 die("At least one search parameter is required if you select All.");
573 $query = "SELECT f.id, f.pid, f.encounter, f.date, " .
574 "f.last_level_billed, f.last_level_closed, f.stmt_count, " .
575 "p.fname, p.mname, p.lname, p.pubpid, p.genericname2, p.genericval2, " .
576 "( SELECT SUM(b.fee) FROM billing AS b WHERE " .
577 "b.pid = f.pid AND b.encounter = f.encounter AND " .
578 "b.activity = 1 AND b.code_type != 'COPAY' ) AS charges, " .
579 "( SELECT SUM(b.fee) FROM billing AS b WHERE " .
580 "b.pid = f.pid AND b.encounter = f.encounter AND " .
581 "b.activity = 1 AND b.code_type = 'COPAY' ) AS copays, " .
582 "( SELECT SUM(a.pay_amount) FROM ar_activity AS a WHERE " .
583 "a.pid = f.pid AND a.encounter = f.encounter ) AS payments, " .
584 "( SELECT SUM(a.adj_amount) FROM ar_activity AS a WHERE " .
585 "a.pid = f.pid AND a.encounter = f.encounter ) AS adjustments " .
586 "FROM form_encounter AS f " .
587 "JOIN patient_data AS p ON p.pid = f.pid " .
589 "ORDER BY p.lname, p.fname, p.mname, f.pid, f.encounter";
591 // Note that unlike the SQL-Ledger case, this query does not weed
592 // out encounters that are paid up. Also the use of sub-selects
593 // will require MySQL 4.1 or greater.
595 // echo "<!-- $query -->\n"; // debugging
597 $t_res = sqlStatement($query);
599 $num_invoices = mysql_num_rows($t_res);
600 if ($eracount && $num_invoices != $eracount) {
601 $alertmsg .= "Of $eracount remittances, there are $num_invoices " .
602 "matching encounters in OpenEMR. ";
604 } // end $INTEGRATED_AR
607 // Note that parse_era() modified $eracount and $where.
608 if (! $where) $where = '1 = 2';
612 if ($where) $where .= " AND ";
613 // Allow the last name to be followed by a comma and some part of a first name.
614 if (preg_match('/^(.*\S)\s*,\s*(.*)/', $form_name, $matches)) {
615 $where .= "customer.name ILIKE '" . $matches[2] . '% ' . $matches[1] . "%'";
616 // Allow a filter like "A-C" on the first character of the last name.
617 } else if (preg_match('/^(\S)\s*-\s*(\S)$/', $form_name, $matches)) {
619 while (ord($matches[1]) <= ord($matches[2])) {
620 // $tmp .= " OR customer.name ILIKE '% " . $matches[1] . "%'";
621 // Fixing the above which was also matching on middle names:
622 $tmp .= " OR customer.name ~* ' " . $matches[1] . "[A-Z]*$'";
623 $matches[1] = chr(ord($matches[1]) +
1);
625 $where .= "( $tmp ) ";
627 $where .= "customer.name ILIKE '%$form_name%'";
630 if ($form_pid && $form_encounter) {
631 if ($where) $where .= " AND ";
632 $where .= "ar.invnumber = '$form_pid.$form_encounter'";
634 else if ($form_pid) {
635 if ($where) $where .= " AND ";
636 $where .= "ar.invnumber LIKE '$form_pid.%'";
638 else if ($form_encounter) {
639 if ($where) $where .= " AND ";
640 $where .= "ar.invnumber like '%.$form_encounter'";
644 if ($where) $where .= " AND ";
645 $date1 = substr($form_date, 0, 4) . substr($form_date, 5, 2) .
646 substr($form_date, 8, 2);
648 $date2 = substr($form_to_date, 0, 4) . substr($form_to_date, 5, 2) .
649 substr($form_to_date, 8, 2);
650 $where .= "((CAST (substring(ar.invnumber from position('.' in ar.invnumber) + 1 for 8) AS integer) " .
651 "BETWEEN '$date1' AND '$date2')";
652 $tmp = "date >= '$form_date' AND date <= '$form_to_date'";
655 // This catches old converted invoices where we have no encounters:
656 $where .= "(ar.invnumber LIKE '%.$date1'";
657 $tmp = "date = '$form_date'";
659 // Pick out the encounters from MySQL with the desired DOS:
660 $rez = sqlStatement("SELECT pid, encounter FROM form_encounter WHERE $tmp");
661 while ($row = sqlFetchArray($rez)) {
662 $where .= " OR ar.invnumber = '" . $row['pid'] . "." . $row['encounter'] . "'";
668 if ($_POST['form_category'] == 'All') {
669 die("At least one search parameter is required if you select All.");
676 $query = "SELECT ar.id, ar.invnumber, ar.duedate, ar.amount, ar.paid, " .
677 "ar.intnotes, ar.notes, ar.shipvia, customer.name, customer.id AS custid, ";
678 if ($got_address_table) $query .=
679 "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS lname, " .
680 "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS fname, ";
682 "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS lname, " .
683 "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS fname, ";
685 "(SELECT SUM(invoice.sellprice * invoice.qty) FROM invoice WHERE " .
686 "invoice.trans_id = ar.id AND invoice.sellprice > 0) AS charges, " .
687 "(SELECT SUM(invoice.sellprice * invoice.qty) FROM invoice WHERE " .
688 "invoice.trans_id = ar.id AND invoice.sellprice < 0) AS adjustments " .
689 "FROM ar, customer WHERE ( $where ) AND customer.id = ar.customer_id ";
690 if ($_POST['form_category'] != 'All' && !$eracount) {
691 $query .= "AND ar.amount != ar.paid ";
692 // if ($_POST['form_category'] == 'Due') {
693 // $query .= "AND ar.duedate <= CURRENT_DATE ";
696 $query .= "ORDER BY lname, fname, ar.invnumber";
698 // echo "<!-- $query -->\n"; // debugging
700 $t_res = SLQuery($query);
701 if ($sl_err) die($sl_err);
703 $num_invoices = SLRowCount($t_res);
704 if ($eracount && $num_invoices != $eracount) {
705 $alertmsg .= "Of $eracount remittances, there are $num_invoices " .
706 "matching claims in OpenEMR. ";
709 } // end not $INTEGRATED_AR
712 <table border
='0' cellpadding
='1' cellspacing
='2' width
='98%'>
714 <tr bgcolor
="#dddddd">
716  
;<?php
xl('Patient','e'); ?
>
719  
;<?php
xl('Invoice','e'); ?
>
722  
;<?php
xl('Svc Date','e'); ?
>
725  
;<?php
xl('Due Date','e'); ?
>
727 <td
class="dehead" align
="right">
728 <?php
xl('Charge','e'); ?
> 
;
730 <td
class="dehead" align
="right">
731 <?php
xl('Adjust','e'); ?
> 
;
733 <td
class="dehead" align
="right">
734 <?php
xl('Paid','e'); ?
> 
;
736 <td
class="dehead" align
="right">
737 <?php
xl('Balance','e'); ?
> 
;
739 <td
class="dehead" align
="center">
740 <?php
xl('Prv','e'); ?
>
742 <?php
if (!$eracount) { ?
>
743 <td
class="dehead" align
="left">
744 <?php
xl('Sel','e'); ?
>
752 if ($INTEGRATED_AR) {
753 while ($row = sqlFetchArray($t_res)) {
754 $balance = $row['charges'] +
$row['copays'] - $row['payments'] - $row['adjustments'];
756 if ($_POST['form_category'] != 'All' && !$eracount && !$balance) continue;
758 // $duncount was originally supposed to be the number of times that
759 // the patient was sent a statement for this invoice.
761 $duncount = $row['stmt_count'];
763 // But if we have not yet billed the patient, then compute $duncount as a
764 // negative count of the number of insurance plans for which we have not
765 // yet closed out insurance.
768 for ($i = 1; $i <= 3 && arGetPayerID($row['pid'], $row['date'], $i); ++
$i) ;
769 $duncount = $row['last_level_closed'] +
1 - $i;
772 $isdueany = ($balance > 0);
774 // An invoice is now due from the patient if money is owed and we are
775 // not waiting for insurance to pay.
777 $isduept = ($duncount >= 0 && $isdueany) ?
" checked" : "";
779 // Skip invoices not in the desired "Due..." category.
781 if (substr($_POST['form_category'], 0, 3) == 'Due' && !$isdueany) continue;
782 if ($_POST['form_category'] == 'Due Ins' && ($duncount >= 0 ||
!$isdueany)) continue;
783 if ($_POST['form_category'] == 'Due Pt' && ($duncount < 0 ||
!$isdueany)) continue;
785 $bgcolor = ((++
$orow & 1) ?
"#ffdddd" : "#ddddff");
787 $svcdate = substr($row['date'], 0, 10);
789 // Determine if customer is in collections.
791 $billnote = ($row['genericname2'] == 'Billing') ?
$row['genericval2'] : '';
792 $in_collections = stristr($billnote, 'IN COLLECTIONS') !== false;
794 <tr bgcolor
='<?php echo $bgcolor ?>'>
796  
;<a href
="" onclick
="return npopup(<?php echo $pid ?>)"
797 ><?php
echo $row['lname'] . ', ' . $row['fname']; ?
></a
>
800  
;<a href
="sl_eob_invoice.php?id=<?php echo $row['id'] ?>"
801 target
="_blank"><?php
echo $row['pid'] . '.' . $row['encounter']; ?
></a
>
804  
;<?php
echo $svcdate ?
>
809 <td
class="detail" align
="right">
810 <?php
bucks($row['charges']) ?
> 
;
812 <td
class="detail" align
="right">
813 <?php
bucks($row['adjustments']) ?
> 
;
815 <td
class="detail" align
="right">
816 <?php
bucks($row['payments'] - $row['copays']); ?
> 
;
818 <td
class="detail" align
="right">
819 <?php
bucks($row['charges'] - $row['adjustments'] - $row['payments'] +
$row['copays']); ?
> 
;
821 <td
class="detail" align
="center">
822 <?php
echo $duncount ?
$duncount : " " ?
>
824 <?php
if (!$eracount) { ?
>
825 <td
class="detail" align
="left">
826 <input type
='checkbox' name
='form_cb[<?php echo($row['id
']) ?>]'<?php
echo $isduept ?
> />
827 <?php
if ($in_collections) echo "<b><font color='red'>IC</font></b>"; ?
>
833 } // end $INTEGRATED_AR
835 else { // not $INTEGRATED_AR
836 for ($irow = 0; $irow < $num_invoices; ++
$irow) {
837 $row = SLGetRow($t_res, $irow);
839 // $duncount was originally supposed to be the number of times that
840 // the patient was sent a statement for this invoice.
842 $duncount = substr_count(strtolower($row['intnotes']), "statement sent");
844 // But if we have not yet billed the patient, then compute $duncount as a
845 // negative count of the number of insurance plans for which we have not
846 // yet closed out insurance.
849 $insgot = strtolower($row['notes']);
850 $inseobs = strtolower($row['shipvia']);
851 foreach (array('ins1', 'ins2', 'ins3') as $value) {
852 if (strpos($insgot, $value) !== false &&
853 strpos($inseobs, $value) === false)
858 // $isdue = ($row['duedate'] <= $today && $row['amount'] > $row['paid']) ? " checked" : "";
860 $isdueany = sprintf("%.2f",$row['amount']) > sprintf("%.2f",$row['paid']);
862 // An invoice is now due from the patient if money is owed and we are
863 // not waiting for insurance to pay. We no longer look at the due date
866 $isduept = ($duncount >= 0 && $isdueany) ?
" checked" : "";
868 // Skip invoices not in the desired "Due..." category.
870 if (substr($_POST['form_category'], 0, 3) == 'Due' && !$isdueany) continue;
871 if ($_POST['form_category'] == 'Due Ins' && ($duncount >= 0 ||
!$isdueany)) continue;
872 if ($_POST['form_category'] == 'Due Pt' && ($duncount < 0 ||
!$isdueany)) continue;
874 $bgcolor = ((++
$orow & 1) ?
"#ffdddd" : "#ddddff");
876 // Determine the date of service. If this was a search parameter
877 // then we already know it. Or an 8-digit encounter number is
878 // presumed to be a date of service imported during conversion.
879 // Otherwise look it up in the form_encounter table.
882 list($pid, $encounter) = explode(".", $row['invnumber']);
884 // $svcdate = $form_date;
886 if (strlen($encounter) == 8) {
887 $svcdate = substr($encounter, 0, 4) . "-" . substr($encounter, 4, 2) .
888 "-" . substr($encounter, 6, 2);
890 else if ($encounter) {
891 $tmp = sqlQuery("SELECT date FROM form_encounter WHERE " .
892 "encounter = $encounter");
893 $svcdate = substr($tmp['date'], 0, 10);
896 // Get billing note to determine if customer is in collections.
898 $pdrow = sqlQuery("SELECT pd.genericname2, pd.genericval2 FROM " .
899 "integration_mapping AS im, patient_data AS pd WHERE " .
900 "im.foreign_id = " . $row['custid'] . " AND " .
901 "im.foreign_table = 'customer' AND " .
902 "pd.id = im.local_id");
903 $row['billnote'] = ($pdrow['genericname2'] == 'Billing') ?
$pdrow['genericval2'] : '';
904 $in_collections = stristr($row['billnote'], 'IN COLLECTIONS') !== false;
906 <tr bgcolor
='<?php echo $bgcolor ?>'>
908  
;<a href
="" onclick
="return npopup(<?php echo $pid ?>)"
909 ><?php
echo $row['lname'] . ', ' . $row['fname']; ?
></a
>
912  
;<a href
="sl_eob_invoice.php?id=<?php echo $row['id'] ?>"
913 target
="_blank"><?php
echo $row['invnumber'] ?
></a
>
916  
;<?php
echo $svcdate ?
>
919  
;<?php
echo $row['duedate'] ?
>
921 <td
class="detail" align
="right">
922 <?php
bucks($row['charges']) ?
> 
;
924 <td
class="detail" align
="right">
925 <?php
bucks($row['adjustments']) ?
> 
;
927 <td
class="detail" align
="right">
928 <?php
bucks($row['paid']) ?
> 
;
930 <td
class="detail" align
="right">
931 <?php
bucks($row['charges'] +
$row['adjustments'] - $row['paid']) ?
> 
;
933 <td
class="detail" align
="center">
934 <?php
echo $duncount ?
$duncount : " " ?
>
936 <?php
if (!$eracount) { ?
>
937 <td
class="detail" align
="left">
938 <input type
='checkbox' name
='form_cb[<?php echo($row['id
']) ?>]'<?php
echo $isduept ?
> />
939 <?php
if ($in_collections) echo "<b><font color='red'>IC</font></b>"; ?
>
945 } // end not $INTEGRATED_AR
946 } // end search/print logic
948 if (!$INTEGRATED_AR) SLClose();
954 <?php
if ($eracount) { ?
>
955 <input type
='button' value
='Process ERA File' onclick
='processERA()' />  
;
957 <input type
='button' value
='Select All' onclick
='checkAll(true)' />  
;
958 <input type
='button' value
='Clear All' onclick
='checkAll(false)' />  
;
959 <input type
='submit' name
='form_print' value
='Print Selected Statements' />  
;
961 <input type
='checkbox' name
='form_without' value
='1' /> <?php
xl('Without Update','e'); ?
>
966 <script language
="JavaScript">
967 function processERA() {
968 var f
= document
.forms
[0];
969 var debug
= f
.form_without
.checked ?
'1' : '0';
970 var paydate
= f
.form_paydate
.value
;
971 window
.open('sl_eob_process.php?eraname=<?php echo $eraname ?>&debug=' + debug +
'&paydate=' + paydate
, '_blank');
976 echo "alert('" . htmlentities($alertmsg) . "');\n";