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.php");
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\Common\Twig\TwigContainer
;
63 use OpenEMR\Core\Header
;
64 use OpenEMR\OeUI\OemrUI
;
65 use OpenEMR\Services\FacilityService
;
67 $facilityService = new FacilityService();
69 // Change this to get the old appearance.
70 $TAXES_AFTER_ADJUSTMENT = true;
72 $currdecimals = intval($GLOBALS['currency_decimals'] ??
2);
74 // Details default to yes now.
75 $details = (!isset($_GET['details']) ||
!empty($_GET['details'])) ?
1 : 0;
77 $patient_id = empty($_GET['ptid']) ?
$pid : intval($_GET['ptid']);
78 $encounter_id = empty($_GET['enid']) ?
0 : intval($_GET['enid']);
79 $checkout_id = empty($_GET['coid']) ?
'' : $_GET['coid']; // timestamp of checkout
81 // This flag comes from the Fee Sheet form and perhaps later others.
82 $rapid_data_entry = empty($_GET['rde']) ?
0 : 1;
85 !AclMain
::aclCheckCore('admin', 'super') &&
86 !AclMain
::aclCheckCore('acct', 'bill') &&
87 !AclMain
::aclCheckCore('acct', 'disc')
89 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Client Receipt")]);
93 // This will be used for SQL timestamps that we write.
94 $this_bill_date = date('Y-m-d H:i:s');
96 // Get the patient's name and chart number.
97 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code');
99 // Adjustments from the ar_activity table.
102 // Holds possible javascript error messages.
105 // Format a money amount with decimals but no other decoration.
106 // Second argument is used when extra precision is required.
107 function formatMoneyNumber($value, $extradecimals = 0)
109 return sprintf('%01.' . ($GLOBALS['currency_decimals'] +
$extradecimals) . 'f', $value);
112 // Get a list item's title, translated if appropriate.
114 function getListTitle($list, $option)
117 "SELECT title FROM list_options WHERE list_id = ? AND option_id = ? AND activity = 1",
118 array($list, $option)
120 if (empty($row['title'])) {
123 return xl_list_label($row['title']);
126 function generate_layout_display_field($formid, $fieldid, $currvalue)
129 "SELECT * FROM layout_options WHERE form_id = ? AND field_id = ? LIMIT 1",
130 array($formid, $fieldid)
135 return generate_display_field($frow, $currvalue);
138 // This creates and loads the array $aAdjusts of adjustment data for this encounter.
140 function load_adjustments($patient_id, $encounter_id)
143 // Create array aAdjusts from ar_activity rows for $encounter_id.
145 $ares = sqlStatement(
147 "a.payer_type, a.adj_amount, a.memo, a.code_type, a.code, a.post_time, a.post_date, " .
148 "s.session_id, s.reference, s.check_date, lo.title AS memotitle " .
149 "FROM ar_activity AS a " .
150 "LEFT JOIN list_options AS lo ON lo.list_id = 'adjreason' AND lo.option_id = a.memo AND " .
152 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
153 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND " .
154 "( a.adj_amount != 0 OR a.pay_amount = 0 ) " .
155 "ORDER BY s.check_date, a.sequence_no",
156 array($patient_id, $encounter_id)
158 while ($arow = sqlFetchArray($ares)) {
159 if (empty($arow['memotitle'])) {
160 $arow['memotitle'] = $arow['memo'];
166 // Total and clear adjustments in $aAdjusts matching this line item. Should only
167 // happen for billed items, and matching includes the billing timestamp in order
168 // to handle the case of multiple checkouts.
169 function pull_adjustment($code_type, $code, $billtime, &$memo)
174 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
175 for ($i = 0; $i < count($aAdjusts); ++
$i) {
177 $aAdjusts[$i]['code_type'] == $code_type && $aAdjusts[$i]['code'] == $code &&
178 $aAdjusts[$i]['post_time'] == $billtime &&
179 ($aAdjusts[$i]['adj_amount'] != 0 ||
$aAdjusts[$i]['memotitle'] !== '')
181 $adjust +
= $aAdjusts[$i]['adj_amount'];
182 if ($memo && $aAdjusts[$i]['memotitle']) {
185 $memo .= $aAdjusts[$i]['memotitle'];
186 $aAdjusts[$i]['adj_amount'] = 0;
187 $aAdjusts[$i]['memotitle'] = '';
194 // Generate $aTaxNames = array of tax names, and $aInvTaxes = array of taxes for this invoice.
195 // For a given tax ID and line ID, $aInvTaxes[$taxID][$lineID] = tax amount.
196 // $lineID identifies the invoice line item (product or service) that was taxed, and is of the
197 // form S:<billing.id> or P:<drug_sales.sale_id>
198 // Taxes may change from time to time and $aTaxNames reflects only the taxes that were present
201 function load_taxes($patient_id, $encounter)
203 global $aTaxNames, $aInvTaxes, $taxes;
204 global $num_optional_columns, $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
205 global $form_num_type_columns, $form_num_method_columns, $form_num_ref_columns, $form_num_amount_columns;
207 $aTaxNames = array();
208 $aInvTaxes = array();
209 foreach ($taxes as $taxid => $taxarr) {
210 $aTaxNames[$taxid] = $taxarr[0];
211 $aInvTaxes[$taxid] = array();
214 $taxres = sqlStatement(
215 "SELECT code, fee, ndc_info FROM billing WHERE " .
216 "pid = ? AND encounter = ? AND code_type = 'TAX' AND activity = 1 " .
218 array($patient_id, $encounter)
220 while ($taxrow = sqlFetchArray($taxres)) {
221 $aInvTaxes[$taxrow['code']][$taxrow['ndc_info']] = $taxrow['fee'];
224 // Knowing the number of tax columns we can now compute the total number of optional
225 // columns and from that the colspan values for various things.
226 $num_optional_columns = (empty($GLOBALS['gbl_checkout_charges']) ?
0 : 1) +
227 (empty($GLOBALS['gbl_charge_categories']) ?
0 : 1) +
228 (empty($GLOBALS['gbl_checkout_line_adjustments']) ?
0 : 2) +
230 // Compute colspans for receipt payment rows.
231 // What's in play here are columns for Qty, Price, the optionals, and Total.
232 $rcpt_num_method_columns = 1;
233 $rcpt_num_ref_columns = 1;
234 if ($num_optional_columns == 1) {
235 $rcpt_num_method_columns = 2;
236 } else if ($num_optional_columns > 1) {
237 $rcpt_num_method_columns = 3;
238 $rcpt_num_ref_columns = $num_optional_columns - 1;
240 $rcpt_num_amount_columns = 3 +
$num_optional_columns - $rcpt_num_method_columns - $rcpt_num_ref_columns;
241 // Compute colspans for form payment rows.
242 $form_num_type_columns = 2;
243 $form_num_method_columns = 1;
244 $form_num_ref_columns = 1;
245 if ($num_optional_columns > 0) {
246 $form_num_method_columns = 2;
248 if ($num_optional_columns > 1) {
249 $form_num_type_columns = 3;
251 $form_num_amount_columns = 5 +
$num_optional_columns - $form_num_type_columns - $form_num_method_columns - $form_num_ref_columns;
254 // Use $lineid to match up (and delete) entries in $aInvTaxes with the line.
255 // $lineid looks like: S:<billing.id> or P:<drug_sales.sale_id>.
256 // This writes to the $aTaxes argument and returns the total tax for the line.
257 function pull_tax($lineid, &$aTaxes)
261 foreach ($aInvTaxes as $taxid => $taxarr) {
263 if ($lineid !== '') {
264 foreach ($taxarr as $taxlineid => $tax) {
265 if ($taxlineid === $lineid) {
266 $aTaxes[$taxid] +
= $tax;
268 $aInvTaxes[$taxid][$taxlineid] = 0;
273 // $aTaxes now contains the total of each tax type (keyed on tax ID) for this line item,
274 // and those matched amounts are removed from $aInvTaxes.
278 // Output HTML for a receipt line item.
280 function receiptDetailLine(
292 global $details, $TAXES_AFTER_ADJUSTMENT;
294 // Use $lineid to match up (and delete) entries in $aInvTaxes with the line.
296 $totlinetax = pull_tax($lineid, $aTaxes);
297 // $aTaxes now contains the total of each tax type for this line item, and those matched
298 // amounts are removed from $aInvTaxes.
304 // If an adjustment, do appropriate interpretation.
305 if ($code_type === '') {
307 $adjust = 0 - $charge;
309 list($payer, $code_type, $code) = explode('|', $code);
310 $memo = $description;
311 $description = $GLOBALS['simplified_demographics'] ?
'' : "$payer ";
312 $description .= $code ?
xl('Item Adjustment') : xl('Invoice Adjustment');
315 // Total and clear adjustments in $aAdjusts matching this line item.
316 $adjust +
= pull_adjustment($code_type, $code, $billtime, $memo);
319 $charge = formatMoneyNumber($charge);
320 $total = formatMoneyNumber($charge +
$totlinetax - $adjust);
321 if (empty($quantity)) {
324 $price = formatMoneyNumber($charge / $quantity, 2);
325 $tmp = formatMoneyNumber($price);
326 if ($price == $tmp) {
329 if (is_array($aTotals)) {
330 $aTotals[0] +
= $quantity;
331 $aTotals[1] +
= $price;
332 $aTotals[2] +
= $charge;
333 $aTotals[3] +
= $adjust;
334 $aTotals[4] +
= $total;
335 // Accumulate columns 5 and beyond for taxes.
337 foreach ($aTaxes as $tax) {
338 $aTotals[$i++
] +
= $tax;
345 if (empty($postdate) ||
substr($postdate, 0, 4) == '0000') {
346 $postdate = $billtime;
349 echo " <td title='" . xla('Entered') . ' ' .
350 text(oeFormatShortDate($billtime)) . attr(substr($billtime, 10)) . "'>" .
351 text(oeFormatShortDate($postdate)) . "</td>\n";
352 echo " <td>" . text($code) . "</td>\n";
353 echo " <td>" . text($description) . "</td>\n";
354 echo " <td class='text-center'>" . ($isadjust ?
'' : $quantity) . "</td>\n";
355 echo " <td class='text-right'>" . text(oeFormatMoney($price, false, true)) . "</td>\n";
357 if (!empty($GLOBALS['gbl_checkout_charges'])) {
358 echo " <td class='text-right'>" . text(oeFormatMoney($charge, false, true)) . "</td>\n";
361 if (!$TAXES_AFTER_ADJUSTMENT) {
362 // Write tax amounts.
363 foreach ($aTaxes as $tax) {
364 echo " <td class='text-right'>" . text(oeFormatMoney($tax, false, true)) . "</td>\n";
369 if (!empty($GLOBALS['gbl_charge_categories'])) {
370 echo " <td class='text-right'>" . text($chargecat) . "</td>\n";
373 // Adjustment and its description.
374 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
375 echo " <td class='text-right'>" . text($memo) . "</td>\n";
376 echo " <td class='text-right'>" . text(oeFormatMoney($adjust, false, true)) . "</td>\n";
379 if ($TAXES_AFTER_ADJUSTMENT) {
380 // Write tax amounts.
381 foreach ($aTaxes as $tax) {
382 echo " <td class='text-right'>" . text(oeFormatMoney($tax, false, true)) . "</td>\n";
386 echo " <td class='text-right'>" . text(oeFormatMoney($total)) . "</td>\n";
390 // Output HTML for a receipt payment line.
392 function receiptPaymentLine($paydate, $amount, $description = '', $method = '', $refno = '', $billtime = '')
394 global $aTaxNames, $num_optional_columns;
395 global $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
396 $amount = formatMoneyNumber($amount); // make it negative
397 if ($description == 'Pt') {
400 // Resolve the payment method portion of the memo to display properly.
401 if (!empty($method)) {
402 $tmp = explode(' ', $method, 2);
403 $method = getListTitle('paymethod', $tmp[0]);
404 if (isset($tmp[1])) {
405 // If the description is not interesting then let it hold the check number
406 // or similar, otherwise append that to the payment method.
407 if ($description == '') {
408 $description = $tmp[1];
410 $method .= ' ' . $tmp[1];
416 if (!empty($billtime) && substr($billtime, 0, 4) != '0000') {
417 echo " title='" . xla('Entered') . ' ' .
418 text(oeFormatShortDate($billtime)) . attr(substr($billtime, 10)) . "'";
420 echo ">" . text(oeFormatShortDate($paydate)) . "</td>\n";
421 echo " <td colspan='2'>" . text($refno) . "</td>\n";
422 echo " <td colspan='$rcpt_num_method_columns' class='text-left'>" . text($method) . "</td>\n";
423 echo " <td colspan='$rcpt_num_ref_columns' class='text-left'>" . text($description) . "</td>\n";
424 echo " <td colspan='$rcpt_num_amount_columns' class='text-right'>" . text(oeFormatMoney($amount)) . "</td>\n";
428 // Compute a current checksum of this encounter's invoice-related data from the database.
430 function invoiceChecksum($pid, $encounter)
433 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
434 "id, code, modifier, units, fee, authorized, provider_id, ndc_info, justify, billed, user, bill_date" .
435 "))) AS checksum FROM billing WHERE " .
436 "pid = ? AND encounter = ? AND activity = 1",
437 array($pid, $encounter)
440 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
441 "sale_id, inventory_id, prescription_id, quantity, fee, sale_date, billed, bill_date" .
442 "))) AS checksum FROM drug_sales WHERE " .
443 "pid = ? AND encounter = ?",
444 array($pid, $encounter)
447 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
448 "sequence_no, code, modifier, payer_type, post_time, post_user, memo, pay_amount, adj_amount, post_date" .
449 "))) AS checksum FROM ar_activity WHERE " .
450 "pid = ? AND encounter = ?",
451 array($pid, $encounter)
454 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
455 "id, date, reason, facility_id, provider_id, supervisor_id, invoice_refno" .
456 "))) AS checksum FROM form_encounter WHERE " .
457 "pid = ? AND encounter = ?",
458 array($pid, $encounter)
460 return (0 +
$row1['checksum']) ^
(0 +
$row2['checksum']) ^
(0 +
$row3['checksum']) ^
(0 +
$row4['checksum']);
463 //////////////////////////////////////////////////////////////////////
465 // Generate a receipt from the last-billed invoice for this patient,
466 // or for the encounter specified as a GET parameter.
468 function generate_receipt($patient_id, $encounter = 0)
470 global $details, $rapid_data_entry, $aAdjusts;
471 global $web_root, $webserver_root, $code_types;
472 global $aTaxNames, $aInvTaxes, $checkout_times, $current_checksum;
473 global $num_optional_columns, $rcpt_num_method_columns, $rcpt_num_ref_columns, $rcpt_num_amount_columns;
474 global $TAXES_AFTER_ADJUSTMENT;
475 global $facilityService, $alertmsg;
477 // Get the most recent invoice data or that for the specified encounter.
480 "SELECT id, date, encounter, facility_id, invoice_refno " .
481 "FROM form_encounter WHERE pid = ? AND encounter = ?",
482 array($patient_id, $encounter)
486 "SELECT id, date, encounter, facility_id, invoice_refno " .
487 "FROM form_encounter WHERE pid = ? ORDER BY id DESC LIMIT 1",
492 die(xlt("This patient has no activity."));
494 $trans_id = $ferow['id'];
495 $encounter = $ferow['encounter'];
496 $svcdate = substr($ferow['date'], 0, 10);
497 $invoice_refno = $ferow['invoice_refno'];
499 // Generate checksum.
500 $current_checksum = invoiceChecksum($patient_id, $encounter);
502 // Get details for the visit's facility.
503 $frow = $facilityService->getById($ferow['facility_id']);
505 $patdata = getPatientData($patient_id, 'fname,mname,lname,pubpid,street,city,state,postal_code');
507 // Get array of checkout timestamps.
508 $checkout_times = craGetTimestamps($patient_id, $encounter);
510 // Generate $aTaxNames = array of tax names, and $aInvTaxes = array of taxes for this invoice.
511 load_taxes($patient_id, $encounter);
513 <!-- The following is from the php
function generate_receipt
. -->
517 <?php Header
::setupHeader(['datetime-picker']);?
>
518 <title
><?php
echo xlt('Client Receipt'); ?
></title
>
522 <?php
require($GLOBALS['srcdir'] . "/restoreSession.php"); ?
>
525 var win
= top
.printLogSetup ? top
: opener
.top
;
526 win
.printLogSetup(document
.getElementById('printbutton'));
529 // Process click on Print button.
530 function printme(checkout_id
) {
531 <?php
if (!empty($GLOBALS['gbl_custom_receipt'])) { ?
>
532 // Custom checkout receipt needs to be sent as a PDF in a new window or tab.
533 window
.open('pos_checkout.php?ptid=' +
<?php
echo js_url($patient_id); ?
>
534 +
'&enc=' +
<?php
echo js_url($encounter); ?
>
535 +
'&pdf=1&coid=' +
encodeURIComponent(checkout_id
),
536 '_blank', 'width=750,height=550,resizable=1,scrollbars=1');
538 var divstyle
= document
.getElementById('hideonprint').style
;
539 divstyle
.display
= 'none';
540 if (checkout_id
!= '*') {
547 // Process click on Print button before printing.
548 function printlog_before_print() {
549 // * means do not call window.print().
553 // Process click on Delete button.
554 function deleteme() {
555 dlgopen('deleter.php?billing=' +
<?php
echo js_url($patient_id . "." . $encounter); ?
> +
'&csrf_token_form=' +
<?php
echo js_url(CsrfUtils
::collectCsrfToken()); ?
>, '_blank', 500, 450);
559 // Called by the deleteme.php window on a successful delete.
560 function imdeleted() {
564 var voidaction
= ''; // saves action argument from voidme()
566 // Submit the form to complete a void operation.
567 function voidwrap(form_reason
, form_notes
) {
568 top
.restoreSession();
569 document
.location
.href
= 'pos_checkout.php?ptid=' +
<?php
echo js_url($patient_id); ?
> +
570 '&' +
encodeURIComponent(voidaction
) +
'=' +
<?php
echo js_url($encounter); ?
> +
571 '&form_checksum=' +
<?php
echo js_url($current_checksum); ?
> +
572 '&form_reason=' +
encodeURIComponent(form_reason
) +
573 '&form_notes=' +
encodeURIComponent(form_notes
) +
574 '<?php if (!empty($_GET['framed
'])) {
575 echo '&framed
=1';} ?>';
579 // Process click on a void option.
580 // action can be 'regen', 'void' or 'voidall'.
581 function voidme(action
) {
583 if (action
== 'void' || action
== 'voidall') {
584 if (!confirm(<?php
echo xlj('This will advance the receipt number. Please print the receipt if you have not already done so.'); ?
>)) {
587 dlgopen('void_dialog.php', '_blank', 500, 450);
590 // TBD: Better defaults for void reason and notes.
598 @media
(min
-width
: 992px
){
600 width
: 1000px
!Important
;
604 <title
><?php
echo xlt('Patient Checkout'); ?
></title
>
608 <div
class='container mt-3'>
610 <div
class='col text-center'>
611 <table
class='table' width
='95%'>
613 <td width
='25%' align
='left' valign
='top'>
615 // TBD: Maybe make a global for this file name.
616 if ($tmp = UrlIfImageExists('ma_logo.png')) {
617 echo "<img src='$tmp' />";
623 <td width
='50%' align
='center' valign
='top' class='font-weight-bold'>
624 <?php
echo text($frow['name']); ?
>
625 <br
><?php
echo text($frow['street']); ?
>
627 echo text($frow['city']) . ", ";
628 echo text($frow['state']) . " ";
629 echo text($frow['postal_code']); ?
>
630 <br
><?php
echo text($frow['phone']); ?
>
632 <td width
='25%' align
='right' valign
='top'>
633 <!-- This space available
. -->
638 <p
class='font-weight-bold'>
640 echo xlt("Client Receipt");
641 if ($invoice_refno) {
642 echo " " . xlt("for Invoice") . text(" $invoice_refno");
648 // Compute numbers for summary on right side of page.
649 $head_begbal = get_patient_balance_excluding($patient_id, $encounter);
651 "SELECT SUM(fee) AS amount FROM billing WHERE " .
652 "pid = ? AND encounter = ? AND activity = 1 AND " .
653 "code_type != 'COPAY'",
654 array($patient_id, $encounter)
656 $head_charges = $row['amount'];
658 "SELECT SUM(fee) AS amount FROM drug_sales WHERE pid = ? AND encounter = ?",
659 array($patient_id, $encounter)
661 $head_charges +
= $row['amount'];
663 "SELECT SUM(pay_amount) AS payments, " .
664 "SUM(adj_amount) AS adjustments FROM ar_activity WHERE " .
665 "pid = ? AND encounter = ? AND deleted IS NULL",
666 array($patient_id, $encounter)
668 $head_adjustments = $row['adjustments'];
669 $head_payments = $row['payments'];
671 "SELECT SUM(fee) AS amount FROM billing WHERE " .
672 "pid = ? AND encounter = ? AND activity = 1 AND " .
673 "code_type = 'COPAY'",
674 array($patient_id, $encounter)
676 $head_payments -= $row['amount'];
677 $head_endbal = $head_begbal +
$head_charges - $head_adjustments - $head_payments;
679 <table
class='table' width
='95%'>
681 <td width
='50%' class='text-left' valign
='top'>
682 <?php
echo text($patdata['fname'] . ' ' . $patdata['mname'] . ' ' . $patdata['lname']); ?
>
683 <br
/><?php
echo text($patdata['street']); ?
>
685 echo generate_layout_display_field('DEM', 'city', $patdata['city']) . ", ";
686 echo generate_layout_display_field('DEM', 'state', $patdata['state']) . " ";
687 echo text($patdata['postal_code']); ?
>
689 <td width
='50%' class='text-right' valign
='top'>
692 <td
><?php
echo xlt('Beginning Account Balance'); ?
> 
; 
; 
; 
;</td
>
693 <td
class='text-right'><?php
echo text(oeFormatMoney($head_begbal)); ?
></td
>
696 <td
><?php
echo xlt('Total Visit Charges'); ?
> 
; 
; 
; 
;</td
>
697 <td
class='text-right'><?php
echo text(oeFormatMoney($head_charges)); ?
></td
>
700 <td
><?php
echo xlt('Adjustments'); ?
> 
; 
; 
; 
;</td
>
701 <td
class='text-right'><?php
echo text(oeFormatMoney($head_adjustments)); ?
></td
>
704 <td
><?php
echo xlt('Payments'); ?
> 
; 
; 
; 
;</td
>
705 <td
class='text-right'><?php
echo text(oeFormatMoney($head_payments)); ?
></td
>
708 <td
><?php
echo xlt('Ending Account Balance'); ?
> 
; 
; 
; 
;</td
>
709 <td
class='text-right'><?php
echo text(oeFormatMoney($head_endbal)); ?
></td
>
716 <table
class='table' width
='95%'>
717 <?php
if ($details) { ?
>
719 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
720 style
='padding-top:5pt;' class='font-weight-bold'>
721 <?php
echo xlt('Charges for') . ' ' . text(oeFormatShortDate($svcdate)); ?
>
726 <td
class='font-weight-bold'><?php
echo xlt('Date'); ?
></td
>
727 <td
class='font-weight-bold'><?php
echo xlt('Code'); ?
></td
>
728 <td
class='font-weight-bold'><?php
echo xlt('Description'); ?
></td
>
729 <td
class='font-weight-bold text-center'><?php
echo $details ?
xlt('Qty') : ' '; ?
></td
>
730 <td
class='font-weight-bold text-right'><?php
echo $details ?
xlt('Price') : ' '; ?
></td
>
731 <?php
if (!empty($GLOBALS['gbl_checkout_charges'])) { ?
>
732 <td
class='font-weight-bold text-right'><?php
echo xlt('Charge'); ?
></td
>
735 if (!$TAXES_AFTER_ADJUSTMENT) {
736 foreach ($aTaxNames as $taxname) {
737 echo " <td class='font-weight-bold text-right'>" . text($taxname) . "</td>\n";
741 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { ?
>
742 <td
class='font-weight-bold text-right'><?php
echo xlt('Customer'); ?
></td
>
744 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
745 <td
class='font-weight-bold text-right'><?php
echo xlt('Adj Type'); ?
></td
>
746 <td
class='font-weight-bold text-right'><?php
echo xlt('Adj Amt'); ?
></td
>
749 if ($TAXES_AFTER_ADJUSTMENT) {
750 foreach ($aTaxNames as $taxname) {
751 echo " <td class='font-weight-bold text-right'>" . text($taxname) . "</td>\n";
755 <td
class='font-weight-bold text-right'><?php
echo xlt('Total'); ?
></td
>
758 <?php
} // end if details ?>
761 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
762 style
='border-top:1px solid black; font-size:1px; padding:0;'>
767 // Create array aAdjusts from ar_activity rows for $encounter.
768 load_adjustments($patient_id, $encounter);
770 $aTotals = array(0, 0, 0, 0, 0);
771 for ($i = 0; $i < count($aTaxNames); ++
$i) {
772 $aTotals[5 +
$i] = 0;
776 $inres = sqlStatement(
777 "SELECT s.sale_id, s.sale_date, s.fee, " .
778 "s.quantity, s.drug_id, s.billed, s.bill_date, s.selector, d.name, lo.title " .
779 "FROM drug_sales AS s " .
780 "LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
781 "LEFT JOIN list_options AS lo ON lo.list_id = 'chargecats' and lo.option_id = s.chargecat AND lo.activity = 1 " .
782 "WHERE s.pid = ? AND s.encounter = ? " .
783 "ORDER BY s.sale_id",
784 array($patient_id, $encounter)
786 while ($inrow = sqlFetchArray($inres)) {
787 $billtime = $inrow['billed'] ?
$inrow['bill_date'] : '';
788 $tmpname = $inrow['name'];
789 if ($tmpname !== $inrow['selector']) {
790 $tmpname .= ' / ' . $inrow['selector'];
792 $units = $inrow['quantity'] / FeeSheet
::getBasicUnits($inrow['drug_id'], $inrow['selector']);
800 'P:' . $inrow['sale_id'],
808 $inres = sqlStatement(
809 "SELECT * FROM billing AS b " .
810 "LEFT JOIN list_options AS lo ON lo.list_id = 'chargecats' and lo.option_id = b.chargecat AND lo.activity = 1 " .
811 "WHERE b.pid = ? AND b.encounter = ? AND " .
812 "b.code_type != 'COPAY' AND b.code_type != 'TAX' AND b.activity = 1 " .
814 array($patient_id, $encounter)
816 while ($inrow = sqlFetchArray($inres)) {
817 // Write the line item if it allows fees or is not a diagnosis.
818 if (!empty($code_types[$inrow['code_type']]['fee']) ||
empty($code_types[$inrow['code_type']]['diag'])) {
819 $billtime = $inrow['billed'] ?
$inrow['bill_date'] : '';
835 // Write any adjustments left in the aAdjusts array.
836 foreach ($aAdjusts as $arow) {
837 if ($arow['adj_amount'] == 0 && $arow['memotitle'] == '') {
840 $payer = empty($arow['payer_type']) ?
'Pt' : ('Ins' . $arow['payer_type']);
843 "$payer|" . $arow['code_type'] . "|" . $arow['code'],
846 0 - $arow['adj_amount'],
855 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
856 style
='border-top:1px solid black; font-size:1px; padding:0;'>
862 // Sub-Total line with totals of all numeric columns.
865 echo " <td colspan='3' align='right'><b>" . xlt('Sub-Total') . "</b></td>\n";
866 echo " <td align='center'>" . text($aTotals[0]) . "</td>\n";
867 echo " <td align='right'>" . text(oeFormatMoney($aTotals[1])) . "</td>\n";
868 // Optional charge amount.
869 if (!empty($GLOBALS['gbl_checkout_charges'])) {
870 echo " <td align='right'>" . text(oeFormatMoney($aTotals[2])) . "</td>\n";
872 if (!$TAXES_AFTER_ADJUSTMENT) {
873 // Put tax columns, if any, in the subtotals.
874 for ($i = 0; $i < count($aTaxNames); ++
$i) {
875 echo " <td align='right'>" . text(oeFormatMoney($aTotals[5 +
$i])) . "</td>\n";
878 // Optional charge category empty column.
879 if (!empty($GLOBALS['gbl_charge_categories'])) {
880 echo " <td align='right'> </td>\n";
882 // Optional adjustment columns.
883 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
884 echo " <td align='right'> </td>\n";
885 echo " <td align='right'>" . text(oeFormatMoney($aTotals[3])) . "</td>\n";
887 if ($TAXES_AFTER_ADJUSTMENT) {
888 // Put tax columns, if any, in the subtotals.
889 for ($i = 0; $i < count($aTaxNames); ++
$i) {
890 echo " <td align='right'>" . text(oeFormatMoney($aTotals[5 +
$i])) . "</td>\n";
893 echo " <td align='right'>" . text(oeFormatMoney($aTotals[4])) . "</td>\n";
897 // Write a line for each tax item that did not match.
898 // Should only happen for old invoices before taxes were assigned to line items.
899 foreach ($aInvTaxes as $taxid => $taxarr) {
900 foreach ($taxarr as $taxlineid => $tax) {
902 receiptDetailLine('TAX', $taxid, $aTaxNames[$taxid], 1, $tax, $aTotals);
903 $aInvTaxes[$taxid][$taxlineid] = 0;
908 // Total Charges line.
910 echo " <td colspan='" . (3 +
$num_optional_columns) . "'> </td>\n";
911 echo " <td colspan='" . 2 .
912 "' align='right'><b>" . xlt('Total Charges') . "</b></td>\n";
913 echo " <td align='right'>" . text(oeFormatMoney($aTotals[4])) . "</td>\n";
918 <td colspan
='<?php echo 6 + $num_optional_columns; ?>' style
='padding-top:5pt;'>
919 <b
><?php
echo xlt('Payments'); ?
></b
>
924 <td
><b
><?php
echo xlt('Date'); ?
></b
></td
>
925 <td colspan
='2'><b
><?php
echo xlt('Checkout Receipt Ref'); ?
></b
></td
>
926 <td colspan
="<?php echo text($rcpt_num_method_columns); ?>"
927 align
='left'><b
><?php
echo xlt('Payment Method'); ?
></b
></td
>
928 <td colspan
="<?php echo text($rcpt_num_ref_columns); ?>"
929 align
='left'><b
><?php
echo xlt('Ref No'); ?
></b
></td
>
930 <td colspan
='<?php echo text($rcpt_num_amount_columns); ?>'
931 align
='right'><b
><?php
echo xlt('Amount'); ?
></b
></td
>
935 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
936 style
='border-top:1px solid black; font-size:1px; padding:0;'>
945 $inres = sqlStatement(
946 "SELECT fee, code_text FROM billing WHERE " .
947 "pid = ? AND encounter = ? AND " .
948 "code_type = 'COPAY' AND activity = 1 AND fee != 0 " .
950 array($patient_id, $encounter)
952 while ($inrow = sqlFetchArray($inres)) {
953 $payments -= formatMoneyNumber($inrow['fee']);
954 receiptPaymentLine($svcdate, 0 - $inrow['fee'], $inrow['code_text'], 'COPAY');
957 // Get other payments.
958 $inres = sqlStatement(
960 "a.code, a.modifier, a.memo, a.payer_type, a.adj_amount, a.pay_amount, " .
961 "a.post_time, IFNULL(a.post_date, a.post_time) AS post_date, " .
962 "s.payer_id, s.reference, s.check_date, s.deposit_date " .
963 "FROM ar_activity AS a " .
964 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
965 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND " .
966 "a.pay_amount != 0 " .
967 "ORDER BY s.check_date, a.sequence_no",
968 array($patient_id, $encounter)
970 $payer = empty($inrow['payer_type']) ?
'Pt' : ('Ins' . $inrow['payer_type']);
971 while ($inrow = sqlFetchArray($inres)) {
972 $payments +
= formatMoneyNumber($inrow['pay_amount']);
973 // Compute invoice number with payment suffix.
974 $tmp = array_search($inrow['post_time'], $checkout_times);
975 $tmp = $tmp === false ?
0 : ($tmp +
1);
976 $refno = $invoice_refno ?
"$invoice_refno-$tmp" : "$encounter-$tmp";
979 $inrow['pay_amount'],
980 trim($payer . ' ' . $inrow['reference']),
989 <td colspan
='<?php echo 6 + $num_optional_columns; ?>'
990 style
='border-top:1px solid black; font-size:1px; padding:0;'>
996 <td colspan
='<?php echo 3 + $num_optional_columns; ?>'> 
;</td
>
997 <td colspan
='2' align
='right'><b
><?php
echo xlt('Total Payments'); ?
></b
></td
>
998 <td align
='right'><?php
echo str_replace(' ', ' ', text(oeFormatMoney($payments, true))); ?
></td
>
1009 // The user-customizable note.
1010 if (!empty($GLOBALS['gbl_checkout_receipt_note'])) {
1012 echo str_repeat('*', 80) . '<br />';
1013 echo ' ' . text($GLOBALS['gbl_checkout_receipt_note']) . '<br />';
1014 echo str_repeat('*', 80) . '<br />';
1020 <b
><?php
echo xlt("Printed on") . ' ' . text(dateformat()); ?
></b
>
1023 <div id
='hideonprint'>
1028 if (count($checkout_times) > 1 && !empty($GLOBALS['gbl_custom_receipt'])) {
1029 // Multiple checkouts so allow selection of the one to print.
1030 // This is only applicable for custom checkout receipts.
1031 echo "<select onchange='printme(this.value)' >\n";
1032 echo " <option value=''>" . xlt('Print Checkout') . "</option>\n";
1034 foreach ($checkout_times as $tmp) {
1036 echo " <option value='" . attr($tmp) . "'>" . text("$i: $tmp") . "</option>\n";
1040 echo "<a href='#' onclick='return printme(\"\");'>" . xlt('Print') . "</a>\n";
1044 <?php
if (AclMain
::aclCheckCore('acct', 'disc')) { ?
>
1045  
; 
; 
; 
; 
;
1046 <a href
='#' onclick
='return voidme("regen");'><?php
echo xlt('Generate New Receipt Number'); ?
></a
>
1047  
; 
; 
; 
; 
;
1048 <a href
='#' onclick
='return voidme("void");' title
='<?php echo xla('Applies to this visit only
'); ?>'>
1049 <?php
echo xlt('Void Last Checkout'); ?
></a
>
1050  
; 
; 
; 
; 
;
1051 <a href
='#' onclick
='return voidme("voidall");' title
='<?php echo xla('Applies to this visit only
'); ?>'>
1052 <?php
echo xlt('Void All Checkouts'); ?
></a
>
1055  
; 
; 
; 
; 
;
1057 <?php
if ($details) { ?
>
1058 <a href
='pos_checkout.php?details=0&ptid=<?php echo attr_url($patient_id); ?>&enc=<?php echo attr_url($encounter); ?>'
1059 onclick
='top.restoreSession()'><?php
echo xlt('Hide Details'); ?
></a
>
1061 <a href
='pos_checkout.php?details=1&ptid=<?php echo attr_url($patient_id); ?>&enc=<?php echo attr_url($encounter); ?>'
1062 onclick
='top.restoreSession()'><?php
echo xlt('Show Details'); ?
></a
>
1065 </div
><!-- end hideonprint
-->
1066 </div
><!-- end col
-->
1067 </div
><!-- end row
-->
1068 </div
><!-- end container
-->
1072 echo " alert(" . js_escape($alertmsg) . ");\n";
1081 // end function generate_receipt()
1083 //////////////////////////////////////////////////////////////////////
1085 // Function to write the heading lines for the data entry form.
1086 // This is deferred because we need to know which encounter was chosen.
1088 $form_headers_written = false;
1089 function write_form_headers()
1091 global $form_headers_written, $patdata, $patient_id, $encounter_id, $aAdjusts;
1092 global $taxes, $encounter_date, $num_optional_columns, $TAXES_AFTER_ADJUSTMENT;
1094 if ($form_headers_written) {
1097 $form_headers_written = true;
1099 // Create arrays $aAdjusts, $aTaxNames and $aInvTaxes for this encounter.
1100 load_adjustments($patient_id, $encounter_id);
1101 // This also initializes $num_optional_columns and related colspan values.
1102 load_taxes($patient_id, $encounter_id);
1105 "SELECT date FROM form_encounter WHERE pid = ? AND encounter = ?",
1106 array($patient_id, $encounter_id)
1108 $encounter_date = substr($ferow['date'], 0, 10);
1111 <td colspan
='<?php echo 5 + $num_optional_columns; ?>' align
='center' class='title'>
1112 <?php
echo xlt('Patient Checkout for '); ?
><?php
echo text($patdata['fname']) . " " .
1113 text($patdata['lname']) . " (" . text($patdata['pubpid']) . ")" ?
>
1117 $prvbal = get_patient_balance_excluding($patient_id, $encounter_id);
1118 echo xlt('Previous Balance') . ' ';
1119 echo "<input type='text' value='" . attr(oeFormatMoney($prvbal)) . "' size='6' ";
1120 echo "style='text-align:right;background-color:transparent' readonly />\n";
1122 echo " <input type='button' value='" . xla('Pay Previous Balance') .
1123 "' onclick='payprevious()' />\n";
1132 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1133 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1) + count($taxes); ?>' class='bold'>
1135 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1); ?>' class='bold'>
1139 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { ?
>
1140 <td align
='right' class='bold' nowrap
>
1141 <?php
echo xlt('Default Customer'); ?
>
1144 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
1145 <td align
='right' class='bold' nowrap
>
1146 <?php
echo xlt('Default Adjust Type'); ?
>
1152 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1155 <td colspan
='<?php echo 1 + count($taxes); ?>' class='bold'>
1162 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1163 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1) + count($taxes); ?>' class='title'>
1165 <td colspan
='<?php echo 4 + (empty($GLOBALS['gbl_checkout_charges
']) ? 0 : 1); ?>' class='title'>
1167 <?php
echo xlt('Current Charges'); ?
>
1169 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { // charge category default ?>
1170 <td align
='right' class='bold'>
1171 <?php
echo generate_select_list('form_charge_category', 'chargecats', '', '', ' ', '', 'chargeCategoryChanged();'); ?
>
1174 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { // adjustmenty reason default ?>
1175 <td align
='right' class='bold'>
1176 <?php
echo generate_select_list('form_discount_type', 'adjreason', '', '', ' ', '', 'discountTypeChanged();billingChanged();'); ?
>
1182 <?php
if (!$TAXES_AFTER_ADJUSTMENT) { ?
>
1185 <td colspan
='<?php echo 1 + count($taxes); ?>' class='bold'>
1192 <td
class='bold'><?php
echo xlt('Date'); ?
></td
>
1193 <td
class='bold'><?php
echo xlt('Description'); ?
></td
>
1194 <td align
='right' class='bold'><?php
echo xlt('Quantity'); ?
></td
>
1195 <?php
if (empty($GLOBALS['gbl_checkout_charges'])) { // if no charges column ?>
1196 <td align
='right' class='bold'><?php
echo xlt('Charge'); ?
></td
>
1197 <?php
} else { // charges column needed ?>
1198 <td align
='right' class='bold'><?php
echo xlt('Price'); ?
></td
>
1199 <td align
='right' class='bold'><?php
echo xlt('Charge'); ?
></td
>
1202 if (!$TAXES_AFTER_ADJUSTMENT) {
1203 foreach ($taxes as $taxarr) {
1204 echo " <td align='right' class='bold'>" . text($taxarr[0]) . "</td>";
1208 <?php
if (!empty($GLOBALS['gbl_charge_categories'])) { // charge category ?>
1209 <td align
='right' class='bold'><?php
echo xlt('Customer'); ?
></td
>
1211 <?php
if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) { ?
>
1212 <td align
='right' class='bold'><?php
echo xlt('Adjust Type'); ?
></td
>
1213 <td align
='right' class='bold'><?php
echo xlt('Adj'); ?
></td
>
1216 if ($TAXES_AFTER_ADJUSTMENT) {
1217 foreach ($taxes as $taxarr) {
1218 echo " <td align='right' class='bold'>" . text($taxarr[0]) . "</td>";
1222 <td align
='right' class='bold'><?php
echo xlt('Total'); ?
></td
>
1227 // Function to output a line item for the input form.
1229 $totalchg = 0; // totals charges after adjustments
1230 function write_form_line(
1242 global $lino, $totalchg, $aAdjusts, $taxes, $encounter_date, $TAXES_AFTER_ADJUSTMENT;
1244 // Write heading rows if that is not already done.
1245 write_form_headers();
1246 $amount = formatMoneyNumber($amount);
1247 if (empty($units)) {
1250 $price = formatMoneyNumber($amount / $units, 2); // should be even cents, but...
1251 if (substr($price, -2) === '00') {
1252 $price = formatMoneyNumber($price);
1255 // Total and clear adjustments in aAdjusts matching this line item. Should only
1256 // happen for billed items, and matching includes the billing timestamp in order
1257 // to handle the case of multiple checkouts.
1259 $adjust = pull_adjustment($code_type, $code, $billtime, $memo);
1260 $total = formatMoneyNumber($amount - $adjust);
1261 if (empty($GLOBALS['discount_by_money'])) {
1262 // Convert $adjust to a percentage of the amount, up to 4 decimal places.
1263 $adjust = round(100 * $adjust / $amount, 4);
1266 // Compute the string of numeric tax rates to store with the charge line.
1268 $arates = explode(':', $taxrates);
1269 foreach ($taxes as $taxid => $taxarr) {
1271 if (empty($arates) ||
!in_array($taxid, $arates)) {
1274 $taxnumrates .= $rate . ':';
1278 echo " <td class='text'>" . text(oeFormatShortDate($encounter_date));
1279 echo "<input type='hidden' name='line[$lino][code_type]' value='" . attr($code_type) . "'>";
1280 echo "<input type='hidden' name='line[$lino][code]' value='" . attr($code) . "'>";
1281 echo "<input type='hidden' name='line[$lino][id]' value='" . attr($id) . "'>";
1282 echo "<input type='hidden' name='line[$lino][description]' value='" . attr($description) . "'>";
1283 // String of numeric tax rates is written here as a form field only for JavaScript tax computations.
1284 echo "<input type='hidden' name='line[$lino][taxnumrates]' value='" . attr($taxnumrates) . "'>";
1285 echo "<input type='hidden' name='line[$lino][units]' value='" . attr($units) . "'>";
1286 // Indicator of whether and when this line item was previously billed:
1287 echo "<input type='hidden' name='line[$lino][billtime]' value='" . attr($billtime) . "'>";
1289 echo " <td class='text'>" . text($description) . "</td>";
1290 echo " <td class='text' align='right'>" . text($units) . "</td>";
1292 if (empty($GLOBALS['gbl_checkout_charges'])) {
1293 // We show only total charges here.
1294 echo " <td class='text' align='right'>";
1295 echo "<input type='hidden' name='line[$lino][price]' value='" . attr($price) . "'>";
1296 echo "<input type='text' name='line[$lino][charge]' value='" . attr($amount) . "' size='6'";
1297 echo " style='text-align:right;background-color:transparent' readonly />";
1300 // In this case show price and extended charge amount.
1301 echo " <td class='text' align='right'>";
1302 echo "<input type='text' name='line[$lino][price]' value='" . attr($price) . "' size='6'";
1303 echo " style='text-align:right;background-color:transparent' readonly />";
1305 echo " <td class='text' align='right'>";
1306 echo "<input type='text' name='line[$lino][charge]' value='" . attr($amount) . "' size='6'";
1307 echo " style='text-align:right;background-color:transparent' readonly />";
1311 // Match up (and delete) entries in $aInvTaxes with the line.
1312 $lineid = $code_type == 'PROD' ?
"P:$id" : "S:$id";
1314 pull_tax($lineid, $aTaxes); // fills in $aTaxes
1316 if (!$TAXES_AFTER_ADJUSTMENT) {
1317 // A tax column for each tax. JavaScript will compute the amounts and
1318 // account for how the discount affects them.
1320 foreach ($taxes as $taxid => $dummy) {
1321 echo " <td class='text' align='right'>";
1322 echo "<input type='text' name='line[$lino][tax][$i]' size='6'";
1323 // Set tax amounts for existing billed items. JS must not recompute those.
1324 echo " value='" . attr(formatMoneyNumber($aTaxes[$taxid])) . "'";
1325 echo " style='text-align:right;background-color:transparent' readonly />";
1331 // Optional Charge Category.
1332 if (!empty($GLOBALS['gbl_charge_categories'])) {
1333 echo " <td class='text' align='right'>";
1334 echo generate_select_list(
1335 "line[$lino][chargecat]",
1343 $billtime ?
array('disabled' => 'disabled') : null
1348 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
1349 echo " <td class='text' align='right'>";
1350 echo generate_select_list(
1351 "line[$lino][memo]",
1359 $billtime ?
array('disabled' => 'disabled') : null
1362 echo " <td class='text' align='right' nowrap>";
1363 echo empty($GLOBALS['discount_by_money']) ?
'' : text($GLOBALS['gbl_currency_symbol']);
1364 echo "<input type='text' name='line[$lino][adjust]' size='6'";
1365 echo " value='" . attr(formatMoneyNumber($adjust)) . "'";
1366 // Modifying discount requires the acct/disc permission.
1367 if ($billtime ||
$code_type == 'TAX' ||
$code_type == 'COPAY' ||
!AclMain
::aclCheckCore('acct', 'disc')) {
1368 echo " style='text-align:right;background-color:transparent' readonly";
1370 echo " style='text-align:right' maxlength='8' onkeyup='lineDiscountChanged($lino)'";
1373 echo empty($GLOBALS['discount_by_money']) ?
'%' : '';
1377 if ($TAXES_AFTER_ADJUSTMENT) {
1378 // A tax column for each tax. JavaScript will compute the amounts and
1379 // account for how the discount affects them.
1381 foreach ($taxes as $taxid => $dummy) {
1382 echo " <td class='text' align='right'>";
1383 echo "<input type='text' name='line[$lino][tax][$i]' size='6'";
1384 // Set tax amounts for existing billed items. JS must not recompute those.
1385 echo " value='" . attr(formatMoneyNumber($aTaxes[$taxid])) . "'";
1386 echo " style='text-align:right;background-color:transparent' readonly />";
1392 // Extended amount after adjustments and taxes.
1393 echo " <td class='text' align='right'>";
1394 echo "<input type='text' name='line[$lino][amount]' value='" . attr($total) . "' size='6'";
1395 echo " style='text-align:right;background-color:transparent' readonly />";
1400 $totalchg +
= $amount;
1403 // Function to output a past payment/adjustment line to the form.
1405 function write_old_payment_line($pay_type, $date, $method, $reference, $amount)
1407 global $lino, $taxes, $num_optional_columns;
1408 global $form_num_type_columns, $form_num_method_columns, $form_num_ref_columns, $form_num_amount_columns;
1409 // Write heading rows if that is not already done.
1410 write_form_headers();
1411 $amount = formatMoneyNumber($amount);
1413 echo " <td class='text' colspan='$form_num_type_columns'>" . text($pay_type) . "</td>\n";
1414 echo " <td class='text' colspan='$form_num_method_columns'>" . text($method) . "</td>\n";
1415 echo " <td class='text' colspan='$form_num_ref_columns'>" . text($reference) . "</td>\n";
1416 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='oldpay[$lino][amount]' " .
1417 "value='$amount' size='6' maxlength='8'";
1418 echo " style='text-align:right;background-color:transparent' readonly";
1424 // Mark the tax rates that are referenced in this invoice.
1425 function markTaxes($taxrates)
1428 $arates = explode(':', $taxrates);
1429 if (empty($arates)) {
1432 foreach ($arates as $value) {
1433 if (!empty($taxes[$value])) {
1434 $taxes[$value][2] = '1';
1439 // Create the taxes array. Key is tax id, value is
1440 // (description, rate, indicator). Indicator seems to be unused.
1442 $pres = sqlStatement(
1443 "SELECT option_id, title, option_value " .
1444 "FROM list_options WHERE list_id = 'taxrate' AND activity = 1 ORDER BY seq, title, option_id"
1446 while ($prow = sqlFetchArray($pres)) {
1447 $taxes[$prow['option_id']] = array($prow['title'], $prow['option_value'], 0);
1450 // Array of HTML for the 4 or 5 cells of an input payment row.
1451 // "%d" will be replaced by a payment line number on the client side.
1453 $aCellHTML = array();
1454 $aCellHTML[] = "<span id='paytitle_%d'>" . text(xl('New Payment')) . "</span>";
1455 $aCellHTML[] = strtr(generate_select_list('payment[%d][method]', 'paymethod', '', '', ''), array("\n" => ""));
1456 $aCellHTML[] = "<input type='text' name='payment[%d][refno]' size='10' />";
1457 $aCellHTML[] = "<input type='text' name='payment[%d][amount]' size='6' style='text-align:right' onkeyup='setComputedValues()' />";
1459 $alertmsg = ''; // anything here pops up in an alert box
1461 // Make sure we have the encounter ID applicable to this request.
1462 if (!empty($_POST['form_save'])) {
1463 $patient_id = (int) $_POST['form_pid'];
1464 $encounter_id = (int) $_POST['form_encounter'];
1466 foreach (array('regen', 'enc', 'void', 'voidall') as $key) {
1467 if (!empty($_GET[$key])) {
1468 $encounter_id = (int) $_GET[$key];
1474 // Compute and validate the checksum.
1475 $current_checksum = 0;
1476 if ($patient_id && $encounter_id) {
1477 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1478 if (!empty($_REQUEST['form_checksum'])) {
1479 if ($_REQUEST['form_checksum'] != $current_checksum) {
1480 $alertmsg = xl('Someone else has just changed this visit. Please cancel this page and try again.');
1485 // If the Save button was clicked...
1487 if (!empty($_POST['form_save']) && !$alertmsg) {
1488 if (!CsrfUtils
::verifyCsrfToken($_POST["csrf_token_form"])) {
1489 CsrfUtils
::csrfNotVerified();
1492 // On a save, do the following:
1493 // Flag this form's drug_sales and billing items as billed.
1494 // Post line-level adjustments, replacing any existing ones for the same charges.
1495 // Post any invoice-level adjustment.
1496 // Post payments and be careful to use a unique invoice number.
1497 // Call the generate-receipt function.
1500 // A current invoice reference number may be present if there was a previous checkout.
1502 "SELECT invoice_refno FROM form_encounter WHERE " .
1503 "pid = ? AND encounter = ?",
1504 array($patient_id, $encounter_id)
1506 $current_irnumber = $tmprow['invoice_refno'];
1508 // Get the posting date from the form as yyyy-mm-dd.
1509 $postdate = substr($this_bill_date, 0, 10);
1510 if (preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $_POST['form_date'], $matches)) {
1511 $postdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
1513 $dosdate = $postdate; // not sure if this is appropriate
1515 if (! $encounter_id) {
1516 die("Internal error: Encounter ID is missing!");
1519 // Delete unbilled TAX rows from billing because they will be recalculated.
1520 // Do not delete already-billed taxes; we must not touch billed stuff.
1522 "UPDATE billing SET activity = 0 WHERE " .
1523 "pid = ? AND encounter = ? AND " .
1524 "code_type = 'TAX' AND billed = 0 AND activity = 1",
1525 array($patient_id, $encounter_id)
1528 $form_amount = $_POST['form_totalpay'];
1529 $lines = $_POST['line'];
1531 for ($lino = 0; !empty($lines[$lino]['code_type']); ++
$lino) {
1532 $line = $lines[$lino];
1533 $code_type = $line['code_type'];
1534 $code = $line['code'];
1536 $chargecat = $line['chargecat'] ??
'';
1537 $amount = formatMoneyNumber(trim($line['amount']));
1540 // Skip saving taxes and adjustments for billed items.
1541 if (!empty($line['billtime'])) {
1545 // Insert any taxes for this line.
1546 // There's a chance of input data and the $taxes array being out of sync if someone
1547 // updates the taxrate list during data entry... we oughta do something about that.
1548 if (is_array($line['tax'])) {
1549 // For tax rows the ndc_info field is used to identify the charge item that is taxed.
1550 // P indicates drug_sales.sale_id, S indicates billing.id.
1551 $ndc_info = $code_type == 'PROD' ?
"P:$id" : "S:$id";
1553 foreach ($taxes as $taxid => $taxarr) {
1554 $taxamount = $line['tax'][$i++
] +
0;
1555 if ($taxamount != 0) {
1556 BillingUtilities
::addBilling(
1571 // billed=0 because we will set billed and bill_date for unbilled items below.
1572 $linetax +
= $taxamount;
1577 // If there is an adjustment for this line, insert it.
1578 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
1579 $adjust = 0.00 +
trim($line['adjust']);
1580 $memo = formDataCore($line['memo']);
1581 if ($adjust != 0 ||
$memo !== '') {
1582 // $memo = xl('Discount');
1584 $memo = formData('form_discount_type');
1587 $sequence_no = sqlQuery(
1588 "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
1589 "pid = ? AND encounter = ?",
1590 array($patient_id, $encounter_id)
1592 $query = "INSERT INTO ar_activity ( " .
1593 "pid, encounter, sequence_no, code_type, code, modifier, payer_type, " .
1594 "post_user, post_time, post_date, session_id, memo, adj_amount " .
1596 "?, ?, ?, ?, ?, '', '0', ?, ?, ?, '0', ?, ? " .
1598 sqlStatement($query, array(
1601 $sequence_no['increment'],
1604 $_SESSION['authUserID'],
1614 if (!empty($GLOBALS['gbl_charge_categories'])) {
1615 // Update charge category for this line item.
1616 if ($code_type == 'PROD') {
1617 $query = "UPDATE drug_sales SET chargecat = ? WHERE sale_id = ?";
1618 sqlQuery($query, array($chargecat, $id));
1620 $query = "UPDATE billing SET chargecat = ? WHERE id = ?";
1621 sqlQuery($query, array($chargecat, $id));
1626 // Flag the encounter as billed.
1627 $query = "UPDATE billing SET billed = 1, bill_date = ? WHERE " .
1628 "pid = ? AND encounter = ? AND activity = 1 AND billed = 0";
1629 sqlQuery($query, array($this_bill_date, $patient_id, $encounter_id));
1630 $query = "update drug_sales SET billed = 1, bill_date = ? WHERE " .
1631 "pid = ? AND encounter = ? AND billed = 0";
1632 sqlQuery($query, array($this_bill_date, $patient_id, $encounter_id));
1635 if ($_POST['form_discount'] != 0) {
1636 if ($GLOBALS['discount_by_money']) {
1637 $amount = formatMoneyNumber(trim($_POST['form_discount']));
1639 $amount = formatMoneyNumber(trim($_POST['form_discount']) * $form_amount / 100);
1641 $memo = formData('form_discount_type');
1643 $sequence_no = sqlQuery(
1644 "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
1645 "pid = ? AND encounter = ?",
1646 array($patient_id, $encounter_id)
1648 $query = "INSERT INTO ar_activity ( " .
1649 "pid, encounter, sequence_no, code, modifier, payer_type, post_user, post_time, " .
1650 "post_date, session_id, memo, adj_amount " .
1652 "?, ?, ?, '', '', '0', ?, ?, ?, '0', ?, ? " .
1654 sqlStatement($query, array(
1657 $sequence_no['increment'],
1658 $_SESSION['authUserID'],
1667 // Post the payments.
1668 if (is_array($_POST['payment'])) {
1669 $lines = $_POST['payment'];
1670 for ($lino = 0; isset($lines[$lino]['amount']); ++
$lino) {
1671 $line = $lines[$lino];
1672 $amount = formatMoneyNumber(trim($line['amount']));
1673 if ($amount != 0.00) {
1674 $method = $line['method'];
1675 $refno = $line['refno'];
1676 if ($method !== '' && $refno !== '') {
1677 $method .= " $refno";
1679 $session_id = 0; // Is this OK?
1680 SLEOB
::arPostPayment(
1697 // If applicable, set the invoice reference number.
1698 if (!$current_irnumber) {
1699 $invoice_refno = '';
1700 if (isset($_POST['form_irnumber'])) {
1701 $invoice_refno = formData('form_irnumber', 'P', true);
1703 $invoice_refno = add_escape_custom(BillingUtilities
::updateInvoiceRefNumber());
1705 if ($invoice_refno) {
1707 "UPDATE form_encounter SET invoice_refno = ? WHERE pid = ? AND encounter = ?",
1708 array($invoice_refno, $patient_id, $encounter_id)
1713 // If appropriate, update the status of the related appointment to
1715 updateAppointmentStatus($patient_id, $dosdate, '>');
1717 generate_receipt($patient_id, $encounter_id);
1722 $form_reason = empty($_GET['form_reason']) ?
'' : $_GET['form_reason'];
1723 $form_notes = empty($_GET['form_notes' ]) ?
'' : $_GET['form_notes'];
1725 // If "regen" encounter ID was given, then we must generate a new receipt ID.
1727 if (!$alertmsg && $patient_id && !empty($_GET['regen'])) {
1728 BillingUtilities
::doVoid(
1736 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1737 $_GET['enc'] = $encounter_id;
1740 // If "enc" encounter ID was given, then we must generate a receipt and exit.
1742 if ($patient_id && !empty($_GET['enc'])) {
1743 if (empty($_GET['pdf'])) {
1744 generate_receipt($patient_id, $_GET['enc']);
1746 // PDF receipt is requested. In this case we are probably in a new window.
1747 require_once($GLOBALS['OE_SITE_DIR'] . "/" . $GLOBALS['gbl_custom_receipt']);
1748 // $checkout_id is an optional specified checkout timestamp.
1749 $billtime = $checkout_id;
1751 // No timestamp specified so use the last one.
1752 $checkout_times = craGetTimestamps($patient_id, $_GET['enc']);
1753 $billtime = empty($checkout_times) ?
'' : $checkout_times[count($checkout_times) - 1];
1755 generateCheckoutReceipt($patient_id, $_GET['enc'], $billtime);
1760 // If "void" encounter ID was given, then we must undo the last checkout.
1761 // Or for "voidall" undo all checkouts for the encounter.
1763 if (!$alertmsg && $patient_id && !empty($_GET['void'])) {
1764 BillingUtilities
::doVoid($patient_id, $encounter_id, true, '', $form_reason, $form_notes);
1765 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1766 } else if (!$alertmsg && $patient_id && !empty($_GET['voidall'])) {
1767 BillingUtilities
::doVoid($patient_id, $encounter_id, true, 'all', $form_reason, $form_notes);
1768 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1771 // Get the specified or first unbilled encounter ID for this patient.
1773 if (!$encounter_id) {
1774 $query = "SELECT encounter FROM billing WHERE " .
1775 "pid = ? AND activity = 1 AND billed = 0 AND code_type != 'TAX' " .
1776 "ORDER BY encounter DESC LIMIT 1";
1777 $brow = sqlQuery($query, array($patient_id));
1778 $query = "SELECT encounter FROM drug_sales WHERE " .
1779 "pid = ? AND billed = 0 " .
1780 "ORDER BY encounter DESC LIMIT 1";
1781 $drow = sqlQuery($query, array($patient_id));
1782 if (!empty($brow['encounter'])) {
1783 if (!empty($drow['encounter'])) {
1784 $encounter_id = min(intval($brow['encounter']), intval($drow['encounter']));
1786 $encounter_id = $brow['encounter'];
1788 } else if (!empty($drow['encounter'])) {
1789 $encounter_id = $drow['encounter'];
1793 // If there are none, just redisplay the last receipt and exit.
1795 if (!$encounter_id) {
1796 generate_receipt($patient_id);
1800 // Form requires billing permission.
1801 if (!AclMain
::aclCheckCore('admin', 'super') && !AclMain
::aclCheckCore('acct', 'bill')) {
1802 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Patient Checkout")]);
1806 // We have $patient_id and $encounter_id. Generate checksum if not already done.
1807 if (!$current_checksum) {
1808 $current_checksum = invoiceChecksum($patient_id, $encounter_id);
1811 // Get the valid practitioners, including those not active.
1812 $arr_users = array();
1813 $ures = sqlStatement(
1814 "SELECT id, username FROM users WHERE " .
1815 "( authorized = 1 OR info LIKE '%provider%' ) AND username != ''"
1817 while ($urow = sqlFetchArray($ures)) {
1818 $arr_users[$urow['id']] = '1';
1821 // Now write a data entry form:
1822 // List unbilled billing items (cpt, hcpcs, copays) for the patient.
1823 // List unbilled product sales for the patient.
1824 // Present an editable dollar amount for each line item, a total
1825 // which is also the default value of the input payment amount,
1826 // and OK and Cancel buttons.
1831 <?php Header
::setupHeader(['datetime-picker']);?
>
1833 <title
><?php
echo xlt('Patient Checkout'); ?
></title
>
1836 @media
(min
-width
: 992px
){
1838 width
: 1000px
!Important
;
1844 var mypcc
= <?php
echo js_escape($GLOBALS['phone_country_code']); ?
>;
1846 <?php
require($GLOBALS['srcdir'] . "/restoreSession.php"); ?
>
1848 // This clears tax amounts in preparation for recomputing taxes.
1849 // TBD: Probably don't need this at all.
1850 function clearTax(visible
) {
1851 var f
= document
.forms
[0];
1852 for (var i
= 0; f
['totaltax[' + i +
']']; ++i
) {
1853 f
['totaltax[' + i +
']'].value
= '0.00';
1857 // This computes taxes and extended amount for the specified line, and returns
1858 // the extended amount.
1859 function calcTax(lino
) {
1860 var f
= document
.forms
[0];
1861 var pfx
= 'line[' + lino +
']';
1862 var taxable
= parseFloat(f
[pfx +
'[charge]'].value
);
1864 if (f
[pfx +
'[adjust]']) {
1865 adjust
= parseFloat(f
[pfx +
'[adjust]'].value
);
1868 if (f
[pfx +
'[memo]']) {
1869 adjreason
= f
[pfx +
'[memo]'].value
;
1871 var extended
= taxable
- adjust
;
1874 // Generate JavaScript that checks if the chosen adjustment type is to be
1875 // applied before taxes are computed. option_value 1 indicates that the
1876 // "After Taxes" checkbox is checked for an adjustment type.
1877 $tmpres = sqlStatement(
1878 "SELECT option_id FROM list_options WHERE " .
1879 "list_id = 'adjreason' AND option_value = 1 AND activity = 1"
1881 while ($tmprow = sqlFetchArray($tmpres)) {
1882 echo " && adjreason != " . js_escape($tmprow['option_id']) . "\n";
1888 var taxnumrates
= f
[pfx +
'[taxnumrates]'].value
;
1889 var rates
= taxnumrates
.split(':');
1890 for (var i
= 0; i
< rates
.length
; ++i
) {
1891 if (! f
[pfx +
'[tax][' + i +
']']) {
1895 if (f
[pfx +
'[billtime]'].value
) {
1896 // Line item is billed, use the tax amounts that were previously set for it.
1897 tax
= parseFloat(f
[pfx +
'[tax][' + i +
']'].value
);
1900 tax
= taxable
* parseFloat(rates
[i
]);
1901 tax
= parseFloat(tax
.toFixed(<?php
echo $currdecimals ?
>));
1903 alert('Tax rate not numeric at line ' + lino
);
1905 f
[pfx +
'[tax][' + i +
']'].value
= tax
.toFixed(<?php
echo $currdecimals ?
>);
1908 var totaltax
= parseFloat(f
['totaltax[' + i +
']'].value
) + tax
;
1909 f
['totaltax[' + i +
']'].value
= totaltax
.toFixed(<?php
echo $currdecimals ?
>);
1911 f
[pfx +
'[amount]'].value
= extended
.toFixed(<?php
echo $currdecimals ?
>);
1915 // This mess recomputes total charges and optionally applies a discount.
1916 // As part of this, taxes and extended amount are recomputed for each line.
1917 function computeDiscountedTotals(discount
, visible
) {
1919 var f
= document
.forms
[0];
1921 for (var lino
= 0; f
['line[' + lino +
'][code_type]']; ++lino
) {
1922 var code_type
= f
['line[' + lino +
'][code_type]'].value
;
1923 // price is price per unit when the form was originally generated.
1924 // By contrast, amount is the dynamically-generated discounted line total.
1925 var price
= parseFloat(f
['line[' + lino +
'][price]'].value
);
1927 alert('Price not numeric at line ' + lino
);
1929 if (code_type
== 'COPAY' || code_type
== 'TAX') {
1930 // I think this case is obsolete now.
1931 total +
= parseFloat(price
.toFixed(<?php
echo $currdecimals ?
>));
1934 // Compute and set taxes and extended amount for the given line.
1935 // This also returns the extended amount.
1936 total +
= calcTax(lino
);
1939 f
.totalchg
.value
= total
.toFixed(<?php
echo $currdecimals ?
>);
1941 return total
- discount
;
1944 // This computes and returns the total of payments.
1945 function computePaymentTotal() {
1946 var f
= document
.forms
[0];
1948 for (var lino
= 0; ('oldpay[' + lino +
'][amount]') in f
; ++lino
) {
1949 var amount
= parseFloat(f
['oldpay[' + lino +
'][amount]'].value
);
1950 if (isNaN(amount
)) {
1953 amount
= parseFloat(amount
.toFixed(<?php
echo $currdecimals ?
>));
1956 for (var lino
= 0; ('payment[' + lino +
'][amount]') in f
; ++lino
) {
1957 var amount
= parseFloat(f
['payment[' + lino +
'][amount]'].value
);
1958 if (isNaN(amount
)) {
1959 amount
= parseFloat(0);
1961 amount
= parseFloat(amount
.toFixed(<?php
echo $currdecimals ?
>));
1963 // Set payment row's description to Refund if the amount is negative.
1964 var title
= amount
< 0 ?
<?php
echo xlj('Refund'); ?
> : <?php
echo xlj('New payment'); ?
>;
1965 var span
= document
.getElementById('paytitle_' + lino
);
1966 span
.innerHTML
= title
;
1971 // Recompute default payment amount with any discount applied, but
1972 // not if there is more than one input payment line.
1973 // This is called when the discount amount is changed, and initially.
1974 // As a side effect the tax line items are recomputed and
1975 // setComputedValues() is called.
1976 function billingChanged() {
1977 var f
= document
.forms
[0];
1978 var discount
= parseFloat(f
.form_discount
.value
);
1979 if (isNaN(discount
)) {
1982 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
1983 // This site discounts by percentage, so convert it to a money amount.
1984 if (discount
> 100) {
1987 if (discount
< 0 ) {
1990 discount
= 0.01 * discount
* computeDiscountedTotals(0, false);
1992 var total
= computeDiscountedTotals(discount
, true);
1993 // Get out if there is more than one input payment line.
1994 if (!('payment[1][amount]' in f
)) {
1995 f
['payment[0][amount]'].value
= 0;
1996 total
-= computePaymentTotal();
1997 f
['payment[0][amount]'].value
= total
.toFixed(<?php
echo $currdecimals ?
>);
1999 setComputedValues();
2003 // Function to return the adjustment type, if any, identified in a customer's Notes field.
2004 function adjTypeFromCustomer(customer
) {
2007 $tmpres = sqlStatement(
2008 "SELECT option_id, notes FROM list_options WHERE " .
2009 "list_id = 'chargecats' AND activity = 1"
2011 while ($tmprow = sqlFetchArray($tmpres)) {
2013 preg_match('/ADJ=(\w+)/', $tmprow['notes'], $matches) ||
2014 preg_match('/ADJ="(.*?)"/', $tmprow['notes'], $matches)
2016 echo " if (customer == " . js_escape($tmprow['option_id']) . ") ret = " . js_escape($matches[1]) . ";\n";
2023 // A line item adjustment was changed, so recompute stuff.
2024 function lineDiscountChanged(lino
) {
2025 var f
= document
.forms
[0];
2026 var discount
= parseFloat(f
['line[' + lino +
'][adjust]'].value
);
2027 if (isNaN(discount
)) {
2030 var charge
= parseFloat(f
['line[' + lino +
'][charge]'].value
);
2031 if (isNaN(charge
)) {
2034 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2035 // This site discounts by percentage, so convert it to a money amount.
2036 if (discount
> 100) {
2039 if (discount
< 0 ) {
2042 discount
= 0.01 * discount
* charge
;
2044 var amount
= charge
- discount
;
2045 f
['line[' + lino +
'][amount]'].value
= amount
.toFixed(<?php
echo $currdecimals ?
>);
2046 // alert(f['line[' + lino + '][amount]'].value); // debugging
2048 // Apply default adjustment type if one is specified in the customer (charge category).
2049 var custElem
= f
['line[' + lino +
'][chargecat]'];
2050 var adjtElem
= f
['line[' + lino +
'][memo]'];
2051 if (custElem
&& custElem
.value
&& adjtElem
&& !adjtElem
.value
) {
2052 var ccAdjType
= adjTypeFromCustomer(custElem
.value
);
2054 adjtElem
.value
= ccAdjType
;
2058 return billingChanged();
2061 // Set Total Payments, Difference and Balance Due when any amount changes.
2062 function setComputedValues() {
2063 var f
= document
.forms
[0];
2064 var payment
= computePaymentTotal();
2065 var difference
= computeDiscountedTotals(0, false) - payment
;
2066 var discount
= parseFloat(f
.form_discount
.value
);
2067 if (isNaN(discount
)) {
2070 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2071 // This site discounts by percentage, so convert it to a money amount.
2072 if (discount
> 100) {
2075 if (discount
< 0 ) {
2078 discount
= 0.01 * discount
* computeDiscountedTotals(0, false);
2080 var balance
= difference
- discount
;
2081 f
.form_totalpay
.value
= payment
.toFixed(<?php
echo $currdecimals ?
>);
2082 f
.form_difference
.value
= difference
.toFixed(<?php
echo $currdecimals ?
>);
2083 f
.form_balancedue
.value
= balance
.toFixed(<?php
echo $currdecimals ?
>);
2087 // This is called when [Compute] is clicked by the user.
2088 // Computes and sets the discount value from total charges less payment.
2089 // This also calls setComputedValues() so the balance due will be correct.
2090 function computeDiscount() {
2091 var f
= document
.forms
[0];
2092 var charges
= computeDiscountedTotals(0, false);
2093 var payment
= computePaymentTotal();
2094 var discount
= charges
- payment
;
2095 <?php
if (!$GLOBALS['discount_by_money']) { ?
>
2096 // This site discounts by percentage, so convert to that.
2097 discount
= charges ?
(100 * discount
/ charges
) : 0;
2098 f
.form_discount
.value
= discount
.toFixed(4);
2100 f
.form_discount
.value
= discount
.toFixed(<?php
echo $currdecimals ?
>);
2102 setComputedValues();
2106 // When the main adjustment reason changes, duplicate it to all per-line reasons.
2107 function discountTypeChanged() {
2108 var f
= document
.forms
[0];
2109 if (f
.form_discount_type
&& f
.form_discount_type
.selectedIndex
) {
2110 for (lino
= 0; f
['line[' + lino +
'][memo]']; ++lino
) {
2111 // But do not change adjustment reason for billed items.
2112 if (f
['line[' + lino +
'][billtime]'].value
) {
2115 f
['line[' + lino +
'][memo]'].selectedIndex
= f
.form_discount_type
.selectedIndex
;
2120 // When the main charge category changes, duplicate it to all per-line categories.
2121 function chargeCategoryChanged() {
2122 var f
= document
.forms
[0];
2123 if (f
.form_charge_category
&& f
.form_charge_category
.selectedIndex
) {
2124 for (lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2125 // But do not change categories for billed items.
2126 if (f
['line[' + lino +
'][billtime]'].value
) {
2129 f
['line[' + lino +
'][chargecat]'].selectedIndex
= f
.form_charge_category
.selectedIndex
;
2134 // This is specific to IPPF and Suriname.
2135 function check_referrals() {
2136 <?php
if (!empty($GLOBALS['gbl_menu_surinam_insurance'])) { ?
>
2138 var f
= document
.forms
[0];
2139 var services_needing_referral
= '';
2140 for (var lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2141 var pfx
= 'line[' + lino +
']';
2142 var cust
= f
[pfx+
'[chargecat]'].value
;
2143 var price
= parseFloat(f
[pfx +
'[price]'].value
);
2147 if ((cust
== 'SZF' || cust
== 'KL-0098') && f
[pfx+
'[code_type]'].value
!= 'PROD' && price
!= 0) {
2148 services_needing_referral +
= f
[pfx+
'[code_type]'].value +
':' + f
[pfx+
'[code]'].value +
';';
2151 if (services_needing_referral
) {
2152 top
.restoreSession();
2155 async
: false, // We cannot continue without an answer.
2156 url
: "<?php echo $GLOBALS['webroot']; ?>/library/ajax/check_szf_referrals_ajax.php",
2158 "pid": <?php
echo intval($patient_id); ?
>,
2159 "encounter": <?php
echo intval($encounter_id); ?
>,
2160 "services": services_needing_referral
2162 success
: function (jsondata
, textstatus
) {
2163 msg
= jsondata
['message'];
2167 if (msg
&& !confirm(msg
)) {
2174 // This is specific to IPPF and the NetSuite project.
2175 function check_giftcards() {
2176 <?php
if (empty($GLOBALS['gbl_menu_netsuite'])) { ?
>
2179 var f
= document
.forms
[0];
2180 // If there is no gift card customer return true.
2181 var gc_customer_exists
= false;
2182 for (lino
= 0; f
['line[' + lino +
'][chargecat]']; ++lino
) {
2183 var chargecat
= f
['line[' + lino +
'][chargecat]'].value
;
2187 $lres = sqlStatement(
2188 "SELECT option_id FROM list_options WHERE " .
2189 "list_id = 'chargecats' AND activity = 1 AND notes LIKE '%GIFTCARD=Y%' ORDER BY seq, title"
2191 while ($lrow = sqlFetchArray($lres)) {
2192 echo " || chargecat == " . js_escape($lrow['option_id']) . "\n";
2196 gc_customer_exists
= true;
2199 if (!gc_customer_exists
) {
2202 // If there is a gift card payment method in the form return true.
2203 for (lino
= 0; f
['payment[' + lino +
'][method]']; ++lino
) {
2204 var method
= f
['payment[' + lino +
'][method]'].value
;
2208 $lres = sqlStatement(
2209 "SELECT option_id FROM list_options WHERE " .
2210 "list_id = 'paymethod' AND activity = 1 AND notes LIKE '%GIFTCARD=Y%' ORDER BY seq, title"
2212 while ($lrow = sqlFetchArray($lres)) {
2213 echo " || method == " . js_escape($lrow['option_id']) . "\n";
2217 if (!f
['payment[' + lino +
'][refno]'].value
) {
2218 // There is a gift card payment method but no gift card ID is entered.
2219 alert(<?php
echo xlj("Enter Gift Card number in the Reference field"); ?
>);
2222 return true; // There is a gift card payment method or no such methods exist.
2225 // There is a gift card customer but no gift card payment method.
2226 alert(<?php
echo xlj("Gift Card Customer requires at least one Gift Card Payment Method"); ?
>);
2231 function validate() {
2232 var f
= document
.forms
[0];
2233 var missingtypeamt
= false;
2234 var missingtypeany
= false;
2235 for (lino
= 0; f
['line[' + lino +
'][memo]']; ++lino
) {
2236 if (f
['line[' + lino +
'][memo]'].selectedIndex
== 0 && f
['line[' + lino +
'][billtime]'].value
== '') {
2237 missingtypeany
= true;
2238 if (parseFloat(f
['line[' + lino +
'][adjust]'].value
) != 0) {
2239 missingtypeamt
= true;
2243 <?php
if (false /* adjustments_indicate_insurance */) { ?
>
2244 if (missingtypeany
) {
2245 alert(<?php
echo xlj('Adjustment type is required for every line item.') ?
>);
2249 if (missingtypeamt
) {
2250 alert(<?php
echo xlj('Adjustment type is required for each line with an adjustment.') ?
>);
2254 if (!check_referrals()) {
2257 if (!check_giftcards()) {
2260 top
.restoreSession();
2266 // TBD: Not sure this will be used here.
2267 $arrOeUiSettings = array(
2268 'heading_title' => xl('Patient Checkout'),
2269 'include_patient_name' => true,// use only in appropriate pages
2270 'expandable' => false,
2271 'expandable_files' => array(),//all file names need suffix _xpd
2272 'action' => "",//conceal, reveal, search, reset, link or back
2273 'action_title' => "",
2274 'action_href' => "",//only for actions - reset, link or back
2275 'show_help_icon' => false,
2276 'help_file_name' => ""
2278 $oemr_ui = new OemrUI($arrOeUiSettings);
2285 echo "<form method='post' action='pos_checkout.php?rde=" . attr_url($rapid_data_entry);
2286 if ($encounter_id) {
2287 echo "&enid=" . attr_url($encounter_id);
2289 if (!empty($_GET['framed'])) {
2292 echo "' onsubmit='return validate()'>\n";
2293 echo "<input type='hidden' name='form_pid' value='" . attr($patient_id) . "' />\n";
2295 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
2300 <table cellspacing
='5' id
='paytable' width
='85%'>
2305 $gcac_related_visit = false;
2306 $gcac_service_provided = false;
2308 // This is set by write_form_headers() when the encounter is known.
2309 $encounter_date = '';
2311 // This to save copays from the billing table.
2316 $query = "SELECT id, date, code_type, code, modifier, code_text, " .
2317 "provider_id, payer_id, units, fee, encounter, billed, bill_date, chargecat " .
2318 "FROM billing WHERE pid = ? AND encounter = ? AND activity = 1 AND " .
2319 "code_type != 'TAX' ORDER BY id ASC";
2320 $bres = sqlStatement($query, array($patient_id, $encounter_id));
2322 $query = "SELECT s.sale_id, s.sale_date, s.prescription_id, s.fee, s.quantity, " .
2323 "s.encounter, s.drug_id, s.billed, s.bill_date, s.selector, s.chargecat, d.name, r.provider_id " .
2324 "FROM drug_sales AS s " .
2325 "LEFT JOIN drugs AS d ON d.drug_id = s.drug_id " .
2326 "LEFT OUTER JOIN prescriptions AS r ON r.id = s.prescription_id " .
2327 "WHERE s.pid = ? AND s.encounter = ? " .
2328 "ORDER BY s.sale_id ASC";
2329 $dres = sqlStatement($query, array($patient_id, $encounter_id));
2331 // Process billing table items. Note this includes co-pays.
2332 // Items that are not allowed to have a fee are skipped.
2334 while ($brow = sqlFetchArray($bres)) {
2335 $thisdate = substr($brow['date'], 0, 10);
2336 $code_type = $brow['code_type'];
2337 $inv_payer = $brow['payer_id'];
2338 if (!$inv_date ||
$inv_date < $thisdate) {
2339 $inv_date = $thisdate;
2341 // Co-pays are saved for later.
2342 if ($code_type == 'COPAY') {
2347 $billtime = $brow['billed'] ?
$brow['bill_date'] : '';
2349 // Collect tax rates, related code and provider ID.
2352 if (!empty($code_types[$code_type]['fee'])) {
2353 $query = "SELECT taxrates, related_code FROM codes WHERE code_type = ? AND " .
2355 $binds = array($code_types[$code_type]['id'], $brow['code']);
2356 if ($brow['modifier']) {
2357 $query .= "modifier = ?";
2358 $binds[] = $brow['modifier'];
2360 $query .= "(modifier IS NULL OR modifier = '')";
2362 $query .= " LIMIT 1";
2363 $tmp = sqlQuery($query, $binds);
2364 $taxrates = $tmp['taxrates'];
2365 $related_code = $tmp['related_code'];
2366 markTaxes($taxrates);
2369 // Write the line item if it allows fees or is not a diagnosis.
2370 if (!empty($code_types[$code_type]['fee']) ||
empty($code_types[$code_type]['diag'])) {
2376 ucfirst(strtolower($brow['code_text'])),
2385 // Custom logic for IPPF to determine if a GCAC issue applies.
2386 if ($GLOBALS['ippf_specific'] && $related_code) {
2387 $relcodes = explode(';', $related_code);
2388 foreach ($relcodes as $codestring) {
2389 if ($codestring === '') {
2392 list($codetype, $code) = explode(':', $codestring);
2393 if ($codetype !== 'IPPF2') {
2396 if (preg_match('/^211/', $code)) {
2397 $gcac_related_visit = true;
2399 preg_match('/^211313030110/', $code) // Medical
2400 ||
preg_match('/^211323030230/', $code) // Surgical
2401 ||
preg_match('/^211403030110/', $code) // Incomplete Medical
2402 ||
preg_match('/^211403030230/', $code) // Incomplete Surgical
2404 $gcac_service_provided = true;
2411 // Process drug sales / products.
2413 while ($drow = sqlFetchArray($dres)) {
2414 if ($encounter_id && $drow['encounter'] != $encounter_id) {
2417 $thisdate = $drow['sale_date'];
2418 if (!$encounter_id) {
2419 $encounter_id = $drow['encounter'];
2421 if (!$inv_provider && !empty($arr_users[$drow['provider_id']])) {
2422 $inv_provider = $drow['provider_id'] +
0;
2424 if (!$inv_date ||
$inv_date < $thisdate) {
2425 $inv_date = $thisdate;
2427 $billtime = $drow['billed'] ?
$drow['bill_date'] : '';
2429 // Accumulate taxes for this product.
2431 "SELECT taxrates FROM drug_templates WHERE drug_id = ? ORDER BY selector LIMIT 1",
2432 array($drow['drug_id'])
2434 $taxrates = $tmp['taxrates'];
2435 markTaxes($taxrates);
2437 $tmpname = $drow['name'];
2438 if ($tmpname !== $drow['selector']) {
2439 $tmpname .= ' / ' . $drow['selector'];
2441 $units = $drow['quantity'] / FeeSheet
::getBasicUnits($drow['drug_id'], $drow['selector']);
2457 // Line for total charges.
2458 $totalchg = formatMoneyNumber($totalchg);
2460 echo " <td class='bold' colspan='" . (!empty($GLOBALS['gbl_checkout_charges']) ?
4 : 3) .
2461 "' align='right'>" . xlt('Total Charges This Visit') . "</td>\n";
2462 echo " <td class='text' align='right'><input type='text' name='totalcba' " .
2463 "value='" . attr($totalchg) . "' size='6' maxlength='8' " .
2464 "style='text-align:right;background-color:transparent' readonly";
2466 if (!$TAXES_AFTER_ADJUSTMENT) {
2467 for ($i = 0; $i < count($taxes); ++
$i) {
2468 echo " <td class='text' align='right'><input type='text' name='totaltax[$i]' " .
2469 "value='0.00' size='6' maxlength='8' " .
2470 "style='text-align:right;background-color:transparent' readonly";
2474 if (!empty($GLOBALS['gbl_charge_categories'])) {
2475 echo " <td class='text' align='right'> </td>\n"; // Empty space in charge category column.
2477 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2478 // Note $totalchg is the total of charges before adjustments, and the following
2479 // field will be recomputed at onload time and as adjustments are entered.
2480 echo " <td class='text' align='right'> </td>\n"; // Empty space in adjustment type column.
2481 echo " <td class='text' align='right'> </td>\n"; // TBD: Total adjustments can go here.
2483 if ($TAXES_AFTER_ADJUSTMENT) {
2484 for ($i = 0; $i < count($taxes); ++
$i) {
2485 echo " <td class='text' align='right'><input type='text' name='totaltax[$i]' " .
2486 "value='0.00' size='6' maxlength='8' " .
2487 "style='text-align:right;background-color:transparent' readonly";
2491 echo " <td class='text' align='right'><input type='text' name='totalchg' " .
2492 "value='" . attr($totalchg) . "' size='6' maxlength='8' " .
2493 "style='text-align:right;background-color:transparent' readonly";
2499 <td
class='title' colspan
='<?php echo 5 + $num_optional_columns; ?>'
2500 style
='border-top:1px solid black; padding-top:5pt;'>
2501 <b
><?php
echo xlt('Payments'); ?
></b
>
2506 // Start new section for payments.
2507 echo " <td class='bold' colspan='$form_num_type_columns'>" . xlt('Type') . "</td>\n";
2508 echo " <td class='bold' colspan='$form_num_method_columns'>" . xlt('Payment Method') . "</td>\n";
2509 echo " <td class='bold' colspan='$form_num_ref_columns'>" . xlt('Reference') . "</td>\n";
2510 echo " <td class='bold' colspan='$form_num_amount_columns' align='right' nowrap>" . xlt('Payment Amount') . "</td>\n";
2516 foreach ($aCopays as $brow) {
2517 $thisdate = substr($brow['date'], 0, 10);
2518 write_old_payment_line(
2527 // Write any adjustments left in the aAdjusts array. This should only happen if
2528 // there was an invoice-level discount in a prior checkout of this encounter.
2529 foreach ($aAdjusts as $arow) {
2530 $memo = $arow['memotitle'];
2531 if ($arow['adj_amount'] == 0 && $memo === '') {
2534 $reference = $arow['reference'];
2535 write_old_payment_line(
2544 // Write ar_activity payments.
2545 $ares = sqlStatement(
2547 "a.payer_type, a.pay_amount, a.memo, s.session_id, s.reference, s.check_date " .
2548 "FROM ar_activity AS a " .
2549 "LEFT JOIN ar_session AS s ON s.session_id = a.session_id WHERE " .
2550 "a.pid = ? AND a.encounter = ? AND a.deleted IS NULL AND a.pay_amount != 0 " .
2551 "ORDER BY s.check_date, a.sequence_no",
2552 array($patient_id, $encounter_id)
2554 while ($arow = sqlFetchArray($ares)) {
2555 $memo = $arow['memo'];
2556 $reference = $arow['reference'];
2557 if (empty($arow['session_id'])) {
2558 $atmp = explode(' ', $memo, 2);
2560 $reference = $atmp[1];
2562 $rowtype = $arow['payer_type'] ?
xl('Insurance payment') : xl('Prepayment');
2563 write_old_payment_line(
2572 // Line for total payments.
2573 echo " <tr id='totalpay'>\n";
2574 echo " <td class='bold' colspan='$form_num_type_columns'><a href='#' onclick='return addPayLine()'>[" . xlt('Add Row') . "]</a></td>\n";
2575 echo " <td class='bold' colspan='" . ($form_num_method_columns +
$form_num_ref_columns) .
2576 "' align='right'>" . xlt('Total Payments This Visit') . "</td>\n";
2577 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='form_totalpay' " .
2578 "value='' size='6' maxlength='8' " .
2579 "style='text-align:right;background-color:transparent' readonly";
2583 // Line for Difference.
2585 echo " <td class='text' colspan='" . (5 +
$num_optional_columns) .
2586 "' style='border-top:1px solid black; font-size:1pt; padding:0px;'> </td>\n";
2590 // Hide this if only showing line item adjustments.
2591 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2592 echo " style='display:none'";
2595 echo " <td class='title' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2596 "' align='right'><b>" . xlt('Difference') . "</b></td>\n";
2597 echo " <td class='text' align='right' colspan='$form_num_amount_columns'><input type='text' name='form_difference' " .
2598 "value='' size='6' maxlength='8' " .
2599 "style='text-align:right;background-color:transparent' readonly";
2603 if ($encounter_id) {
2605 "SELECT provider_id FROM form_encounter WHERE pid = ? AND encounter = ? " .
2606 "ORDER BY id DESC LIMIT 1",
2607 array($patient_id, $encounter_id)
2609 $inv_provider = $erow['provider_id'] +
0;
2612 // Line for Discount.
2614 // Hide this if only showing line item adjustments.
2615 if (!empty($GLOBALS['gbl_checkout_line_adjustments'])) {
2616 echo " style='display:none'";
2619 echo " <td class='bold' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2621 if (AclMain
::aclCheckCore('acct', 'disc') || AclMain
::aclCheckCore('admin', 'super')) {
2622 echo "<a href='#' onclick='return computeDiscount()'>[" . xlt('Compute') . "]</a> <b>";
2623 echo xlt('Discount/Adjustment') . "</b></td>\n";
2624 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2625 "<input type='text' name='form_discount' " .
2626 "value='' size='6' maxlength='8' onkeyup='billingChanged()' " .
2627 "style='text-align:right' />";
2629 echo "" . xlt('Discount/Adjustment') . "</td>\n";
2630 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2631 "<input type='text' name='form_discount' value='' size='6' " .
2632 "style='text-align:right;background-color:transparent' readonly />";
2637 // Line for Balance Due
2639 echo " <td class='title' colspan='" . ($form_num_type_columns +
$form_num_method_columns +
$form_num_ref_columns) .
2640 "' align='right'>" . xlt('Balance Due') . "</td>\n";
2641 echo " <td class='text' align='right' colspan='$form_num_amount_columns'>" .
2642 "<input type='text' name='form_balancedue' " .
2643 "value='' size='6' maxlength='8' " .
2644 "style='text-align:right;background-color:transparent' readonly";
2650 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2651 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2652 <label
class="control-label" for="form_date"><?php
echo xlt('Posting Date'); ?
>:</label
>
2654 <td
class='text' colspan
='<?php echo $form_num_amount_columns; ?>' align
='right'>
2655 <input type
='text' class='form-control datepicker' id
='form_date' name
='form_date'
2656 title
='yyyy-mm-dd date of service'
2657 value
='<?php echo attr($encounter_date) ?>' />
2662 // A current invoice reference number may be present if there was a previous checkout.
2664 "SELECT invoice_refno FROM form_encounter WHERE " .
2665 "pid = ? AND encounter = ?",
2666 array($patient_id, $encounter_id)
2668 $current_irnumber = $tmprow['invoice_refno'];
2670 if (!$current_irnumber) {
2671 // If this user has a non-empty irnpool assigned, show the pending
2672 // invoice reference number.
2673 $irnumber = BillingUtilities
::getInvoiceRefNumber();
2674 if (!empty($irnumber)) {
2677 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2678 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2679 <?php
echo xlt('Tentative Invoice Ref No'); ?
>
2681 <td
class='text' align
='right' colspan
='<?php echo $form_num_amount_columns; ?>'>
2682 <?php
echo text($irnumber); ?
>
2686 } else if (!empty($GLOBALS['gbl_mask_invoice_number'])) {
2687 // Otherwise if there is an invoice reference number mask, ask for the refno.
2690 <td
class='bold' colspan
='<?php echo ($form_num_type_columns + $form_num_method_columns + $form_num_ref_columns) +
2691 (empty($GLOBALS['gbl_charge_categories
']) ? 0 : 1); ?>' align
='right'>
2692 <?php
echo xlt('Invoice Reference Number'); ?
>
2694 <td
class='text' align
='right' colspan
='<?php echo $form_num_amount_columns; ?>'>
2695 <input type
='text' name
='form_irnumber' size
='10' value
=''
2696 onkeyup
='maskkeyup(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number
']); ?>")'
2697 onblur
='maskblur(this,"<?php echo addslashes($GLOBALS['gbl_mask_invoice_number
']); ?>")'
2707 <td
class='text' colspan
='<?php echo 5 + $num_optional_columns; ?>' align
='center'>
2709 <input type
='submit' name
='form_save' value
='<?php echo xlt('Save
'); ?>'
2710 <?php
if ($rapid_data_entry) { ?
>
2711 style
='background-color:#cc0000';color
:#ffffff'
2714 <?php
if (empty($_GET['framed'])) { ?
>
2715 <input type
='button' value
='Cancel' onclick
='window.close()' />
2717 <input type
='hidden' name
='form_provider' value
='<?php echo attr($inv_provider); ?>' />
2718 <input type
='hidden' name
='form_payer' value
='<?php echo attr($inv_payer); ?>' />
2719 <input type
='hidden' name
='form_encounter' value
='<?php echo attr($encounter_id); ?>' />
2720 <input type
='hidden' name
='form_checksum' value
='<?php echo attr($current_checksum); ?>' />
2732 // Add a line for entering a payment.
2733 // Declared down here because $form_num_*_columns must be defined.
2735 function addPayLine() {
2736 var table
= document
.getElementById('paytable');
2737 for (var i
= 0; i
< table
.rows
.length
; ++i
) {
2738 if (table
.rows
[i
].id
== 'totalpay') {
2739 var row
= table
.insertRow(i
);
2742 foreach ($aCellHTML as $ix => $html) {
2743 echo " var html = \"$html\";\n";
2744 echo " cell = row.insertCell(row.cells.length);\n";
2746 echo " cell.colSpan = $form_num_type_columns;\n";
2749 echo " cell.colSpan = $form_num_method_columns;\n";
2752 echo " cell.colSpan = $form_num_ref_columns;\n";
2755 echo " cell.colSpan = $form_num_amount_columns;\n";
2757 echo " cell.innerHTML = html.replace(/%d/, paylino);\n";
2760 cell
.align
= 'right'; // last cell is right-aligned
2769 // TBD: Clean up javascript indentation from here on. ////////////////////////////
2772 // Pop up the Payments window and close this one.
2773 function payprevious() {
2776 var loc
= '../patient_file/front_payment.php?omitenc=' +
<?php
echo js_url($encounter_id); ?
>;
2777 <?php
if (empty($_GET['framed'])) { ?
>
2778 opener
.parent
.left_nav
.dlgopen(loc
, '_blank', width
, height
);
2781 var tmp
= parent
.left_nav ? parent
.left_nav
: parent
.parent
.left_nav
;
2782 tmp
.dlgopen(loc
, '_blank', width
, height
);
2786 discountTypeChanged();
2792 echo "alert(" . js_escape($alertmsg) . ");\n";
2795 if ($gcac_related_visit && !$gcac_service_provided) {
2796 // Skip this warning if the GCAC visit form is not allowed.
2798 "SELECT COUNT(*) AS count FROM layout_group_properties " .
2799 "WHERE grp_form_id = 'LBFgcac' AND grp_group_id = '' AND grp_activity = 1"
2801 if (!empty($grow['count'])) { // if gcac is used
2802 // Skip this warning if referral or abortion in TS.
2804 "SELECT COUNT(*) AS count FROM transactions " .
2805 "WHERE title = 'Referral' AND refer_date IS NOT NULL AND " .
2806 "refer_date = ? AND pid = ?",
2807 array($inv_date, $patient_id)
2809 if (empty($grow['count'])) { // if there is no referral
2811 "SELECT COUNT(*) AS count FROM forms " .
2812 "WHERE pid = ? AND encounter = ? AND " .
2813 "deleted = 0 AND formdir = 'LBFgcac'",
2814 array($patient_id, $encounter_id)
2816 if (empty($grow['count'])) { // if there is no gcac form
2817 echo " alert(" . xlj('This visit will need a GCAC form, referral or procedure service.') . ");\n";
2821 } // end if ($gcac_related_visit)
2823 if ($GLOBALS['ippf_specific']) {
2825 // o If there is an initial contraceptive consult, make sure a LBFccicon form exists with that method on it.
2826 // o If a LBFccicon form exists with a new method on it, make sure the TS initial consult exists.
2828 require_once("$srcdir/contraception_billing_scan.inc.php");
2829 contraception_billing_scan($patient_id, $encounter_id);
2832 "SELECT field_value FROM shared_attributes WHERE pid = ? AND encounter = ? AND field_id = 'cgen_MethAdopt'",
2833 array($patient_id, $encounter_id)
2835 $csmethod = empty($csrow['field_value']) ?
'' : $csrow['field_value'];
2837 if (($csmethod ||
$contraception_billing_code) && $csmethod != "IPPFCM:$contraception_billing_code") {
2838 $warningMessage = xl('Warning') . ': ';
2840 $warningMessage .= xl('there is a contraception service but no contraception form new method');
2841 } else if (!$contraception_billing_code) {
2842 $warningMessage .= xl('there is a contraception form new method but no contraception service');
2844 $warningMessage .= xl('new method in contraception form does not match the contraception service');
2846 echo " alert(" . js_escape($warningMessage) . ");\n";