New onsite patient portal, take 4.
[openemr.git] / sites / default / statement.inc.php
blob9804804bc683b134d455f2bbc2629b4fe0fc0ee5
1 <?php
3 // Copyright (C) 2005-2006 Rod Roark <rod@sunsetsystems.com>
4 // Copyright (C) 2016 Raymond Magauran <magauran@medfetch.com>
5 //
6 // Windows compatibility mods 2009 Bill Cernansky [mi-squared.com]
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
13 // Updated by Medical Information Integration, LLC to support download
14 // and multi OS use - tony@mi-squared..com 12-2009
16 //////////////////////////////////////////////////////////////////////
17 // This is a template for printing patient statements and collection
18 // letters. You must customize it to suit your practice. If your
19 // needs are simple then you do not need programming experience to do
20 // this - just read the comments and make appropriate substitutions.
21 // All you really need to do is replace the [strings in brackets].
22 //////////////////////////////////////////////////////////////////////
24 // The location/name of a temporary file to hold printable statements.
25 // May want to alter these names to allow multi-site installs out-of-the-box
27 $STMT_TEMP_FILE = $GLOBALS['temporary_files_dir'] . "/openemr_statements.txt";
28 $STMT_TEMP_FILE_PDF = $GLOBALS['temporary_files_dir'] . "/openemr_statements.pdf";
29 $STMT_PRINT_CMD = $GLOBALS['print_command'];
31 /** There are two options to print a batch of PDF statements:
32 * 1. The original statement, a text based statement, using CezPDF
33 * Altering this statement is labor intensive, but capable of being altered any way desired...
35 * 2. Branded Statement, whose core is build from 1., the original statement, using HTML2PDF.
37 * To customize 2., add your practice location/images/practice_logo.gif
38 * In the base/default install this is located at '/openemr/sites/default/images/practice_logo.gif',
39 * Adjust directory paths per your installation.
40 * Further customize 2. manually in functions report_2() and create_HTML_statement(), below.
43 function make_statement($stmt) {
44 if ($GLOBALS['statement_appearance'] == "1") {
45 if(is_auth_portal($stmt['pid']) && $_POST['form_portalnotify'])
46 return osp_create_HTML_statement($stmt);
47 else
48 return create_HTML_statement($stmt);
49 } else {
50 return create_statement($stmt);
53 /**
54 * This prints a header for documents. Keeps the brand uniform...
55 * @param string $pid patient_id
56 * @param string $direction, options "web" or anything else. Web provides apache-friendly url links.
57 * @return outputs to be displayed however requested
59 function report_header_2($stmt,$direction='',$providerID='1') {
60 $titleres = getPatientData($stmt['pid'], "fname,lname,DOB");
61 if ($_SESSION['pc_facility']) {
62 $sql = "select * from facility where id=?";
63 $facility = sqlQuery($sql,array($_SESSION['pc_facility']));
64 } else {
65 $sql = "SELECT * FROM facility ORDER BY billing_location DESC LIMIT 1";
66 $facility = sqlQuery($sql);
68 $DOB = oeFormatShortDate($titleres['DOB']);
69 /******************************************************************/
70 ob_start();
71 // Use logo if it exists as 'practice_logo.gif' in the site dir
72 // old code used the global custom dir which is no longer a valid
74 <table style="width:7in;">
75 <tr>
76 <td style='width:100px;text-align:top;'>
77 <?php
78 $practice_logo = $GLOBALS['OE_SITE_DIR']."/images/practice_logo.gif";
79 if (file_exists($practice_logo)) {
80 echo "<img src='$practice_logo' align='left' style='width:125px;margin:0px;'><br />\n";
83 </td>
84 <td style='width:40%;'>
85 <em style="font-weight:bold;font-size:1.4em;"><?php echo text($facility['name']); ?></em><br />
86 <?php echo text($facility['street']); ?><br />
87 <?php echo text($facility['city']); ?>, <?php echo text($facility['state']); ?> <?php echo text($facility['postal_code']); ?><br />
88 <?php echo xlt('Phone').': ' .text($facility['phone']); ?><br />
89 <?php echo xlt('Fax').': ' .text($facility['fax']); ?><br />
90 <br clear='all' />
91 </td>
92 <td>
93 <em style="font-weight:bold;font-size:1.4em;"><?php echo text($titleres['fname']) . " " . text($titleres['lname']); ?></em><br />
94 <b style="font-weight:bold;"><?php echo xlt('Chart Number'); ?>:</b> <?php echo text($stmt['pid']); ?><br />
95 <b style="font-weight:bold;"><?php echo xlt('Generated on'); ?>:</b> <?php echo oeFormatShortDate(); ?><br />
96 <b><?php echo xlt('Provider') . ':</b> '; ?><?php echo text(getProviderName($providerID)); ?> <br />
97 </td>
98 </tr>
99 </table>
100 <?php
101 $output = ob_get_contents();
102 ob_end_clean();
103 return $output;
106 function create_HTML_statement($stmt) {
107 if (! $stmt['pid']) return ""; // get out if no data
109 #minimum_amount_due_to _print
110 if ($stmt['amount'] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) return "";
112 // Facility (service location)
113 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code,f.attn,f.phone from facility f " .
114 " left join users u on f.id=u.facility_id " .
115 " left join billing b on b.provider_id=u.id and b.pid = ? ".
116 " where service_location=1",array($stmt['pid']));
117 $row = sqlFetchArray($atres);
118 $clinic_name = "{$row['name']}";
119 $clinic_addr = "{$row['street']}";
120 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
121 // Contacts
122 $billing_contact = "{$row['attn']}";
123 $billing_phone = "{$row['phone']}";
124 // Billing location
125 $remit_name = $clinic_name;
126 $remit_addr = $clinic_addr;
127 $remit_csz = $clinic_csz;
129 ob_start();
130 ?><div style="padding-left:25px;">
131 <?php
132 $find_provider = sqlQuery("SELECT * FROM form_encounter " .
133 "WHERE pid = ? AND encounter = ? " .
134 "ORDER BY id DESC LIMIT 1", array($stmt['pid'],$stmt['encounter']) );
135 $providerID = $find_provider['provider_id'];
136 echo report_header_2($stmt,$direction,$providerID);
138 // dunning message setup
140 // insurance has paid something
141 // $stmt['age'] how old is the invoice
142 // $stmt['dun_count'] number of statements run
143 // $stmt['level_closed'] <= 3 insurance 4 = patient
145 if ($GLOBALS['use_dunning_message']) {
146 if ($stmt['ins_paid'] != 0 || $stmt['level_closed'] == 4) {
148 // do collection messages
149 switch ($stmt{'age'}) {
150 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
151 $dun_message = $GLOBALS['first_dun_msg_text'];
152 break;
153 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
154 $dun_message = $GLOBALS['second_dun_msg_text'];
155 break;
156 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
157 $dun_message = $GLOBALS['third_dun_msg_text'];
158 break;
159 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
160 $dun_message = $GLOBALS['fourth_dun_msg_text'];
161 break;
162 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
163 $dun_message = $GLOBALS['fifth_dun_msg_text'];
164 break;
168 // Text only labels
170 $label_addressee = xl('ADDRESSEE');
171 $label_remitto = xl('REMIT TO');
172 $label_chartnum = xl('Chart Number');
173 $label_insinfo = xl('Insurance information on file');
174 $label_totaldue = xl('Total amount due');
175 $label_payby = xl('If paying by');
176 $label_cards = xl('VISA/MC/Discovery/HSA');
177 $label_cardnum = xl('Card');
178 $label_expiry = xl('Exp');
179 $label_sign = xl('Signature');
180 $label_retpay = xl('Please return this bottom part with your payment');
181 $label_pgbrk = xl('STATEMENT SUMMARY');
182 $label_visit = xl('Visit Date');
183 $label_desc = xl('Description');
184 $label_amt = xl('Amount');
186 // This is the text for the top part of the page, up to but not
187 // including the detail lines. Some examples of variable fields are:
188 // %s = string with no minimum width
189 // %9s = right-justified string of 9 characters padded with spaces
190 // %-25s = left-justified string of 25 characters padded with spaces
191 // Note that "\n" is a line feed (new line) character.
192 // reformatted to handle i8n by tony
194 $out = "<div style='margin-left:60px;margin-top:20px;'><pre>";
195 $out .= "\n";
196 $out .= sprintf("_______________________ %s _______________________\n",$label_pgbrk);
197 $out .= "\n";
198 $out .= sprintf("%-11s %-46s %s\n",$label_visit,$label_desc,$label_amt);
199 $out .= "\n";
201 // This must be set to the number of lines generated above.
203 $count = 6;
204 $num_ages = 4;
205 $aging = array();
206 for ($age_index = 0; $age_index < $num_ages; ++$age_index) {
207 $aging[$age_index] = 0.00;
209 $todays_time = strtotime(date('Y-m-d'));
211 // This generates the detail lines. Again, note that the values must be specified in the order used.
212 foreach ($stmt['lines'] as $line) {
213 if ($GLOBALS['use_custom_statement']) {
214 $description = substr($line['desc'],0,30);
215 } else {
216 $description = $line['desc'];
219 $tmp = substr($description, 0, 14);
220 if ($tmp == 'Procedure 9920' || $tmp == 'Procedure 9921' || $tmp == 'Procedure 9200' || $tmp == 'Procedure 9201')
221 $description = str_replace("Procedure",xl('Office Visit').":",$description);
222 //92002-14 are Eye Office Visit Codes
224 $dos = $line['dos'];
225 ksort($line['detail']);
226 # Compute the aging bucket index and accumulate into that bucket.
227 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
228 $age_index = (int) (($age_in_days - 1) / 30);
229 $age_index = max(0, min($num_ages - 1, $age_index));
230 $aging[$age_index] += $line['amount'] - $line['paid'];
232 foreach ($line['detail'] as $dkey => $ddata) {
233 $ddate = substr($dkey, 0, 10);
234 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
235 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
237 $amount = '';
239 if ($ddata['pmt']) {
240 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
241 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
242 } else if ($ddata['rsn']) {
243 if ($ddata['chg']) {
244 $amount = sprintf("%.2f", $ddata['chg']);
245 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
246 } else {
247 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
249 } else if ($ddata['chg'] < 0) {
250 $amount = sprintf("%.2f", $ddata['chg']);
251 $desc = xl('Patient Payment');
252 } else {
253 $amount = sprintf("%.2f", $ddata['chg']);
254 $desc = $description;
258 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
259 $dos = '';
260 ++$count;
264 // This generates blank lines until we are at line 20.
265 // At line 20 we start middle third.
267 while ($count++ < 16) $out .= "\n";
268 # Generate the string of aging text. This will look like:
269 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
270 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
272 $ageline = xl('Current') .': ' . sprintf("%.2f", $aging[0]);
273 for ($age_index = 1; $age_index < ($num_ages - 1); ++$age_index) {
274 $ageline .= ' | ' . ($age_index * 30 + 1) . '-' . ($age_index * 30 + 30) . ':' .
275 sprintf(" %.2f", $GLOBALS['gbl_currency_symbol'].''.$aging[$age_index]);
278 // Fixed text labels
279 $label_ptname = xl('Name');
280 $label_today = xl('Date');
281 $label_due = xl('Due');
282 $label_thanks = xl('Thank you for choosing');
283 $label_call = xl('Please call if any of the above information is incorrect.');
284 $label_prompt = xl('We appreciate prompt payment of balances due.');
285 $label_dept = xl('Billing Department');
286 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ? $GLOBALS['billing_phone_number'] : $billing_phone );
287 $label_appointments = xl('Future Appointments').':';
289 // This is the top portion of the page.
290 $out .= "\n\n\n";
291 if(strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
292 $out .= sprintf("%-46s\n",$stmt['bill_note']);
293 $count++;
295 if ($GLOBALS['use_dunning_message']) {
296 $out .= sprintf("%-46s\n",$dun_message);
297 $count++;
299 $out .= "\n";
300 $out .= sprintf("%-s: %-25s %-s: %-14s %-s: %8s\n",$label_ptname,$stmt['patient'],
301 $label_today,oeFormatShortDate($stmt['today']),$label_due,$stmt['amount']);
302 $out .= sprintf("__________________________________________________________________\n");
303 $out .= "\n";
304 $out .= sprintf("%-s\n",$label_call);
305 $out .= sprintf("%-s\n",$label_prompt);
306 $out .= "\n";
307 $out .= sprintf("%-s\n",$billing_contact);
308 $out .= sprintf(" %-s %-25s\n",$label_dept,$label_bill_phone);
309 if($GLOBALS['statement_message_to_patient']) {
310 $out .= "\n";
311 $statement_message = $GLOBALS['statement_msg_text'];
312 $out .= sprintf("%-40s\n",$statement_message);
313 $count++;
315 if($GLOBALS['show_aging_on_custom_statement']) {
316 # code for ageing
317 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
318 sprintf(" %.2f", $aging[$age_index]);
319 $out .= "\n" . $ageline . "\n\n";
320 $count++;
322 if($GLOBALS['number_appointments_on_statement']!=0) {
323 $out .= "\n";
324 $num_appts = $GLOBALS['number_appointments_on_statement'];
325 $next_day = mktime(0,0,0,date('m'),date('d')+1,date('Y'));
326 # add one day to date so it will not get todays appointment
327 $current_date2 = date('Y-m-d', $next_day);
328 $events = fetchNextXAppts($current_date2,$stmt['pid'],$num_appts);
329 $j=0;
330 $out .= sprintf("%-s\n",$label_appointments);
331 #loop to add the appointments
332 for ($x = 1; $x <= $num_appts; $x++) {
333 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
334 $next_appoint_time = substr($events[$j]['pc_startTime'],0,5);
335 if(strlen(umname) != 0 ) {
336 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] . ' ' . $events[$j]['ulname'];
338 else
340 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
342 if(strlen($next_appoint_time) != 0) {
343 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
344 $out .= sprintf("%-s\n",$label_plsnote[$j]);
346 $j++;
347 $count++;
350 while ($count++ < 29) $out .= "\n";
351 $out .= sprintf("%-10s %s\n",null,$label_retpay);
352 $out .= '</pre></div>';
353 $out .= '<div style="width:7.0in;border-top:1pt dotted black;font-size:12px;margin:0px;"><br /><br />
354 <table style="width:7in;margin-left:20px;"><tr><td style="width:4.5in;"><br />
356 $out .= $label_payby.' '.$label_cards;
357 $out .= "<br /><br />";
358 $out .= $label_cardnum .': __________________________________ '.$label_expiry.': ___ / _____ <br /><br />';
359 $out .= $label_sign .' ____________________________<br />';
360 $out .=" </td><td style=width:2.0in;vertical-align:middle;'>";
361 $practice_cards = $GLOBALS['webroot']."/sites/" . $_SESSION['site_id'] . "/images/visa_mc_disc_credit_card_logos_176x35.gif";
362 if (file_exists($GLOBALS['OE_SITE_DIR']."/images/visa_mc_disc_credit_card_logos_176x35.gif")) {
363 $out .= "<img src='$practice_cards' style='width:100%;margin:4px auto;'><br /><p>\n".$label_totaldue.": ".$stmt['amount']."</p>";
366 $out .="</td></tr></table>";
368 $out .= '</div><br />
369 <pre>';
370 if($stmt['to'][3]!='')//to avoid double blank lines the if condition is put.
371 $out .= sprintf(" %-32s\n",$stmt['to'][3]);
372 $out .= ' </pre>
373 <div style="width:7in;border-top:1pt solid black;"><br />';
374 $out .= " <table style='width:6.0in;margin-left:40px;'><tr>";
375 $out .= '<td style="width:3.0in;"><b>'
376 .$label_addressee.'</b><br />'
377 .$stmt['to'][0].'<br />'
378 .$stmt['to'][1].'<br />'
379 .$stmt['to'][2].'
380 </td>
381 <td style="width:3.0in;"><b>'.$label_remitto.'</b><br />'
382 .$remit_name.'<br />'
383 .$remit_addr.'<br />'
384 .$remit_csz.'
385 </td>
386 </tr></table>';
388 $out .= " </div></div>";
389 $out .= "\014
390 <br /><br />"; // this is a form feed
391 echo $out;
392 $output = ob_get_clean();
393 return $output;
396 // This function builds a printable statement or collection letter from
397 // an associative array having the following keys:
399 // today = statement date yyyy-mm-dd
400 // pid = patient ID
401 // patient = patient name
402 // amount = total amount due
403 // to = array of addressee name/address lines
404 // lines = array of lines, each with the following keys:
405 // dos = date of service yyyy-mm-dd
406 // desc = description
407 // amount = charge less adjustments
408 // paid = amount paid
409 // notice = 1 for first notice, 2 for second, etc.
410 // detail = associative array of details
412 // Each detail array is keyed on a string beginning with a date in
413 // yyyy-mm-dd format, or blanks in the case of the original charge
414 // items. Its values are associative arrays like this:
416 // pmt - payment amount as a positive number, only for payments
417 // src - check number or other source, only for payments
418 // chg - invoice line item amount amount, only for charges or
419 // adjustments (adjustments may be zero)
420 // rsn - adjustment reason, only for adjustments
422 // The returned value is a string that can be sent to a printer.
423 // This example is plain text, but if you are a hotshot programmer
424 // then you could make a PDF or PostScript or whatever peels your
425 // banana. These strings are sent in succession, so append a form
426 // feed if that is appropriate.
429 // A sample of the text based format follows:
431 //[Your Clinic Name] Patient Name 2009-12-29
432 //[Your Clinic Address] Chart Number: 1848
433 //[City, State Zip] Insurance information on file
436 //ADDRESSEE REMIT TO
437 //Patient Name [Your Clinic Name]
438 //patient address [Your Clinic Address]
439 //city, state zipcode [City, State Zip]
440 // If paying by VISA/MC/AMEX/Dis
442 //Card_____________________ Exp______ Signature___________________
443 // Return above part with your payment
444 //-----------------------------------------------------------------
446 //_______________________ STATEMENT SUMMARY _______________________
448 //Visit Date Description Amount
450 //2009-08-20 Procedure 99345 198.90
451 // Paid 2009-12-15: -51.50
452 //... more details ...
453 //...
454 //...
455 // skipping blanks in example
458 //Name: Patient Name Date: 2009-12-29 Due: 147.40
459 //_________________________________________________________________
461 //Please call if any of the above information is incorrect
462 //We appreciate prompt payment of balances due
464 //[Your billing contact name]
465 // Billing Department
466 // [Your billing dept phone]
468 function create_statement($stmt) {
469 if (! $stmt['pid']) return ""; // get out if no data
471 #minimum_amount_to _print
472 if ($stmt[amount] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) return "";
474 // These are your clinics return address, contact etc. Edit them.
475 // TBD: read this from the facility table
477 // Facility (service location)
478 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code from facility f " .
479 " left join users u on f.id=u.facility_id " .
480 " left join billing b on b.provider_id=u.id and b.pid = '".$stmt['pid']."' " .
481 " where service_location=1");
482 $row = sqlFetchArray($atres);
484 // Facility (service location)
486 $clinic_name = "{$row['name']}";
487 $clinic_addr = "{$row['street']}";
488 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
491 // Billing location
492 $remit_name = $clinic_name;
493 $remit_addr = $clinic_addr;
494 $remit_csz = $clinic_csz;
496 // Contacts
497 $atres = sqlStatement("select f.attn,f.phone from facility f " .
498 " left join users u on f.id=u.facility_id " .
499 " left join billing b on b.provider_id=u.id and b.pid = '".$stmt['pid']."' " .
500 " where billing_location=1");
501 $row = sqlFetchArray($atres);
502 $billing_contact = "{$row['attn']}";
503 $billing_phone = "{$row['phone']}";
505 // dunning message setup
507 // insurance has paid something
508 // $stmt['age'] how old is the invoice
509 // $stmt['dun_count'] number of statements run
510 // $stmt['level_closed'] <= 3 insurance 4 = patient
512 if ($GLOBALS['use_dunning_message']) {
513 if ($stmt['ins_paid'] != 0 || $stmt['level_closed'] == 4) {
515 // do collection messages
516 switch ($stmt{'age'}) {
517 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
518 $dun_message = $GLOBALS['first_dun_msg_text'];
519 break;
520 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
521 $dun_message = $GLOBALS['second_dun_msg_text'];
522 break;
523 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
524 $dun_message = $GLOBALS['third_dun_msg_text'];
525 break;
526 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
527 $dun_message = $GLOBALS['fourth_dun_msg_text'];
528 break;
529 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
530 $dun_message = $GLOBALS['fifth_dun_msg_text'];
531 break;
535 // Text only labels
537 $label_addressee = xl('ADDRESSEE');
538 $label_remitto = xl('REMIT TO');
539 $label_chartnum = xl('Chart Number');
540 $label_insinfo = xl('Insurance information on file');
541 $label_totaldue = xl('Total amount due');
542 $label_payby = xl('If paying by');
543 $label_cards = xl('VISA/MC/Discovery/HSA');
544 $label_cardnum = xl('Card');
545 $label_expiry = xl('Exp');
546 $label_sign = xl('Signature');
547 $label_retpay = xl('Return above part with your payment');
548 $label_pgbrk = xl('STATEMENT SUMMARY');
549 $label_visit = xl('Visit Date');
550 $label_desc = xl('Description');
551 $label_amt = xl('Amount');
553 // This is the text for the top part of the page, up to but not
554 // including the detail lines. Some examples of variable fields are:
555 // %s = string with no minimum width
556 // %9s = right-justified string of 9 characters padded with spaces
557 // %-25s = left-justified string of 25 characters padded with spaces
558 // Note that "\n" is a line feed (new line) character.
559 // reformatted to handle i8n by tony
560 $out = "\n\n";
561 $providerNAME = getProviderName($stmt['providerID']);
562 $out .= sprintf("%-30s %s %-s\n",$clinic_name,$stmt['patient'],$stmt['today']);
563 $out .= sprintf("%-30s %s: %-s\n",$providerNAME,$label_chartnum,$stmt['pid']);
564 $out .= sprintf("%-30s %s\n",$clinic_addr,$label_insinfo);
565 $out .= sprintf("%-30s %-s: %-s\n",$clinic_csz,$label_totaldue,$stmt['amount']);
566 $out .= "\n";
567 $out .= sprintf(" %-30s %-s\n",$label_addressee,$label_remitto);
568 $out .= sprintf(" %-30s %s\n",$stmt['to'][0],$remit_name);
569 $out .= sprintf(" %-30s %s\n",$stmt['to'][1],$remit_addr);
570 $out .= sprintf(" %-30s %s\n",$stmt['to'][2],$remit_csz);
572 if($stmt['to'][3]!='')//to avoid double blank lines the if condition is put.
573 $out .= sprintf(" %-32s\n",$stmt['to'][3]);
574 $out .= sprintf("_________________________________________________________________\n");
575 $out .= "\n";
576 $out .= sprintf("%-32s\n",$label_payby.' '.$label_cards);
577 $out .= "\n";
578 $out .= sprintf("%s_____________________ %s______ %s___________________%s\n\n",
579 $label_cardnum,$label_expiry,$label_sign);
580 $out .= sprintf("-----------------------------------------------------------------\n");
581 $out .= sprintf("%-20s %s\n",null,$label_retpay);
582 $out .= "\n";
583 $out .= sprintf("_______________________ %s _______________________\n",$label_pgbrk);
584 $out .= "\n";
585 $out .= sprintf("%-11s %-46s %s\n",$label_visit,$label_desc,$label_amt);
586 $out .= "\n";
588 // This must be set to the number of lines generated above.
590 $count = 25;
591 $num_ages = 4;
592 $aging = array();
593 for ($age_index = 0; $age_index < $num_ages; ++$age_index) {
594 $aging[$age_index] = 0.00;
596 $todays_time = strtotime(date('Y-m-d'));
598 // This generates the detail lines. Again, note that the values must
599 // be specified in the order used.
603 foreach ($stmt['lines'] as $line) {
604 if ($GLOBALS['use_custom_statement']) {
605 $description = substr($line['desc'],0,30);
608 else {
609 $description = $line['desc'];
613 $tmp = substr($description, 0, 14);
614 if ($tmp == 'Procedure 9920' || $tmp == 'Procedure 9921' || $tmp == 'Procedure 9200' || $tmp == 'Procedure 9201')
615 $description = str_replace("Procedure",xl('Office Visit').":",$description);
616 //92002-14 are Eye Office Visit Codes
618 $dos = $line['dos'];
619 ksort($line['detail']);
620 # Compute the aging bucket index and accumulate into that bucket.
622 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
623 $age_index = (int) (($age_in_days - 1) / 30);
624 $age_index = max(0, min($num_ages - 1, $age_index));
625 $aging[$age_index] += $line['amount'] - $line['paid'];
627 foreach ($line['detail'] as $dkey => $ddata) {
628 $ddate = substr($dkey, 0, 10);
629 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
630 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
632 $amount = '';
634 if ($ddata['pmt']) {
635 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
636 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
637 } else if ($ddata['rsn']) {
638 if ($ddata['chg']) {
639 $amount = sprintf("%.2f", $ddata['chg']);
640 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
641 } else {
642 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
644 } else if ($ddata['chg'] < 0) {
645 $amount = sprintf("%.2f", $ddata['chg']);
646 $desc = xl('Patient Payment');
647 } else {
648 $amount = sprintf("%.2f", $ddata['chg']);
649 $desc = $description;
653 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
654 $dos = '';
655 ++$count;
659 // This generates blank lines until we are at line 42.
661 while ($count++ < 42) $out .= "\n";
662 # Generate the string of aging text. This will look like:
663 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
664 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
666 $ageline = xl('Current') .' ' . sprintf("%.2f", $aging[0]);
667 for ($age_index = 1; $age_index < ($num_ages - 1); ++$age_index) {
668 $ageline .= ' / ' . ($age_index * 30 + 1) . '-' . ($age_index * 30 + 30) .
669 sprintf(" %.2f", $aging[$age_index]);
672 // Fixed text labels
673 $label_ptname = xl('Name');
674 $label_today = xl('Date');
675 $label_due = xl('Amount Due');
676 $label_thanks = xl('Thank you for choosing');
677 $label_call = xl('Please call if any of the above information is incorrect.');
678 $label_prompt = xl('We appreciate prompt payment of balances due.');
679 $label_dept = xl('Billing Department');
680 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ? $GLOBALS['billing_phone_number'] : $billing_phone );
681 $label_appointments = xl('Future Appointments').':';
683 // This is the bottom portion of the page.
684 $out .= "\n";
685 if(strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
686 $out .= sprintf("%-46s\n",$stmt['bill_note']);
688 if ($GLOBALS['use_dunning_message']) {
689 $out .= sprintf("%-46s\n",$dun_message);
691 $out .= "\n";
692 $out .= sprintf("%-s: %-25s %-s: %-14s %-s: %8s\n",$label_ptname,$stmt['patient'],
693 $label_today,oeFormatShortDate($stmt['today']),$label_due,$stmt['amount']);
694 $out .= sprintf("__________________________________________________________________\n");
695 $out .= "\n";
696 $out .= sprintf("%-s\n",$label_call);
697 $out .= sprintf("%-s\n",$label_prompt);
698 $out .= "\n";
699 $out .= sprintf("%-s\n",$billing_contact);
700 $out .= sprintf(" %-s %-25s\n",$label_dept,$label_bill_phone);
701 if($GLOBALS['statement_message_to_patient']) {
702 $out .= "\n";
703 $statement_message = $GLOBALS['statement_msg_text'];
704 $out .= sprintf("%-40s\n",$statement_message);
706 if($GLOBALS['show_aging_on_custom_statement']) {
707 # code for ageing
708 $ageline .= ' / ' . xl('Over') . '-' . ($age_index * 30) .
709 sprintf(" %.2f", $aging[$age_index]);
710 $out .= "\n" . $ageline . "\n\n";
713 if($GLOBALS['number_appointments_on_statement']!=0) {
714 $out .= "\n";
715 $num_appts = $GLOBALS['number_appointments_on_statement'];
716 $next_day = mktime(0,0,0,date('m'),date('d')+1,date('Y'));
717 # add one day to date so it will not get todays appointment
718 $current_date2 = date('Y-m-d', $next_day);
719 $events = fetchNextXAppts($current_date2,$stmt['pid'],$num_appts);
720 $j=0;
721 $out .= sprintf("%-s\n",$label_appointments);
722 #loop to add the appointments
723 for ($x = 1; $x <= $num_appts; $x++) {
724 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
725 $next_appoint_time = substr($events[$j]['pc_startTime'],0,5);
726 if(strlen(umname) != 0 ) {
727 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] . ' ' . $events[$j]['ulname'];
729 else
731 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
733 if(strlen($next_appoint_time) != 0) {
734 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
735 $out .= sprintf("%-s\n",$label_plsnote[$j]);
737 $j++;
740 $out .= "\014"; // this is a form feed
742 return $out;
745 function osp_create_HTML_statement($stmt) {
746 if (! $stmt['pid']) return ""; // get out if no data
748 #minimum_amount_due_to _print
749 if ($stmt['amount'] <= ($GLOBALS['minimum_amount_to_print']) && $GLOBALS['use_statement_print_exclusion']) return "";
751 // Facility (service location)
752 $atres = sqlStatement("select f.name,f.street,f.city,f.state,f.postal_code,f.attn,f.phone from facility f " .
753 " left join users u on f.id=u.facility_id " .
754 " left join billing b on b.provider_id=u.id and b.pid = ? ".
755 " where service_location=1",array($stmt['pid']));
756 $row = sqlFetchArray($atres);
757 $clinic_name = "{$row['name']}";
758 $clinic_addr = "{$row['street']}";
759 $clinic_csz = "{$row['city']}, {$row['state']}, {$row['postal_code']}";
760 // Contacts
761 $billing_contact = "{$row['attn']}";
762 $billing_phone = "{$row['phone']}";
763 // Billing location
764 $remit_name = $clinic_name;
765 $remit_addr = $clinic_addr;
766 $remit_csz = $clinic_csz;
768 ob_start();
769 ?><div style="padding-left:25px;">
770 <?php
771 $find_provider = sqlQuery("SELECT * FROM form_encounter " .
772 "WHERE pid = ? AND encounter = ? " .
773 "ORDER BY id DESC LIMIT 1", array($stmt['pid'],$stmt['encounter']) );
774 $providerID = $find_provider['provider_id'];
775 echo report_header_2($stmt,$direction,$providerID);
777 // dunning message setup
779 // insurance has paid something
780 // $stmt['age'] how old is the invoice
781 // $stmt['dun_count'] number of statements run
782 // $stmt['level_closed'] <= 3 insurance 4 = patient
784 if ($GLOBALS['use_dunning_message']) {
785 if ($stmt['ins_paid'] != 0 || $stmt['level_closed'] == 4) {
787 // do collection messages
788 switch ($stmt{'age'}) {
789 case $stmt{'age'} <= $GLOBALS['first_dun_msg_set']:
790 $dun_message = $GLOBALS['first_dun_msg_text'];
791 break;
792 case $stmt{'age'} <= $GLOBALS['second_dun_msg_set']:
793 $dun_message = $GLOBALS['second_dun_msg_text'];
794 break;
795 case $stmt{'age'} <= $GLOBALS['third_dun_msg_set']:
796 $dun_message = $GLOBALS['third_dun_msg_text'];
797 break;
798 case $stmt{'age'} <= $GLOBALS['fourth_dun_msg_set']:
799 $dun_message = $GLOBALS['fourth_dun_msg_text'];
800 break;
801 case $stmt{'age'} >= $GLOBALS['fifth_dun_msg_set']:
802 $dun_message = $GLOBALS['fifth_dun_msg_text'];
803 break;
807 // Text only labels
809 $label_addressee = xl('ADDRESSEE');
810 $label_remitto = xl('REMIT TO');
811 $label_chartnum = xl('Chart Number');
812 $label_insinfo = xl('Insurance information on file');
813 $label_totaldue = xl('Total amount due');
814 $label_payby = xl('If paying by');
815 $label_cards = xl('VISA/MC/Discovery/HSA');
816 $label_cardnum = xl('Card');
817 $label_expiry = xl('Exp');
818 $label_sign = xl('Signature');
819 $label_retpay = xl('Please fill in credit information and send for review.');
820 $label_pgbrk = xl('STATEMENT SUMMARY');
821 $label_visit = xl('Visit Date');
822 $label_desc = xl('Description');
823 $label_amt = xl('Amount');
825 // This is the text for the top part of the page, up to but not
826 // including the detail lines. Some examples of variable fields are:
827 // %s = string with no minimum width
828 // %9s = right-justified string of 9 characters padded with spaces
829 // %-25s = left-justified string of 25 characters padded with spaces
830 // Note that "\n" is a line feed (new line) character.
831 // reformatted to handle i8n by tony
833 $out = "<div style='margin-left:60px;margin-top:0px;'>";
834 $out .= "\n";
835 $out .= sprintf("_______________________ %s _______________________\n",$label_pgbrk);
836 $out .= "\n";
837 $out .= sprintf("%-11s %-46s %s\n",$label_visit,$label_desc,$label_amt);
838 $out .= "\n";
840 // This must be set to the number of lines generated above.
842 $count = 6;
843 $num_ages = 4;
844 $aging = array();
845 for ($age_index = 0; $age_index < $num_ages; ++$age_index) {
846 $aging[$age_index] = 0.00;
848 $todays_time = strtotime(date('Y-m-d'));
850 // This generates the detail lines. Again, note that the values must be specified in the order used.
851 foreach ($stmt['lines'] as $line) {
852 if ($GLOBALS['use_custom_statement']) {
853 $description = substr($line['desc'],0,30);
854 } else {
855 $description = $line['desc'];
858 $tmp = substr($description, 0, 14);
859 if ($tmp == 'Procedure 9920' || $tmp == 'Procedure 9921' || $tmp == 'Procedure 9200' || $tmp == 'Procedure 9201')
860 $description = str_replace("Procedure",xl('Office Visit').":",$description);
861 //92002-14 are Eye Office Visit Codes
863 $dos = $line['dos'];
864 ksort($line['detail']);
865 # Compute the aging bucket index and accumulate into that bucket.
866 $age_in_days = (int) (($todays_time - strtotime($dos)) / (60 * 60 * 24));
867 $age_index = (int) (($age_in_days - 1) / 30);
868 $age_index = max(0, min($num_ages - 1, $age_index));
869 $aging[$age_index] += $line['amount'] - $line['paid'];
871 foreach ($line['detail'] as $dkey => $ddata) {
872 $ddate = substr($dkey, 0, 10);
873 if (preg_match('/^(\d\d\d\d)(\d\d)(\d\d)\s*$/', $ddate, $matches)) {
874 $ddate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
876 $amount = '';
878 if ($ddata['pmt']) {
879 $amount = sprintf("%.2f", 0 - $ddata['pmt']);
880 $desc = xl('Paid') .' '. oeFormatShortDate($ddate) .': '. $ddata['src'].' '. $ddata['pmt_method'].' '. $ddata['insurance_company'];
881 } else if ($ddata['rsn']) {
882 if ($ddata['chg']) {
883 $amount = sprintf("%.2f", $ddata['chg']);
884 $desc = xl('Adj') .' '. oeFormatShortDate($ddate) .': ' . $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
885 } else {
886 $desc = xl('Note') .' '. oeFormatShortDate($ddate) .': '. $ddata['rsn'].' '.$ddata['pmt_method'].' '. $ddata['insurance_company'];
888 } else if ($ddata['chg'] < 0) {
889 $amount = sprintf("%.2f", $ddata['chg']);
890 $desc = xl('Patient Payment');
891 } else {
892 $amount = sprintf("%.2f", $ddata['chg']);
893 $desc = $description;
897 $out .= sprintf("%-10s %-45s%8s\n", oeFormatShortDate($dos), $desc, $amount);
898 $dos = '';
899 ++$count;
903 // This generates blank lines until we are at line 20.
904 // At line 20 we start middle third.
906 //while ($count++ < 16) $out .= "\n";
907 # Generate the string of aging text. This will look like:
908 # Current xxx.xx / 31-60 x.xx / 61-90 x.xx / Over-90 xxx.xx
909 # ....+....1....+....2....+....3....+....4....+....5....+....6....+
911 $ageline = xl('Current') .': ' . sprintf("%.2f", $aging[0]);
912 for ($age_index = 1; $age_index < ($num_ages - 1); ++$age_index) {
913 $ageline .= ' | ' . ($age_index * 30 + 1) . '-' . ($age_index * 30 + 30) . ':' .
914 sprintf(" %.2f", $GLOBALS['gbl_currency_symbol'].''.$aging[$age_index]);
917 // Fixed text labels
918 $label_ptname = xl('Name');
919 $label_today = xl('Date');
920 $label_due = xl('Due');
921 $label_thanks = xl('Thank you for choosing');
922 $label_call = xl('Please call or message if any of the above information is incorrect.');
923 $label_prompt = xl('We appreciate prompt payment of balances due.');
924 $label_dept = xl('Billing Department');
925 $label_bill_phone = (!empty($GLOBALS['billing_phone_number']) ? $GLOBALS['billing_phone_number'] : $billing_phone );
926 $label_appointments = xl('Future Appointments').':';
928 // This is the top portion of the page.
929 $out .= "\n";
930 if(strlen($stmt['bill_note']) !=0 && $GLOBALS['statement_bill_note_print']) {
931 $out .= sprintf("%-46s\n",$stmt['bill_note']);
932 $count++;
934 if ($GLOBALS['use_dunning_message']) {
935 $out .= sprintf("%-46s\n",$dun_message);
936 $count++;
938 $out .= "\n";
939 $out .= sprintf("%-s: %-25s %-s: %-14s %-s: %8s\n",$label_ptname,$stmt['patient'],
940 $label_today,oeFormatShortDate($stmt['today']),$label_due,$stmt['amount']);
941 $out .= sprintf("__________________________________________________________________\n");
942 $out .= "\n";
943 $out .= sprintf("%-s\n",$label_call);
944 $out .= sprintf("%-s\n",$label_prompt);
945 $out .= "\n";
946 $out .= sprintf("%-s\n",$billing_contact);
947 $out .= sprintf(" %-s %-25s\n",$label_dept,$label_bill_phone);
948 if($GLOBALS['statement_message_to_patient']) {
949 $out .= "\n";
950 $statement_message = $GLOBALS['statement_msg_text'];
951 $out .= sprintf("%-40s\n",$statement_message);
952 $count++;
954 if($GLOBALS['show_aging_on_custom_statement']) {
955 # code for ageing
956 $ageline .= ' | ' . xl('Over') . ' ' . ($age_index * 30) .':'.
957 sprintf(" %.2f", $aging[$age_index]);
958 $out .= "\n" . $ageline . "\n\n";
959 $count++;
961 if($GLOBALS['number_appointments_on_statement']!=0) {
962 $out .= "\n";
963 $num_appts = $GLOBALS['number_appointments_on_statement'];
964 $next_day = mktime(0,0,0,date('m'),date('d')+1,date('Y'));
965 # add one day to date so it will not get todays appointment
966 $current_date2 = date('Y-m-d', $next_day);
967 $events = fetchNextXAppts($current_date2,$stmt['pid'],$num_appts);
968 $j=0;
969 $out .= sprintf("%-s\n",$label_appointments);
970 #loop to add the appointments
971 for ($x = 1; $x <= $num_appts; $x++) {
972 $next_appoint_date = oeFormatShortDate($events[$j]['pc_eventDate']);
973 $next_appoint_time = substr($events[$j]['pc_startTime'],0,5);
974 if(strlen(umname) != 0 ) {
975 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['umname'] . ' ' . $events[$j]['ulname'];
977 else
979 $next_appoint_provider = $events[$j]['ufname'] . ' ' . $events[$j]['ulname'];
981 if(strlen($next_appoint_time) != 0) {
982 $label_plsnote[$j] = xlt('Date') . ': ' . text($next_appoint_date) . ' ' . xlt('Time') . ' ' . text($next_appoint_time) . ' ' . xlt('Provider') . ' ' . text($next_appoint_provider);
983 $out .= sprintf("%-s\n",$label_plsnote[$j]);
985 $j++;
986 $count++;
989 // while ($count++ < 29) $out .= "\n";
990 $out .= sprintf("%-10s %s\n",null,$label_retpay);
991 $out .= '</pre></div>';
992 $out .= '<div style="width:7.0in;border-top:1pt dotted black;font-size:12px;margin:0px;"><br /><br />
993 <table style="width:8in;margin-left:20px;"><tr><td style="width:4.5in;"><br />
995 $out .= $label_payby.' '.$label_cards;
996 $out .= "<br /><br />";
997 $out .= $label_cardnum .': {TextInput} '.$label_expiry.': {smTextInput} / {smTextInput} <br /><br />';
998 $out .= $label_sign .' {PatientSignature}<br />';
999 $out .=" </td><td style=width:2.0in;vertical-align:middle;'>";
1000 $practice_cards = $GLOBALS['webroot']."/sites/" . $_SESSION['site_id'] . "/images/visa_mc_disc_credit_card_logos_176x35.gif";
1001 if (file_exists($GLOBALS['OE_SITE_DIR']."/images/visa_mc_disc_credit_card_logos_176x35.gif")) {
1002 //$out .= "<img onclick='getPayment()' src='$practice_cards' style='width:100%;margin:4px auto;'><br /><p>\n".$label_totaldue.": ".$stmt['amount']."</p>";
1003 $out .= "<br /><p>".$label_totaldue.": ".$stmt['amount']."</p>";
1006 $out .="</td></tr></table>";
1008 $out .= '</div><br />';
1009 if($stmt['to'][3]!='')//to avoid double blank lines the if condition is put.
1010 $out .= sprintf(" %-32s\n",$stmt['to'][3]);
1011 $out .= ' </pre>
1012 <div style="width:8in;border-top:1pt solid black;"><br />';
1013 $out .= " <table style='width:6.0in;margin-left:40px;'><tr>";
1014 $out .= '<td style="width:3.0in;"><b>'
1015 .$label_addressee.'</b><br />'
1016 .$stmt['to'][0].'<br />'
1017 .$stmt['to'][1].'<br />'
1018 .$stmt['to'][2].'
1019 </td>
1020 <td style="width:3.0in;"><b>'.$label_remitto.'</b><br />'
1021 .$remit_name.'<br />'
1022 .$remit_addr.'<br />'
1023 .$remit_csz.'
1024 </td>
1025 </tr></table>';
1027 $out .= " </div></div>";
1028 $out .= "\014
1029 <br /><br />"; // this is a form feed
1030 echo $out;
1031 $output = ob_get_clean();
1032 return $output;