support spaces and colons as modifier separators, as suggest by yehster
[openemr.git] / interface / forms / fee_sheet / new.php
blob5b5509c2fedf6c696f13eda01605046cfd45047a
1 <?php
2 // Copyright (C) 2005-2011 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once("../../globals.php");
10 require_once("$srcdir/acl.inc");
11 require_once("$srcdir/api.inc");
12 require_once("codes.php");
13 require_once("../../../custom/code_types.inc.php");
14 require_once("../../drugs/drugs.inc.php");
15 require_once("$srcdir/formatting.inc.php");
16 require_once("$srcdir/options.inc.php");
17 require_once("$srcdir/formdata.inc.php");
19 // Some table cells will not be displayed unless insurance billing is used.
20 $usbillstyle = $GLOBALS['ippf_specific'] ? " style='display:none'" : "";
22 function alphaCodeType($id) {
23 global $code_types;
24 foreach ($code_types as $key => $value) {
25 if ($value['id'] == $id) return $key;
27 return '';
30 // Helper function for creating drop-lists.
31 function endFSCategory() {
32 global $i, $last_category, $FEE_SHEET_COLUMNS;
33 if (! $last_category) return;
34 echo " </select>\n";
35 echo " </td>\n";
36 if ($i >= $FEE_SHEET_COLUMNS) {
37 echo " </tr>\n";
38 $i = 0;
42 // Generate JavaScript to build the array of diagnoses.
43 function genDiagJS($code_type, $code) {
44 global $code_types;
45 if ($code_types[$code_type]['diag']) {
46 echo "diags.push('$code');\n";
50 // For IPPF only. Returns 0 = none, 1 = nonsurgical, 2 = surgical.
52 function contraceptionClass($code_type, $code) {
53 global $code_types;
54 if (!$GLOBALS['ippf_specific']) return 0;
55 $contra = 0;
56 // Get the related service codes.
57 $codesrow = sqlQuery("SELECT related_code FROM codes WHERE " .
58 "code_type = '" . $code_types[$code_type]['id'] .
59 "' AND code = '$code' LIMIT 1");
60 if (!empty($codesrow['related_code']) && $code_type == 'MA') {
61 $relcodes = explode(';', $codesrow['related_code']);
62 foreach ($relcodes as $relstring) {
63 if ($relstring === '') continue;
64 list($reltype, $relcode) = explode(':', $relstring);
65 if ($reltype !== 'IPPF') continue;
66 if (preg_match('/^11....110/' , $relcode)) $contra |= 1;
67 else if (preg_match('/^11....999/' , $relcode)) $contra |= 1;
68 else if (preg_match('/^112152010/' , $relcode)) $contra |= 1;
69 else if (preg_match('/^11317[1-2]111/', $relcode)) $contra |= 1;
70 else if (preg_match('/^12118[1-2].13/', $relcode)) $contra |= 2;
71 else if (preg_match('/^12118[1-2]999/', $relcode)) $contra |= 2;
74 return $contra;
77 // This writes a billing line item to the output page.
79 function echoLine($lino, $codetype, $code, $modifier, $ndc_info='',
80 $auth = TRUE, $del = FALSE, $units = NULL, $fee = NULL, $id = NULL,
81 $billed = FALSE, $code_text = NULL, $justify = NULL, $provider_id = 0, $notecodes='')
83 global $code_types, $ndc_applies, $ndc_uom_choices, $justinit, $pid;
84 global $contraception, $usbillstyle;
86 if ($codetype == 'COPAY') {
87 if (!$code_text) $code_text = 'Cash';
88 if ($fee > 0) $fee = 0 - $fee;
90 if (! $code_text) {
91 $query = "select id, units, code_text from codes where code_type = '" .
92 $code_types[$codetype]['id'] . "' and " .
93 "code = '$code' and ";
94 if ($modifier) {
95 $query .= "modifier = '$modifier'";
96 } else {
97 $query .= "(modifier is null or modifier = '')";
99 $result = sqlQuery($query);
100 $code_text = $result['code_text'];
101 if (empty($units)) $units = max(1, intval($result['units']));
102 if (!isset($fee)) {
103 // Fees come from the prices table now.
104 $query = "SELECT prices.pr_price " .
105 "FROM patient_data, prices WHERE " .
106 "patient_data.pid = '$pid' AND " .
107 "prices.pr_id = '" . $result['id'] . "' AND " .
108 "prices.pr_selector = '' AND " .
109 "prices.pr_level = patient_data.pricelevel " .
110 "LIMIT 1";
111 echo "\n<!-- $query -->\n"; // debugging
112 $prrow = sqlQuery($query);
113 $fee = empty($prrow) ? 0 : $prrow['pr_price'];
116 $fee = sprintf('%01.2f', $fee);
117 if (empty($units)) $units = 1;
118 $units = max(1, intval($units));
119 // We put unit price on the screen, not the total line item fee.
120 $price = $fee / $units;
121 $strike1 = ($id && $del) ? "<strike>" : "";
122 $strike2 = ($id && $del) ? "</strike>" : "";
123 echo " <tr>\n";
124 echo " <td class='billcell'>$strike1" .
125 ($codetype == 'COPAY' ? xl($codetype) : $codetype) . $strike2;
126 if ($id) {
127 echo "<input type='hidden' name='bill[$lino][id]' value='$id'>";
129 echo "<input type='hidden' name='bill[$lino][code_type]' value='$codetype'>";
130 echo "<input type='hidden' name='bill[$lino][code]' value='$code'>";
131 echo "<input type='hidden' name='bill[$lino][billed]' value='$billed'>";
132 echo "</td>\n";
133 if ($codetype != 'COPAY') {
134 echo " <td class='billcell'>$strike1$code$strike2</td>\n";
135 } else {
136 echo " <td class='billcell'>&nbsp;</td>\n";
138 if ($billed) {
139 if (modifiers_are_used(true)) {
140 echo " <td class='billcell'>$strike1$modifier$strike2" .
141 "<input type='hidden' name='bill[$lino][mod]' value='$modifier'></td>\n";
143 if (fees_are_used()) {
144 echo " <td class='billcell' align='right'>" . oeFormatMoney($price) . "</td>\n";
145 if ($codetype != 'COPAY') {
146 echo " <td class='billcell' align='center'>$units</td>\n";
147 } else {
148 echo " <td class='billcell'>&nbsp;</td>\n";
150 echo " <td class='billcell' align='center'$usbillstyle>$justify</td>\n";
153 // Show provider for this line.
154 echo " <td class='billcell' align='center'>";
155 genProviderSelect('', '-- Default --', $provider_id, true);
156 echo "</td>\n";
157 if ($codetype == 'HCPCS' || $codetype == 'CPT4') {
158 echo " <td class='billcell' align='center'$usbillstyle>" .
159 htmlspecialchars($notecodes, ENT_NOQUOTES) . "</td>\n";
161 else {
162 echo " <td class='billcell' align='center'$usbillstyle></td>\n";
164 echo " <td class='billcell' align='center'$usbillstyle><input type='checkbox'" .
165 ($auth ? " checked" : "") . " disabled /></td>\n";
166 echo " <td class='billcell' align='center'><input type='checkbox'" .
167 " disabled /></td>\n";
169 else { // not billed
170 if (modifiers_are_used(true)) {
171 if ($codetype != 'COPAY' && ($code_types[$codetype]['mod'] || $modifier)) {
172 echo " <td class='billcell'><input type='text' name='bill[$lino][mod]' " .
173 "value='$modifier' " .
174 "title='" . xl("Multiple modifiers can be separated by colons or spaces, maximum of 4 (M1:M2:M3:M4)") . "' " .
175 "value='$modifier' size='" . $code_types[$codetype]['mod'] . "'></td>\n";
176 } else {
177 echo " <td class='billcell'>&nbsp;</td>\n";
180 if (fees_are_used()) {
181 if ($codetype == 'COPAY' || $code_types[$codetype]['fee'] || $fee != 0) {
182 echo " <td class='billcell' align='right'>" .
183 "<input type='text' name='bill[$lino][price]' " .
184 "value='$price' size='6'";
185 if (acl_check('acct','disc'))
186 echo " style='text-align:right'";
187 else
188 echo " style='text-align:right;background-color:transparent' readonly";
189 echo "></td>\n";
190 echo " <td class='billcell' align='center'>";
191 if ($codetype != 'COPAY') {
192 echo "<input type='text' name='bill[$lino][units]' " .
193 "value='$units' size='2' style='text-align:right'>";
194 } else {
195 echo "<input type='hidden' name='bill[$lino][units]' value='$units'>";
197 echo "</td>\n";
198 if ($code_types[$codetype]['just'] || $justify) {
199 echo " <td class='billcell' align='center'$usbillstyle ";
200 echo "title='" . xl("Select one or more diagnosis codes to justify the service") . "' >";
201 echo "<select name='bill[$lino][justify]' onchange='setJustify(this)'>";
202 echo "<option value='$justify'>$justify</option></select>";
203 echo "</td>\n";
204 $justinit .= "setJustify(f['bill[$lino][justify]']);\n";
205 } else {
206 echo " <td class='billcell'$usbillstyle>&nbsp;</td>\n";
208 } else {
209 echo " <td class='billcell'>&nbsp;</td>\n";
210 echo " <td class='billcell'>&nbsp;</td>\n";
211 echo " <td class='billcell'$usbillstyle>&nbsp;</td>\n"; // justify
215 // Provider drop-list for this line.
216 echo " <td class='billcell' align='center'>";
217 genProviderSelect("bill[$lino][provid]", '-- Default --', $provider_id);
218 echo "</td>\n";
219 if ($codetype == 'HCPCS' || $codetype == 'CPT4') {
220 echo " <td class='billcell' align='center'$usbillstyle><input type='text' name='bill[$lino][notecodes]' " .
221 "value='" . htmlspecialchars($notecodes, ENT_QUOTES) . "' maxlength='10' size='8' /></td>\n";
223 else {
224 echo " <td class='billcell' align='center'$usbillstyle></td>\n";
226 echo " <td class='billcell' align='center'$usbillstyle><input type='checkbox' name='bill[$lino][auth]' " .
227 "value='1'" . ($auth ? " checked" : "") . " /></td>\n";
228 echo " <td class='billcell' align='center'><input type='checkbox' name='bill[$lino][del]' " .
229 "value='1'" . ($del ? " checked" : "") . " /></td>\n";
232 echo " <td class='billcell'>$strike1" . ucfirst(strtolower($code_text)) . "$strike2</td>\n";
233 echo " </tr>\n";
235 // If NDC info exists or may be required, add a line for it.
236 if ($codetype == 'HCPCS' && $ndc_applies && !$billed) {
237 $ndcnum = ''; $ndcuom = ''; $ndcqty = '';
238 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndc_info, $tmp)) {
239 $ndcnum = $tmp[1]; $ndcuom = $tmp[2]; $ndcqty = $tmp[3];
241 echo " <tr>\n";
242 echo " <td class='billcell' colspan='2'>&nbsp;</td>\n";
243 echo " <td class='billcell' colspan='6'>&nbsp;NDC:&nbsp;";
244 echo "<input type='text' name='bill[$lino][ndcnum]' value='$ndcnum' " .
245 "size='11' style='background-color:transparent'>";
246 echo " &nbsp;Qty:&nbsp;";
247 echo "<input type='text' name='bill[$lino][ndcqty]' value='$ndcqty' " .
248 "size='3' style='background-color:transparent;text-align:right'>";
249 echo " ";
250 echo "<select name='bill[$lino][ndcuom]' style='background-color:transparent'>";
251 foreach ($ndc_uom_choices as $key => $value) {
252 echo "<option value='$key'";
253 if ($key == $ndcuom) echo " selected";
254 echo ">$value</option>";
256 echo "</select>";
257 echo "</td>\n";
258 echo " </tr>\n";
260 else if ($ndc_info) {
261 echo " <tr>\n";
262 echo " <td class='billcell' colspan='2'>&nbsp;</td>\n";
263 echo " <td class='billcell' colspan='6'>&nbsp;NDC Data: $ndc_info</td>\n";
264 echo " </tr>\n";
267 // For IPPF. Track contraceptive services.
268 if (!$del) $contraception |= contraceptionClass($codetype, $code);
271 // This writes a product (drug_sales) line item to the output page.
273 function echoProdLine($lino, $drug_id, $del = FALSE, $units = NULL,
274 $fee = NULL, $sale_id = 0, $billed = FALSE)
276 global $code_types, $ndc_applies, $pid, $usbillstyle;
278 $drow = sqlQuery("SELECT name FROM drugs WHERE drug_id = '$drug_id'");
279 $code_text = $drow['name'];
281 $fee = sprintf('%01.2f', $fee);
282 if (empty($units)) $units = 1;
283 $units = max(1, intval($units));
284 // We put unit price on the screen, not the total line item fee.
285 $price = $fee / $units;
286 $strike1 = ($sale_id && $del) ? "<strike>" : "";
287 $strike2 = ($sale_id && $del) ? "</strike>" : "";
288 echo " <tr>\n";
289 echo " <td class='billcell'>{$strike1}Product$strike2";
290 echo "<input type='hidden' name='prod[$lino][sale_id]' value='$sale_id'>";
291 echo "<input type='hidden' name='prod[$lino][drug_id]' value='$drug_id'>";
292 echo "<input type='hidden' name='prod[$lino][billed]' value='$billed'>";
293 echo "</td>\n";
294 echo " <td class='billcell'>$strike1$drug_id$strike2</td>\n";
295 if (modifiers_are_used(true)) {
296 echo " <td class='billcell'>&nbsp;</td>\n";
298 if ($billed) {
299 if (fees_are_used()) {
300 echo " <td class='billcell' align='right'>" . oeFormatMoney($price) . "</td>\n";
301 echo " <td class='billcell' align='center'>$units</td>\n";
302 echo " <td class='billcell' align='center'$usbillstyle>&nbsp;</td>\n"; // justify
304 echo " <td class='billcell' align='center'>&nbsp;</td>\n"; // provider
305 echo " <td class='billcell' align='center'$usbillstyle>&nbsp;</td>\n"; // auth
306 echo " <td class='billcell' align='center'><input type='checkbox'" . // del
307 " disabled /></td>\n";
308 } else {
309 if (fees_are_used()) {
310 echo " <td class='billcell' align='right'>" .
311 "<input type='text' name='prod[$lino][price]' " .
312 "value='$price' size='6'";
313 if (acl_check('acct','disc'))
314 echo " style='text-align:right'";
315 else
316 echo " style='text-align:right;background-color:transparent' readonly";
317 echo "></td>\n";
318 echo " <td class='billcell' align='center'>";
319 echo "<input type='text' name='prod[$lino][units]' " .
320 "value='$units' size='2' style='text-align:right'>";
321 echo "</td>\n";
322 echo " <td class='billcell'$usbillstyle>&nbsp;</td>\n"; // justify
324 echo " <td class='billcell' align='center'>&nbsp;</td>\n"; // provider
325 echo " <td class='billcell' align='center'$usbillstyle>&nbsp;</td>\n"; // auth
326 echo " <td class='billcell' align='center'><input type='checkbox' name='prod[$lino][del]' " .
327 "value='1'" . ($del ? " checked" : "") . " /></td>\n";
330 echo " <td class='billcell'>$strike1" . ucfirst(strtolower($code_text)) . "$strike2</td>\n";
331 echo " </tr>\n";
334 // Build a drop-down list of providers. This includes users who
335 // have the word "provider" anywhere in their "additional info"
336 // field, so that we can define providers (for billing purposes)
337 // who do not appear in the calendar.
339 function genProviderSelect($selname, $toptext, $default=0, $disabled=false) {
340 $query = "SELECT id, lname, fname FROM users WHERE " .
341 "( authorized = 1 OR info LIKE '%provider%' ) AND username != '' " .
342 "AND active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) " .
343 "ORDER BY lname, fname";
344 $res = sqlStatement($query);
345 echo " <select name='$selname'";
346 if ($disabled) echo " disabled";
347 echo ">\n";
348 echo " <option value=''>$toptext\n";
349 while ($row = sqlFetchArray($res)) {
350 $provid = $row['id'];
351 echo " <option value='$provid'";
352 if ($provid == $default) echo " selected";
353 echo ">" . $row['lname'] . ", " . $row['fname'] . "\n";
355 echo " </select>\n";
358 // This is just for IPPF, to indicate if the visit includes contraceptive services.
359 $contraception = 0;
361 // Possible units of measure for NDC drug quantities.
363 $ndc_uom_choices = array(
364 'ML' => 'ML',
365 'GR' => 'Grams',
366 'ME' => 'Milligrams',
367 'F2' => 'I.U.',
368 'UN' => 'Units'
371 // $FEE_SHEET_COLUMNS should be defined in codes.php.
372 if (empty($FEE_SHEET_COLUMNS)) $FEE_SHEET_COLUMNS = 2;
374 $returnurl = $GLOBALS['concurrent_layout'] ? 'encounter_top.php' : 'patient_encounter.php';
376 // Update price level in patient demographics.
377 if (!empty($_POST['pricelevel'])) {
378 sqlStatement("UPDATE patient_data SET pricelevel = '" .
379 $_POST['pricelevel'] . "' WHERE pid = '$pid'");
382 // Get some info about this visit.
383 $visit_row = sqlQuery("SELECT fe.date, opc.pc_catname " .
384 "FROM form_encounter AS fe " .
385 "LEFT JOIN openemr_postcalendar_categories AS opc ON opc.pc_catid = fe.pc_catid " .
386 "WHERE fe.pid = '$pid' AND fe.encounter = '$encounter' LIMIT 1");
387 $visit_date = substr($visit_row['date'], 0, 10);
389 // If Save was clicked, save the new and modified billing lines;
390 // then if no error, redirect to $returnurl.
392 if ($_POST['bn_save']) {
393 $main_provid = 0 + $_POST['ProviderID'];
394 $main_supid = 0 + $_POST['SupervisorID'];
395 if ($main_supid == $main_provid) $main_supid = 0;
396 $default_warehouse = $_POST['default_warehouse'];
398 $bill = $_POST['bill'];
399 for ($lino = 1; $bill["$lino"]['code_type']; ++$lino) {
400 $iter = $bill["$lino"];
401 $code_type = $iter['code_type'];
402 $code = $iter['code'];
403 $del = $iter['del'];
405 // Get some information about this service code.
406 $codesrow = sqlQuery("SELECT code_text FROM codes WHERE " .
407 "code_type = '" . $code_types[$code_type]['id'] .
408 "' AND code = '$code' LIMIT 1");
410 // Skip disabled (billed) line items.
411 if ($iter['billed']) continue;
413 $id = $iter['id'];
414 $modifier = trim($iter['mod']);
415 $units = max(1, intval(trim($iter['units'])));
416 $fee = sprintf('%01.2f',(0 + trim($iter['price'])) * $units);
417 if ($code_type == 'COPAY') {
418 if ($fee > 0) $fee = 0 - $fee;
419 $code = sprintf('%01.2f', 0 - $fee);
421 $justify = trim($iter['justify']);
422 $notecodes = trim(strip_escape_custom($iter['notecodes']));
423 if ($justify) $justify = str_replace(',', ':', $justify) . ':';
424 // $auth = $iter['auth'] ? "1" : "0";
425 $auth = "1";
426 $provid = 0 + $iter['provid'];
428 $ndc_info = '';
429 if ($iter['ndcnum']) {
430 $ndc_info = 'N4' . trim($iter['ndcnum']) . ' ' . $iter['ndcuom'] .
431 trim($iter['ndcqty']);
434 // If the item is already in the database...
435 if ($id) {
436 if ($del) {
437 deleteBilling($id);
439 else {
440 // authorizeBilling($id, $auth);
441 sqlQuery("UPDATE billing SET code = '$code', " .
442 "units = '$units', fee = '$fee', modifier = '$modifier', " .
443 "authorized = $auth, provider_id = '$provid', " .
444 "ndc_info = '$ndc_info', justify = '$justify', notecodes = '" .
445 add_escape_custom($notecodes) . "' WHERE " .
446 "id = '$id' AND billed = 0 AND activity = 1");
450 // Otherwise it's a new item...
451 else if (! $del) {
452 $code_text = addslashes($codesrow['code_text']);
453 addBilling($encounter, $code_type, $code, $code_text, $pid, $auth,
454 $provid, $modifier, $units, $fee, $ndc_info, $justify, 0, $notecodes);
456 } // end for
458 // Doing similarly to the above but for products.
459 $prod = $_POST['prod'];
460 for ($lino = 1; $prod["$lino"]['drug_id']; ++$lino) {
461 $iter = $prod["$lino"];
463 if (!empty($iter['billed'])) continue;
465 $drug_id = $iter['drug_id'];
466 $sale_id = $iter['sale_id']; // present only if already saved
467 $units = max(1, intval(trim($iter['units'])));
468 $fee = sprintf('%01.2f',(0 + trim($iter['price'])) * $units);
469 $del = $iter['del'];
471 // If the item is already in the database...
472 if ($sale_id) {
473 if ($del) {
474 // Zero out this sale and reverse its inventory update. We bring in
475 // drug_sales twice so that the original quantity can be referenced
476 // unambiguously.
477 sqlStatement("UPDATE drug_sales AS dsr, drug_sales AS ds, " .
478 "drug_inventory AS di " .
479 "SET di.on_hand = di.on_hand + dsr.quantity, " .
480 "ds.quantity = 0, ds.fee = 0 WHERE " .
481 "dsr.sale_id = '$sale_id' AND ds.sale_id = dsr.sale_id AND " .
482 "di.inventory_id = ds.inventory_id");
483 // And delete the sale for good measure.
484 sqlStatement("DELETE FROM drug_sales WHERE sale_id = '$sale_id'");
486 else {
487 // Modify the sale and adjust inventory accordingly.
488 $query = "UPDATE drug_sales AS dsr, drug_sales AS ds, " .
489 "drug_inventory AS di " .
490 "SET di.on_hand = di.on_hand + dsr.quantity - $units, " .
491 "ds.quantity = '$units', ds.fee = '$fee', " .
492 "ds.sale_date = '$visit_date' WHERE " .
493 "dsr.sale_id = '$sale_id' AND ds.sale_id = dsr.sale_id AND " .
494 "di.inventory_id = ds.inventory_id";
495 sqlStatement($query);
499 // Otherwise it's a new item...
500 else if (! $del) {
501 $sale_id = sellDrug($drug_id, $units, $fee, $pid, $encounter, 0,
502 $visit_date, '', $default_warehouse);
503 if (!$sale_id) die("Insufficient inventory for product ID \"$drug_id\".");
505 } // end for
507 // Set the main/default service provider in the new-encounter form.
508 /*******************************************************************
509 sqlStatement("UPDATE forms, users SET forms.user = users.username WHERE " .
510 "forms.pid = '$pid' AND forms.encounter = '$encounter' AND " .
511 "forms.formdir = 'newpatient' AND users.id = '$provid'");
512 *******************************************************************/
513 sqlStatement("UPDATE form_encounter SET provider_id = '$main_provid', " .
514 "supervisor_id = '$main_supid' WHERE " .
515 "pid = '$pid' AND encounter = '$encounter'");
517 // More IPPF stuff.
518 if (!empty($_POST['contrastart'])) {
519 $contrastart = $_POST['contrastart'];
520 sqlStatement("UPDATE patient_data SET contrastart = '" .
521 $contrastart . "' WHERE pid = '$pid'");
524 // Note: Taxes are computed at checkout time (in pos_checkout.php which
525 // also posts to SL). Currently taxes with insurance claims make no sense,
526 // so for now we'll ignore tax computation in the insurance billing logic.
528 formHeader("Redirecting....");
529 formJump();
530 formFooter();
531 exit;
534 $billresult = getBillingByEncounter($pid, $encounter, "*");
536 <html>
537 <head>
538 <?php html_header_show(); ?>
539 <link rel="stylesheet" href="<?php echo $css_header;?>" type="text/css">
540 <style>
541 .billcell { font-family: sans-serif; font-size: 10pt }
542 </style>
543 <script language="JavaScript">
545 var diags = new Array();
547 <?php
548 if ($billresult) {
549 foreach ($billresult as $iter) {
550 genDiagJS($iter["code_type"], trim($iter["code"]));
553 if ($_POST['bill']) {
554 foreach ($_POST['bill'] as $iter) {
555 if ($iter["del"]) continue; // skip if Delete was checked
556 if ($iter["id"]) continue; // skip if it came from the database
557 genDiagJS($iter["code_type"], $iter["code"]);
560 if ($_POST['newcodes']) {
561 $arrcodes = explode('~', $_POST['newcodes']);
562 foreach ($arrcodes as $codestring) {
563 if ($codestring === '') continue;
564 $arrcode = explode('|', $codestring);
565 list($code, $modifier) = explode(":", $arrcode[1]);
566 genDiagJS($arrcode[0], $code);
571 // This is invoked by <select onchange> for the various dropdowns,
572 // including search results.
573 function codeselect(selobj) {
574 var i = selobj.selectedIndex;
575 if (i > 0) {
576 top.restoreSession();
577 var f = document.forms[0];
578 f.newcodes.value = selobj.options[i].value;
579 f.submit();
583 function copayselect() {
584 top.restoreSession();
585 var f = document.forms[0];
586 f.newcodes.value = 'COPAY||';
587 f.submit();
590 function validate(f) {
591 for (var lino = 1; f['bill['+lino+'][code_type]']; ++lino) {
592 var pfx = 'bill['+lino+']';
593 if (f[pfx+'[ndcnum]'] && f[pfx+'[ndcnum]'].value) {
594 // Check NDC number format.
595 var ndcok = true;
596 var ndc = f[pfx+'[ndcnum]'].value;
597 var a = ndc.split('-');
598 if (a.length != 3) {
599 ndcok = false;
601 else if (a[0].length < 1 || a[1].length < 1 || a[2].length < 1 ||
602 a[0].length > 5 || a[1].length > 4 || a[2].length > 2) {
603 ndcok = false;
605 else {
606 for (var i = 0; i < 3; ++i) {
607 for (var j = 0; j < a[i].length; ++j) {
608 var c = a[i].charAt(j);
609 if (c < '0' || c > '9') ndcok = false;
613 if (!ndcok) {
614 alert('<?php xl('Format incorrect for NDC','e') ?> "' + ndc +
615 '", <?php xl('should be like nnnnn-nnnn-nn','e') ?>');
616 if (f[pfx+'[ndcnum]'].focus) f[pfx+'[ndcnum]'].focus();
617 return false;
619 // Check for valid quantity.
620 var qty = f[pfx+'[ndcqty]'].value - 0;
621 if (isNaN(qty) || qty <= 0) {
622 alert('<?php xl('Quantity for NDC','e') ?> "' + ndc +
623 '" <?php xl('is not valid (decimal fractions are OK).','e') ?>');
624 if (f[pfx+'[ndcqty]'].focus) f[pfx+'[ndcqty]'].focus();
625 return false;
629 top.restoreSession();
630 return true;
633 // When a justify selection is made, apply it to the current list for
634 // this procedure and then rebuild its selection list.
636 function setJustify(seljust) {
637 var theopts = seljust.options;
638 var jdisplay = theopts[0].text;
639 // Compute revised justification string. Note this does nothing if
640 // the first entry is still selected, which is handy at startup.
641 if (seljust.selectedIndex > 0) {
642 var newdiag = seljust.value;
643 if (newdiag.length == 0) {
644 jdisplay = '';
646 else {
647 if (jdisplay.length) jdisplay += ',';
648 jdisplay += newdiag;
651 // Rebuild selection list.
652 var jhaystack = ',' + jdisplay + ',';
653 var j = 0;
654 theopts.length = 0;
655 theopts[j++] = new Option(jdisplay,jdisplay,true,true);
656 for (var i = 0; i < diags.length; ++i) {
657 if (jhaystack.indexOf(',' + diags[i] + ',') < 0) {
658 theopts[j++] = new Option(diags[i],diags[i],false,false);
661 theopts[j++] = new Option('Clear','',false,false);
664 </script>
665 </head>
667 <body class="body_top">
668 <form method="post" action="<?php echo $rootdir; ?>/forms/fee_sheet/new.php"
669 onsubmit="return validate(this)">
670 <span class="title"><?php xl('Fee Sheet','e'); ?></span><br>
671 <input type='hidden' name='newcodes' value=''>
673 <center>
675 <?php
676 $isBilled = isEncounterBilled($pid, $encounter);
677 if ($isBilled) {
678 echo "<p><font color='green'>This encounter has been billed. If you " .
679 "need to change it, it must be re-opened.</font></p>\n";
681 else { // the encounter is not yet billed
684 <table width='95%'>
685 <?php
686 $i = 0;
687 $last_category = '';
689 // Create drop-lists based on the fee_sheet_options table.
690 $res = sqlStatement("SELECT * FROM fee_sheet_options " .
691 "ORDER BY fs_category, fs_option");
692 while ($row = sqlFetchArray($res)) {
693 $fs_category = $row['fs_category'];
694 $fs_option = $row['fs_option'];
695 $fs_codes = $row['fs_codes'];
696 if($fs_category !== $last_category) {
697 endFSCategory();
698 $last_category = $fs_category;
699 ++$i;
700 echo ($i <= 1) ? " <tr>\n" : "";
701 echo " <td width='50%' align='center' nowrap>\n";
702 echo " <select style='width:96%' onchange='codeselect(this)'>\n";
703 echo " <option value=''> " . substr($fs_category, 1) . "</option>\n";
705 echo " <option value='$fs_codes'>" . substr($fs_option, 1) . "</option>\n";
707 endFSCategory();
709 // Create drop-lists based on categories defined within the codes.
710 $pres = sqlStatement("SELECT option_id, title FROM list_options " .
711 "WHERE list_id = 'superbill' ORDER BY seq");
712 while ($prow = sqlFetchArray($pres)) {
713 global $code_types;
714 ++$i;
715 echo ($i <= 1) ? " <tr>\n" : "";
716 echo " <td width='50%' align='center' nowrap>\n";
717 echo " <select style='width:96%' onchange='codeselect(this)'>\n";
718 echo " <option value=''> " . $prow['title'] . "\n";
719 $res = sqlStatement("SELECT code_type, code, code_text,modifier FROM codes " .
720 "WHERE superbill = '" . $prow['option_id'] . "' AND active = 1 " .
721 "ORDER BY code_text");
722 while ($row = sqlFetchArray($res)) {
723 $ctkey = alphaCodeType($row['code_type']);
724 if ($code_types[$ctkey]['nofs']) continue;
725 echo " <option value='$ctkey|" .
726 $row['code'] . ':'. $row['modifier']. "|'>" . $row['code_text'] . "</option>\n";
728 echo " </select>\n";
729 echo " </td>\n";
730 if ($i >= $FEE_SHEET_COLUMNS) {
731 echo " </tr>\n";
732 $i = 0;
736 // Create one more drop-list, for Products.
737 if ($GLOBALS['sell_non_drug_products']) {
738 ++$i;
739 echo ($i <= 1) ? " <tr>\n" : "";
740 echo " <td width='50%' align='center' nowrap>\n";
741 echo " <select name='Products' style='width:96%' onchange='codeselect(this)'>\n";
742 echo " <option value=''> " . xl('Products') . "\n";
743 $tres = sqlStatement("SELECT dt.drug_id, dt.selector, d.name " .
744 "FROM drug_templates AS dt, drugs AS d WHERE " .
745 "d.drug_id = dt.drug_id AND d.active = 1 " .
746 "ORDER BY d.name, dt.selector, dt.drug_id");
747 while ($trow = sqlFetchArray($tres)) {
748 echo " <option value='PROD|" . $trow['drug_id'] . '|' . $trow['selector'] . "'>" .
749 $trow['drug_id'] . ':' . $trow['selector'];
750 if ($trow['name'] !== $trow['selector']) echo ' ' . $trow['name'];
751 echo "</option>\n";
753 echo " </select>\n";
754 echo " </td>\n";
755 if ($i >= $FEE_SHEET_COLUMNS) {
756 echo " </tr>\n";
757 $i = 0;
761 $search_type = $default_search_type;
762 if ($_POST['search_type']) $search_type = $_POST['search_type'];
764 $ndc_applies = true; // Assume all payers require NDC info.
766 echo $i ? " <td></td>\n </tr>\n" : "";
767 echo " <tr>\n";
768 echo " <td colspan='$FEE_SHEET_COLUMNS' align='center' nowrap>\n";
770 // If Search was clicked, do it and write the list of results here.
771 // There's no limit on the number of results!
773 $numrows = 0;
774 if ($_POST['bn_search'] && $_POST['search_term']) {
775 $query = "SELECT code, modifier, code_text FROM codes WHERE " .
776 "(code_text LIKE '%" . $_POST['search_term'] . "%' OR " .
777 "code LIKE '%" . $_POST['search_term'] . "%') AND " .
778 "code_type = '" . $code_types[$search_type]['id'] . "' " .
779 "AND active = 1 ORDER BY code";
780 $res = sqlStatement($query);
781 $numrows = mysql_num_rows($res); // FIXME - not portable!
784 echo " <select name='Search Results' style='width:98%' " .
785 "onchange='codeselect(this)'";
786 if (! $numrows) echo ' disabled';
787 echo ">\n";
788 echo " <option value=''> Search Results ($numrows items)\n";
790 if ($numrows) {
791 while ($row = sqlFetchArray($res)) {
792 $code = $row['code'];
793 if ($row['modifier']) $code .= ":" . $row['modifier'];
794 echo " <option value='$search_type|$code|'>$code " .
795 ucfirst(strtolower($row['code_text'])) . "</option>\n";
799 echo " </select>\n";
800 echo " </td>\n";
801 echo " </tr>\n";
804 </table>
806 <p style='margin-top:8px;margin-bottom:8px'>
807 <table>
808 <tr>
809 <td>
810 <input type='button' value='<?php xl('Add Copay','e');?>'
811 onclick="copayselect()" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
812 </td>
813 <td>
814 <?php xl('Search','e'); ?>&nbsp;
815 <?php
816 foreach ($code_types as $key => $value) {
817 if (!empty($value['nofs'])) continue;
818 echo " <input type='radio' name='search_type' value='$key'";
819 if ($key == $default_search_type) echo " checked";
820 echo " />$key&nbsp;\n";
823 <?php xl('for','e'); ?>&nbsp;
824 </td>
825 <td>
826 <input type='text' name='search_term' value=''> &nbsp;
827 </td>
828 <td>
829 <input type='submit' name='bn_search' value='<?php xl('Search','e');?>'>
830 </td>
831 </tr>
832 </table>
833 </p>
834 <p style='margin-top:16px;margin-bottom:8px'>
836 <?php } // end encounter not billed ?>
838 <table cellspacing='5'>
839 <tr>
840 <td class='billcell'><b><?php xl('Type','e');?></b></td>
841 <td class='billcell'><b><?php xl('Code','e');?></b></td>
842 <?php if (modifiers_are_used(true)) { ?>
843 <td class='billcell'><b><?php xl('Modifiers','e');?></b></td>
844 <?php } ?>
845 <?php if (fees_are_used()) { ?>
846 <td class='billcell' align='right'><b><?php xl('Price','e');?></b>&nbsp;</td>
847 <td class='billcell' align='center'><b><?php xl('Units','e');?></b></td>
848 <td class='billcell' align='center'<?php echo $usbillstyle; ?>><b><?php xl('Justify','e');?></b></td>
849 <?php } ?>
850 <td class='billcell' align='center'><b><?php xl('Provider','e');?></b></td>
851 <td class='billcell' align='center'<?php echo $usbillstyle; ?>><b><?php xl('Note Codes','e');?></b></td>
852 <td class='billcell' align='center'<?php echo $usbillstyle; ?>><b><?php xl('Auth','e');?></b></td>
853 <td class='billcell' align='center'><b><?php xl('Delete','e');?></b></td>
854 <td class='billcell'><b><?php xl('Description','e');?></b></td>
855 </tr>
857 <?php
858 $justinit = "var f = document.forms[0];\n";
860 // $encounter_provid = -1;
862 // Generate lines for items already in the billing table for this encounter,
863 // and also set the rendering provider if we come across one.
865 $bill_lino = 0;
866 if ($billresult) {
867 foreach ($billresult as $iter) {
868 ++$bill_lino;
869 $bline = $_POST['bill']["$bill_lino"];
870 $del = $bline['del']; // preserve Delete if checked
872 $modifier = trim($iter["modifier"]);
873 $units = $iter["units"];
874 $fee = $iter["fee"];
875 $authorized = $iter["authorized"];
876 $ndc_info = $iter["ndc_info"];
877 $justify = trim($iter['justify']);
878 $notecodes = trim($iter['notecodes']);
879 if ($justify) $justify = substr(str_replace(':', ',', $justify), 0, strlen($justify) - 1);
880 $provider_id = $iter['provider_id'];
882 // Also preserve other items from the form, if present.
883 if ($bline['id'] && !$iter["billed"]) {
884 $modifier = trim($bline['mod']);
885 $units = max(1, intval(trim($bline['units'])));
886 $fee = sprintf('%01.2f',(0 + trim($bline['price'])) * $units);
887 $authorized = $bline['auth'];
888 $ndc_info = '';
889 if ($bline['ndcnum']) {
890 $ndc_info = 'N4' . trim($bline['ndcnum']) . ' ' . $bline['ndcuom'] .
891 trim($bline['ndcqty']);
893 $justify = $bline['justify'];
894 $notecodes = strip_escape_custom(trim($bline['notecodes']));
895 $provider_id = 0 + $bline['provid'];
898 // list($code, $modifier) = explode("-", $iter["code"]);
899 echoLine($bill_lino, $iter["code_type"], trim($iter["code"]),
900 $modifier, $ndc_info, $authorized,
901 $del, $units, $fee, $iter["id"], $iter["billed"],
902 $iter["code_text"], $justify, $provider_id, $notecodes);
906 // Echo new billing items from this form here, but omit any line
907 // whose Delete checkbox is checked.
909 if ($_POST['bill']) {
910 foreach ($_POST['bill'] as $key => $iter) {
911 if ($iter["id"]) continue; // skip if it came from the database
912 if ($iter["del"]) continue; // skip if Delete was checked
913 $ndc_info = '';
914 if ($iter['ndcnum']) {
915 $ndc_info = 'N4' . trim($iter['ndcnum']) . ' ' . $iter['ndcuom'] .
916 trim($iter['ndcqty']);
918 // $fee = 0 + trim($iter['fee']);
919 $units = max(1, intval(trim($iter['units'])));
920 $fee = sprintf('%01.2f',(0 + trim($iter['price'])) * $units);
921 if ($iter['code_type'] == 'COPAY' && $fee > 0) $fee = 0 - $fee;
922 echoLine(++$bill_lino, $iter["code_type"], $iter["code"], trim($iter["mod"]),
923 $ndc_info, $iter["auth"], $iter["del"], $units,
924 $fee, NULL, FALSE, NULL, $iter["justify"], 0 + $iter['provid'],
925 strip_escape_custom($iter['notecodes']));
929 // Generate lines for items already in the drug_sales table for this encounter.
931 $query = "SELECT * FROM drug_sales WHERE " .
932 "pid = '$pid' AND encounter = '$encounter' " .
933 "ORDER BY sale_id";
934 $sres = sqlStatement($query);
935 $prod_lino = 0;
936 while ($srow = sqlFetchArray($sres)) {
937 ++$prod_lino;
938 $pline = $_POST['prod']["$prod_lino"];
939 $del = $pline['del']; // preserve Delete if checked
940 $sale_id = $srow['sale_id'];
941 $drug_id = $srow['drug_id'];
942 $units = $srow['quantity'];
943 $fee = $srow['fee'];
944 $billed = $srow['billed'];
945 // Also preserve other items from the form, if present and unbilled.
946 if ($pline['sale_id'] && !$srow['billed']) {
947 // $units = trim($pline['units']);
948 // $fee = trim($pline['fee']);
949 $units = max(1, intval(trim($pline['units'])));
950 $fee = sprintf('%01.2f',(0 + trim($pline['price'])) * $units);
952 echoProdLine($prod_lino, $drug_id, $del, $units, $fee, $sale_id, $billed);
955 // Echo new product items from this form here, but omit any line
956 // whose Delete checkbox is checked.
958 if ($_POST['prod']) {
959 foreach ($_POST['prod'] as $key => $iter) {
960 if ($iter["sale_id"]) continue; // skip if it came from the database
961 if ($iter["del"]) continue; // skip if Delete was checked
962 // $fee = 0 + trim($iter['fee']);
963 $units = max(1, intval(trim($iter['units'])));
964 $fee = sprintf('%01.2f',(0 + trim($iter['price'])) * $units);
965 echoProdLine(++$prod_lino, $iter['drug_id'], FALSE, $units, $fee);
969 // If new billing code(s) were <select>ed, add their line(s) here.
971 if ($_POST['newcodes']) {
972 $arrcodes = explode('~', $_POST['newcodes']);
973 foreach ($arrcodes as $codestring) {
974 if ($codestring === '') continue;
975 $arrcode = explode('|', $codestring);
976 $newtype = $arrcode[0];
977 $newcode = $arrcode[1];
978 $newsel = $arrcode[2];
979 if ($newtype == 'COPAY') {
980 $tmp = sqlQuery("SELECT copay FROM insurance_data WHERE pid = '$pid' " .
981 "AND type = 'primary' ORDER BY date DESC LIMIT 1");
982 $code = sprintf('%01.2f', 0 + $tmp['copay']);
983 echoLine(++$bill_lino, $newtype, $code, '', '', '1', '0', '1',
984 sprintf('%01.2f', 0 - $code));
986 else if ($newtype == 'PROD') {
987 $result = sqlQuery("SELECT * FROM drug_templates WHERE " .
988 "drug_id = '$newcode' AND selector = '$newsel'");
989 $units = max(1, intval($result['quantity']));
990 $prrow = sqlQuery("SELECT prices.pr_price " .
991 "FROM patient_data, prices WHERE " .
992 "patient_data.pid = '$pid' AND " .
993 "prices.pr_id = '$newcode' AND " .
994 "prices.pr_selector = '$newsel' AND " .
995 "prices.pr_level = patient_data.pricelevel " .
996 "LIMIT 1");
997 $fee = empty($prrow) ? 0 : $prrow['pr_price'];
998 echoProdLine(++$prod_lino, $newcode, FALSE, $units, $fee);
1000 else {
1001 list($code, $modifier) = explode(":", $newcode);
1002 $ndc_info = '';
1003 // If HCPCS, find last NDC string used for this code.
1004 if ($newtype == 'HCPCS' && $ndc_applies) {
1005 $tmp = sqlQuery("SELECT ndc_info FROM billing WHERE " .
1006 "code_type = '$newtype' AND code = '$code' AND ndc_info LIKE 'N4%' " .
1007 "ORDER BY date DESC LIMIT 1");
1008 if (!empty($tmp)) $ndc_info = $tmp['ndc_info'];
1010 echoLine(++$bill_lino, $newtype, $code, trim($modifier), $ndc_info);
1015 $tmp = sqlQuery("SELECT provider_id, supervisor_id FROM form_encounter " .
1016 "WHERE pid = '$pid' AND encounter = '$encounter' " .
1017 "ORDER BY id DESC LIMIT 1");
1018 $encounter_provid = 0 + $tmp['provider_id'];
1019 $encounter_supid = 0 + $tmp['supervisor_id'];
1021 </table>
1022 </p>
1024 <br />
1025 &nbsp;
1027 <?php
1028 // Choose rendering and supervising providers.
1029 echo "<span class='billcell'><b>\n";
1030 echo xl('Providers') . ": &nbsp;";
1032 echo "&nbsp;&nbsp;" . xl('Rendering') . "\n";
1033 genProviderSelect('ProviderID', '-- Please Select --', $encounter_provid, $isBilled);
1035 if (!$GLOBALS['ippf_specific']) {
1036 echo "&nbsp;&nbsp;" . xl('Supervising') . "\n";
1037 genProviderSelect('SupervisorID', '-- N/A --', $encounter_supid, $isBilled);
1040 echo "</b></span>\n";
1044 &nbsp;
1046 <?php
1047 // If applicable, ask for the contraceptive services start date.
1048 $trow = sqlQuery("SELECT count(*) AS count FROM layout_options WHERE " .
1049 "form_id = 'DEM' AND field_id = 'contrastart' AND uor > 0");
1050 if ($trow['count'] && $contraception && !$isBilled) {
1051 $date1 = substr($visit_row['date'], 0, 10);
1052 // If admission or surgical, then force contrastart.
1053 if ($contraception > 1 ||
1054 strpos(strtolower($visit_row['pc_catname']), 'admission') !== false)
1056 echo " <input type='hidden' name='contrastart' value='$date1' />\n";
1058 else {
1059 // echo "<!-- contraception = $contraception -->\n"; // debugging
1060 $trow = sqlQuery("SELECT contrastart " .
1061 "FROM patient_data WHERE " .
1062 "pid = '$pid' LIMIT 1");
1063 if (empty($trow['contrastart']) || substr($trow['contrastart'], 0, 4) == '0000') {
1064 $date0 = date('Y-m-d', strtotime($date1) - (60 * 60 * 24));
1065 echo " <select name='contrastart'>\n";
1066 echo " <option value='$date1'>" . xl('This visit begins new contraceptive use') . "</option>\n";
1067 echo " <option value='$date0'>" . xl('Contraceptive services previously started') . "</option>\n";
1068 echo " <option value=''>" . xl('None of the above') . "</option>\n";
1069 echo " </select>\n";
1070 echo "&nbsp; &nbsp; &nbsp;\n";
1075 // If there is a choice of warehouses, allow override of user default.
1076 if ($prod_lino > 0) { // if any products are in this form
1077 $trow = sqlQuery("SELECT count(*) AS count FROM list_options WHERE list_id = 'warehouse'");
1078 if ($trow['count'] > 1) {
1079 $trow = sqlQuery("SELECT default_warehouse FROM users WHERE username = '" .
1080 $_SESSION['authUser'] . "'");
1081 echo " <span class='billcell'><b>" . xl('Warehouse') . ":</b></span>\n";
1082 echo generate_select_list('default_warehouse', 'warehouse',
1083 $trow['default_warehouse'], '');
1084 echo "&nbsp; &nbsp; &nbsp;\n";
1088 // Allow the patient price level to be fixed here.
1089 $plres = sqlStatement("SELECT option_id, title FROM list_options " .
1090 "WHERE list_id = 'pricelevel' ORDER BY seq");
1091 if (true) {
1092 $trow = sqlQuery("SELECT pricelevel FROM patient_data WHERE " .
1093 "pid = '$pid' LIMIT 1");
1094 $pricelevel = $trow['pricelevel'];
1095 echo " <span class='billcell'><b>" . xl('Price Level') . ":</b></span>\n";
1096 echo " <select name='pricelevel'";
1097 if ($isBilled) echo " disabled";
1098 echo ">\n";
1099 while ($plrow = sqlFetchArray($plres)) {
1100 $key = $plrow['option_id'];
1101 $val = $plrow['title'];
1102 echo " <option value='$key'";
1103 if ($key == $pricelevel) echo ' selected';
1104 echo ">$val</option>\n";
1106 echo " </select>\n";
1110 &nbsp; &nbsp; &nbsp;
1112 <?php if (!$isBilled) { ?>
1113 <input type='submit' name='bn_save' value='<?php xl('Save','e');?>' />
1114 &nbsp;
1115 <input type='submit' name='bn_refresh' value='<?php xl('Refresh','e');?>'>
1116 &nbsp;
1117 <?php } ?>
1119 <input type='button' value='<?php xl('Cancel','e');?>'
1120 onclick="top.restoreSession();location='<?php echo "$rootdir/patient_file/encounter/$returnurl" ?>'" />
1122 <?php if ($code_types['UCSMC']) { ?>
1123 <p style='font-family:sans-serif;font-size:8pt;color:#666666;'>
1124 &nbsp;<br>
1125 <?php xl('UCSMC codes provided by the University of Calgary Sports Medicine Centre','e');?>
1126 </p>
1127 <?php } ?>
1129 </center>
1131 </form>
1133 <?php
1134 // TBD: If $alertmsg, display it with a JavaScript alert().
1137 <script language='JavaScript'>
1138 <?php echo $justinit; ?>
1139 </script>
1141 </body>
1142 </html>