PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/interface/billing/sl_eob_search.php

https://bitbucket.org/astawiarski/openemr
PHP | 1081 lines | 824 code | 112 blank | 145 comment | 219 complexity | 6138b30abfd0cc7c2720710d946a1b1d MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-1.0, GPL-2.0, MPL-2.0
  1. <?php
  2. // Copyright (C) 2005-2010 Rod Roark <rod@sunsetsystems.com>
  3. //
  4. // Windows compatibility and statement downloading:
  5. // 2009 Bill Cernansky and Tony McCormick [mi-squared.com]
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. // This is the first of two pages to support posting of EOBs.
  12. // The second is sl_eob_invoice.php.
  13. require_once("../globals.php");
  14. require_once("$srcdir/patient.inc");
  15. require_once("$srcdir/sql-ledger.inc");
  16. require_once("$srcdir/invoice_summary.inc.php");
  17. require_once($GLOBALS['OE_SITE_DIR'] . "/statement.inc.php");
  18. require_once("$srcdir/parse_era.inc.php");
  19. require_once("$srcdir/sl_eob.inc.php");
  20. require_once("$srcdir/formatting.inc.php");
  21. require_once("$srcdir/classes/class.ezpdf.php");//for the purpose of pdf creation
  22. $DEBUG = 0; // set to 0 for production, 1 to test
  23. $INTEGRATED_AR = $GLOBALS['oer_config']['ws_accounting']['enabled'] === 2;
  24. $alertmsg = '';
  25. $where = '';
  26. $eraname = '';
  27. $eracount = 0;
  28. // This is called back by parse_era() if we are processing X12 835's.
  29. //
  30. function era_callback(&$out) {
  31. global $where, $eracount, $eraname, $INTEGRATED_AR;
  32. // print_r($out); // debugging
  33. ++$eracount;
  34. // $eraname = $out['isa_control_number'];
  35. $eraname = $out['gs_date'] . '_' . ltrim($out['isa_control_number'], '0') .
  36. '_' . ltrim($out['payer_id'], '0');
  37. list($pid, $encounter, $invnumber) = slInvoiceNumber($out);
  38. if ($pid && $encounter) {
  39. if ($where) $where .= ' OR ';
  40. if ($INTEGRATED_AR) {
  41. $where .= "( f.pid = '$pid' AND f.encounter = '$encounter' )";
  42. } else {
  43. $where .= "invnumber = '$invnumber'";
  44. }
  45. }
  46. }
  47. function bucks($amount) {
  48. if ($amount) echo oeFormatMoney($amount);
  49. }
  50. // Upload a file to the client's browser
  51. //
  52. function upload_file_to_client($file_to_send) {
  53. header("Pragma: public");
  54. header("Expires: 0");
  55. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  56. header("Content-Type: application/force-download");
  57. header("Content-Length: " . filesize($file_to_send));
  58. header("Content-Disposition: attachment; filename=" . basename($file_to_send));
  59. header("Content-Description: File Transfer");
  60. readfile($file_to_send);
  61. // flush the content to the browser. If you don't do this, the text from the subsequent
  62. // output from this script will be in the file instead of sent to the browser.
  63. flush();
  64. exit(); //added to exit from process properly in order to stop bad html code -ehrlive
  65. // sleep one second to ensure there's no follow-on.
  66. sleep(1);
  67. }
  68. function upload_file_to_client_pdf($file_to_send) {
  69. //Function reads a text file and converts to pdf.
  70. global $STMT_TEMP_FILE_PDF;
  71. $pdf =& new Cezpdf('LETTER');//pdf creation starts
  72. $pdf->ezSetMargins(36,0,36,0);
  73. $pdf->selectFont($GLOBALS['fileroot'] . "/library/fonts/Courier.afm");
  74. $pdf->ezSetY($pdf->ez['pageHeight'] - $pdf->ez['topMargin']);
  75. $countline=1;
  76. $file = fopen($file_to_send, "r");//this file contains the text to be converted to pdf.
  77. while(!feof($file))
  78. {
  79. $OneLine=fgets($file);//one line is read
  80. if(stristr($OneLine, "\014") == true && !feof($file))//form feed means we should start a new page.
  81. {
  82. $pdf->ezNewPage();
  83. $pdf->ezSetY($pdf->ez['pageHeight'] - $pdf->ez['topMargin']);
  84. str_replace("\014", "", $OneLine);
  85. }
  86. if(stristr($OneLine, 'REMIT TO') == true || stristr($OneLine, 'Visit Date') == true)//lines are made bold when 'REMIT TO' or 'Visit Date' is there.
  87. $pdf->ezText('<b>'.$OneLine.'</b>', 12, array('justification' => 'left', 'leading' => 6));
  88. else
  89. $pdf->ezText($OneLine, 12, array('justification' => 'left', 'leading' => 6));
  90. $countline++;
  91. }
  92. $fh = @fopen($STMT_TEMP_FILE_PDF, 'w');//stored to a pdf file
  93. if ($fh) {
  94. fwrite($fh, $pdf->ezOutput());
  95. fclose($fh);
  96. }
  97. header("Pragma: public");//this section outputs the pdf file to browser
  98. header("Expires: 0");
  99. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  100. header("Content-Type: application/force-download");
  101. header("Content-Length: " . filesize($STMT_TEMP_FILE_PDF));
  102. header("Content-Disposition: attachment; filename=" . basename($STMT_TEMP_FILE_PDF));
  103. header("Content-Description: File Transfer");
  104. readfile($STMT_TEMP_FILE_PDF);
  105. // flush the content to the browser. If you don't do this, the text from the subsequent
  106. // output from this script will be in the file instead of sent to the browser.
  107. flush();
  108. exit(); //added to exit from process properly in order to stop bad html code -ehrlive
  109. // sleep one second to ensure there's no follow-on.
  110. sleep(1);
  111. }
  112. $today = date("Y-m-d");
  113. if ($INTEGRATED_AR) {
  114. // Print or download statements if requested.
  115. //
  116. if (($_POST['form_print'] || $_POST['form_download'] || $_POST['form_pdf']) && $_POST['form_cb']) {
  117. $fhprint = fopen($STMT_TEMP_FILE, 'w');
  118. $where = "";
  119. foreach ($_POST['form_cb'] as $key => $value) $where .= " OR f.id = $key";
  120. $where = substr($where, 4);
  121. $res = sqlStatement("SELECT " .
  122. "f.id, f.date, f.pid, f.encounter, f.stmt_count, f.last_stmt_date, " .
  123. "p.fname, p.mname, p.lname, p.street, p.city, p.state, p.postal_code " .
  124. "FROM form_encounter AS f, patient_data AS p " .
  125. "WHERE ( $where ) AND " .
  126. "p.pid = f.pid " .
  127. "ORDER BY p.lname, p.fname, f.pid, f.date, f.encounter");
  128. $stmt = array();
  129. $stmt_count = 0;
  130. // This loops once for each invoice/encounter.
  131. //
  132. while ($row = sqlFetchArray($res)) {
  133. $svcdate = substr($row['date'], 0, 10);
  134. $duedate = $svcdate; // TBD?
  135. $duncount = $row['stmt_count'];
  136. // If this is a new patient then print the pending statement
  137. // and start a new one. This is an associative array:
  138. //
  139. // cid = same as pid
  140. // pid = OpenEMR patient ID
  141. // patient = patient name
  142. // amount = total amount due
  143. // adjust = adjustments (already applied to amount)
  144. // duedate = due date of the oldest included invoice
  145. // age = number of days from duedate to today
  146. // to = array of addressee name/address lines
  147. // lines = array of:
  148. // dos = date of service "yyyy-mm-dd"
  149. // desc = description
  150. // amount = charge less adjustments
  151. // paid = amount paid
  152. // notice = 1 for first notice, 2 for second, etc.
  153. // detail = array of details, see invoice_summary.inc.php
  154. //
  155. if ($stmt['cid'] != $row['pid']) {
  156. if (!empty($stmt)) ++$stmt_count;
  157. fwrite($fhprint, create_statement($stmt));
  158. $stmt['cid'] = $row['pid'];
  159. $stmt['pid'] = $row['pid'];
  160. $stmt['patient'] = $row['fname'] . ' ' . $row['lname'];
  161. $stmt['to'] = array($row['fname'] . ' ' . $row['lname']);
  162. if ($row['street']) $stmt['to'][] = $row['street'];
  163. $stmt['to'][] = $row['city'] . ", " . $row['state'] . " " . $row['postal_code'];
  164. $stmt['lines'] = array();
  165. $stmt['amount'] = '0.00';
  166. $stmt['today'] = $today;
  167. $stmt['duedate'] = $duedate;
  168. } else {
  169. // Report the oldest due date.
  170. if ($duedate < $stmt['duedate']) {
  171. $stmt['duedate'] = $duedate;
  172. }
  173. }
  174. // Recompute age at each invoice.
  175. $stmt['age'] = round((strtotime($today) - strtotime($stmt['duedate'])) /
  176. (24 * 60 * 60));
  177. $invlines = ar_get_invoice_summary($row['pid'], $row['encounter'], true);
  178. foreach ($invlines as $key => $value) {
  179. $line = array();
  180. $line['dos'] = $svcdate;
  181. $line['desc'] = ($key == 'CO-PAY') ? "Patient Payment" : "Procedure $key";
  182. $line['amount'] = sprintf("%.2f", $value['chg']);
  183. $line['adjust'] = sprintf("%.2f", $value['adj']);
  184. $line['paid'] = sprintf("%.2f", $value['chg'] - $value['bal']);
  185. $line['notice'] = $duncount + 1;
  186. $line['detail'] = $value['dtl'];
  187. $stmt['lines'][] = $line;
  188. $stmt['amount'] = sprintf("%.2f", $stmt['amount'] + $value['bal']);
  189. }
  190. // Record that this statement was run.
  191. if (! $DEBUG && ! $_POST['form_without']) {
  192. sqlStatement("UPDATE form_encounter SET " .
  193. "last_stmt_date = '$today', stmt_count = stmt_count + 1 " .
  194. "WHERE id = " . $row['id']);
  195. }
  196. } // end for
  197. if (!empty($stmt)) ++$stmt_count;
  198. fwrite($fhprint, create_statement($stmt));
  199. fclose($fhprint);
  200. sleep(1);
  201. // Download or print the file, as selected
  202. if ($_POST['form_download']) {
  203. upload_file_to_client($STMT_TEMP_FILE);
  204. } elseif ($_POST['form_pdf']) {
  205. upload_file_to_client_pdf($STMT_TEMP_FILE);
  206. } else { // Must be print!
  207. if ($DEBUG) {
  208. $alertmsg = xl("Printing skipped; see test output in") .' '. $STMT_TEMP_FILE;
  209. } else {
  210. exec("$STMT_PRINT_CMD $STMT_TEMP_FILE");
  211. if ($_POST['form_without']) {
  212. $alertmsg = xl('Now printing') .' '. $stmt_count .' '. xl('statements; invoices will not be updated.');
  213. } else {
  214. $alertmsg = xl('Now printing') .' '. $stmt_count .' '. xl('statements and updating invoices.');
  215. }
  216. } // end not debug
  217. } // end not form_download
  218. } // end statements requested
  219. } // end $INTEGRATED_AR
  220. else {
  221. SLConnect();
  222. // This will be true starting with SQL-Ledger 2.8.x:
  223. $got_address_table = SLQueryValue("SELECT count(*) FROM pg_tables WHERE " .
  224. "schemaname = 'public' AND tablename = 'address'");
  225. // Print or download statements if requested.
  226. //
  227. if (($_POST['form_print'] || $_POST['form_download'] || $_POST['form_pdf']) && $_POST['form_cb']) {
  228. $fhprint = fopen($STMT_TEMP_FILE, 'w');
  229. $where = "";
  230. foreach ($_POST['form_cb'] as $key => $value) $where .= " OR ar.id = $key";
  231. $where = substr($where, 4);
  232. // Sort by patient so that multiple invoices can be
  233. // represented on a single statement.
  234. if ($got_address_table) {
  235. $res = SLQuery("SELECT ar.*, customer.name, " .
  236. "address.address1, address.address2, " .
  237. "address.city, address.state, address.zipcode, " .
  238. "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS fname, " .
  239. "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS lname " .
  240. "FROM ar, customer, address WHERE ( $where ) AND " .
  241. "customer.id = ar.customer_id AND " .
  242. "address.trans_id = ar.customer_id " .
  243. "ORDER BY lname, fname, ar.customer_id, ar.transdate");
  244. }
  245. else {
  246. $res = SLQuery("SELECT ar.*, customer.name, " .
  247. "customer.address1, customer.address2, " .
  248. "customer.city, customer.state, customer.zipcode, " .
  249. "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS lname, " .
  250. "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS fname " .
  251. "FROM ar, customer WHERE ( $where ) AND " .
  252. "customer.id = ar.customer_id " .
  253. "ORDER BY lname, fname, ar.customer_id, ar.transdate");
  254. }
  255. if ($sl_err) die($sl_err);
  256. $stmt = array();
  257. $stmt_count = 0;
  258. for ($irow = 0; $irow < SLRowCount($res); ++$irow) {
  259. $row = SLGetRow($res, $irow);
  260. // Determine the date of service. An 8-digit encounter number is
  261. // presumed to be a date of service imported during conversion.
  262. // Otherwise look it up in the form_encounter table.
  263. //
  264. $svcdate = "";
  265. list($pid, $encounter) = explode(".", $row['invnumber']);
  266. if (strlen($encounter) == 8) {
  267. $svcdate = substr($encounter, 0, 4) . "-" . substr($encounter, 4, 2) .
  268. "-" . substr($encounter, 6, 2);
  269. } else if ($encounter) {
  270. $tmp = sqlQuery("SELECT date FROM form_encounter WHERE " .
  271. "encounter = $encounter");
  272. $svcdate = substr($tmp['date'], 0, 10);
  273. }
  274. // How many times have we dunned them for this invoice?
  275. $intnotes = trim($row['intnotes']);
  276. $duncount = substr_count(strtolower($intnotes), "statement sent");
  277. // If this is a new patient then print the pending statement
  278. // and start a new one. This is an associative array:
  279. //
  280. // cid = SQL-Ledger customer ID
  281. // pid = OpenEMR patient ID
  282. // patient = patient name
  283. // amount = total amount due
  284. // adjust = adjustments (already applied to amount)
  285. // duedate = due date of the oldest included invoice
  286. // age = number of days from duedate to today
  287. // to = array of addressee name/address lines
  288. // lines = array of:
  289. // dos = date of service "yyyy-mm-dd"
  290. // desc = description
  291. // amount = charge less adjustments
  292. // paid = amount paid
  293. // notice = 1 for first notice, 2 for second, etc.
  294. // detail = array of details, see invoice_summary.inc.php
  295. //
  296. if ($stmt['cid'] != $row['customer_id']) {
  297. if (!empty($stmt)) ++$stmt_count;
  298. fwrite($fhprint, create_statement($stmt));
  299. $stmt['cid'] = $row['customer_id'];
  300. $stmt['pid'] = $pid;
  301. if ($got_address_table) {
  302. $stmt['patient'] = $row['fname'] . ' ' . $row['lname'];
  303. $stmt['to'] = array($row['fname'] . ' ' . $row['lname']);
  304. } else {
  305. $stmt['patient'] = $row['name'];
  306. $stmt['to'] = array($row['name']);
  307. }
  308. if ($row['address1']) $stmt['to'][] = $row['address1'];
  309. if ($row['address2']) $stmt['to'][] = $row['address2'];
  310. $stmt['to'][] = $row['city'] . ", " . $row['state'] . " " . $row['zipcode'];
  311. $stmt['lines'] = array();
  312. $stmt['amount'] = '0.00';
  313. $stmt['today'] = $today;
  314. $stmt['duedate'] = $row['duedate'];
  315. } else {
  316. // Report the oldest due date.
  317. if ($row['duedate'] < $stmt['duedate']) {
  318. $stmt['duedate'] = $row['duedate'];
  319. }
  320. }
  321. $stmt['age'] = round((strtotime($today) - strtotime($stmt['duedate'])) /
  322. (24 * 60 * 60));
  323. $invlines = get_invoice_summary($row['id'], true); // true added by Rod 2006-06-09
  324. foreach ($invlines as $key => $value) {
  325. $line = array();
  326. $line['dos'] = $svcdate;
  327. $line['desc'] = ($key == 'CO-PAY') ? "Patient Payment" : "Procedure $key";
  328. $line['amount'] = sprintf("%.2f", $value['chg']);
  329. $line['adjust'] = sprintf("%.2f", $value['adj']);
  330. $line['paid'] = sprintf("%.2f", $value['chg'] - $value['bal']);
  331. $line['notice'] = $duncount + 1;
  332. $line['detail'] = $value['dtl']; // Added by Rod 2006-06-09
  333. $stmt['lines'][] = $line;
  334. $stmt['amount'] = sprintf("%.2f", $stmt['amount'] + $value['bal']);
  335. }
  336. // Record something in ar.intnotes about this statement run.
  337. if ($intnotes) $intnotes .= "\n";
  338. $intnotes = addslashes($intnotes . "Statement sent $today");
  339. if (! $DEBUG && ! $_POST['form_without']) {
  340. SLQuery("UPDATE ar SET intnotes = '$intnotes' WHERE id = " . $row['id']);
  341. if ($sl_err) die($sl_err);
  342. }
  343. } // end for
  344. if (!empty($stmt)) ++$stmt_count;
  345. fwrite($fhprint, create_statement($stmt));
  346. fclose($fhprint);
  347. sleep(1);
  348. // Download or print the file, as selected
  349. if ($_POST['form_download']) {
  350. upload_file_to_client($STMT_TEMP_FILE);
  351. } elseif ($_POST['form_pdf']) {
  352. upload_file_to_client_pdf($STMT_TEMP_FILE);
  353. } else { // Must be print!
  354. if ($DEBUG) {
  355. $alertmsg = xl("Printing skipped; see test output in") .' '. $STMT_TEMP_FILE;
  356. } else {
  357. exec("$STMT_PRINT_CMD $STMT_TEMP_FILE");
  358. if ($_POST['form_without']) {
  359. $alertmsg = xl('Now printing') .' '. $stmt_count .' '. xl('statements; invoices will not be updated.');
  360. } else {
  361. $alertmsg = xl('Now printing') .' '. $stmt_count .' '. xl('statements and updating invoices.');
  362. }
  363. } // end not debug
  364. } // end if form_download
  365. } // end statements requested
  366. } // end not $INTEGRATED_AR
  367. ?>
  368. <html>
  369. <head>
  370. <?php html_header_show(); ?>
  371. <link rel=stylesheet href="<?php echo $css_header;?>" type="text/css">
  372. <title><?php xl('EOB Posting - Search','e'); ?></title>
  373. <script type="text/javascript" src="../../library/textformat.js"></script>
  374. <script language="JavaScript">
  375. var mypcc = '1';
  376. function checkAll(checked) {
  377. var f = document.forms[0];
  378. for (var i = 0; i < f.elements.length; ++i) {
  379. var ename = f.elements[i].name;
  380. if (ename.indexOf('form_cb[') == 0)
  381. f.elements[i].checked = checked;
  382. }
  383. }
  384. function npopup(pid) {
  385. window.open('sl_eob_patient_note.php?patient_id=' + pid, '_blank', 'width=500,height=250,resizable=1');
  386. return false;
  387. }
  388. </script>
  389. </head>
  390. <body leftmargin='0' topmargin='0' marginwidth='0' marginheight='0'>
  391. <center>
  392. <form method='post' action='sl_eob_search.php' enctype='multipart/form-data'>
  393. <table border='0' cellpadding='5' cellspacing='0'>
  394. <tr>
  395. <?php
  396. if ($INTEGRATED_AR) {
  397. // Identify the payer to support resumable posting sessions.
  398. echo " <td>\n";
  399. echo " " . xl('Payer') . ":\n";
  400. echo " </td>\n";
  401. echo " <td>\n";
  402. $insurancei = getInsuranceProviders();
  403. echo " <select name='form_payer_id'>\n";
  404. echo " <option value='0'>-- " . xl('Patient') . " --</option>\n";
  405. foreach ($insurancei as $iid => $iname) {
  406. echo "<option value='$iid'";
  407. if ($iid == $_POST['form_payer_id']) echo " selected";
  408. echo ">" . $iname . "</option>\n";
  409. }
  410. echo " </select>\n";
  411. echo " </td>\n";
  412. }
  413. ?>
  414. <td>
  415. <?php xl('Source:','e'); ?>
  416. </td>
  417. <td>
  418. <input type='text' name='form_source' size='10' value='<?php echo $_POST['form_source']; ?>'
  419. title='<?php xl("A check number or claim number to identify the payment","e"); ?>'>
  420. </td>
  421. <td>
  422. <?php xl('Pay Date:','e'); ?>
  423. </td>
  424. <td>
  425. <input type='text' name='form_paydate' size='10' value='<?php echo $_POST['form_paydate']; ?>'
  426. onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)'
  427. title='<?php xl("Date of payment yyyy-mm-dd","e"); ?>'>
  428. </td>
  429. <?php if ($INTEGRATED_AR) { // include deposit date ?>
  430. <td>
  431. <?php xl('Deposit Date:','e'); ?>
  432. </td>
  433. <td>
  434. <input type='text' name='form_deposit_date' size='10' value='<?php echo $_POST['form_deposit_date']; ?>'
  435. onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)'
  436. title='<?php xl("Date of bank deposit yyyy-mm-dd","e"); ?>'>
  437. </td>
  438. <?php } ?>
  439. <td>
  440. <?php xl('Amount:','e'); ?>
  441. </td>
  442. <td>
  443. <input type='text' name='form_amount' size='10' value='<?php echo $_POST['form_amount']; ?>'
  444. title='<?php xl("Paid amount that you will allocate","e"); ?>'>
  445. </td>
  446. <td align='right'>
  447. <a href='sl_eob_help.php' target='_blank'><?php xl('Help','e'); ?></a>
  448. </td>
  449. </tr>
  450. </table>
  451. <table border='0' cellpadding='5' cellspacing='0'>
  452. <tr bgcolor='#ddddff'>
  453. <td>
  454. <?php xl('Name:','e'); ?>
  455. </td>
  456. <td>
  457. <input type='text' name='form_name' size='10' value='<?php echo $_POST['form_name']; ?>'
  458. title='<?php xl("Any part of the patient name, or \"last,first\", or \"X-Y\"","e"); ?>'>
  459. </td>
  460. <td>
  461. <?php xl('Chart ID:','e'); ?>
  462. </td>
  463. <td>
  464. <input type='text' name='form_pid' size='10' value='<?php echo $_POST['form_pid']; ?>'
  465. title='<?php xl("Patient chart ID","e"); ?>'>
  466. </td>
  467. <td>
  468. <?php xl('Encounter:','e'); ?>
  469. </td>
  470. <td>
  471. <input type='text' name='form_encounter' size='10' value='<?php echo $_POST['form_encounter']; ?>'
  472. title='<?php xl("Encounter number","e"); ?>'>
  473. </td>
  474. <td>
  475. <?php xl('Svc Date:','e'); ?>
  476. </td>
  477. <td>
  478. <input type='text' name='form_date' size='10' value='<?php echo $_POST['form_date']; ?>'
  479. title='<?php xl("Date of service mm/dd/yyyy","e"); ?>'>
  480. </td>
  481. <td>
  482. <?php xl('To:','e'); ?>
  483. </td>
  484. <td>
  485. <input type='text' name='form_to_date' size='10' value='<?php echo $_POST['form_to_date']; ?>'
  486. title='<?php xl("Ending DOS mm/dd/yyyy if you wish to enter a range","e"); ?>'>
  487. </td>
  488. <td>
  489. <select name='form_category'>
  490. <?php
  491. foreach (array(xl('Open'), xl('All'), xl('Due Pt'), xl('Due Ins')) as $value) {
  492. echo " <option value='$value'";
  493. if ($_POST['form_category'] == $value) echo " selected";
  494. echo ">$value</option>\n";
  495. }
  496. ?>
  497. </select>
  498. </td>
  499. <td>
  500. <input type='submit' name='form_search' value='<?php xl("Search","e"); ?>'>
  501. </td>
  502. </tr>
  503. <!-- Support for X12 835 upload -->
  504. <tr bgcolor='#ddddff'>
  505. <td colspan='12'>
  506. <?php xl('Or upload ERA file:','e'); ?>
  507. <input type="hidden" name="MAX_FILE_SIZE" value="5000000" />
  508. <input name="form_erafile" type="file" />
  509. </td>
  510. </tr>
  511. <tr>
  512. <td height="1" colspan="10">
  513. </td>
  514. </tr>
  515. </table>
  516. <?php
  517. if ($_POST['form_search'] || $_POST['form_print']) {
  518. $form_name = trim($_POST['form_name']);
  519. $form_pid = trim($_POST['form_pid']);
  520. $form_encounter = trim($_POST['form_encounter']);
  521. $form_date = fixDate($_POST['form_date'], "");
  522. $form_to_date = fixDate($_POST['form_to_date'], "");
  523. $where = "";
  524. // Handle X12 835 file upload.
  525. //
  526. if ($_FILES['form_erafile']['size']) {
  527. $tmp_name = $_FILES['form_erafile']['tmp_name'];
  528. // Handle .zip extension if present. Probably won't work on Windows.
  529. if (strtolower(substr($_FILES['form_erafile']['name'], -4)) == '.zip') {
  530. rename($tmp_name, "$tmp_name.zip");
  531. exec("unzip -p $tmp_name.zip > $tmp_name");
  532. unlink("$tmp_name.zip");
  533. }
  534. echo "<!-- Notes from ERA upload processing:\n";
  535. $alertmsg .= parse_era($tmp_name, 'era_callback');
  536. echo "-->\n";
  537. $erafullname = $GLOBALS['OE_SITE_DIR'] . "/era/$eraname.edi";
  538. if (is_file($erafullname)) {
  539. $alertmsg .= "Warning: Set $eraname was already uploaded ";
  540. if (is_file($GLOBALS['OE_SITE_DIR'] . "/era/$eraname.html"))
  541. $alertmsg .= "and processed. ";
  542. else
  543. $alertmsg .= "but not yet processed. ";
  544. }
  545. rename($tmp_name, $erafullname);
  546. } // End 835 upload
  547. if ($INTEGRATED_AR) {
  548. if ($eracount) {
  549. // Note that parse_era() modified $eracount and $where.
  550. if (! $where) $where = '1 = 2';
  551. }
  552. else {
  553. if ($form_name) {
  554. if ($where) $where .= " AND ";
  555. // Allow the last name to be followed by a comma and some part of a first name.
  556. if (preg_match('/^(.*\S)\s*,\s*(.*)/', $form_name, $matches)) {
  557. $where .= "p.lname LIKE '" . $matches[1] . "%' AND p.fname LIKE '" . $matches[2] . "%'";
  558. // Allow a filter like "A-C" on the first character of the last name.
  559. } else if (preg_match('/^(\S)\s*-\s*(\S)$/', $form_name, $matches)) {
  560. $tmp = '1 = 2';
  561. while (ord($matches[1]) <= ord($matches[2])) {
  562. $tmp .= " OR p.lname LIKE '" . $matches[1] . "%'";
  563. $matches[1] = chr(ord($matches[1]) + 1);
  564. }
  565. $where .= "( $tmp ) ";
  566. } else {
  567. $where .= "p.lname LIKE '%$form_name%'";
  568. }
  569. }
  570. if ($form_pid) {
  571. if ($where) $where .= " AND ";
  572. $where .= "f.pid = '$form_pid'";
  573. }
  574. if ($form_encounter) {
  575. if ($where) $where .= " AND ";
  576. $where .= "f.encounter = '$form_encounter'";
  577. }
  578. if ($form_date) {
  579. if ($where) $where .= " AND ";
  580. if ($form_to_date) {
  581. $where .= "f.date >= '$form_date' AND f.date <= '$form_to_date'";
  582. }
  583. else {
  584. $where .= "f.date = '$form_date'";
  585. }
  586. }
  587. if (! $where) {
  588. if ($_POST['form_category'] == 'All') {
  589. die(xl("At least one search parameter is required if you select All."));
  590. } else {
  591. $where = "1 = 1";
  592. }
  593. }
  594. }
  595. // Notes that as of release 4.1.1 the copays are stored
  596. // in the ar_activity table marked with a PCP in the account_code column.
  597. $query = "SELECT f.id, f.pid, f.encounter, f.date, " .
  598. "f.last_level_billed, f.last_level_closed, f.last_stmt_date, f.stmt_count, " .
  599. "p.fname, p.mname, p.lname, p.pubpid, p.genericname2, p.genericval2, " .
  600. "( SELECT SUM(b.fee) FROM billing AS b WHERE " .
  601. "b.pid = f.pid AND b.encounter = f.encounter AND " .
  602. "b.activity = 1 AND b.code_type != 'COPAY' ) AS charges, " .
  603. "( SELECT SUM(a.pay_amount) FROM ar_activity AS a WHERE " .
  604. "a.pid = f.pid AND a.encounter = f.encounter AND a.payer_type = 0 AND a.account_code = 'PCP')*-1 AS copays, " .
  605. "( SELECT SUM(a.pay_amount) FROM ar_activity AS a WHERE " .
  606. "a.pid = f.pid AND a.encounter = f.encounter AND a.account_code != 'PCP') AS payments, " .
  607. "( SELECT SUM(a.adj_amount) FROM ar_activity AS a WHERE " .
  608. "a.pid = f.pid AND a.encounter = f.encounter ) AS adjustments " .
  609. "FROM form_encounter AS f " .
  610. "JOIN patient_data AS p ON p.pid = f.pid " .
  611. "WHERE $where " .
  612. "ORDER BY p.lname, p.fname, p.mname, f.pid, f.encounter";
  613. // Note that unlike the SQL-Ledger case, this query does not weed
  614. // out encounters that are paid up. Also the use of sub-selects
  615. // will require MySQL 4.1 or greater.
  616. // echo "<!-- $query -->\n"; // debugging
  617. $t_res = sqlStatement($query);
  618. $num_invoices = mysql_num_rows($t_res);
  619. if ($eracount && $num_invoices != $eracount) {
  620. $alertmsg .= "Of $eracount remittances, there are $num_invoices " .
  621. "matching encounters in OpenEMR. ";
  622. }
  623. } // end $INTEGRATED_AR
  624. else {
  625. if ($eracount) {
  626. // Note that parse_era() modified $eracount and $where.
  627. if (! $where) $where = '1 = 2';
  628. }
  629. else {
  630. if ($form_name) {
  631. if ($where) $where .= " AND ";
  632. // Allow the last name to be followed by a comma and some part of a first name.
  633. if (preg_match('/^(.*\S)\s*,\s*(.*)/', $form_name, $matches)) {
  634. $where .= "customer.name ILIKE '" . $matches[2] . '% ' . $matches[1] . "%'";
  635. // Allow a filter like "A-C" on the first character of the last name.
  636. } else if (preg_match('/^(\S)\s*-\s*(\S)$/', $form_name, $matches)) {
  637. $tmp = '1 = 2';
  638. while (ord($matches[1]) <= ord($matches[2])) {
  639. // $tmp .= " OR customer.name ILIKE '% " . $matches[1] . "%'";
  640. // Fixing the above which was also matching on middle names:
  641. $tmp .= " OR customer.name ~* ' " . $matches[1] . "[A-Z]*$'";
  642. $matches[1] = chr(ord($matches[1]) + 1);
  643. }
  644. $where .= "( $tmp ) ";
  645. } else {
  646. $where .= "customer.name ILIKE '%$form_name%'";
  647. }
  648. }
  649. if ($form_pid && $form_encounter) {
  650. if ($where) $where .= " AND ";
  651. $where .= "ar.invnumber = '$form_pid.$form_encounter'";
  652. }
  653. else if ($form_pid) {
  654. if ($where) $where .= " AND ";
  655. $where .= "ar.invnumber LIKE '$form_pid.%'";
  656. }
  657. else if ($form_encounter) {
  658. if ($where) $where .= " AND ";
  659. $where .= "ar.invnumber like '%.$form_encounter'";
  660. }
  661. if ($form_date) {
  662. if ($where) $where .= " AND ";
  663. $date1 = substr($form_date, 0, 4) . substr($form_date, 5, 2) .
  664. substr($form_date, 8, 2);
  665. if ($form_to_date) {
  666. $date2 = substr($form_to_date, 0, 4) . substr($form_to_date, 5, 2) .
  667. substr($form_to_date, 8, 2);
  668. $where .= "((CAST (substring(ar.invnumber from position('.' in ar.invnumber) + 1 for 8) AS integer) " .
  669. "BETWEEN '$date1' AND '$date2')";
  670. $tmp = "date >= '$form_date' AND date <= '$form_to_date'";
  671. }
  672. else {
  673. // This catches old converted invoices where we have no encounters:
  674. $where .= "(ar.invnumber LIKE '%.$date1'";
  675. $tmp = "date = '$form_date'";
  676. }
  677. // Pick out the encounters from MySQL with the desired DOS:
  678. $rez = sqlStatement("SELECT pid, encounter FROM form_encounter WHERE $tmp");
  679. while ($row = sqlFetchArray($rez)) {
  680. $where .= " OR ar.invnumber = '" . $row['pid'] . "." . $row['encounter'] . "'";
  681. }
  682. $where .= ")";
  683. }
  684. if (! $where) {
  685. if ($_POST['form_category'] == 'All') {
  686. die(xl("At least one search parameter is required if you select All."));
  687. } else {
  688. $where = "1 = 1";
  689. }
  690. }
  691. }
  692. $query = "SELECT ar.id, ar.invnumber, ar.duedate, ar.amount, ar.paid, " .
  693. "ar.intnotes, ar.notes, ar.shipvia, customer.name, customer.id AS custid, ";
  694. if ($got_address_table) $query .=
  695. "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS lname, " .
  696. "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS fname, ";
  697. else $query .=
  698. "substring(trim(both from customer.name) from '% #\"%#\"' for '#') AS lname, " .
  699. "substring(trim(both from customer.name) from '#\"%#\" %' for '#') AS fname, ";
  700. $query .=
  701. "(SELECT SUM(invoice.sellprice * invoice.qty) FROM invoice WHERE " .
  702. "invoice.trans_id = ar.id AND invoice.sellprice > 0) AS charges, " .
  703. "(SELECT SUM(invoice.sellprice * invoice.qty) FROM invoice WHERE " .
  704. "invoice.trans_id = ar.id AND invoice.sellprice < 0) AS adjustments " .
  705. "FROM ar, customer WHERE ( $where ) AND customer.id = ar.customer_id ";
  706. if ($_POST['form_category'] != 'All' && !$eracount) {
  707. $query .= "AND ar.amount != ar.paid ";
  708. // if ($_POST['form_category'] == 'Due') {
  709. // $query .= "AND ar.duedate <= CURRENT_DATE ";
  710. // }
  711. }
  712. $query .= "ORDER BY lname, fname, ar.invnumber";
  713. // echo "<!-- $query -->\n"; // debugging
  714. $t_res = SLQuery($query);
  715. if ($sl_err) die($sl_err);
  716. $num_invoices = SLRowCount($t_res);
  717. if ($eracount && $num_invoices != $eracount) {
  718. $alertmsg .= "Of $eracount remittances, there are $num_invoices " .
  719. "matching claims in OpenEMR. ";
  720. }
  721. } // end not $INTEGRATED_AR
  722. ?>
  723. <table border='0' cellpadding='1' cellspacing='2' width='98%'>
  724. <tr bgcolor="#dddddd">
  725. <td class="dehead">
  726. &nbsp;<?php xl('Patient','e'); ?>
  727. </td>
  728. <td class="dehead">
  729. &nbsp;<?php xl('Invoice','e'); ?>
  730. </td>
  731. <td class="dehead">
  732. &nbsp;<?php xl('Svc Date','e'); ?>
  733. </td>
  734. <td class="dehead">
  735. &nbsp;<?php xl($INTEGRATED_AR ? 'Last Stmt' : 'Due Date','e'); ?>
  736. </td>
  737. <td class="dehead" align="right">
  738. <?php xl('Charge','e'); ?>&nbsp;
  739. </td>
  740. <td class="dehead" align="right">
  741. <?php xl('Adjust','e'); ?>&nbsp;
  742. </td>
  743. <td class="dehead" align="right">
  744. <?php xl('Paid','e'); ?>&nbsp;
  745. </td>
  746. <td class="dehead" align="right">
  747. <?php xl('Balance','e'); ?>&nbsp;
  748. </td>
  749. <td class="dehead" align="center">
  750. <?php xl('Prv','e'); ?>
  751. </td>
  752. <?php if (!$eracount) { ?>
  753. <td class="dehead" align="left">
  754. <?php xl('Sel','e'); ?>
  755. </td>
  756. <?php } ?>
  757. </tr>
  758. <?php
  759. $orow = -1;
  760. if ($INTEGRATED_AR) {
  761. while ($row = sqlFetchArray($t_res)) {
  762. $balance = sprintf("%.2f", $row['charges'] + $row['copays'] - $row['payments'] - $row['adjustments']);
  763. if ($_POST['form_category'] != 'All' && $eracount == 0 && $balance == 0) continue;
  764. // $duncount was originally supposed to be the number of times that
  765. // the patient was sent a statement for this invoice.
  766. //
  767. $duncount = $row['stmt_count'];
  768. // But if we have not yet billed the patient, then compute $duncount as a
  769. // negative count of the number of insurance plans for which we have not
  770. // yet closed out insurance.
  771. //
  772. if (! $duncount) {
  773. for ($i = 1; $i <= 3 && arGetPayerID($row['pid'], $row['date'], $i); ++$i) ;
  774. $duncount = $row['last_level_closed'] + 1 - $i;
  775. }
  776. $isdueany = ($balance > 0);
  777. // An invoice is now due from the patient if money is owed and we are
  778. // not waiting for insurance to pay.
  779. //
  780. $isduept = ($duncount >= 0 && $isdueany) ? " checked" : "";
  781. // Skip invoices not in the desired "Due..." category.
  782. //
  783. if (substr($_POST['form_category'], 0, 3) == 'Due' && !$isdueany) continue;
  784. if ($_POST['form_category'] == 'Due Ins' && ($duncount >= 0 || !$isdueany)) continue;
  785. if ($_POST['form_category'] == 'Due Pt' && ($duncount < 0 || !$isdueany)) continue;
  786. $bgcolor = ((++$orow & 1) ? "#ffdddd" : "#ddddff");
  787. $svcdate = substr($row['date'], 0, 10);
  788. $last_stmt_date = empty($row['last_stmt_date']) ? '' : $row['last_stmt_date'];
  789. // Determine if customer is in collections.
  790. //
  791. $billnote = ($row['genericname2'] == 'Billing') ? $row['genericval2'] : '';
  792. $in_collections = stristr($billnote, 'IN COLLECTIONS') !== false;
  793. ?>
  794. <tr bgcolor='<?php echo $bgcolor ?>'>
  795. <td class="detail">
  796. &nbsp;<a href="" onclick="return npopup(<?php echo $row['pid'] ?>)"
  797. ><?php echo $row['lname'] . ', ' . $row['fname']; ?></a>
  798. </td>
  799. <td class="detail">
  800. &nbsp;<a href="sl_eob_invoice.php?id=<?php echo $row['id'] ?>"
  801. target="_blank"><?php echo $row['pid'] . '.' . $row['encounter']; ?></a>
  802. </td>
  803. <td class="detail">
  804. &nbsp;<?php echo oeFormatShortDate($svcdate) ?>
  805. </td>
  806. <td class="detail">
  807. &nbsp;<?php echo oeFormatShortDate($last_stmt_date) ?>
  808. </td>
  809. <td class="detail" align="right">
  810. <?php bucks($row['charges']) ?>&nbsp;
  811. </td>
  812. <td class="detail" align="right">
  813. <?php bucks($row['adjustments']) ?>&nbsp;
  814. </td>
  815. <td class="detail" align="right">
  816. <?php bucks($row['payments'] - $row['copays']); ?>&nbsp;
  817. </td>
  818. <td class="detail" align="right">
  819. <?php bucks($balance); ?>&nbsp;
  820. </td>
  821. <td class="detail" align="center">
  822. <?php echo $duncount ? $duncount : "&nbsp;" ?>
  823. </td>
  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>"; ?>
  828. </td>
  829. <?php } ?>
  830. </tr>
  831. <?php
  832. } // end while
  833. } // end $INTEGRATED_AR
  834. else { // not $INTEGRATED_AR
  835. for ($irow = 0; $irow < $num_invoices; ++$irow) {
  836. $row = SLGetRow($t_res, $irow);
  837. // $duncount was originally supposed to be the number of times that
  838. // the patient was sent a statement for this invoice.
  839. //
  840. $duncount = substr_count(strtolower($row['intnotes']), "statement sent");
  841. // But if we have not yet billed the patient, then compute $duncount as a
  842. // negative count of the number of insurance plans for which we have not
  843. // yet closed out insurance.
  844. //
  845. if (! $duncount) {
  846. $insgot = strtolower($row['notes']);
  847. $inseobs = strtolower($row['shipvia']);
  848. foreach (array('ins1', 'ins2', 'ins3') as $value) {
  849. if (strpos($insgot, $value) !== false &&
  850. strpos($inseobs, $value) === false)
  851. --$duncount;
  852. }
  853. }
  854. // $isdue = ($row['duedate'] <= $today && $row['amount'] > $row['paid']) ? " checked" : "";
  855. $isdueany = sprintf("%.2f",$row['amount']) > sprintf("%.2f",$row['paid']);
  856. // An invoice is now due from the patient if money is owed and we are
  857. // not waiting for insurance to pay. We no longer look at the due date
  858. // for this.
  859. //
  860. $isduept = ($duncount >= 0 && $isdueany) ? " checked" : "";
  861. // Skip invoices not in the desired "Due..." category.
  862. //
  863. if (substr($_POST['form_category'], 0, 3) == 'Due' && !$isdueany) continue;
  864. if ($_POST['form_category'] == 'Due Ins' && ($duncount >= 0 || !$isdueany)) continue;
  865. if ($_POST['form_category'] == 'Due Pt' && ($duncount < 0 || !$isdueany)) continue;
  866. $bgcolor = ((++$orow & 1) ? "#ffdddd" : "#ddddff");
  867. // Determine the date of service. If this was a search parameter
  868. // then we already know it. Or an 8-digit encounter number is
  869. // presumed to be a date of service imported during conversion.
  870. // Otherwise look it up in the form_encounter table.
  871. //
  872. $svcdate = "";
  873. list($pid, $encounter) = explode(".", $row['invnumber']);
  874. // if ($form_date) {
  875. // $svcdate = $form_date;
  876. // } else
  877. if (strlen($encounter) == 8) {
  878. $svcdate = substr($encounter, 0, 4) . "-" . substr($encounter, 4, 2) .
  879. "-" . substr($encounter, 6, 2);
  880. }
  881. else if ($encounter) {
  882. $tmp = sqlQuery("SELECT date FROM form_encounter WHERE " .
  883. "encounter = $encounter");
  884. $svcdate = substr($tmp['date'], 0, 10);
  885. }
  886. // Get billing note to determine if customer is in collections.
  887. //
  888. $pdrow = sqlQuery("SELECT pd.genericname2, pd.genericval2 FROM " .
  889. "integration_mapping AS im, patient_data AS pd WHERE " .
  890. "im.foreign_id = " . $row['custid'] . " AND " .
  891. "im.foreign_table = 'customer' AND " .
  892. "pd.id = im.local_id");
  893. $row['billnote'] = ($pdrow['genericname2'] == 'Billing') ? $pdrow['genericval2'] : '';
  894. $in_collections = stristr($row['billnote'], 'IN COLLECTIONS') !== false;
  895. ?>
  896. <tr bgcolor='<?php echo $bgcolor ?>'>
  897. <td class="detail">
  898. &nbsp;<a href="" onclick="return npopup(<?php echo $pid ?>)"
  899. ><?php echo $row['lname'] . ', ' . $row['fname']; ?></a>
  900. </td>
  901. <td class="detail">
  902. &nbsp;<a href="sl_eob_invoice.php?id=<?php echo $row['id'] ?>"
  903. target="_blank"><?php echo $row['invnumber'] ?></a>
  904. </td>
  905. <td class="detail">
  906. &nbsp;<?php echo oeFormatShortDate($svcdate) ?>
  907. </td>
  908. <td class="detail">
  909. &nbsp;<?php echo oeFormatShortDate($row['duedate']) ?>
  910. </td>
  911. <td class="detail" align="right">
  912. <?php bucks($row['charges']) ?>&nbsp;
  913. </td>
  914. <td class="detail" align="right">
  915. <?php bucks($row['adjustments']) ?>&nbsp;
  916. </td>
  917. <td class="detail" align="right">
  918. <?php bucks($row['paid']) ?>&nbsp;
  919. </td>
  920. <td class="detail" align="right">
  921. <?php bucks($row['charges'] + $row['adjustments'] - $row['paid']) ?>&nbsp;
  922. </td>
  923. <td class="detail" align="center">
  924. <?php echo $duncount ? $duncount : "&nbsp;" ?>
  925. </td>
  926. <?php if (!$eracount) { ?>
  927. <td class="detail" align="left">
  928. <input type='checkbox' name='form_cb[<?php echo($row['id']) ?>]'<?php echo $isduept ?> />
  929. <?php if ($in_collections) echo "<b><font color='red'>IC</font></b>"; ?>
  930. </td>
  931. <?php } ?>
  932. </tr>
  933. <?php
  934. } // end for
  935. } // end not $INTEGRATED_AR
  936. } // end search/print logic
  937. if (!$INTEGRATED_AR) SLClose();
  938. ?>
  939. </table>
  940. <p>
  941. <?php if ($eracount) { ?>
  942. <input type='button' value='<?php xl('Process ERA File','e')?>' onclick='processERA()' /> &nbsp;
  943. <?php } else { ?>
  944. <input type='button' value='<?php xl('Select All','e')?>' onclick='checkAll(true)' /> &nbsp;
  945. <input type='button' value='<?php xl('Clear All','e')?>' onclick='checkAll(false)' /> &nbsp;
  946. <input type='submit' name='form_print' value='<?php xl('Print Selected Statements','e'); ?>' /> &nbsp;
  947. <input type='submit' name='form_download' value='<?php xl('Download Selected Statements','e'); ?>' /> &nbsp;
  948. <input type='submit' name='form_pdf' value='<?php xl('PDF Download Selected Statements','e'); ?>' /> &nbsp;
  949. <?php } ?>
  950. <input type='checkbox' name='form_without' value='1' /> <?php xl('Without Update','e'); ?>
  951. </p>
  952. </form>
  953. </center>
  954. <script language="JavaScript">
  955. function processERA() {
  956. var f = document.forms[0];
  957. var debug = f.form_without.checked ? '1' : '0';
  958. var paydate = f.form_paydate.value;
  959. window.open('sl_eob_process.php?eraname=<?php echo $eraname ?>&debug=' + debug + '&paydate=' + paydate + '&original=original', '_blank');
  960. return false;
  961. }
  962. <?php
  963. if ($alertmsg) {
  964. echo "alert('" . htmlentities($alertmsg) . "');\n";
  965. }
  966. ?>
  967. </script>
  968. </body>
  969. </html>