6 * This module supports a popup window to handle patient checkout
7 * as a point-of-sale transaction. Support for in-house drug sales
11 * Important notes about system design:
12 * (1) Drug sales may or may not be associated with an encounter;
13 * they are if they are paid for concurrently with an encounter, or
14 * if they are "product" (non-prescription) sales via the Fee Sheet.
15 * UPDATE: ENCOUNTER IS NOW ALWAYS REQUIRED.
16 * (2) Drug sales without an encounter will have 20YYMMDD, possibly
17 * with a suffix, as the encounter-number portion of their invoice
19 * (3) Payments are saved as AR only, don't mess with the billing table.
20 * See library/classes/WSClaim.class.php for posting code.
21 * (4) On checkout, the billing and drug_sales table entries are marked
22 * as billed and so become unavailable for further billing.
23 * (5) Receipt printing must be a separate operation from payment,
27 * If this user has 'irnpool' set
28 * on display of checkout form
29 * show pending next invoice number
30 * on applying checkout
31 * save next invoice number to form_encounter
32 * compute new next invoice number
38 * @link http://www.open-emr.org
39 * @author Rod Roark <rod@sunsetsystems.com>
40 * @author Brady Miller <brady.g.miller@gmail.com>
41 * @author Ranganath Pathak <pathak@scrs1.org>
42 * @author Jerry Padgett <sjpadgett@gmail.com>
43 * @author Stephen Waite <stephen.waite@cmsvt.com>
44 * @copyright Copyright (c) 2006-2021 Rod Roark <rod@sunsetsystems.com>
45 * @copyright Copyright (c) 2017-2019 Brady Miller <brady.g.miller@gmail.com>
46 * @copyright Copyright (c) 2018 Ranganath Pathak <pathak@scrs1.org>
47 * @copyright Copyright (c) 2019 Jerry Padgett <sjpadgett@gmail.com>
48 * @copyright Copyright (c) 2019 Stephen Waite <stephen.waite@cmsvt.com>
49 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
52 require_once("$srcdir/patient.inc");
53 require_once("$srcdir/options.inc.php");
54 require_once("../../custom/code_types.inc.php");
55 require_once("$srcdir/checkout_receipt_array.inc.php");
56 require_once("$srcdir/appointment_status.inc.php");
58 use OpenEMR\Billing\BillingUtilities
;
59 use OpenEMR\Billing\SLEOB
;
60 use OpenEMR\Common\Acl\AclMain
;
61 use OpenEMR\Common\Csrf\CsrfUtils
;
62 use OpenEMR\Core\Header
;
63 use OpenEMR\OeUI\OemrUI
;
64 use OpenEMR\Services\FacilityService
;
66 $facilityService = new FacilityService();
68 // Change this to get the old appearance.
69 $TAXES_AFTER_ADJUSTMENT = true;
71 $currdecimals = intval($GLOBALS['currency_decimals'] ??
2);
73 // Details default to yes now.
74 $details = (!isset($_GET['details']) ||
!empty($_GET['details'])) ?
1 : 0;
76 $patient_id = empty($_GET['ptid']) ?
$pid : intval($_GET['ptid']);
77 $encounter_id = empty($_GET['enid']) ?
0 : intval($_GET['enid']);
78 $checkout_id = empty($_GET['coid']) ?
'' : $_GET['coid']; // timestamp of checkout
80 // This flag comes from the Fee Sheet form and perhaps later others.
81 $rapid_data_entry = empty($_GET['rde']) ?
0 : 1;
84 !AclMain
::aclCheckCore('admin', 'super') &&
85 !AclMain
::aclCheckCore('acct', 'bill') &&
86 !AclMain
::aclCheckCore('acct', 'disc')
88 die("Not authorized!");
91 // This will be used for SQL timestamps that we write.
92 $this_bill_date = date('Y-m-d H:i:s');
94 // Get the patient's name and chart number.
95 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code');
97 // Adjustments from the ar_activity table.
100 // Holds possible javascript error messages.
103 // Format a money amount with decimals but no other decoration.
104 // Second argument is used when extra precision is required.
105 function formatMoneyNumber($value, $extradecimals = 0)
107 return sprintf('%01.' . ($GLOBALS['currency_decimals'] +
$extradecimals) . 'f', $value);
110 // Get a list item's title, translated if appropriate.
112 function getListTitle($list, $option)
115 "SELECT title FROM list_options WHERE list_id = ? AND option_id = ? AND activity = 1",
116 array($list, $option)
118 if (empty($row['title'])) {
121 return xl_list_label($row['title']);
124 function generate_layout_display_field($formid, $fieldid, $currvalue)
127 "SELECT * FROM layout_options WHERE form_id = ? AND field_id = ? LIMIT 1",
128 array($formid, $fieldid)
133 return generate_display_field($frow, $currvalue);
136 // This creates and loads the array $aAdjusts of adjustment data for this encounter.
138 function load_adjustments($patient_id, $encounter_id)
141 // Create array aAdjusts from ar_activity rows for $encounter_id.
143 $ares = sqlStatement(
145 "a.payer_type, a.adj_amount, a.memo, a.code_type, a.code, a.post_time, a.post_date, " .
146 "s.session_id, s.reference, s.check_date, lo.title AS memotitle " .
147 "FROM ar_activity AS a " .
148 "LEFT JOIN list_options AS lo ON lo.list_id = 'adjreason' AND lo.option_id = a.memo AND " .
150 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
151 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND " .
152 "( a.adj_amount != 0 OR a.pay_amount = 0 ) " .
153 "ORDER BY s.check_date, a.sequence_no",
154 array($patient_id, $encounter_id)
156 while ($arow = sqlFetchArray($ares)) {
157 if (empty($arow['memotitle'])) {
158 $arow['memotitle'] = $arow['memo'];
164 // Total and clear adjustments in $aAdjusts matching this line item. Should only
165 // happen for billed items, and matching includes the billing timestamp in order
166 // to handle the case of multiple checkouts.
167 function pull_adjustment($code_type, $code, $billtime, &$memo)
172 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
173 for ($i = 0; $i < count($aAdjusts); ++
$i) {
175 $aAdjusts[$i]['code_type'] == $code_type && $aAdjusts[$i]['code'] == $code &&
176 $aAdjusts[$i]['post_time'] == $billtime &&
177 ($aAdjusts[$i]['adj_amount'] != 0 ||
$aAdjusts[$i]['memotitle'] !== '')
179 $adjust +
= $aAdjusts[$i]['adj_amount'];
180 if ($memo && $aAdjusts[$i]['memotitle']) {
183 $memo .= $aAdjusts[$i]['memotitle'];
184 $aAdjusts[$i]['adj_amount'] = 0;
185 $aAdjusts[$i]['memotitle'] = '';
192 // Generate $aTaxNames = array of tax names, and $aInvTaxes = array of taxes for this invoice.
193 // For a given tax ID and line ID, $aInvTaxes[$taxID][$lineID] = tax amount.
194 // $lineID identifies the invoice line item (product or service) that was taxed, and is of the
195 // form S:<billing.id> or P:<drug_sales.sale_id>
196 // Taxes may change from time to time and $aTaxNames reflects only the taxes that were present
199 function load_taxes($patient_id, $encounter)
201 global $aTaxNames, $aInvTaxes, $taxes;
202 global $num_optional_columns, $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
203 global $form_num_type_columns, $form_num_method_columns, $form_num_ref_columns, $form_num_amount_columns;
205 $aTaxNames = array();
206 $aInvTaxes = array();
207 foreach ($taxes as $taxid => $taxarr) {
208 $aTaxNames[$taxid] = $taxarr[0];
209 $aInvTaxes[$taxid] = array();
212 $taxres = sqlStatement(
213 "SELECT code, fee, ndc_info FROM billing WHERE " .
214 "pid = ? AND encounter = ? AND code_type = 'TAX' AND activity = 1 " .
216 array($patient_id, $encounter)
218 while ($taxrow = sqlFetchArray($taxres)) {
219 $aInvTaxes[$taxrow['code']][$taxrow['ndc_info']] = $taxrow['fee'];
222 // Knowing the number of tax columns we can now compute the total number of optional
223 // columns and from that the colspan values for various things.
224 $num_optional_columns = (empty($GLOBALS['gbl_checkout_charges']) ?
0 : 1) +
225 (empty($GLOBALS['gbl_charge_categories']) ?
0 : 1) +
226 (empty($GLOBALS['gbl_checkout_line_adjustments']) ?
0 : 2) +
228 // Compute colspans for receipt payment rows.
229 // What's in play here are columns for Qty, Price, the optionals, and Total.
230 $rcpt_num_method_columns = 1;
231 $rcpt_num_ref_columns = 1;
232 if ($num_optional_columns == 1) {
233 $rcpt_num_method_columns = 2;
234 } else if ($num_optional_columns > 1) {
235 $rcpt_num_method_columns = 3;
236 $rcpt_num_ref_columns = $num_optional_columns - 1;
238 $rcpt_num_amount_columns = 3 +
$num_optional_columns - $rcpt_num_method_columns - $rcpt_num_ref_columns;
239 // Compute colspans for form payment rows.
240 $form_num_type_columns = 2;
241 $form_num_method_columns = 1;
242 $form_num_ref_columns = 1;
243 if ($num_optional_columns > 0) {
244 $form_num_method_columns = 2;
246 if ($num_optional_columns > 1) {
247 $form_num_type_columns = 3;
249 $form_num_amount_columns = 5 +
$num_optional_columns - $form_num_type_columns - $form_num_method_columns - $form_num_ref_columns;
252 // Use $lineid to match up (and delete) entries in $aInvTaxes with the line.
253 // $lineid looks like: S:<billing.id> or P:<drug_sales.sale_id>.
254 // This writes to the $aTaxes argument and returns the total tax for the line.
255 function pull_tax($lineid, &$aTaxes)
259 foreach ($aInvTaxes as $taxid => $taxarr) {
261 if ($lineid !== '') {
262 foreach ($taxarr as $taxlineid => $tax) {
263 if ($taxlineid === $lineid) {
264 $aTaxes[$taxid] +
= $tax;
266 $aInvTaxes[$taxid][$taxlineid] = 0;
271 // $aTaxes now contains the total of each tax type (keyed on tax ID) for this line item,
272 // and those matched amounts are removed from $aInvTaxes.
276 // Output HTML for a receipt line item.
278 function receiptDetailLine(
290 global $details, $TAXES_AFTER_ADJUSTMENT;
292 // Use $lineid to match up (and delete) entries in $aInvTaxes with the line.
294 $totlinetax = pull_tax($lineid, $aTaxes);
295 // $aTaxes now contains the total of each tax type for this line item, and those matched
296 // amounts are removed from $aInvTaxes.
302 // If an adjustment, do appropriate interpretation.
303 if ($code_type === '') {
305 $adjust = 0 - $charge;
307 list($payer, $code_type, $code) = explode('|', $code);
308 $memo = $description;
309 $description = $GLOBALS['simplified_demographics'] ?
'' : "$payer ";
310 $description .= $code ?
xl('Item Adjustment') : xl('Invoice Adjustment');
313 // Total and clear adjustments in $aAdjusts matching this line item.
314 $adjust +
= pull_adjustment($code_type, $code, $billtime, $memo);
317 $charge = formatMoneyNumber($charge);
318 $total = formatMoneyNumber($charge +
$totlinetax - $adjust);
319 if (empty($quantity)) {
322 $price = formatMoneyNumber($charge / $quantity, 2);
323 $tmp = formatMoneyNumber($price);
324 if ($price == $tmp) {
327 if (is_array($aTotals)) {
328 $aTotals[0] +
= $quantity;
329 $aTotals[1] +
= $price;
330 $aTotals[2] +
= $charge;
331 $aTotals[3] +
= $adjust;
332 $aTotals[4] +
= $total;
333 // Accumulate columns 5 and beyond for taxes.
335 foreach ($aTaxes as $tax) {
336 $aTotals[$i++
] +
= $tax;
343 if (empty($postdate) ||
substr($postdate, 0, 4) == '0000') {
344 $postdate = $billtime;
347 echo " <td title='" . xla('Entered') . ' ' .
348 text(oeFormatShortDate($billtime)) . attr(substr($billtime, 10)) . "'>" .
349 text(oeFormatShortDate($postdate)) . "</td>\n";
350 echo " <td>" . text($code) . "</td>\n";
351 echo " <td>" . text($description) . "</td>\n";
352 echo " <td class='text-center'>" . ($isadjust ?
'' : $quantity) . "</td>\n";
353 echo " <td class='text-right'>" . text(oeFormatMoney($price, false, true)) . "</td>\n";
355 if (!empty($GLOBALS['gbl_checkout_charges'])) {
356 echo " <td class='text-right'>" . text(oeFormatMoney($charge, false, true)) . "</td>\n";
359 if (!$TAXES_AFTER_ADJUSTMENT) {
360 // Write tax amounts.
361 foreach ($aTaxes as $tax) {
362 echo " <td class='text-right'>" . text(oeFormatMoney($tax, false, true)) . "</td>\n";
367 if (!empty($GLOBALS['gbl_charge_categories'])) {
368 echo " <td class='text-right'>" . text($chargecat) . "</td>\n";
371 // Adjustment and its description.
372 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
373 echo " <td class='text-right'>" . text($memo) . "</td>\n";
374 echo " <td class='text-right'>" . text(oeFormatMoney($adjust, false, true)) . "</td>\n";
377 if ($TAXES_AFTER_ADJUSTMENT) {
378 // Write tax amounts.
379 foreach ($aTaxes as $tax) {
380 echo " <td class='text-right'>" . text(oeFormatMoney($tax, false, true)) . "</td>\n";
384 echo " <td class='text-right'>" . text(oeFormatMoney($total)) . "</td>\n";
388 // Output HTML for a receipt payment line.
390 function receiptPaymentLine($paydate, $amount, $description = '', $method = '', $refno = '', $billtime = '')
392 global $aTaxNames, $num_optional_columns;
393 global $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
394 $amount = formatMoneyNumber($amount); // make it negative
395 if ($description == 'Pt') {
398 // Resolve the payment method portion of the memo to display properly.
399 if (!empty($method)) {
400 $tmp = explode(' ', $method, 2);
401 $method = getListTitle('paymethod', $tmp[0]);
402 if (isset($tmp[1])) {
403 // If the description is not interesting then let it hold the check number
404 // or similar, otherwise append that to the payment method.
405 if ($description == '') {
406 $description = $tmp[1];
408 $method .= ' ' . $tmp[1];
414 if (!empty($billtime) && substr($billtime, 0, 4) != '0000') {
415 echo " title='" . xla('Entered') . ' ' .
416 text(oeFormatShortDate($billtime)) . attr(substr($billtime, 10)) . "'";
418 echo ">" . text(oeFormatShortDate($paydate)) . "</td>\n";
419 echo " <td colspan='2'>" . text($refno) . "</td>\n";
420 echo " <td colspan='$rcpt_num_method_columns' class='text-left'>" . text($method) . "</td>\n";
421 echo " <td colspan='$rcpt_num_ref_columns' class='text-left'>" . text($description) . "</td>\n";
422 echo " <td colspan='$rcpt_num_amount_columns' class='text-right'>" . text(oeFormatMoney($amount)) . "</td>\n";
426 // Compute a current checksum of this encounter's invoice-related data from the database.
428 function invoiceChecksum($pid, $encounter)
431 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
432 "id, code, modifier, units, fee, authorized, provider_id, ndc_info, justify, billed, user, bill_date" .
433 "))) AS checksum FROM billing WHERE " .
434 "pid = ? AND encounter = ? AND activity = 1",
435 array($pid, $encounter)
438 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
439 "sale_id, inventory_id, prescription_id, quantity, fee, sale_date, billed, bill_date" .
440 "))) AS checksum FROM drug_sales WHERE " .
441 "pid = ? AND encounter = ?",
442 array($pid, $encounter)
445 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
446 "sequence_no, code, modifier, payer_type, post_time, post_user, memo, pay_amount, adj_amount, post_date" .
447 "))) AS checksum FROM ar_activity WHERE " .
448 "pid = ? AND encounter = ?",
449 array($pid, $encounter)
452 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
453 "id, date, reason, facility_id, provider_id, supervisor_id, invoice_refno" .
454 "))) AS checksum FROM form_encounter WHERE " .
455 "pid = ? AND encounter = ?",
456 array($pid, $encounter)
458 return (0 +
$row1['checksum']) ^
(0 +
$row2['checksum']) ^
(0 +
$row3['checksum']) ^
(0 +
$row4['checksum']);
461 //////////////////////////////////////////////////////////////////////
463 // Generate a receipt from the last-billed invoice for this patient,
464 // or for the encounter specified as a GET parameter.
466 function generate_receipt($patient_id, $encounter = 0)
468 global $details, $rapid_data_entry, $aAdjusts;
469 global $web_root, $webserver_root, $code_types;
470 global $aTaxNames, $aInvTaxes, $checkout_times, $current_checksum;
471 global $num_optional_columns, $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
472 global $TAXES_AFTER_ADJUSTMENT;
473 global $facilityService, $alertmsg;
475 // Get the most recent invoice data or that for the specified encounter.
478 "SELECT id, date, encounter, facility_id, invoice_refno " .
479 "FROM form_encounter WHERE pid = ? AND encounter = ?",
480 array($patient_id, $encounter)
484 "SELECT id, date, encounter, facility_id, invoice_refno " .
485 "FROM form_encounter WHERE pid = ? ORDER BY id DESC LIMIT 1",
490 die(xlt("This patient has no activity."));
492 $trans_id = $ferow['id'];
493 $encounter = $ferow['encounter'];
494 $svcdate = substr($ferow['date'], 0, 10);
495 $invoice_refno = $ferow['invoice_refno'];
497 // Generate checksum.
498 $current_checksum = invoiceChecksum($patient_id, $encounter);
500 // Get details for the visit's facility.
501 $frow = $facilityService->getById($ferow['facility_id']);
503 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code');
505 // Get array of checkout timestamps.
506 $checkout_times = craGetTimestamps($patient_id, $encounter);
508 // Generate $aTaxNames = array of tax names, and $aInvTaxes = array of taxes for this invoice.
509 load_taxes($patient_id, $encounter);
511 <!-- The following is from the php
function generate_receipt
. -->
515 <?php Header
::setupHeader(['datetime-picker']);?
>
516 <title
><?php
echo xlt('Client Receipt'); ?
></title
>
520 <?php
require($GLOBALS['srcdir'] . "/restoreSession.php"); ?
>
523 var win
= top
.printLogSetup ? top
: opener
.top
;
524 win
.printLogSetup(document
.getElementById('printbutton'));
527 // Process click on Print button.
528 function printme(checkout_id
) {
529 <?php
if (!empty($GLOBALS['gbl_custom_receipt'])) { ?
>
530 // Custom checkout receipt needs to be sent as a PDF in a new window or tab.
531 window
.open('pos_checkout.php?ptid=' +
<?php
echo js_url($patient_id); ?
>
532 +
'&enc=' +
<?php
echo js_url($encounter); ?
>
533 +
'&pdf=1&coid=' +
encodeURIComponent(checkout_id
),
534 '_blank', 'width=750,height=550,resizable=1,scrollbars=1');
536 var divstyle
= document
.getElementById('hideonprint').style
;
537 divstyle
.display
= 'none';
538 if (checkout_id
!= '*') {
545 // Process click on Print button before printing.
546 function printlog_before_print() {
547 // * means do not call window.print().
551 // Process click on Delete button.
552 function deleteme() {
553 dlgopen('deleter.php?billing=' +
<?php
echo js_url($patient_id . "." . $encounter); ?
> +
'&csrf_token_form=' +
<?php
echo js_url(CsrfUtils
::collectCsrfToken()); ?
>, '_blank', 500, 450);
557 // Called by the deleteme.php window on a successful delete.
558 function imdeleted() {
562 var voidaction
= ''; // saves action argument from voidme()
564 // Submit the form to complete a void operation.
565 function voidwrap(form_reason
, form_notes
) {
566 top
.restoreSession();
567 document
.location
.href
= 'pos_checkout.php?ptid=' +
<?php
echo js_url($patient_id); ?
> +
568 '&' +
encodeURIComponent(voidaction
) +
'=' +
<?php
echo js_url($encounter); ?
> +
569 '&form_checksum=' +
<?php
echo js_url($current_checksum); ?
> +
570 '&form_reason=' +
encodeURIComponent(form_reason
) +
571 '&form_notes=' +
encodeURIComponent(form_notes
) +
572 '<?php if (!empty($_GET['framed
'])) {
573 echo '&framed
=1';} ?>';
577 // Process click on a void option.
578 // action can be 'regen', 'void' or 'voidall'.
579 function voidme(action
) {
581 if (action
== 'void' || action
== 'voidall') {
582 if (!confirm(<?php
echo xlj('This will advance the receipt number. Please print the receipt if you have not already done so.'); ?
>)) {
585 dlgopen('void_dialog.php', '_blank', 500, 450);
588 // TBD: Better defaults for void reason and notes.
596 @media
(min
-width
: 992px
){
598 width
: 1000px
!Important
;
602 <title
><?php
echo xlt('Patient Checkout'); ?
></title
>
606 <div
class='container mt-3'>
608 <div
class='col text-center'>
609 <table
class='table' width
='95%'>
611 <td width
='25%' align
='left' valign
='top'>
613 // TBD: Maybe make a global for this file name.
614 if ($tmp = UrlIfImageExists('ma_logo.png')) {
615 echo "<img src='$tmp' />";
621 <td width
='50%' align
='center' valign
='top' class='font-weight-bold'>
622 <?php
echo text($frow['name']); ?
>
623 <br
><?php
echo text($frow['street']); ?
>
625 echo text($frow['city']) . ", ";
626 echo text($frow['state']) . " ";
627 echo text($frow['postal_code']); ?
>
628 <br
><?php
echo text($frow['phone']); ?
>
630 <td width
='25%' align
='right' valign
='top'>
631 <!-- This space available
. -->
636 <p
class='font-weight-bold'>
638 echo xlt("Client Receipt");
639 if ($invoice_refno) {
640 echo " " . xlt("for Invoice") . text(" $invoice_refno");
646 // Compute numbers for summary on right side of page.
647 $head_begbal = get_patient_balance_excluding($patient_id, $encounter);
649 "SELECT SUM(fee) AS amount FROM billing WHERE " .
650 "pid = ? AND encounter = ? AND activity = 1 AND " .
651 "code_type != 'COPAY'",
652 array($patient_id, $encounter)
654 $head_charges = $row['amount'];
656 "SELECT SUM(fee) AS amount FROM drug_sales WHERE pid = ? AND encounter = ?",
657 array($patient_id, $encounter)
659 $head_charges +
= $row['amount'];
661 "SELECT SUM(pay_amount) AS payments, " .
662 "SUM(adj_amount) AS adjustments FROM ar_activity WHERE " .
663 "pid = ? AND encounter = ? AND deleted IS NULL",
664 array($patient_id, $encounter)
666 $head_adjustments = $row['adjustments'];
667 $head_payments = $row['payments'];
669 "SELECT SUM(fee) AS amount FROM billing WHERE " .
670 "pid = ? AND encounter = ? AND activity = 1 AND " .
671 "code_type = 'COPAY'",
672 array($patient_id, $encounter)
674 $head_payments -= $row['amount'];
675 $head_endbal = $head_begbal +
$head_charges - $head_adjustments - $head_payments;
677 <table
class='table' width
='95%'>
679 <td width
='50%' class='text-left' valign
='top'>
680 <?php
echo text($patdata['fname'] . ' ' . $patdata['mname'] . ' ' . $patdata['lname']); ?
>
681 <br
/><?php
echo text($patdata['street']); ?
>
683 echo generate_layout_display_field('DEM', 'city', $patdata['city']) . ", ";
684 echo generate_layout_display_field('DEM', 'state', $patdata['state']) . " ";
685 echo text($patdata['postal_code']); ?
>
687 <td width
='50%' class='text-right' valign
='top'>
690 <td
><?php
echo xlt('Beginning Account Balance'); ?
> 
; 
; 
; 
;</td
>
691 <td
class='text-right'><?php
echo text(oeFormatMoney($head_begbal)); ?
></td
>
694 <td
><?php
echo xlt('Total Visit Charges'); ?
> 
; 
; 
; 
;</td
>
695 <td
class='text-right'><?php
echo text(oeFormatMoney($head_charges)); ?
></td
>
698 <td
><?php
echo xlt('Adjustments'); ?
> 
; 
; 
; 
;</td
>
699 <td
class='text-right'><?php
echo text(oeFormatMoney($head_adjustments)); ?
></td
>
702 <td
><?php
echo xlt('Payments'); ?
> 
; 
; 
; 
;</td
>
703 <td
class='text-right'><?php
echo text(oeFormatMoney($head_payments)); ?
></td
>
706 <td
><?php
echo xlt('Ending Account Balance'); ?
> 
; 
; 
; 
;</td
>
707 <td
class='text-right'><?php
echo text(oeFormatMoney($head_endbal)); ?
></td
>
714 <table
class='table' width
='95%'>
715 <?php
if ($details) { ?
>
717 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
718 style
='padding-top:5pt;' class='font-weight-bold'>
719 <?php
echo xlt('Charges for') . ' ' . text(oeFormatShortDate($svcdate)); ?
>
724 <td
class='font-weight-bold'><?php
echo xlt('Date'); ?
></td
>
725 <td
class='font-weight-bold'><?php
echo xlt('Code'); ?
></td
>
726 <td
class='font-weight-bold'><?php
echo xlt('Description'); ?
></td
>
727 <td
class='font-weight-bold text-center'><?php
echo $details ?
xlt('Qty') : ' '; ?
></td
>
728 <td
class='font-weight-bold text-right'><?php
echo $details ?
xlt('Price') : ' '; ?
></td
>
729 <?php
if (!empty($GLOBALS['gbl_checkout_charges'])) { ?
>
730 <td
class='font-weight-bold text-right'><?php
echo xlt('Charge'); ?
></td
>
733 if (!$TAXES_AFTER_ADJUSTMENT) {
734 foreach ($aTaxNames as $taxname) {
735 echo " <td class='font-weight-bold text-right'>" . text($taxname) . "</td>\n";
739 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { ?
>
740 <td
class='font-weight-bold text-right'><?php
echo xlt('Customer'); ?
></td
>
742 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
743 <td
class='font-weight-bold text-right'><?php
echo xlt('Adj Type'); ?
></td
>
744 <td
class='font-weight-bold text-right'><?php
echo xlt('Adj Amt'); ?
></td
>
747 if ($TAXES_AFTER_ADJUSTMENT) {
748 foreach ($aTaxNames as $taxname) {
749 echo " <td class='font-weight-bold text-right'>" . text($taxname) . "</td>\n";
753 <td
class='font-weight-bold text-right'><?php
echo xlt('Total'); ?
></td
>
756 <?php
} // end if details ?>
759 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
760 style
='border-top:1px solid black; font-size:1px; padding:0;'>
765 // Create array aAdjusts from ar_activity rows for $encounter.
766 load_adjustments($patient_id, $encounter);
768 $aTotals = array(0, 0, 0, 0, 0);
769 for ($i = 0; $i < count($aTaxNames); ++
$i) {
770 $aTotals[5 +
$i] = 0;
774 $inres = sqlStatement(
775 "SELECT s.sale_id, s.sale_date, s.fee, " .
776 "s.quantity, s.drug_id, s.billed, s.bill_date, s.selector, d.name, lo.title " .
777 "FROM drug_sales AS s " .
778 "LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
779 "LEFT JOIN list_options AS lo ON lo.list_id = 'chargecats' and lo.option_id = s.chargecat AND lo.activity = 1 " .
780 "WHERE s.pid = ? AND s.encounter = ? " .
781 "ORDER BY s.sale_id",
782 array($patient_id, $encounter)
784 while ($inrow = sqlFetchArray($inres)) {
785 $billtime = $inrow['billed'] ?
$inrow['bill_date'] : '';
786 $tmpname = $inrow['name'];
787 if ($tmpname !== $inrow['selector']) {
788 $tmpname .= ' / ' . $inrow['selector'];
790 $units = $inrow['quantity'] / FeeSheet
::getBasicUnits($inrow['drug_id'], $inrow['selector']);
798 'P:' . $inrow['sale_id'],
806 $inres = sqlStatement(
807 "SELECT * FROM billing AS b " .
808 "LEFT JOIN list_options AS lo ON lo.list_id = 'chargecats' and lo.option_id = b.chargecat AND lo.activity = 1 " .
809 "WHERE b.pid = ? AND b.encounter = ? AND " .
810 "b.code_type != 'COPAY' AND b.code_type != 'TAX' AND b.activity = 1 " .
812 array($patient_id, $encounter)
814 while ($inrow = sqlFetchArray($inres)) {
815 // Write the line item if it allows fees or is not a diagnosis.
816 if (!empty($code_types[$inrow['code_type']]['fee']) ||
empty($code_types[$inrow['code_type']]['diag'])) {
817 $billtime = $inrow['billed'] ?
$inrow['bill_date'] : '';
833 // Write any adjustments left in the aAdjusts array.
834 foreach ($aAdjusts as $arow) {
835 if ($arow['adj_amount'] == 0 && $arow['memotitle'] == '') {
838 $payer = empty($arow['payer_type']) ?
'Pt' : ('Ins' . $arow['payer_type']);
841 "$payer|" . $arow['code_type'] . "|" . $arow['code'],
844 0 - $arow['adj_amount'],
853 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
854 style
='border-top:1px solid black; font-size:1px; padding:0;'>
860 // Sub-Total line with totals of all numeric columns.
863 echo " <td colspan='3' align='right'><b>" . xlt('Sub-Total') . "</b></td>\n";
864 echo " <td align='center'>" . text($aTotals[0]) . "</td>\n";
865 echo " <td align='right'>" . text(oeFormatMoney($aTotals[1])) . "</td>\n";
866 // Optional charge amount.
867 if (!empty($GLOBALS['gbl_checkout_charges'])) {
868 echo " <td align='right'>" . text(oeFormatMoney($aTotals[2])) . "</td>\n";
870 if (!$TAXES_AFTER_ADJUSTMENT) {
871 // Put tax columns, if any, in the subtotals.
872 for ($i = 0; $i < count($aTaxNames); ++
$i) {
873 echo " <td align='right'>" . text(oeFormatMoney($aTotals[5 +
$i])) . "</td>\n";
876 // Optional charge category empty column.
877 if (!empty($GLOBALS['gbl_charge_categories'])) {
878 echo " <td align='right'> </td>\n";
880 // Optional adjustment columns.
881 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
882 echo " <td align='right'> </td>\n";
883 echo " <td align='right'>" . text(oeFormatMoney($aTotals[3])) . "</td>\n";
885 if ($TAXES_AFTER_ADJUSTMENT) {
886 // Put tax columns, if any, in the subtotals.
887 for ($i = 0; $i < count($aTaxNames); ++
$i) {
888 echo " <td align='right'>" . text(oeFormatMoney($aTotals[5 +
$i])) . "</td>\n";
891 echo " <td align='right'>" . text(oeFormatMoney($aTotals[4])) . "</td>\n";
895 // Write a line for each tax item that did not match.
896 // Should only happen for old invoices before taxes were assigned to line items.
897 foreach ($aInvTaxes as $taxid => $taxarr) {
898 foreach ($taxarr as $taxlineid => $tax) {
900 receiptDetailLine('TAX', $taxid, $aTaxNames[$taxid], 1, $tax, $aTotals);
901 $aInvTaxes[$taxid][$taxlineid] = 0;
906 // Total Charges line.
908 echo " <td colspan='" . (3 +
$num_optional_columns) . "'> </td>\n";
909 echo " <td colspan='" . 2 .
910 "' align='right'><b>" . xlt('Total Charges') . "</b></td>\n";
911 echo " <td align='right'>" . text(oeFormatMoney($aTotals[4])) . "</td>\n";
916 <td colspan
='<?php echo 6 + $num_optional_columns; ?>' style
='padding-top:5pt;'>
917 <b
><?php
echo xlt('Payments'); ?
></b
>
922 <td
><b
><?php
echo xlt('Date'); ?
></b
></td
>
923 <td colspan
='2'><b
><?php
echo xlt('Checkout Receipt Ref'); ?
></b
></td
>
924 <td colspan
="<?php echo text($rcpt_num_method_columns); ?>"
925 align
='left'><b
><?php
echo xlt('Payment Method'); ?
></b
></td
>
926 <td colspan
="<?php echo text($rcpt_num_ref_columns); ?>"
927 align
='left'><b
><?php
echo xlt('Ref No'); ?
></b
></td
>
928 <td colspan
='<?php echo text($rcpt_num_amount_columns); ?>'
929 align
='right'><b
><?php
echo xlt('Amount'); ?
></b
></td
>
933 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
934 style
='border-top:1px solid black; font-size:1px; padding:0;'>
943 $inres = sqlStatement(
944 "SELECT fee, code_text FROM billing WHERE " .
945 "pid = ? AND encounter = ? AND " .
946 "code_type = 'COPAY' AND activity = 1 AND fee != 0 " .
948 array($patient_id, $encounter)
950 while ($inrow = sqlFetchArray($inres)) {
951 $payments -= formatMoneyNumber($inrow['fee']);
952 receiptPaymentLine($svcdate, 0 - $inrow['fee'], $inrow['code_text'], 'COPAY');
955 // Get other payments.
956 $inres = sqlStatement(
958 "a.code, a.modifier, a.memo, a.payer_type, a.adj_amount, a.pay_amount, " .
959 "a.post_time, IFNULL(a.post_date, a.post_time) AS post_date, " .
960 "s.payer_id, s.reference, s.check_date, s.deposit_date " .
961 "FROM ar_activity AS a " .
962 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
963 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND " .
964 "a.pay_amount != 0 " .
965 "ORDER BY s.check_date, a.sequence_no",
966 array($patient_id, $encounter)
968 $payer = empty($inrow['payer_type']) ?
'Pt' : ('Ins' . $inrow['payer_type']);
969 while ($inrow = sqlFetchArray($inres)) {
970 $payments +
= formatMoneyNumber($inrow['pay_amount']);
971 // Compute invoice number with payment suffix.
972 $tmp = array_search($inrow['post_time'], $checkout_times);
973 $tmp = $tmp === false ?
0 : ($tmp +
1);
974 $refno = $invoice_refno ?
"$invoice_refno-$tmp" : "$encounter-$tmp";
977 $inrow['pay_amount'],
978 trim($payer . ' ' . $inrow['reference']),
987 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
988 style
='border-top:1px solid black; font-size:1px; padding:0;'>
994 <td colspan
='<?php echo 3 + $num_optional_columns; ?>'> 
;</td
>
995 <td colspan
='2' align
='right'><b
><?php
echo xlt('Total Payments'); ?
></b
></td
>
996 <td align
='right'><?php
echo str_replace(' ', ' ', text(oeFormatMoney($payments, true))); ?
></td
>
1007 // The user-customizable note.
1008 if (!empty($GLOBALS['gbl_checkout_receipt_note'])) {
1010 echo str_repeat('*', 80) . '<br />';
1011 echo ' ' . text($GLOBALS['gbl_checkout_receipt_note']) . '<br />';
1012 echo str_repeat('*', 80) . '<br />';
1018 <b
><?php
echo xlt("Printed on") . ' ' . text(dateformat()); ?
></b
>
1021 <div id
='hideonprint'>
1026 if (count($checkout_times) > 1 && !empty($GLOBALS['gbl_custom_receipt'])) {
1027 // Multiple checkouts so allow selection of the one to print.
1028 // This is only applicable for custom checkout receipts.
1029 echo "<select onchange='printme(this.value)' >\n";
1030 echo " <option value=''>" . xlt('Print Checkout') . "</option>\n";
1032 foreach ($checkout_times as $tmp) {
1034 echo " <option value='" . attr($tmp) . "'>" . text("$i: $tmp") . "</option>\n";
1038 echo "<a href='#' onclick='return printme(\"\");'>" . xlt('Print') . "</a>\n";
1042 <?php
if (AclMain
::aclCheckCore('acct', 'disc')) { ?
>
1043  
; 
; 
; 
; 
;
1044 <a href
='#' onclick
='return voidme("regen");'><?php
echo xlt('Generate New Receipt Number'); ?
></a
>
1045  
; 
; 
; 
; 
;
1046 <a href
='#' onclick
='return voidme("void");' title
='<?php echo xla('Applies to this visit only
'); ?>'>
1047 <?php
echo xlt('Void Last Checkout'); ?
></a
>
1048  
; 
; 
; 
; 
;
1049 <a href
='#' onclick
='return voidme("voidall");' title
='<?php echo xla('Applies to this visit only
'); ?>'>
1050 <?php
echo xlt('Void All Checkouts'); ?
></a
>
1053  
; 
; 
; 
; 
;
1055 <?php
if ($details) { ?
>
1056 <a href
='pos_checkout.php?details=0&ptid=<?php echo attr_url($patient_id); ?>&enc=<?php echo attr_url($encounter); ?>'
1057 onclick
='top.restoreSession()'><?php
echo xlt('Hide Details'); ?
></a
>
1059 <a href
='pos_checkout.php?details=1&ptid=<?php echo attr_url($patient_id); ?>&enc=<?php echo attr_url($encounter); ?>'
1060 onclick
='top.restoreSession()'><?php
echo xlt('Show Details'); ?
></a
>
1063 </div
><!-- end hideonprint
-->
1064 </div
><!-- end col
-->
1065 </div
><!-- end row
-->
1066 </div
><!-- end container
-->
1070 echo " alert(" . js_escape($alertmsg) . ");\n";
1079 // end function generate_receipt()
1081 //////////////////////////////////////////////////////////////////////
1083 // Function to write the heading lines for the data entry form.
1084 // This is deferred because we need to know which encounter was chosen.
1086 $form_headers_written = false;
1087 function write_form_headers()
1089 global $form_headers_written, $patdata, $patient_id, $encounter_id, $aAdjusts;
1090 global $taxes, $encounter_date, $num_optional_columns, $TAXES_AFTER_ADJUSTMENT;
1092 if ($form_headers_written) {
1095 $form_headers_written = true;
1097 // Create arrays $aAdjusts, $aTaxNames and $aInvTaxes for this encounter.
1098 load_adjustments($patient_id, $encounter_id);
1099 // This also initializes $num_optional_columns and related colspan values.
1100 load_taxes($patient_id, $encounter_id);
1103 "SELECT date FROM form_encounter WHERE pid = ? AND encounter = ?",
1104 array($patient_id, $encounter_id)
1106 $encounter_date = substr($ferow['date'], 0, 10);
1109 <td colspan
='<?php echo 5 + $num_optional_columns; ?>' align
='center' class='title'>
1110 <?php
echo xlt('Patient Checkout for '); ?
><?php
echo text($patdata['fname']) . " " .
1111 text($patdata['lname']) . " (" . text($patdata['pubpid']) . ")" ?
>
1115 $prvbal = get_patient_balance_excluding($patient_id, $encounter_id);
1116 echo xlt('Previous Balance') . ' ';
1117 echo "<input type='text' value='" . attr(oeFormatMoney($prvbal)) . "' size='6' ";
1118 echo "style='text-align:right;background-color:transparent' readonly />\n";
1120 echo " <input type='button' value='" . xla('Pay Previous Balance') .
1121 "' onclick='payprevious()' />\n";
1130 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1131 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1) + count($taxes); ?>' class='bold'>
1133 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1); ?>' class='bold'>
1137 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { ?
>
1138 <td align
='right' class='bold' nowrap
>
1139 <?php
echo xlt('Default Customer'); ?
>
1142 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
1143 <td align
='right' class='bold' nowrap
>
1144 <?php
echo xlt('Default Adjust Type'); ?
>
1150 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1153 <td colspan
='<?php echo 1 + count($taxes); ?>' class='bold'>
1160 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1161 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1) + count($taxes); ?>' class='title'>
1163 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1); ?>' class='title'>
1165 <?php
echo xlt('Current Charges'); ?
>
1167 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { // charge category default ?>
1168 <td align
='right' class='bold'>
1169 <?php
echo generate_select_list('form_charge_category', 'chargecats', '', '', ' ', '', 'chargeCategoryChanged();'); ?
>
1172 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { // adjustmenty reason default ?>
1173 <td align
='right' class='bold'>
1174 <?php
echo generate_select_list('form_discount_type', 'adjreason', '', '', ' ', '', 'discountTypeChanged();billingChanged();'); ?
>
1180 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1183 <td colspan
='<?php echo 1 + count($taxes); ?>' class='bold'>
1190 <td
class='bold'><?php
echo xlt('Date'); ?
></td
>
1191 <td
class='bold'><?php
echo xlt('Description'); ?
></td
>
1192 <td align
='right' class='bold'><?php
echo xlt('Quantity'); ?
></td
>
1193 <?php
if (empty($GLOBALS['gbl_checkout_charges'])) { // if no charges column ?>
1194 <td align
='right' class='bold'><?php
echo xlt('Charge'); ?
></td
>
1195 <?php
} else { // charges column needed ?>
1196 <td align
='right' class='bold'><?php
echo xlt('Price'); ?
></td
>
1197 <td align
='right' class='bold'><?php
echo xlt('Charge'); ?
></td
>
1200 if (!$TAXES_AFTER_ADJUSTMENT) {
1201 foreach ($taxes as $taxarr) {
1202 echo " <td align='right' class='bold'>" . text($taxarr[0]) . "</td>";
1206 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { // charge category ?>
1207 <td align
='right' class='bold'><?php
echo xlt('Customer'); ?
></td
>
1209 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
1210 <td align
='right' class='bold'><?php
echo xlt('Adjust Type'); ?
></td
>
1211 <td align
='right' class='bold'><?php
echo xlt('Adj'); ?
></td
>
1214 if ($TAXES_AFTER_ADJUSTMENT) {
1215 foreach ($taxes as $taxarr) {
1216 echo " <td align='right' class='bold'>" . text($taxarr[0]) . "</td>";
1220 <td align
='right' class='bold'><?php
echo xlt('Total'); ?
></td
>
1225 // Function to output a line item for the input form.
1227 $totalchg = 0; // totals charges after adjustments
1228 function write_form_line(
1240 global $lino, $totalchg, $aAdjusts, $taxes, $encounter_date, $TAXES_AFTER_ADJUSTMENT;
1242 // Write heading rows if that is not already done.
1243 write_form_headers();
1244 $amount = formatMoneyNumber($amount);
1245 if (empty($units)) {
1248 $price = formatMoneyNumber($amount / $units, 2); // should be even cents, but...
1249 if (substr($price, -2) === '00') {
1250 $price = formatMoneyNumber($price);
1253 // Total and clear adjustments in aAdjusts matching this line item. Should only
1254 // happen for billed items, and matching includes the billing timestamp in order
1255 // to handle the case of multiple checkouts.
1257 $adjust = pull_adjustment($code_type, $code, $billtime, $memo);
1258 $total = formatMoneyNumber($amount - $adjust);
1259 if (empty($GLOBALS['discount_by_money'])) {
1260 // Convert $adjust to a percentage of the amount, up to 4 decimal places.
1261 $adjust = round(100 * $adjust / $amount, 4);
1264 // Compute the string of numeric tax rates to store with the charge line.
1266 $arates = explode(':', $taxrates);
1267 foreach ($taxes as $taxid => $taxarr) {
1269 if (empty($arates) ||
!in_array($taxid, $arates)) {
1272 $taxnumrates .= $rate . ':';
1276 echo " <td class='text'>" . text(oeFormatShortDate($encounter_date));
1277 echo "<input type='hidden' name='line[$lino][code_type]' value='" . attr($code_type) . "'>";
1278 echo "<input type='hidden' name='line[$lino][code]' value='" . attr($code) . "'>";
1279 echo "<input type='hidden' name='line[$lino][id]' value='" . attr($id) . "'>";
1280 echo "<input type='hidden' name='line[$lino][description]' value='" . attr($description) . "'>";
1281 // String of numeric tax rates is written here as a form field only for JavaScript tax computations.
1282 echo "<input type='hidden' name='line[$lino][taxnumrates]' value='" . attr($taxnumrates) . "'>";
1283 echo "<input type='hidden' name='line[$lino][units]' value='" . attr($units) . "'>";
1284 // Indicator of whether and when this line item was previously billed:
1285 echo "<input type='hidden' name='line[$lino][billtime]' value='" . attr($billtime) . "'>";
1287 echo " <td class='text'>" . text($description) . "</td>";
1288 echo " <td class='text' align='right'>" . text($units) . "</td>";
1290 if (empty($GLOBALS['gbl_checkout_charges'])) {
1291 // We show only total charges here.
1292 echo " <td class='text' align='right'>";
1293 echo "<input type='hidden' name='line[$lino][price]' value='" . attr($price) . "'>";
1294 echo "<input type='text' name='line[$lino][charge]' value='" . attr($amount) . "' size='6'";
1295 echo " style='text-align:right;background-color:transparent' readonly />";
1298 // In this case show price and extended charge amount.
1299 echo " <td class='text' align='right'>";
1300 echo "<input type='text' name='line[$lino][price]' value='" . attr($price) . "' size='6'";
1301 echo " style='text-align:right;background-color:transparent' readonly />";
1303 echo " <td class='text' align='right'>";
1304 echo "<input type='text' name='line[$lino][charge]' value='" . attr($amount) . "' size='6'";
1305 echo " style='text-align:right;background-color:transparent' readonly />";
1309 // Match up (and delete) entries in $aInvTaxes with the line.
1310 $lineid = $code_type == 'PROD' ?
"P:$id" : "S:$id";
1312 pull_tax($lineid, $aTaxes); // fills in $aTaxes
1314 if (!$TAXES_AFTER_ADJUSTMENT) {
1315 // A tax column for each tax. JavaScript will compute the amounts and
1316 // account for how the discount affects them.
1318 foreach ($taxes as $taxid => $dummy) {
1319 echo " <td class='text' align='right'>";
1320 echo "<input type='text' name='line[$lino][tax][$i]' size='6'";
1321 // Set tax amounts for existing billed items. JS must not recompute those.
1322 echo " value='" . attr(formatMoneyNumber($aTaxes[$taxid])) . "'";
1323 echo " style='text-align:right;background-color:transparent' readonly />";
1329 // Optional Charge Category.
1330 if (!empty($GLOBALS['gbl_charge_categories'])) {
1331 echo " <td class='text' align='right'>";
1332 echo generate_select_list(
1333 "line[$lino][chargecat]",
1341 $billtime ?
array('disabled' => 'disabled') : null
1346 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
1347 echo " <td class='text' align='right'>";
1348 echo generate_select_list(
1349 "line[$lino][memo]",
1357 $billtime ?
array('disabled' => 'disabled') : null
1360 echo " <td class='text' align='right' nowrap>";
1361 echo empty($GLOBALS['discount_by_money']) ?
'' : text($GLOBALS['gbl_currency_symbol']);
1362 echo "<input type='text' name='line[$lino][adjust]' size='6'";
1363 echo " value='" . attr(formatMoneyNumber($adjust)) . "'";
1364 // Modifying discount requires the acct/disc permission.
1365 if ($billtime ||
$code_type == 'TAX' ||
$code_type == 'COPAY' ||
!AclMain
::aclCheckCore('acct', 'disc')) {
1366 echo " style='text-align:right;background-color:transparent' readonly";
1368 echo " style='text-align:right' maxlength='8' onkeyup='lineDiscountChanged($lino)'";
1371 echo empty($GLOBALS['discount_by_money']) ?
'%' : '';
1375 if ($TAXES_AFTER_ADJUSTMENT) {
1376 // A tax column for each tax. JavaScript will compute the amounts and
1377 // account for how the discount affects them.
1379 foreach ($taxes as $taxid => $dummy) {
1380 echo " <td class='text' align='right'>";
1381 echo "<input type='text' name='line[$lino][tax][$i]' size='6'";
1382 // Set tax amounts for existing billed items. JS must not recompute those.
1383 echo " value='" . attr(formatMoneyNumber($aTaxes[$taxid])) . "'";
1384 echo " style='text-align:right;background-color:transparent' readonly />";
1390 // Extended amount after adjustments and taxes.
1391 echo " <td class='text' align='right'>";
1392 echo "<input type='text' name='line[$lino][amount]' value='" . attr($total) . "' size='6'";
1393 echo " style='text-align:right;background-color:transparent' readonly />";
1398 $totalchg +
= $amount;
1401 // Function to output a past payment/adjustment line to the form.
1403 function write_old_payment_line($pay_type, $date, $method, $reference, $amount)
1405 global $lino, $taxes, $num_optional_columns;
1406 global $form_num_type_columns, $form_num_method_columns, $form_num_ref_columns, $form_num_amount_columns;
1407 // Write heading rows if that is not already done.
1408 write_form_headers();
1409 $amount = formatMoneyNumber($amount);
1411 echo " <td class='text' colspan='$form_num_type_columns'>" . text($pay_type) . "</td>\n";
1412 echo " <td class='text' colspan='$form_num_method_columns'>" . text($method) . "</td>\n";
1413 echo " <td class='text' colspan='$form_num_ref_columns'>" . text($reference) . "</td>\n";
1414 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='oldpay[$lino][amount]' " .
1415 "value='$amount' size='6' maxlength='8'";
1416 echo " style='text-align:right;background-color:transparent' readonly";
1422 // Mark the tax rates that are referenced in this invoice.
1423 function markTaxes($taxrates)
1426 $arates = explode(':', $taxrates);
1427 if (empty($arates)) {
1430 foreach ($arates as $value) {
1431 if (!empty($taxes[$value])) {
1432 $taxes[$value][2] = '1';
1437 // Create the taxes array. Key is tax id, value is
1438 // (description, rate, indicator). Indicator seems to be unused.
1440 $pres = sqlStatement(
1441 "SELECT option_id, title, option_value " .
1442 "FROM list_options WHERE list_id = 'taxrate' AND activity = 1 ORDER BY seq, title, option_id"
1444 while ($prow = sqlFetchArray($pres)) {
1445 $taxes[$prow['option_id']] = array($prow['title'], $prow['option_value'], 0);
1448 // Array of HTML for the 4 or 5 cells of an input payment row.
1449 // "%d" will be replaced by a payment line number on the client side.
1451 $aCellHTML = array();
1452 $aCellHTML[] = "<span id='paytitle_%d'>" . text(xl('New Payment')) . "</span>";
1453 $aCellHTML[] = strtr(generate_select_list('payment[%d][method]', 'paymethod', '', '', ''), array("\n" => ""));
1454 $aCellHTML[] = "<input type='text' name='payment[%d][refno]' size='10' />";
1455 $aCellHTML[] = "<input type='text' name='payment[%d][amount]' size='6' style='text-align:right' onkeyup='setComputedValues()' />";
1457 $alertmsg = ''; // anything here pops up in an alert box
1459 // Make sure we have the encounter ID applicable to this request.
1460 if (!empty($_POST['form_save'])) {
1461 $patient_id = 0 +
$_POST['form_pid'];
1462 $encounter_id = 0 +
$_POST['form_encounter'];
1464 foreach (array('regen', 'enc', 'void', 'voidall') as $key) {
1465 if (!empty($_GET[$key])) {
1466 $encounter_id = 0 +
$_GET[$key];
1472 // Compute and validate the checksum.
1473 $current_checksum = 0;
1474 if ($patient_id && $encounter_id) {
1475 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1476 if (!empty($_REQUEST['form_checksum'])) {
1477 if ($_REQUEST['form_checksum'] != $current_checksum) {
1478 $alertmsg = xl('Someone else has just changed this visit. Please cancel this page and try again.');
1483 // If the Save button was clicked...
1485 if (!empty($_POST['form_save']) && !$alertmsg) {
1486 if (!CsrfUtils
::verifyCsrfToken($_POST["csrf_token_form"])) {
1487 CsrfUtils
::csrfNotVerified();
1490 // On a save, do the following:
1491 // Flag this form's drug_sales and billing items as billed.
1492 // Post line-level adjustments, replacing any existing ones for the same charges.
1493 // Post any invoice-level adjustment.
1494 // Post payments and be careful to use a unique invoice number.
1495 // Call the generate-receipt function.
1498 // A current invoice reference number may be present if there was a previous checkout.
1500 "SELECT invoice_refno FROM form_encounter WHERE " .
1501 "pid = ? AND encounter = ?",
1502 array($patient_id, $encounter_id)
1504 $current_irnumber = $tmprow['invoice_refno'];
1506 // Get the posting date from the form as yyyy-mm-dd.
1507 $postdate = substr($this_bill_date, 0, 10);
1508 if (preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $_POST['form_date'], $matches)) {
1509 $postdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
1511 $dosdate = $postdate; // not sure if this is appropriate
1513 if (! $encounter_id) {
1514 die("Internal error: Encounter ID is missing!");
1517 // Delete unbilled TAX rows from billing because they will be recalculated.
1518 // Do not delete already-billed taxes; we must not touch billed stuff.
1520 "UPDATE billing SET activity = 0 WHERE " .
1521 "pid = ? AND encounter = ? AND " .
1522 "code_type = 'TAX' AND billed = 0 AND activity = 1",
1523 array($patient_id, $encounter_id)
1526 $form_amount = $_POST['form_totalpay'];
1527 $lines = $_POST['line'];
1529 for ($lino = 0; !empty($lines[$lino]['code_type']); ++
$lino) {
1530 $line = $lines[$lino];
1531 $code_type = $line['code_type'];
1532 $code = $line['code'];
1534 $chargecat = $line['chargecat'] ??
'';
1535 $amount = formatMoneyNumber(trim($line['amount']));
1538 // Skip saving taxes and adjustments for billed items.
1539 if (!empty($line['billtime'])) {
1543 // Insert any taxes for this line.
1544 // There's a chance of input data and the $taxes array being out of sync if someone
1545 // updates the taxrate list during data entry... we oughta do something about that.
1546 if (is_array($line['tax'])) {
1547 // For tax rows the ndc_info field is used to identify the charge item that is taxed.
1548 // P indicates drug_sales.sale_id, S indicates billing.id.
1549 $ndc_info = $code_type == 'PROD' ?
"P:$id" : "S:$id";
1551 foreach ($taxes as $taxid => $taxarr) {
1552 $taxamount = $line['tax'][$i++
] +
0;
1553 if ($taxamount != 0) {
1554 BillingUtilities
::addBilling(
1569 // billed=0 because we will set billed and bill_date for unbilled items below.
1570 $linetax +
= $taxamount;
1575 // If there is an adjustment for this line, insert it.
1576 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
1577 $adjust = 0.00 +
trim($line['adjust']);
1578 $memo = formDataCore($line['memo']);
1579 if ($adjust != 0 ||
$memo !== '') {
1580 // $memo = xl('Discount');
1582 $memo = formData('form_discount_type');
1585 $sequence_no = sqlQuery(
1586 "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
1587 "pid = ? AND encounter = ?",
1588 array($patient_id, $encounter_id)
1590 $query = "INSERT INTO ar_activity ( " .
1591 "pid, encounter, sequence_no, code_type, code, modifier, payer_type, " .
1592 "post_user, post_time, post_date, session_id, memo, adj_amount " .
1594 "?, ?, ?, ?, ?, '', '0', ?, ?, ?, '0', ?, ? " .
1596 sqlStatement($query, array(
1599 $sequence_no['increment'],
1602 $_SESSION['authUserID'],
1612 if (!empty($GLOBALS['gbl_charge_categories'])) {
1613 // Update charge category for this line item.
1614 if ($code_type == 'PROD') {
1615 $query = "UPDATE drug_sales SET chargecat = ? WHERE sale_id = ?";
1616 sqlQuery($query, array($chargecat, $id));
1618 $query = "UPDATE billing SET chargecat = ? WHERE id = ?";
1619 sqlQuery($query, array($chargecat, $id));
1624 // Flag the encounter as billed.
1625 $query = "UPDATE billing SET billed = 1, bill_date = ? WHERE " .
1626 "pid = ? AND encounter = ? AND activity = 1 AND billed = 0";
1627 sqlQuery($query, array($this_bill_date, $patient_id, $encounter_id));
1628 $query = "update drug_sales SET billed = 1, bill_date = ? WHERE " .
1629 "pid = ? AND encounter = ? AND billed = 0";
1630 sqlQuery($query, array($this_bill_date, $patient_id, $encounter_id));
1633 if ($_POST['form_discount'] != 0) {
1634 if ($GLOBALS['discount_by_money']) {
1635 $amount = formatMoneyNumber(trim($_POST['form_discount']));
1637 $amount = formatMoneyNumber(trim($_POST['form_discount']) * $form_amount / 100);
1639 $memo = formData('form_discount_type');
1641 $sequence_no = sqlQuery(
1642 "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
1643 "pid = ? AND encounter = ?",
1644 array($patient_id, $encounter_id)
1646 $query = "INSERT INTO ar_activity ( " .
1647 "pid, encounter, sequence_no, code, modifier, payer_type, post_user, post_time, " .
1648 "post_date, session_id, memo, adj_amount " .
1650 "?, ?, ?, '', '', '0', ?, ?, ?, '0', ?, ? " .
1652 sqlStatement($query, array(
1655 $sequence_no['increment'],
1656 $_SESSION['authUserID'],
1665 // Post the payments.
1666 if (is_array($_POST['payment'])) {
1667 $lines = $_POST['payment'];
1668 for ($lino = 0; isset($lines[$lino]['amount']); ++
$lino) {
1669 $line = $lines[$lino];
1670 $amount = formatMoneyNumber(trim($line['amount']));
1671 if ($amount != 0.00) {
1672 $method = $line['method'];
1673 $refno = $line['refno'];
1674 if ($method !== '' && $refno !== '') {
1675 $method .= " $refno";
1677 $session_id = 0; // Is this OK?
1678 SLEOB
::arPostPayment(
1695 // If applicable, set the invoice reference number.
1696 if (!$current_irnumber) {
1697 $invoice_refno = '';
1698 if (isset($_POST['form_irnumber'])) {
1699 $invoice_refno = formData('form_irnumber', 'P', true);
1701 $invoice_refno = add_escape_custom(BillingUtilities
::updateInvoiceRefNumber());
1703 if ($invoice_refno) {
1705 "UPDATE form_encounter SET invoice_refno = ? WHERE pid = ? AND encounter = ?",
1706 array($invoice_refno, $patient_id, $encounter_id)
1711 // If appropriate, update the status of the related appointment to
1713 updateAppointmentStatus($patient_id, $dosdate, '>');
1715 generate_receipt($patient_id, $encounter_id);
1720 $form_reason = empty($_GET['form_reason']) ?
'' : $_GET['form_reason'];
1721 $form_notes = empty($_GET['form_notes' ]) ?
'' : $_GET['form_notes'];
1723 // If "regen" encounter ID was given, then we must generate a new receipt ID.
1725 if (!$alertmsg && $patient_id && !empty($_GET['regen'])) {
1726 BillingUtilities
::doVoid(
1734 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1735 $_GET['enc'] = $encounter_id;
1738 // If "enc" encounter ID was given, then we must generate a receipt and exit.
1740 if ($patient_id && !empty($_GET['enc'])) {
1741 if (empty($_GET['pdf'])) {
1742 generate_receipt($patient_id, $_GET['enc']);
1744 // PDF receipt is requested. In this case we are probably in a new window.
1745 require_once($GLOBALS['OE_SITE_DIR'] . "/" . $GLOBALS['gbl_custom_receipt']);
1746 // $checkout_id is an optional specified checkout timestamp.
1747 $billtime = $checkout_id;
1749 // No timestamp specified so use the last one.
1750 $checkout_times = craGetTimestamps($patient_id, $_GET['enc']);
1751 $billtime = empty($checkout_times) ?
'' : $checkout_times[count($checkout_times) - 1];
1753 generateCheckoutReceipt($patient_id, $_GET['enc'], $billtime);
1758 // If "void" encounter ID was given, then we must undo the last checkout.
1759 // Or for "voidall" undo all checkouts for the encounter.
1761 if (!$alertmsg && $patient_id && !empty($_GET['void'])) {
1762 BillingUtilities
::doVoid($patient_id, $encounter_id, true, '', $form_reason, $form_notes);
1763 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1764 } else if (!$alertmsg && $patient_id && !empty($_GET['voidall'])) {
1765 BillingUtilities
::doVoid($patient_id, $encounter_id, true, 'all', $form_reason, $form_notes);
1766 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1769 // Get the specified or first unbilled encounter ID for this patient.
1771 if (!$encounter_id) {
1772 $query = "SELECT encounter FROM billing WHERE " .
1773 "pid = ? AND activity = 1 AND billed = 0 AND code_type != 'TAX' " .
1774 "ORDER BY encounter DESC LIMIT 1";
1775 $brow = sqlQuery($query, array($patient_id));
1776 $query = "SELECT encounter FROM drug_sales WHERE " .
1777 "pid = ? AND billed = 0 " .
1778 "ORDER BY encounter DESC LIMIT 1";
1779 $drow = sqlQuery($query, array($patient_id));
1780 if (!empty($brow['encounter'])) {
1781 if (!empty($drow['encounter'])) {
1782 $encounter_id = min(intval($brow['encounter']), intval($drow['encounter']));
1784 $encounter_id = $brow['encounter'];
1786 } else if (!empty($drow['encounter'])) {
1787 $encounter_id = $drow['encounter'];
1791 // If there are none, just redisplay the last receipt and exit.
1793 if (!$encounter_id) {
1794 generate_receipt($patient_id);
1798 // Form requires billing permission.
1799 if (!AclMain
::aclCheckCore('admin', 'super') && !AclMain
::aclCheckCore('acct', 'bill')) {
1800 die("Not authorized!");
1803 // We have $patient_id and $encounter_id. Generate checksum if not already done.
1804 if (!$current_checksum) {
1805 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1808 // Get the valid practitioners, including those not active.
1809 $arr_users = array();
1810 $ures = sqlStatement(
1811 "SELECT id, username FROM users WHERE " .
1812 "( authorized = 1 OR info LIKE '%provider%' ) AND username != ''"
1814 while ($urow = sqlFetchArray($ures)) {
1815 $arr_users[$urow['id']] = '1';
1818 // Now write a data entry form:
1819 // List unbilled billing items (cpt, hcpcs, copays) for the patient.
1820 // List unbilled product sales for the patient.
1821 // Present an editable dollar amount for each line item, a total
1822 // which is also the default value of the input payment amount,
1823 // and OK and Cancel buttons.
1828 <?php Header
::setupHeader(['datetime-picker']);?
>
1830 <title
><?php
echo xlt('Patient Checkout'); ?
></title
>
1833 @media
(min
-width
: 992px
){
1835 width
: 1000px
!Important
;
1841 var mypcc
= <?php
echo js_escape($GLOBALS['phone_country_code']); ?
>;
1843 <?php
require($GLOBALS['srcdir'] . "/restoreSession.php"); ?
>
1845 // This clears tax amounts in preparation for recomputing taxes.
1846 // TBD: Probably don't need this at all.
1847 function clearTax(visible
) {
1848 var f
= document
.forms
[0];
1849 for (var i
= 0; f
['totaltax[' + i +
']']; ++i
) {
1850 f
['totaltax[' + i +
']'].value
= '0.00';
1854 // This computes taxes and extended amount for the specified line, and returns
1855 // the extended amount.
1856 function calcTax(lino
) {
1857 var f
= document
.forms
[0];
1858 var pfx
= 'line[' + lino +
']';
1859 var taxable
= parseFloat(f
[pfx +
'[charge]'].value
);
1861 if (f
[pfx +
'[adjust]']) {
1862 adjust
= parseFloat(f
[pfx +
'[adjust]'].value
);
1865 if (f
[pfx +
'[memo]']) {
1866 adjreason
= f
[pfx +
'[memo]'].value
;
1868 var extended
= taxable
- adjust
;
1871 // Generate JavaScript that checks if the chosen adjustment type is to be
1872 // applied before taxes are computed. option_value 1 indicates that the
1873 // "After Taxes" checkbox is checked for an adjustment type.
1874 $tmpres = sqlStatement(
1875 "SELECT option_id FROM list_options WHERE " .
1876 "list_id = 'adjreason' AND option_value = 1 AND activity = 1"
1878 while ($tmprow = sqlFetchArray($tmpres)) {
1879 echo " && adjreason != " . js_escape($tmprow['option_id']) . "\n";
1885 var taxnumrates
= f
[pfx +
'[taxnumrates]'].value
;
1886 var rates
= taxnumrates
.split(':');
1887 for (var i
= 0; i
< rates
.length
; ++i
) {
1888 if (! f
[pfx +
'[tax][' + i +
']']) {
1892 if (f
[pfx +
'[billtime]'].value
) {
1893 // Line item is billed, use the tax amounts that were previously set for it.
1894 tax
= parseFloat(f
[pfx +
'[tax][' + i +
']'].value
);
1897 tax
= taxable
* parseFloat(rates
[i
]);
1898 tax
= parseFloat(tax
.toFixed(<?php
echo $currdecimals ?
>));
1900 alert('Tax rate not numeric at line ' + lino
);
1902 f
[pfx +
'[tax][' + i +
']'].value
= tax
.toFixed(<?php
echo $currdecimals ?
>);
1905 var totaltax
= parseFloat(f
['totaltax[' + i +
']'].value
) + tax
;
1906 f
['totaltax[' + i +
']'].value
= totaltax
.toFixed(<?php
echo $currdecimals ?
>);
1908 f
[pfx +
'[amount]'].value
= extended
.toFixed(<?php
echo $currdecimals ?
>);
1912 // This mess recomputes total charges and optionally applies a discount.
1913 // As part of this, taxes and extended amount are recomputed for each line.
1914 function computeDiscountedTotals(discount
, visible
) {
1916 var f
= document
.forms
[0];
1918 for (var lino
= 0; f
['line[' + lino +
'][code_type]']; ++lino
) {
1919 var code_type
= f
['line[' + lino +
'][code_type]'].value
;
1920 // price is price per unit when the form was originally generated.
1921 // By contrast, amount is the dynamically-generated discounted line total.
1922 var price
= parseFloat(f
['line[' + lino +
'][price]'].value
);
1924 alert('Price not numeric at line ' + lino
);
1926 if (code_type
== 'COPAY' || code_type
== 'TAX') {
1927 // I think this case is obsolete now.
1928 total +
= parseFloat(price
.toFixed(<?php
echo $currdecimals ?
>));
1931 // Compute and set taxes and extended amount for the given line.
1932 // This also returns the extended amount.
1933 total +
= calcTax(lino
);
1936 f
.totalchg
.value
= total
.toFixed(<?php
echo $currdecimals ?
>);
1938 return total
- discount
;
1941 // This computes and returns the total of payments.
1942 function computePaymentTotal() {
1943 var f
= document
.forms
[0];
1945 for (var lino
= 0; ('oldpay[' + lino +
'][amount]') in f
; ++lino
) {
1946 var amount
= parseFloat(f
['oldpay[' + lino +
'][amount]'].value
);
1947 if (isNaN(amount
)) {
1950 amount
= parseFloat(amount
.toFixed(<?php
echo $currdecimals ?
>));
1953 for (var lino
= 0; ('payment[' + lino +
'][amount]') in f
; ++lino
) {
1954 var amount
= parseFloat(f
['payment[' + lino +
'][amount]'].value
);
1955 if (isNaN(amount
)) {
1956 amount
= parseFloat(0);
1958 amount
= parseFloat(amount
.toFixed(<?php
echo $currdecimals ?
>));
1960 // Set payment row's description to Refund if the amount is negative.
1961 var title
= amount
< 0 ?
<?php
echo xlj('Refund'); ?
> : <?php
echo xlj('New payment'); ?
>;
1962 var span
= document
.getElementById('paytitle_' + lino
);
1963 span
.innerHTML
= title
;
1968 // Recompute default payment amount with any discount applied, but
1969 // not if there is more than one input payment line.
1970 // This is called when the discount amount is changed, and initially.
1971 // As a side effect the tax line items are recomputed and
1972 // setComputedValues() is called.
1973 function billingChanged() {
1974 var f
= document
.forms
[0];
1975 var discount
= parseFloat(f
.form_discount
.value
);
1976 if (isNaN(discount
)) {
1979 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
1980 // This site discounts by percentage, so convert it to a money amount.
1981 if (discount
> 100) {
1984 if (discount
< 0 ) {
1987 discount
= 0.01 * discount
* computeDiscountedTotals(0, false);
1989 var total
= computeDiscountedTotals(discount
, true);
1990 // Get out if there is more than one input payment line.
1991 if (!('payment[1][amount]' in f
)) {
1992 f
['payment[0][amount]'].value
= 0;
1993 total
-= computePaymentTotal();
1994 f
['payment[0][amount]'].value
= total
.toFixed(<?php
echo $currdecimals ?
>);
1996 setComputedValues();
2000 // Function to return the adjustment type, if any, identified in a customer's Notes field.
2001 function adjTypeFromCustomer(customer
) {
2004 $tmpres = sqlStatement(
2005 "SELECT option_id, notes FROM list_options WHERE " .
2006 "list_id = 'chargecats' AND activity = 1"
2008 while ($tmprow = sqlFetchArray($tmpres)) {
2010 preg_match('/ADJ=(\w+)/', $tmprow['notes'], $matches) ||
2011 preg_match('/ADJ="(.*?)"/', $tmprow['notes'], $matches)
2013 echo " if (customer == " . js_escape($tmprow['option_id']) . ") ret = " . js_escape($matches[1]) . ";\n";
2020 // A line item adjustment was changed, so recompute stuff.
2021 function lineDiscountChanged(lino
) {
2022 var f
= document
.forms
[0];
2023 var discount
= parseFloat(f
['line[' + lino +
'][adjust]'].value
);
2024 if (isNaN(discount
)) {
2027 var charge
= parseFloat(f
['line[' + lino +
'][charge]'].value
);
2028 if (isNaN(charge
)) {
2031 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2032 // This site discounts by percentage, so convert it to a money amount.
2033 if (discount
> 100) {
2036 if (discount
< 0 ) {
2039 discount
= 0.01 * discount
* charge
;
2041 var amount
= charge
- discount
;
2042 f
['line[' + lino +
'][amount]'].value
= amount
.toFixed(<?php
echo $currdecimals ?
>);
2043 // alert(f['line[' + lino + '][amount]'].value); // debugging
2045 // Apply default adjustment type if one is specified in the customer (charge category).
2046 var custElem
= f
['line[' + lino +
'][chargecat]'];
2047 var adjtElem
= f
['line[' + lino +
'][memo]'];
2048 if (custElem
&& custElem
.value
&& adjtElem
&& !adjtElem
.value
) {
2049 var ccAdjType
= adjTypeFromCustomer(custElem
.value
);
2051 adjtElem
.value
= ccAdjType
;
2055 return billingChanged();
2058 // Set Total Payments, Difference and Balance Due when any amount changes.
2059 function setComputedValues() {
2060 var f
= document
.forms
[0];
2061 var payment
= computePaymentTotal();
2062 var difference
= computeDiscountedTotals(0, false) - payment
;
2063 var discount
= parseFloat(f
.form_discount
.value
);
2064 if (isNaN(discount
)) {
2067 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2068 // This site discounts by percentage, so convert it to a money amount.
2069 if (discount
> 100) {
2072 if (discount
< 0 ) {
2075 discount
= 0.01 * discount
* computeDiscountedTotals(0, false);
2077 var balance
= difference
- discount
;
2078 f
.form_totalpay
.value
= payment
.toFixed(<?php
echo $currdecimals ?
>);
2079 f
.form_difference
.value
= difference
.toFixed(<?php
echo $currdecimals ?
>);
2080 f
.form_balancedue
.value
= balance
.toFixed(<?php
echo $currdecimals ?
>);
2084 // This is called when [Compute] is clicked by the user.
2085 // Computes and sets the discount value from total charges less payment.
2086 // This also calls setComputedValues() so the balance due will be correct.
2087 function computeDiscount() {
2088 var f
= document
.forms
[0];
2089 var charges
= computeDiscountedTotals(0, false);
2090 var payment
= computePaymentTotal();
2091 var discount
= charges
- payment
;
2092 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2093 // This site discounts by percentage, so convert to that.
2094 discount
= charges ?
(100 * discount
/ charges
) : 0;
2095 f
.form_discount
.value
= discount
.toFixed(4);
2097 f
.form_discount
.value
= discount
.toFixed(<?php
echo $currdecimals ?
>);
2099 setComputedValues();
2103 // When the main adjustment reason changes, duplicate it to all per-line reasons.
2104 function discountTypeChanged() {
2105 var f
= document
.forms
[0];
2106 if (f
.form_discount_type
&& f
.form_discount_type
.selectedIndex
) {
2107 for (lino
= 0; f
['line[' + lino +
'][memo]']; ++lino
) {
2108 // But do not change adjustment reason for billed items.
2109 if (f
['line[' + lino +
'][billtime]'].value
) {
2112 f
['line[' + lino +
'][memo]'].selectedIndex
= f
.form_discount_type
.selectedIndex
;
2117 // When the main charge category changes, duplicate it to all per-line categories.
2118 function chargeCategoryChanged() {
2119 var f
= document
.forms
[0];
2120 if (f
.form_charge_category
&& f
.form_charge_category
.selectedIndex
) {
2121 for (lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2122 // But do not change categories for billed items.
2123 if (f
['line[' + lino +
'][billtime]'].value
) {
2126 f
['line[' + lino +
'][chargecat]'].selectedIndex
= f
.form_charge_category
.selectedIndex
;
2131 // This is specific to IPPF and Suriname.
2132 function check_referrals() {
2133 <?php
if (!empty($GLOBALS['gbl_menu_surinam_insurance'])) { ?
>
2135 var f
= document
.forms
[0];
2136 var services_needing_referral
= '';
2137 for (var lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2138 var pfx
= 'line[' + lino +
']';
2139 var cust
= f
[pfx+
'[chargecat]'].value
;
2140 var price
= parseFloat(f
[pfx +
'[price]'].value
);
2144 if ((cust
== 'SZF' || cust
== 'KL-0098') && f
[pfx+
'[code_type]'].value
!= 'PROD' && price
!= 0) {
2145 services_needing_referral +
= f
[pfx+
'[code_type]'].value +
':' + f
[pfx+
'[code]'].value +
';';
2148 if (services_needing_referral
) {
2149 top
.restoreSession();
2152 async
: false, // We cannot continue without an answer.
2153 url
: "<?php echo $GLOBALS['webroot']; ?>/library/ajax/check_szf_referrals_ajax.php",
2155 "pid": <?php
echo intval($patient_id); ?
>,
2156 "encounter": <?php
echo intval($encounter_id); ?
>,
2157 "services": services_needing_referral
2159 success
: function (jsondata
, textstatus
) {
2160 msg
= jsondata
['message'];
2164 if (msg
&& !confirm(msg
)) {
2171 // This is specific to IPPF and the NetSuite project.
2172 function check_giftcards() {
2173 <?php
if (empty($GLOBALS['gbl_menu_netsuite'])) { ?
>
2176 var f
= document
.forms
[0];
2177 // If there is no gift card customer return true.
2178 var gc_customer_exists
= false;
2179 for (lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2180 var chargecat
= f
['line[' + lino +
'][chargecat]'].value
;
2184 $lres = sqlStatement(
2185 "SELECT option_id FROM list_options WHERE " .
2186 "list_id = 'chargecats' AND activity = 1 AND notes LIKE '%GIFTCARD=Y%' ORDER BY seq, title"
2188 while ($lrow = sqlFetchArray($lres)) {
2189 echo " || chargecat == " . js_escape($lrow['option_id']) . "\n";
2193 gc_customer_exists
= true;
2196 if (!gc_customer_exists
) {
2199 // If there is a gift card payment method in the form return true.
2200 for (lino
= 0; f
['payment[' + lino +
'][method]']; ++lino
) {
2201 var method
= f
['payment[' + lino +
'][method]'].value
;
2205 $lres = sqlStatement(
2206 "SELECT option_id FROM list_options WHERE " .
2207 "list_id = 'paymethod' AND activity = 1 AND notes LIKE '%GIFTCARD=Y%' ORDER BY seq, title"
2209 while ($lrow = sqlFetchArray($lres)) {
2210 echo " || method == " . js_escape($lrow['option_id']) . "\n";
2214 if (!f
['payment[' + lino +
'][refno]'].value
) {
2215 // There is a gift card payment method but no gift card ID is entered.
2216 alert(<?php
echo xlj("Enter Gift Card number in the Reference field"); ?
>);
2219 return true; // There is a gift card payment method or no such methods exist.
2222 // There is a gift card customer but no gift card payment method.
2223 alert(<?php
echo xlj("Gift Card Customer requires at least one Gift Card Payment Method"); ?
>);
2228 function validate() {
2229 var f
= document
.forms
[0];
2230 var missingtypeamt
= false;
2231 var missingtypeany
= false;
2232 for (lino
= 0; f
['line[' + lino +
'][memo]']; ++lino
) {
2233 if (f
['line[' + lino +
'][memo]'].selectedIndex
== 0 && f
['line[' + lino +
'][billtime]'].value
== '') {
2234 missingtypeany
= true;
2235 if (parseFloat(f
['line[' + lino +
'][adjust]'].value
) != 0) {
2236 missingtypeamt
= true;
2240 <?php
if (false /* adjustments_indicate_insurance */) { ?
>
2241 if (missingtypeany
) {
2242 alert(<?php
echo xlj('Adjustment type is required for every line item.') ?
>);
2246 if (missingtypeamt
) {
2247 alert(<?php
echo xlj('Adjustment type is required for each line with an adjustment.') ?
>);
2251 if (!check_referrals()) {
2254 if (!check_giftcards()) {
2257 top
.restoreSession();
2263 // TBD: Not sure this will be used here.
2264 $arrOeUiSettings = array(
2265 'heading_title' => xl('Patient Checkout'),
2266 'include_patient_name' => true,// use only in appropriate pages
2267 'expandable' => false,
2268 'expandable_files' => array(),//all file names need suffix _xpd
2269 'action' => "",//conceal, reveal, search, reset, link or back
2270 'action_title' => "",
2271 'action_href' => "",//only for actions - reset, link or back
2272 'show_help_icon' => false,
2273 'help_file_name' => ""
2275 $oemr_ui = new OemrUI($arrOeUiSettings);
2282 echo "<form method='post' action='pos_checkout.php?rde=" . attr_url($rapid_data_entry);
2283 if ($encounter_id) {
2284 echo "&enid=" . attr_url($encounter_id);
2286 if (!empty($_GET['framed'])) {
2289 echo "' onsubmit='return validate()'>\n";
2290 echo "<input type='hidden' name='form_pid' value='" . attr($patient_id) . "' />\n";
2292 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
2297 <table cellspacing
='5' id
='paytable' width
='85%'>
2302 $gcac_related_visit = false;
2303 $gcac_service_provided = false;
2305 // This is set by write_form_headers() when the encounter is known.
2306 $encounter_date = '';
2308 // This to save copays from the billing table.
2313 $query = "SELECT id, date, code_type, code, modifier, code_text, " .
2314 "provider_id, payer_id, units, fee, encounter, billed, bill_date, chargecat " .
2315 "FROM billing WHERE pid = ? AND encounter = ? AND activity = 1 AND " .
2316 "code_type != 'TAX' ORDER BY id ASC";
2317 $bres = sqlStatement($query, array($patient_id, $encounter_id));
2319 $query = "SELECT s.sale_id, s.sale_date, s.prescription_id, s.fee, s.quantity, " .
2320 "s.encounter, s.drug_id, s.billed, s.bill_date, s.selector, s.chargecat, d.name, r.provider_id " .
2321 "FROM drug_sales AS s " .
2322 "LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
2323 "LEFT OUTER JOIN prescriptions AS r ON r.id = s.prescription_id " .
2324 "WHERE s.pid = ? AND s.encounter = ? " .
2325 "ORDER BY s.sale_id ASC";
2326 $dres = sqlStatement($query, array($patient_id, $encounter_id));
2328 // Process billing table items. Note this includes co-pays.
2329 // Items that are not allowed to have a fee are skipped.
2331 while ($brow = sqlFetchArray($bres)) {
2332 $thisdate = substr($brow['date'], 0, 10);
2333 $code_type = $brow['code_type'];
2334 $inv_payer = $brow['payer_id'];
2335 if (!$inv_date ||
$inv_date < $thisdate) {
2336 $inv_date = $thisdate;
2338 // Co-pays are saved for later.
2339 if ($code_type == 'COPAY') {
2344 $billtime = $brow['billed'] ?
$brow['bill_date'] : '';
2346 // Collect tax rates, related code and provider ID.
2349 if (!empty($code_types[$code_type]['fee'])) {
2350 $query = "SELECT taxrates, related_code FROM codes WHERE code_type = ? AND " .
2352 $binds = array($code_types[$code_type]['id'], $brow['code']);
2353 if ($brow['modifier']) {
2354 $query .= "modifier = ?";
2355 $binds[] = $brow['modifier'];
2357 $query .= "(modifier IS NULL OR modifier = '')";
2359 $query .= " LIMIT 1";
2360 $tmp = sqlQuery($query, $binds);
2361 $taxrates = $tmp['taxrates'];
2362 $related_code = $tmp['related_code'];
2363 markTaxes($taxrates);
2366 // Write the line item if it allows fees or is not a diagnosis.
2367 if (!empty($code_types[$code_type]['fee']) ||
empty($code_types[$code_type]['diag'])) {
2373 ucfirst(strtolower($brow['code_text'])),
2382 // Custom logic for IPPF to determine if a GCAC issue applies.
2383 if ($GLOBALS['ippf_specific'] && $related_code) {
2384 $relcodes = explode(';', $related_code);
2385 foreach ($relcodes as $codestring) {
2386 if ($codestring === '') {
2389 list($codetype, $code) = explode(':', $codestring);
2390 if ($codetype !== 'IPPF2') {
2393 if (preg_match('/^211/', $code)) {
2394 $gcac_related_visit = true;
2396 preg_match('/^211313030110/', $code) // Medical
2397 ||
preg_match('/^211323030230/', $code) // Surgical
2398 ||
preg_match('/^211403030110/', $code) // Incomplete Medical
2399 ||
preg_match('/^211403030230/', $code) // Incomplete Surgical
2401 $gcac_service_provided = true;
2408 // Process drug sales / products.
2410 while ($drow = sqlFetchArray($dres)) {
2411 if ($encounter_id && $drow['encounter'] != $encounter_id) {
2414 $thisdate = $drow['sale_date'];
2415 if (!$encounter_id) {
2416 $encounter_id = $drow['encounter'];
2418 if (!$inv_provider && !empty($arr_users[$drow['provider_id']])) {
2419 $inv_provider = $drow['provider_id'] +
0;
2421 if (!$inv_date ||
$inv_date < $thisdate) {
2422 $inv_date = $thisdate;
2424 $billtime = $drow['billed'] ?
$drow['bill_date'] : '';
2426 // Accumulate taxes for this product.
2428 "SELECT taxrates FROM drug_templates WHERE drug_id = ? ORDER BY selector LIMIT 1",
2429 array($drow['drug_id'])
2431 $taxrates = $tmp['taxrates'];
2432 markTaxes($taxrates);
2434 $tmpname = $drow['name'];
2435 if ($tmpname !== $drow['selector']) {
2436 $tmpname .= ' / ' . $drow['selector'];
2438 $units = $drow['quantity'] / FeeSheet
::getBasicUnits($drow['drug_id'], $drow['selector']);
2454 // Line for total charges.
2455 $totalchg = formatMoneyNumber($totalchg);
2457 echo " <td class='bold' colspan='" . (!empty($GLOBALS['gbl_checkout_charges']) ?
4 : 3) .
2458 "' align='right'>" . xlt('Total Charges This Visit') . "</td>\n";
2459 echo " <td class='text' align='right'><input type='text' name='totalcba' " .
2460 "value='" . attr($totalchg) . "' size='6' maxlength='8' " .
2461 "style='text-align:right;background-color:transparent' readonly";
2463 if (!$TAXES_AFTER_ADJUSTMENT) {
2464 for ($i = 0; $i < count($taxes); ++
$i) {
2465 echo " <td class='text' align='right'><input type='text' name='totaltax[$i]' " .
2466 "value='0.00' size='6' maxlength='8' " .
2467 "style='text-align:right;background-color:transparent' readonly";
2471 if (!empty($GLOBALS['gbl_charge_categories'])) {
2472 echo " <td class='text' align='right'> </td>\n"; // Empty space in charge category column.
2474 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2475 // Note $totalchg is the total of charges before adjustments, and the following
2476 // field will be recomputed at onload time and as adjustments are entered.
2477 echo " <td class='text' align='right'> </td>\n"; // Empty space in adjustment type column.
2478 echo " <td class='text' align='right'> </td>\n"; // TBD: Total adjustments can go here.
2480 if ($TAXES_AFTER_ADJUSTMENT) {
2481 for ($i = 0; $i < count($taxes); ++
$i) {
2482 echo " <td class='text' align='right'><input type='text' name='totaltax[$i]' " .
2483 "value='0.00' size='6' maxlength='8' " .
2484 "style='text-align:right;background-color:transparent' readonly";
2488 echo " <td class='text' align='right'><input type='text' name='totalchg' " .
2489 "value='" . attr($totalchg) . "' size='6' maxlength='8' " .
2490 "style='text-align:right;background-color:transparent' readonly";
2496 <td
class='title' colspan
='<?php echo 5 + $num_optional_columns; ?>'
2497 style
='border-top:1px solid black; padding-top:5pt;'>
2498 <b
><?php
echo xlt('Payments'); ?
></b
>
2503 // Start new section for payments.
2504 echo " <td class='bold' colspan='$form_num_type_columns'>" . xlt('Type') . "</td>\n";
2505 echo " <td class='bold' colspan='$form_num_method_columns'>" . xlt('Payment Method') . "</td>\n";
2506 echo " <td class='bold' colspan='$form_num_ref_columns'>" . xlt('Reference') . "</td>\n";
2507 echo " <td class='bold' colspan='$form_num_amount_columns' align='right' nowrap>" . xlt('Payment Amount') . "</td>\n";
2513 foreach ($aCopays as $brow) {
2514 $thisdate = substr($brow['date'], 0, 10);
2515 write_old_payment_line(
2524 // Write any adjustments left in the aAdjusts array. This should only happen if
2525 // there was an invoice-level discount in a prior checkout of this encounter.
2526 foreach ($aAdjusts as $arow) {
2527 $memo = $arow['memotitle'];
2528 if ($arow['adj_amount'] == 0 && $memo === '') {
2531 $reference = $arow['reference'];
2532 write_old_payment_line(
2541 // Write ar_activity payments.
2542 $ares = sqlStatement(
2544 "a.payer_type, a.pay_amount, a.memo, s.session_id, s.reference, s.check_date " .
2545 "FROM ar_activity AS a " .
2546 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
2547 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND a.pay_amount != 0 " .
2548 "ORDER BY s.check_date, a.sequence_no",
2549 array($patient_id, $encounter_id)
2551 while ($arow = sqlFetchArray($ares)) {
2552 $memo = $arow['memo'];
2553 $reference = $arow['reference'];
2554 if (empty($arow['session_id'])) {
2555 $atmp = explode(' ', $memo, 2);
2557 $reference = $atmp[1];
2559 $rowtype = $arow['payer_type'] ?
xl('Insurance payment') : xl('Prepayment');
2560 write_old_payment_line(
2569 // Line for total payments.
2570 echo " <tr id='totalpay'>\n";
2571 echo " <td class='bold' colspan='$form_num_type_columns'><a href='#' onclick='return addPayLine()'>[" . xlt('Add Row') . "]</a></td>\n";
2572 echo " <td class='bold' colspan='" . ($form_num_method_columns +
$form_num_ref_columns) .
2573 "' align='right'>" . xlt('Total Payments This Visit') . "</td>\n";
2574 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='form_totalpay' " .
2575 "value='' size='6' maxlength='8' " .
2576 "style='text-align:right;background-color:transparent' readonly";
2580 // Line for Difference.
2582 echo " <td class='text' colspan='" . (5 +
$num_optional_columns) .
2583 "' style='border-top:1px solid black; font-size:1pt; padding:0px;'> </td>\n";
2587 // Hide this if only showing line item adjustments.
2588 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2589 echo " style='display:none'";
2592 echo " <td class='title' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2593 "' align='right'><b>" . xlt('Difference') . "</b></td>\n";
2594 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='form_difference' " .
2595 "value='' size='6' maxlength='8' " .
2596 "style='text-align:right;background-color:transparent' readonly";
2600 if ($encounter_id) {
2602 "SELECT provider_id FROM form_encounter WHERE pid = ? AND encounter = ? " .
2603 "ORDER BY id DESC LIMIT 1",
2604 array($patient_id, $encounter_id)
2606 $inv_provider = $erow['provider_id'] +
0;
2609 // Line for Discount.
2611 // Hide this if only showing line item adjustments.
2612 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2613 echo " style='display:none'";
2616 echo " <td class='bold' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2618 if (AclMain
::aclCheckCore('acct', 'disc') || AclMain
::aclCheckCore('admin', 'super')) {
2619 echo "<a href='#' onclick='return computeDiscount()'>[" . xlt('Compute') . "]</a> <b>";
2620 echo xlt('Discount/Adjustment') . "</b></td>\n";
2621 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2622 "<input type='text' name='form_discount' " .
2623 "value='' size='6' maxlength='8' onkeyup='billingChanged()' " .
2624 "style='text-align:right' />";
2626 echo "" . xlt('Discount/Adjustment') . "</td>\n";
2627 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2628 "<input type='text' name='form_discount' value='' size='6' " .
2629 "style='text-align:right;background-color:transparent' readonly />";
2634 // Line for Balance Due
2636 echo " <td class='title' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2637 "' align='right'>" . xlt('Balance Due') . "</td>\n";
2638 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2639 "<input type='text' name='form_balancedue' " .
2640 "value='' size='6' maxlength='8' " .
2641 "style='text-align:right;background-color:transparent' readonly";
2647 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2648 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2649 <label
class="control-label" for="form_date"><?php
echo xlt('Posting Date'); ?
>:</label
>
2651 <td
class='text' colspan
='<?php echo $form_num_amount_columns; ?>' align
='right'>
2652 <input type
='text' class='form-control datepicker' id
='form_date' name
='form_date'
2653 title
='yyyy-mm-dd date of service'
2654 value
='<?php echo attr($encounter_date) ?>' />
2659 // A current invoice reference number may be present if there was a previous checkout.
2661 "SELECT invoice_refno FROM form_encounter WHERE " .
2662 "pid = ? AND encounter = ?",
2663 array($patient_id, $encounter_id)
2665 $current_irnumber = $tmprow['invoice_refno'];
2667 if (!$current_irnumber) {
2668 // If this user has a non-empty irnpool assigned, show the pending
2669 // invoice reference number.
2670 $irnumber = BillingUtilities
::getInvoiceRefNumber();
2671 if (!empty($irnumber)) {
2674 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2675 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2676 <?php
echo xlt('Tentative Invoice Ref No'); ?
>
2678 <td
class='text' align
='right' colspan
='<?php echo $form_num_amount_columns; ?>'>
2679 <?php
echo text($irnumber); ?
>
2683 } else if (!empty($GLOBALS['gbl_mask_invoice_number'])) {
2684 // Otherwise if there is an invoice reference number mask, ask for the refno.
2687 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2688 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2689 <?php
echo xlt('Invoice Reference Number'); ?
>
2691 <td
class='text' align
='right' colspan
='<?php echo $form_num_amount_columns; ?>'>
2692 <input type
='text' name
='form_irnumber' size
='10' value
=''
2693 onkeyup
='maskkeyup(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number
']); ?>")'
2694 onblur
='maskblur(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number
']); ?>")'
2704 <td
class='text' colspan
='<?php echo 5 + $num_optional_columns; ?>' align
='center'>
2706 <input type
='submit' name
='form_save' value
='<?php echo xlt('Save
'); ?>'
2707 <?php
if ($rapid_data_entry) { ?
>
2708 style
='background-color:#cc0000';color
:#ffffff'
2711 <?php
if (empty($_GET['framed'])) { ?
>
2712 <input type
='button' value
='Cancel' onclick
='window.close()' />
2714 <input type
='hidden' name
='form_provider' value
='<?php echo attr($inv_provider); ?>' />
2715 <input type
='hidden' name
='form_payer' value
='<?php echo attr($inv_payer); ?>' />
2716 <input type
='hidden' name
='form_encounter' value
='<?php echo attr($encounter_id); ?>' />
2717 <input type
='hidden' name
='form_checksum' value
='<?php echo attr($current_checksum); ?>' />
2729 // Add a line for entering a payment.
2730 // Declared down here because $form_num_*_columns must be defined.
2732 function addPayLine() {
2733 var table
= document
.getElementById('paytable');
2734 for (var i
= 0; i
< table
.rows
.length
; ++i
) {
2735 if (table
.rows
[i
].id
== 'totalpay') {
2736 var row
= table
.insertRow(i
);
2739 foreach ($aCellHTML as $ix => $html) {
2740 echo " var html = \"$html\";\n";
2741 echo " cell = row.insertCell(row.cells.length);\n";
2743 echo " cell.colSpan = $form_num_type_columns;\n";
2746 echo " cell.colSpan = $form_num_method_columns;\n";
2749 echo " cell.colSpan = $form_num_ref_columns;\n";
2752 echo " cell.colSpan = $form_num_amount_columns;\n";
2754 echo " cell.innerHTML = html.replace(/%d/, paylino);\n";
2757 cell
.align
= 'right'; // last cell is right-aligned
2766 // TBD: Clean up javascript indentation from here on. ////////////////////////////
2769 // Pop up the Payments window and close this one.
2770 function payprevious() {
2773 var loc
= '../patient_file/front_payment.php?omitenc=' +
<?php
echo js_url($encounter_id); ?
>;
2774 <?php
if (empty($_GET['framed'])) { ?
>
2775 opener
.parent
.left_nav
.dlgopen(loc
, '_blank', width
, height
);
2778 var tmp
= parent
.left_nav ? parent
.left_nav
: parent
.parent
.left_nav
;
2779 tmp
.dlgopen(loc
, '_blank', width
, height
);
2783 discountTypeChanged();
2789 echo "alert(" . js_escape($alertmsg) . ");\n";
2792 if ($gcac_related_visit && !$gcac_service_provided) {
2793 // Skip this warning if the GCAC visit form is not allowed.
2795 "SELECT COUNT(*) AS count FROM layout_group_properties " .
2796 "WHERE grp_form_id = 'LBFgcac' AND grp_group_id = '' AND grp_activity = 1"
2798 if (!empty($grow['count'])) { // if gcac is used
2799 // Skip this warning if referral or abortion in TS.
2801 "SELECT COUNT(*) AS count FROM transactions " .
2802 "WHERE title = 'Referral' AND refer_date IS NOT NULL AND " .
2803 "refer_date = ? AND pid = ?",
2804 array($inv_date, $patient_id)
2806 if (empty($grow['count'])) { // if there is no referral
2808 "SELECT COUNT(*) AS count FROM forms " .
2809 "WHERE pid = ? AND encounter = ? AND " .
2810 "deleted = 0 AND formdir = 'LBFgcac'",
2811 array($patient_id, $encounter_id)
2813 if (empty($grow['count'])) { // if there is no gcac form
2814 echo " alert(" . xlj('This visit will need a GCAC form, referral or procedure service.') . ");\n";
2818 } // end if ($gcac_related_visit)
2820 if ($GLOBALS['ippf_specific']) {
2822 // o If there is an initial contraceptive consult, make sure a LBFccicon form exists with that method on it.
2823 // o If a LBFccicon form exists with a new method on it, make sure the TS initial consult exists.
2825 require_once("$srcdir/contraception_billing_scan.inc.php");
2826 contraception_billing_scan($patient_id, $encounter_id);
2829 "SELECT field_value FROM shared_attributes WHERE pid = ? AND encounter = ? AND field_id = 'cgen_MethAdopt'",
2830 array($patient_id, $encounter_id)
2832 $csmethod = empty($csrow['field_value']) ?
'' : $csrow['field_value'];
2834 if (($csmethod ||
$contraception_billing_code) && $csmethod != "IPPFCM:$contraception_billing_code") {
2835 $warningMessage = xl('Warning') . ': ';
2837 $warningMessage .= xl('there is a contraception service but no contraception form new method');
2838 } else if (!$contraception_billing_code) {
2839 $warningMessage .= xl('there is a contraception form new method but no contraception service');
2841 $warningMessage .= xl('new method in contraception form does not match the contraception service');
2843 echo " alert(" . js_escape($warningMessage) . ");\n";