Update API.php (#7723)
[openemr.git] / src / Billing / SLEOB.php
blob343a7b709ba70ad0757325fbc7631c4f1ef79af6
1 <?php
3 /**
4 * This class has various billing functions for posting charges and payments.
6 * @package OpenEMR
7 * @author Rod Roark <rod@sunsetsystems.com>
8 * @author Stephen Waite <stephen.waite@cmsvt.com>
9 * @copyright Copyright (c) 2005-2009 Rod Roark <rod@sunsetsystems.com>
10 * @copyright Copyright (c) 2018 Stephen Waite <stephen.waite@cmsvt.com>
11 * @link https://www.open-emr.org
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
15 namespace OpenEMR\Billing;
17 require_once(dirname(__FILE__) . "/../../library/patient.inc.php");
19 use OpenEMR\Billing\BillingUtilities;
21 class SLEOB
23 // Try to figure out our invoice number (pid.encounter) from the
24 // claim ID and other stuff in the ERA. This should be straightforward
25 // except that some payers mangle the claim ID that we give them.
27 public static function slInvoiceNumber(&$out)
29 $invnumber = $out['our_claim_id'];
30 $atmp = preg_split('/[ -]/', $invnumber);
31 $acount = count($atmp);
33 $pid = 0;
34 $encounter = 0;
35 if ($acount == 2) {
36 $pid = $atmp[0];
37 $encounter = $atmp[1];
38 } elseif ($acount == 3) {
39 $pid = $atmp[0];
40 $brow = sqlQuery("SELECT encounter FROM billing WHERE " .
41 "pid = '$pid' AND encounter = ? AND activity = 1", array($atmp[1]));
43 $encounter = $brow['encounter'];
44 } elseif ($acount == 1) {
45 $pres = sqlStatement("SELECT pid FROM patient_data WHERE " .
46 "lname LIKE ? AND " .
47 "fname LIKE ? " .
48 "ORDER BY pid DESC", array($out['patient_lname'], $out['patient_fname']));
49 while ($prow = sqlFetchArray($pres)) {
50 if (strpos($invnumber, $prow['pid']) === 0) {
51 $pid = $prow['pid'];
52 $encounter = substr($invnumber, strlen($pid));
53 break;
58 if ($pid && $encounter) {
59 $invnumber = "$pid.$encounter";
62 return array($pid, $encounter, $invnumber);
65 // This gets a posting session ID. If the payer ID is not 0 and a matching
66 // session already exists, then its ID is returned. Otherwise a new session
67 // is created.
69 public static function arGetSession($payer_id, $reference, $check_date, $deposit_date = '', $pay_total = 0)
71 if (empty($deposit_date)) {
72 $deposit_date = $check_date;
75 if ($payer_id) {
76 $row = sqlQuery("SELECT session_id FROM ar_session WHERE " .
77 "payer_id = ? AND reference = ? AND " .
78 "check_date = ? AND deposit_date = ? " .
79 "ORDER BY session_id DESC LIMIT 1", array($payer_id, $reference, $check_date, $deposit_date));
80 if (!empty($row['session_id'])) {
81 return $row['session_id'];
85 return sqlInsert("INSERT INTO ar_session ( " .
86 "payer_id, user_id, reference, check_date, deposit_date, pay_total " .
87 ") VALUES ( ?, ?, ?, ?, ?, ? )", array($payer_id, $_SESSION['authUserID'], $reference, $check_date, $deposit_date, $pay_total));
90 //writing the check details to Session Table on ERA proxcessing
91 public static function arPostSession($payer_id, $check_number, $check_date, $pay_total, $post_to_date, $deposit_date, $debug)
93 $query = "INSERT INTO ar_session( " .
94 "payer_id,user_id,closed,reference,check_date,pay_total,post_to_date,deposit_date,patient_id,payment_type,adjustment_code,payment_method " .
95 ") VALUES (?, ?, 0, ?, ?, ?, ? ,?, 0, 'insurance', 'insurance_payment', 'electronic')";
96 if ($debug) {
97 echo text($query) . "<br />\n";
98 } else {
99 $sessionId = sqlInsert($query, array($payer_id, $_SESSION['authUserID'], 'ePay - ' . $check_number, $check_date, $pay_total, $post_to_date, $deposit_date));
100 return $sessionId;
104 // Post a payment, new style.
106 public static function arPostPayment(
107 $patient_id,
108 $encounter_id,
109 $session_id,
110 $amount,
111 $code,
112 $payer_type,
113 $memo,
114 $debug,
115 $time = '',
116 $codetype = '',
117 $date = null,
118 $payer_claim_number = null
120 $codeonly = $code;
121 $modifier = '';
122 $tmp = strpos($code, ':');
123 if ($tmp) {
124 $codeonly = substr($code, 0, $tmp);
125 $modifier = substr($code, $tmp + 1);
128 if (empty($time)) {
129 $time = date('Y-m-d H:i:s');
132 sqlBeginTrans();
133 $sequence_no = sqlQuery(
134 "SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE pid = ? AND encounter = ?",
135 array($patient_id, $encounter_id)
137 $query = "INSERT INTO ar_activity ( " .
138 "pid, encounter, sequence_no, code_type, code, modifier, payer_type, post_time, post_date, post_user, " .
139 "session_id, memo, pay_amount, payer_claim_number " .
140 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
141 sqlStatement(
142 $query,
143 array(
144 $patient_id,
145 $encounter_id,
146 $sequence_no['increment'],
147 $codetype,
148 $codeonly,
149 $modifier,
150 $payer_type,
151 $time,
152 $date,
153 $_SESSION['authUserID'],
154 $session_id,
155 $memo,
156 $amount,
157 $payer_claim_number
160 sqlCommitTrans();
161 return;
164 // Post a charge. This is called only from sl_eob_process.php where
165 // automated remittance processing can create a new service item.
166 // Here we add it as an unauthorized item to the billing table.
168 public static function arPostCharge($patient_id, $encounter_id, $session_id, $amount, $units, $thisdate, $code, $description, $debug, $codetype = '')
170 /*****************************************************************
171 * // Select an existing billing item as a template.
172 * $row= sqlQuery("SELECT * FROM billing WHERE " .
173 * "pid = '$patient_id' AND encounter = '$encounter_id' AND " .
174 * "code_type = 'CPT4' AND activity = 1 " .
175 * "ORDER BY id DESC LIMIT 1");
176 * $this_authorized = 0;
177 * $this_provider = 0;
178 * if (!empty($row)) {
179 * $this_authorized = $row['authorized'];
180 * $this_provider = $row['provider_id'];
182 *****************************************************************/
184 if (empty($codetype)) {
185 // default to CPT4 if empty, which is consistent with previous functionality.
186 $codetype = "CPT4";
189 $codeonly = $code;
190 $modifier = '';
191 $tmp = strpos($code, ':');
192 if ($tmp) {
193 $codeonly = substr($code, 0, $tmp);
194 $modifier = substr($code, $tmp + 1);
197 BillingUtilities::addBilling(
198 $encounter_id,
199 $codetype,
200 $codeonly,
201 $description,
202 $patient_id,
205 $modifier,
206 $units,
207 $amount,
213 // Post an adjustment, new style.
215 public static function arPostAdjustment($patient_id, $encounter_id, $session_id, $amount, $code, $payer_type, $reason, $debug, $time = '', $codetype = '')
217 $codeonly = $code;
218 $modifier = '';
219 $tmp = strpos($code, ':');
220 if ($tmp) {
221 $codeonly = substr($code, 0, $tmp);
222 $modifier = substr($code, $tmp + 1);
225 if (empty($time)) {
226 $time = date('Y-m-d H:i:s');
229 sqlBeginTrans();
230 $sequence_no = sqlQuery("SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE pid = ? AND encounter = ?", array($patient_id, $encounter_id));
231 $query = "INSERT INTO ar_activity ( " .
232 "pid, encounter, sequence_no, code_type, code, modifier, payer_type, post_user, post_time, " .
233 "session_id, memo, adj_amount " .
234 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
235 sqlStatement($query, array($patient_id, $encounter_id, $sequence_no['increment'], $codetype, $codeonly, $modifier, $payer_type, $_SESSION['authUserID'], $time, $session_id, $reason, $amount));
236 sqlCommitTrans();
237 return;
240 public static function arGetPayerID($patient_id, $date_of_service, $payer_type)
242 if ($payer_type < 1 || $payer_type > 3) {
243 return 0;
246 $tmp = array(1 => 'primary', 2 => 'secondary', 3 => 'tertiary');
247 $value = $tmp[$payer_type];
248 $query = "SELECT provider FROM insurance_data WHERE " .
249 "pid = ? AND type = ? AND (date <= ? OR date IS NULL) AND (date_end >= ? OR date_end IS NULL) " .
250 "ORDER BY date DESC LIMIT 1";
251 $nprow = sqlQuery($query, array($patient_id, $value, $date_of_service, $date_of_service));
252 if (empty($nprow)) {
253 return 0;
256 return $nprow['provider'];
259 // Make this invoice re-billable, new style.
261 public static function arSetupSecondary($patient_id, $encounter_id, $debug, $crossover = 0)
263 if ($crossover == 1) {
264 //if claim forwarded setting a new status
265 $status = 6;
266 } else {
267 $status = 1;
270 // Determine the next insurance level to be billed.
271 $ferow = sqlQuery("SELECT date, last_level_billed " .
272 "FROM form_encounter WHERE " .
273 "pid = ? AND encounter = ?", array($patient_id, $encounter_id));
274 $date_of_service = substr($ferow['date'], 0, 10);
275 $new_payer_type = 0 + $ferow['last_level_billed'];
276 if ($new_payer_type < 3 && !empty($ferow['last_level_billed']) || $new_payer_type == 0) {
277 ++$new_payer_type;
280 $new_payer_id = self::arGetPayerID($patient_id, $date_of_service, $new_payer_type);
282 if ($new_payer_id) {
283 // Queue up the claim.
284 if (!$debug) {
285 BillingUtilities::updateClaim(true, $patient_id, $encounter_id, $new_payer_id, $new_payer_type, $status, 5, '', 'hcfa', '', $crossover);
287 } else {
288 // Just reopen the claim.
289 if (!$debug) {
290 BillingUtilities::updateClaim(true, $patient_id, $encounter_id, -1, -1, $status, 0, '', '', '', $crossover);
294 return xl("Encounter ") . $encounter_id . xl(" is ready for re-billing.");