some fixes for php-fpm dev docker
[openemr.git] / sites / default / statement.inc.php
blob8d6d149c2338c0c9b3bed9e9fe22a061b8d998e8
1 <?php
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].
8 * @package OpenEMR
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);
49 } else {
50 return create_HTML_statement($stmt);
52 } else {
53 return create_statement($stmt);
56 /**
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']));
68 } else {
69 $sql = "SELECT * FROM facility ORDER BY billing_location DESC LIMIT 1";
70 $facility = sqlQuery($sql);
73 $DOB = oeFormatShortDate($titleres['DOB']);
74 /******************************************************************/
75 ob_start();
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;">
80 <tr>
81 <td style='width:100px;text-align:top;'>
82 <?php
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";
88 </td>
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 />
95 <br clear='all' />
96 </td>
97 <td>
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 />
102 </td>
103 </tr>
104 </table>
105 <?php
106 $output = ob_get_contents();
107 ob_end_clean();
108 return $output;
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']) {
119 return "";
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']}";
131 // Contacts
132 $billing_contact = "{$row['attn']}";
133 $billing_phone = "{$row['phone']}";
134 // Billing location
135 $remit_name = $clinic_name;
136 $remit_addr = $clinic_addr;
137 $remit_csz = $clinic_csz;
139 ob_start();
140 ?><div style="padding-left:25px;">
141 <?php
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'];
161 break;
162 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
163 $dun_message = $GLOBALS['second_dun_msg_text'];
164 break;
165 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
166 $dun_message = $GLOBALS['third_dun_msg_text'];
167 break;
168 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
169 $dun_message = $GLOBALS['fourth_dun_msg_text'];
170 break;
171 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
172 $dun_message = $GLOBALS['fifth_dun_msg_text'];
173 break;
178 // Text only labels
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>";
206 $out .= "\n";
207 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
208 $out .= "\n";
209 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
210 $out .= "\n";
212 // This must be set to the number of lines generated above.
214 $count = 6;
215 $num_ages = 4;
216 $aging = array();
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);
227 } else {
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
238 $dos = $line['dos'];
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];
252 $amount = '';
254 if ($ddata['pmt']) {
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']) {
258 if ($ddata['chg']) {
259 $amount = sprintf("%.2f", $ddata['chg']);
260 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
261 } else {
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');
267 } else {
268 $amount = sprintf("%.2f", $ddata['chg']);
269 $desc = $description;
272 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
273 $dos = '';
274 ++$count;
278 // This generates blank lines until we are at line 20.
279 // At line 20 we start middle third.
281 while ($count++ < 16) {
282 $out .= "\n";
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]);
295 // Fixed text labels
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.
307 $out .= "\n\n\n";
308 if (strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
309 $out .= sprintf("%-46s\n", $stmt['bill_note']);
310 $count++;
313 if ($GLOBALS['use_dunning_message']) {
314 $out .= sprintf("%-46s\n", $dun_message);
315 $count++;
318 $out .= "\n";
319 $out .= sprintf(
320 "%-s: %-25s %-s: %-14s %-s: %8s\n",
321 $label_ptname,
322 $stmt['patient'],
323 $label_today,
324 oeFormatShortDate($stmt['today']),
325 $label_due,
326 $stmt['amount']
328 $out .= sprintf("__________________________________________________________________\n");
329 $out .= "\n";
330 $out .= sprintf("%-s\n", $label_call);
331 $out .= sprintf("%-s\n", $label_prompt);
332 $out .= "\n";
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']) {
336 $out .= "\n";
337 $statement_message = $GLOBALS['statement_msg_text'];
338 $out .= sprintf("%-40s\n", $statement_message);
339 $count++;
342 if ($GLOBALS['show_aging_on_custom_statement']) {
343 # code for ageing
344 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
345 sprintf(" %.2f", $aging[$age_index]);
346 $out .= "\n" . $ageline . "\n\n";
347 $count++;
350 if ($GLOBALS['number_appointments_on_statement']!=0) {
351 $out .= "\n";
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);
357 $j=0;
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'];
365 } else {
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]);
374 $j++;
375 $count++;
379 while ($count++ < 29) {
380 $out .= "\n";
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') . ": " .
397 text($stmt['pid']);
398 $out .= "<br />" . xlt('Amount Paid') . ": _______ " . xlt('Check') . " #:</p>";
399 } else {
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 />
408 <pre>';
409 if ($stmt['to'][3]!='') { //to avoid double blank lines the if condition is put.
410 $out .= sprintf(" %-32s\n", $stmt['to'][3]);
413 $out .= ' </pre>
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 />'
420 .$stmt['to'][2].'
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 />'
425 .$remit_csz.'
426 </td>
427 </tr></table>';
429 $out .= " </div></div>";
430 $out .= "\014
431 <br /><br />"; // this is a form feed
432 echo $out;
433 $output = ob_get_clean();
434 return $output;
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
441 // pid = patient ID
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
477 //ADDRESSEE REMIT TO
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 ...
494 //...
495 //...
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']) {
517 return "";
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']}";
537 // Billing location
538 $remit_name = $clinic_name;
539 $remit_addr = $clinic_addr;
540 $remit_csz = $clinic_csz;
542 // Contacts
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'];
564 break;
565 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
566 $dun_message = $GLOBALS['second_dun_msg_text'];
567 break;
568 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
569 $dun_message = $GLOBALS['third_dun_msg_text'];
570 break;
571 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
572 $dun_message = $GLOBALS['fourth_dun_msg_text'];
573 break;
574 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
575 $dun_message = $GLOBALS['fifth_dun_msg_text'];
576 break;
581 // Text only labels
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
607 $out = "\n\n";
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']);
613 $out .= "\n";
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");
624 $out .= "\n";
625 $out .= sprintf("%-32s\n", $label_payby.' '.$label_cards);
626 $out .= "\n";
627 $out .= sprintf(
628 "%s_____________________ %s______ %s______ %s___________________\n\n",
629 $label_cardnum,
630 $label_expiry,
631 $label_cvv,
632 $label_sign
634 $out .= sprintf("-----------------------------------------------------------------\n");
635 $out .= sprintf("%-20s %s\n", null, $label_retpay);
636 $out .= "\n";
637 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
638 $out .= "\n";
639 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
640 $out .= "\n";
642 // This must be set to the number of lines generated above.
644 $count = 25;
645 $num_ages = 4;
646 $aging = array();
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);
661 } else {
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
672 $dos = $line['dos'];
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];
687 $amount = '';
689 if ($ddata['pmt']) {
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']) {
693 if ($ddata['chg']) {
694 $amount = sprintf("%.2f", $ddata['chg']);
695 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
696 } else {
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');
702 } else {
703 $amount = sprintf("%.2f", $ddata['chg']);
704 $desc = $description;
707 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
708 $dos = '';
709 ++$count;
713 // This generates blank lines until we are at line 42.
715 while ($count++ < 42) {
716 $out .= "\n";
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]);
729 // Fixed text labels
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.
741 $out .= "\n";
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);
750 $out .= "\n";
751 $out .= sprintf(
752 "%-s: %-25s %-s: %-14s %-s: %8s\n",
753 $label_ptname,
754 $stmt['patient'],
755 $label_today,
756 oeFormatShortDate($stmt['today']),
757 $label_due,
758 $stmt['amount']
760 $out .= sprintf("__________________________________________________________________\n");
761 $out .= "\n";
762 $out .= sprintf("%-s\n", $label_call);
763 $out .= sprintf("%-s\n", $label_prompt);
764 $out .= "\n";
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']) {
768 $out .= "\n";
769 $statement_message = $GLOBALS['statement_msg_text'];
770 $out .= sprintf("%-40s\n", $statement_message);
773 if ($GLOBALS['show_aging_on_custom_statement']) {
774 # code for ageing
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) {
781 $out .= "\n";
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);
787 $j=0;
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'];
796 } else {
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]);
806 $j++;
810 $out .= "\014"; // this is a form feed
812 return $out;
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']) {
823 return "";
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']}";
835 // Contacts
836 $billing_contact = "{$row['attn']}";
837 $billing_phone = "{$row['phone']}";
838 // Billing location
839 $remit_name = $clinic_name;
840 $remit_addr = $clinic_addr;
841 $remit_csz = $clinic_csz;
843 ob_start();
844 ?><div style="padding-left:25px;">
845 <?php
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'];
865 break;
866 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
867 $dun_message = $GLOBALS['second_dun_msg_text'];
868 break;
869 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
870 $dun_message = $GLOBALS['third_dun_msg_text'];
871 break;
872 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
873 $dun_message = $GLOBALS['fourth_dun_msg_text'];
874 break;
875 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
876 $dun_message = $GLOBALS['fifth_dun_msg_text'];
877 break;
882 // Text only labels
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;'>";
909 $out .= "\n";
910 $out .= sprintf("_______________________ %s _______________________\n", $label_pgbrk);
911 $out .= "\n";
912 $out .= sprintf("%-11s %-46s %s\n", $label_visit, $label_desc, $label_amt);
913 $out .= "\n";
915 // This must be set to the number of lines generated above.
917 $count = 6;
918 $num_ages = 4;
919 $aging = array();
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);
930 } else {
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
941 $dos = $line['dos'];
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];
955 $amount = '';
957 if ($ddata['pmt']) {
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']) {
961 if ($ddata['chg']) {
962 $amount = sprintf("%.2f", $ddata['chg']);
963 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
964 } else {
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');
970 } else {
971 $amount = sprintf("%.2f", $ddata['chg']);
972 $desc = $description;
975 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
976 $dos = '';
977 ++$count;
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]);
995 // Fixed text labels
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.
1007 $out .= "\n";
1008 if (strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
1009 $out .= sprintf("%-46s\n", $stmt['bill_note']);
1010 $count++;
1013 if ($GLOBALS['use_dunning_message']) {
1014 $out .= sprintf("%-46s\n", $dun_message);
1015 $count++;
1018 $out .= "\n";
1019 $out .= sprintf(
1020 "%-s: %-25s %-s: %-14s %-s: %8s\n",
1021 $label_ptname,
1022 $stmt['patient'],
1023 $label_today,
1024 oeFormatShortDate($stmt['today']),
1025 $label_due,
1026 $stmt['amount']
1028 $out .= sprintf("__________________________________________________________________\n");
1029 $out .= "\n";
1030 $out .= sprintf("%-s\n", $label_call);
1031 $out .= sprintf("%-s\n", $label_prompt);
1032 $out .= "\n";
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']) {
1036 $out .= "\n";
1037 $statement_message = $GLOBALS['statement_msg_text'];
1038 $out .= sprintf("%-40s\n", $statement_message);
1039 $count++;
1042 if ($GLOBALS['show_aging_on_custom_statement']) {
1043 # code for ageing
1044 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
1045 sprintf(" %.2f", $aging[$age_index]);
1046 $out .= "\n" . $ageline . "\n\n";
1047 $count++;
1050 if ($GLOBALS['number_appointments_on_statement']!=0) {
1051 $out .= "\n";
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);
1057 $j=0;
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'];
1065 } else {
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]);
1074 $j++;
1075 $count++;
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]);
1103 $out .= ' </pre>
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 />'
1110 .$stmt['to'][2].'
1111 </td>
1112 <td style="width:3.0in;"><b>'.$label_remitto.'</b><br />'
1113 .$remit_name.'<br />'
1114 .$remit_addr.'<br />'
1115 .$remit_csz.'
1116 </td>
1117 </tr></table>';
1119 $out .= " </div></div>";
1120 $out .= "\014
1121 <br /><br />"; // this is a form feed
1122 echo $out;
1123 $output = ob_get_clean();
1124 return $output;