2 /* This is a template for printing patient statements and collection
3 * letters. You must customize it to suit your practice. If your
4 * needs are simple then you do not need programming experience to do
5 * this - just read the comments and make appropriate substitutions.
6 * All you really need to do is replace the [strings in brackets].
9 * @author Rod Roark <rod@sunsetsystems.com>
10 * @author Bill Cernansky <bill@mi-squared.com>
11 * @author Tony McCormick <tony@mi-squared.com>
12 * @author Raymond Magauran <magauran@medfetch.com>
13 * @author Jerry Padgett <sjpadgett@gmail.com>
14 * @author Stephen Waite <stephen.waite@cmsvt.com>
15 * @copyright Copyright (c) 2006 Rod Roark <rod@sunsetsystems.com>
16 * @copyright Copyright (c) 2009 Bill Cernansky <bill@mi-squared.com>
17 * @copyright Copyright (c) 2009 Tony McCormick <tony@mi-squared.com>
18 * @copyright Copyright (c) 2016 Raymond Magauran <magauran@medfetch.com>
19 * @copyright Copyright (c) 2017 Jerry Padgett <sjpadgett@gmail.com>
20 * @copyright Copyright (c) 2017 Stephen Waite <stephen.waite@cmsvt.com>
21 * @link https://github.com/openemr/openemr/tree/master
22 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
25 // The location/name of a temporary file to hold printable statements.
26 // May want to alter these names to allow multi-site installs out-of-the-box
28 $STMT_TEMP_FILE = $GLOBALS['temporary_files_dir'] . "/openemr_statements.txt";
29 $STMT_TEMP_FILE_PDF = $GLOBALS['temporary_files_dir'] . "/openemr_statements.pdf";
30 $STMT_PRINT_CMD = $GLOBALS['print_command'];
32 /** There are two options to print a batch of PDF statements:
33 * 1. The original statement, a text based statement, using CezPDF
34 * Altering this statement is labor intensive, but capable of being altered any way desired...
36 * 2. Branded Statement, whose core is build from 1., the original statement, using HTML2PDF.
38 * To customize 2., add your practice location/images/practice_logo.gif
39 * In the base/default install this is located at '/openemr/sites/default/images/practice_logo.gif',
40 * Adjust directory paths per your installation.
41 * Further customize 2. manually in functions report_2() and create_HTML_statement(), below.
44 function make_statement($stmt)
46 if ($GLOBALS['statement_appearance'] == "1") {
47 if ($_POST['form_portalnotify'] && is_auth_portal($stmt['pid'])) {
48 return osp_create_HTML_statement($stmt);
50 return create_HTML_statement($stmt);
53 return create_statement($stmt);
57 * This prints a header for documents. Keeps the brand uniform...
58 * @param string $pid patient_id
59 * @param string $direction, options "web" or anything else. Web provides apache-friendly url links.
60 * @return outputs to be displayed however requested
62 function report_header_2($stmt, $direction = '', $providerID = '1')
64 $titleres = getPatientData($stmt['pid'], "fname,lname,DOB");
65 if ($_SESSION['pc_facility']) {
66 $sql = "select * from facility where id=?";
67 $facility = sqlQuery($sql, array($_SESSION['pc_facility']));
69 $sql = "SELECT * FROM facility ORDER BY billing_location DESC LIMIT 1";
70 $facility = sqlQuery($sql);
73 $DOB = oeFormatShortDate($titleres['DOB']);
74 /******************************************************************/
76 // Use logo if it exists as 'practice_logo.gif' in the site dir
77 // old code used the global custom dir which is no longer a valid
79 <table style
="width:7in;">
81 <td style
='width:100px;text-align:top;'>
83 $practice_logo = $GLOBALS['OE_SITE_DIR']."/images/practice_logo.gif";
84 if (file_exists($practice_logo)) {
85 echo "<img src='$practice_logo' align='left' style='width:125px;margin:0px;'><br />\n";
89 <td style
='width:40%;'>
90 <em style
="font-weight:bold;font-size:1.4em;"><?php
echo text($facility['name']); ?
></em
><br
/>
91 <?php
echo text($facility['street']); ?
><br
/>
92 <?php
echo text($facility['city']); ?
>, <?php
echo text($facility['state']); ?
> <?php
echo text($facility['postal_code']); ?
><br
/>
93 <?php
echo xlt('Phone').': ' .text($facility['phone']); ?
><br
/>
94 <?php
echo xlt('Fax').': ' .text($facility['fax']); ?
><br
/>
98 <em style
="font-weight:bold;font-size:1.4em;"><?php
echo text($titleres['fname']) . " " . text($titleres['lname']); ?
></em
><br
/>
99 <b style
="font-weight:bold;"><?php
echo xlt('Chart Number'); ?
>:</b
> <?php
echo text($stmt['pid']); ?
><br
/>
100 <b style
="font-weight:bold;"><?php
echo xlt('Generated on'); ?
>:</b
> <?php
echo text(oeFormatShortDate()); ?
><br
/>
101 <b
><?php
echo xlt('Provider') . ':</b> '; ?
><?php
echo text(getProviderName($providerID)); ?
> <br
/>
106 $output = ob_get_contents();
111 function create_HTML_statement($stmt)
113 if (! $stmt['pid']) {
114 return ""; // get out if no data
117 #minimum_amount_due_to _print
118 if ($stmt['amount'] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) {
122 // Facility (service location)
123 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code,f.attn,f.phone from facility f " .
124 " left join users u on f.id=u.facility_id " .
125 " left join billing b on b.provider_id=u.id and b.pid = ? ".
126 " where service_location=1", array($stmt['pid']));
127 $row = sqlFetchArray($atres);
128 $clinic_name = "{$row['name']}";
129 $clinic_addr = "{$row['street']}";
130 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
132 $billing_contact = "{$row['attn']}";
133 $billing_phone = "{$row['phone']}";
135 $remit_name = $clinic_name;
136 $remit_addr = $clinic_addr;
137 $remit_csz = $clinic_csz;
140 ?
><div style
="padding-left:25px;">
142 $find_provider = sqlQuery("SELECT * FROM form_encounter " .
143 "WHERE pid = ? AND encounter = ? " .
144 "ORDER BY id DESC LIMIT 1", array($stmt['pid'],$stmt['encounter']));
145 $providerID = $find_provider['provider_id'];
146 echo report_header_2($stmt, $direction, $providerID);
148 // dunning message setup
150 // insurance has paid something
151 // $stmt['age'] how old is the invoice
152 // $stmt['dun_count'] number of statements run
153 // $stmt['level_closed'] <= 3 insurance 4 = patient
155 if ($GLOBALS['use_dunning_message']) {
156 if ($stmt['ins_paid'] != 0 ||
$stmt['level_closed'] == 4) {
157 // do collection messages
158 switch ($stmt{'age'}) {
159 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
160 $dun_message = $GLOBALS['first_dun_msg_text'];
162 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
163 $dun_message = $GLOBALS['second_dun_msg_text'];
165 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
166 $dun_message = $GLOBALS['third_dun_msg_text'];
168 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
169 $dun_message = $GLOBALS['fourth_dun_msg_text'];
171 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
172 $dun_message = $GLOBALS['fifth_dun_msg_text'];
180 $label_addressee = xl('ADDRESSED TO');
181 $label_remitto = xl('REMIT TO');
182 $label_chartnum = xl('Chart Number');
183 $label_insinfo = xl('Insurance information on file');
184 $label_totaldue = xl('Total amount due');
185 $label_payby = xl('If paying by');
186 $label_cards = xl('VISA/MC/Discovery/HSA');
187 $label_cardnum = xl('Card');
188 $label_expiry = xl('Exp');
189 $label_cvv = xl('CVV');
190 $label_sign = xl('Signature');
191 $label_retpay = xl('Please return this bottom part with your payment');
192 $label_pgbrk = xl('STATEMENT SUMMARY');
193 $label_visit = xl('Visit Date');
194 $label_desc = xl('Description');
195 $label_amt = xl('Amount');
197 // This is the text for the top part of the page, up to but not
198 // including the detail lines. Some examples of variable fields are:
199 // %s = string with no minimum width
200 // %9s = right-justified string of 9 characters padded with spaces
201 // %-25s = left-justified string of 25 characters padded with spaces
202 // Note that "\n" is a line feed (new line) character.
203 // reformatted to handle i8n by tony
205 $out = "<div style='margin-left:60px;margin-top:20px;'><pre>";
207 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
209 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
212 // This must be set to the number of lines generated above.
217 for ($age_index = 0; $age_index < $num_ages; ++
$age_index) {
218 $aging[$age_index] = 0.00;
221 $todays_time = strtotime(date('Y-m-d'));
223 // This generates the detail lines. Again, note that the values must be specified in the order used.
224 foreach ($stmt['lines'] as $line) {
225 if ($GLOBALS['use_custom_statement']) {
226 $description = substr($line['desc'], 0, 30);
228 $description = $line['desc'];
231 $tmp = substr($description, 0, 14);
232 if ($tmp == 'Procedure 9920' ||
$tmp == 'Procedure 9921' ||
$tmp == 'Procedure 9200' ||
$tmp == 'Procedure 9201') {
233 $description = str_replace("Procedure", xl('Office Visit').":", $description);
236 //92002-14 are Eye Office Visit Codes
239 ksort($line['detail']);
240 # Compute the aging bucket index and accumulate into that bucket.
241 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
242 $age_index = (int) (($age_in_days - 1) / 30);
243 $age_index = max(0, min($num_ages - 1, $age_index));
244 $aging[$age_index] +
= $line['amount'] - $line['paid'];
246 foreach ($line['detail'] as $dkey => $ddata) {
247 $ddate = substr($dkey, 0, 10);
248 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
249 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
255 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
256 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
257 } else if ($ddata['rsn']) {
259 $amount = sprintf("%.2f", $ddata['chg']);
260 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
262 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
264 } else if ($ddata['chg'] < 0) {
265 $amount = sprintf("%.2f", $ddata['chg']);
266 $desc = xl('Patient Payment');
268 $amount = sprintf("%.2f", $ddata['chg']);
269 $desc = $description;
272 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
278 // This generates blank lines until we are at line 20.
279 // At line 20 we start middle third.
281 while ($count++
< 16) {
285 # Generate the string of aging text. This will look like:
286 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
287 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
289 $ageline = xl('Current') .': ' . sprintf("%.2f", $aging[0]);
290 for ($age_index = 1; $age_index < ($num_ages - 1); ++
$age_index) {
291 $ageline .= ' | ' . ($age_index * 30 +
1) . '-' . ($age_index * 30 +
30) . ':' .
292 sprintf(" %.2f", $GLOBALS['gbl_currency_symbol'].''.$aging[$age_index]);
296 $label_ptname = xl('Name');
297 $label_today = xl('Date');
298 $label_due = xl('Due');
299 $label_thanks = xl('Thank you for choosing');
300 $label_call = xl('Please call if any of the above information is incorrect.');
301 $label_prompt = xl('We appreciate prompt payment of balances due.');
302 $label_dept = xl('Billing Department');
303 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ?
$GLOBALS['billing_phone_number'] : $billing_phone );
304 $label_appointments = xl('Future Appointments').':';
306 // This is the top portion of the page.
308 if (strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
309 $out .= sprintf("%-46s\n", $stmt['bill_note']);
313 if ($GLOBALS['use_dunning_message']) {
314 $out .= sprintf("%-46s\n", $dun_message);
320 "%-s: %-25s %-s: %-14s %-s: %8s\n",
324 oeFormatShortDate($stmt['today']),
328 $out .= sprintf("__________________________________________________________________\n");
330 $out .= sprintf("%-s\n", $label_call);
331 $out .= sprintf("%-s\n", $label_prompt);
333 $out .= sprintf("%-s\n", $billing_contact);
334 $out .= sprintf(" %-s %-25s\n", $label_dept, $label_bill_phone);
335 if ($GLOBALS['statement_message_to_patient']) {
337 $statement_message = $GLOBALS['statement_msg_text'];
338 $out .= sprintf("%-40s\n", $statement_message);
342 if ($GLOBALS['show_aging_on_custom_statement']) {
344 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
345 sprintf(" %.2f", $aging[$age_index]);
346 $out .= "\n" . $ageline . "\n\n";
350 if ($GLOBALS['number_appointments_on_statement']!=0) {
352 $num_appts = $GLOBALS['number_appointments_on_statement'];
353 $next_day = mktime(0, 0, 0, date('m'), date('d')+
1, date('Y'));
354 # add one day to date so it will not get todays appointment
355 $current_date2 = date('Y-m-d', $next_day);
356 $events = fetchNextXAppts($current_date2, $stmt['pid'], $num_appts);
358 $out .= sprintf("%-s\n", $label_appointments);
359 #loop to add the appointments
360 for ($x = 1; $x <= $num_appts; $x++
) {
361 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
362 $next_appoint_time = substr($events[$j]['pc_startTime'], 0, 5);
363 if (strlen(umname
) != 0) {
364 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] . ' ' . $events[$j]['ulname'];
366 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
369 if (strlen($next_appoint_time) != 0) {
370 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
371 $out .= sprintf("%-s\n", $label_plsnote[$j]);
379 while ($count++
< 29) {
383 $out .= sprintf("%-10s %s\n", null, $label_retpay);
384 $out .= '</pre></div>';
385 $out .= '<div style="width:7.0in;border-top:1pt dotted black;font-size:12px;margin:0px;"><br /><br />
386 <table style="width:7in;margin-left:20px;"><tr><td style="width:4.5in;"><br />
388 $out .= $label_payby.' '.$label_cards;
389 $out .= "<br /><br />";
390 $out .= $label_cardnum .': __________________________________ '.$label_expiry.': ___ / ____ '.$label_cvv.':____<br /><br />';
391 $out .= $label_sign .' ______________________________________________<br />';
392 $out .=" </td><td style=width:2.0in;vertical-align:middle;'>";
393 $practice_cards = $GLOBALS['OE_SITE_DIR']. "/images/visa_mc_disc_credit_card_logos_176x35.gif";
394 if (file_exists($GLOBALS['OE_SITE_DIR']."/images/visa_mc_disc_credit_card_logos_176x35.gif")) {
395 $out .= "<img src='$practice_cards' style='width:100%; margin:4px auto;'><br /><p>\n<b>" .
396 $label_totaldue . "</b>: " . $stmt['amount']. "<br/>". xlt('Payment Tracking Id') . ": " .
398 $out .= "<br />" . xlt('Amount Paid') . ": _______ " . xlt('Check') . " #:</p>";
400 $out .= "<br /><p><b>" . $label_totaldue . "</b>: " . $stmt['amount'] . "<br/>".
401 xlt('Payment Tracking Id') . ": " . text($stmt['pid']) . "</p>";
402 $out .= "<br /><p>" . xlt('Amount Paid') . ": _______ " . xlt('Check') . " #:</p>";
405 $out .="</td></tr></table>";
407 $out .= '</div><br />
409 if ($stmt['to'][3]!='') { //to avoid double blank lines the if condition is put.
410 $out .= sprintf(" %-32s\n", $stmt['to'][3]);
414 <div style="width:7.0in;border-top:1pt solid black;"><br />';
415 $out .= " <table style='width:7.0in;margin:auto;'><tr>";
416 $out .= '<td style="margin:auto;"></td><td style="width:3.0in;"><b>'
417 .$label_addressee.'</b><br />'
418 .$stmt['to'][0].'<br />'
419 .$stmt['to'][1].'<br />'
421 </td><td style="width:0.5in;"></td>
422 <td style="margin:auto;"><b>'.$label_remitto.'</b><br />'
423 .$remit_name.'<br />'
424 .$remit_addr.'<br />'
429 $out .= " </div></div>";
431 <br /><br />"; // this is a form feed
433 $output = ob_get_clean();
437 // This function builds a printable statement or collection letter from
438 // an associative array having the following keys:
440 // today = statement date yyyy-mm-dd
442 // patient = patient name
443 // amount = total amount due
444 // to = array of addressee name/address lines
445 // lines = array of lines, each with the following keys:
446 // dos = date of service yyyy-mm-dd
447 // desc = description
448 // amount = charge less adjustments
449 // paid = amount paid
450 // notice = 1 for first notice, 2 for second, etc.
451 // detail = associative array of details
453 // Each detail array is keyed on a string beginning with a date in
454 // yyyy-mm-dd format, or blanks in the case of the original charge
455 // items. Its values are associative arrays like this:
457 // pmt - payment amount as a positive number, only for payments
458 // src - check number or other source, only for payments
459 // chg - invoice line item amount amount, only for charges or
460 // adjustments (adjustments may be zero)
461 // rsn - adjustment reason, only for adjustments
463 // The returned value is a string that can be sent to a printer.
464 // This example is plain text, but if you are a hotshot programmer
465 // then you could make a PDF or PostScript or whatever peels your
466 // banana. These strings are sent in succession, so append a form
467 // feed if that is appropriate.
470 // A sample of the text based format follows:
472 //[Your Clinic Name] Patient Name 2009-12-29
473 //[Your Clinic Address] Chart Number: 1848
474 //[City, State Zip] Insurance information on file
478 //Patient Name [Your Clinic Name]
479 //patient address [Your Clinic Address]
480 //city, state zipcode [City, State Zip]
481 // If paying by VISA/MC/AMEX/Dis
483 //Card_____________________ Exp______ Signature___________________
484 // Return above part with your payment
485 //-----------------------------------------------------------------
487 //_______________________ STATEMENT SUMMARY _______________________
489 //Visit Date Description Amount
491 //2009-08-20 Procedure 99345 198.90
492 // Paid 2009-12-15: -51.50
493 //... more details ...
496 // skipping blanks in example
499 //Name: Patient Name Date: 2009-12-29 Due: 147.40
500 //_________________________________________________________________
502 //Please call if any of the above information is incorrect
503 //We appreciate prompt payment of balances due
505 //[Your billing contact name]
506 // Billing Department
507 // [Your billing dept phone]
509 function create_statement($stmt)
511 if (! $stmt['pid']) {
512 return ""; // get out if no data
515 #minimum_amount_to _print
516 if ($stmt[amount
] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) {
520 // These are your clinics return address, contact etc. Edit them.
521 // TBD: read this from the facility table
523 // Facility (service location)
524 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code from facility f " .
525 " left join users u on f.id=u.facility_id " .
526 " left join billing b on b.provider_id=u.id and b.pid = '".$stmt['pid']."' " .
527 " where service_location=1");
528 $row = sqlFetchArray($atres);
530 // Facility (service location)
532 $clinic_name = "{$row['name']}";
533 $clinic_addr = "{$row['street']}";
534 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
538 $remit_name = $clinic_name;
539 $remit_addr = $clinic_addr;
540 $remit_csz = $clinic_csz;
543 $atres = sqlStatement("select f.attn,f.phone from facility f " .
544 " left join users u on f.id=u.facility_id " .
545 " left join billing b on b.provider_id=u.id and b.pid = '".$stmt['pid']."' " .
546 " where billing_location=1");
547 $row = sqlFetchArray($atres);
548 $billing_contact = "{$row['attn']}";
549 $billing_phone = "{$row['phone']}";
551 // dunning message setup
553 // insurance has paid something
554 // $stmt['age'] how old is the invoice
555 // $stmt['dun_count'] number of statements run
556 // $stmt['level_closed'] <= 3 insurance 4 = patient
558 if ($GLOBALS['use_dunning_message']) {
559 if ($stmt['ins_paid'] != 0 ||
$stmt['level_closed'] == 4) {
560 // do collection messages
561 switch ($stmt{'age'}) {
562 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
563 $dun_message = $GLOBALS['first_dun_msg_text'];
565 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
566 $dun_message = $GLOBALS['second_dun_msg_text'];
568 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
569 $dun_message = $GLOBALS['third_dun_msg_text'];
571 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
572 $dun_message = $GLOBALS['fourth_dun_msg_text'];
574 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
575 $dun_message = $GLOBALS['fifth_dun_msg_text'];
583 $label_addressee = xl('ADDRESSED TO');
584 $label_remitto = xl('REMIT TO');
585 $label_chartnum = xl('Chart Number');
586 $label_insinfo = xl('Insurance information on file');
587 $label_totaldue = xl('Total amount due');
588 $label_payby = xl('If paying by');
589 $label_cards = xl('VISA/MC/Discovery/HSA');
590 $label_cardnum = xl('Card');
591 $label_expiry = xl('Exp');
592 $label_cvv = xl('CVV');
593 $label_sign = xl('Signature');
594 $label_retpay = xl('Return above part with your payment');
595 $label_pgbrk = xl('STATEMENT SUMMARY');
596 $label_visit = xl('Visit Date');
597 $label_desc = xl('Description');
598 $label_amt = xl('Amount');
600 // This is the text for the top part of the page, up to but not
601 // including the detail lines. Some examples of variable fields are:
602 // %s = string with no minimum width
603 // %9s = right-justified string of 9 characters padded with spaces
604 // %-25s = left-justified string of 25 characters padded with spaces
605 // Note that "\n" is a line feed (new line) character.
606 // reformatted to handle i8n by tony
608 $providerNAME = getProviderName($stmt['providerID']);
609 $out .= sprintf("%-30s %s %-s\n", $clinic_name, $stmt['patient'], $stmt['today']);
610 $out .= sprintf("%-30s %s: %-s\n", $providerNAME, $label_chartnum, $stmt['pid']);
611 $out .= sprintf("%-30s %s\n", $clinic_addr, $label_insinfo);
612 $out .= sprintf("%-30s %-s: %-s\n", $clinic_csz, $label_totaldue, $stmt['amount']);
614 $out .= sprintf(" %-30s %-s\n", $label_addressee, $label_remitto);
615 $out .= sprintf(" %-30s %s\n", $stmt['to'][0], $remit_name);
616 $out .= sprintf(" %-30s %s\n", $stmt['to'][1], $remit_addr);
617 $out .= sprintf(" %-30s %s\n", $stmt['to'][2], $remit_csz);
619 if ($stmt['to'][3]!='') { //to avoid double blank lines the if condition is put.
620 $out .= sprintf(" %-32s\n", $stmt['to'][3]);
623 $out .= sprintf("_________________________________________________________________\n");
625 $out .= sprintf("%-32s\n", $label_payby.' '.$label_cards);
628 "%s_____________________ %s______ %s______ %s___________________\n\n",
634 $out .= sprintf("-----------------------------------------------------------------\n");
635 $out .= sprintf("%-20s %s\n", null, $label_retpay);
637 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
639 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
642 // This must be set to the number of lines generated above.
647 for ($age_index = 0; $age_index < $num_ages; ++
$age_index) {
648 $aging[$age_index] = 0.00;
651 $todays_time = strtotime(date('Y-m-d'));
653 // This generates the detail lines. Again, note that the values must
654 // be specified in the order used.
658 foreach ($stmt['lines'] as $line) {
659 if ($GLOBALS['use_custom_statement']) {
660 $description = substr($line['desc'], 0, 30);
662 $description = $line['desc'];
665 $tmp = substr($description, 0, 14);
666 if ($tmp == 'Procedure 9920' ||
$tmp == 'Procedure 9921' ||
$tmp == 'Procedure 9200' ||
$tmp == 'Procedure 9201') {
667 $description = str_replace("Procedure", xl('Office Visit').":", $description);
670 //92002-14 are Eye Office Visit Codes
673 ksort($line['detail']);
674 # Compute the aging bucket index and accumulate into that bucket.
676 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
677 $age_index = (int) (($age_in_days - 1) / 30);
678 $age_index = max(0, min($num_ages - 1, $age_index));
679 $aging[$age_index] +
= $line['amount'] - $line['paid'];
681 foreach ($line['detail'] as $dkey => $ddata) {
682 $ddate = substr($dkey, 0, 10);
683 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
684 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
690 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
691 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
692 } else if ($ddata['rsn']) {
694 $amount = sprintf("%.2f", $ddata['chg']);
695 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
697 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
699 } else if ($ddata['chg'] < 0) {
700 $amount = sprintf("%.2f", $ddata['chg']);
701 $desc = xl('Patient Payment');
703 $amount = sprintf("%.2f", $ddata['chg']);
704 $desc = $description;
707 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
713 // This generates blank lines until we are at line 42.
715 while ($count++
< 42) {
719 # Generate the string of aging text. This will look like:
720 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
721 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
723 $ageline = xl('Current') .' ' . sprintf("%.2f", $aging[0]);
724 for ($age_index = 1; $age_index < ($num_ages - 1); ++
$age_index) {
725 $ageline .= ' / ' . ($age_index * 30 +
1) . '-' . ($age_index * 30 +
30) .
726 sprintf(" %.2f", $aging[$age_index]);
730 $label_ptname = xl('Name');
731 $label_today = xl('Date');
732 $label_due = xl('Amount Due');
733 $label_thanks = xl('Thank you for choosing');
734 $label_call = xl('Please call if any of the above information is incorrect.');
735 $label_prompt = xl('We appreciate prompt payment of balances due.');
736 $label_dept = xl('Billing Department');
737 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ?
$GLOBALS['billing_phone_number'] : $billing_phone );
738 $label_appointments = xl('Future Appointments').':';
740 // This is the bottom portion of the page.
742 if (strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
743 $out .= sprintf("%-46s\n", $stmt['bill_note']);
746 if ($GLOBALS['use_dunning_message']) {
747 $out .= sprintf("%-46s\n", $dun_message);
752 "%-s: %-25s %-s: %-14s %-s: %8s\n",
756 oeFormatShortDate($stmt['today']),
760 $out .= sprintf("__________________________________________________________________\n");
762 $out .= sprintf("%-s\n", $label_call);
763 $out .= sprintf("%-s\n", $label_prompt);
765 $out .= sprintf("%-s\n", $billing_contact);
766 $out .= sprintf(" %-s %-25s\n", $label_dept, $label_bill_phone);
767 if ($GLOBALS['statement_message_to_patient']) {
769 $statement_message = $GLOBALS['statement_msg_text'];
770 $out .= sprintf("%-40s\n", $statement_message);
773 if ($GLOBALS['show_aging_on_custom_statement']) {
775 $ageline .= ' / ' . xl('Over') . '-' . ($age_index * 30) .
776 sprintf(" %.2f", $aging[$age_index]);
777 $out .= "\n" . $ageline . "\n\n";
780 if ($GLOBALS['number_appointments_on_statement']!=0) {
782 $num_appts = $GLOBALS['number_appointments_on_statement'];
783 $next_day = mktime(0, 0, 0, date('m'), date('d')+
1, date('Y'));
784 # add one day to date so it will not get todays appointment
785 $current_date2 = date('Y-m-d', $next_day);
786 $events = fetchNextXAppts($current_date2, $stmt['pid'], $num_appts);
788 $out .= sprintf("%-s\n", $label_appointments);
789 #loop to add the appointments
790 for ($x = 1; $x <= $num_appts; $x++
) {
791 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
792 $next_appoint_time = substr($events[$j]['pc_startTime'], 0, 5);
793 if (strlen(umname
) != 0) {
794 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] .
795 ' ' . $events[$j]['ulname'];
797 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
800 if (strlen($next_appoint_time) != 0) {
801 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') .
802 ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
803 $out .= sprintf("%-s\n", $label_plsnote[$j]);
810 $out .= "\014"; // this is a form feed
815 function osp_create_HTML_statement($stmt)
817 if (! $stmt['pid']) {
818 return ""; // get out if no data
821 #minimum_amount_due_to _print
822 if ($stmt['amount'] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) {
826 // Facility (service location)
827 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code,f.attn,f.phone from facility f " .
828 " left join users u on f.id=u.facility_id " .
829 " left join billing b on b.provider_id=u.id and b.pid = ? ".
830 " where service_location=1", array($stmt['pid']));
831 $row = sqlFetchArray($atres);
832 $clinic_name = "{$row['name']}";
833 $clinic_addr = "{$row['street']}";
834 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
836 $billing_contact = "{$row['attn']}";
837 $billing_phone = "{$row['phone']}";
839 $remit_name = $clinic_name;
840 $remit_addr = $clinic_addr;
841 $remit_csz = $clinic_csz;
844 ?
><div style
="padding-left:25px;">
846 $find_provider = sqlQuery("SELECT * FROM form_encounter " .
847 "WHERE pid = ? AND encounter = ? " .
848 "ORDER BY id DESC LIMIT 1", array($stmt['pid'],$stmt['encounter']));
849 $providerID = $find_provider['provider_id'];
850 echo report_header_2($stmt, $direction, $providerID);
852 // dunning message setup
854 // insurance has paid something
855 // $stmt['age'] how old is the invoice
856 // $stmt['dun_count'] number of statements run
857 // $stmt['level_closed'] <= 3 insurance 4 = patient
859 if ($GLOBALS['use_dunning_message']) {
860 if ($stmt['ins_paid'] != 0 ||
$stmt['level_closed'] == 4) {
861 // do collection messages
862 switch ($stmt{'age'}) {
863 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
864 $dun_message = $GLOBALS['first_dun_msg_text'];
866 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
867 $dun_message = $GLOBALS['second_dun_msg_text'];
869 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
870 $dun_message = $GLOBALS['third_dun_msg_text'];
872 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
873 $dun_message = $GLOBALS['fourth_dun_msg_text'];
875 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
876 $dun_message = $GLOBALS['fifth_dun_msg_text'];
884 $label_addressee = xl('ADDRESSED TO');
885 $label_remitto = xl('REMIT TO');
886 $label_chartnum = xl('Chart Number');
887 $label_insinfo = xl('Insurance information on file');
888 $label_totaldue = xl('Total amount due');
889 $label_payby = xl('If paying by');
890 $label_cards = xl('VISA/MC/Discovery/HSA');
891 $label_cardnum = xl('Card');
892 $label_expiry = xl('Exp');
893 $label_sign = xl('Signature');
894 $label_retpay = xl('Please fill in credit information and send for review.');
895 $label_pgbrk = xl('STATEMENT SUMMARY');
896 $label_visit = xl('Visit Date');
897 $label_desc = xl('Description');
898 $label_amt = xl('Amount');
900 // This is the text for the top part of the page, up to but not
901 // including the detail lines. Some examples of variable fields are:
902 // %s = string with no minimum width
903 // %9s = right-justified string of 9 characters padded with spaces
904 // %-25s = left-justified string of 25 characters padded with spaces
905 // Note that "\n" is a line feed (new line) character.
906 // reformatted to handle i8n by tony
908 $out = "<div style='margin-left:60px;margin-top:0px;'>";
910 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
912 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
915 // This must be set to the number of lines generated above.
920 for ($age_index = 0; $age_index < $num_ages; ++
$age_index) {
921 $aging[$age_index] = 0.00;
924 $todays_time = strtotime(date('Y-m-d'));
926 // This generates the detail lines. Again, note that the values must be specified in the order used.
927 foreach ($stmt['lines'] as $line) {
928 if ($GLOBALS['use_custom_statement']) {
929 $description = substr($line['desc'], 0, 30);
931 $description = $line['desc'];
934 $tmp = substr($description, 0, 14);
935 if ($tmp == 'Procedure 9920' ||
$tmp == 'Procedure 9921' ||
$tmp == 'Procedure 9200' ||
$tmp == 'Procedure 9201') {
936 $description = str_replace("Procedure", xl('Office Visit').":", $description);
939 //92002-14 are Eye Office Visit Codes
942 ksort($line['detail']);
943 # Compute the aging bucket index and accumulate into that bucket.
944 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
945 $age_index = (int) (($age_in_days - 1) / 30);
946 $age_index = max(0, min($num_ages - 1, $age_index));
947 $aging[$age_index] +
= $line['amount'] - $line['paid'];
949 foreach ($line['detail'] as $dkey => $ddata) {
950 $ddate = substr($dkey, 0, 10);
951 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
952 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
958 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
959 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
960 } else if ($ddata['rsn']) {
962 $amount = sprintf("%.2f", $ddata['chg']);
963 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
965 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
967 } else if ($ddata['chg'] < 0) {
968 $amount = sprintf("%.2f", $ddata['chg']);
969 $desc = xl('Patient Payment');
971 $amount = sprintf("%.2f", $ddata['chg']);
972 $desc = $description;
975 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
981 // This generates blank lines until we are at line 20.
982 // At line 20 we start middle third.
984 //while ($count++ < 16) $out .= "\n";
985 # Generate the string of aging text. This will look like:
986 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
987 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
989 $ageline = xl('Current') .': ' . sprintf("%.2f", $aging[0]);
990 for ($age_index = 1; $age_index < ($num_ages - 1); ++
$age_index) {
991 $ageline .= ' | ' . ($age_index * 30 +
1) . '-' . ($age_index * 30 +
30) . ':' .
992 sprintf(" %.2f", $GLOBALS['gbl_currency_symbol'].''.$aging[$age_index]);
996 $label_ptname = xl('Name');
997 $label_today = xl('Date');
998 $label_due = xl('Due');
999 $label_thanks = xl('Thank you for choosing');
1000 $label_call = xl('Please call or message if any of the above information is incorrect.');
1001 $label_prompt = xl('We appreciate prompt payment of balances due.');
1002 $label_dept = xl('Billing Department');
1003 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ?
$GLOBALS['billing_phone_number'] : $billing_phone );
1004 $label_appointments = xl('Future Appointments').':';
1006 // This is the top portion of the page.
1008 if (strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
1009 $out .= sprintf("%-46s\n", $stmt['bill_note']);
1013 if ($GLOBALS['use_dunning_message']) {
1014 $out .= sprintf("%-46s\n", $dun_message);
1020 "%-s: %-25s %-s: %-14s %-s: %8s\n",
1024 oeFormatShortDate($stmt['today']),
1028 $out .= sprintf("__________________________________________________________________\n");
1030 $out .= sprintf("%-s\n", $label_call);
1031 $out .= sprintf("%-s\n", $label_prompt);
1033 $out .= sprintf("%-s\n", $billing_contact);
1034 $out .= sprintf(" %-s %-25s\n", $label_dept, $label_bill_phone);
1035 if ($GLOBALS['statement_message_to_patient']) {
1037 $statement_message = $GLOBALS['statement_msg_text'];
1038 $out .= sprintf("%-40s\n", $statement_message);
1042 if ($GLOBALS['show_aging_on_custom_statement']) {
1044 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
1045 sprintf(" %.2f", $aging[$age_index]);
1046 $out .= "\n" . $ageline . "\n\n";
1050 if ($GLOBALS['number_appointments_on_statement']!=0) {
1052 $num_appts = $GLOBALS['number_appointments_on_statement'];
1053 $next_day = mktime(0, 0, 0, date('m'), date('d')+
1, date('Y'));
1054 # add one day to date so it will not get todays appointment
1055 $current_date2 = date('Y-m-d', $next_day);
1056 $events = fetchNextXAppts($current_date2, $stmt['pid'], $num_appts);
1058 $out .= sprintf("%-s\n", $label_appointments);
1059 #loop to add the appointments
1060 for ($x = 1; $x <= $num_appts; $x++
) {
1061 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
1062 $next_appoint_time = substr($events[$j]['pc_startTime'], 0, 5);
1063 if (strlen(umname
) != 0) {
1064 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] . ' ' . $events[$j]['ulname'];
1066 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
1069 if (strlen($next_appoint_time) != 0) {
1070 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
1071 $out .= sprintf("%-s\n", $label_plsnote[$j]);
1079 // while ($count++ < 29) $out .= "\n";
1080 $out .= sprintf("%-10s %s\n", null, $label_retpay);
1081 $out .= '</pre></div>';
1082 $out .= '<div style="width:7.0in;border-top:1pt dotted black;font-size:12px;margin:0px;"><br /><br />
1083 <table style="width:8in;margin-left:20px;"><tr><td style="width:4.5in;"><br />
1085 $out .= $label_payby.' '.$label_cards;
1086 $out .= "<br /><br />";
1087 $out .= $label_cardnum .': {TextInput} '.$label_expiry.': {smTextInput} / {smTextInput} <br /><br />';
1088 $out .= $label_sign .' {PatientSignature}<br />';
1089 $out .=" </td><td style=width:2.0in;vertical-align:middle;'>";
1090 $practice_cards = $GLOBALS['OE_SITE_DIR']. "/images/visa_mc_disc_credit_card_logos_176x35.gif";
1091 if (file_exists($GLOBALS['OE_SITE_DIR']."/images/visa_mc_disc_credit_card_logos_176x35.gif")) {
1092 //$out .= "<img onclick='getPayment()' src='$practice_cards' style='width:100%;margin:4px auto;'><br /><p>\n".$label_totaldue.": ".$stmt['amount']."</p>";
1093 $out .= "<br /><p>".$label_totaldue.": ".$stmt['amount']."</p>";
1096 $out .="</td></tr></table>";
1098 $out .= '</div><br />';
1099 if ($stmt['to'][3]!='') { //to avoid double blank lines the if condition is put.
1100 $out .= sprintf(" %-32s\n", $stmt['to'][3]);
1104 <div style="width:8in;border-top:1pt solid black;"><br />';
1105 $out .= " <table style='width:6.0in;margin-left:40px;'><tr>";
1106 $out .= '<td style="width:3.0in;"><b>'
1107 .$label_addressee.'</b><br />'
1108 .$stmt['to'][0].'<br />'
1109 .$stmt['to'][1].'<br />'
1112 <td style="width:3.0in;"><b>'.$label_remitto.'</b><br />'
1113 .$remit_name.'<br />'
1114 .$remit_addr.'<br />'
1119 $out .= " </div></div>";
1121 <br /><br />"; // this is a form feed
1123 $output = ob_get_clean();