Fixing accounting/billing bugs
[openemr.git] / interface / patient_file / printed_fee_sheet.php
blobe913b688fc6dd61637cbc594e7b1977093984c3e
1 <?php
3 // Copyright (C) 2007-2010 Rod Roark <rod@sunsetsystems.com>
4 //
5 // 2012 - Refactored extensively to allow for creating multiple feesheets on demand
6 // uses a session array of PIDS by Medical Information Integration, LLC - 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 require_once("../globals.php");
14 require_once("$srcdir/acl.inc");
15 require_once("$srcdir/patient.inc");
16 require_once("$srcdir/billing.inc");
17 require_once("$srcdir/classes/Address.class.php");
18 require_once("$srcdir/classes/InsuranceCompany.class.php");
19 require_once("$srcdir/formatting.inc.php");
21 function genColumn($ix) {
22 global $html;
23 global $SBCODES;
24 for ($imax = count($SBCODES); $ix < $imax; ++$ix) {
25 $a = explode('|', $SBCODES[$ix], 2);
26 $cmd = trim($a[0]);
27 if ($cmd == '*C') { // column break
28 return++$ix;
30 if ($cmd == '*B') { // Borderless and empty
31 $html .= " <tr><td colspan='5' class='fscode' style='border-width:0 1px 0 0;padding-top:1px;' nowrap>&nbsp;</td></tr>\n";
32 } else if ($cmd == '*G') {
33 $title = htmlspecialchars($a[1]);
34 if (!$title)
35 $title = '&nbsp;';
36 $html .= " <tr><td colspan='5' align='center' class='fsgroup' style='vertical-align:middle' nowrap>$title</td></tr>\n";
38 else if ($cmd == '*H') {
39 $title = htmlspecialchars($a[1]);
40 if (!$title)
41 $title = '&nbsp;';
42 $html .= " <tr><td colspan='5' class='fshead' style='vertical-align:middle' nowrap>$title</td></tr>\n";
44 else {
45 $title = htmlspecialchars($a[1]);
46 if (!$title)
47 $title = '&nbsp;';
48 $b = explode(':', $cmd);
49 $html .= " <tr>\n";
50 $html .= " <td class='fscode' style='vertical-align:middle;width:14pt' nowrap>&nbsp;</td>\n";
51 if (count($b) <= 1) {
52 $code = $b[0];
53 if (!$code)
54 $code = '&nbsp;';
55 $html .= " <td class='fscode' style='vertical-align:middle' nowrap>$code</td>\n";
56 $html .= " <td colspan='3' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
58 else {
59 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>" . $b[0] . '/' . $b[1] . "</td>\n";
60 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
62 $html .= " </tr>\n";
65 return $ix;
68 // MAIN Body
70 // Build output to handle multiple pids and and superbill for each patient.
71 // This value is initially a maximum, and will be recomputed to
72 // distribute lines evenly among the pages. (was 55)
73 $lines_per_page = 55;
75 $lines_in_stats = 8;
77 $header_height = 44; // height of page headers in points
78 // This tells us if patient/encounter data is to be filled in.
79 // 1 = single PID from popup, 2=array of PIDs for session
81 if (empty($_GET['fill'])) {
82 $form_fill = 0;
83 } else {
84 $form_fill = $_GET['fill'];
87 // Show based on session array or single pid?
88 $pid_list = array();
90 if(!empty($_SESSION['pidList']) and $form_fill == 2)
92 $pid_list = $_SESSION['pidList'];
94 else if ($form_fill == 1)
96 array_push($pid_list,$pid); //get from active PID
97 } else {
98 array_push($pid_list,''); // empty element for blank form
101 // make sure to clean up the session
102 // else we'll build off of trash in the combo-drop down for a single patient later
103 unset($_SESSION['pidList']);
105 // This file is optional. You can create it to customize how the printed
106 // fee sheet looks, otherwise you'll get a mirror of your actual fee sheet.
108 if (file_exists("../../custom/fee_sheet_codes.php"))
109 include_once ("../../custom/fee_sheet_codes.php");
111 // TBD: Move these to globals.php, or make them user-specific.
112 $fontsize = 7;
113 $page_height = 700;
115 $padding = 0;
117 // The $SBCODES table is a simple indexed array whose values are
118 // strings of the form "code|text" where code may be either a billing
119 // code or one of the following:
121 // *H - A main heading, where "text" is its title (to be centered).
122 // *G - Specifies a new category, where "text" is its name.
123 // *B - A borderless blank row.
124 // *C - Ends the current column and starts a new one.
125 // If $SBCODES is not provided, then manufacture it from the Fee Sheet.
127 if (empty($SBCODES)) {
128 $SBCODES = array();
129 $last_category = '';
131 // Create entries based on the fee_sheet_options table.
132 $res = sqlStatement("SELECT * FROM fee_sheet_options " .
133 "ORDER BY fs_category, fs_option");
134 while ($row = sqlFetchArray($res)) {
135 $fs_category = $row['fs_category'];
136 $fs_option = $row['fs_option'];
137 $fs_codes = $row['fs_codes'];
138 if ($fs_category !== $last_category) {
139 $last_category = $fs_category;
140 $SBCODES[] = '*G|' . substr($fs_category, 1);
142 $SBCODES[] = " |" . substr($fs_option, 1);
145 // Create entries based on categories defined within the codes.
146 $pres = sqlStatement("SELECT option_id, title FROM list_options " .
147 "WHERE list_id = 'superbill' ORDER BY seq");
148 while ($prow = sqlFetchArray($pres)) {
149 $SBCODES[] = '*G|' . $prow['title'];
150 $res = sqlStatement("SELECT code_type, code, code_text FROM codes " .
151 "WHERE superbill = '" . $prow['option_id'] . "' AND active = 1 " .
152 "ORDER BY code_text");
153 while ($row = sqlFetchArray($res)) {
154 $SBCODES[] = $row['code'] . '|' . $row['code_text'];
158 // Create one more group, for Products.
159 if ($GLOBALS['sell_non_drug_products']) {
160 $SBCODES[] = '*G|' . xl('Products');
161 $tres = sqlStatement("SELECT " .
162 "dt.drug_id, dt.selector, d.name, d.ndc_number " .
163 "FROM drug_templates AS dt, drugs AS d WHERE " .
164 "d.drug_id = dt.drug_id AND d.active = 1 " .
165 "ORDER BY d.name, dt.selector, dt.drug_id");
166 while ($trow = sqlFetchArray($tres)) {
167 $tmp = $trow['selector'];
168 if ($trow['name'] !== $trow['selector'])
169 $tmp .= ' ' . $trow['name'];
170 $prodcode = empty($trow['ndc_number']) ? ('(' . $trow['drug_id'] . ')') :
171 $trow['ndc_number'];
172 $SBCODES[] = "$prodcode|$tmp";
176 // Extra stuff for the labs section.
177 $SBCODES[] = '*G|' . xl('Notes');
178 $percol = intval((count($SBCODES) + 2) / 3);
179 while (count($SBCODES) < $percol * 3)
180 $SBCODES[] = '*B|';
182 // Adjust lines per page to distribute lines evenly among the pages.
183 $pages = intval(($percol + $lines_in_stats + $lines_per_page - 1) / $lines_per_page);
184 $lines_per_page = intval(($percol + $lines_in_stats + $pages - 1) / $pages);
186 // Figure out page and column breaks.
187 $pages = 1;
188 $lines = $percol;
189 $page_start_index = 0;
190 while ($lines + $lines_in_stats > $lines_per_page) {
191 ++$pages;
192 $lines_this_page = $lines > $lines_per_page ? $lines_per_page : $lines;
193 $lines -= $lines_this_page;
194 array_splice($SBCODES, $lines_this_page * 3 + $page_start_index, 0, '*C|');
195 array_splice($SBCODES, $lines_this_page * 2 + $page_start_index, 0, '*C|');
196 array_splice($SBCODES, $lines_this_page * 1 + $page_start_index, 0, '*C|');
197 $page_start_index += $lines_this_page * 3 + 3;
199 array_splice($SBCODES, $lines * 2 + $page_start_index, 0, '*C|');
200 array_splice($SBCODES, $lines * 1 + $page_start_index, 0, '*C|');
203 $lheight = sprintf('%d', ($page_height - $header_height) / $lines_per_page);
205 // Common HTML Header information
207 $html = "<html>
208 <head>";
210 $html .= "
211 <style>
212 body {
213 font-family: sans-serif;
214 font-weight: normal;
216 .bordertbl {
217 width: 100%;
218 border-style: solid;
219 border-width: 0 0 1px 1px;
220 border-spacing: 0;
221 border-collapse: collapse;
222 border-color: #999999;
224 td.toprow {
225 height: 1px;
226 padding: 0;
227 border-style: solid;
228 border-width: 0 0 0 0;
229 border-color: #999999;
231 td.fsgroup {
232 height: ${lheight}pt;
233 font-family: sans-serif;
234 font-weight: bold;
235 font-size: $fontsize pt;
236 background-color: #cccccc;
237 padding: ${padding}pt 2pt 0pt 2pt;
238 border-style: solid;
239 border-width: 1px 1px 0 0;
240 border-color: #999999;
242 td.fshead {
243 height: ${lheight}pt;
244 font-family: sans-serif;
245 font-weight: bold;
246 font-size: ${fontsize}pt;
247 padding: ${padding}pt 2pt 0pt 2pt;
248 border-style: solid;
249 border-width: 1px 1px 0 0;
250 border-color: #999999;
252 td.fscode {
253 height: ${lheight}pt;
254 font-family: sans-serif;
255 font-weight: normal;
256 font-size: ${fontsize}pt;
257 padding: ${padding}pt 2pt 0pt 2pt;
258 border-style: solid;
259 border-width: 1px 1px 0 0;
260 border-color: #999999;
263 .ftitletable {
264 width: 100%;
265 height: ${header_height}pt;
266 margin: 0 0 8pt 0;
268 .ftitlecell1 {
269 vertical-align: top;
270 text-align: left;
271 font-size: 14pt;
272 font-weight: bold;
274 .ftitlecell2 {
275 vertical-align: top;
276 text-align: right;
277 font-size: 9pt;
279 div.pagebreak {
280 page-break-after: always;
281 height: ${page_height}pt;
283 </style>";
285 $html .= "<title>" . htmlspecialchars($frow['name']) . "</title>
286 <script type=\"text/javascript\" src=\"../../library/dialog.js\"></script>
287 <script language=\"JavaScript\">";
289 $html .= "
290 // Process click on Print button.
291 function printme() {
292 var divstyle = document.getElementById('hideonprint').style;
293 divstyle.display = 'none';
294 window.print();
297 </script>
298 </head>
299 <body bgcolor='#ffffff'>
300 <form name='theform' method='post' action='printed_fee_sheet.php?fill=$form_fill'
301 onsubmit='return opener.top.restoreSession()'>
302 <center>";
304 // Set Pagebreak for multi forms
305 if ($form_fill == 2) {
306 $html .= "<div class=pagebreak>\n";
307 } else {
308 $html .= "<div>\n";
311 $today = date('Y-m-d');
313 $alertmsg = ''; // anything here pops up in an alert box
315 // Get details for the primary facility.
316 $frow = sqlQuery("SELECT * FROM facility WHERE primary_business_entity = 1");
318 // If primary is not set try to old method of guessing...for backward compatibility
319 if (empty($frow)) {
320 $frow = sqlQuery("SELECT * FROM facility " .
321 "ORDER BY billing_location DESC, accepts_assignment DESC, id LIMIT 1");
324 // Still missing...
325 if (empty($frow)) {
326 $alertmsg = xl("No Primary Business Entity selected in facility list");
329 // Loop on array of PIDS
330 $saved_pages = $pages; //Save calculated page count of a single fee sheet
332 foreach ($pid_list as $pid) {
334 if ($form_fill) {
335 // Get the patient's name and chart number.
336 $patdata = getPatientData($pid);
339 // This tracks our position in the $SBCODES array.
340 $cindex = 0;
342 while (--$pages >= 0) {
344 $html .= genFacilityTitle(xl('Superbill/Fee Sheet'), -1);
346 $html .="
347 <table class='bordertbl' cellspacing='0' cellpadding='0' width='100%'>
348 <tr>
349 <td valign='top'>
350 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
351 <tr>
352 <td class='toprow' style='width:10%'></td>
353 <td class='toprow' style='width:10%'></td>
354 <td class='toprow' style='width:25%'></td>
355 <td class='toprow' style='width:55%'></td>
356 </tr>";
358 $cindex = genColumn($cindex); // Column 1
360 if ($pages == 0) { // if this is the last page
361 $html .= "<tr>
362 <td colspan='3' valign='top' class='fshead' style='height:" . $lheight * 2 . "pt'>";
363 $html .= xl('Patient', 'r');
364 $html .= ":<br />";
366 if ($form_fill) {
367 $html .= $patdata['fname'] . ' ' . $patdata['mname'] . ' ' . $patdata['lname'] . "<br />\n";
368 $html .= $patdata['street'] . "<br />\n";
369 $html .= $patdata['city'] . ', ' . $patdata['state'] . ' ' . $patdata['postal_code'] . "\n";
372 $html .= "</td>
373 <td valign='top' class='fshead'>";
374 $html .= xl('DOB', 'r');
375 $html .= ":<br />";
377 if ($form_fill)
378 $html .= $patdata['DOB'];
380 $html .= xl('ID', 'r');
381 $html .= ":<br />";
383 if ($form_fill)
384 $html .= $patdata['pubpid'];
386 $html .= "</td>
387 </tr>
388 <tr>
389 <td colspan='3' valign='top' class='fshead' style='height:${lheight}pt'>";
390 $html .= xl('Doctor', 'r');
391 $html .= ":<br />";
393 $encdata = false;
394 if ($form_fill && $encounter) {
395 $query = "SELECT fe.reason, fe.date, u.fname, u.mname, u.lname, u.username " .
396 "FROM forms AS f " .
397 "JOIN form_encounter AS fe ON fe.id = f.form_id " .
398 "LEFT JOIN users AS u ON u.username = f.user " .
399 "WHERE f.pid = '$pid' AND f.encounter = '$encounter' AND f.formdir = 'newpatient' AND f.deleted = 0 " .
400 "ORDER BY f.id LIMIT 1";
401 $encdata = sqlQuery($query);
402 if (!empty($encdata['username'])) {
403 $html .= $encdata['fname'] . ' ' . $encdata['mname'] . ' ' . $encdata['lname'];
407 $html .= "</td>
408 <td valign='top' class='fshead'>";
409 $html .= xl('Reason', 'r');
410 $html .= ":<br />";
412 if (!empty($encdata)) {
413 $html .= $encdata['reason'];
416 $html .= "</td>
417 </tr>
418 <tr>
419 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
421 if (empty($GLOBALS['ippf_specific'])) {
422 $html .= xl('Insurance', 'r').":";
423 if ($form_fill) {
424 foreach (array('primary', 'secondary', 'tertiary') as $instype) {
425 $query = "SELECT * FROM insurance_data WHERE " .
426 "pid = '$pid' AND type = '$instype' " .
427 "ORDER BY date DESC LIMIT 1";
428 $row = sqlQuery($query);
429 if ($row['provider']) {
430 $icobj = new InsuranceCompany($row['provider']);
431 $adobj = $icobj->get_address();
432 $insco_name = trim($icobj->get_name());
433 if ($instype != 'primary')
434 $html .= ",";
435 if ($insco_name) {
436 $html .= "&nbsp;$insco_name";
437 } else {
438 $html .= "&nbsp;<font color='red'><b>Missing Name</b></font>";
443 } else {
444 // IPPF wants a visit date box with the current date in it.
445 $html .= xl('Visit date','r');
446 $html .= ":<br />\n";
447 if (!empty($encdata)) {
448 $html .= substr($encdata['date'], 0, 10);
449 } else {
450 $html .= oeFormatShortDate(date('Y-m-d')) . "\n";
454 $html .= "</td>
455 </tr>
456 <tr>
457 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
458 $html .= xl('Prior Visit', 'r');
459 $html .= ":<br />
460 </td>
461 </tr>
462 <tr>
463 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
464 $html .= xl('Today\'s Charges', 'r');
465 $html .= ":<br />
466 </td>
467 </tr>
468 <tr>
469 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
470 $html .= xl('Today\'s Balance', 'r');
471 $html .= ":<br />
472 </td>
473 </tr>
474 <tr>
475 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
476 $html .= xl('Notes', 'r');
477 $html .= ":<br />
478 </td>
479 </tr>";
480 } // end if last page
482 $html .= "</table>
483 </td>
484 <td valign='top'>
485 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
486 <tr>
487 <td class='toprow' style='width:10%'></td>
488 <td class='toprow' style='width:10%'></td>
489 <td class='toprow' style='width:25%'></td>
490 <td class='toprow' style='width:55%'></td>
491 </tr>";
493 $cindex = genColumn($cindex); // Column 2
495 if ($pages == 0) { // if this is the last page
496 $html .= "<tr>
497 <td colspan='4' valign='top' class='fshead' style='height:" . $lheight * 8 . "pt'>";
498 $html .= xl('Notes', 'r');
499 $html .= ":<br />
500 </td>
501 </tr>";
502 } // end if last page
504 $html .= "</table>
505 </td>
506 <td valign='top'>
507 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
508 <tr>
509 <td class='toprow' style='width:10%'></td>
510 <td class='toprow' style='width:10%'></td>
511 <td class='toprow' style='width:25%'></td>
512 <td class='toprow' style='width:55%'></td>
513 </tr>";
515 $cindex = genColumn($cindex); // Column 3
517 if ($pages == 0) { // if this is the last page
518 $html .= "<tr>
519 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 6 . "pt;border-width:0 1px 0 0'>
520 &nbsp;
521 </td>
522 </tr>
523 <tr>
524 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 2 . "pt'>";
525 $html .= xl('Signature', 'r');
526 $html .= ":<br />
527 </td>
528 </tr>";
529 } // end if last page
531 $html .= "</table>
532 </td>
533 </tr>
535 </table>";
537 $html .= "</div>"; //end of div.pageLetter
539 } // end while
540 $pages = $saved_pages; //RESET
543 // Common End Code
544 if ($form_fill != 2) { //use native browser 'print' for multipage
545 $html .= "<div id='hideonprint'>
547 <input type='button' value='";
549 $html .= xl('Print', 'r');
550 $html .="' onclick='printme()' />
551 </div>";
554 $html .= "
555 </form>
556 </center>
557 </body>
558 </html>";
560 // Send final result to display
561 echo $html;