Improved Code Sniffing (#928)
[openemr.git] / library / billing.inc
blobc76c4d5710b77cbe6348385685ea654eeddf0e77
1 <?php
4 function getBillingById($id, $cols = "*")
6     return sqlQuery("select $cols from billing where id='$id' and activity=1 order by date DESC limit 0,1");
9 function getBillingByPid($pid, $cols = "*")
11     return  sqlQuery("select $cols from billing where pid ='$pid' and activity=1 order by date DESC limit 0,1");
14 function getBillingByEncounter($pid,$encounter, $cols = "code_type, code, code_text")
16     $res = sqlStatement("select $cols from billing where encounter = ? and pid=? and activity=1 order by code_type, date ASC", array($encounter,$pid) );
18     $all=array();
19     for($iter=0; $row=sqlFetchArray($res); $iter++)
20     {
21         $all[$iter] = $row;
22     }
23     return $all;
26 function addBilling(
27     $encounter_id,
28     $code_type,
29     $code,
30     $code_text,
31     $pid,
32     $authorized="0",
33     $provider,
34     $modifier="",
35     $units="",
36     $fee="0.00",
37     $ndc_info='',
38     $justify='',
39     $billed=0,
40     $notecodes='',
41     $pricelevel=''
42 ) {
44     $sql = "insert into billing (date, encounter, code_type, code, code_text, " .
45     "pid, authorized, user, groupname, activity, billed, provider_id, " .
46     "modifier, units, fee, ndc_info, justify, notecodes, pricelevel) values (" .
47     "NOW(), ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
48     return sqlInsert($sql, array($encounter_id,$code_type,$code,$code_text,$pid,$authorized,
49     $_SESSION['authId'],$_SESSION['authProvider'],$billed,$provider,$modifier,$units,$fee,
50     $ndc_info,$justify,$notecodes,$pricelevel));
53 function authorizeBilling($id, $authorized = "1")
55     sqlQuery("update billing set authorized = '$authorized' where id = '$id'");
58 function deleteBilling($id)
60     sqlStatement("update billing set activity = 0 where id = '$id'");
63 function clearBilling($id)
65     sqlStatement("update billing set justify = '' where id = '$id'");
68 // This function supports the Billing page (billing_process.php),
69 // and initiation of secondary processing (sl_eob.inc.php). 
70 // It is called in the following situations:
72 // * billing_process.php sets bill_time, bill_process, payer and target on
73 //   queueing a claim for processing.  Create claims row.
74 // * billing_process.php sets claim status to 2, and payer, on marking a
75 //   claim as billed without actually generating any billing.  Create a
76 //   claims row.  In this case bill_process will remain at 0 and process_time
77 //   and process_file will not be set.
78 // * billing_process.php sets bill_process, payer, target and x12 partner
79 //   before calling gen_x12_837.  Create a claims row.
80 // * billing_process.php sets claim status to 2 (billed), bill_process to 2,
81 //   process_time and process_file after calling gen_x12_837.  Claims row
82 //   already exists.
83 // * billing_process.php sets claim status to 2 (billed) after creating
84 //   an electronic batch (hcfa-only with recent changes).  Claims
85 //   row already exists.
86 // * EOB posting updates claim status to mark a payer as done.  Claims row
87 //   already exists.
88 // * EOB posting reopens an encounter for billing a secondary payer.  Create
89 //   a claims row.
91 // $newversion should be passed to us to indicate if a new claims row
92 // is to be generated, otherwise one must already exist.  The payer, if
93 // passed in for the latter case, must match the existing claim.
95 // Currently on the billing page the user can select any of the patient's
96 // payers.  That logic will tailor the payer choices to the encounter date.
98 function updateClaim(
99     $newversion,
100     $patient_id,
101     $encounter_id,
102     $payer_id=-1,
103     $payer_type=-1,
104     $status=-1,
105     $bill_process=-1,
106     $process_file='',
107     $target='',
108     $partner_id=-1,
109     $crossover=0
110 ) {
112     if (!$newversion) {
113         $sql = "SELECT * FROM claims WHERE patient_id = '$patient_id' AND " .
114         "encounter_id = '$encounter_id' AND status > 0 AND status < 4 ";
115         if ($payer_id >= 0) $sql .= "AND payer_id = '$payer_id' ";
116         $sql .= "ORDER BY version DESC LIMIT 1";
117         $row = sqlQuery($sql);
118         if (!$row) return 0;
119         if ($payer_id     < 0) $payer_id     = $row['payer_id'];
120         if ($status       < 0) $status       = $row['status'];
121         if ($bill_process < 0) $bill_process = $row['bill_process'];
122         if ($partner_id   < 0) $partner_id   = $row['x12_partner_id'];
123         if (!$process_file   ) $process_file = $row['process_file'];
124         if (!$target         ) $target       = $row['target'];
125     }
127     $claimset = "";
128     $billset = "";
129     if (empty($payer_id) || $payer_id < 0) $payer_id = 0;
131     if ($status==7) {//$status==7 is the claim denial case.
132         $claimset .= ", status = '$status'";
133     }
134     elseif ($status >= 0) {
135         $claimset .= ", status = '$status'";
136         if ($status > 1) {
137             $billset .= ", billed = 1";
138             if ($status == 2) $billset  .= ", bill_date = NOW()";
139         } else {
140             $billset .= ", billed = 0";
141         }
142     }
143     if ($status==7) {//$status==7 is the claim denial case.
144         $billset  .= ", bill_process = '$status'";
145     }
146     elseif ($bill_process >= 0) {
147         $claimset .= ", bill_process = '$bill_process'";
148         $billset  .= ", bill_process = '$bill_process'";
149     }
150     if ($status==7) {//$status==7 is the claim denial case.
151         $claimset  .= ", process_file = '$process_file'";//Denial reason code is stored here
152     }
153     elseif ($process_file) {
154         $claimset .= ", process_file = '$process_file', process_time = NOW()";
155         $billset  .= ", process_file = '$process_file', process_date = NOW()";
156     }
157     if ($target) {
158         $claimset .= ", target = '$target'";
159         $billset  .= ", target = '$target'";
160     }
161     if ($payer_id >= 0) {
162         $claimset .= ", payer_id = '$payer_id', payer_type = '$payer_type'";
163         $billset  .= ", payer_id = '$payer_id'";
164     }
165     if ($partner_id >= 0) {
166         $claimset .= ", x12_partner_id = '$partner_id'";
167         $billset  .= ", x12_partner_id = '$partner_id'";
168     }
170     if ($billset) {
171         $billset = substr($billset, 2);
172         sqlStatement("UPDATE billing SET $billset WHERE " .
173         "encounter = '$encounter_id' AND pid='$patient_id' AND activity = 1");
174     }
176   // If a new claim version is requested, insert its row.
177   //
178     if ($newversion) {
179         /****
180         $payer_id = ($payer_id < 0) ? $row['payer_id'] : $payer_id;
181         $bill_process = ($bill_process < 0) ? $row['bill_process'] : $bill_process;
182         $process_file = ($process_file) ? $row['process_file'] : $process_file;
183         $target = ($target) ? $row['target'] : $target;
184         $partner_id = ($partner_id < 0) ? $row['x12_partner_id'] : $partner_id;
185         $sql = "INSERT INTO claims SET " .
186         "patient_id = '$patient_id', " .
187         "encounter_id = '$encounter_id', " .
188         "bill_time = UNIX_TIMESTAMP(NOW()), " .
189         "payer_id = '$payer_id', " .
190         "status = '$status', " .
191         "payer_type = '" . $row['payer_type'] . "', " .
192         "bill_process = '$bill_process', " .
193         "process_time = '" . $row['process_time'] . "', " .
194         "process_file = '$process_file', " .
195         "target = '$target', " .
196         "x12_partner_id = '$partner_id'";
197         ****/
198         sqlBeginTrans();
199         $version = sqlQuery( "SELECT IFNULL(MAX(version),0) + 1 AS increment FROM claims WHERE patient_id = ? AND encounter_id = ?", array( $patient_id, $encounter_id ) );
200         if($crossover<>1)
201         {
202             $sql .= "INSERT INTO claims SET " .
203               "patient_id = $patient_id, " .
204               "encounter_id = $encounter_id, " .
205               "bill_time = NOW() $claimset ," .
206               "version = " . $version['increment'];
207         }
208         else
209         {//Claim automatic forward case.startTra
210             $sql= "INSERT INTO claims SET " .
211               "patient_id = $patient_id, " .
212               "encounter_id = $encounter_id, " .
213               "bill_time = NOW(), status=$status ," .
214               "version = " . $version['increment'];
215         }
216         sqlStatement($sql);
217         sqlCommitTrans();
218     }
220   // Otherwise update the existing claim row.
221   //
222     else if ($claimset) {
223         $claimset = substr($claimset, 2);
224         sqlStatement("UPDATE claims SET $claimset WHERE " .
225         "patient_id = '$patient_id' AND encounter_id = '$encounter_id' AND " .
226         // "payer_id = '" . $row['payer_id'] . "' AND " .
227         "version = '" . $row['version'] . "'");
228     }
230   // Whenever a claim is marked billed, update A/R accordingly.
231   //
232     if ($status == 2) {
233         if ($payer_type > 0) {
234             sqlStatement("UPDATE form_encounter SET " .
235             "last_level_billed = '$payer_type' WHERE " .
236             "pid = '$patient_id' AND encounter = '$encounter_id'");
237         }
238     }
240     return 1;
243 // Determine if the encounter is billed.  It is considered billed if it
244 // has at least one chargeable item, and all of them are billed.
246 function isEncounterBilled($pid, $encounter)
248     $billed = -1; // no chargeable services yet
250     $bres = sqlStatement("SELECT " .
251     "billing.billed FROM billing, code_types WHERE " .
252     "billing.pid = ? AND " .
253     "billing.encounter = ? AND " .
254     "billing.activity = 1 AND " .
255     "code_types.ct_key = billing.code_type AND " .
256     "code_types.ct_fee = 1 " .
257     "UNION " .
258     "SELECT billed FROM drug_sales WHERE " .
259     "pid = ? AND " .
260     "encounter = ?",
261     array($pid, $encounter, $pid, $encounter));
263     while ($brow = sqlFetchArray($bres)) {
264         if ($brow['billed'] == 0) {
265             $billed = 0;
266         }
267         else {
268             if ($billed < 0) $billed = 1;
269         }
270     }
272     return $billed > 0;
275 // Get the co-pay amount that is effective on the given date.
276 // Or if no insurance on that date, return -1.
278 function getCopay($patient_id, $encdate)
280     $tmp = sqlQuery("SELECT provider, copay FROM insurance_data " .
281     "WHERE pid = '$patient_id' AND type = 'primary' " .
282     "AND date <= '$encdate' ORDER BY date DESC LIMIT 1");
283     if ($tmp['provider']) return sprintf('%01.2f', 0 + $tmp['copay']);
284     return 0;
287 // Get the total co-pay amount paid by the patient for an encounter
288 function getPatientCopay($patient_id, $encounter)
290         $resMoneyGot = sqlStatement("SELECT sum(pay_amount) as PatientPay FROM ar_activity where ".
291           "pid = ? and encounter = ? and payer_type=0 and account_code='PCP'",
292           array($patient_id,$encounter));
293          //new fees screen copay gives account_code='PCP'
294         $rowMoneyGot = sqlFetchArray($resMoneyGot);
295         $Copay=$rowMoneyGot['PatientPay'];
296         return $Copay*-1;
299 // Get the "next invoice reference number" from this user's pool of reference numbers.
301 function getInvoiceRefNumber()
303     $trow = sqlQuery("SELECT lo.notes " .
304     "FROM users AS u, list_options AS lo " .
305     "WHERE u.username = ? AND " .
306     "lo.list_id = 'irnpool' AND lo.option_id = u.irnpool AND lo.activity = 1 LIMIT 1",
307     array($_SESSION['authUser']));
308     return empty($trow['notes']) ? '' : $trow['notes'];
311 // Increment the "next invoice reference number" of this user's pool.
312 // This identifies the "digits" portion of that number and adds 1 to it.
313 // If it contains more than one string of digits, the last is used.
315 function updateInvoiceRefNumber()
317     $irnumber = getInvoiceRefNumber();
318   // Here "?" specifies a minimal match, to get the most digits possible:
319     if (preg_match('/^(.*?)(\d+)(\D*)$/', $irnumber, $matches)) {
320         $newdigs = sprintf('%0' . strlen($matches[2]) . 'd', $matches[2] + 1);
321         $newnumber = $matches[1] . $newdigs . $matches[3];
322         sqlStatement("UPDATE users AS u, list_options AS lo " .
323         "SET lo.notes = ? WHERE " .
324         "u.username = ? AND " .
325         "lo.list_id = 'irnpool' AND lo.option_id = u.irnpool",
326         array($newnumber, $_SESSION['authUser']));
327     }
328     return $irnumber;
331 // Common function for voiding a receipt or checkout.  When voiding a checkout you can specify
332 // $time as a timestamp (yyyy-mm-dd hh:mm:ss) or 'all'; default is the last checkout.
334 function doVoid($patient_id, $encounter_id, $purge=false, $time='')
336     $what_voided = $purge ? 'checkout' : 'receipt';
337     $date_original = '';
338     $adjustments = 0;
339     $payments = 0;
341     if (!$time) {
342         // Get last checkout timestamp.
343         $corow = sqlQuery("(SELECT bill_date FROM billing WHERE " .
344         "pid = ? AND encounter = ? AND activity = 1 AND bill_date IS NOT NULL) " .
345         "UNION " .
346         "(SELECT bill_date FROM drug_sales WHERE " .
347         "pid = ? AND encounter = ? AND bill_date IS NOT NULL) " .
348         "ORDER BY bill_date DESC LIMIT 1",
349         array($patient_id, $encounter_id, $patient_id, $encounter_id));
350         if (!empty($corow['bill_date'])) {
351               $date_original = $corow['bill_date'];
352         }
353     }
354     else if ($time == 'all') {
355         $row = sqlQuery("SELECT SUM(pay_amount) AS payments, " .
356         "SUM(adj_amount) AS adjustments FROM ar_activity WHERE " .
357         "pid = ? AND encounter = ?",
358         array($patient_id, $encounter_id));
359         $adjustments = $row['adjustments'];
360         $payments = $row['payments'];
361     }
362     else {
363         $date_original = $time;
364     }
365   // Get its charges and adjustments.
366     if ($date_original) {
367         $row = sqlQuery("SELECT SUM(pay_amount) AS payments, " .
368         "SUM(adj_amount) AS adjustments FROM ar_activity WHERE " .
369         "pid = ? AND encounter = ? AND post_time = ?",
370         array($patient_id, $encounter_id, $date_original));
371         $adjustments = $row['adjustments'];
372         $payments = $row['payments'];
373     }
375   // Get old invoice reference number.
376     $encrow = sqlQuery("SELECT invoice_refno FROM form_encounter WHERE " .
377     "pid = ? AND encounter = ? LIMIT 1",
378     array($patient_id, $encounter_id));
379     $old_invoice_refno = $encrow['invoice_refno'];
380   //
381     $usingirnpools = getInvoiceRefNumber();
382   // If not (undoing a checkout or using IRN pools), nothing is done.
383     if ($purge || $usingirnpools) {
384         $query = "INSERT INTO voids SET " .
385         "patient_id = ?, " .
386         "encounter_id = ?, " .
387         "what_voided = ?, " .
388         "date_voided = NOW(), " .
389         "user_id = ?, " .
390         "amount1 = ?, " .
391         "amount2 = ?, " .
392         "other_info = ?";
393         $sqlarr = array($patient_id, $encounter_id, $what_voided, $_SESSION['authUserID'], $row['adjustments'],
394         $row['payments'], $old_invoice_refno);
395         if ($date_original) {
396               $query .= ", date_original = ?";
397               $sqlarr[] = $date_original;
398         }
399         sqlStatement($query, $sqlarr);
400     }
401     if ($purge) {
402         // Purge means delete adjustments and payments from the last checkout
403         // and re-open the visit.
404         if ($date_original) {
405             sqlStatement("DELETE FROM ar_activity WHERE " .
406             "pid = ? AND encounter = ? AND post_time = ?",
407             array($patient_id, $encounter_id, $date_original));
408             sqlStatement("UPDATE billing SET billed = 0, bill_date = NULL WHERE " .
409               "pid = ? AND encounter = ? AND activity = 1 AND " .
410               "bill_date IS NOT NULL AND bill_date = ?",
411               array($patient_id, $encounter_id, $date_original));
412             sqlStatement("update drug_sales SET billed = 0, bill_date = NULL WHERE " .
413               "pid = ? AND encounter = ? AND " .
414               "bill_date IS NOT NULL AND bill_date = ?",
415               array($patient_id, $encounter_id, $date_original));
416         }
417         else {
418             if ($time == 'all') {
419                 sqlStatement("DELETE FROM ar_activity WHERE " .
420                 "pid = ? AND encounter = ?",
421                 array($patient_id, $encounter_id));
422             }
423             sqlStatement("UPDATE billing SET billed = 0, bill_date = NULL WHERE " .
424             "pid = ? AND encounter = ? AND activity = 1",
425               array($patient_id, $encounter_id));
426             sqlStatement("update drug_sales SET billed = 0, bill_date = NULL WHERE " .
427               "pid = ? AND encounter = ?",
428               array($patient_id, $encounter_id));
429         }
430         sqlStatement("UPDATE form_encounter SET last_level_billed = 0, " .
431         "last_level_closed = 0, stmt_count = 0, last_stmt_date = NULL " .
432         "WHERE pid = ? AND encounter = ?",
433         array($patient_id, $encounter_id));
434     }
435     else if ($usingirnpools) {
436         // Non-purge means just assign a new invoice reference number.
437         $new_invoice_refno = add_escape_custom(updateInvoiceRefNumber());
438         sqlStatement("UPDATE form_encounter " .
439         "SET invoice_refno = ? " .
440         "WHERE pid = ? AND encounter = ?",
441         array($new_invoice_refno, $patient_id, $encounter_id));
442     }