4 * This is a report to create a patient ledger of charges with payments
8 * @link https://www.open-emr.org
10 * @author Terry Hill <terry@lillysystems.com>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @copyright Copyright (c) 2015 Rich Genandt <rgenandt@gmail.com>
13 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
14 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
17 require_once("./../verify_session.php");
18 $ignoreAuth_onsite_portal = true;
19 global $ignoreAuth_onsite_portal;
22 require_once('../../interface/globals.php');
23 require_once($GLOBALS['srcdir'] . '/patient.inc');
24 require_once($GLOBALS['srcdir'] . '/options.inc.php');
25 require_once($GLOBALS['srcdir'] . '/appointments.inc.php');
27 use OpenEMR\Core\Header
;
29 $enc_units = $total_units = 0;
30 $enc_chg = $total_chg = 0;
31 $enc_pmt = $total_pmt = 0;
32 $enc_adj = $total_adj = 0;
33 $enc_bal = $total_bal = 0;
37 function GetAllUnapplied($pat = '', $from_dt = '', $to_dt = '')
44 $sql = "SELECT ar_session.*, ins.name, " .
45 "pat.lname, pat.fname, pat.mname, " .
46 "(SELECT SUM(ar_activity.pay_amount) FROM ar_activity WHERE " .
47 "ar_activity.deleted IS NULL AND ar_activity.session_id = ar_session.session_id) AS applied " .
49 "LEFT JOIN insurance_companies AS ins on ar_session.payer_id = ins.id " .
50 "LEFT JOIN patient_data AS pat on ar_session.patient_id = pat.pid " .
52 "ar_session.created_time >= ? AND ar_session.created_time <= ? " .
53 "AND ar_session.patient_id=?";
54 $result = sqlStatement($sql, array($from_dt, $to_dt, $pat));
56 while ($row = sqlFetchArray($result)) {
64 function User_Id_Look($thisField)
71 $rlist = sqlStatement("SELECT lname, fname, mname FROM users WHERE id=?", array($thisField));
72 $rrow = sqlFetchArray($rlist);
74 $ret = $rrow['lname'] . ', ' . $rrow['fname'] . ' ' . $rrow['mname'];
80 function List_Look($thisData, $thisList)
82 if ($thisList == 'occurrence') {
83 if (!$thisData ||
$thisData == '') {
84 return xl('Unknown or N/A');
88 if ($thisData == '') {
92 $fres = sqlStatement("SELECT title FROM list_options WHERE list_id=? " .
93 "AND option_id=?", array($thisList, $thisData));
95 $rret = sqlFetchArray($fres);
96 $dispValue = xl_list_label($rret['title']);
97 if ($thisList == 'occurrence' && $dispValue == '') {
98 $dispValue = xl('Unknown or N/A');
101 $dispValue = xl('Not Found');
107 function GetAllCredits($enc = '', $pat = '')
110 if (!$enc ||
!$pat) {
114 $sql = "SELECT activity.*, session.*, ins.name FROM ar_activity AS " .
115 "activity LEFT JOIN ar_session AS session USING (session_id) " .
116 "LEFT JOIN insurance_companies AS ins ON session.payer_id = " .
117 "ins.id WHERE deleted IS NULL AND encounter = ? AND pid = ? " .
118 "ORDER BY sequence_no";
119 $result = sqlStatement($sql, array($enc, $pat));
121 while ($row = sqlFetchArray($result)) {
128 function PrintEncHeader($dt, $rsn, $dr)
130 global $bgcolor, $orow;
131 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
132 echo "<tr class='bg-white'>";
133 if (strlen($rsn) > 50) {
134 $rsn = substr($rsn, 0, 50) . '...';
137 echo "<td colspan='4'><span class='font-weight-bold'>" . xlt('Encounter Dt / Rsn') . ": </span><span class='detail'>" . text(substr($dt, 0, 10)) . " / " . text($rsn) . "</span></td>";
138 echo "<td colspan='5'><span class='font-weight-bold'>" . xlt('Provider') . ": </span><span class='detail'>" . text(User_Id_Look($dr)) . "</span></td>";
142 function PrintEncFooter()
144 global $enc_units, $enc_chg, $enc_pmt, $enc_adj, $enc_bal;
145 echo "<tr bgcolor='#DDFFFF'>";
146 echo "<td colspan='3'> </td>";
147 echo "<td class='detail'>" . xlt('Encounter Balance') . ":</td>";
148 echo "<td class='detail text-right'>" . text($enc_units) . "</td>";
149 echo "<td class='detail text-right'>" . text(oeFormatMoney($enc_chg)) . "</td>";
150 echo "<td class='detail text-right'>" . text(oeFormatMoney($enc_pmt)) . "</td>";
151 echo "<td class='detail text-right'>" . text(oeFormatMoney($enc_adj)) . "</td>";
152 echo "<td class='detail text-right'>" . text(oeFormatMoney($enc_bal)) . "</td>";
155 function PrintCreditDetail($detail, $pat, $unassigned = false)
157 global $enc_pmt, $total_pmt, $enc_adj, $total_adj, $enc_bal, $total_bal;
158 global $bgcolor, $orow, $enc_units, $enc_chg;
159 foreach ($detail as $pmt) {
161 if (($pmt['pay_total'] - $pmt['applied']) == 0) {
166 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
167 $print = "<tr bgcolor='" . attr($bgcolor) . "'>";
168 $print .= "<td class='detail'> </td>";
169 $method = List_Look($pmt['payment_method'], 'payment_method');
170 $desc = $pmt['description'];
171 $ref = $pmt['reference'];
173 $memo = List_Look($pmt['adjustment_code'], 'payment_adjustment_code');
175 $memo = $pmt['memo'];
178 $description = $method;
181 $description .= ' - ';
184 $description .= $ref;
189 $description .= ': ';
192 $description .= $desc;
200 $description .= '[' . $memo . ']';
203 $print .= "<td class='detail' colspan='2'>" .
204 text($description) . " </td>";
205 $payer = ($pmt['name'] == '') ?
xl('Patient') : $pmt['name'];
207 $pmt_date = substr($pmt['post_to_date'], 0, 10);
209 $pmt_date = substr($pmt['post_time'], 0, 10);
212 $print .= "<td class='detail'>" .
213 text($pmt_date) . " / " . text($payer) . "</td>";
214 $type = List_Look($pmt['payment_type'], 'payment_type');
215 $print .= "<td class='detail'>" . text($type) . " </td>";
217 $pmt_amt = $pmt['pay_total'] - $pmt['applied'];
218 $uac_bal = oeFormatMoney($pmt_amt * -1);
219 $uac_appl = oeFormatMoney($pmt['applied']);
220 $uac_total = oeFormatMoney($pmt['pay_total']);
221 $pmt_amt = $pmt['pay_total'];
222 $total_pmt = $total_pmt - $uac_bal;
227 $pmt_amt = $pmt['pay_amount'];
228 $adj_amt = $pmt['adj_amount'];
229 $enc_pmt = $enc_pmt +
$pmt['pay_amount'];
230 $total_pmt = $total_pmt +
$pmt['pay_amount'];
231 $enc_adj = $enc_adj +
$pmt['adj_amount'];
232 $total_adj = $total_adj +
$pmt['adj_amount'];
237 $print_pmt = oeFormatMoney($pmt_amt);
242 $print_adj = oeFormatMoney($adj_amt);
245 $print .= "<td class='detail text-right'>" . text($uac_appl) . " </td>";
246 $print .= "<td class='detail text-right'>" . text($print_pmt) . " </td>";
247 $print .= "<td class='detail text-right'>" . text($print_adj) . " </td>";
248 $print .= "<td class='detail text-right'>" . text($uac_bal) . " </td>";
251 if ($pmt['follow_up_note'] != '') {
252 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
253 $print = "<tr bgcolor='" . attr($bgcolor) . "'>";
254 $print .= "<td class='detail' colspan='2'> </td>";
255 $print .= "<td colspan='7'>" . xlt('Follow Up Note') . ": ";
256 $print .= text($pmt['follow_up_note']);
257 $print .= "</td></tr>\n";
262 $total_bal = $total_bal +
$uac_bal;
264 $enc_bal = $enc_bal - $pmt_amt - $adj_amt;
265 $total_bal = $total_bal - $pmt_amt - $adj_amt;
271 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
273 if (!isset($_REQUEST['form_from_date'])) {
274 $_REQUEST['form_from_date'] = '';
277 if (!isset($_REQUEST['form_to_date'])) {
278 $_REQUEST['form_to_date'] = '';
281 if (!isset($_REQUEST['form_refresh'])) {
282 $_REQUEST['form_refresh'] = '';
285 if (substr($GLOBALS['ledger_begin_date'], 0, 1) == 'Y') {
286 $ledger_time = substr($GLOBALS['ledger_begin_date'], 1, 1);
287 $last_year = mktime(0, 0, 0, date('m'), date('d'), date('Y') - $ledger_time);
288 } elseif (substr($GLOBALS['ledger_begin_date'], 0, 1) == 'M') {
289 $ledger_time = substr($GLOBALS['ledger_begin_date'], 1, 1);
290 $last_year = mktime(0, 0, 0, date('m') - $ledger_time, date('d'), date('Y'));
291 } elseif (substr($GLOBALS['ledger_begin_date'], 0, 1) == 'D') {
292 $ledger_time = substr($GLOBALS['ledger_begin_date'], 1, 1);
293 $last_year = mktime(0, 0, 0, date('m'), date('d') - $ledger_time, date('Y'));
296 $form_from_date = date('Y-m-d', $last_year);
297 if ($_REQUEST['form_from_date']) {
298 $form_from_date = fixDate($_REQUEST['form_from_date'], $last_year);
301 $form_to_date = fixDate($_REQUEST['form_to_date'], date('Y-m-d')); ?
>
305 <?php Header
::setupHeader(['no_main-theme', 'datetime-picker', 'patientportal-style']); ?
>
306 <script src
="<?php echo $GLOBALS['webroot'] ?>/library/js/common.js?v=<?php echo $v_js_includes; ?>"></script
>
309 function checkSubmit() {
310 document
.forms
[0].elements
['form_refresh'].value
= true;
311 document
.forms
[0].submit();
318 /* specifically include & exclude from printing */
324 #report_parameters_daterange {
340 /* specifically exclude some from the screen */
342 #report_parameters_daterange {
357 <title
><?php
echo xlt('Patient Ledger by Date') ?
></title
>
361 $
('.datepicker').datetimepicker({
362 <?php
$datetimepicker_timepicker = false; ?
>
363 <?php
$datetimepicker_formatInput = false; ?
>
364 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
365 <?php
// can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
371 <body
class="skin-blue">
372 <h2
><?php
echo xlt('Patient Ledger'); ?
></h2
>
373 <form method
='post' action
='./pat_ledger.php' id
='theform'>
374 <div id
="report_parameters">
375 <input type
='hidden' name
='form_refresh' id
='form_refresh' value
=''/>
379 <div
class="float-left">
382 <td
class='col-form-label'>
383 <?php
echo xlt('From'); ?
>:
386 <input type
='text' class='datepicker form-control' name
='form_from_date' id
="form_from_date" size
='10' value
='<?php echo attr($form_from_date) ?>' title
='yyyy-mm-dd'>
388 <td
class='col-form-label'>
389  
; 
; 
; 
;<?php
echo xlt('To{{Range}}'); ?
>:
392 <input type
='text' class='datepicker form-control' name
='form_to_date' id
="form_to_date" size
='10' value
='<?php echo attr($form_to_date) ?>' title
='yyyy-mm-dd'>
399 <td align
='left' class='align-middle h-100'>
400 <table
class="w-100 h-100">
403 <div style
='margin-left: 15px'>
404 <a href
='#' class='btn btn-primary' onclick
="checkSubmit();" ><?php
echo xlt('Submit'); ?
></a
>
412 </div
> <!-- end of parameters
-->
415 $from_date = $form_from_date . ' 00:00:00';
416 $to_date = $form_to_date . ' 23:59:59';
417 if ($_REQUEST['form_refresh']) {
419 $sqlBindArray = array();
420 $query = "select b.code_type, b.code, b.code_text, b.pid, b.provider_id, " .
421 "b.billed, b.payer_id, b.units, b.fee, b.bill_date, b.id, " .
423 "fe.encounter, fe.date, fe.reason, fe.provider_id " .
424 "FROM form_encounter AS fe " .
425 "LEFT JOIN billing AS b ON b.pid=fe.pid AND b.encounter=fe.encounter " .
426 "LEFT JOIN insurance_companies AS ins ON b.payer_id = ins.id " .
427 "LEFT OUTER JOIN code_types AS c ON c.ct_key = b.code_type " .
428 "WHERE fe.date >= ? AND fe.date <= ? AND fe.pid = ? ";
429 array_push($sqlBindArray, $from_date, $to_date, $pid);
431 $query .= "AND c.ct_proc = '1' ";
432 $query .= "AND activity > 0 ORDER BY fe.date, fe.id ";
433 $res = sqlStatement($query, $sqlBindArray);
435 $patient = sqlQuery("SELECT * from patient_data WHERE pid=?", array($pid));
436 $pat_dob = $patient['DOB'];
437 $pat_name = $patient['fname'] . ' ' . $patient['lname'];
439 <div id
="report_header">
440 <table width
="98%" class="border-0" cellspacing
="0" cellpadding
="0">
442 <td
class="title"><?php
echo xlt('Patient Ledger'); ?
></td
>
446 $title = xl('All Providers');
448 <td
class="title" ><?php
echo text($title); ?
></td
>
452 $title = xl('For Dates') . ': ' . $form_from_date . ' - ' . $form_to_date;
454 <td
class="title"><?php
echo text($title); ?
></td
>
458 <table
class="w-100 border-0" cellspacing
="0" cellpadding
="0">
460 <td
class='font-weight-bold'><?php
echo xlt('Date')?
>: <?php
echo text(date('Y-m-d')); ?
></td
>
461 <td
class='font-weight-bold'><?php
echo xlt('Patient')?
>: <?php
echo text($pat_name); ?
></td
>
462 <td
class='font-weight-bold'><?php
echo xlt('DOB')?
>: <?php
echo text($pat_dob);?
></td
>
463 <td
class='font-weight-bold'> <?php
echo xlt('ID')?
>: <?php
echo text($pid);?
></td
>
467 <div id
="report_results">
468 <table
class="table">
470 <td
class='font-weight-bold'><?php
echo xlt('Code'); ?
></td
>
471 <td colspan
="2" class='font-weight-bold'><?php
echo xlt('Description'); ?
></td
>
472 <td
class='font-weight-bold'><?php
echo xlt('Billed Date'); ?
> / <?php
echo xlt('Payor'); ?
></td
>
473 <td
class='font-weight-bold'><?php
echo xlt('Type'); ?
> 
; 
; 
; 
; 
; 
; 
;
474 <?php
echo xlt('Units'); ?
></td
>
475 <td
class='font-weight-bold'> 
; 
; 
; 
; 
;<?php
echo xlt('Charge'); ?
></td
>
476 <td
class='text-right font-weight-bold'> 
; 
;<?php
echo xlt('Payment'); ?
></td
>
477 <td
class='text-right font-weight-bold'> 
; 
; 
; 
; 
;<?php
echo xlt('Adjustment'); ?
></td
>
478 <td
class='text-right font-weight-bold'> 
; 
; 
;<?php
echo xlt('Balance'); ?
></td
>
481 <td
> 
; 
; 
;</td
>
482 <td colspan
="2"> 
; 
; 
;</td
>
483 <td
> 
; 
; 
; 
; 
; 
;</td
>
484 <td
> 
; 
; 
; 
; 
; 
; 
; 
; 
; 
; 
;</td
>
485 <td
class='font-weight-bold'> 
; 
; 
;<?php
echo xlt('UAC Appl'); ?
></td
>
486 <td
class='text-right font-weight-bold'> 
; 
; 
; 
; 
;<?php
echo xlt('UAC Tot'); ?
></td
>
492 $prev_encounter_id = -1;
493 $hdr_printed = false;
495 while ($erow = sqlFetchArray($res)) {
498 if ($erow['encounter'] != $prev_encounter_id) {
499 if ($prev_encounter_id != -1) {
500 $credits = GetAllCredits($prev_encounter_id, $pid);
501 if (count($credits) > 0) {
506 $prev_row['provider_id']
510 PrintCreditDetail($credits, $pid);
517 $hdr_printed = false;
520 $enc_units = $enc_chg = $enc_pmt = $enc_adj = $enc_bal = 0;
524 // Now print an encounter heading line -
534 $code_desc = $erow['code_text'];
535 if (strlen($code_desc) > 50) {
536 $code_desc = substr($code_desc, 0, 50) . '...';
539 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
540 $print = "<tr bgcolor='" . attr($bgcolor) . "'>";
541 $print .= "<td class='detail'>" . text($erow['code']) . "</td>";
542 $print .= "<td class='detail' colspan='2'>" . text($code_desc) . "</td>";
543 $who = ($erow['name'] == '') ?
xl('Self') : $erow['name'];
544 $bill = substr($erow['bill_date'], 0, 10);
549 $print .= "<td class='detail'>" . text($bill) . " / " . text($who) . "</td>";
550 $print .= "<td class='detail text-right'>" . text($erow['units']) . "</td>";
551 $print .= "<td class='detail text-right'>" . text(oeFormatMoney($erow['fee'])) . "</td>";
552 $print .= "<td> </td><td> </td><td> </td>";
555 $total_units +
= $erow['units'];
556 $total_chg +
= $erow['fee'];
557 $total_bal +
= $erow['fee'];
558 $enc_units +
= $erow['units'];
559 $enc_chg +
= $erow['fee'];
560 $enc_bal +
= $erow['fee'];
566 $prev_encounter_id = $erow['encounter'];
570 if ($prev_encounter_id != -1) {
571 $credits = GetAllCredits($prev_encounter_id, $pid);
572 if (count($credits) > 0) {
577 $prev_row['provider_id']
581 PrintCreditDetail($credits, $pid);
589 // This is the end of the encounter/charge loop -
590 $uac = GetAllUnapplied($pid, $from_date, $to_date);
591 if (count($uac) > 0) {
593 $bgcolor = (($bgcolor == "#FFFFDD") ?
"#FFDDDD" : "#FFFFDD");
594 echo "<tr class='bg-white'><td colspan='9'> </td></tr>\n";
597 PrintCreditDetail($uac, $pid, true);
601 echo "<tr bgcolor='#DDFFFF'>\n";
602 echo " <td colspan='2'> </td>";
603 echo " <td class='font-weight-bold' colspan='2'>" . xlt("Grand Total") . "</td>\n";
604 echo " <td class='font-weight-bold text-right'>" . text($total_units) . "</td>\n";
605 echo " <td class='font-weight-bold text-right'>" . text(oeFormatMoney($total_chg)) . "</td>\n";
606 echo " <td class='font-weight-bold text-right'>" . text(oeFormatMoney($total_pmt)) . "</td>\n";
607 echo " <td class='font-weight-bold text-right'>" . text(oeFormatMoney($total_adj)) . "</td>\n";
608 echo " <td class='font-weight-bold text-right'>" . text(oeFormatMoney($total_bal)) . "</td>\n";
612 <tr
><td
> 
;</td
></tr
><br
/><br
/>
613 <?php
if ($GLOBALS['print_next_appointment_on_ledger'] == 1) {
614 $next_day = mktime(0, 0, 0, date('m'), date('d') +
1, date('Y'));
615 # add one day to date so it will not get todays appointment
616 $current_date2 = date('Y-m-d', $next_day);
617 $events = fetchNextXAppts($current_date2, $pid);
618 $next_appoint_date = oeFormatShortDate($events[0]['pc_eventDate']);
619 $next_appoint_time = substr($events[0]['pc_startTime'], 0, 5);
620 if (strlen($events[0]['umname']) != 0) {
621 $next_appoint_provider = $events[0]['ufname'] . ' ' . $events[0]['umname'] . ' ' . $events[0]['ulname'];
623 $next_appoint_provider = $events[0]['ufname'] . ' ' . $events[0]['ulname'];
626 if (strlen($next_appoint_time) != 0) {
629 <td
class="title"><?php
echo xlt('Next Appointment Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider); ?
></td
>
634 } // end ($GLOBALS['print_next_appointment_on_ledger'] == 1)
640 if ($_REQUEST['form_refresh'] && $orow <= 0) {
641 echo "<span style='font-size: 0.8125rem;'>";
642 echo xlt('No matches found. Try search again.');
644 echo '<script>document.getElementById("report_results").style.display="none";</script>';
645 echo '<script>document.getElementById("controls").style.display="none";</script>';
648 if (!$_REQUEST['form_refresh']) { ?
>
650 <?php
echo xlt('Please input search criteria above, and click Submit to view results.'); ?
>