removed redundant docs (it is on the wiki)
[openemr.git] / library / Claim.class.php
blob528fbc6e6f66fde616e064161dcde33f73c1f7f6
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__) . "/invoice_summary.inc.php");
13 // This enforces the X12 Basic Character Set. Page A2.
15 function x12clean($str) {
16 return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?= ]/', '', strtoupper($str));
19 // Make sure dates have no formatting and zero filled becomes blank
20 // Handles date time stamp formats as well
22 function cleanDate($date_field)
24 $cleandate = str_replace('-', '', substr($date_field, 0, 10));
26 if(substr_count($cleandate,'0')==8)
28 $cleandate='';
31 return ($cleandate);
34 class Claim {
36 var $pid; // patient id
37 var $encounter_id; // encounter id
38 var $procs; // array of procedure rows from billing table
39 var $diags; // array of icd9 codes from billing table
40 var $diagtype= "ICD9"; // diagnosis code_type.Assume ICD9 unless otherwise specified.
41 var $x12_partner; // row from x12_partners table
42 var $encounter; // row from form_encounter table
43 var $facility; // row from facility table
44 var $billing_facility; // row from facility table
45 var $provider; // row from users table (rendering provider)
46 var $referrer; // row from users table (referring provider)
47 var $supervisor; // row from users table (supervising provider)
48 var $insurance_numbers; // row from insurance_numbers table for current payer
49 var $supervisor_numbers; // row from insurance_numbers table for current payer
50 var $patient_data; // row from patient_data table
51 var $billing_options; // row from form_misc_billing_options table
52 var $invoice; // result from get_invoice_summary()
53 var $payers; // array of arrays, for all payers
54 var $copay; // total of copays from the ar_activity table
56 function loadPayerInfo(&$billrow) {
57 global $sl_err;
58 $encounter_date = substr($this->encounter['date'], 0, 10);
60 // Create the $payers array. This contains data for all insurances
61 // with the current one always at index 0, and the others in payment
62 // order starting at index 1.
64 $this->payers = array();
65 $this->payers[0] = array();
66 $query = "SELECT * FROM insurance_data WHERE " .
67 "pid = '{$this->pid}' AND " .
68 "date <= '$encounter_date' " .
69 "ORDER BY type ASC, date DESC";
70 $dres = sqlStatement($query);
71 $prevtype = '';
72 while ($drow = sqlFetchArray($dres)) {
73 if (strcmp($prevtype, $drow['type']) == 0) continue;
74 $prevtype = $drow['type'];
75 // Very important to look at entries with a missing provider because
76 // they indicate no insurance as of the given date.
77 if (empty($drow['provider'])) continue;
78 $ins = count($this->payers);
79 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers[0]['data'])) $ins = 0;
80 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
81 "id = '" . $drow['provider'] . "'");
82 $orow = new InsuranceCompany($drow['provider']);
83 $this->payers[$ins] = array();
84 $this->payers[$ins]['data'] = $drow;
85 $this->payers[$ins]['company'] = $crow;
86 $this->payers[$ins]['object'] = $orow;
89 // This kludge hands most cases of a rare ambiguous situation, where
90 // the primary insurance company is the same as the secondary. It seems
91 // nobody planned for that!
93 for ($i = 1; $i < count($this->payers); ++$i) {
94 if ($billrow['process_date'] &&
95 $this->payers[0]['data']['provider'] == $this->payers[$i]['data']['provider'])
97 $tmp = $this->payers[0];
98 $this->payers[0] = $this->payers[$i];
99 $this->payers[$i] = $tmp;
103 $this->using_modifiers = true;
105 // Get payment and adjustment details if there are any previous payers.
107 $this->invoice = array();
108 if ($this->payerSequence() != 'P') {
109 $this->invoice = ar_get_invoice_summary($this->pid, $this->encounter_id, true);
110 // Secondary claims might not have modifiers in SQL-Ledger data.
111 // In that case, note that we should not try to match on them.
112 $this->using_modifiers = false;
113 foreach ($this->invoice as $key => $trash) {
114 if (strpos($key, ':')) $this->using_modifiers = true;
119 // Constructor. Loads relevant database information.
121 function __construct($pid, $encounter_id) {
122 $this->pid = $pid;
123 $this->encounter_id = $encounter_id;
124 $this->procs = array();
125 $this->diags = array();
126 $this->copay = 0;
128 // We need the encounter date before we can identify the payers.
129 $sql = "SELECT * FROM form_encounter WHERE " .
130 "pid = '{$this->pid}' AND " .
131 "encounter = '{$this->encounter_id}'";
132 $this->encounter = sqlQuery($sql);
134 // Sort by procedure timestamp in order to get some consistency.
135 $sql = "SELECT b.id, b.date, b.code_type, b.code, b.pid, b.provider_id, " .
136 "b.user, b.groupname, b.authorized, b.encounter, b.code_text, b.billed, " .
137 "b.activity, b.payer_id, b.bill_process, b.bill_date, b.process_date, " .
138 "b.process_file, b.modifier, b.units, b.fee, b.justify, b.target, b.x12_partner_id, " .
139 "b.ndc_info, b.notecodes, ct.ct_diag " .
140 "FROM billing as b INNER JOIN code_types as ct " .
141 "ON b.code_type = ct.ct_key " .
142 "WHERE ct.ct_claim = '1' AND ct.ct_active = '1' AND " .
143 "b.encounter = '{$this->encounter_id}' AND b.pid = '{$this->pid}' AND " .
144 "b.activity = '1' ORDER BY b.date, b.id";
145 $res = sqlStatement($sql);
146 while ($row = sqlFetchArray($res)) {
147 // Save all diagnosis codes.
148 if ($row['ct_diag'] == '1') {
149 $this->diags[$row['code']] = $row['code'];
150 continue;
152 if (!$row['units']) $row['units'] = 1;
153 // Load prior payer data at the first opportunity in order to get
154 // the using_modifiers flag that is referenced below.
155 if (empty($this->procs)) $this->loadPayerInfo($row);
157 // The consolidate duplicate procedures, which was previously here, was removed
158 // from codebase on 12/9/15. Reason: Some insurance companies decline consolidated
159 // procedures, and this can be left up to the billing coder when they input the items.
161 // If there is a row-specific provider then get its details.
162 if (!empty($row['provider_id'])) {
163 // Get service provider data for this row.
164 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
165 $row['provider'] = sqlQuery($sql);
166 // Get insurance numbers for this row's provider.
167 $sql = "SELECT * FROM insurance_numbers WHERE " .
168 "(insurance_company_id = '" . $row['payer_id'] .
169 "' OR insurance_company_id is NULL) AND " .
170 "provider_id = '" . $row['provider_id'] . "' " .
171 "ORDER BY insurance_company_id DESC LIMIT 1";
172 $row['insurance_numbers'] = sqlQuery($sql);
175 $this->procs[] = $row;
178 $resMoneyGot = sqlStatement("SELECT pay_amount as PatientPay,session_id as id,".
179 "date(post_time) as date FROM ar_activity where pid ='{$this->pid}' and encounter ='{$this->encounter_id}' ".
180 "and payer_type=0 and account_code='PCP'");
181 //new fees screen copay gives account_code='PCP'
182 while($rowMoneyGot = sqlFetchArray($resMoneyGot)){
183 $PatientPay=$rowMoneyGot['PatientPay']*-1;
184 $this->copay -= $PatientPay;
187 $sql = "SELECT * FROM x12_partners WHERE " .
188 "id = '" . $this->procs[0]['x12_partner_id'] . "'";
189 $this->x12_partner = sqlQuery($sql);
191 $sql = "SELECT * FROM facility WHERE " .
192 "id = '" . addslashes($this->encounter['facility_id']) . "' " .
193 "LIMIT 1";
194 $this->facility = sqlQuery($sql);
196 /*****************************************************************
197 $provider_id = $this->procs[0]['provider_id'];
198 *****************************************************************/
199 $provider_id = $this->encounter['provider_id'];
200 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
201 $this->provider = sqlQuery($sql);
202 // Selecting the billing facility assigned to the encounter. If none,
203 // try the first (and hopefully only) facility marked as a billing location.
204 if (empty($this->encounter['billing_facility'])) {
205 $sql = "SELECT * FROM facility " .
206 "ORDER BY billing_location DESC, id ASC LIMIT 1";
208 else {
209 $sql = "SELECT * FROM facility " .
210 " where id ='" . addslashes($this->encounter['billing_facility']) . "' ";
212 $this->billing_facility = sqlQuery($sql);
214 $sql = "SELECT * FROM insurance_numbers WHERE " .
215 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
216 "' OR insurance_company_id is NULL) AND " .
217 "provider_id = '$provider_id' " .
218 "ORDER BY insurance_company_id DESC LIMIT 1";
219 $this->insurance_numbers = sqlQuery($sql);
221 $sql = "SELECT * FROM patient_data WHERE " .
222 "pid = '{$this->pid}' " .
223 "ORDER BY id LIMIT 1";
224 $this->patient_data = sqlQuery($sql);
226 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
227 "ON fpa.id = forms.form_id WHERE " .
228 "forms.encounter = '{$this->encounter_id}' AND " .
229 "forms.pid = '{$this->pid}' AND " .
230 "forms.deleted = 0 AND " .
231 "forms.formdir = 'misc_billing_options' " .
232 "ORDER BY forms.date";
233 $this->billing_options = sqlQuery($sql);
235 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
236 $this->insurance_numbers['provider_number_type'] != '1C') ?
237 $this->patient_data['ref_providerID'] : $provider_id;
238 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
239 $this->referrer = sqlQuery($sql);
240 if (!$this->referrer) $this->referrer = array();
242 $supervisor_id = $this->encounter['supervisor_id'];
243 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
244 $this->supervisor = sqlQuery($sql);
245 if (!$this->supervisor) $this->supervisor = array();
247 $sql = "SELECT * FROM insurance_numbers WHERE " .
248 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
249 "' OR insurance_company_id is NULL) AND " .
250 "provider_id = '$supervisor_id' " .
251 "ORDER BY insurance_company_id DESC LIMIT 1";
252 $this->supervisor_numbers = sqlQuery($sql);
253 if (!$this->supervisor_numbers) $this->supervisor_numbers = array();
255 } // end constructor
257 // Return an array of adjustments from the designated prior payer for the
258 // designated procedure key (might be procedure:modifier), or for the claim
259 // level. For each adjustment give date, group code, reason code, amount.
260 // Note this will include "patient responsibility" adjustments which are
261 // not adjustments to OUR invoice, but they reduce the amount that the
262 // insurance company pays.
264 function payerAdjustments($ins, $code='Claim') {
265 $aadj = array();
267 // If we have no modifiers stored in SQL-Ledger for this claim,
268 // then we cannot use a modifier passed in with the key.
269 $tmp = strpos($code, ':');
270 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
272 // For payments, source always starts with "Ins" or "Pt".
273 // Nonzero adjustment reason examples:
274 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
275 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
276 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
277 // Ins1 adjust code A2 (Contractual adjustment)
278 // Ins adjust Ins1
279 // adjust code 45
280 // Zero adjustment reason examples:
281 // Co-pay: 25.00
282 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
283 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
284 // To copay Ins1 (manual entry)
285 // To ded'ble Ins1 (manual entry)
287 if (!empty($this->invoice[$code])) {
288 $date = '';
289 $deductible = 0;
290 $coinsurance = 0;
291 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
292 $insnumber = substr($inslabel, 3);
294 // Compute this procedure's patient responsibility amount as of this
295 // prior payer, which is the original charge minus all insurance
296 // payments and "hard" adjustments up to this payer.
297 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
298 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
300 // plv (from ar_activity.payer_type) exists to
301 // indicate the payer level.
302 if (isset($value['pmt']) && $value['pmt'] != 0) {
303 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
304 $ptresp -= $value['pmt'];
306 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
307 // non-blank key indicates this is an adjustment and not a charge
308 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
309 $ptresp += $value['chg']; // adjustments are negative charges
312 $msp = isset( $value['msp'] ) ? $value['msp'] : null; // record the reason for adjustment
314 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
316 // Main loop, to extract adjustments for this payer and procedure.
317 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
318 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
319 if ($tmp) $date = $tmp;
320 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
321 $rsn = $value['rsn'];
322 $chg = 0 - $value['chg']; // adjustments are negative charges
324 $gcode = 'CO'; // default group code = contractual obligation
325 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
327 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
328 // From manual post. Take the defaults.
330 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
331 $coinsurance = $ptresp; // from manual post
332 continue;
334 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
335 $deductible = $ptresp; // from manual post
336 continue;
338 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
339 $coinsurance = $tmp[1]; // from 835 as of 6/2007
340 continue;
342 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
343 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
344 continue;
346 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
347 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
348 continue;
350 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
351 continue; // from 835 as of 6/2007
353 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
354 $rcode = $tmp[1]; // from 835
356 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
357 // Take the defaults.
359 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
360 continue; // it's for some other payer
362 else if ($insnumber == '1') {
363 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
364 $rcode = $tmp[1]; // from 835
366 else if ($chg) {
367 // Other adjustments default to Ins1.
369 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
370 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
371 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
372 continue;
374 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
375 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
376 continue;
378 else {
379 continue; // there is no adjustment amount
382 else {
383 continue; // it's for primary and that's not us
386 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
387 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
389 } // end if
390 } // end foreach
392 // If we really messed it up, at least avoid negative numbers.
393 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
394 if ($deductible > $ptresp) $deductible = $ptresp;
396 // Find out if this payer paid anything at all on this claim. This will
397 // help us allocate any unknown patient responsibility amounts.
398 $thispaidanything = 0;
399 foreach($this->invoice as $codekey => $codeval) {
400 foreach ($codeval['dtl'] as $key => $value) {
401 // plv exists to indicate the payer level.
402 if ($value['plv'] == $insnumber) {
403 $thispaidanything += $value['pmt'];
408 // Allocate any unknown patient responsibility by guessing if the
409 // deductible has been satisfied.
410 if ($thispaidanything)
411 $coinsurance = $ptresp - $deductible;
412 else
413 $deductible = $ptresp - $coinsurance;
415 $deductible = sprintf('%.2f', $deductible);
416 $coinsurance = sprintf('%.2f', $coinsurance);
418 if ($date && $deductible != 0)
419 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
420 if ($date && $coinsurance != 0)
421 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
423 } // end if
425 return $aadj;
428 // Return date, total payments and total "hard" adjustments from the given
429 // prior payer. If $code is specified then only that procedure key is
430 // selected, otherwise it's for the whole claim.
432 function payerTotals($ins, $code='') {
433 // If we have no modifiers stored in SQL-Ledger for this claim,
434 // then we cannot use a modifier passed in with the key.
435 $tmp = strpos($code, ':');
436 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
438 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
439 $insnumber = substr($inslabel, 3);
440 $paytotal = 0;
441 $adjtotal = 0;
442 $date = '';
443 foreach($this->invoice as $codekey => $codeval) {
444 if ($code && strcmp($codekey,$code) != 0) continue;
445 foreach ($codeval['dtl'] as $key => $value) {
446 // plv (from ar_activity.payer_type) exists to
447 // indicate the payer level.
448 if ($value['plv'] == $insnumber) {
449 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
450 $paytotal += $value['pmt'];
453 $aarr = $this->payerAdjustments($ins, $codekey);
454 foreach ($aarr as $a) {
455 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
456 if (!$date) $date = $a[0];
459 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
462 // Return the amount already paid by the patient.
464 function patientPaidAmount() {
465 // For primary claims $this->invoice is not loaded, so get the co-pay
466 // from the ar_activity table instead.
467 if (empty($this->invoice)) return $this->copay;
469 $amount = 0;
470 foreach($this->invoice as $codekey => $codeval) {
471 foreach ($codeval['dtl'] as $key => $value) {
472 // plv exists to indicate the payer level.
473 if ($value['plv'] == 0) { // 0 indicates patient
474 $amount += $value['pmt'];
478 return sprintf('%.2f', $amount);
481 // Return invoice total, including adjustments but not payments.
483 function invoiceTotal() {
484 $amount = 0;
485 foreach($this->invoice as $codekey => $codeval) {
486 $amount += $codeval['chg'];
488 return sprintf('%.2f', $amount);
491 // Number of procedures in this claim.
492 function procCount() {
493 return count($this->procs);
496 // Number of payers for this claim. Ranges from 1 to 3.
497 function payerCount() {
498 return count($this->payers);
501 function x12gsversionstring() {
502 return x12clean(trim($this->x12_partner['x12_version']));
505 function x12gssenderid() {
506 $tmp = $this->x12_partner['x12_sender_id'];
507 while (strlen($tmp) < 15) $tmp .= " ";
508 return $tmp;
511 function x12gs03() {
513 * GS03: Application Receiver's Code
514 * Code Identifying Party Receiving Transmission
516 * In most cases, the ISA08 and GS03 are the same. However
518 * In some clearing houses ISA08 and GS03 are different
519 * Example: http://www.acs-gcro.com/downloads/DOL/DOL_CG_X12N_5010_837_v1_02.pdf - Page 18
520 * In this .pdf, the ISA08 is specified to be 100000 while the GS03 is specified to be 77044
522 * Therefore if the x12_gs03 segement is explicitly specified we use that value,
523 * otherwise we simply use the same receiver ID as specified for ISA03
525 if($this->x12_partner['x12_gs03'] !== '')
526 return $this->x12_partner['x12_gs03'];
527 else
528 return $this->x12_partner['x12_receiver_id'];
531 function x12gsreceiverid() {
532 $tmp = $this->x12_partner['x12_receiver_id'];
533 while (strlen($tmp) < 15) $tmp .= " ";
534 return $tmp;
537 function x12gsisa05() {
538 return $this->x12_partner['x12_isa05'];
540 //adding in functions for isa 01 - isa 04
542 function x12gsisa01() {
543 return $this->x12_partner['x12_isa01'];
546 function x12gsisa02() {
547 return $this->x12_partner['x12_isa02'];
550 function x12gsisa03() {
551 return $this->x12_partner['x12_isa03'];
553 function x12gsisa04() {
554 return $this->x12_partner['x12_isa04'];
557 /////////
558 function x12gsisa07() {
559 return $this->x12_partner['x12_isa07'];
562 function x12gsisa14() {
563 return $this->x12_partner['x12_isa14'];
566 function x12gsisa15() {
567 return $this->x12_partner['x12_isa15'];
570 function x12gsgs02() {
571 $tmp = $this->x12_partner['x12_gs02'];
572 if ($tmp === '') $tmp = $this->x12_partner['x12_sender_id'];
573 return $tmp;
576 function x12gsper06() {
577 return $this->x12_partner['x12_per06'];
580 function cliaCode() {
581 return x12clean(trim($this->facility['domain_identifier']));
584 function billingFacilityName() {
585 return x12clean(trim($this->billing_facility['name']));
588 function billingFacilityStreet() {
589 return x12clean(trim($this->billing_facility['street']));
592 function billingFacilityCity() {
593 return x12clean(trim($this->billing_facility['city']));
596 function billingFacilityState() {
597 return x12clean(trim($this->billing_facility['state']));
600 function billingFacilityZip() {
601 return x12clean(trim($this->billing_facility['postal_code']));
604 function billingFacilityETIN() {
605 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
608 function billingFacilityNPI() {
609 return x12clean(trim($this->billing_facility['facility_npi']));
612 function federalIdType() {
613 if ($this->billing_facility['tax_id_type'])
615 return $this->billing_facility['tax_id_type'];
617 else{
618 return null;
622 # The billing facility and the patient must both accept for this to return true.
623 function billingFacilityAssignment($ins=0) {
624 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
625 if (strcmp($tmp,'FALSE') == 0) return '0';
626 return !empty($this->billing_facility['accepts_assignment']);
629 function billingContactName() {
630 return x12clean(trim($this->billing_facility['attn']));
633 function billingContactPhone() {
634 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
635 $this->billing_facility['phone'], $tmp))
637 return $tmp[1] . $tmp[2] . $tmp[3];
639 return '';
642 function facilityName() {
643 return x12clean(trim($this->facility['name']));
646 function facilityStreet() {
647 return x12clean(trim($this->facility['street']));
650 function facilityCity() {
651 return x12clean(trim($this->facility['city']));
654 function facilityState() {
655 return x12clean(trim($this->facility['state']));
658 function facilityZip() {
659 return x12clean(trim($this->facility['postal_code']));
662 function facilityETIN() {
663 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
666 function facilityNPI() {
667 return x12clean(trim($this->facility['facility_npi']));
670 function facilityPOS() {
671 return sprintf('%02d', trim($this->facility['pos_code']));
674 function clearingHouseName() {
675 return x12clean(trim($this->x12_partner['name']));
678 function clearingHouseETIN() {
679 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
682 function providerNumberType($prockey=-1) {
683 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
684 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
685 return $tmp['provider_number_type'];
688 function providerNumber($prockey=-1) {
689 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
690 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
691 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
694 function providerGroupNumber($prockey=-1) {
695 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
696 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
697 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
700 // Returns 'P', 'S' or 'T'.
702 function payerSequence($ins=0) {
703 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
706 // Returns the HIPAA code of the patient-to-subscriber relationship.
708 function insuredRelationship($ins=0) {
709 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
710 if (strcmp($tmp,'self' ) == 0) return '18';
711 if (strcmp($tmp,'spouse') == 0) return '01';
712 if (strcmp($tmp,'child' ) == 0) return '19';
713 if (strcmp($tmp,'other' ) == 0) return 'G8';
714 return $tmp; // should not happen
717 function insuredTypeCode($ins=0) {
718 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
719 return $this->payers[$ins]['data']['policy_type'];
720 return '';
723 // Is the patient also the subscriber?
725 function isSelfOfInsured($ins=0) {
726 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
727 return (strcmp($tmp,'self') == 0);
730 function planName($ins=0) {
731 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
734 function policyNumber($ins=0) { // "ID"
735 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
738 function groupNumber($ins=0) {
739 return x12clean(trim($this->payers[$ins]['data']['group_number']));
742 function groupName($ins=0) {
743 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
746 // Claim types are:
747 // 16 Other HCFA
748 // MB Medicare Part B
749 // MC Medicaid
750 // CH ChampUSVA
751 // CH ChampUS
752 // BL Blue Cross Blue Shield
753 // 16 FECA
754 // 09 Self Pay
755 // 10 Central Certification
756 // 11 Other Non-Federal Programs
757 // 12 Preferred Provider Organization (PPO)
758 // 13 Point of Service (POS)
759 // 14 Exclusive Provider Organization (EPO)
760 // 15 Indemnity Insurance
761 // 16 Health Maintenance Organization (HMO) Medicare Risk
762 // AM Automobile Medical
763 // CI Commercial Insurance Co.
764 // DS Disability
765 // HM Health Maintenance Organization
766 // LI Liability
767 // LM Liability Medical
768 // OF Other Federal Program
769 // TV Title V
770 // VA Veterans Administration Plan
771 // WC Workers Compensation Health Plan
772 // ZZ Mutually Defined
774 function claimType($ins=0) {
775 if (empty($this->payers[$ins]['object'])) return '';
776 return $this->payers[$ins]['object']->get_freeb_claim_type();
779 function claimTypeRaw($ins=0) {
780 if (empty($this->payers[$ins]['object'])) return 0;
781 return $this->payers[$ins]['object']->get_freeb_type();
784 function insuredLastName($ins=0) {
785 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
788 function insuredFirstName($ins=0) {
789 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
792 function insuredMiddleName($ins=0) {
793 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
796 function insuredStreet($ins=0) {
797 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
800 function insuredCity($ins=0) {
801 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
804 function insuredState($ins=0) {
805 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
808 function insuredZip($ins=0) {
809 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
812 function insuredPhone($ins=0) {
813 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
814 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
815 return $tmp[1] . $tmp[2] . $tmp[3];
816 return '';
819 function insuredDOB($ins=0) {
820 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
823 function insuredSex($ins=0) {
824 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
827 function payerName($ins=0) {
828 return x12clean(trim($this->payers[$ins]['company']['name']));
831 function payerAttn($ins=0) {
832 return x12clean(trim($this->payers[$ins]['company']['attn']));
835 function payerStreet($ins=0) {
836 if (empty($this->payers[$ins]['object'])) return '';
837 $tmp = $this->payers[$ins]['object'];
838 $tmp = $tmp->get_address();
839 return x12clean(trim($tmp->get_line1()));
842 function payerCity($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_city()));
849 function payerState($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_state()));
856 function payerZip($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_zip()));
863 function payerID($ins=0) {
864 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
867 function payerAltID($ins=0) {
868 return x12clean(trim($this->payers[$ins]['company']['alt_cms_id']));
871 function patientLastName() {
872 return x12clean(trim($this->patient_data['lname']));
875 function patientFirstName() {
876 return x12clean(trim($this->patient_data['fname']));
879 function patientMiddleName() {
880 return x12clean(trim($this->patient_data['mname']));
883 function patientStreet() {
884 return x12clean(trim($this->patient_data['street']));
887 function patientCity() {
888 return x12clean(trim($this->patient_data['city']));
891 function patientState() {
892 return x12clean(trim($this->patient_data['state']));
895 function patientZip() {
896 return x12clean(trim($this->patient_data['postal_code']));
899 function patientPhone() {
900 $ptphone = $this->patient_data['phone_home'];
901 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
902 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
903 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
904 return $tmp[1] . $tmp[2] . $tmp[3];
905 return '';
908 function patientDOB() {
909 return str_replace('-', '', $this->patient_data['DOB']);
912 function patientSex() {
913 return strtoupper(substr($this->patient_data['sex'], 0, 1));
916 // Patient Marital Status: M = Married, S = Single, or something else.
917 function patientStatus() {
918 return strtoupper(substr($this->patient_data['status'], 0, 1));
921 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
922 // indicate employed.
923 function patientOccupation() {
924 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
927 function cptCode($prockey) {
928 return x12clean(trim($this->procs[$prockey]['code']));
931 function cptModifier($prockey) {
932 // Split on the colon or space and clean each modifier
933 $mods = array();
934 $cln_mods = array();
935 $mods = preg_split("/[: ]/",trim($this->procs[$prockey]['modifier']));
936 foreach ($mods as $mod) {
937 array_push($cln_mods, x12clean($mod));
939 return (implode (':', $cln_mods));
942 function cptNotecodes($prockey) {
943 return x12clean(trim($this->procs[$prockey]['notecodes']));
946 // Returns the procedure code, followed by ":modifier" if there is one.
947 function cptKey($prockey) {
948 $tmp = $this->cptModifier($prockey);
949 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
952 function cptCharges($prockey) {
953 return x12clean(trim($this->procs[$prockey]['fee']));
956 function cptUnits($prockey) {
957 if (empty($this->procs[$prockey]['units'])) return '1';
958 return x12clean(trim($this->procs[$prockey]['units']));
961 // NDC drug ID.
962 function cptNDCID($prockey) {
963 $ndcinfo = $this->procs[$prockey]['ndc_info'];
964 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
965 $ndc = $tmp[1];
966 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
967 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
969 return x12clean($ndc); // format is bad but return it anyway
971 return '';
974 // NDC drug unit of measure code.
975 function cptNDCUOM($prockey) {
976 $ndcinfo = $this->procs[$prockey]['ndc_info'];
977 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
978 return x12clean($tmp[2]);
979 return '';
982 // NDC drug number of units.
983 function cptNDCQuantity($prockey) {
984 $ndcinfo = $this->procs[$prockey]['ndc_info'];
985 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
986 return x12clean(ltrim($tmp[3], '0'));
988 return '';
991 function onsetDate() {
992 return cleanDate($this->encounter['onset_date']);
995 function onsetDateValid()
997 return $this->onsetDate()!=='';
1000 function serviceDate() {
1001 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
1004 function priorAuth() {
1005 return x12clean(trim($this->billing_options['prior_auth_number']));
1008 function isRelatedEmployment() {
1009 return !empty($this->billing_options['employment_related']);
1012 function isRelatedAuto() {
1013 return !empty($this->billing_options['auto_accident']);
1016 function isRelatedOther() {
1017 return !empty($this->billing_options['other_accident']);
1020 function autoAccidentState() {
1021 return x12clean(trim($this->billing_options['accident_state']));
1024 function isUnableToWork() {
1025 return !empty($this->billing_options['is_unable_to_work']);
1028 function offWorkFrom() {
1029 return cleanDate($this->billing_options['off_work_from']);
1032 function offWorkTo() {
1033 return cleanDate($this->billing_options['off_work_to']);
1036 function isHospitalized() {
1037 return !empty($this->billing_options['is_hospitalized']);
1040 function hospitalizedFrom() {
1041 return cleanDate($this->billing_options['hospitalization_date_from']);
1044 function hospitalizedTo() {
1045 return cleanDate($this->billing_options['hospitalization_date_to']);
1048 function isOutsideLab() {
1049 return !empty($this->billing_options['outside_lab']);
1052 function outsideLabAmount() {
1053 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
1056 function medicaidResubmissionCode() {
1057 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
1060 function medicaidOriginalReference() {
1061 return x12clean(trim($this->billing_options['medicaid_original_reference']));
1064 function frequencyTypeCode() {
1065 return ($this->billing_options['replacement_claim'] == 1) ? '7' : '1';
1068 function additionalNotes() {
1069 return x12clean(trim($this->billing_options['comments']));
1072 function dateInitialTreatment() {
1073 return cleanDate($this->billing_options['date_initial_treatment']);
1076 function box14qualifier()
1078 // If no box qualifier specified use "431" indicating Onset
1079 return empty($this->billing_options['box_14_date_qual']) ? '431' :
1080 $this->billing_options['box_14_date_qual'];
1083 function box15qualifier()
1085 // If no box qualifier specified use "454" indicating Initial Treatment
1086 return empty($this->billing_options['box_15_date_qual']) ? '454' :
1087 $this->billing_options['box_15_date_qual'];
1089 // Returns an array of unique diagnoses. Periods are stripped by default
1090 // Option to keep periods is to support HCFA 1500 02/12 version
1091 function diagArray($strip_periods=true) {
1092 $da = array();
1093 foreach ($this->procs as $row) {
1094 $atmp = explode(':', $row['justify']);
1095 foreach ($atmp as $tmp) {
1096 if (!empty($tmp)) {
1097 $code_data = explode('|',$tmp);
1099 // If there was a | in the code data, the the first part of the array is the type, and the second is the identifier
1100 if (!empty($code_data[1])) {
1102 // This is the simplest way to determine if the claim is using ICD9 or ICD10 codes
1103 // a mix of code types is generally not allowed as there is only one specifier for all diagnoses on HCFA-1500 form
1104 // and there would be ambiguity with E and V codes
1105 $this->diagtype=$code_data[0];
1107 //code is in the second part of the $code_data array.
1108 if($strip_periods==true)
1110 $diag = str_replace('.', '', $code_data[1]);
1113 else
1115 $diag=$code_data[1];
1119 else {
1120 //No prepended code type label
1121 if($strip_periods) {
1122 $diag = str_replace('.', '', $code_data[0]);
1124 else
1126 $diag=$code_data[0];
1129 $da[$diag] = $diag;
1133 // The above got all the diagnoses used for justification, in the order
1134 // used for justification. Next we go through all diagnoses, justified
1135 // or not, to make sure they all get into the claim. We do it this way
1136 // so that the more important diagnoses appear first.
1137 foreach ($this->diags as $diag) {
1138 if($strip_periods) {$diag = str_replace('.', '', $diag);}
1139 $da[$diag] = $diag;
1141 return $da;
1144 // Compute one 1-relative index in diagArray for the given procedure.
1145 // This function is obsolete, use diagIndexArray() instead.
1146 function diagIndex($prockey) {
1147 $da = $this->diagArray();
1148 $tmp = explode(':', $this->procs[$prockey]['justify']);
1149 if (empty($tmp)) return '';
1150 $diag = str_replace('.', '', $tmp[0]);
1151 $i = 0;
1152 foreach ($da as $value) {
1153 ++$i;
1154 if (strcmp($value,$diag) == 0) return $i;
1156 return '';
1159 // Compute array of 1-relative diagArray indices for the given procedure.
1160 function diagIndexArray($prockey) {
1161 $dia = array();
1162 $da = $this->diagArray();
1163 $atmp = explode(':', $this->procs[$prockey]['justify']);
1164 foreach ($atmp as $tmp) {
1165 if (!empty($tmp)) {
1166 $code_data = explode('|',$tmp);
1167 if (!empty($code_data[1])) {
1168 //Strip the prepended code type label
1169 $diag = str_replace('.', '', $code_data[1]);
1171 else {
1172 //No prepended code type label
1173 $diag = str_replace('.', '', $code_data[0]);
1175 $i = 0;
1176 foreach ($da as $value) {
1177 ++$i;
1178 if (strcmp($value,$diag) == 0) $dia[] = $i;
1182 return $dia;
1185 function providerLastName($prockey=-1) {
1186 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1187 $this->provider : $this->procs[$prockey]['provider'];
1188 return x12clean(trim($tmp['lname']));
1191 function providerFirstName($prockey=-1) {
1192 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1193 $this->provider : $this->procs[$prockey]['provider'];
1194 return x12clean(trim($tmp['fname']));
1197 function providerMiddleName($prockey=-1) {
1198 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1199 $this->provider : $this->procs[$prockey]['provider'];
1200 return x12clean(trim($tmp['mname']));
1203 function providerNPI($prockey=-1) {
1204 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1205 $this->provider : $this->procs[$prockey]['provider'];
1206 return x12clean(trim($tmp['npi']));
1209 function NPIValid($npi)
1211 // A NPI MUST be a 10 digit number
1212 if($npi==='') return false;
1213 if(strlen($npi)!=10) return false;
1214 if(!preg_match("/[0-9]*/",$npi)) return false;
1215 return true;
1218 function providerNPIValid($prockey=-1)
1220 return $this->NPIValid($this->providerNPI($prockey));
1223 function providerUPIN($prockey=-1) {
1224 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1225 $this->provider : $this->procs[$prockey]['provider'];
1226 return x12clean(trim($tmp['upin']));
1229 function providerSSN($prockey=-1) {
1230 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1231 $this->provider : $this->procs[$prockey]['provider'];
1232 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1235 function providerTaxonomy($prockey=-1) {
1236 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1237 $this->provider : $this->procs[$prockey]['provider'];
1238 if (empty($tmp['taxonomy'])) return '207Q00000X';
1239 return x12clean(trim($tmp['taxonomy']));
1242 function referrerLastName() {
1243 return x12clean(trim($this->referrer['lname']));
1246 function referrerFirstName() {
1247 return x12clean(trim($this->referrer['fname']));
1250 function referrerMiddleName() {
1251 return x12clean(trim($this->referrer['mname']));
1254 function referrerNPI() {
1255 return x12clean(trim($this->referrer['npi']));
1258 function referrerUPIN() {
1259 return x12clean(trim($this->referrer['upin']));
1262 function referrerSSN() {
1263 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1266 function referrerTaxonomy() {
1267 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1268 return x12clean(trim($this->referrer['taxonomy']));
1271 function supervisorLastName() {
1272 return x12clean(trim($this->supervisor['lname']));
1275 function supervisorFirstName() {
1276 return x12clean(trim($this->supervisor['fname']));
1279 function supervisorMiddleName() {
1280 return x12clean(trim($this->supervisor['mname']));
1283 function supervisorNPI() {
1284 return x12clean(trim($this->supervisor['npi']));
1287 function supervisorUPIN() {
1288 return x12clean(trim($this->supervisor['upin']));
1291 function supervisorSSN() {
1292 return x12clean(trim(str_replace('-', '', $this->supervisor['federaltaxid'])));
1295 function supervisorTaxonomy() {
1296 if (empty($this->supervisor['taxonomy'])) return '207Q00000X';
1297 return x12clean(trim($this->supervisor['taxonomy']));
1300 function supervisorNumberType() {
1301 return $this->supervisor_numbers['provider_number_type'];
1304 function supervisorNumber() {
1305 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers['provider_number'])));