added new datepicker to amendments gui
[openemr.git] / interface / patient_file / pos_checkout.php
blobee01992c76ea59b0083cea8bdbe97446a0262421
1 <?php
2 /**
3 * Checkout Module.
5 * This module supports a popup window to handle patient checkout
6 * as a point-of-sale transaction. Support for in-house drug sales
7 * is included.
9 * <pre>
10 * Important notes about system design:
11 * (1) Drug sales may or may not be associated with an encounter;
12 * they are if they are paid for concurrently with an encounter, or
13 * if they are "product" (non-prescription) sales via the Fee Sheet.
14 * (2) Drug sales without an encounter will have 20YYMMDD, possibly
15 * with a suffix, as the encounter-number portion of their invoice
16 * number.
17 * (3) Payments are saved as AR only, don't mess with the billing table.
18 * See library/classes/WSClaim.class.php for posting code.
19 * (4) On checkout, the billing and drug_sales table entries are marked
20 * as billed and so become unavailable for further billing.
21 * (5) Receipt printing must be a separate operation from payment,
22 * and repeatable.
24 * TBD:
25 * If this user has 'irnpool' set
26 * on display of checkout form
27 * show pending next invoice number
28 * on applying checkout
29 * save next invoice number to form_encounter
30 * compute new next invoice number
31 * on receipt display
32 * show invoice number
33 * </pre>
35 * Copyright (C) 2006-2016 Rod Roark <rod@sunsetsystems.com>
37 * LICENSE: This program is free software; you can redistribute it and/or
38 * modify it under the terms of the GNU General Public License
39 * as published by the Free Software Foundation; either version 2
40 * of the License, or (at your option) any later version.
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Public License for more details.
45 * You should have received a copy of the GNU General Public License
46 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
48 * @package OpenEMR
49 * @author Rod Roark <rod@sunsetsystems.com>
50 * @author Brady Miller <brady@sparmy.com>
51 * @link http://www.open-emr.org
55 $fake_register_globals=false;
56 $sanitize_all_escapes=true;
58 require_once("../globals.php");
59 require_once("$srcdir/acl.inc");
60 require_once("$srcdir/patient.inc");
61 require_once("$srcdir/billing.inc");
62 require_once("$srcdir/formatting.inc.php");
63 require_once("../../custom/code_types.inc.php");
65 $currdecimals = $GLOBALS['currency_decimals'];
67 $details = empty($_GET['details']) ? 0 : 1;
69 $patient_id = empty($_GET['ptid']) ? $pid : 0 + $_GET['ptid'];
71 // Get the patient's name and chart number.
72 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code');
74 // Output HTML for an invoice line item.
76 $prevsvcdate = '';
77 function receiptDetailLine($svcdate, $description, $amount, $quantity) {
78 global $prevsvcdate, $details;
79 if (!$details) return;
80 $amount = sprintf('%01.2f', $amount);
81 if (empty($quantity)) $quantity = 1;
82 $price = sprintf('%01.4f', $amount / $quantity);
83 $tmp = sprintf('%01.2f', $price);
84 if ($price == $tmp) $price = $tmp;
85 echo " <tr>\n";
86 echo " <td>" . ($svcdate == $prevsvcdate ? '&nbsp;' : text(oeFormatShortDate($svcdate))) . "</td>\n";
87 echo " <td>" . text($description) . "</td>\n";
88 echo " <td align='right'>" . text(oeFormatMoney($price)) . "</td>\n";
89 echo " <td align='right'>" . text($quantity) . "</td>\n";
90 echo " <td align='right'>" . text(oeFormatMoney($amount)) . "</td>\n";
91 echo " </tr>\n";
92 $prevsvcdate = $svcdate;
95 // Output HTML for an invoice payment.
97 function receiptPaymentLine($paydate, $amount, $description='') {
98 $amount = sprintf('%01.2f', 0 - $amount); // make it negative
99 echo " <tr>\n";
100 echo " <td>" . text(oeFormatShortDate($paydate)) . "</td>\n";
101 echo " <td>" . xlt('Payment') . " " . text($description) . "</td>\n";
102 echo " <td colspan='2'>&nbsp;</td>\n";
103 echo " <td align='right'>" . text(oeFormatMoney($amount)) . "</td>\n";
104 echo " </tr>\n";
107 // Generate a receipt from the last-billed invoice for this patient,
108 // or for the encounter specified as a GET parameter.
110 function generate_receipt($patient_id, $encounter=0) {
111 global $sl_err, $sl_cash_acc, $css_header, $details;
113 // Get details for what we guess is the primary facility.
114 $frow = sqlQuery("SELECT * FROM facility " .
115 "ORDER BY billing_location DESC, accepts_assignment DESC, id LIMIT 1");
117 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code,providerID');
119 // Get the most recent invoice data or that for the specified encounter.
121 // Adding a provider check so that their info can be displayed on receipts
122 if ($encounter) {
123 $ferow = sqlQuery("SELECT id, date, encounter, provider_id FROM form_encounter " .
124 "WHERE pid = ? AND encounter = ?", array($patient_id,$encounter) );
125 } else {
126 $ferow = sqlQuery("SELECT id, date, encounter, provider_id FROM form_encounter " .
127 "WHERE pid = ? " .
128 "ORDER BY id DESC LIMIT 1", array($patient_id) );
130 if (empty($ferow)) die(xlt("This patient has no activity."));
131 $trans_id = $ferow['id'];
132 $encounter = $ferow['encounter'];
133 $svcdate = substr($ferow['date'], 0, 10);
135 if ($GLOBALS['receipts_by_provider']){
136 if (isset($ferow['provider_id']) ) {
137 $encprovider = $ferow['provider_id'];
138 } else if (isset($patdata['providerID'])){
139 $encprovider = $patdata['providerID'];
140 } else { $encprovider = -1; }
143 if ($encprovider){
144 $providerrow = sqlQuery("SELECT fname, mname, lname, title, street, streetb, " .
145 "city, state, zip, phone, fax FROM users WHERE id = ?", array($encprovider) );
148 // Get invoice reference number.
149 $encrow = sqlQuery("SELECT invoice_refno FROM form_encounter WHERE " .
150 "pid = ? AND encounter = ? LIMIT 1", array($patient_id,$encounter) );
151 $invoice_refno = $encrow['invoice_refno'];
153 <html>
154 <head>
155 <?php html_header_show(); ?>
156 <link rel='stylesheet' href='<?php echo $css_header ?>' type='text/css'>
157 <title><?php echo xlt('Receipt for Payment'); ?></title>
158 <script type="text/javascript" src="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-1-2-2/index.js"></script>
159 <script type="text/javascript" src="../../library/dialog.js?v=<?php echo $v_js_includes; ?>"></script>
160 <script language="JavaScript">
162 <?php require($GLOBALS['srcdir'] . "/restoreSession.php"); ?>
164 $(document).ready(function() {
165 var win = top.printLogSetup ? top : opener.top;
166 win.printLogSetup(document.getElementById('printbutton'));
169 // Process click on Print button.
170 function printlog_before_print() {
171 var divstyle = document.getElementById('hideonprint').style;
172 divstyle.display = 'none';
175 // Process click on Delete button.
176 function deleteme() {
177 dlgopen('deleter.php?billing=<?php echo attr("$patient_id.$encounter"); ?>', '_blank', 500, 450);
178 return false;
181 // Called by the deleteme.php window on a successful delete.
182 function imdeleted() {
183 window.close();
186 </script>
187 </head>
188 <body class="body_top">
189 <center>
190 <?php
191 if ( $GLOBALS['receipts_by_provider'] && !empty($providerrow) ) { printProviderHeader($providerrow); }
192 else { printFacilityHeader($frow); }
194 <?php
195 echo xlt("Receipt Generated") . ":" . text(date(' F j, Y'));
196 if ($invoice_refno) echo " " . xlt("Invoice Number") . ": " . text($invoice_refno) . " " . xlt("Service Date") . ": " . text($svcdate);
198 <br>&nbsp;
199 </b></p>
200 </center>
202 <?php echo text($patdata['fname']) . ' ' . text($patdata['mname']) . ' ' . text($patdata['lname']) ?>
203 <br><?php echo text($patdata['street']) ?>
204 <br><?php echo text($patdata['city']) . ', ' . text($patdata['state']) . ' ' . text($patdata['postal_code']) ?>
205 <br>&nbsp;
206 </p>
207 <center>
208 <table cellpadding='5'>
209 <tr>
210 <td><b><?php echo xlt('Date'); ?></b></td>
211 <td><b><?php echo xlt('Description'); ?></b></td>
212 <td align='right'><b><?php echo $details ? xlt('Price') : '&nbsp;'; ?></b></td>
213 <td align='right'><b><?php echo $details ? xlt('Qty' ) : '&nbsp;'; ?></b></td>
214 <td align='right'><b><?php echo xlt('Total'); ?></b></td>
215 </tr>
217 <?php
218 $charges = 0.00;
220 // Product sales
221 $inres = sqlStatement("SELECT s.sale_id, s.sale_date, s.fee, " .
222 "s.quantity, s.drug_id, d.name " .
223 "FROM drug_sales AS s LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
224 // "WHERE s.pid = '$patient_id' AND s.encounter = '$encounter' AND s.fee != 0 " .
225 "WHERE s.pid = ? AND s.encounter = ? " .
226 "ORDER BY s.sale_id", array($patient_id,$encounter) );
227 while ($inrow = sqlFetchArray($inres)) {
228 $charges += sprintf('%01.2f', $inrow['fee']);
229 receiptDetailLine($inrow['sale_date'], $inrow['name'],
230 $inrow['fee'], $inrow['quantity']);
232 // Service and tax items
233 $inres = sqlStatement("SELECT * FROM billing WHERE " .
234 "pid = ? AND encounter = ? AND " .
235 // "code_type != 'COPAY' AND activity = 1 AND fee != 0 " .
236 "code_type != 'COPAY' AND activity = 1 " .
237 "ORDER BY id", array($patient_id,$encounter) );
238 while ($inrow = sqlFetchArray($inres)) {
239 $charges += sprintf('%01.2f', $inrow['fee']);
240 receiptDetailLine($svcdate, $inrow['code_text'],
241 $inrow['fee'], $inrow['units']);
243 // Adjustments.
244 $inres = sqlStatement("SELECT " .
245 "a.code_type, a.code, a.modifier, a.memo, a.payer_type, a.adj_amount, a.pay_amount, " .
246 "s.payer_id, s.reference, s.check_date, s.deposit_date " .
247 "FROM ar_activity AS a " .
248 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
249 "a.pid = ? AND a.encounter = ? AND " .
250 "a.adj_amount != 0 " .
251 "ORDER BY s.check_date, a.sequence_no", array($patient_id,$encounter) );
252 while ($inrow = sqlFetchArray($inres)) {
253 $charges -= sprintf('%01.2f', $inrow['adj_amount']);
254 $payer = empty($inrow['payer_type']) ? 'Pt' : ('Ins' . $inrow['payer_type']);
255 receiptDetailLine($svcdate, $payer . ' ' . $inrow['memo'],
256 0 - $inrow['adj_amount'], 1);
260 <tr>
261 <td colspan='5'>&nbsp;</td>
262 </tr>
263 <tr>
264 <td><?php echo text(oeFormatShortDate($svcdispdate)); ?></td>
265 <td><b><?php echo xlt('Total Charges'); ?></b></td>
266 <td align='right'>&nbsp;</td>
267 <td align='right'>&nbsp;</td>
268 <td align='right'><?php echo text(oeFormatMoney($charges, true)) ?></td>
269 </tr>
270 <tr>
271 <td colspan='5'>&nbsp;</td>
272 </tr>
274 <?php
275 // Get co-pays.
276 $inres = sqlStatement("SELECT fee, code_text FROM billing WHERE " .
277 "pid = ? AND encounter = ? AND " .
278 "code_type = 'COPAY' AND activity = 1 AND fee != 0 " .
279 "ORDER BY id", array($patient_id,$encounter) );
280 while ($inrow = sqlFetchArray($inres)) {
281 $charges += sprintf('%01.2f', $inrow['fee']);
282 receiptPaymentLine($svcdate, 0 - $inrow['fee'], $inrow['code_text']);
284 // Get other payments.
285 $inres = sqlStatement("SELECT " .
286 "a.code_type, a.code, a.modifier, a.memo, a.payer_type, a.adj_amount, a.pay_amount, " .
287 "s.payer_id, s.reference, s.check_date, s.deposit_date " .
288 "FROM ar_activity AS a " .
289 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
290 "a.pid = ? AND a.encounter = ? AND " .
291 "a.pay_amount != 0 " .
292 "ORDER BY s.check_date, a.sequence_no", array($patient_id,$encounter) );
293 while ($inrow = sqlFetchArray($inres)) {
294 $payer = empty($inrow['payer_type']) ? 'Pt' : ('Ins' . $inrow['payer_type']);
295 $charges -= sprintf('%01.2f', $inrow['pay_amount']);
296 receiptPaymentLine($svcdate, $inrow['pay_amount'],
297 $payer . ' ' . $inrow['reference']);
300 <tr>
301 <td colspan='5'>&nbsp;</td>
302 </tr>
303 <tr>
304 <td>&nbsp;</td>
305 <td><b><?php echo xlt('Balance Due'); ?></b></td>
306 <td colspan='2'>&nbsp;</td>
307 <td align='right'><?php echo text(oeFormatMoney($charges, true)) ?></td>
308 </tr>
309 </table>
310 </center>
311 <div id='hideonprint'>
313 &nbsp;
314 <a href='#' id='printbutton'><?php echo xlt('Print'); ?></a>
315 <?php if (acl_check('acct','disc')) { ?>
316 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
317 <a href='#' onclick='return deleteme();'><?php echo xlt('Undo Checkout'); ?></a>
318 <?php } ?>
319 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
320 <?php if ($details) { ?>
321 <a href='pos_checkout.php?details=0&ptid=<?php echo attr($patient_id); ?>&enc=<?php echo attr($encounter); ?>' onclick='top.restoreSession()'><?php echo xlt('Hide Details'); ?></a>
322 <?php } else { ?>
323 <a href='pos_checkout.php?details=1&ptid=<?php echo attr($patient_id); ?>&enc=<?php echo attr($encounter); ?>' onclick='top.restoreSession()'><?php echo xlt('Show Details'); ?></a>
324 <?php } ?>
325 </p>
326 </div>
327 </body>
328 </html>
329 <?php
330 } // end function generate_receipt()
332 // Function to output a line item for the input form.
334 $lino = 0;
335 function write_form_line($code_type, $code, $id, $date, $description,
336 $amount, $units, $taxrates) {
337 global $lino;
338 $amount = sprintf("%01.2f", $amount);
339 if (empty($units)) $units = 1;
340 $price = $amount / $units; // should be even cents, but ok here if not
341 if ($code_type == 'COPAY' && !$description) $description = xl('Payment');
342 echo " <tr>\n";
343 echo " <td>" . text(oeFormatShortDate($date));
344 echo "<input type='hidden' name='line[$lino][code_type]' value='" . attr($code_type) . "'>";
345 echo "<input type='hidden' name='line[$lino][code]' value='" . attr($code) . "'>";
346 echo "<input type='hidden' name='line[$lino][id]' value='" . attr($id) . "'>";
347 echo "<input type='hidden' name='line[$lino][description]' value='" . attr($description) . "'>";
348 echo "<input type='hidden' name='line[$lino][taxrates]' value='" . attr($taxrates) . "'>";
349 echo "<input type='hidden' name='line[$lino][price]' value='" . attr($price) . "'>";
350 echo "<input type='hidden' name='line[$lino][units]' value='" . attr($units) . "'>";
351 echo "</td>\n";
352 echo " <td>" . text($description) . "</td>";
353 echo " <td align='right'>" . text($units) . "</td>";
354 echo " <td align='right'><input type='text' name='line[$lino][amount]' " .
355 "value='" . attr($amount) . "' size='6' maxlength='8'";
356 // Modifying prices requires the acct/disc permission.
357 // if ($code_type == 'TAX' || ($code_type != 'COPAY' && !acl_check('acct','disc')))
358 echo " style='text-align:right;background-color:transparent' readonly";
359 // else echo " style='text-align:right' onkeyup='computeTotals()'";
360 echo "></td>\n";
361 echo " </tr>\n";
362 ++$lino;
365 // Create the taxes array. Key is tax id, value is
366 // (description, rate, accumulated total).
367 $taxes = array();
368 $pres = sqlStatement("SELECT option_id, title, option_value " .
369 "FROM list_options WHERE list_id = 'taxrate' AND activity = 1 ORDER BY seq, title, option_id");
370 while ($prow = sqlFetchArray($pres)) {
371 $taxes[$prow['option_id']] = array($prow['title'], $prow['option_value'], 0);
374 // Print receipt header for facility
375 function printFacilityHeader($frow){
376 echo "<p><b>" . text($frow['name']) .
377 "<br>" . text($frow['street']) .
378 "<br>" . text($frow['city']) . ', ' . text($frow['state']) . ' ' . text($frow['postal_code']) .
379 "<br>" . text($frow['phone']) .
380 "<br>&nbsp" .
381 "<br>";
384 // Pring receipt header for Provider
385 function printProviderHeader($pvdrow){
386 echo "<p><b>" . text($pvdrow['title']) . " " . text($pvdrow['fname']) . " " . text($pvdrow['mname']) . " " . text($pvdrow['lname']) . " " .
387 "<br>" . text($pvdrow['street']) .
388 "<br>" . text($pvdrow['city']) . ', ' . text($pvdrow['state']) . ' ' . text($pvdrow['postal_code']) .
389 "<br>" . text($pvdrow['phone']) .
390 "<br>&nbsp" .
391 "<br>";
394 // Mark the tax rates that are referenced in this invoice.
395 function markTaxes($taxrates) {
396 global $taxes;
397 $arates = explode(':', $taxrates);
398 if (empty($arates)) return;
399 foreach ($arates as $value) {
400 if (!empty($taxes[$value])) $taxes[$value][2] = '1';
404 $payment_methods = array(
405 'Cash',
406 'Check',
407 'MC',
408 'VISA',
409 'AMEX',
410 'DISC',
411 'Other');
413 $alertmsg = ''; // anything here pops up in an alert box
415 // If the Save button was clicked...
417 if ($_POST['form_save']) {
419 // On a save, do the following:
420 // Flag drug_sales and billing items as billed.
421 // Post the corresponding invoice with its payment(s) to sql-ledger
422 // and be careful to use a unique invoice number.
423 // Call the generate-receipt function.
424 // Exit.
426 $form_pid = $_POST['form_pid'];
427 $form_encounter = $_POST['form_encounter'];
429 // Get the posting date from the form as yyyy-mm-dd.
430 $dosdate = date("Y-m-d");
431 if (preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $_POST['form_date'], $matches)) {
432 $dosdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
435 // If there is no associated encounter (i.e. this invoice has only
436 // prescriptions) then assign an encounter number of the service
437 // date, with an optional suffix to ensure that it's unique.
439 if (! $form_encounter) {
440 $form_encounter = substr($dosdate,0,4) . substr($dosdate,5,2) . substr($dosdate,8,2);
441 $tmp = '';
442 while (true) {
443 $ferow = sqlQuery("SELECT id FROM form_encounter WHERE " .
444 "pid = ? AND encounter = ?", array($form_pid, $form_encounter.$tmp) );
445 if (empty($ferow)) break;
446 $tmp = $tmp ? $tmp + 1 : 1;
448 $form_encounter .= $tmp;
451 // Delete any TAX rows from billing because they will be recalculated.
452 sqlStatement("UPDATE billing SET activity = 0 WHERE " .
453 "pid = ? AND encounter = ? AND " .
454 "code_type = 'TAX'", array($form_pid,$form_encounter) );
456 $form_amount = $_POST['form_amount'];
457 $lines = $_POST['line'];
459 for ($lino = 0; $lines[$lino]['code_type']; ++$lino) {
460 $line = $lines[$lino];
461 $code_type = $line['code_type'];
462 $id = $line['id'];
463 $amount = sprintf('%01.2f', trim($line['amount']));
466 if ($code_type == 'PROD') {
467 // Product sales. The fee and encounter ID may have changed.
468 $query = "update drug_sales SET fee = ?, " .
469 "encounter = ?, billed = 1 WHERE " .
470 "sale_id = ?";
471 sqlQuery($query, array($amount,$form_encounter,$id) );
473 else if ($code_type == 'TAX') {
474 // In the SL case taxes show up on the invoice as line items.
475 // Otherwise we gotta save them somewhere, and in the billing
476 // table with a code type of TAX seems easiest.
477 // They will have to be stripped back out when building this
478 // script's input form.
479 addBilling($form_encounter, 'TAX', 'TAX', 'Taxes', $form_pid, 0, 0,
480 '', '', $amount, '', '', 1);
482 else {
483 // Because there is no insurance here, there is no need for a claims
484 // table entry and so we do not call updateClaim(). Note we should not
485 // eliminate billed and bill_date from the billing table!
486 $query = "UPDATE billing SET fee = ?, billed = 1, " .
487 "bill_date = NOW() WHERE id = ?";
488 sqlQuery($query, array($amount,$id) );
492 // Post discount.
493 if ($_POST['form_discount']) {
494 if ($GLOBALS['discount_by_money']) {
495 $amount = sprintf('%01.2f', trim($_POST['form_discount']));
497 else {
498 $amount = sprintf('%01.2f', trim($_POST['form_discount']) * $form_amount / 100);
500 $memo = xl('Discount');
501 $time = date('Y-m-d H:i:s');
502 sqlBeginTrans();
503 $sequence_no = sqlQuery( "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE pid = ? AND encounter = ?", array($form_pid, $form_encounter));
504 $query = "INSERT INTO ar_activity ( " .
505 "pid, encounter, sequence_no, code, modifier, payer_type, post_user, post_time, " .
506 "session_id, memo, adj_amount " .
507 ") VALUES ( " .
508 "?, " .
509 "?, " .
510 "?, " .
511 "'', " .
512 "'', " .
513 "'0', " .
514 "?, " .
515 "?, " .
516 "'0', " .
517 "?, " .
518 "? " .
519 ")";
520 sqlStatement($query, array($form_pid,$form_encounter,$sequence_no['increment'],$_SESSION['authUserID'],$time,$memo,$amount) );
521 sqlCommitTrans();
524 // Post payment.
525 if ($_POST['form_amount']) {
526 $amount = sprintf('%01.2f', trim($_POST['form_amount']));
527 $form_source = trim($_POST['form_source']);
528 $paydesc = trim($_POST['form_method']);
529 //Fetching the existing code and modifier
530 $ResultSearchNew = sqlStatement("SELECT * FROM billing LEFT JOIN code_types ON billing.code_type=code_types.ct_key ".
531 "WHERE code_types.ct_fee=1 AND billing.activity!=0 AND billing.pid =? AND encounter=? ORDER BY billing.code,billing.modifier",
532 array($form_pid,$form_encounter));
533 if($RowSearch = sqlFetchArray($ResultSearchNew))
535 $Codetype=$RowSearch['code_type'];
536 $Code=$RowSearch['code'];
537 $Modifier=$RowSearch['modifier'];
538 }else{
539 $Codetype='';
540 $Code='';
541 $Modifier='';
543 $session_id=sqlInsert("INSERT INTO ar_session (payer_id,user_id,reference,check_date,deposit_date,pay_total,".
544 " global_amount,payment_type,description,patient_id,payment_method,adjustment_code,post_to_date) ".
545 " VALUES ('0',?,?,now(),?,?,'','patient','COPAY',?,?,'patient_payment',now())",
546 array($_SESSION['authId'],$form_source,$dosdate,$amount,$form_pid,$paydesc));
548 sqlBeginTrans();
549 $sequence_no = sqlQuery( "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE pid = ? AND encounter = ?", array($form_pid, $form_encounter));
550 $insrt_id=sqlInsert("INSERT INTO ar_activity (pid,encounter,sequence_no,code_type,code,modifier,payer_type,post_time,post_user,session_id,pay_amount,account_code)".
551 " VALUES (?,?,?,?,?,?,0,?,?,?,?,'PCP')",
552 array($form_pid,$form_encounter,$sequence_no['increment'],$Codetype,$Code,$Modifier,$dosdate,$_SESSION['authId'],$session_id,$amount));
553 sqlCommitTrans();
556 // If applicable, set the invoice reference number.
557 $invoice_refno = '';
558 if (isset($_POST['form_irnumber'])) {
559 $invoice_refno = trim($_POST['form_irnumber']);
561 else {
562 $invoice_refno = updateInvoiceRefNumber();
564 if ($invoice_refno) {
565 sqlStatement("UPDATE form_encounter " .
566 "SET invoice_refno = ? " .
567 "WHERE pid = ? AND encounter = ?", array($invoice_refno,$form_pid,$form_encounter) );
570 generate_receipt($form_pid, $form_encounter);
571 exit();
574 // If an encounter ID was given, then we must generate a receipt.
576 if (!empty($_GET['enc'])) {
577 generate_receipt($patient_id, $_GET['enc']);
578 exit();
581 // Get the unbilled billing table items for this patient.
582 $query = "SELECT id, date, code_type, code, modifier, code_text, " .
583 "provider_id, payer_id, units, fee, encounter " .
584 "FROM billing WHERE pid = ? AND activity = 1 AND " .
585 "billed = 0 AND code_type != 'TAX' " .
586 "ORDER BY encounter DESC, id ASC";
587 $bres = sqlStatement($query, array($patient_id) );
589 // Get the product sales for this patient.
590 $query = "SELECT s.sale_id, s.sale_date, s.prescription_id, s.fee, " .
591 "s.quantity, s.encounter, s.drug_id, d.name, r.provider_id " .
592 "FROM drug_sales AS s " .
593 "LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
594 "LEFT OUTER JOIN prescriptions AS r ON r.id = s.prescription_id " .
595 "WHERE s.pid = ? AND s.billed = 0 " .
596 "ORDER BY s.encounter DESC, s.sale_id ASC";
597 $dres = sqlStatement($query, array($patient_id) );
599 // If there are none, just redisplay the last receipt and exit.
601 if (sqlNumRows($bres) == 0 && sqlNumRows($dres) == 0) {
602 generate_receipt($patient_id);
603 exit();
606 // Get the valid practitioners, including those not active.
607 $arr_users = array();
608 $ures = sqlStatement("SELECT id, username FROM users WHERE " .
609 "( authorized = 1 OR info LIKE '%provider%' ) AND username != ''");
610 while ($urow = sqlFetchArray($ures)) {
611 $arr_users[$urow['id']] = '1';
614 // Now write a data entry form:
615 // List unbilled billing items (cpt, hcpcs, copays) for the patient.
616 // List unbilled product sales for the patient.
617 // Present an editable dollar amount for each line item, a total
618 // which is also the default value of the input payment amount,
619 // and OK and Cancel buttons.
621 <html>
622 <head>
623 <link rel='stylesheet' href='<?php echo $css_header ?>' type='text/css'>
624 <title><?php echo xlt('Patient Checkout'); ?></title>
625 <style>
626 </style>
627 <style type="text/css">@import url(../../library/dynarch_calendar.css);</style>
628 <script type="text/javascript" src="../../library/textformat.js"></script>
629 <script type="text/javascript" src="../../library/dynarch_calendar.js"></script>
630 <?php include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?>
631 <script type="text/javascript" src="../../library/dynarch_calendar_setup.js"></script>
632 <script type="text/javascript" src="../../library/dialog.js?v=<?php echo $v_js_includes; ?>"></script>
633 <script type="text/javascript" src="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-1-2-2/index.js"></script>
634 <script language="JavaScript">
635 var mypcc = '<?php echo $GLOBALS['phone_country_code'] ?>';
637 <?php require($GLOBALS['srcdir'] . "/restoreSession.php"); ?>
639 // This clears the tax line items in preparation for recomputing taxes.
640 function clearTax(visible) {
641 var f = document.forms[0];
642 for (var lino = 0; true; ++lino) {
643 var pfx = 'line[' + lino + ']';
644 if (! f[pfx + '[code_type]']) break;
645 if (f[pfx + '[code_type]'].value != 'TAX') continue;
646 f[pfx + '[price]'].value = '0.00';
647 if (visible) f[pfx + '[amount]'].value = '0.00';
651 // For a given tax ID and amount, compute the tax on that amount and add it
652 // to the "price" (same as "amount") of the corresponding tax line item.
653 // Note the tax line items include their "taxrate" to make this easy.
654 function addTax(rateid, amount, visible) {
655 if (rateid.length == 0) return 0;
656 var f = document.forms[0];
657 for (var lino = 0; true; ++lino) {
658 var pfx = 'line[' + lino + ']';
659 if (! f[pfx + '[code_type]']) break;
660 if (f[pfx + '[code_type]'].value != 'TAX') continue;
661 if (f[pfx + '[code]'].value != rateid) continue;
662 var tax = amount * parseFloat(f[pfx + '[taxrates]'].value);
663 tax = parseFloat(tax.toFixed(<?php echo $currdecimals ?>));
664 var cumtax = parseFloat(f[pfx + '[price]'].value) + tax;
665 f[pfx + '[price]'].value = cumtax.toFixed(<?php echo $currdecimals ?>); // requires JS 1.5
666 if (visible) f[pfx + '[amount]'].value = cumtax.toFixed(<?php echo $currdecimals ?>); // requires JS 1.5
667 if (isNaN(tax)) alert('Tax rate not numeric at line ' + lino);
668 return tax;
670 return 0;
673 // This mess recomputes the invoice total and optionally applies a discount.
674 function computeDiscountedTotals(discount, visible) {
675 clearTax(visible);
676 var f = document.forms[0];
677 var total = 0.00;
678 for (var lino = 0; f['line[' + lino + '][code_type]']; ++lino) {
679 var code_type = f['line[' + lino + '][code_type]'].value;
680 // price is price per unit when the form was originally generated.
681 // By contrast, amount is the dynamically-generated discounted line total.
682 var price = parseFloat(f['line[' + lino + '][price]'].value);
683 if (isNaN(price)) alert('Price not numeric at line ' + lino);
684 if (code_type == 'COPAY' || code_type == 'TAX') {
685 // This works because the tax lines come last.
686 total += parseFloat(price.toFixed(<?php echo $currdecimals ?>));
687 continue;
689 var units = f['line[' + lino + '][units]'].value;
690 var amount = price * units;
691 amount = parseFloat(amount.toFixed(<?php echo $currdecimals ?>));
692 if (visible) f['line[' + lino + '][amount]'].value = amount.toFixed(<?php echo $currdecimals ?>);
693 total += amount;
694 var taxrates = f['line[' + lino + '][taxrates]'].value;
695 var taxids = taxrates.split(':');
696 for (var j = 0; j < taxids.length; ++j) {
697 addTax(taxids[j], amount, visible);
700 return total - discount;
703 // Recompute displayed amounts with any discount applied.
704 function computeTotals() {
705 var f = document.forms[0];
706 var discount = parseFloat(f.form_discount.value);
707 if (isNaN(discount)) discount = 0;
708 <?php if (!$GLOBALS['discount_by_money']) { ?>
709 // This site discounts by percentage, so convert it to a money amount.
710 if (discount > 100) discount = 100;
711 if (discount < 0 ) discount = 0;
712 discount = 0.01 * discount * computeDiscountedTotals(0, false);
713 <?php } ?>
714 var total = computeDiscountedTotals(discount, true);
715 f.form_amount.value = total.toFixed(<?php echo $currdecimals ?>);
716 return true;
719 </script>
720 </head>
722 <body class="body_top">
724 <form method='post' action='pos_checkout.php'>
725 <input type='hidden' name='form_pid' value='<?php echo attr($patient_id) ?>' />
727 <center>
730 <table cellspacing='5'>
731 <tr>
732 <td colspan='3' align='center'>
733 <b><?php echo xlt('Patient Checkout for '); ?><?php echo text($patdata['fname']) . " " .
734 text($patdata['lname']) . " (" . text($patdata['pubpid']) . ")" ?></b>
735 </td>
736 </tr>
737 <tr>
738 <td><b><?php echo xlt('Date'); ?></b></td>
739 <td><b><?php echo xlt('Description'); ?></b></td>
740 <td align='right'><b><?php echo xlt('Qty'); ?></b></td>
741 <td align='right'><b><?php echo xlt('Amount'); ?></b></td>
742 </tr>
743 <?php
744 $inv_encounter = '';
745 $inv_date = '';
746 $inv_provider = 0;
747 $inv_payer = 0;
748 $gcac_related_visit = false;
749 $gcac_service_provided = false;
751 // Process billing table items.
752 // Items that are not allowed to have a fee are skipped.
754 while ($brow = sqlFetchArray($bres)) {
755 // Skip all but the most recent encounter.
756 if ($inv_encounter && $brow['encounter'] != $inv_encounter) continue;
758 $thisdate = substr($brow['date'], 0, 10);
759 $code_type = $brow['code_type'];
761 // Collect tax rates, related code and provider ID.
762 $taxrates = '';
763 $related_code = '';
764 $sqlBindArray = array();
765 if (!empty($code_types[$code_type]['fee'])) {
766 $query = "SELECT taxrates, related_code FROM codes WHERE code_type = ? " .
767 " AND " .
768 "code = ? AND ";
769 array_push($sqlBindArray,$code_types[$code_type]['id'],$brow['code']);
770 if ($brow['modifier']) {
771 $query .= "modifier = ?";
772 array_push($sqlBindArray,$brow['modifier']);
773 } else {
774 $query .= "(modifier IS NULL OR modifier = '')";
776 $query .= " LIMIT 1";
777 $tmp = sqlQuery($query,$sqlBindArray);
778 $taxrates = $tmp['taxrates'];
779 $related_code = $tmp['related_code'];
780 markTaxes($taxrates);
783 write_form_line($code_type, $brow['code'], $brow['id'], $thisdate,
784 $brow['code_text'], $brow['fee'], $brow['units'],
785 $taxrates);
786 if (!$inv_encounter) $inv_encounter = $brow['encounter'];
787 $inv_payer = $brow['payer_id'];
788 if (!$inv_date || $inv_date < $thisdate) $inv_date = $thisdate;
790 // Custom logic for IPPF to determine if a GCAC issue applies.
791 if ($GLOBALS['ippf_specific'] && $related_code) {
792 $relcodes = explode(';', $related_code);
793 foreach ($relcodes as $codestring) {
794 if ($codestring === '') continue;
795 list($codetype, $code) = explode(':', $codestring);
796 if ($codetype !== 'IPPF') continue;
797 if (preg_match('/^25222/', $code)) {
798 $gcac_related_visit = true;
799 if (preg_match('/^25222[34]/', $code))
800 $gcac_service_provided = true;
806 // Process copays
808 $totalCopay = getPatientCopay($patient_id,$encounter);
809 if ($totalCopay < 0) {
810 write_form_line("COPAY", "", "", "", "", $totalCopay, "", "");
813 // Process drug sales / products.
815 while ($drow = sqlFetchArray($dres)) {
816 if ($inv_encounter && $drow['encounter'] && $drow['encounter'] != $inv_encounter) continue;
818 $thisdate = $drow['sale_date'];
819 if (!$inv_encounter) $inv_encounter = $drow['encounter'];
821 if (!$inv_provider && !empty($arr_users[$drow['provider_id']]))
822 $inv_provider = $drow['provider_id'] + 0;
824 if (!$inv_date || $inv_date < $thisdate) $inv_date = $thisdate;
826 // Accumulate taxes for this product.
827 $tmp = sqlQuery("SELECT taxrates FROM drug_templates WHERE drug_id = ? " .
828 " ORDER BY selector LIMIT 1", array($drow['drug_id']) );
829 // accumTaxes($drow['fee'], $tmp['taxrates']);
830 $taxrates = $tmp['taxrates'];
831 markTaxes($taxrates);
833 write_form_line('PROD', $drow['drug_id'], $drow['sale_id'],
834 $thisdate, $drow['name'], $drow['fee'], $drow['quantity'], $taxrates);
837 // Write a form line for each tax that has money, adding to $total.
838 foreach ($taxes as $key => $value) {
839 if ($value[2]) {
840 write_form_line('TAX', $key, $key, date('Y-m-d'), $value[0], 0, 1, $value[1]);
844 // Besides copays, do not collect any other information from ar_activity,
845 // since this is for appt checkout.
847 if ($inv_encounter) {
848 $erow = sqlQuery("SELECT provider_id FROM form_encounter WHERE " .
849 "pid = ? AND encounter = ? " .
850 "ORDER BY id DESC LIMIT 1", array($patient_id,$inv_encounter) );
851 $inv_provider = $erow['provider_id'] + 0;
854 </table>
857 <table border='0' cellspacing='4'>
859 <tr>
860 <td>
861 <?php echo $GLOBALS['discount_by_money'] ? xlt('Discount Amount') : xlt('Discount Percentage'); ?>:
862 </td>
863 <td>
864 <input type='text' name='form_discount' size='6' maxlength='8' value=''
865 style='text-align:right' onkeyup='computeTotals()'>
866 </td>
867 </tr>
869 <tr>
870 <td>
871 <?php echo xlt('Payment Method'); ?>:
872 </td>
873 <td>
874 <select name='form_method'>
875 <?php
876 $query1112 = "SELECT * FROM list_options where list_id=? ORDER BY seq, title ";
877 $bres1112 = sqlStatement($query1112,array('payment_method'));
878 while ($brow1112 = sqlFetchArray($bres1112))
880 if($brow1112['option_id']=='electronic' || $brow1112['option_id']=='bank_draft')
881 continue;
882 echo "<option value='".attr($brow1112['option_id'])."'>".text(xl_list_label($brow1112['title']))."</option>";
885 </select>
886 </td>
887 </tr>
889 <tr>
890 <td>
891 <?php echo xlt('Check/Reference Number'); ?>:
892 </td>
893 <td>
894 <input type='text' name='form_source' size='10' value=''>
895 </td>
896 </tr>
898 <tr>
899 <td>
900 <?php echo xlt('Amount Paid'); ?>:
901 </td>
902 <td>
903 <input type='text' name='form_amount' size='10' value='0.00'>
904 </td>
905 </tr>
907 <tr>
908 <td>
909 <?php echo xlt('Posting Date'); ?>:
910 </td>
911 <td>
912 <input type='text' size='10' name='form_date' id='form_date'
913 value='<?php echo attr($inv_date) ?>'
914 title='yyyy-mm-dd date of service'
915 onkeyup='datekeyup(this,mypcc)' onblur='dateblur(this,mypcc)' />
916 <img src='../pic/show_calendar.gif' align='absbottom' width='24' height='22'
917 id='img_date' border='0' alt='[?]' style='cursor:pointer'
918 title='<?php echo xla("Click here to choose a date"); ?>'>
919 </td>
920 </tr>
922 <?php
923 // If this user has a non-empty irnpool assigned, show the pending
924 // invoice reference number.
925 $irnumber = getInvoiceRefNumber();
926 if (!empty($irnumber)) {
928 <tr>
929 <td>
930 <?php echo xlt('Tentative Invoice Ref No'); ?>:
931 </td>
932 <td>
933 <?php echo text($irnumber); ?>
934 </td>
935 </tr>
936 <?php
938 // Otherwise if there is an invoice reference number mask, ask for the refno.
939 else if (!empty($GLOBALS['gbl_mask_invoice_number'])) {
941 <tr>
942 <td>
943 <?php echo xlt('Invoice Reference Number'); ?>:
944 </td>
945 <td>
946 <input type='text' name='form_irnumber' size='10' value=''
947 onkeyup='maskkeyup(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number']); ?>")'
948 onblur='maskblur(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number']); ?>")'
950 </td>
951 </tr>
952 <?php
956 <tr>
957 <td colspan='2' align='center'>
958 &nbsp;<br>
959 <input type='submit' name='form_save' value='<?php echo xla('Save'); ?>' /> &nbsp;
960 <?php if (empty($_GET['framed'])) { ?>
961 <input type='button' value='<?php echo xla('Cancel'); ?>' onclick='window.close()' />
962 <?php } ?>
963 <input type='hidden' name='form_provider' value='<?php echo attr($inv_provider) ?>' />
964 <input type='hidden' name='form_payer' value='<?php echo attr($inv_payer) ?>' />
965 <input type='hidden' name='form_encounter' value='<?php echo attr($inv_encounter) ?>' />
966 </td>
967 </tr>
969 </table>
970 </center>
972 </form>
974 <script language='JavaScript'>
975 Calendar.setup({inputField:"form_date", ifFormat:"%Y-%m-%d", button:"img_date"});
976 computeTotals();
978 <?php
979 if ($gcac_related_visit && !$gcac_service_provided) {
980 // Skip this warning if the GCAC visit form is not allowed.
981 $grow = sqlQuery("SELECT COUNT(*) AS count FROM list_options " .
982 "WHERE list_id = 'lbfnames' AND option_id = 'LBFgcac' AND activity = 1");
983 if (!empty($grow['count'])) { // if gcac is used
984 // Skip this warning if referral or abortion in TS.
985 $grow = sqlQuery("SELECT COUNT(*) AS count FROM transactions " .
986 "WHERE title = 'Referral' AND refer_date IS NOT NULL AND " .
987 "refer_date = ? AND pid = ?", array($inv_date,$patient_id) );
988 if (empty($grow['count'])) { // if there is no referral
989 $grow = sqlQuery("SELECT COUNT(*) AS count FROM forms " .
990 "WHERE pid = ? AND encounter = ? AND " .
991 "deleted = 0 AND formdir = 'LBFgcac'", array($patient_id,$inv_encounter) );
992 if (empty($grow['count'])) { // if there is no gcac form
993 echo " alert('" . addslashes(xl('This visit will need a GCAC form, referral or procedure service.')) . "');\n";
997 } // end if ($gcac_related_visit)
999 </script>
1001 </body>
1002 </html>