updated adodb package to work with php 7.1
[openemr.git] / library / Claim.class.php
blobf4f3ad7cf808a435dadb9ebbb62f90a0c9c0359a
1 <?php
2 // Copyright (C) 2007-2009 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once(dirname(__FILE__) . "/invoice_summary.inc.php");
11 // This enforces the X12 Basic Character Set. Page A2.
13 function x12clean($str) {
14 return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?= ]/', '', strtoupper($str));
17 // Make sure dates have no formatting and zero filled becomes blank
18 // Handles date time stamp formats as well
20 function cleanDate($date_field)
22 $cleandate = str_replace('-', '', substr($date_field, 0, 10));
24 if(substr_count($cleandate,'0')==8)
26 $cleandate='';
29 return ($cleandate);
32 class Claim {
34 var $pid; // patient id
35 var $encounter_id; // encounter id
36 var $procs; // array of procedure rows from billing table
37 var $diags; // array of icd9 codes from billing table
38 var $diagtype= "ICD9"; // diagnosis code_type.Assume ICD9 unless otherwise specified.
39 var $x12_partner; // row from x12_partners table
40 var $encounter; // row from form_encounter table
41 var $facility; // row from facility table
42 var $billing_facility; // row from facility table
43 var $provider; // row from users table (rendering provider)
44 var $referrer; // row from users table (referring provider)
45 var $supervisor; // row from users table (supervising provider)
46 var $insurance_numbers; // row from insurance_numbers table for current payer
47 var $supervisor_numbers; // row from insurance_numbers table for current payer
48 var $patient_data; // row from patient_data table
49 var $billing_options; // row from form_misc_billing_options table
50 var $invoice; // result from get_invoice_summary()
51 var $payers; // array of arrays, for all payers
52 var $copay; // total of copays from the ar_activity table
54 function loadPayerInfo(&$billrow) {
55 global $sl_err;
56 $encounter_date = substr($this->encounter['date'], 0, 10);
58 // Create the $payers array. This contains data for all insurances
59 // with the current one always at index 0, and the others in payment
60 // order starting at index 1.
62 $this->payers = array();
63 $this->payers[0] = array();
64 $query = "SELECT * FROM insurance_data WHERE " .
65 "pid = '{$this->pid}' AND " .
66 "date <= '$encounter_date' " .
67 "ORDER BY type ASC, date DESC";
68 $dres = sqlStatement($query);
69 $prevtype = '';
70 while ($drow = sqlFetchArray($dres)) {
71 if (strcmp($prevtype, $drow['type']) == 0) continue;
72 $prevtype = $drow['type'];
73 // Very important to look at entries with a missing provider because
74 // they indicate no insurance as of the given date.
75 if (empty($drow['provider'])) continue;
76 $ins = count($this->payers);
77 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers[0]['data'])) $ins = 0;
78 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
79 "id = '" . $drow['provider'] . "'");
80 $orow = new InsuranceCompany($drow['provider']);
81 $this->payers[$ins] = array();
82 $this->payers[$ins]['data'] = $drow;
83 $this->payers[$ins]['company'] = $crow;
84 $this->payers[$ins]['object'] = $orow;
87 // This kludge hands most cases of a rare ambiguous situation, where
88 // the primary insurance company is the same as the secondary. It seems
89 // nobody planned for that!
91 for ($i = 1; $i < count($this->payers); ++$i) {
92 if ($billrow['process_date'] &&
93 $this->payers[0]['data']['provider'] == $this->payers[$i]['data']['provider'])
95 $tmp = $this->payers[0];
96 $this->payers[0] = $this->payers[$i];
97 $this->payers[$i] = $tmp;
101 $this->using_modifiers = true;
103 // Get payment and adjustment details if there are any previous payers.
105 $this->invoice = array();
106 if ($this->payerSequence() != 'P') {
107 $this->invoice = ar_get_invoice_summary($this->pid, $this->encounter_id, true);
108 // Secondary claims might not have modifiers in SQL-Ledger data.
109 // In that case, note that we should not try to match on them.
110 $this->using_modifiers = false;
111 foreach ($this->invoice as $key => $trash) {
112 if (strpos($key, ':')) $this->using_modifiers = true;
117 // Constructor. Loads relevant database information.
119 function __construct($pid, $encounter_id) {
120 $this->pid = $pid;
121 $this->encounter_id = $encounter_id;
122 $this->procs = array();
123 $this->diags = array();
124 $this->copay = 0;
126 // We need the encounter date before we can identify the payers.
127 $sql = "SELECT * FROM form_encounter WHERE " .
128 "pid = '{$this->pid}' AND " .
129 "encounter = '{$this->encounter_id}'";
130 $this->encounter = sqlQuery($sql);
132 // Sort by procedure timestamp in order to get some consistency.
133 $sql = "SELECT b.id, b.date, b.code_type, b.code, b.pid, b.provider_id, " .
134 "b.user, b.groupname, b.authorized, b.encounter, b.code_text, b.billed, " .
135 "b.activity, b.payer_id, b.bill_process, b.bill_date, b.process_date, " .
136 "b.process_file, b.modifier, b.units, b.fee, b.justify, b.target, b.x12_partner_id, " .
137 "b.ndc_info, b.notecodes, ct.ct_diag " .
138 "FROM billing as b INNER JOIN code_types as ct " .
139 "ON b.code_type = ct.ct_key " .
140 "WHERE ct.ct_claim = '1' AND ct.ct_active = '1' AND " .
141 "b.encounter = '{$this->encounter_id}' AND b.pid = '{$this->pid}' AND " .
142 "b.activity = '1' ORDER BY b.date, b.id";
143 $res = sqlStatement($sql);
144 while ($row = sqlFetchArray($res)) {
145 // Save all diagnosis codes.
146 if ($row['ct_diag'] == '1') {
147 $this->diags[$row['code']] = $row['code'];
148 continue;
150 if (!$row['units']) $row['units'] = 1;
151 // Load prior payer data at the first opportunity in order to get
152 // the using_modifiers flag that is referenced below.
153 if (empty($this->procs)) $this->loadPayerInfo($row);
155 // The consolidate duplicate procedures, which was previously here, was removed
156 // from codebase on 12/9/15. Reason: Some insurance companies decline consolidated
157 // procedures, and this can be left up to the billing coder when they input the items.
159 // If there is a row-specific provider then get its details.
160 if (!empty($row['provider_id'])) {
161 // Get service provider data for this row.
162 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
163 $row['provider'] = sqlQuery($sql);
164 // Get insurance numbers for this row's provider.
165 $sql = "SELECT * FROM insurance_numbers WHERE " .
166 "(insurance_company_id = '" . $row['payer_id'] .
167 "' OR insurance_company_id is NULL) AND " .
168 "provider_id = '" . $row['provider_id'] . "' " .
169 "ORDER BY insurance_company_id DESC LIMIT 1";
170 $row['insurance_numbers'] = sqlQuery($sql);
173 $this->procs[] = $row;
176 $resMoneyGot = sqlStatement("SELECT pay_amount as PatientPay,session_id as id,".
177 "date(post_time) as date FROM ar_activity where pid ='{$this->pid}' and encounter ='{$this->encounter_id}' ".
178 "and payer_type=0 and account_code='PCP'");
179 //new fees screen copay gives account_code='PCP'
180 while($rowMoneyGot = sqlFetchArray($resMoneyGot)){
181 $PatientPay=$rowMoneyGot['PatientPay']*-1;
182 $this->copay -= $PatientPay;
185 $sql = "SELECT * FROM x12_partners WHERE " .
186 "id = '" . $this->procs[0]['x12_partner_id'] . "'";
187 $this->x12_partner = sqlQuery($sql);
189 $sql = "SELECT * FROM facility WHERE " .
190 "id = '" . addslashes($this->encounter['facility_id']) . "' " .
191 "LIMIT 1";
192 $this->facility = sqlQuery($sql);
194 /*****************************************************************
195 $provider_id = $this->procs[0]['provider_id'];
196 *****************************************************************/
197 $provider_id = $this->encounter['provider_id'];
198 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
199 $this->provider = sqlQuery($sql);
200 // Selecting the billing facility assigned to the encounter. If none,
201 // try the first (and hopefully only) facility marked as a billing location.
202 if (empty($this->encounter['billing_facility'])) {
203 $sql = "SELECT * FROM facility " .
204 "ORDER BY billing_location DESC, id ASC LIMIT 1";
206 else {
207 $sql = "SELECT * FROM facility " .
208 " where id ='" . addslashes($this->encounter['billing_facility']) . "' ";
210 $this->billing_facility = sqlQuery($sql);
212 $sql = "SELECT * FROM insurance_numbers WHERE " .
213 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
214 "' OR insurance_company_id is NULL) AND " .
215 "provider_id = '$provider_id' " .
216 "ORDER BY insurance_company_id DESC LIMIT 1";
217 $this->insurance_numbers = sqlQuery($sql);
219 $sql = "SELECT * FROM patient_data WHERE " .
220 "pid = '{$this->pid}' " .
221 "ORDER BY id LIMIT 1";
222 $this->patient_data = sqlQuery($sql);
224 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
225 "ON fpa.id = forms.form_id WHERE " .
226 "forms.encounter = '{$this->encounter_id}' AND " .
227 "forms.pid = '{$this->pid}' AND " .
228 "forms.deleted = 0 AND " .
229 "forms.formdir = 'misc_billing_options' " .
230 "ORDER BY forms.date";
231 $this->billing_options = sqlQuery($sql);
233 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
234 $this->insurance_numbers['provider_number_type'] != '1C') ?
235 $this->patient_data['ref_providerID'] : $provider_id;
236 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
237 $this->referrer = sqlQuery($sql);
238 if (!$this->referrer) $this->referrer = array();
240 $supervisor_id = $this->encounter['supervisor_id'];
241 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
242 $this->supervisor = sqlQuery($sql);
243 if (!$this->supervisor) $this->supervisor = array();
245 $billing_options_id = $this->billing_options['provider_id'];
246 $sql = "SELECT * FROM users WHERE id = ?";
247 $this->billing_prov_id = sqlQuery($sql, array($billing_options_id));
248 if (!$this->billing_prov_id) $this->billing_prov_id = array();
250 $sql = "SELECT * FROM insurance_numbers WHERE " .
251 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
252 "' OR insurance_company_id is NULL) AND " .
253 "provider_id = '$supervisor_id' " .
254 "ORDER BY insurance_company_id DESC LIMIT 1";
255 $this->supervisor_numbers = sqlQuery($sql);
256 if (!$this->supervisor_numbers) $this->supervisor_numbers = array();
258 } // end constructor
260 // Return an array of adjustments from the designated prior payer for the
261 // designated procedure key (might be procedure:modifier), or for the claim
262 // level. For each adjustment give date, group code, reason code, amount.
263 // Note this will include "patient responsibility" adjustments which are
264 // not adjustments to OUR invoice, but they reduce the amount that the
265 // insurance company pays.
267 function payerAdjustments($ins, $code='Claim') {
268 $aadj = array();
270 // If we have no modifiers stored in SQL-Ledger for this claim,
271 // then we cannot use a modifier passed in with the key.
272 $tmp = strpos($code, ':');
273 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
275 // For payments, source always starts with "Ins" or "Pt".
276 // Nonzero adjustment reason examples:
277 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
278 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
279 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
280 // Ins1 adjust code A2 (Contractual adjustment)
281 // Ins adjust Ins1
282 // adjust code 45
283 // Zero adjustment reason examples:
284 // Co-pay: 25.00
285 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
286 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
287 // To copay Ins1 (manual entry)
288 // To ded'ble Ins1 (manual entry)
290 if (!empty($this->invoice[$code])) {
291 $date = '';
292 $deductible = 0;
293 $coinsurance = 0;
294 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
295 $insnumber = substr($inslabel, 3);
297 // Compute this procedure's patient responsibility amount as of this
298 // prior payer, which is the original charge minus all insurance
299 // payments and "hard" adjustments up to this payer.
300 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
301 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
303 // plv (from ar_activity.payer_type) exists to
304 // indicate the payer level.
305 if (isset($value['pmt']) && $value['pmt'] != 0) {
306 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
307 $ptresp -= $value['pmt'];
309 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
310 // non-blank key indicates this is an adjustment and not a charge
311 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
312 $ptresp += $value['chg']; // adjustments are negative charges
315 $msp = isset( $value['msp'] ) ? $value['msp'] : null; // record the reason for adjustment
317 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
319 // Main loop, to extract adjustments for this payer and procedure.
320 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
321 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
322 if ($tmp) $date = $tmp;
323 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
324 $rsn = $value['rsn'];
325 $chg = 0 - $value['chg']; // adjustments are negative charges
327 $gcode = 'CO'; // default group code = contractual obligation
328 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
330 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
331 // From manual post. Take the defaults.
333 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
334 $coinsurance = $ptresp; // from manual post
335 continue;
337 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
338 $deductible = $ptresp; // from manual post
339 continue;
341 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
342 $coinsurance = $tmp[1]; // from 835 as of 6/2007
343 continue;
345 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
346 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
347 continue;
349 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
350 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
351 continue;
353 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
354 continue; // from 835 as of 6/2007
356 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
357 $rcode = $tmp[1]; // from 835
359 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
360 // Take the defaults.
362 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
363 continue; // it's for some other payer
365 else if ($insnumber == '1') {
366 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
367 $rcode = $tmp[1]; // from 835
369 else if ($chg) {
370 // Other adjustments default to Ins1.
372 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
373 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
374 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
375 continue;
377 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
378 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
379 continue;
381 else {
382 continue; // there is no adjustment amount
385 else {
386 continue; // it's for primary and that's not us
389 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
390 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
392 } // end if
393 } // end foreach
395 // If we really messed it up, at least avoid negative numbers.
396 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
397 if ($deductible > $ptresp) $deductible = $ptresp;
399 // Find out if this payer paid anything at all on this claim. This will
400 // help us allocate any unknown patient responsibility amounts.
401 $thispaidanything = 0;
402 foreach($this->invoice as $codekey => $codeval) {
403 foreach ($codeval['dtl'] as $key => $value) {
404 // plv exists to indicate the payer level.
405 if ($value['plv'] == $insnumber) {
406 $thispaidanything += $value['pmt'];
411 // Allocate any unknown patient responsibility by guessing if the
412 // deductible has been satisfied.
413 if ($thispaidanything)
414 $coinsurance = $ptresp - $deductible;
415 else
416 $deductible = $ptresp - $coinsurance;
418 $deductible = sprintf('%.2f', $deductible);
419 $coinsurance = sprintf('%.2f', $coinsurance);
421 if ($date && $deductible != 0)
422 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
423 if ($date && $coinsurance != 0)
424 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
426 } // end if
428 return $aadj;
431 // Return date, total payments and total "hard" adjustments from the given
432 // prior payer. If $code is specified then only that procedure key is
433 // selected, otherwise it's for the whole claim.
435 function payerTotals($ins, $code='') {
436 // If we have no modifiers stored in SQL-Ledger for this claim,
437 // then we cannot use a modifier passed in with the key.
438 $tmp = strpos($code, ':');
439 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
441 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
442 $insnumber = substr($inslabel, 3);
443 $paytotal = 0;
444 $adjtotal = 0;
445 $date = '';
446 foreach($this->invoice as $codekey => $codeval) {
447 if ($code && strcmp($codekey,$code) != 0) continue;
448 foreach ($codeval['dtl'] as $key => $value) {
449 // plv (from ar_activity.payer_type) exists to
450 // indicate the payer level.
451 if ($value['plv'] == $insnumber) {
452 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
453 $paytotal += $value['pmt'];
456 $aarr = $this->payerAdjustments($ins, $codekey);
457 foreach ($aarr as $a) {
458 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
459 if (!$date) $date = $a[0];
462 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
465 // Return the amount already paid by the patient.
467 function patientPaidAmount() {
468 // For primary claims $this->invoice is not loaded, so get the co-pay
469 // from the ar_activity table instead.
470 if (empty($this->invoice)) return $this->copay;
472 $amount = 0;
473 foreach($this->invoice as $codekey => $codeval) {
474 foreach ($codeval['dtl'] as $key => $value) {
475 // plv exists to indicate the payer level.
476 if ($value['plv'] == 0) { // 0 indicates patient
477 $amount += $value['pmt'];
481 return sprintf('%.2f', $amount);
484 // Return invoice total, including adjustments but not payments.
486 function invoiceTotal() {
487 $amount = 0;
488 foreach($this->invoice as $codekey => $codeval) {
489 $amount += $codeval['chg'];
491 return sprintf('%.2f', $amount);
494 // Number of procedures in this claim.
495 function procCount() {
496 return count($this->procs);
499 // Number of payers for this claim. Ranges from 1 to 3.
500 function payerCount() {
501 return count($this->payers);
504 function x12gsversionstring() {
505 return x12clean(trim($this->x12_partner['x12_version']));
508 function x12gssenderid() {
509 $tmp = $this->x12_partner['x12_sender_id'];
510 while (strlen($tmp) < 15) $tmp .= " ";
511 return $tmp;
514 function x12gs03() {
516 * GS03: Application Receiver's Code
517 * Code Identifying Party Receiving Transmission
519 * In most cases, the ISA08 and GS03 are the same. However
521 * In some clearing houses ISA08 and GS03 are different
522 * Example: http://www.acs-gcro.com/downloads/DOL/DOL_CG_X12N_5010_837_v1_02.pdf - Page 18
523 * In this .pdf, the ISA08 is specified to be 100000 while the GS03 is specified to be 77044
525 * Therefore if the x12_gs03 segement is explicitly specified we use that value,
526 * otherwise we simply use the same receiver ID as specified for ISA03
528 if($this->x12_partner['x12_gs03'] !== '')
529 return $this->x12_partner['x12_gs03'];
530 else
531 return $this->x12_partner['x12_receiver_id'];
534 function x12gsreceiverid() {
535 $tmp = $this->x12_partner['x12_receiver_id'];
536 while (strlen($tmp) < 15) $tmp .= " ";
537 return $tmp;
540 function x12gsisa05() {
541 return $this->x12_partner['x12_isa05'];
543 //adding in functions for isa 01 - isa 04
545 function x12gsisa01() {
546 return $this->x12_partner['x12_isa01'];
549 function x12gsisa02() {
550 return $this->x12_partner['x12_isa02'];
553 function x12gsisa03() {
554 return $this->x12_partner['x12_isa03'];
556 function x12gsisa04() {
557 return $this->x12_partner['x12_isa04'];
560 /////////
561 function x12gsisa07() {
562 return $this->x12_partner['x12_isa07'];
565 function x12gsisa14() {
566 return $this->x12_partner['x12_isa14'];
569 function x12gsisa15() {
570 return $this->x12_partner['x12_isa15'];
573 function x12gsgs02() {
574 $tmp = $this->x12_partner['x12_gs02'];
575 if ($tmp === '') $tmp = $this->x12_partner['x12_sender_id'];
576 return $tmp;
579 function x12gsper06() {
580 return $this->x12_partner['x12_per06'];
583 function cliaCode() {
584 return x12clean(trim($this->facility['domain_identifier']));
587 function billingFacilityName() {
588 return x12clean(trim($this->billing_facility['name']));
591 function billingFacilityStreet() {
592 return x12clean(trim($this->billing_facility['street']));
595 function billingFacilityCity() {
596 return x12clean(trim($this->billing_facility['city']));
599 function billingFacilityState() {
600 return x12clean(trim($this->billing_facility['state']));
603 function billingFacilityZip() {
604 return x12clean(trim($this->billing_facility['postal_code']));
607 function billingFacilityETIN() {
608 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
611 function billingFacilityNPI() {
612 return x12clean(trim($this->billing_facility['facility_npi']));
615 function federalIdType() {
616 if ($this->billing_facility['tax_id_type'])
618 return $this->billing_facility['tax_id_type'];
620 else{
621 return null;
625 # The billing facility and the patient must both accept for this to return true.
626 function billingFacilityAssignment($ins=0) {
627 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
628 if (strcmp($tmp,'FALSE') == 0) return '0';
629 return !empty($this->billing_facility['accepts_assignment']);
632 function billingContactName() {
633 return x12clean(trim($this->billing_facility['attn']));
636 function billingContactPhone() {
637 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
638 $this->billing_facility['phone'], $tmp))
640 return $tmp[1] . $tmp[2] . $tmp[3];
642 return '';
645 function facilityName() {
646 return x12clean(trim($this->facility['name']));
649 function facilityStreet() {
650 return x12clean(trim($this->facility['street']));
653 function facilityCity() {
654 return x12clean(trim($this->facility['city']));
657 function facilityState() {
658 return x12clean(trim($this->facility['state']));
661 function facilityZip() {
662 return x12clean(trim($this->facility['postal_code']));
665 function facilityETIN() {
666 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
669 function facilityNPI() {
670 return x12clean(trim($this->facility['facility_npi']));
673 function facilityPOS() {
674 if($this->encounter['pos_code']){
675 return sprintf('%02d', trim($this->encounter['pos_code']));
676 }else{
677 return sprintf('%02d', trim($this->facility['pos_code']));
681 function clearingHouseName() {
682 return x12clean(trim($this->x12_partner['name']));
685 function clearingHouseETIN() {
686 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
689 function providerNumberType($prockey=-1) {
690 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
691 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
692 return $tmp['provider_number_type'];
695 function providerNumber($prockey=-1) {
696 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
697 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
698 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
701 function providerGroupNumber($prockey=-1) {
702 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
703 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
704 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
707 // Returns 'P', 'S' or 'T'.
709 function payerSequence($ins=0) {
710 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
713 // Returns the HIPAA code of the patient-to-subscriber relationship.
715 function insuredRelationship($ins=0) {
716 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
717 if (strcmp($tmp,'self' ) == 0) return '18';
718 if (strcmp($tmp,'spouse') == 0) return '01';
719 if (strcmp($tmp,'child' ) == 0) return '19';
720 if (strcmp($tmp,'other' ) == 0) return 'G8';
721 return $tmp; // should not happen
724 function insuredTypeCode($ins=0) {
725 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
726 return $this->payers[$ins]['data']['policy_type'];
727 return '';
730 // Is the patient also the subscriber?
732 function isSelfOfInsured($ins=0) {
733 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
734 return (strcmp($tmp,'self') == 0);
737 function planName($ins=0) {
738 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
741 function policyNumber($ins=0) { // "ID"
742 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
745 function groupNumber($ins=0) {
746 return x12clean(trim($this->payers[$ins]['data']['group_number']));
749 function groupName($ins=0) {
750 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
753 // Claim types are:
754 // 16 Other HCFA
755 // MB Medicare Part B
756 // MC Medicaid
757 // CH ChampUSVA
758 // CH ChampUS
759 // BL Blue Cross Blue Shield
760 // 16 FECA
761 // 09 Self Pay
762 // 10 Central Certification
763 // 11 Other Non-Federal Programs
764 // 12 Preferred Provider Organization (PPO)
765 // 13 Point of Service (POS)
766 // 14 Exclusive Provider Organization (EPO)
767 // 15 Indemnity Insurance
768 // 16 Health Maintenance Organization (HMO) Medicare Risk
769 // AM Automobile Medical
770 // CI Commercial Insurance Co.
771 // DS Disability
772 // HM Health Maintenance Organization
773 // LI Liability
774 // LM Liability Medical
775 // OF Other Federal Program
776 // TV Title V
777 // VA Veterans Administration Plan
778 // WC Workers Compensation Health Plan
779 // ZZ Mutually Defined
781 function claimType($ins=0) {
782 if (empty($this->payers[$ins]['object'])) return '';
783 return $this->payers[$ins]['object']->get_ins_claim_type();
786 function claimTypeRaw($ins=0) {
787 if (empty($this->payers[$ins]['object'])) return 0;
788 return $this->payers[$ins]['object']->get_ins_type_code();
791 function insuredLastName($ins=0) {
792 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
795 function insuredFirstName($ins=0) {
796 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
799 function insuredMiddleName($ins=0) {
800 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
803 function insuredStreet($ins=0) {
804 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
807 function insuredCity($ins=0) {
808 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
811 function insuredState($ins=0) {
812 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
815 function insuredZip($ins=0) {
816 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
819 function insuredPhone($ins=0) {
820 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
821 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
822 return $tmp[1] . $tmp[2] . $tmp[3];
823 return '';
826 function insuredDOB($ins=0) {
827 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
830 function insuredSex($ins=0) {
831 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
834 function payerName($ins=0) {
835 return x12clean(trim($this->payers[$ins]['company']['name']));
838 function payerAttn($ins=0) {
839 return x12clean(trim($this->payers[$ins]['company']['attn']));
842 function payerStreet($ins=0) {
843 if (empty($this->payers[$ins]['object'])) return '';
844 $tmp = $this->payers[$ins]['object'];
845 $tmp = $tmp->get_address();
846 return x12clean(trim($tmp->get_line1()));
849 function payerCity($ins=0) {
850 if (empty($this->payers[$ins]['object'])) return '';
851 $tmp = $this->payers[$ins]['object'];
852 $tmp = $tmp->get_address();
853 return x12clean(trim($tmp->get_city()));
856 function payerState($ins=0) {
857 if (empty($this->payers[$ins]['object'])) return '';
858 $tmp = $this->payers[$ins]['object'];
859 $tmp = $tmp->get_address();
860 return x12clean(trim($tmp->get_state()));
863 function payerZip($ins=0) {
864 if (empty($this->payers[$ins]['object'])) return '';
865 $tmp = $this->payers[$ins]['object'];
866 $tmp = $tmp->get_address();
867 return x12clean(trim($tmp->get_zip()));
870 function payerID($ins=0) {
871 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
874 function payerAltID($ins=0) {
875 return x12clean(trim($this->payers[$ins]['company']['alt_cms_id']));
878 function patientLastName() {
879 return x12clean(trim($this->patient_data['lname']));
882 function patientFirstName() {
883 return x12clean(trim($this->patient_data['fname']));
886 function patientMiddleName() {
887 return x12clean(trim($this->patient_data['mname']));
890 function patientStreet() {
891 return x12clean(trim($this->patient_data['street']));
894 function patientCity() {
895 return x12clean(trim($this->patient_data['city']));
898 function patientState() {
899 return x12clean(trim($this->patient_data['state']));
902 function patientZip() {
903 return x12clean(trim($this->patient_data['postal_code']));
906 function patientPhone() {
907 $ptphone = $this->patient_data['phone_home'];
908 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
909 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
910 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
911 return $tmp[1] . $tmp[2] . $tmp[3];
912 return '';
915 function patientDOB() {
916 return str_replace('-', '', $this->patient_data['DOB']);
919 function patientSex() {
920 return strtoupper(substr($this->patient_data['sex'], 0, 1));
923 // Patient Marital Status: M = Married, S = Single, or something else.
924 function patientStatus() {
925 return strtoupper(substr($this->patient_data['status'], 0, 1));
928 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
929 // indicate employed.
930 function patientOccupation() {
931 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
934 function cptCode($prockey) {
935 return x12clean(trim($this->procs[$prockey]['code']));
938 function cptModifier($prockey) {
939 // Split on the colon or space and clean each modifier
940 $mods = array();
941 $cln_mods = array();
942 $mods = preg_split("/[: ]/",trim($this->procs[$prockey]['modifier']));
943 foreach ($mods as $mod) {
944 array_push($cln_mods, x12clean($mod));
946 return (implode (':', $cln_mods));
949 function cptNotecodes($prockey) {
950 return x12clean(trim($this->procs[$prockey]['notecodes']));
953 // Returns the procedure code, followed by ":modifier" if there is one.
954 function cptKey($prockey) {
955 $tmp = $this->cptModifier($prockey);
956 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
959 function cptCharges($prockey) {
960 return x12clean(trim($this->procs[$prockey]['fee']));
963 function cptUnits($prockey) {
964 if (empty($this->procs[$prockey]['units'])) return '1';
965 return x12clean(trim($this->procs[$prockey]['units']));
968 // NDC drug ID.
969 function cptNDCID($prockey) {
970 $ndcinfo = $this->procs[$prockey]['ndc_info'];
971 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
972 $ndc = $tmp[1];
973 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
974 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
976 return x12clean($ndc); // format is bad but return it anyway
978 return '';
981 // NDC drug unit of measure code.
982 function cptNDCUOM($prockey) {
983 $ndcinfo = $this->procs[$prockey]['ndc_info'];
984 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
985 return x12clean($tmp[2]);
986 return '';
989 // NDC drug number of units.
990 function cptNDCQuantity($prockey) {
991 $ndcinfo = $this->procs[$prockey]['ndc_info'];
992 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
993 return x12clean(ltrim($tmp[3], '0'));
995 return '';
998 function onsetDate() {
999 return cleanDate($this->encounter['onset_date']);
1002 function onsetDateValid()
1004 return $this->onsetDate()!=='';
1007 function serviceDate() {
1008 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
1011 function priorAuth() {
1012 return x12clean(trim($this->billing_options['prior_auth_number']));
1015 function isRelatedEmployment() {
1016 return !empty($this->billing_options['employment_related']);
1019 function isRelatedAuto() {
1020 return !empty($this->billing_options['auto_accident']);
1023 function isRelatedOther() {
1024 return !empty($this->billing_options['other_accident']);
1027 function autoAccidentState() {
1028 return x12clean(trim($this->billing_options['accident_state']));
1031 function isUnableToWork() {
1032 return !empty($this->billing_options['is_unable_to_work']);
1035 function offWorkFrom() {
1036 return cleanDate($this->billing_options['off_work_from']);
1039 function offWorkTo() {
1040 return cleanDate($this->billing_options['off_work_to']);
1043 function isHospitalized() {
1044 return !empty($this->billing_options['is_hospitalized']);
1047 function hospitalizedFrom() {
1048 return cleanDate($this->billing_options['hospitalization_date_from']);
1051 function hospitalizedTo() {
1052 return cleanDate($this->billing_options['hospitalization_date_to']);
1055 function isOutsideLab() {
1056 return !empty($this->billing_options['outside_lab']);
1059 function outsideLabAmount() {
1060 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
1063 function medicaidReferralCode() {
1064 return x12clean(trim($this->billing_options['medicaid_referral_code']));
1067 function epsdtFlag() {
1068 return x12clean(trim($this->billing_options['epsdt_flag']));
1071 function medicaidResubmissionCode() {
1072 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
1075 function medicaidOriginalReference() {
1076 return x12clean(trim($this->billing_options['medicaid_original_reference']));
1079 function frequencyTypeCode() {
1080 return ($this->billing_options['replacement_claim'] == 1) ? '7' : '1';
1083 function icnResubmissionNumber() {
1084 return x12clean($this->billing_options['icn_resubmission_number']);
1087 function additionalNotes() {
1088 return x12clean(trim($this->billing_options['comments']));
1091 function dateInitialTreatment() {
1092 return cleanDate($this->billing_options['date_initial_treatment']);
1095 function box14qualifier()
1097 // If no box qualifier specified use "431" indicating Onset
1098 return empty($this->billing_options['box_14_date_qual']) ? '431' :
1099 $this->billing_options['box_14_date_qual'];
1102 function box15qualifier()
1104 // If no box qualifier specified use "454" indicating Initial Treatment
1105 return empty($this->billing_options['box_15_date_qual']) ? '454' :
1106 $this->billing_options['box_15_date_qual'];
1108 // Returns an array of unique diagnoses. Periods are stripped by default
1109 // Option to keep periods is to support HCFA 1500 02/12 version
1110 function diagArray($strip_periods=true) {
1111 $da = array();
1112 foreach ($this->procs as $row) {
1113 $atmp = explode(':', $row['justify']);
1114 foreach ($atmp as $tmp) {
1115 if (!empty($tmp)) {
1116 $code_data = explode('|',$tmp);
1118 // If there was a | in the code data, the the first part of the array is the type, and the second is the identifier
1119 if (!empty($code_data[1])) {
1121 // This is the simplest way to determine if the claim is using ICD9 or ICD10 codes
1122 // a mix of code types is generally not allowed as there is only one specifier for all diagnoses on HCFA-1500 form
1123 // and there would be ambiguity with E and V codes
1124 $this->diagtype=$code_data[0];
1126 //code is in the second part of the $code_data array.
1127 if($strip_periods==true)
1129 $diag = str_replace('.', '', $code_data[1]);
1132 else
1134 $diag=$code_data[1];
1138 else {
1139 //No prepended code type label
1140 if($strip_periods) {
1141 $diag = str_replace('.', '', $code_data[0]);
1143 else
1145 $diag=$code_data[0];
1148 $da[$diag] = $diag;
1152 // The above got all the diagnoses used for justification, in the order
1153 // used for justification. Next we go through all diagnoses, justified
1154 // or not, to make sure they all get into the claim. We do it this way
1155 // so that the more important diagnoses appear first.
1156 foreach ($this->diags as $diag) {
1157 if($strip_periods) {$diag = str_replace('.', '', $diag);}
1158 $da[$diag] = $diag;
1160 return $da;
1163 // Compute one 1-relative index in diagArray for the given procedure.
1164 // This function is obsolete, use diagIndexArray() instead.
1165 function diagIndex($prockey) {
1166 $da = $this->diagArray();
1167 $tmp = explode(':', $this->procs[$prockey]['justify']);
1168 if (empty($tmp)) return '';
1169 $diag = str_replace('.', '', $tmp[0]);
1170 $i = 0;
1171 foreach ($da as $value) {
1172 ++$i;
1173 if (strcmp($value,$diag) == 0) return $i;
1175 return '';
1178 // Compute array of 1-relative diagArray indices for the given procedure.
1179 function diagIndexArray($prockey) {
1180 $dia = array();
1181 $da = $this->diagArray();
1182 $atmp = explode(':', $this->procs[$prockey]['justify']);
1183 foreach ($atmp as $tmp) {
1184 if (!empty($tmp)) {
1185 $code_data = explode('|',$tmp);
1186 if (!empty($code_data[1])) {
1187 //Strip the prepended code type label
1188 $diag = str_replace('.', '', $code_data[1]);
1190 else {
1191 //No prepended code type label
1192 $diag = str_replace('.', '', $code_data[0]);
1194 $i = 0;
1195 foreach ($da as $value) {
1196 ++$i;
1197 if (strcmp($value,$diag) == 0) $dia[] = $i;
1201 return $dia;
1204 function providerLastName($prockey=-1) {
1205 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1206 $this->provider : $this->procs[$prockey]['provider'];
1207 return x12clean(trim($tmp['lname']));
1210 function providerFirstName($prockey=-1) {
1211 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1212 $this->provider : $this->procs[$prockey]['provider'];
1213 return x12clean(trim($tmp['fname']));
1216 function providerMiddleName($prockey=-1) {
1217 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1218 $this->provider : $this->procs[$prockey]['provider'];
1219 return x12clean(trim($tmp['mname']));
1222 function providerNPI($prockey=-1) {
1223 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1224 $this->provider : $this->procs[$prockey]['provider'];
1225 return x12clean(trim($tmp['npi']));
1228 function NPIValid($npi)
1230 // A NPI MUST be a 10 digit number
1231 if($npi==='') return false;
1232 if(strlen($npi)!=10) return false;
1233 if(!preg_match("/[0-9]*/",$npi)) return false;
1234 return true;
1237 function providerNPIValid($prockey=-1)
1239 return $this->NPIValid($this->providerNPI($prockey));
1242 function providerUPIN($prockey=-1) {
1243 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1244 $this->provider : $this->procs[$prockey]['provider'];
1245 return x12clean(trim($tmp['upin']));
1248 function providerSSN($prockey=-1) {
1249 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1250 $this->provider : $this->procs[$prockey]['provider'];
1251 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1254 function providerTaxonomy($prockey=-1) {
1255 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1256 $this->provider : $this->procs[$prockey]['provider'];
1257 if (empty($tmp['taxonomy'])) return '207Q00000X';
1258 return x12clean(trim($tmp['taxonomy']));
1261 function referrerLastName() {
1262 return x12clean(trim($this->referrer['lname']));
1265 function referrerFirstName() {
1266 return x12clean(trim($this->referrer['fname']));
1269 function referrerMiddleName() {
1270 return x12clean(trim($this->referrer['mname']));
1273 function referrerNPI() {
1274 return x12clean(trim($this->referrer['npi']));
1277 function referrerUPIN() {
1278 return x12clean(trim($this->referrer['upin']));
1281 function referrerSSN() {
1282 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1285 function referrerTaxonomy() {
1286 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1287 return x12clean(trim($this->referrer['taxonomy']));
1290 function supervisorLastName() {
1291 return x12clean(trim($this->supervisor['lname']));
1294 function supervisorFirstName() {
1295 return x12clean(trim($this->supervisor['fname']));
1298 function supervisorMiddleName() {
1299 return x12clean(trim($this->supervisor['mname']));
1302 function supervisorNPI() {
1303 return x12clean(trim($this->supervisor['npi']));
1306 function supervisorUPIN() {
1307 return x12clean(trim($this->supervisor['upin']));
1310 function supervisorSSN() {
1311 return x12clean(trim(str_replace('-', '', $this->supervisor['federaltaxid'])));
1314 function supervisorTaxonomy() {
1315 if (empty($this->supervisor['taxonomy'])) return '207Q00000X';
1316 return x12clean(trim($this->supervisor['taxonomy']));
1319 function supervisorNumberType() {
1320 return $this->supervisor_numbers['provider_number_type'];
1323 function supervisorNumber() {
1324 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers['provider_number'])));
1327 function billingProviderLastName() {
1328 return x12clean(trim($this->billing_prov_id['lname']));
1331 function billingProviderFirstName() {
1332 return x12clean(trim($this->billing_prov_id['fname']));
1335 function billingProviderMiddleName() {
1336 return x12clean(trim($this->billing_prov_id['mname']));
1339 function billingProviderNPI() {
1340 return x12clean(trim($this->billing_prov_id['npi']));
1343 function billingProviderUPIN() {
1344 return x12clean(trim($this->billing_prov_id['upin']));
1347 function billingProviderSSN() {
1348 return x12clean(trim(str_replace('-', '', $this->billing_prov_id['federaltaxid'])));
1351 function billingProviderTaxonomy() {
1352 if (empty($this->billing_prov_id['taxonomy'])) return '207Q00000X';
1353 return x12clean(trim($this->billing_prov_id['taxonomy']));