Compact css tweak
[openemr.git] / interface / patient_file / printed_fee_sheet.php
blobd5aa53c29f0c132215164ab47e58e7f60d369457
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 use OpenEMR\Services\FacilityService;
20 $facilityService = new FacilityService();
22 function genColumn($ix)
24 global $html;
25 global $SBCODES;
26 for ($imax = count($SBCODES); $ix < $imax; ++$ix) {
27 $a = explode('|', $SBCODES[$ix], 2);
28 $cmd = trim($a[0]);
29 if ($cmd == '*C') { // column break
30 return++$ix;
33 if ($cmd == '*B') { // Borderless and empty
34 $html .= " <tr><td colspan='5' class='fscode' style='border-width:0 1px 0 0;padding-top:1px;' nowrap>&nbsp;</td></tr>\n";
35 } else if ($cmd == '*G') {
36 $title = htmlspecialchars($a[1]);
37 if (!$title) {
38 $title = '&nbsp;';
41 $html .= " <tr><td colspan='5' align='center' class='fsgroup' style='vertical-align:middle' nowrap>$title</td></tr>\n";
42 } else if ($cmd == '*H') {
43 $title = htmlspecialchars($a[1]);
44 if (!$title) {
45 $title = '&nbsp;';
48 $html .= " <tr><td colspan='5' class='fshead' style='vertical-align:middle' nowrap>$title</td></tr>\n";
49 } else {
50 $title = htmlspecialchars($a[1]);
51 if (!$title) {
52 $title = '&nbsp;';
55 $b = explode(':', $cmd);
56 $html .= " <tr>\n";
57 $html .= " <td class='fscode' style='vertical-align:middle;width:14pt' nowrap>&nbsp;</td>\n";
58 if (count($b) <= 1) {
59 $code = $b[0];
60 if (!$code) {
61 $code = '&nbsp;';
64 $html .= " <td class='fscode' style='vertical-align:middle' nowrap>$code</td>\n";
65 $html .= " <td colspan='3' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
66 } else {
67 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>" . $b[0] . '/' . $b[1] . "</td>\n";
68 $html .= " <td colspan='2' class='fscode' style='vertical-align:middle' nowrap>$title</td>\n";
71 $html .= " </tr>\n";
75 return $ix;
78 // MAIN Body
80 // Build output to handle multiple pids and and superbill for each patient.
81 // This value is initially a maximum, and will be recomputed to
82 // distribute lines evenly among the pages. (was 55)
83 $lines_per_page = 55;
85 $lines_in_stats = 8;
87 $header_height = 44; // height of page headers in points
88 // This tells us if patient/encounter data is to be filled in.
89 // 1 = single PID from popup, 2=array of PIDs for session
91 if (empty($_GET['fill'])) {
92 $form_fill = 0;
93 } else {
94 $form_fill = $_GET['fill'];
97 // Show based on session array or single pid?
98 $pid_list = array();
100 if (!empty($_SESSION['pidList']) and $form_fill == 2) {
101 $pid_list = $_SESSION['pidList'];
102 } else if ($form_fill == 1) {
103 array_push($pid_list, $pid); //get from active PID
104 } else {
105 array_push($pid_list, ''); // empty element for blank form
108 // This file is optional. You can create it to customize how the printed
109 // fee sheet looks, otherwise you'll get a mirror of your actual fee sheet.
111 if (file_exists("../../custom/fee_sheet_codes.php")) {
112 include_once("../../custom/fee_sheet_codes.php");
115 // TBD: Move these to globals.php, or make them user-specific.
116 $fontsize = 7;
117 $page_height = 700;
119 $padding = 0;
121 // The $SBCODES table is a simple indexed array whose values are
122 // strings of the form "code|text" where code may be either a billing
123 // code or one of the following:
125 // *H - A main heading, where "text" is its title (to be centered).
126 // *G - Specifies a new category, where "text" is its name.
127 // *B - A borderless blank row.
128 // *C - Ends the current column and starts a new one.
129 // If $SBCODES is not provided, then manufacture it from the Fee Sheet.
131 if (empty($SBCODES)) {
132 $SBCODES = array();
133 $last_category = '';
135 // Create entries based on the fee_sheet_options table.
136 $res = sqlStatement("SELECT * FROM fee_sheet_options " .
137 "ORDER BY fs_category, fs_option");
138 while ($row = sqlFetchArray($res)) {
139 $fs_category = $row['fs_category'];
140 $fs_option = $row['fs_option'];
141 $fs_codes = $row['fs_codes'];
142 if ($fs_category !== $last_category) {
143 $last_category = $fs_category;
144 $SBCODES[] = '*G|' . substr($fs_category, 1);
147 $SBCODES[] = " |" . substr($fs_option, 1);
150 // Create entries based on categories defined within the codes.
151 $pres = sqlStatement("SELECT option_id, title FROM list_options " .
152 "WHERE list_id = 'superbill' AND activity = 1 ORDER BY seq");
153 while ($prow = sqlFetchArray($pres)) {
154 $SBCODES[] = '*G|' . xl_list_label($prow['title']);
155 $res = sqlStatement("SELECT code_type, code, code_text FROM codes " .
156 "WHERE superbill = '" . $prow['option_id'] . "' AND active = 1 " .
157 "ORDER BY code_text");
158 while ($row = sqlFetchArray($res)) {
159 $SBCODES[] = $row['code'] . '|' . $row['code_text'];
163 // Create one more group, for Products.
164 if ($GLOBALS['sell_non_drug_products']) {
165 $SBCODES[] = '*G|' . xl('Products');
166 $tres = sqlStatement("SELECT " .
167 "dt.drug_id, dt.selector, d.name, d.ndc_number " .
168 "FROM drug_templates AS dt, drugs AS d WHERE " .
169 "d.drug_id = dt.drug_id AND d.active = 1 " .
170 "ORDER BY d.name, dt.selector, dt.drug_id");
171 while ($trow = sqlFetchArray($tres)) {
172 $tmp = $trow['selector'];
173 if ($trow['name'] !== $trow['selector']) {
174 $tmp .= ' ' . $trow['name'];
177 $prodcode = empty($trow['ndc_number']) ? ('(' . $trow['drug_id'] . ')') :
178 $trow['ndc_number'];
179 $SBCODES[] = "$prodcode|$tmp";
183 // Extra stuff for the labs section.
184 $SBCODES[] = '*G|' . xl('Notes');
185 $percol = intval((count($SBCODES) + 2) / 3);
186 while (count($SBCODES) < $percol * 3) {
187 $SBCODES[] = '*B|';
190 // Adjust lines per page to distribute lines evenly among the pages.
191 $pages = intval(($percol + $lines_in_stats + $lines_per_page - 1) / $lines_per_page);
192 $lines_per_page = intval(($percol + $lines_in_stats + $pages - 1) / $pages);
194 // Figure out page and column breaks.
195 $pages = 1;
196 $lines = $percol;
197 $page_start_index = 0;
198 while ($lines + $lines_in_stats > $lines_per_page) {
199 ++$pages;
200 $lines_this_page = $lines > $lines_per_page ? $lines_per_page : $lines;
201 $lines -= $lines_this_page;
202 array_splice($SBCODES, $lines_this_page * 3 + $page_start_index, 0, '*C|');
203 array_splice($SBCODES, $lines_this_page * 2 + $page_start_index, 0, '*C|');
204 array_splice($SBCODES, $lines_this_page * 1 + $page_start_index, 0, '*C|');
205 $page_start_index += $lines_this_page * 3 + 3;
208 array_splice($SBCODES, $lines * 2 + $page_start_index, 0, '*C|');
209 array_splice($SBCODES, $lines * 1 + $page_start_index, 0, '*C|');
212 $lheight = sprintf('%d', ($page_height - $header_height) / $lines_per_page);
214 // Common HTML Header information
216 $html = "<html>
217 <head>";
219 $html .= "
220 <style>
221 body {
222 font-family: sans-serif;
223 font-weight: normal;
225 .bordertbl {
226 width: 100%;
227 border-style: solid;
228 border-width: 0 0 1px 1px;
229 border-spacing: 0;
230 border-collapse: collapse;
231 border-color: #999999;
233 td.toprow {
234 height: 1px;
235 padding: 0;
236 border-style: solid;
237 border-width: 0 0 0 0;
238 border-color: #999999;
240 td.fsgroup {
241 height: ${lheight}pt;
242 font-family: sans-serif;
243 font-weight: bold;
244 font-size: $fontsize pt;
245 background-color: #cccccc;
246 padding: ${padding}pt 2pt 0pt 2pt;
247 border-style: solid;
248 border-width: 1px 1px 0 0;
249 border-color: #999999;
251 td.fshead {
252 height: ${lheight}pt;
253 font-family: sans-serif;
254 font-weight: bold;
255 font-size: ${fontsize}pt;
256 padding: ${padding}pt 2pt 0pt 2pt;
257 border-style: solid;
258 border-width: 1px 1px 0 0;
259 border-color: #999999;
261 td.fscode {
262 height: ${lheight}pt;
263 font-family: sans-serif;
264 font-weight: normal;
265 font-size: ${fontsize}pt;
266 padding: ${padding}pt 2pt 0pt 2pt;
267 border-style: solid;
268 border-width: 1px 1px 0 0;
269 border-color: #999999;
272 .ftitletable {
273 width: 100%;
274 height: ${header_height}pt;
275 margin: 0 0 8pt 0;
277 .ftitlecell1 {
278 width: 33%;
279 vertical-align: top;
280 text-align: left;
281 font-size: 14pt;
282 font-weight: bold;
284 .ftitlecell2 {
285 width: 33%;
286 vertical-align: top;
287 text-align: right;
288 font-size: 9pt;
290 .ftitlecellm {
291 width: 34%;
292 vertical-align: top;
293 text-align: center;
294 font-size: 14pt;
295 font-weight: bold;
298 div.pagebreak {
299 page-break-after: always;
300 height: ${page_height}pt;
302 </style>";
304 $html .= "<title>" . htmlspecialchars($frow['name']) . "</title>
305 <script type='text/javascript' src='" . $GLOBALS['assets_static_relative'] . "/jquery-min-1-2-2/index.js'></script>
306 <script type=\"text/javascript\" src=\"../../library/dialog.js?v=" . $v_js_includes . "\"></script>
307 <script language=\"JavaScript\">";
309 $html .= "
310 $(document).ready(function() {
311 var win = top.printLogSetup ? top : opener.top;
312 win.printLogSetup(document.getElementById('printbutton'));
315 // Process click on Print button.
316 function printlog_before_print() {
317 var divstyle = document.getElementById('hideonprint').style;
318 divstyle.display = 'none';
321 </script>
322 </head>
323 <body bgcolor='#ffffff'>
324 <form name='theform' method='post' action='printed_fee_sheet.php?fill=" . attr($form_fill) . "'
325 onsubmit='return opener.top.restoreSession()'>
326 <center>";
328 // Set Pagebreak for multi forms
329 if ($form_fill == 2) {
330 $html .= "<div class=pagebreak>\n";
331 } else {
332 $html .= "<div>\n";
335 $today = date('Y-m-d');
337 $alertmsg = ''; // anything here pops up in an alert box
339 // Get details for the primary facility.
340 $frow = $facilityService->getPrimaryBusinessEntity();
342 // If primary is not set try to old method of guessing...for backward compatibility
343 if (empty($frow)) {
344 $frow = $facilityService->getPrimaryBusinessEntity(array("useLegacyImplementation" => true));
347 // Still missing...
348 if (empty($frow)) {
349 $alertmsg = xl("No Primary Business Entity selected in facility list");
352 $logo = '';
353 $ma_logo_path = "sites/" . $_SESSION['site_id'] . "/images/ma_logo.png";
354 if (is_file("$webserver_root/$ma_logo_path")) {
355 $logo = "<img src='$web_root/$ma_logo_path' style='height:" . round(9 * 5.14) . "pt' />";
356 } else {
357 $logo = "<!-- '$ma_logo_path' does not exist. -->";
360 // Loop on array of PIDS
361 $saved_pages = $pages; //Save calculated page count of a single fee sheet
363 foreach ($pid_list as $pid) {
364 if ($form_fill) {
365 // Get the patient's name and chart number.
366 $patdata = getPatientData($pid);
369 // This tracks our position in the $SBCODES array.
370 $cindex = 0;
372 while (--$pages >= 0) {
373 $html .= genFacilityTitle(xl('Superbill/Fee Sheet'), -1, $logo);
375 $html .="
376 <table class='bordertbl' cellspacing='0' cellpadding='0' width='100%'>
377 <tr>
378 <td valign='top'>
379 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
380 <tr>
381 <td class='toprow' style='width:10%'></td>
382 <td class='toprow' style='width:10%'></td>
383 <td class='toprow' style='width:25%'></td>
384 <td class='toprow' style='width:55%'></td>
385 </tr>";
387 $cindex = genColumn($cindex); // Column 1
389 if ($pages == 0) { // if this is the last page
390 $html .= "<tr>
391 <td colspan='3' valign='top' class='fshead' style='height:" . $lheight * 2 . "pt'>";
392 $html .= xl('Patient', 'r');
393 $html .= ":<br />";
395 if ($form_fill) {
396 $html .= $patdata['fname'] . ' ' . $patdata['mname'] . ' ' . $patdata['lname'] . "<br />\n";
397 $html .= $patdata['street'] . "<br />\n";
398 $html .= $patdata['city'] . ', ' . $patdata['state'] . ' ' . $patdata['postal_code'] . "\n";
401 $html .= "</td>
402 <td valign='top' class='fshead'>";
403 $html .= xl('DOB', 'r');
404 $html .= ":<br />";
406 if ($form_fill) {
407 $html .= $patdata['DOB'];
410 $html .= xl('ID', 'r');
411 $html .= ":<br />";
413 if ($form_fill) {
414 $html .= $patdata['pubpid'];
417 $html .= "</td>
418 </tr>
419 <tr>
420 <td colspan='3' valign='top' class='fshead' style='height:${lheight}pt'>";
421 $html .= xl('Doctor', 'r');
422 $html .= ":<br />";
424 $encdata = false;
425 if ($form_fill && $encounter) {
426 $query = "SELECT fe.reason, fe.date, u.fname, u.mname, u.lname, u.username " .
427 "FROM forms AS f " .
428 "JOIN form_encounter AS fe ON fe.id = f.form_id " .
429 "LEFT JOIN users AS u ON u.username = f.user " .
430 "WHERE f.pid = '$pid' AND f.encounter = '$encounter' AND f.formdir = 'newpatient' AND f.deleted = 0 " .
431 "ORDER BY f.id LIMIT 1";
432 $encdata = sqlQuery($query);
433 if (!empty($encdata['username'])) {
434 $html .= $encdata['fname'] . ' ' . $encdata['mname'] . ' ' . $encdata['lname'];
438 $html .= "</td>
439 <td valign='top' class='fshead'>";
440 $html .= xl('Reason', 'r');
441 $html .= ":<br />";
443 if (!empty($encdata)) {
444 $html .= $encdata['reason'];
447 $html .= "</td>
448 </tr>
449 <tr>
450 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
452 if (empty($GLOBALS['ippf_specific'])) {
453 $html .= xl('Insurance', 'r').":";
454 if ($form_fill) {
455 foreach (array('primary', 'secondary', 'tertiary') as $instype) {
456 $query = "SELECT * FROM insurance_data WHERE " .
457 "pid = '$pid' AND type = '$instype' " .
458 "ORDER BY date DESC LIMIT 1";
459 $row = sqlQuery($query);
460 if ($row['provider']) {
461 $icobj = new InsuranceCompany($row['provider']);
462 $adobj = $icobj->get_address();
463 $insco_name = trim($icobj->get_name());
464 if ($instype != 'primary') {
465 $html .= ",";
468 if ($insco_name) {
469 $html .= "&nbsp;$insco_name";
470 } else {
471 $html .= "&nbsp;<font color='red'><b>Missing Name</b></font>";
476 } else {
477 // IPPF wants a visit date box with the current date in it.
478 $html .= xl('Visit date', 'r');
479 $html .= ":<br />\n";
480 if (!empty($encdata)) {
481 $html .= substr($encdata['date'], 0, 10);
482 } else {
483 $html .= text(oeFormatShortDate(date('Y-m-d'))) . "\n";
487 $html .= "</td>
488 </tr>
489 <tr>
490 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
491 $html .= xl('Prior Visit', 'r');
492 $html .= ":<br />
493 </td>
494 </tr>
495 <tr>
496 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
497 $html .= xl('Today\'s Charges', 'r');
498 $html .= ":<br />
499 </td>
500 </tr>
501 <tr>
502 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
503 $html .= xl('Today\'s Balance', 'r');
504 $html .= ":<br />
505 </td>
506 </tr>
507 <tr>
508 <td colspan='4' valign='top' class='fshead' style='height:${lheight}pt'>";
509 $html .= xl('Notes', 'r');
510 $html .= ":<br />
511 </td>
512 </tr>";
513 } // end if last page
515 $html .= "</table>
516 </td>
517 <td valign='top'>
518 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
519 <tr>
520 <td class='toprow' style='width:10%'></td>
521 <td class='toprow' style='width:10%'></td>
522 <td class='toprow' style='width:25%'></td>
523 <td class='toprow' style='width:55%'></td>
524 </tr>";
526 $cindex = genColumn($cindex); // Column 2
528 if ($pages == 0) { // if this is the last page
529 $html .= "<tr>
530 <td colspan='4' valign='top' class='fshead' style='height:" . $lheight * 8 . "pt'>";
531 $html .= xl('Notes', 'r');
532 $html .= ":<br />
533 </td>
534 </tr>";
535 } // end if last page
537 $html .= "</table>
538 </td>
539 <td valign='top'>
540 <table border='0' cellspacing='0' cellpadding='0' width='100%'>
541 <tr>
542 <td class='toprow' style='width:10%'></td>
543 <td class='toprow' style='width:10%'></td>
544 <td class='toprow' style='width:25%'></td>
545 <td class='toprow' style='width:55%'></td>
546 </tr>";
548 $cindex = genColumn($cindex); // Column 3
550 if ($pages == 0) { // if this is the last page
551 $html .= "<tr>
552 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 6 . "pt;border-width:0 1px 0 0'>
553 &nbsp;
554 </td>
555 </tr>
556 <tr>
557 <td valign='top' colspan='4' class='fshead' style='height:" . $lheight * 2 . "pt'>";
558 $html .= xl('Signature', 'r');
559 $html .= ":<br />
560 </td>
561 </tr>";
562 } // end if last page
564 $html .= "</table>
565 </td>
566 </tr>
568 </table>";
570 $html .= "</div>"; //end of div.pageLetter
571 } // end while
572 $pages = $saved_pages; //RESET
575 // Common End Code
576 if ($form_fill != 2) { //use native browser 'print' for multipage
577 $html .= "<div id='hideonprint'>
579 <input type='button' value='";
581 $html .= xla('Print');
582 $html .="' id='printbutton' />
583 </div>";
586 $html .= "
587 </form>
588 </center>
589 </body>
590 </html>";
592 // Send final result to display
593 echo $html;