Moved recent v5.0.0 statement fix to current master.
[openemr.git] / interface / patient_file / printed_fee_sheet.php
blob5774fb1b8a5894c7fcd91457f5f01d51e07d9c64
1 <?php
3 // Copyright (C) 2007-2016 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");
18 $facilityService = new \services\FacilityService();
20 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;
31 if ($cmd == '*B') { // Borderless and empty
32 $html .= " <tr><td colspan='5' class='fscode' style='border-width:0 1px 0 0;padding-top:1px;' nowrap>&nbsp;</td></tr>\n";
33 } else if ($cmd == '*G') {
34 $title = htmlspecialchars($a[1]);
35 if (!$title) {
36 $title = '&nbsp;';
39 $html .= " <tr><td colspan='5' align='center' class='fsgroup' style='vertical-align:middle' nowrap>$title</td></tr>\n";
40 } else if ($cmd == '*H') {
41 $title = htmlspecialchars($a[1]);
42 if (!$title) {
43 $title = '&nbsp;';
46 $html .= " <tr><td colspan='5' class='fshead' style='vertical-align:middle' nowrap>$title</td></tr>\n";
47 } else {
48 $title = htmlspecialchars($a[1]);
49 if (!$title) {
50 $title = '&nbsp;';
53 $b = explode(':', $cmd);
54 $html .= " <tr>\n";
55 $html .= " <td class='fscode' style='vertical-align:middle;width:14pt' nowrap>&nbsp;</td>\n";
56 if (count($b) <= 1) {
57 $code = $b[0];
58 if (!$code) {
59 $code = '&nbsp;';
62 $html .= " <td class='fscode' style='vertical-align:middle' nowrap>$code</td>\n";
63 $html .= " <td colspan='3' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
64 } else {
65 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>" . $b[0] . '/' . $b[1] . "</td>\n";
66 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
69 $html .= " </tr>\n";
73 return $ix;
76 // MAIN Body
78 // Build output to handle multiple pids and and superbill for each patient.
79 // This value is initially a maximum, and will be recomputed to
80 // distribute lines evenly among the pages. (was 55)
81 $lines_per_page = 55;
83 $lines_in_stats = 8;
85 $header_height = 44; // height of page headers in points
86 // This tells us if patient/encounter data is to be filled in.
87 // 1 = single PID from popup, 2=array of PIDs for session
89 if (empty($_GET['fill'])) {
90 $form_fill = 0;
91 } else {
92 $form_fill = $_GET['fill'];
95 // Show based on session array or single pid?
96 $pid_list = array();
98 if (!empty($_SESSION['pidList']) and $form_fill == 2) {
99 $pid_list = $_SESSION['pidList'];
100 } else if ($form_fill == 1) {
101 array_push($pid_list, $pid); //get from active PID
102 } else {
103 array_push($pid_list, ''); // empty element for blank form
106 // This file is optional. You can create it to customize how the printed
107 // fee sheet looks, otherwise you'll get a mirror of your actual fee sheet.
109 if (file_exists("../../custom/fee_sheet_codes.php")) {
110 include_once("../../custom/fee_sheet_codes.php");
113 // TBD: Move these to globals.php, or make them user-specific.
114 $fontsize = 7;
115 $page_height = 700;
117 $padding = 0;
119 // The $SBCODES table is a simple indexed array whose values are
120 // strings of the form "code|text" where code may be either a billing
121 // code or one of the following:
123 // *H - A main heading, where "text" is its title (to be centered).
124 // *G - Specifies a new category, where "text" is its name.
125 // *B - A borderless blank row.
126 // *C - Ends the current column and starts a new one.
127 // If $SBCODES is not provided, then manufacture it from the Fee Sheet.
129 if (empty($SBCODES)) {
130 $SBCODES = array();
131 $last_category = '';
133 // Create entries based on the fee_sheet_options table.
134 $res = sqlStatement("SELECT * FROM fee_sheet_options " .
135 "ORDER BY fs_category, fs_option");
136 while ($row = sqlFetchArray($res)) {
137 $fs_category = $row['fs_category'];
138 $fs_option = $row['fs_option'];
139 $fs_codes = $row['fs_codes'];
140 if ($fs_category !== $last_category) {
141 $last_category = $fs_category;
142 $SBCODES[] = '*G|' . substr($fs_category, 1);
145 $SBCODES[] = " |" . substr($fs_option, 1);
148 // Create entries based on categories defined within the codes.
149 $pres = sqlStatement("SELECT option_id, title FROM list_options " .
150 "WHERE list_id = 'superbill' AND activity = 1 ORDER BY seq");
151 while ($prow = sqlFetchArray($pres)) {
152 $SBCODES[] = '*G|' . xl_list_label($prow['title']);
153 $res = sqlStatement("SELECT code_type, code, code_text FROM codes " .
154 "WHERE superbill = '" . $prow['option_id'] . "' AND active = 1 " .
155 "ORDER BY code_text");
156 while ($row = sqlFetchArray($res)) {
157 $SBCODES[] = $row['code'] . '|' . $row['code_text'];
161 // Create one more group, for Products.
162 if ($GLOBALS['sell_non_drug_products']) {
163 $SBCODES[] = '*G|' . xl('Products');
164 $tres = sqlStatement("SELECT " .
165 "dt.drug_id, dt.selector, d.name, d.ndc_number " .
166 "FROM drug_templates AS dt, drugs AS d WHERE " .
167 "d.drug_id = dt.drug_id AND d.active = 1 " .
168 "ORDER BY d.name, dt.selector, dt.drug_id");
169 while ($trow = sqlFetchArray($tres)) {
170 $tmp = $trow['selector'];
171 if ($trow['name'] !== $trow['selector']) {
172 $tmp .= ' ' . $trow['name'];
175 $prodcode = empty($trow['ndc_number']) ? ('(' . $trow['drug_id'] . ')') :
176 $trow['ndc_number'];
177 $SBCODES[] = "$prodcode|$tmp";
181 // Extra stuff for the labs section.
182 $SBCODES[] = '*G|' . xl('Notes');
183 $percol = intval((count($SBCODES) + 2) / 3);
184 while (count($SBCODES) < $percol * 3) {
185 $SBCODES[] = '*B|';
188 // Adjust lines per page to distribute lines evenly among the pages.
189 $pages = intval(($percol + $lines_in_stats + $lines_per_page - 1) / $lines_per_page);
190 $lines_per_page = intval(($percol + $lines_in_stats + $pages - 1) / $pages);
192 // Figure out page and column breaks.
193 $pages = 1;
194 $lines = $percol;
195 $page_start_index = 0;
196 while ($lines + $lines_in_stats > $lines_per_page) {
197 ++$pages;
198 $lines_this_page = $lines > $lines_per_page ? $lines_per_page : $lines;
199 $lines -= $lines_this_page;
200 array_splice($SBCODES, $lines_this_page * 3 + $page_start_index, 0, '*C|');
201 array_splice($SBCODES, $lines_this_page * 2 + $page_start_index, 0, '*C|');
202 array_splice($SBCODES, $lines_this_page * 1 + $page_start_index, 0, '*C|');
203 $page_start_index += $lines_this_page * 3 + 3;
206 array_splice($SBCODES, $lines * 2 + $page_start_index, 0, '*C|');
207 array_splice($SBCODES, $lines * 1 + $page_start_index, 0, '*C|');
210 $lheight = sprintf('%d', ($page_height - $header_height) / $lines_per_page);
212 // Common HTML Header information
214 $html = "<html>
215 <head>";
217 $html .= "
218 <style>
219 body {
220 font-family: sans-serif;
221 font-weight: normal;
223 .bordertbl {
224 width: 100%;
225 border-style: solid;
226 border-width: 0 0 1px 1px;
227 border-spacing: 0;
228 border-collapse: collapse;
229 border-color: #999999;
231 td.toprow {
232 height: 1px;
233 padding: 0;
234 border-style: solid;
235 border-width: 0 0 0 0;
236 border-color: #999999;
238 td.fsgroup {
239 height: ${lheight}pt;
240 font-family: sans-serif;
241 font-weight: bold;
242 font-size: $fontsize pt;
243 background-color: #cccccc;
244 padding: ${padding}pt 2pt 0pt 2pt;
245 border-style: solid;
246 border-width: 1px 1px 0 0;
247 border-color: #999999;
249 td.fshead {
250 height: ${lheight}pt;
251 font-family: sans-serif;
252 font-weight: bold;
253 font-size: ${fontsize}pt;
254 padding: ${padding}pt 2pt 0pt 2pt;
255 border-style: solid;
256 border-width: 1px 1px 0 0;
257 border-color: #999999;
259 td.fscode {
260 height: ${lheight}pt;
261 font-family: sans-serif;
262 font-weight: normal;
263 font-size: ${fontsize}pt;
264 padding: ${padding}pt 2pt 0pt 2pt;
265 border-style: solid;
266 border-width: 1px 1px 0 0;
267 border-color: #999999;
270 .ftitletable {
271 width: 100%;
272 height: ${header_height}pt;
273 margin: 0 0 8pt 0;
275 .ftitlecell1 {
276 vertical-align: top;
277 text-align: left;
278 font-size: 14pt;
279 font-weight: bold;
281 .ftitlecell2 {
282 vertical-align: top;
283 text-align: right;
284 font-size: 9pt;
286 div.pagebreak {
287 page-break-after: always;
288 height: ${page_height}pt;
290 </style>";
292 $html .= "<title>" . htmlspecialchars($frow['name']) . "</title>
293 <script type='text/javascript' src='" . $GLOBALS['assets_static_relative'] . "/jquery-min-1-2-2/index.js'></script>
294 <script type=\"text/javascript\" src=\"../../library/dialog.js?v=" . $v_js_includes . "\"></script>
295 <script language=\"JavaScript\">";
297 $html .= "
298 $(document).ready(function() {
299 var win = top.printLogSetup ? top : opener.top;
300 win.printLogSetup(document.getElementById('printbutton'));
303 // Process click on Print button.
304 function printlog_before_print() {
305 var divstyle = document.getElementById('hideonprint').style;
306 divstyle.display = 'none';
309 </script>
310 </head>
311 <body bgcolor='#ffffff'>
312 <form name='theform' method='post' action='printed_fee_sheet.php?fill=$form_fill'
313 onsubmit='return opener.top.restoreSession()'>
314 <center>";
316 // Set Pagebreak for multi forms
317 if ($form_fill == 2) {
318 $html .= "<div class=pagebreak>\n";
319 } else {
320 $html .= "<div>\n";
323 $today = date('Y-m-d');
325 $alertmsg = ''; // anything here pops up in an alert box
327 // Get details for the primary facility.
328 $frow = $facilityService->getPrimaryBusinessEntity();
330 // If primary is not set try to old method of guessing...for backward compatibility
331 if (empty($frow)) {
332 $frow = $facilityService->getPrimaryBusinessEntity(array("useLegacyImplementation" => true));
335 // Still missing...
336 if (empty($frow)) {
337 $alertmsg = xl("No Primary Business Entity selected in facility list");
340 // Loop on array of PIDS
341 $saved_pages = $pages; //Save calculated page count of a single fee sheet
343 foreach ($pid_list as $pid) {
344 if ($form_fill) {
345 // Get the patient's name and chart number.
346 $patdata = getPatientData($pid);
349 // This tracks our position in the $SBCODES array.
350 $cindex = 0;
352 while (--$pages >= 0) {
353 $html .= genFacilityTitle(xl('Superbill/Fee Sheet'), -1);
355 $html .="
356 <table class='bordertbl' cellspacing='0' cellpadding='0' width='100%'>
357 <tr>
358 <td valign='top'>
359 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
360 <tr>
361 <td class='toprow' style='width:10%'></td>
362 <td class='toprow' style='width:10%'></td>
363 <td class='toprow' style='width:25%'></td>
364 <td class='toprow' style='width:55%'></td>
365 </tr>";
367 $cindex = genColumn($cindex); // Column 1
369 if ($pages == 0) { // if this is the last page
370 $html .= "<tr>
371 <td colspan='3' valign='top' class='fshead' style='height:" . $lheight * 2 . "pt'>";
372 $html .= xl('Patient', 'r');
373 $html .= ":<br />";
375 if ($form_fill) {
376 $html .= $patdata['fname'] . ' ' . $patdata['mname'] . ' ' . $patdata['lname'] . "<br />\n";
377 $html .= $patdata['street'] . "<br />\n";
378 $html .= $patdata['city'] . ', ' . $patdata['state'] . ' ' . $patdata['postal_code'] . "\n";
381 $html .= "</td>
382 <td valign='top' class='fshead'>";
383 $html .= xl('DOB', 'r');
384 $html .= ":<br />";
386 if ($form_fill) {
387 $html .= $patdata['DOB'];
390 $html .= xl('ID', 'r');
391 $html .= ":<br />";
393 if ($form_fill) {
394 $html .= $patdata['pubpid'];
397 $html .= "</td>
398 </tr>
399 <tr>
400 <td colspan='3' valign='top' class='fshead' style='height:${lheight}pt'>";
401 $html .= xl('Doctor', 'r');
402 $html .= ":<br />";
404 $encdata = false;
405 if ($form_fill && $encounter) {
406 $query = "SELECT fe.reason, fe.date, u.fname, u.mname, u.lname, u.username " .
407 "FROM forms AS f " .
408 "JOIN form_encounter AS fe ON fe.id = f.form_id " .
409 "LEFT JOIN users AS u ON u.username = f.user " .
410 "WHERE f.pid = '$pid' AND f.encounter = '$encounter' AND f.formdir = 'newpatient' AND f.deleted = 0 " .
411 "ORDER BY f.id LIMIT 1";
412 $encdata = sqlQuery($query);
413 if (!empty($encdata['username'])) {
414 $html .= $encdata['fname'] . ' ' . $encdata['mname'] . ' ' . $encdata['lname'];
418 $html .= "</td>
419 <td valign='top' class='fshead'>";
420 $html .= xl('Reason', 'r');
421 $html .= ":<br />";
423 if (!empty($encdata)) {
424 $html .= $encdata['reason'];
427 $html .= "</td>
428 </tr>
429 <tr>
430 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
432 if (empty($GLOBALS['ippf_specific'])) {
433 $html .= xl('Insurance', 'r').":";
434 if ($form_fill) {
435 foreach (array('primary', 'secondary', 'tertiary') as $instype) {
436 $query = "SELECT * FROM insurance_data WHERE " .
437 "pid = '$pid' AND type = '$instype' " .
438 "ORDER BY date DESC LIMIT 1";
439 $row = sqlQuery($query);
440 if ($row['provider']) {
441 $icobj = new InsuranceCompany($row['provider']);
442 $adobj = $icobj->get_address();
443 $insco_name = trim($icobj->get_name());
444 if ($instype != 'primary') {
445 $html .= ",";
448 if ($insco_name) {
449 $html .= "&nbsp;$insco_name";
450 } else {
451 $html .= "&nbsp;<font color='red'><b>Missing Name</b></font>";
456 } else {
457 // IPPF wants a visit date box with the current date in it.
458 $html .= xl('Visit date', 'r');
459 $html .= ":<br />\n";
460 if (!empty($encdata)) {
461 $html .= substr($encdata['date'], 0, 10);
462 } else {
463 $html .= oeFormatShortDate(date('Y-m-d')) . "\n";
467 $html .= "</td>
468 </tr>
469 <tr>
470 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
471 $html .= xl('Prior Visit', 'r');
472 $html .= ":<br />
473 </td>
474 </tr>
475 <tr>
476 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
477 $html .= xl('Today\'s Charges', 'r');
478 $html .= ":<br />
479 </td>
480 </tr>
481 <tr>
482 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
483 $html .= xl('Today\'s Balance', 'r');
484 $html .= ":<br />
485 </td>
486 </tr>
487 <tr>
488 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
489 $html .= xl('Notes', 'r');
490 $html .= ":<br />
491 </td>
492 </tr>";
493 } // end if last page
495 $html .= "</table>
496 </td>
497 <td valign='top'>
498 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
499 <tr>
500 <td class='toprow' style='width:10%'></td>
501 <td class='toprow' style='width:10%'></td>
502 <td class='toprow' style='width:25%'></td>
503 <td class='toprow' style='width:55%'></td>
504 </tr>";
506 $cindex = genColumn($cindex); // Column 2
508 if ($pages == 0) { // if this is the last page
509 $html .= "<tr>
510 <td colspan='4' valign='top' class='fshead' style='height:" . $lheight * 8 . "pt'>";
511 $html .= xl('Notes', 'r');
512 $html .= ":<br />
513 </td>
514 </tr>";
515 } // end if last page
517 $html .= "</table>
518 </td>
519 <td valign='top'>
520 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
521 <tr>
522 <td class='toprow' style='width:10%'></td>
523 <td class='toprow' style='width:10%'></td>
524 <td class='toprow' style='width:25%'></td>
525 <td class='toprow' style='width:55%'></td>
526 </tr>";
528 $cindex = genColumn($cindex); // Column 3
530 if ($pages == 0) { // if this is the last page
531 $html .= "<tr>
532 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 6 . "pt;border-width:0 1px 0 0'>
533 &nbsp;
534 </td>
535 </tr>
536 <tr>
537 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 2 . "pt'>";
538 $html .= xl('Signature', 'r');
539 $html .= ":<br />
540 </td>
541 </tr>";
542 } // end if last page
544 $html .= "</table>
545 </td>
546 </tr>
548 </table>";
550 $html .= "</div>"; //end of div.pageLetter
551 } // end while
552 $pages = $saved_pages; //RESET
555 // Common End Code
556 if ($form_fill != 2) { //use native browser 'print' for multipage
557 $html .= "<div id='hideonprint'>
559 <input type='button' value='";
561 $html .= xla('Print');
562 $html .="' id='printbutton' />
563 </div>";
566 $html .= "
567 </form>
568 </center>
569 </body>
570 </html>";
572 // Send final result to display
573 echo $html;