3 * Report - Cash receipts by Provider
5 * This module was written for one of my clients to report on cash
6 * receipts by practitioner. It is not as complete as it should be
7 * but I wanted to make the code available to the project because
8 * many other practices have this same need. - rod@sunsetsystems.com
11 * @link http://www.open-emr.org
12 * @author Rod Roark <rod@sunsetsystems.com>
13 * @author Terry Hill <terry@lillysystems.com>
14 * @author Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2006-2016 Rod Roark <rod@sunsetsystems.com>
16 * @copyright Copyright (c) 2016 Terry Hill <terry@lillysystems.com>
17 * @copyright Copyright (c) 2017 Brady Miller <brady.g.miller@gmail.com>
18 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
22 require_once('../globals.php');
23 require_once($GLOBALS['srcdir'].'/patient.inc');
24 require_once($GLOBALS['srcdir'].'/acl.inc');
25 require_once($GLOBALS['srcdir'].'/options.inc.php');
26 require_once($GLOBALS['fileroot'].'/custom/code_types.inc.php');
27 // This determines if a particular procedure code corresponds to receipts
28 // for the "Clinic" column as opposed to receipts for the practitioner. Each
29 // practice will have its own policies in this regard, so you'll probably
30 // have to customize this function. If you use the "fee sheet" encounter
31 // form then the code below may work for you.
33 require_once('../forms/fee_sheet/codes.php');
35 use OpenEMR\Core\Header
;
37 function is_clinic($code)
40 $i = strpos($code, ':');
42 $code = substr($code, 0, $i);
45 return ($bcodes['CPT4'][xl('Lab')][$code] ||
46 $bcodes['CPT4'][xl('Immunizations')][$code] ||
47 $bcodes['HCPCS'][xl('Therapeutic Injections')][$code]);
50 function bucks($amount)
53 echo attr(oeFormatMoney($amount));
57 if (! acl_check('acct', 'rep')) {
58 die(xlt("Unauthorized access."));
62 $form_use_edate = $_POST['form_use_edate'];
64 $form_proc_codefull = trim($_POST['form_proc_codefull']);
65 // Parse the code type and the code from <code_type>:<code>
66 $tmp_code_array = explode(':', $form_proc_codefull);
67 $form_proc_codetype = $tmp_code_array[0];
68 $form_proc_code = $tmp_code_array[1];
70 $form_dx_codefull = trim($_POST['form_dx_codefull']);
71 // Parse the code type and the code from <code_type>:<code>
72 $tmp_code_array = explode(':', $form_dx_codefull);
73 $form_dx_codetype = $tmp_code_array[0];
74 $form_dx_code = $tmp_code_array[1];
76 $form_procedures = empty($_POST['form_procedures']) ?
0 : 1;
77 $form_from_date = (isset($_POST['form_from_date'])) ?
DateToYYYYMMDD($_POST['form_from_date']) : date('Y-m-01');
78 $form_to_date = (isset($_POST['form_to_date'])) ?
DateToYYYYMMDD($_POST['form_to_date']) : date('Y-m-d');
80 $form_facility = $_POST['form_facility'];
85 <title
><?php
echo xlt('Cash Receipts by Provider')?
></title
>
87 <?php Header
::setupHeader(['datetime-picker', 'report-helper']); ?
>
89 <style type
="text/css">
90 /* specifically include & exclude from printing */
96 #report_parameters_daterange {
105 /* specifically exclude some from the screen */
107 #report_parameters_daterange {
114 <script language
="JavaScript">
115 $
(document
).ready(function() {
116 oeFixedHeaderSetup(document
.getElementById('mymaintable'));
117 var win
= top
.printLogSetup ? top
: opener
.top
;
118 win
.printLogSetup(document
.getElementById('printbutton'));
120 $
('.datepicker').datetimepicker({
121 <?php
$datetimepicker_timepicker = false; ?
>
122 <?php
$datetimepicker_showseconds = false; ?
>
123 <?php
$datetimepicker_formatInput = true; ?
>
124 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
125 <?php
// can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
129 // This is for callback by the find-code popup.
130 // Erases the current entry
131 // The target element is set by the find-code popup
132 // (this allows use of this in multiple form elements on the same page)
133 function set_related_target(codetype
, code
, selector
, codedesc
, target_element
) {
134 var f
= document
.forms
[0];
135 var s
= f
[target_element
].value
;
137 s
= codetype +
':' + code
;
141 f
[target_element
].value
= s
;
144 // This invokes the find-code (procedure/service codes) popup.
145 function sel_procedure() {
146 dlgopen('../patient_file/encounter/find_code_popup.php?target_element=form_proc_codefull&codetype=<?php echo attr(collect_codetypes("procedure", "csv")) ?>', '_blank', 500, 400);
149 // This invokes the find-code (diagnosis codes) popup.
150 function sel_diagnosis() {
151 dlgopen('../patient_file/encounter/find_code_popup.php?target_element=form_dx_codefull&codetype=<?php echo attr(collect_codetypes("diagnosis", "csv")) ?>', '_blank', 500, 400);
157 <body
class="body_top">
159 <span
class='title'><?php
echo xlt('Report'); ?
> - <?php
echo xlt('Cash Receipts by Provider'); ?
></span
>
161 <form method
='post' action
='sl_receipts_report.php' id
='theform' onsubmit
='return top.restoreSession()'>
163 <div id
="report_parameters">
165 <input type
='hidden' name
='form_refresh' id
='form_refresh' value
=''/>
170 <div style
='float:left'>
174 <td
class='control-label'>
175 <?php
echo xlt('Facility'); ?
>:
178 <?php
dropdown_facility($form_facility, 'form_facility'); ?
>
180 <td
class='control-label'>
181 <?php
echo xlt('Provider'); ?
>:
185 if (acl_check('acct', 'rep_a')) {
186 // Build a drop-down list of providers.
188 $query = "select id, lname, fname from users where " .
189 "authorized = 1 order by lname, fname";
190 $res = sqlStatement($query);
191 echo " <select name='form_doctor' class='form-control'>\n";
192 echo " <option value=''>-- " . xlt('All Providers') . " --\n";
193 while ($row = sqlFetchArray($res)) {
194 $provid = $row['id'];
195 echo " <option value='". attr($provid) ."'";
196 if ($provid == $_POST['form_doctor']) {
200 echo ">" . text($row['lname']) . ", " . text($row['fname']) . "\n";
205 echo "<input type='hidden' name='form_doctor' value='" . attr($_SESSION['authUserID']) . "'>";
210 <select name
='form_use_edate' class='form-control'>
211 <option value
='0'><?php
echo xlt('Payment Date'); ?
></option
>
212 <option value
='1'<?php
echo ($form_use_edate) ?
' selected' : ''; ?
>><?php
echo xlt('Invoice Date'); ?
></option
>
217 <td
class='control-label'>
218 <?php
echo xlt('From'); ?
>:
221 <input type
='text' class='datepicker form-control' name
='form_from_date' id
="form_from_date" size
='10' value
='<?php echo attr(oeFormatShortDate($form_from_date)); ?>'
222 title
='<?php echo xla('Date of appointments
'); ?>' >
224 <td
class='control-label'>
225 <?php
echo xlt('To'); ?
>:
228 <input type
='text' class='datepicker form-control' name
='form_to_date' id
="form_to_date" size
='10' value
='<?php echo attr(oeFormatShortDate($form_to_date)); ?>'
229 title
='<?php echo xla('Optional end date
'); ?>' >
234 <td
class='control-label'>
236 if (!$GLOBALS['simplified_demographics']) {
237 echo ' ' . xlt('Procedure/Service') . ':';
241 <input type
='text' class='form-control' name
='form_proc_codefull' size
='11' value
='<?php echo attr($form_proc_codefull); ?>' onclick
='sel_procedure()'
242 title
='<?php echo xla('Optional procedure
/service code
'); ?>'
244 if ($GLOBALS['simplified_demographics']) {
245 echo "style='display:none'";
249 <td
class='control-label'>
251 if (!$GLOBALS['simplified_demographics']) {
252 echo ' ' . xlt('Diagnosis') . ':';
256 <input type
='text' class='form-control' name
='form_dx_codefull' size
='11' value
='<?php echo attr($form_dx_codefull); ?>' onclick
='sel_diagnosis()'
257 title
='<?php echo xla('Enter a diagnosis code to exclude all invoices not containing it
'); ?>'
259 if ($GLOBALS['simplified_demographics']) {
260 echo "style='display:none'";
265 <div
class='checkbox'>
266 <label
><input type
='checkbox' name
='form_details' value
='1'<?php
echo ($_POST['form_details']) ?
" checked" : ""; ?
>><?php
echo xlt('Details')?
></label
>
268 <div
class='checkbox'>
269 <label
><input type
='checkbox' name
='form_procedures' value
='1'<?php
echo ($form_procedures) ?
" checked" : ""; ?
>><?php
echo xlt('Procedures')?
></label
>
280 <td align
='left' valign
='middle' height
="100%">
282 <table style
='border-left:1px solid; width:100%; height:100%' >
285 <div
class="text-center">
286 <div
class="btn-group" role
="group">
287 <a href
='#' class='btn btn-default btn-save' onclick
='$("#form_refresh").attr("value","true"); $("#theform").submit();'>
288 <?php
echo xlt('Submit'); ?
>
290 <?php
if ($_POST['form_refresh']) { ?
>
291 <a href
='#' class='btn btn-default btn-print' id
='printbutton'>
292 <?php
echo xlt('Print'); ?
>
306 if ($_POST['form_refresh']) {
308 <div id
="report_results">
309 <table border
='0' cellpadding
='1' cellspacing
='2' width
='98%' id
='mymaintable'>
312 <?php
echo xlt('Practitioner') ?
>
315 <?php
echo xlt('Date') ?
>
317 <?php
if ($form_procedures) { ?
>
320 if ($GLOBALS['cash_receipts_report_invoice'] == '0') {
327 <?php
if ($form_proc_codefull) { ?
>
329 <?php
echo xlt('InvAmt') ?
>
332 <?php
if ($form_proc_codefull) { ?
>
334 <?php
echo xlt('Insurance') ?
>
337 <?php
if ($form_procedures) { ?
>
339 <?php
echo xlt('Procedure') ?
>
342 <?php
echo xlt('Prof.') ?
>
345 <?php
echo xlt('Clinic') ?
>
349 <?php
echo xlt('Received') ?
>
354 if ($_POST['form_refresh']) {
355 $form_doctor = $_POST['form_doctor'];
358 $ids_to_skip = array();
361 // Get copays. These will be ignored if a CPT code was specified.
363 if (!$form_proc_code ||
!$form_proc_codetype) {
364 /*************************************************************
365 $query = "SELECT b.fee, b.pid, b.encounter, b.code_type, b.code, b.modifier, " .
366 "fe.date, fe.id AS trans_id, u.id AS docid " .
367 "FROM billing AS b " .
368 "JOIN form_encounter AS fe ON fe.pid = b.pid AND fe.encounter = b.encounter " .
369 "JOIN forms AS f ON f.pid = b.pid AND f.encounter = b.encounter AND f.formdir = 'newpatient' " .
370 "LEFT OUTER JOIN users AS u ON u.username = f.user " .
371 "WHERE b.code_type = 'COPAY' AND b.activity = 1 AND " .
372 "fe.date >= '$form_from_date 00:00:00' AND fe.date <= '$form_to_date 23:59:59'";
373 // If a facility was specified.
374 if ($form_facility) {
375 $query .= " AND fe.facility_id = '$form_facility'";
377 // If a doctor was specified.
379 $query .= " AND u.id = '$form_doctor'";
381 *************************************************************/
382 $sqlBindArray = array();
383 $query = "SELECT b.fee, b.pid, b.encounter, b.code_type, b.code, b.modifier, " .
384 "fe.date, fe.id AS trans_id, fe.provider_id AS docid, fe.invoice_refno " .
385 "FROM billing AS b " .
386 "JOIN form_encounter AS fe ON fe.pid = b.pid AND fe.encounter = b.encounter " .
387 "WHERE b.code_type = 'COPAY' AND b.activity = 1 AND " .
388 "fe.date >= ? AND fe.date <= ?";
389 array_push($sqlBindArray, $form_from_date . " 00:00:00", $form_to_date . " 23:59:59");
390 // If a facility was specified.
391 if ($form_facility) {
392 $query .= " AND fe.facility_id = ?";
393 array_push($sqlBindArray, $form_facility);
396 // If a doctor was specified.
398 $query .= " AND fe.provider_id = ?";
399 array_push($sqlBindArray, $form_doctor);
402 /************************************************************/
404 $res = sqlStatement($query, $sqlBindArray);
405 while ($row = sqlFetchArray($res)) {
406 $trans_id = $row['trans_id'];
407 $thedate = substr($row['date'], 0, 10);
408 $patient_id = $row['pid'];
409 $encounter_id = $row['encounter'];
411 if (!empty($ids_to_skip[$trans_id])) {
416 // If a diagnosis code was given then skip any invoices without
418 if ($form_dx_code && $form_dx_codetype) {
419 $tmp = sqlQuery("SELECT count(*) AS count FROM billing WHERE " .
420 "pid = ? AND encounter = ? AND " .
421 "code_type = ? AND code LIKE ? AND " .
422 "activity = 1", array($patient_id,$encounter_id,$form_dx_codetype,$form_dx_code));
423 if (empty($tmp['count'])) {
424 $ids_to_skip[$trans_id] = 1;
431 "%08u%s%08u%08u%06u",
438 $arows[$key] = array();
439 $arows[$key]['transdate'] = $thedate;
440 $arows[$key]['amount'] = $row['fee'];
441 $arows[$key]['docid'] = $row['docid'];
442 $arows[$key]['project_id'] = 0;
443 $arows[$key]['memo'] = '';
444 if ($GLOBALS['cash_receipts_report_invoice'] == '0') {
445 $arows[$key]['invnumber'] = "$patient_id.$encounter_id";
447 $arows[$key]['invnumber'] = "$patient_name";
450 $arows[$key]['irnumber'] = $row['invoice_refno'];
452 } // end copays (not $form_proc_code)
454 // Get ar_activity (having payments), form_encounter, forms, users, optional ar_session
455 /***************************************************************
456 $query = "SELECT a.pid, a.encounter, a.post_time, a.code, a.modifier, a.pay_amount, " .
457 "fe.date, fe.id AS trans_id, u.id AS docid, s.deposit_date, s.payer_id " .
458 "FROM ar_activity AS a " .
459 "JOIN form_encounter AS fe ON fe.pid = a.pid AND fe.encounter = a.encounter " .
460 "JOIN forms AS f ON f.pid = a.pid AND f.encounter = a.encounter AND f.formdir = 'newpatient' " .
461 "LEFT OUTER JOIN users AS u ON u.username = f.user " .
462 "LEFT OUTER JOIN ar_session AS s ON s.session_id = a.session_id " .
463 "WHERE a.pay_amount != 0 AND ( " .
464 "a.post_time >= '$form_from_date 00:00:00' AND a.post_time <= '$form_to_date 23:59:59' " .
465 "OR fe.date >= '$form_from_date 00:00:00' AND fe.date <= '$form_to_date 23:59:59' " .
466 "OR s.deposit_date >= '$form_from_date' AND s.deposit_date <= '$form_to_date' )";
467 // If a procedure code was specified.
468 if ($form_proc_code) $query .= " AND a.code = '$form_proc_code'";
469 // If a facility was specified.
470 if ($form_facility) $query .= " AND fe.facility_id = '$form_facility'";
471 // If a doctor was specified.
472 if ($form_doctor) $query .= " AND u.id = '$form_doctor'";
473 ***************************************************************/
474 $sqlBindArray = array();
475 $query = "SELECT a.pid, a.encounter, a.post_time, a.code, a.modifier, a.pay_amount, " .
476 "fe.date, fe.id AS trans_id, fe.provider_id AS docid, fe.invoice_refno, s.deposit_date, s.payer_id, " .
477 "b.provider_id, concat(p.lname, ' ', p.fname) as 'pat_fulname' " .
478 "FROM ar_activity AS a " .
479 "JOIN form_encounter AS fe ON fe.pid = a.pid AND fe.encounter = a.encounter " .
480 "LEFT OUTER JOIN ar_session AS s ON s.session_id = a.session_id " .
481 "LEFT OUTER JOIN patient_data AS p ON p.pid = a.pid " .
482 "LEFT OUTER JOIN billing AS b ON b.pid = a.pid AND b.encounter = a.encounter AND " .
483 "b.code = a.code AND b.modifier = a.modifier AND b.activity = 1 AND " .
484 "b.code_type != 'COPAY' AND b.code_type != 'TAX' " .
485 "WHERE a.pay_amount != 0 AND ( " .
486 "a.post_time >= ? AND a.post_time <= ? " .
487 "OR fe.date >= ? AND fe.date <= ? " .
488 "OR s.deposit_date >= ? AND s.deposit_date <= ? )";
489 array_push($sqlBindArray, $form_from_date . " 00:00:00", $form_to_date . " 23:59:59", $form_from_date . " 00:00:00", $form_to_date . " 23:59:59", $form_from_date, $form_to_date);
490 // If a procedure code was specified.
491 // Support code type if it is in the ar_activity table. Note it is not always included, so
492 // also support a blank code type in ar_activity table.
493 if ($form_proc_codetype && $form_proc_code) {
494 $query .= " AND (a.code_type = ? OR a.code_type = '') AND a.code = ?";
495 array_push($sqlBindArray, $form_proc_codetype, $form_proc_code);
498 // If a facility was specified.
499 if ($form_facility) {
500 $query .= " AND fe.facility_id = ?";
501 array_push($sqlBindArray, $form_facility);
504 // If a doctor was specified.
506 $query .= " AND ( b.provider_id = ? OR " .
507 "( ( b.provider_id IS NULL OR b.provider_id = 0 ) AND " .
508 "fe.provider_id = ? ) )";
509 array_push($sqlBindArray, $form_doctor, $form_doctor);
512 /**************************************************************/
514 $res = sqlStatement($query, $sqlBindArray);
515 while ($row = sqlFetchArray($res)) {
516 $trans_id = $row['trans_id'];
517 $patient_id = $row['pid'];
518 $encounter_id = $row['encounter'];
519 $patient_name = $row['pat_fulname'];
521 if (!empty($ids_to_skip[$trans_id])) {
526 if ($form_use_edate) {
527 $thedate = substr($row['date'], 0, 10);
529 if (!empty($row['deposit_date'])) {
530 $thedate = $row['deposit_date'];
532 $thedate = substr($row['post_time'], 0, 10);
536 if (strcmp($thedate, $form_from_date) < 0 ||
strcmp($thedate, $form_to_date) > 0) {
541 // If a diagnosis code was given then skip any invoices without
543 if ($form_dx_code && $form_dx_codetype) {
544 $tmp = sqlQuery("SELECT count(*) AS count FROM billing WHERE " .
545 "pid = ? AND encounter = ? AND " .
546 "code_type = ? AND code LIKE ? AND " .
547 "activity = 1", array($patient_id,$encounter_id,$form_dx_codetype,$form_dx_code));
548 if (empty($tmp['count'])) {
549 $ids_to_skip[$trans_id] = 1;
555 $docid = empty($row['encounter_id']) ?
$row['docid'] : $row['encounter_id'];
557 "%08u%s%08u%08u%06u",
564 $arows[$key] = array();
565 $arows[$key]['transdate'] = $thedate;
566 $arows[$key]['amount'] = 0 - $row['pay_amount'];
567 $arows[$key]['docid'] = $docid;
568 $arows[$key]['project_id'] = empty($row['payer_id']) ?
0 : $row['payer_id'];
569 $arows[$key]['memo'] = $row['code'];
570 if ($GLOBALS['cash_receipts_report_invoice'] == '0') {
571 $arows[$key]['invnumber'] = "$patient_id.$encounter_id";
573 $arows[$key]['invnumber'] = "$patient_name";
576 $arows[$key]['irnumber'] = $row['invoice_refno'];
582 foreach ($arows as $row) {
583 // Get insurance company name
585 if ($form_proc_codefull && $row['project_id']) {
586 $tmp = sqlQuery("SELECT name FROM insurance_companies WHERE " .
587 "id = ?", array($row['project_id']));
588 $insconame = $tmp['name'];
593 if ($form_procedures && is_clinic($row['memo'])) {
594 $amount2 -= $row['amount'];
596 $amount1 -= $row['amount'];
599 // if ($docid != $row['employee_id']) {
600 if ($docid != $row['docid']) {
605 <tr bgcolor
="#ddddff">
606 <td
class="detail" colspan
="<?php echo ($form_proc_codefull ? 4 : 2) + ($form_procedures ? 2 : 0); ?>">
607 <?php
echo xlt('Totals for ') . text($docname) ?
>
610 <?php
bucks($doctotal1) ?
>
612 <?php
if ($form_procedures) { ?
>
614 <?php
bucks($doctotal2) ?
>
624 $docid = $row['docid'];
625 $tmp = sqlQuery("SELECT lname, fname FROM users WHERE id = ?", array($docid));
626 $docname = empty($tmp) ?
xl('Unknown') : $tmp['fname'] . ' ' . $tmp['lname'];
628 $docnameleft = $docname;
631 if ($_POST['form_details']) {
636 <?php
echo text($docnameleft); $docnameleft = " " ?
>
639 <?php
echo text(oeFormatShortDate($row['transdate'])); ?
>
641 <?php
if ($form_procedures) { ?
>
643 <?php
echo empty($row['irnumber']) ?
text($row['invnumber']) : text($row['irnumber']); ?
>
647 if ($form_proc_code && $form_proc_codetype) {
648 echo " <td class='detail' align='right'>";
649 list($patient_id, $encounter_id) = explode(".", $row['invnumber']);
650 $tmp = sqlQuery("SELECT SUM(fee) AS sum FROM billing WHERE " .
651 "pid = ? AND encounter = ? AND " .
652 "code_type = ? AND code = ? AND activity = 1", array($patient_id,$encounter_id,$form_proc_codetype,$form_proc_code));
658 if ($form_proc_codefull) { ?
>
660 <?php
echo text($insconame) ?
>
663 <?php
if ($form_procedures) { ?
>
665 <?php
echo text($row['memo']) ?
>
668 <td
class="detail" align
="right">
669 <?php
bucks($amount1) ?
>
671 <?php
if ($form_procedures) { ?
>
672 <td
class="detail" align
="right">
673 <?php
bucks($amount2) ?
>
679 $doctotal1 +
= $amount1;
680 $doctotal2 +
= $amount2;
681 $grandtotal1 +
= $amount1;
682 $grandtotal2 +
= $amount2;
686 <tr bgcolor
="#ddddff">
687 <td
class="detail" colspan
="<?php echo ($form_proc_codefull ? 4 : 2) + ($form_procedures ? 2 : 0); ?>">
688 <?php
echo xlt('Totals for ') . text($docname) ?
>
691 <?php
bucks($doctotal1) ?
>
693 <?php
if ($form_procedures) { ?
>
695 <?php
bucks($doctotal2) ?
>
700 <tr bgcolor
="#ffdddd">
701 <td
class="detail" colspan
="<?php echo ($form_proc_codefull ? 4 : 2) + ($form_procedures ? 2 : 0); ?>">
702 <?php
echo xlt('Grand Totals') ?
>
705 <?php
bucks($grandtotal1) ?
>
707 <?php
if ($form_procedures) { ?
>
709 <?php
bucks($grandtotal2) ?
>
713 <?php
$report_from_date = oeFormatShortDate($form_from_date) ;
714 $report_to_date = oeFormatShortDate($form_to_date) ;
716 <div align
='right'><span
class='title' ><?php
echo xlt('Report Date'). ' '; ?
><?php
echo text($report_from_date);?
> - <?php
echo text($report_to_date);?
></span
></div
>
726 <?php
echo xlt('Please input search criteria above, and click Submit to view results.'); ?
>