2 // Copyright (C) 2007-2009 Rod Roark <rod@sunsetsystems.com>
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__
) . "/classes/Address.class.php");
10 require_once(dirname(__FILE__
) . "/classes/InsuranceCompany.class.php");
11 require_once(dirname(__FILE__
) . "/sql-ledger.inc");
12 require_once(dirname(__FILE__
) . "/invoice_summary.inc.php");
14 // This enforces the X12 Basic Character Set. Page A2.
16 function x12clean($str) {
17 return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?= ]/', '', strtoupper($str));
22 var $pid; // patient id
23 var $encounter_id; // encounter id
24 var $procs; // array of procedure rows from billing table
25 var $diags; // array of icd9 codes from billing table
26 var $x12_partner; // row from x12_partners table
27 var $encounter; // row from form_encounter table
28 var $facility; // row from facility table
29 var $billing_facility; // row from facility table
30 var $provider; // row from users table (rendering provider)
31 var $referrer; // row from users table (referring provider)
32 var $supervisor; // row from users table (supervising provider)
33 var $insurance_numbers; // row from insurance_numbers table for current payer
34 var $supervisor_numbers; // row from insurance_numbers table for current payer
35 var $patient_data; // row from patient_data table
36 var $billing_options; // row from form_misc_billing_options table
37 var $invoice; // result from get_invoice_summary()
38 var $payers; // array of arrays, for all payers
39 var $copay; // total of copays from the billing table
41 function loadPayerInfo(&$billrow) {
43 $encounter_date = substr($this->encounter
['date'], 0, 10);
45 // Create the $payers array. This contains data for all insurances
46 // with the current one always at index 0, and the others in payment
47 // order starting at index 1.
49 $this->payers
= array();
50 $this->payers
[0] = array();
51 $query = "SELECT * FROM insurance_data WHERE " .
52 "pid = '{$this->pid}' AND " .
53 "date <= '$encounter_date' " .
54 "ORDER BY type ASC, date DESC";
55 $dres = sqlStatement($query);
57 while ($drow = sqlFetchArray($dres)) {
58 if (strcmp($prevtype, $drow['type']) == 0) continue;
59 $prevtype = $drow['type'];
60 // Very important to look at entries with a missing provider because
61 // they indicate no insurance as of the given date.
62 if (empty($drow['provider'])) continue;
63 $ins = count($this->payers
);
64 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers
[0]['data'])) $ins = 0;
65 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
66 "id = '" . $drow['provider'] . "'");
67 $orow = new InsuranceCompany($drow['provider']);
68 $this->payers
[$ins] = array();
69 $this->payers
[$ins]['data'] = $drow;
70 $this->payers
[$ins]['company'] = $crow;
71 $this->payers
[$ins]['object'] = $orow;
74 // This kludge hands most cases of a rare ambiguous situation, where
75 // the primary insurance company is the same as the secondary. It seems
76 // nobody planned for that!
78 for ($i = 1; $i < count($this->payers
); ++
$i) {
79 if ($billrow['process_date'] &&
80 $this->payers
[0]['data']['provider'] == $this->payers
[$i]['data']['provider'])
82 $tmp = $this->payers
[0];
83 $this->payers
[0] = $this->payers
[$i];
84 $this->payers
[$i] = $tmp;
88 $this->using_modifiers
= true;
90 // Get payment and adjustment details if there are any previous payers.
92 $this->invoice
= array();
93 if ($this->payerSequence() != 'P') {
94 if ($GLOBALS['oer_config']['ws_accounting']['enabled'] === 2) {
95 $this->invoice
= ar_get_invoice_summary($this->pid
, $this->encounter_id
, true);
97 else if ($GLOBALS['oer_config']['ws_accounting']['enabled']) {
99 $arres = SLQuery("select id from ar where invnumber = " .
100 "'{$this->pid}.{$this->encounter_id}'");
101 if ($sl_err) die($sl_err);
102 $arrow = SLGetRow($arres, 0);
104 $this->invoice
= get_invoice_summary($arrow['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 Claim($pid, $encounter_id) {
121 $this->encounter_id
= $encounter_id;
122 $this->procs
= array();
123 $this->diags
= array();
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 * FROM billing WHERE " .
134 "encounter = '{$this->encounter_id}' AND pid = '{$this->pid}' AND " .
135 "(code_type = 'CPT4' OR code_type = 'HCPCS' OR code_type = 'COPAY' OR code_type = 'ICD9') AND " .
136 "activity = '1' ORDER BY date, id";
137 $res = sqlStatement($sql);
138 while ($row = sqlFetchArray($res)) {
139 if ($row['code_type'] == 'COPAY') {
140 $this->copay
-= $row['fee'];
143 // Save all diagnosis codes.
144 if ($row['code_type'] == 'ICD9') {
145 $this->diags
[$row['code']] = $row['code'];
148 if (!$row['units']) $row['units'] = 1;
149 // Load prior payer data at the first opportunity in order to get
150 // the using_modifiers flag that is referenced below.
151 if (empty($this->procs
)) $this->loadPayerInfo($row);
152 // Consolidate duplicate procedures.
153 foreach ($this->procs
as $key => $trash) {
154 if (strcmp($this->procs
[$key]['code'],$row['code']) == 0 &&
155 (strcmp($this->procs
[$key]['modifier'],$row['modifier']) == 0 ||
156 !$this->using_modifiers
))
158 $this->procs
[$key]['units'] +
= $row['units'];
159 $this->procs
[$key]['fee'] +
= $row['fee'];
160 continue 2; // skip to next table row
164 // If there is a row-specific provider then get its details.
165 if (!empty($row['provider_id'])) {
166 // Get service provider data for this row.
167 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
168 $row['provider'] = sqlQuery($sql);
169 // Get insurance numbers for this row's provider.
170 $sql = "SELECT * FROM insurance_numbers WHERE " .
171 "(insurance_company_id = '" . $row['payer_id'] .
172 "' OR insurance_company_id is NULL) AND " .
173 "provider_id = '" . $row['provider_id'] . "' " .
174 "ORDER BY insurance_company_id DESC LIMIT 1";
175 $row['insurance_numbers'] = sqlQuery($sql);
178 $this->procs
[] = $row;
181 $sql = "SELECT * FROM x12_partners WHERE " .
182 "id = '" . $this->procs
[0]['x12_partner_id'] . "'";
183 $this->x12_partner
= sqlQuery($sql);
185 $sql = "SELECT * FROM facility WHERE " .
186 "id = '" . addslashes($this->encounter
['facility_id']) . "' " .
188 $this->facility
= sqlQuery($sql);
190 /*****************************************************************
191 $provider_id = $this->procs[0]['provider_id'];
192 *****************************************************************/
193 $provider_id = $this->encounter
['provider_id'];
194 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
195 $this->provider
= sqlQuery($sql);
196 // Selecting the billing facility assigned to the service facility
197 $sql = "SELECT * FROM facility " .
198 " where id ='" . addslashes($this->encounter
['billing_facility']) . "' ";
199 $this->billing_facility
= sqlQuery($sql);
201 $sql = "SELECT * FROM insurance_numbers WHERE " .
202 "(insurance_company_id = '" . $this->procs
[0]['payer_id'] .
203 "' OR insurance_company_id is NULL) AND " .
204 "provider_id = '$provider_id' " .
205 "ORDER BY insurance_company_id DESC LIMIT 1";
206 $this->insurance_numbers
= sqlQuery($sql);
208 $sql = "SELECT * FROM patient_data WHERE " .
209 "pid = '{$this->pid}' " .
210 "ORDER BY id LIMIT 1";
211 $this->patient_data
= sqlQuery($sql);
213 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
214 "ON fpa.id = forms.form_id WHERE " .
215 "forms.encounter = '{$this->encounter_id}' AND " .
216 "forms.pid = '{$this->pid}' AND " .
217 "forms.formdir = 'misc_billing_options' " .
218 "ORDER BY forms.date";
219 $this->billing_options
= sqlQuery($sql);
221 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
222 $this->insurance_numbers
['provider_number_type'] != '1C') ?
223 $this->patient_data
['providerID'] : $provider_id;
224 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
225 $this->referrer
= sqlQuery($sql);
226 if (!$this->referrer
) $this->referrer
= array();
228 $supervisor_id = $this->encounter
['supervisor_id'];
229 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
230 $this->supervisor
= sqlQuery($sql);
231 if (!$this->supervisor
) $this->supervisor
= array();
233 $sql = "SELECT * FROM insurance_numbers WHERE " .
234 "(insurance_company_id = '" . $this->procs
[0]['payer_id'] .
235 "' OR insurance_company_id is NULL) AND " .
236 "provider_id = '$supervisor_id' " .
237 "ORDER BY insurance_company_id DESC LIMIT 1";
238 $this->supervisor_numbers
= sqlQuery($sql);
239 if (!$this->supervisor_numbers
) $this->supervisor_numbers
= array();
243 // Return an array of adjustments from the designated prior payer for the
244 // designated procedure key (might be procedure:modifier), or for the claim
245 // level. For each adjustment give date, group code, reason code, amount.
246 // Note this will include "patient responsibility" adjustments which are
247 // not adjustments to OUR invoice, but they reduce the amount that the
248 // insurance company pays.
250 function payerAdjustments($ins, $code='Claim') {
253 // If we have no modifiers stored in SQL-Ledger for this claim,
254 // then we cannot use a modifier passed in with the key.
255 $tmp = strpos($code, ':');
256 if ($tmp && !$this->using_modifiers
) $code = substr($code, 0, $tmp);
258 // For payments, source always starts with "Ins" or "Pt".
259 // Nonzero adjustment reason examples:
260 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
261 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
262 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
263 // Ins1 adjust code A2 (Contractual adjustment)
266 // Zero adjustment reason examples:
268 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
269 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
270 // To copay Ins1 (manual entry)
271 // To ded'ble Ins1 (manual entry)
273 if (!empty($this->invoice
[$code])) {
277 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
278 $insnumber = substr($inslabel, 3);
280 // Compute this procedure's patient responsibility amount as of this
281 // prior payer, which is the original charge minus all insurance
282 // payments and "hard" adjustments up to this payer.
283 $ptresp = $this->invoice
[$code]['chg'] +
$this->invoice
[$code]['adj'];
284 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
285 if (isset($value['plv'])) {
286 // New method; plv (from ar_activity.payer_type) exists to
287 // indicate the payer level.
288 if (isset($value['pmt']) && $value['pmt'] != 0) {
289 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
290 $ptresp -= $value['pmt'];
292 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
293 // non-blank key indicates this is an adjustment and not a charge
294 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
295 $ptresp +
= $value['chg']; // adjustments are negative charges
299 // Old method: With SQL-Ledger payer level was stored in the memo.
300 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
301 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
303 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
304 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) ||
$tmp[1] <= $insnumber)
305 $ptresp +
= $value['chg']; // adjustments are negative charges
309 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
311 // Main loop, to extract adjustments for this payer and procedure.
312 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
313 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
314 if ($tmp) $date = $tmp;
315 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
316 $rsn = $value['rsn'];
317 $chg = 0 - $value['chg']; // adjustments are negative charges
319 $gcode = 'CO'; // default group code = contractual obligation
320 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
322 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
323 // From manual post. Take the defaults.
325 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
326 $coinsurance = $ptresp; // from manual post
329 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
330 $deductible = $ptresp; // from manual post
333 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
334 $coinsurance = $tmp[1]; // from 835 as of 6/2007
337 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
338 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
341 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
342 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
345 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
346 continue; // from 835 as of 6/2007
348 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
349 $rcode = $tmp[1]; // from 835
351 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
352 // Take the defaults.
354 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
355 continue; // it's for some other payer
357 else if ($insnumber == '1') {
358 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
359 $rcode = $tmp[1]; // from 835
362 // Other adjustments default to Ins1.
364 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
365 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
366 $coinsurance = 0 +
$tmp[1]; // from 835 before 6/2007
369 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
370 $deductible = 0 +
$tmp[1]; // from 835 before 6/2007
374 continue; // there is no adjustment amount
378 continue; // it's for primary and that's not us
381 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
382 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
387 // If we really messed it up, at least avoid negative numbers.
388 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
389 if ($deductible > $ptresp) $deductible = $ptresp;
391 // Find out if this payer paid anything at all on this claim. This will
392 // help us allocate any unknown patient responsibility amounts.
393 $thispaidanything = 0;
394 foreach($this->invoice
as $codekey => $codeval) {
395 foreach ($codeval['dtl'] as $key => $value) {
396 if (isset($value['plv'])) {
397 // New method; plv exists to indicate the payer level.
398 if ($value['plv'] == $insnumber) {
399 $thispaidanything +
= $value['pmt'];
403 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
404 $thispaidanything +
= $value['pmt'];
410 // Allocate any unknown patient responsibility by guessing if the
411 // deductible has been satisfied.
412 if ($thispaidanything)
413 $coinsurance = $ptresp - $deductible;
415 $deductible = $ptresp - $coinsurance;
417 $deductible = sprintf('%.2f', $deductible);
418 $coinsurance = sprintf('%.2f', $coinsurance);
420 if ($date && $deductible != 0)
421 $aadj[] = array($date, 'PR', '1', $deductible);
422 if ($date && $coinsurance != 0)
423 $aadj[] = array($date, 'PR', '2', $coinsurance);
430 // Return date, total payments and total "hard" adjustments from the given
431 // prior payer. If $code is specified then only that procedure key is
432 // selected, otherwise it's for the whole claim.
434 function payerTotals($ins, $code='') {
435 // If we have no modifiers stored in SQL-Ledger for this claim,
436 // then we cannot use a modifier passed in with the key.
437 $tmp = strpos($code, ':');
438 if ($tmp && !$this->using_modifiers
) $code = substr($code, 0, $tmp);
440 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
441 $insnumber = substr($inslabel, 3);
445 foreach($this->invoice
as $codekey => $codeval) {
446 if ($code && strcmp($codekey,$code) != 0) continue;
447 foreach ($codeval['dtl'] as $key => $value) {
448 if (isset($value['plv'])) {
449 // New method; 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'];
457 // Old method: With SQL-Ledger payer level was stored in the memo.
458 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
459 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
460 $paytotal +
= $value['pmt'];
464 $aarr = $this->payerAdjustments($ins, $codekey);
465 foreach ($aarr as $a) {
466 if (strcmp($a[1],'PR') != 0) $adjtotal +
= $a[3];
467 if (!$date) $date = $a[0];
470 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
473 // Return the amount already paid by the patient.
475 function patientPaidAmount() {
476 // For primary claims $this->invoice is not loaded, so get the co-pay
477 // from the billing table instead.
478 if (empty($this->invoice
)) return $this->copay
;
481 foreach($this->invoice
as $codekey => $codeval) {
482 foreach ($codeval['dtl'] as $key => $value) {
483 if (isset($value['plv'])) {
484 // New method; plv exists to indicate the payer level.
485 if ($value['plv'] == 0) { // 0 indicates patient
486 $amount +
= $value['pmt'];
490 // Old method: With SQL-Ledger payer level was stored in the memo.
491 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
492 $amount +
= $value['pmt'];
497 return sprintf('%.2f', $amount);
500 // Return invoice total, including adjustments but not payments.
502 function invoiceTotal() {
504 foreach($this->invoice
as $codekey => $codeval) {
505 $amount +
= $codeval['chg'];
507 return sprintf('%.2f', $amount);
510 // Number of procedures in this claim.
511 function procCount() {
512 return count($this->procs
);
515 // Number of payers for this claim. Ranges from 1 to 3.
516 function payerCount() {
517 return count($this->payers
);
520 function x12gsversionstring() {
521 return x12clean(trim($this->x12_partner
['x12_version']));
524 function x12gssenderid() {
525 $tmp = $this->x12_partner
['x12_sender_id'];
526 while (strlen($tmp) < 15) $tmp .= " ";
530 function x12gsreceiverid() {
531 $tmp = $this->x12_partner
['x12_receiver_id'];
532 while (strlen($tmp) < 15) $tmp .= " ";
536 function x12gsisa05() {
537 return $this->x12_partner
['x12_isa05'];
540 function x12gsisa07() {
541 return $this->x12_partner
['x12_isa07'];
544 function x12gsisa14() {
545 return $this->x12_partner
['x12_isa14'];
548 function x12gsisa15() {
549 return $this->x12_partner
['x12_isa15'];
552 function x12gsgs02() {
553 $tmp = $this->x12_partner
['x12_gs02'];
554 if ($tmp === '') $tmp = $this->x12_partner
['x12_sender_id'];
558 function x12gsper06() {
559 return $this->x12_partner
['x12_per06'];
562 function cliaCode() {
563 return x12clean(trim($this->facility
['domain_identifier']));
566 function billingFacilityName() {
567 return x12clean(trim($this->billing_facility
['name']));
570 function billingFacilityStreet() {
571 return x12clean(trim($this->billing_facility
['street']));
574 function billingFacilityCity() {
575 return x12clean(trim($this->billing_facility
['city']));
578 function billingFacilityState() {
579 return x12clean(trim($this->billing_facility
['state']));
582 function billingFacilityZip() {
583 return x12clean(trim($this->billing_facility
['postal_code']));
586 function billingFacilityETIN() {
587 return x12clean(trim(str_replace('-', '', $this->billing_facility
['federal_ein'])));
590 function billingFacilityNPI() {
591 return x12clean(trim($this->billing_facility
['facility_npi']));
594 function federalIdType() {
595 if ($this->billing_facility
['tax_id_type'])
597 return $this->billing_facility
['tax_id_type'];
604 # The billing facility and the patient must both accept for this to return true.
605 function billingFacilityAssignment($ins=0) {
606 $tmp = strtoupper($this->payers
[$ins]['data']['accept_assignment']);
607 if (strcmp($tmp,'FALSE') == 0) return '0';
608 return !empty($this->billing_facility
['accepts_assignment']);
611 function billingContactName() {
612 return x12clean(trim($this->billing_facility
['attn']));
615 function billingContactPhone() {
616 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
617 $this->billing_facility
['phone'], $tmp))
619 return $tmp[1] . $tmp[2] . $tmp[3];
624 function facilityName() {
625 return x12clean(trim($this->facility
['name']));
628 function facilityStreet() {
629 return x12clean(trim($this->facility
['street']));
632 function facilityCity() {
633 return x12clean(trim($this->facility
['city']));
636 function facilityState() {
637 return x12clean(trim($this->facility
['state']));
640 function facilityZip() {
641 return x12clean(trim($this->facility
['postal_code']));
644 function facilityETIN() {
645 return x12clean(trim(str_replace('-', '', $this->facility
['federal_ein'])));
648 function facilityNPI() {
649 return x12clean(trim($this->facility
['facility_npi']));
652 function facilityPOS() {
653 return sprintf('%02d', trim($this->facility
['pos_code']));
656 function clearingHouseName() {
657 return x12clean(trim($this->x12_partner
['name']));
660 function clearingHouseETIN() {
661 return x12clean(trim(str_replace('-', '', $this->x12_partner
['id_number'])));
664 function providerNumberType($prockey=-1) {
665 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
666 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
667 return $tmp['provider_number_type'];
670 function providerNumber($prockey=-1) {
671 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
672 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
673 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
676 function providerGroupNumber($prockey=-1) {
677 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
678 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
679 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
682 // Returns 'P', 'S' or 'T'.
684 function payerSequence($ins=0) {
685 return strtoupper(substr($this->payers
[$ins]['data']['type'], 0, 1));
688 // Returns the HIPAA code of the patient-to-subscriber relationship.
690 function insuredRelationship($ins=0) {
691 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
692 if (strcmp($tmp,'self' ) == 0) return '18';
693 if (strcmp($tmp,'spouse') == 0) return '01';
694 if (strcmp($tmp,'child' ) == 0) return '19';
695 if (strcmp($tmp,'other' ) == 0) return 'G8';
696 return $tmp; // should not happen
699 function insuredTypeCode($ins=0) {
700 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
701 return '12'; // medicare secondary working aged beneficiary or
702 // spouse with employer group health plan
706 // Is the patient also the subscriber?
708 function isSelfOfInsured($ins=0) {
709 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
710 return (strcmp($tmp,'self') == 0);
713 function planName($ins=0) {
714 return x12clean(trim($this->payers
[$ins]['data']['plan_name']));
717 function policyNumber($ins=0) { // "ID"
718 return x12clean(trim($this->payers
[$ins]['data']['policy_number']));
721 function groupNumber($ins=0) {
722 return x12clean(trim($this->payers
[$ins]['data']['group_number']));
725 function groupName($ins=0) {
726 return x12clean(trim($this->payers
[$ins]['data']['subscriber_employer']));
731 // MB Medicare Part B
735 // BL Blue Cross Blue Shield
738 // 10 Central Certification
739 // 11 Other Non-Federal Programs
740 // 12 Preferred Provider Organization (PPO)
741 // 13 Point of Service (POS)
742 // 14 Exclusive Provider Organization (EPO)
743 // 15 Indemnity Insurance
744 // 16 Health Maintenance Organization (HMO) Medicare Risk
745 // AM Automobile Medical
746 // CI Commercial Insurance Co.
748 // HM Health Maintenance Organization
750 // LM Liability Medical
751 // OF Other Federal Program
753 // VA Veterans Administration Plan
754 // WC Workers Compensation Health Plan
755 // ZZ Mutually Defined
757 function claimType($ins=0) {
758 if (empty($this->payers
[$ins]['object'])) return '';
759 return $this->payers
[$ins]['object']->get_freeb_claim_type();
762 function insuredLastName($ins=0) {
763 return x12clean(trim($this->payers
[$ins]['data']['subscriber_lname']));
766 function insuredFirstName($ins=0) {
767 return x12clean(trim($this->payers
[$ins]['data']['subscriber_fname']));
770 function insuredMiddleName($ins=0) {
771 return x12clean(trim($this->payers
[$ins]['data']['subscriber_mname']));
774 function insuredStreet($ins=0) {
775 return x12clean(trim($this->payers
[$ins]['data']['subscriber_street']));
778 function insuredCity($ins=0) {
779 return x12clean(trim($this->payers
[$ins]['data']['subscriber_city']));
782 function insuredState($ins=0) {
783 return x12clean(trim($this->payers
[$ins]['data']['subscriber_state']));
786 function insuredZip($ins=0) {
787 return x12clean(trim($this->payers
[$ins]['data']['subscriber_postal_code']));
790 function insuredPhone($ins=0) {
791 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
792 $this->payers
[$ins]['data']['subscriber_phone'], $tmp))
793 return $tmp[1] . $tmp[2] . $tmp[3];
797 function insuredDOB($ins=0) {
798 return str_replace('-', '', $this->payers
[$ins]['data']['subscriber_DOB']);
801 function insuredSex($ins=0) {
802 return strtoupper(substr($this->payers
[$ins]['data']['subscriber_sex'], 0, 1));
805 function payerName($ins=0) {
806 return x12clean(trim($this->payers
[$ins]['company']['name']));
809 function payerAttn($ins=0) {
810 return x12clean(trim($this->payers
[$ins]['company']['attn']));
813 function payerStreet($ins=0) {
814 if (empty($this->payers
[$ins]['object'])) return '';
815 $tmp = $this->payers
[$ins]['object'];
816 $tmp = $tmp->get_address();
817 return x12clean(trim($tmp->get_line1()));
820 function payerCity($ins=0) {
821 if (empty($this->payers
[$ins]['object'])) return '';
822 $tmp = $this->payers
[$ins]['object'];
823 $tmp = $tmp->get_address();
824 return x12clean(trim($tmp->get_city()));
827 function payerState($ins=0) {
828 if (empty($this->payers
[$ins]['object'])) return '';
829 $tmp = $this->payers
[$ins]['object'];
830 $tmp = $tmp->get_address();
831 return x12clean(trim($tmp->get_state()));
834 function payerZip($ins=0) {
835 if (empty($this->payers
[$ins]['object'])) return '';
836 $tmp = $this->payers
[$ins]['object'];
837 $tmp = $tmp->get_address();
838 return x12clean(trim($tmp->get_zip()));
841 function payerID($ins=0) {
842 return x12clean(trim($this->payers
[$ins]['company']['cms_id']));
845 function payerAltID($ins=0) {
846 return x12clean(trim($this->payers
[$ins]['company']['alt_cms_id']));
849 function patientLastName() {
850 return x12clean(trim($this->patient_data
['lname']));
853 function patientFirstName() {
854 return x12clean(trim($this->patient_data
['fname']));
857 function patientMiddleName() {
858 return x12clean(trim($this->patient_data
['mname']));
861 function patientStreet() {
862 return x12clean(trim($this->patient_data
['street']));
865 function patientCity() {
866 return x12clean(trim($this->patient_data
['city']));
869 function patientState() {
870 return x12clean(trim($this->patient_data
['state']));
873 function patientZip() {
874 return x12clean(trim($this->patient_data
['postal_code']));
877 function patientPhone() {
878 $ptphone = $this->patient_data
['phone_home'];
879 if (!$ptphone) $ptphone = $this->patient_data
['phone_biz'];
880 if (!$ptphone) $ptphone = $this->patient_data
['phone_cell'];
881 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
882 return $tmp[1] . $tmp[2] . $tmp[3];
886 function patientDOB() {
887 return str_replace('-', '', $this->patient_data
['DOB']);
890 function patientSex() {
891 return strtoupper(substr($this->patient_data
['sex'], 0, 1));
894 // Patient Marital Status: M = Married, S = Single, or something else.
895 function patientStatus() {
896 return strtoupper(substr($this->patient_data
['status'], 0, 1));
899 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
900 // indicate employed.
901 function patientOccupation() {
902 return strtoupper(x12clean(trim($this->patient_data
['occupation'])));
905 function cptCode($prockey) {
906 return x12clean(trim($this->procs
[$prockey]['code']));
909 function cptModifier($prockey) {
910 return x12clean(trim($this->procs
[$prockey]['modifier']));
913 // Returns the procedure code, followed by ":modifier" if there is one.
914 function cptKey($prockey) {
915 $tmp = $this->cptModifier($prockey);
916 return $this->cptCode($prockey) . ($tmp ?
":$tmp" : "");
919 function cptCharges($prockey) {
920 return x12clean(trim($this->procs
[$prockey]['fee']));
923 function cptUnits($prockey) {
924 if (empty($this->procs
[$prockey]['units'])) return '1';
925 return x12clean(trim($this->procs
[$prockey]['units']));
929 function cptNDCID($prockey) {
930 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
931 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
933 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
934 return sprintf('%05d-%04d-%02d', $tmp[1], $tmp[2], $tmp[3]);
936 return x12clean($ndc); // format is bad but return it anyway
941 // NDC drug unit of measure code.
942 function cptNDCUOM($prockey) {
943 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
944 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
945 return x12clean($tmp[2]);
949 // NDC drug number of units.
950 function cptNDCQuantity($prockey) {
951 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
952 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
953 return x12clean(ltrim($tmp[3], '0'));
958 function onsetDate() {//Without the else clause in the claim zero value is coming.
959 $replace_value=str_replace('-', '', substr($this->encounter
['onset_date'], 0, 10));
960 if($replace_value*1<>0)
962 return $replace_value;
969 function serviceDate() {
970 return str_replace('-', '', substr($this->encounter
['date'], 0, 10));
973 function priorAuth() {
974 return x12clean(trim($this->billing_options
['prior_auth_number']));
977 function isRelatedEmployment() {
978 return !empty($this->billing_options
['employment_related']);
981 function isRelatedAuto() {
982 return !empty($this->billing_options
['auto_accident']);
985 function isRelatedOther() {
986 return !empty($this->billing_options
['other_accident']);
989 function autoAccidentState() {
990 return x12clean(trim($this->billing_options
['accident_state']));
993 function isUnableToWork() {
994 return !empty($this->billing_options
['is_unable_to_work']);
997 function offWorkFrom() {
998 return str_replace('-', '', substr($this->billing_options
['off_work_from'], 0, 10));
1001 function offWorkTo() {
1002 return str_replace('-', '', substr($this->billing_options
['off_work_to'], 0, 10));
1005 function isHospitalized() {
1006 return !empty($this->billing_options
['is_hospitalized']);
1009 function hospitalizedFrom() {
1010 return str_replace('-', '', substr($this->billing_options
['hospitalization_date_from'], 0, 10));
1013 function hospitalizedTo() {
1014 return str_replace('-', '', substr($this->billing_options
['hospitalization_date_to'], 0, 10));
1017 function isOutsideLab() {
1018 return !empty($this->billing_options
['outside_lab']);
1021 function outsideLabAmount() {
1022 return sprintf('%.2f', 0 +
$this->billing_options
['lab_amount']);
1025 function medicaidResubmissionCode() {
1026 return x12clean(trim($this->billing_options
['medicaid_resubmission_code']));
1029 function medicaidOriginalReference() {
1030 return x12clean(trim($this->billing_options
['medicaid_original_reference']));
1033 function frequencyTypeCode() {
1034 return empty($this->billing_options
['replacement_claim']) ?
'1' : '7';
1037 function additionalNotes() {
1038 return x12clean(trim($this->billing_options
['comments']));
1041 // Returns an array of unique diagnoses. Periods are stripped.
1042 function diagArray() {
1044 foreach ($this->procs
as $row) {
1045 $atmp = explode(':', $row['justify']);
1046 foreach ($atmp as $tmp) {
1048 $diag = str_replace('.', '', $tmp);
1053 // The above got all the diagnoses used for justification, in the order
1054 // used for justification. Next we go through all diagnoses, justified
1055 // or not, to make sure they all get into the claim. We do it this way
1056 // so that the more important diagnoses appear first.
1057 foreach ($this->diags
as $diag) {
1058 $diag = str_replace('.', '', $diag);
1064 // Compute one 1-relative index in diagArray for the given procedure.
1065 // This function is obsolete, use diagIndexArray() instead.
1066 function diagIndex($prockey) {
1067 $da = $this->diagArray();
1068 $tmp = explode(':', $this->procs
[$prockey]['justify']);
1069 if (empty($tmp)) return '';
1070 $diag = str_replace('.', '', $tmp[0]);
1072 foreach ($da as $value) {
1074 if (strcmp($value,$diag) == 0) return $i;
1079 // Compute array of 1-relative diagArray indices for the given procedure.
1080 function diagIndexArray($prockey) {
1082 $da = $this->diagArray();
1083 $atmp = explode(':', $this->procs
[$prockey]['justify']);
1084 foreach ($atmp as $tmp) {
1086 $diag = str_replace('.', '', $tmp);
1088 foreach ($da as $value) {
1090 if (strcmp($value,$diag) == 0) $dia[] = $i;
1097 function providerLastName($prockey=-1) {
1098 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1099 $this->provider
: $this->procs
[$prockey]['provider'];
1100 return x12clean(trim($tmp['lname']));
1103 function providerFirstName($prockey=-1) {
1104 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1105 $this->provider
: $this->procs
[$prockey]['provider'];
1106 return x12clean(trim($tmp['fname']));
1109 function providerMiddleName($prockey=-1) {
1110 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1111 $this->provider
: $this->procs
[$prockey]['provider'];
1112 return x12clean(trim($tmp['mname']));
1115 function providerNPI($prockey=-1) {
1116 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1117 $this->provider
: $this->procs
[$prockey]['provider'];
1118 return x12clean(trim($tmp['npi']));
1121 function providerUPIN($prockey=-1) {
1122 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1123 $this->provider
: $this->procs
[$prockey]['provider'];
1124 return x12clean(trim($tmp['upin']));
1127 function providerSSN($prockey=-1) {
1128 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1129 $this->provider
: $this->procs
[$prockey]['provider'];
1130 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1133 function providerTaxonomy($prockey=-1) {
1134 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1135 $this->provider
: $this->procs
[$prockey]['provider'];
1136 if (empty($tmp['taxonomy'])) return '207Q00000X';
1137 return x12clean(trim($tmp['taxonomy']));
1140 function referrerLastName() {
1141 return x12clean(trim($this->referrer
['lname']));
1144 function referrerFirstName() {
1145 return x12clean(trim($this->referrer
['fname']));
1148 function referrerMiddleName() {
1149 return x12clean(trim($this->referrer
['mname']));
1152 function referrerNPI() {
1153 return x12clean(trim($this->referrer
['npi']));
1156 function referrerUPIN() {
1157 return x12clean(trim($this->referrer
['upin']));
1160 function referrerSSN() {
1161 return x12clean(trim(str_replace('-', '', $this->referrer
['federaltaxid'])));
1164 function referrerTaxonomy() {
1165 if (empty($this->referrer
['taxonomy'])) return '207Q00000X';
1166 return x12clean(trim($this->referrer
['taxonomy']));
1169 function supervisorLastName() {
1170 return x12clean(trim($this->supervisor
['lname']));
1173 function supervisorFirstName() {
1174 return x12clean(trim($this->supervisor
['fname']));
1177 function supervisorMiddleName() {
1178 return x12clean(trim($this->supervisor
['mname']));
1181 function supervisorNPI() {
1182 return x12clean(trim($this->supervisor
['npi']));
1185 function supervisorUPIN() {
1186 return x12clean(trim($this->supervisor
['upin']));
1189 function supervisorSSN() {
1190 return x12clean(trim(str_replace('-', '', $this->supervisor
['federaltaxid'])));
1193 function supervisorTaxonomy() {
1194 if (empty($this->supervisor
['taxonomy'])) return '207Q00000X';
1195 return x12clean(trim($this->supervisor
['taxonomy']));
1198 function supervisorNumberType() {
1199 return $this->supervisor_numbers
['provider_number_type'];
1202 function supervisorNumber() {
1203 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers
['provider_number'])));