Added support for secondary Medicare policy types.
[openemr.git] / library / Claim.class.php
blob5752f935c798c0663b49ce49043f659627977823
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__) . "/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));
20 class Claim {
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) {
42 global $sl_err;
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);
56 $prevtype = '';
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']) {
98 SLConnect();
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);
103 if ($arrow) {
104 $this->invoice = get_invoice_summary($arrow['id'], true);
106 SLClose();
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) {
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 * 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'];
141 continue;
143 // Save all diagnosis codes.
144 if ($row['code_type'] == 'ICD9') {
145 $this->diags[$row['code']] = $row['code'];
146 continue;
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']) . "' " .
187 "LIMIT 1";
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 encounter. If none,
197 // try the first (and hopefully only) facility marked as a billing location.
198 if (empty($this->encounter['billing_facility'])) {
199 $sql = "SELECT * FROM facility " .
200 "ORDER BY billing_location DESC, id ASC LIMIT 1";
202 else {
203 $sql = "SELECT * FROM facility " .
204 " where id ='" . addslashes($this->encounter['billing_facility']) . "' ";
206 $this->billing_facility = sqlQuery($sql);
208 $sql = "SELECT * FROM insurance_numbers WHERE " .
209 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
210 "' OR insurance_company_id is NULL) AND " .
211 "provider_id = '$provider_id' " .
212 "ORDER BY insurance_company_id DESC LIMIT 1";
213 $this->insurance_numbers = sqlQuery($sql);
215 $sql = "SELECT * FROM patient_data WHERE " .
216 "pid = '{$this->pid}' " .
217 "ORDER BY id LIMIT 1";
218 $this->patient_data = sqlQuery($sql);
220 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
221 "ON fpa.id = forms.form_id WHERE " .
222 "forms.encounter = '{$this->encounter_id}' AND " .
223 "forms.pid = '{$this->pid}' AND " .
224 "forms.deleted = 0 AND " .
225 "forms.formdir = 'misc_billing_options' " .
226 "ORDER BY forms.date";
227 $this->billing_options = sqlQuery($sql);
229 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
230 $this->insurance_numbers['provider_number_type'] != '1C') ?
231 $this->patient_data['providerID'] : $provider_id;
232 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
233 $this->referrer = sqlQuery($sql);
234 if (!$this->referrer) $this->referrer = array();
236 $supervisor_id = $this->encounter['supervisor_id'];
237 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
238 $this->supervisor = sqlQuery($sql);
239 if (!$this->supervisor) $this->supervisor = array();
241 $sql = "SELECT * FROM insurance_numbers WHERE " .
242 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
243 "' OR insurance_company_id is NULL) AND " .
244 "provider_id = '$supervisor_id' " .
245 "ORDER BY insurance_company_id DESC LIMIT 1";
246 $this->supervisor_numbers = sqlQuery($sql);
247 if (!$this->supervisor_numbers) $this->supervisor_numbers = array();
249 } // end constructor
251 // Return an array of adjustments from the designated prior payer for the
252 // designated procedure key (might be procedure:modifier), or for the claim
253 // level. For each adjustment give date, group code, reason code, amount.
254 // Note this will include "patient responsibility" adjustments which are
255 // not adjustments to OUR invoice, but they reduce the amount that the
256 // insurance company pays.
258 function payerAdjustments($ins, $code='Claim') {
259 $aadj = array();
261 // If we have no modifiers stored in SQL-Ledger for this claim,
262 // then we cannot use a modifier passed in with the key.
263 $tmp = strpos($code, ':');
264 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
266 // For payments, source always starts with "Ins" or "Pt".
267 // Nonzero adjustment reason examples:
268 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
269 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
270 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
271 // Ins1 adjust code A2 (Contractual adjustment)
272 // Ins adjust Ins1
273 // adjust code 45
274 // Zero adjustment reason examples:
275 // Co-pay: 25.00
276 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
277 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
278 // To copay Ins1 (manual entry)
279 // To ded'ble Ins1 (manual entry)
281 if (!empty($this->invoice[$code])) {
282 $date = '';
283 $deductible = 0;
284 $coinsurance = 0;
285 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
286 $insnumber = substr($inslabel, 3);
288 // Compute this procedure's patient responsibility amount as of this
289 // prior payer, which is the original charge minus all insurance
290 // payments and "hard" adjustments up to this payer.
291 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
292 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
293 if (isset($value['plv'])) {
294 // New method; plv (from ar_activity.payer_type) exists to
295 // indicate the payer level.
296 if (isset($value['pmt']) && $value['pmt'] != 0) {
297 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
298 $ptresp -= $value['pmt'];
300 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
301 // non-blank key indicates this is an adjustment and not a charge
302 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
303 $ptresp += $value['chg']; // adjustments are negative charges
306 $msp = isset( $value['msp'] ) ? $value['msp'] : null; // record the reason for adjustment
308 else {
309 // Old method: With SQL-Ledger payer level was stored in the memo.
310 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
311 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
313 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
314 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) || $tmp[1] <= $insnumber)
315 $ptresp += $value['chg']; // adjustments are negative charges
319 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
321 // Main loop, to extract adjustments for this payer and procedure.
322 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
323 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
324 if ($tmp) $date = $tmp;
325 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
326 $rsn = $value['rsn'];
327 $chg = 0 - $value['chg']; // adjustments are negative charges
329 $gcode = 'CO'; // default group code = contractual obligation
330 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
332 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
333 // From manual post. Take the defaults.
335 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
336 $coinsurance = $ptresp; // from manual post
337 continue;
339 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
340 $deductible = $ptresp; // from manual post
341 continue;
343 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
344 $coinsurance = $tmp[1]; // from 835 as of 6/2007
345 continue;
347 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
348 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
349 continue;
351 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
352 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
353 continue;
355 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
356 continue; // from 835 as of 6/2007
358 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
359 $rcode = $tmp[1]; // from 835
361 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
362 // Take the defaults.
364 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
365 continue; // it's for some other payer
367 else if ($insnumber == '1') {
368 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
369 $rcode = $tmp[1]; // from 835
371 else if ($chg) {
372 // Other adjustments default to Ins1.
374 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
375 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
376 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
377 continue;
379 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
380 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
381 continue;
383 else {
384 continue; // there is no adjustment amount
387 else {
388 continue; // it's for primary and that's not us
391 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
392 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
394 } // end if
395 } // end foreach
397 // If we really messed it up, at least avoid negative numbers.
398 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
399 if ($deductible > $ptresp) $deductible = $ptresp;
401 // Find out if this payer paid anything at all on this claim. This will
402 // help us allocate any unknown patient responsibility amounts.
403 $thispaidanything = 0;
404 foreach($this->invoice as $codekey => $codeval) {
405 foreach ($codeval['dtl'] as $key => $value) {
406 if (isset($value['plv'])) {
407 // New method; plv exists to indicate the payer level.
408 if ($value['plv'] == $insnumber) {
409 $thispaidanything += $value['pmt'];
412 else {
413 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
414 $thispaidanything += $value['pmt'];
420 // Allocate any unknown patient responsibility by guessing if the
421 // deductible has been satisfied.
422 if ($thispaidanything)
423 $coinsurance = $ptresp - $deductible;
424 else
425 $deductible = $ptresp - $coinsurance;
427 $deductible = sprintf('%.2f', $deductible);
428 $coinsurance = sprintf('%.2f', $coinsurance);
430 if ($date && $deductible != 0)
431 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
432 if ($date && $coinsurance != 0)
433 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
435 } // end if
437 return $aadj;
440 // Return date, total payments and total "hard" adjustments from the given
441 // prior payer. If $code is specified then only that procedure key is
442 // selected, otherwise it's for the whole claim.
444 function payerTotals($ins, $code='') {
445 // If we have no modifiers stored in SQL-Ledger for this claim,
446 // then we cannot use a modifier passed in with the key.
447 $tmp = strpos($code, ':');
448 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
450 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
451 $insnumber = substr($inslabel, 3);
452 $paytotal = 0;
453 $adjtotal = 0;
454 $date = '';
455 foreach($this->invoice as $codekey => $codeval) {
456 if ($code && strcmp($codekey,$code) != 0) continue;
457 foreach ($codeval['dtl'] as $key => $value) {
458 if (isset($value['plv'])) {
459 // New method; plv (from ar_activity.payer_type) exists to
460 // indicate the payer level.
461 if ($value['plv'] == $insnumber) {
462 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
463 $paytotal += $value['pmt'];
466 else {
467 // Old method: With SQL-Ledger payer level was stored in the memo.
468 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
469 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
470 $paytotal += $value['pmt'];
474 $aarr = $this->payerAdjustments($ins, $codekey);
475 foreach ($aarr as $a) {
476 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
477 if (!$date) $date = $a[0];
480 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
483 // Return the amount already paid by the patient.
485 function patientPaidAmount() {
486 // For primary claims $this->invoice is not loaded, so get the co-pay
487 // from the billing table instead.
488 if (empty($this->invoice)) return $this->copay;
490 $amount = 0;
491 foreach($this->invoice as $codekey => $codeval) {
492 foreach ($codeval['dtl'] as $key => $value) {
493 if (isset($value['plv'])) {
494 // New method; plv exists to indicate the payer level.
495 if ($value['plv'] == 0) { // 0 indicates patient
496 $amount += $value['pmt'];
499 else {
500 // Old method: With SQL-Ledger payer level was stored in the memo.
501 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
502 $amount += $value['pmt'];
507 return sprintf('%.2f', $amount);
510 // Return invoice total, including adjustments but not payments.
512 function invoiceTotal() {
513 $amount = 0;
514 foreach($this->invoice as $codekey => $codeval) {
515 $amount += $codeval['chg'];
517 return sprintf('%.2f', $amount);
520 // Number of procedures in this claim.
521 function procCount() {
522 return count($this->procs);
525 // Number of payers for this claim. Ranges from 1 to 3.
526 function payerCount() {
527 return count($this->payers);
530 function x12gsversionstring() {
531 return x12clean(trim($this->x12_partner['x12_version']));
534 function x12gssenderid() {
535 $tmp = $this->x12_partner['x12_sender_id'];
536 while (strlen($tmp) < 15) $tmp .= " ";
537 return $tmp;
540 function x12gsreceiverid() {
541 $tmp = $this->x12_partner['x12_receiver_id'];
542 while (strlen($tmp) < 15) $tmp .= " ";
543 return $tmp;
546 function x12gsisa05() {
547 return $this->x12_partner['x12_isa05'];
550 function x12gsisa07() {
551 return $this->x12_partner['x12_isa07'];
554 function x12gsisa14() {
555 return $this->x12_partner['x12_isa14'];
558 function x12gsisa15() {
559 return $this->x12_partner['x12_isa15'];
562 function x12gsgs02() {
563 $tmp = $this->x12_partner['x12_gs02'];
564 if ($tmp === '') $tmp = $this->x12_partner['x12_sender_id'];
565 return $tmp;
568 function x12gsper06() {
569 return $this->x12_partner['x12_per06'];
572 function cliaCode() {
573 return x12clean(trim($this->facility['domain_identifier']));
576 function billingFacilityName() {
577 return x12clean(trim($this->billing_facility['name']));
580 function billingFacilityStreet() {
581 return x12clean(trim($this->billing_facility['street']));
584 function billingFacilityCity() {
585 return x12clean(trim($this->billing_facility['city']));
588 function billingFacilityState() {
589 return x12clean(trim($this->billing_facility['state']));
592 function billingFacilityZip() {
593 return x12clean(trim($this->billing_facility['postal_code']));
596 function billingFacilityETIN() {
597 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
600 function billingFacilityNPI() {
601 return x12clean(trim($this->billing_facility['facility_npi']));
604 function federalIdType() {
605 if ($this->billing_facility['tax_id_type'])
607 return $this->billing_facility['tax_id_type'];
609 else{
610 return null;
614 # The billing facility and the patient must both accept for this to return true.
615 function billingFacilityAssignment($ins=0) {
616 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
617 if (strcmp($tmp,'FALSE') == 0) return '0';
618 return !empty($this->billing_facility['accepts_assignment']);
621 function billingContactName() {
622 return x12clean(trim($this->billing_facility['attn']));
625 function billingContactPhone() {
626 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
627 $this->billing_facility['phone'], $tmp))
629 return $tmp[1] . $tmp[2] . $tmp[3];
631 return '';
634 function facilityName() {
635 return x12clean(trim($this->facility['name']));
638 function facilityStreet() {
639 return x12clean(trim($this->facility['street']));
642 function facilityCity() {
643 return x12clean(trim($this->facility['city']));
646 function facilityState() {
647 return x12clean(trim($this->facility['state']));
650 function facilityZip() {
651 return x12clean(trim($this->facility['postal_code']));
654 function facilityETIN() {
655 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
658 function facilityNPI() {
659 return x12clean(trim($this->facility['facility_npi']));
662 function facilityPOS() {
663 return sprintf('%02d', trim($this->facility['pos_code']));
666 function clearingHouseName() {
667 return x12clean(trim($this->x12_partner['name']));
670 function clearingHouseETIN() {
671 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
674 function providerNumberType($prockey=-1) {
675 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
676 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
677 return $tmp['provider_number_type'];
680 function providerNumber($prockey=-1) {
681 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
682 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
683 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
686 function providerGroupNumber($prockey=-1) {
687 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
688 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
689 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
692 // Returns 'P', 'S' or 'T'.
694 function payerSequence($ins=0) {
695 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
698 // Returns the HIPAA code of the patient-to-subscriber relationship.
700 function insuredRelationship($ins=0) {
701 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
702 if (strcmp($tmp,'self' ) == 0) return '18';
703 if (strcmp($tmp,'spouse') == 0) return '01';
704 if (strcmp($tmp,'child' ) == 0) return '19';
705 if (strcmp($tmp,'other' ) == 0) return 'G8';
706 return $tmp; // should not happen
709 function insuredTypeCode($ins=0) {
710 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
711 return $this->payers[$ins]['data']['policy_type'];
712 return '';
715 // Is the patient also the subscriber?
717 function isSelfOfInsured($ins=0) {
718 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
719 return (strcmp($tmp,'self') == 0);
722 function planName($ins=0) {
723 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
726 function policyNumber($ins=0) { // "ID"
727 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
730 function groupNumber($ins=0) {
731 return x12clean(trim($this->payers[$ins]['data']['group_number']));
734 function groupName($ins=0) {
735 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
738 // Claim types are:
739 // 16 Other HCFA
740 // MB Medicare Part B
741 // MC Medicaid
742 // CH ChampUSVA
743 // CH ChampUS
744 // BL Blue Cross Blue Shield
745 // 16 FECA
746 // 09 Self Pay
747 // 10 Central Certification
748 // 11 Other Non-Federal Programs
749 // 12 Preferred Provider Organization (PPO)
750 // 13 Point of Service (POS)
751 // 14 Exclusive Provider Organization (EPO)
752 // 15 Indemnity Insurance
753 // 16 Health Maintenance Organization (HMO) Medicare Risk
754 // AM Automobile Medical
755 // CI Commercial Insurance Co.
756 // DS Disability
757 // HM Health Maintenance Organization
758 // LI Liability
759 // LM Liability Medical
760 // OF Other Federal Program
761 // TV Title V
762 // VA Veterans Administration Plan
763 // WC Workers Compensation Health Plan
764 // ZZ Mutually Defined
766 function claimType($ins=0) {
767 if (empty($this->payers[$ins]['object'])) return '';
768 return $this->payers[$ins]['object']->get_freeb_claim_type();
771 function insuredLastName($ins=0) {
772 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
775 function insuredFirstName($ins=0) {
776 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
779 function insuredMiddleName($ins=0) {
780 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
783 function insuredStreet($ins=0) {
784 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
787 function insuredCity($ins=0) {
788 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
791 function insuredState($ins=0) {
792 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
795 function insuredZip($ins=0) {
796 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
799 function insuredPhone($ins=0) {
800 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
801 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
802 return $tmp[1] . $tmp[2] . $tmp[3];
803 return '';
806 function insuredDOB($ins=0) {
807 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
810 function insuredSex($ins=0) {
811 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
814 function payerName($ins=0) {
815 return x12clean(trim($this->payers[$ins]['company']['name']));
818 function payerAttn($ins=0) {
819 return x12clean(trim($this->payers[$ins]['company']['attn']));
822 function payerStreet($ins=0) {
823 if (empty($this->payers[$ins]['object'])) return '';
824 $tmp = $this->payers[$ins]['object'];
825 $tmp = $tmp->get_address();
826 return x12clean(trim($tmp->get_line1()));
829 function payerCity($ins=0) {
830 if (empty($this->payers[$ins]['object'])) return '';
831 $tmp = $this->payers[$ins]['object'];
832 $tmp = $tmp->get_address();
833 return x12clean(trim($tmp->get_city()));
836 function payerState($ins=0) {
837 if (empty($this->payers[$ins]['object'])) return '';
838 $tmp = $this->payers[$ins]['object'];
839 $tmp = $tmp->get_address();
840 return x12clean(trim($tmp->get_state()));
843 function payerZip($ins=0) {
844 if (empty($this->payers[$ins]['object'])) return '';
845 $tmp = $this->payers[$ins]['object'];
846 $tmp = $tmp->get_address();
847 return x12clean(trim($tmp->get_zip()));
850 function payerID($ins=0) {
851 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
854 function payerAltID($ins=0) {
855 return x12clean(trim($this->payers[$ins]['company']['alt_cms_id']));
858 function patientLastName() {
859 return x12clean(trim($this->patient_data['lname']));
862 function patientFirstName() {
863 return x12clean(trim($this->patient_data['fname']));
866 function patientMiddleName() {
867 return x12clean(trim($this->patient_data['mname']));
870 function patientStreet() {
871 return x12clean(trim($this->patient_data['street']));
874 function patientCity() {
875 return x12clean(trim($this->patient_data['city']));
878 function patientState() {
879 return x12clean(trim($this->patient_data['state']));
882 function patientZip() {
883 return x12clean(trim($this->patient_data['postal_code']));
886 function patientPhone() {
887 $ptphone = $this->patient_data['phone_home'];
888 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
889 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
890 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
891 return $tmp[1] . $tmp[2] . $tmp[3];
892 return '';
895 function patientDOB() {
896 return str_replace('-', '', $this->patient_data['DOB']);
899 function patientSex() {
900 return strtoupper(substr($this->patient_data['sex'], 0, 1));
903 // Patient Marital Status: M = Married, S = Single, or something else.
904 function patientStatus() {
905 return strtoupper(substr($this->patient_data['status'], 0, 1));
908 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
909 // indicate employed.
910 function patientOccupation() {
911 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
914 function cptCode($prockey) {
915 return x12clean(trim($this->procs[$prockey]['code']));
918 function cptModifier($prockey) {
919 return x12clean(trim($this->procs[$prockey]['modifier']));
922 // Returns the procedure code, followed by ":modifier" if there is one.
923 function cptKey($prockey) {
924 $tmp = $this->cptModifier($prockey);
925 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
928 function cptCharges($prockey) {
929 return x12clean(trim($this->procs[$prockey]['fee']));
932 function cptUnits($prockey) {
933 if (empty($this->procs[$prockey]['units'])) return '1';
934 return x12clean(trim($this->procs[$prockey]['units']));
937 // NDC drug ID.
938 function cptNDCID($prockey) {
939 $ndcinfo = $this->procs[$prockey]['ndc_info'];
940 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
941 $ndc = $tmp[1];
942 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
943 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
945 return x12clean($ndc); // format is bad but return it anyway
947 return '';
950 // NDC drug unit of measure code.
951 function cptNDCUOM($prockey) {
952 $ndcinfo = $this->procs[$prockey]['ndc_info'];
953 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
954 return x12clean($tmp[2]);
955 return '';
958 // NDC drug number of units.
959 function cptNDCQuantity($prockey) {
960 $ndcinfo = $this->procs[$prockey]['ndc_info'];
961 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
962 return x12clean(ltrim($tmp[3], '0'));
964 return '';
967 function onsetDate() {//Without the else clause in the claim zero value is coming.
968 $replace_value=str_replace('-', '', substr($this->encounter['onset_date'], 0, 10));
969 if($replace_value*1<>0)
971 return $replace_value;
973 else{
974 return '';
978 function serviceDate() {
979 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
982 function priorAuth() {
983 return x12clean(trim($this->billing_options['prior_auth_number']));
986 function isRelatedEmployment() {
987 return !empty($this->billing_options['employment_related']);
990 function isRelatedAuto() {
991 return !empty($this->billing_options['auto_accident']);
994 function isRelatedOther() {
995 return !empty($this->billing_options['other_accident']);
998 function autoAccidentState() {
999 return x12clean(trim($this->billing_options['accident_state']));
1002 function isUnableToWork() {
1003 return !empty($this->billing_options['is_unable_to_work']);
1006 function offWorkFrom() {
1007 return str_replace('-', '', substr($this->billing_options['off_work_from'], 0, 10));
1010 function offWorkTo() {
1011 return str_replace('-', '', substr($this->billing_options['off_work_to'], 0, 10));
1014 function isHospitalized() {
1015 return !empty($this->billing_options['is_hospitalized']);
1018 function hospitalizedFrom() {
1019 return str_replace('-', '', substr($this->billing_options['hospitalization_date_from'], 0, 10));
1022 function hospitalizedTo() {
1023 return str_replace('-', '', substr($this->billing_options['hospitalization_date_to'], 0, 10));
1026 function isOutsideLab() {
1027 return !empty($this->billing_options['outside_lab']);
1030 function outsideLabAmount() {
1031 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
1034 function medicaidResubmissionCode() {
1035 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
1038 function medicaidOriginalReference() {
1039 return x12clean(trim($this->billing_options['medicaid_original_reference']));
1042 function frequencyTypeCode() {
1043 return empty($this->billing_options['replacement_claim']) ? '1' : '7';
1046 function additionalNotes() {
1047 return x12clean(trim($this->billing_options['comments']));
1050 function dateInitialTreatment() {
1051 return str_replace('-', '', substr($this->billing_options['date_initial_treatment'], 0, 10));
1054 // Returns an array of unique diagnoses. Periods are stripped.
1055 function diagArray() {
1056 $da = array();
1057 foreach ($this->procs as $row) {
1058 $atmp = explode(':', $row['justify']);
1059 foreach ($atmp as $tmp) {
1060 if (!empty($tmp)) {
1061 $diag = str_replace('.', '', $tmp);
1062 $da[$diag] = $diag;
1066 // The above got all the diagnoses used for justification, in the order
1067 // used for justification. Next we go through all diagnoses, justified
1068 // or not, to make sure they all get into the claim. We do it this way
1069 // so that the more important diagnoses appear first.
1070 foreach ($this->diags as $diag) {
1071 $diag = str_replace('.', '', $diag);
1072 $da[$diag] = $diag;
1074 return $da;
1077 // Compute one 1-relative index in diagArray for the given procedure.
1078 // This function is obsolete, use diagIndexArray() instead.
1079 function diagIndex($prockey) {
1080 $da = $this->diagArray();
1081 $tmp = explode(':', $this->procs[$prockey]['justify']);
1082 if (empty($tmp)) return '';
1083 $diag = str_replace('.', '', $tmp[0]);
1084 $i = 0;
1085 foreach ($da as $value) {
1086 ++$i;
1087 if (strcmp($value,$diag) == 0) return $i;
1089 return '';
1092 // Compute array of 1-relative diagArray indices for the given procedure.
1093 function diagIndexArray($prockey) {
1094 $dia = array();
1095 $da = $this->diagArray();
1096 $atmp = explode(':', $this->procs[$prockey]['justify']);
1097 foreach ($atmp as $tmp) {
1098 if (!empty($tmp)) {
1099 $diag = str_replace('.', '', $tmp);
1100 $i = 0;
1101 foreach ($da as $value) {
1102 ++$i;
1103 if (strcmp($value,$diag) == 0) $dia[] = $i;
1107 return $dia;
1110 function providerLastName($prockey=-1) {
1111 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1112 $this->provider : $this->procs[$prockey]['provider'];
1113 return x12clean(trim($tmp['lname']));
1116 function providerFirstName($prockey=-1) {
1117 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1118 $this->provider : $this->procs[$prockey]['provider'];
1119 return x12clean(trim($tmp['fname']));
1122 function providerMiddleName($prockey=-1) {
1123 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1124 $this->provider : $this->procs[$prockey]['provider'];
1125 return x12clean(trim($tmp['mname']));
1128 function providerNPI($prockey=-1) {
1129 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1130 $this->provider : $this->procs[$prockey]['provider'];
1131 return x12clean(trim($tmp['npi']));
1134 function providerUPIN($prockey=-1) {
1135 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1136 $this->provider : $this->procs[$prockey]['provider'];
1137 return x12clean(trim($tmp['upin']));
1140 function providerSSN($prockey=-1) {
1141 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1142 $this->provider : $this->procs[$prockey]['provider'];
1143 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1146 function providerTaxonomy($prockey=-1) {
1147 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1148 $this->provider : $this->procs[$prockey]['provider'];
1149 if (empty($tmp['taxonomy'])) return '207Q00000X';
1150 return x12clean(trim($tmp['taxonomy']));
1153 function referrerLastName() {
1154 return x12clean(trim($this->referrer['lname']));
1157 function referrerFirstName() {
1158 return x12clean(trim($this->referrer['fname']));
1161 function referrerMiddleName() {
1162 return x12clean(trim($this->referrer['mname']));
1165 function referrerNPI() {
1166 return x12clean(trim($this->referrer['npi']));
1169 function referrerUPIN() {
1170 return x12clean(trim($this->referrer['upin']));
1173 function referrerSSN() {
1174 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1177 function referrerTaxonomy() {
1178 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1179 return x12clean(trim($this->referrer['taxonomy']));
1182 function supervisorLastName() {
1183 return x12clean(trim($this->supervisor['lname']));
1186 function supervisorFirstName() {
1187 return x12clean(trim($this->supervisor['fname']));
1190 function supervisorMiddleName() {
1191 return x12clean(trim($this->supervisor['mname']));
1194 function supervisorNPI() {
1195 return x12clean(trim($this->supervisor['npi']));
1198 function supervisorUPIN() {
1199 return x12clean(trim($this->supervisor['upin']));
1202 function supervisorSSN() {
1203 return x12clean(trim(str_replace('-', '', $this->supervisor['federaltaxid'])));
1206 function supervisorTaxonomy() {
1207 if (empty($this->supervisor['taxonomy'])) return '207Q00000X';
1208 return x12clean(trim($this->supervisor['taxonomy']));
1211 function supervisorNumberType() {
1212 return $this->supervisor_numbers['provider_number_type'];
1215 function supervisorNumber() {
1216 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers['provider_number'])));