Security fixes 20
[openemr.git] / library / Claim.class.php
blob060a71d39e4d72b7105188be041311d961f425f3
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 // Make sure dates have no formatting and zero filled becomes blank
21 // Handles date time stamp formats as well
23 function cleanDate($date_field)
25 $cleandate = str_replace('-', '', substr($date_field, 0, 10));
27 if(substr_count($cleandate,'0')==8)
29 $cleandate='';
32 return ($cleandate);
35 class Claim {
37 var $pid; // patient id
38 var $encounter_id; // encounter id
39 var $procs; // array of procedure rows from billing table
40 var $diags; // array of icd9 codes from billing table
41 var $diagtype= "ICD9"; // diagnosis code_type.Assume ICD9 unless otherwise specified.
42 var $x12_partner; // row from x12_partners table
43 var $encounter; // row from form_encounter table
44 var $facility; // row from facility table
45 var $billing_facility; // row from facility table
46 var $provider; // row from users table (rendering provider)
47 var $referrer; // row from users table (referring provider)
48 var $supervisor; // row from users table (supervising provider)
49 var $insurance_numbers; // row from insurance_numbers table for current payer
50 var $supervisor_numbers; // row from insurance_numbers table for current payer
51 var $patient_data; // row from patient_data table
52 var $billing_options; // row from form_misc_billing_options table
53 var $invoice; // result from get_invoice_summary()
54 var $payers; // array of arrays, for all payers
55 var $copay; // total of copays from the ar_activity table
57 function loadPayerInfo(&$billrow) {
58 global $sl_err;
59 $encounter_date = substr($this->encounter['date'], 0, 10);
61 // Create the $payers array. This contains data for all insurances
62 // with the current one always at index 0, and the others in payment
63 // order starting at index 1.
65 $this->payers = array();
66 $this->payers[0] = array();
67 $query = "SELECT * FROM insurance_data WHERE " .
68 "pid = '{$this->pid}' AND " .
69 "date <= '$encounter_date' " .
70 "ORDER BY type ASC, date DESC";
71 $dres = sqlStatement($query);
72 $prevtype = '';
73 while ($drow = sqlFetchArray($dres)) {
74 if (strcmp($prevtype, $drow['type']) == 0) continue;
75 $prevtype = $drow['type'];
76 // Very important to look at entries with a missing provider because
77 // they indicate no insurance as of the given date.
78 if (empty($drow['provider'])) continue;
79 $ins = count($this->payers);
80 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers[0]['data'])) $ins = 0;
81 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
82 "id = '" . $drow['provider'] . "'");
83 $orow = new InsuranceCompany($drow['provider']);
84 $this->payers[$ins] = array();
85 $this->payers[$ins]['data'] = $drow;
86 $this->payers[$ins]['company'] = $crow;
87 $this->payers[$ins]['object'] = $orow;
90 // This kludge hands most cases of a rare ambiguous situation, where
91 // the primary insurance company is the same as the secondary. It seems
92 // nobody planned for that!
94 for ($i = 1; $i < count($this->payers); ++$i) {
95 if ($billrow['process_date'] &&
96 $this->payers[0]['data']['provider'] == $this->payers[$i]['data']['provider'])
98 $tmp = $this->payers[0];
99 $this->payers[0] = $this->payers[$i];
100 $this->payers[$i] = $tmp;
104 $this->using_modifiers = true;
106 // Get payment and adjustment details if there are any previous payers.
108 $this->invoice = array();
109 if ($this->payerSequence() != 'P') {
110 if ($GLOBALS['oer_config']['ws_accounting']['enabled'] === 2) {
111 $this->invoice = ar_get_invoice_summary($this->pid, $this->encounter_id, true);
113 else if ($GLOBALS['oer_config']['ws_accounting']['enabled']) {
114 SLConnect();
115 $arres = SLQuery("select id from ar where invnumber = " .
116 "'{$this->pid}.{$this->encounter_id}'");
117 if ($sl_err) die($sl_err);
118 $arrow = SLGetRow($arres, 0);
119 if ($arrow) {
120 $this->invoice = get_invoice_summary($arrow['id'], true);
122 SLClose();
124 // Secondary claims might not have modifiers in SQL-Ledger data.
125 // In that case, note that we should not try to match on them.
126 $this->using_modifiers = false;
127 foreach ($this->invoice as $key => $trash) {
128 if (strpos($key, ':')) $this->using_modifiers = true;
133 // Constructor. Loads relevant database information.
135 function Claim($pid, $encounter_id) {
136 $this->pid = $pid;
137 $this->encounter_id = $encounter_id;
138 $this->procs = array();
139 $this->diags = array();
140 $this->copay = 0;
142 // We need the encounter date before we can identify the payers.
143 $sql = "SELECT * FROM form_encounter WHERE " .
144 "pid = '{$this->pid}' AND " .
145 "encounter = '{$this->encounter_id}'";
146 $this->encounter = sqlQuery($sql);
148 // Sort by procedure timestamp in order to get some consistency.
149 $sql = "SELECT b.id, b.date, b.code_type, b.code, b.pid, b.provider_id, " .
150 "b.user, b.groupname, b.authorized, b.encounter, b.code_text, b.billed, " .
151 "b.activity, b.payer_id, b.bill_process, b.bill_date, b.process_date, " .
152 "b.process_file, b.modifier, b.units, b.fee, b.justify, b.target, b.x12_partner_id, " .
153 "b.ndc_info, b.notecodes, ct.ct_diag " .
154 "FROM billing as b INNER JOIN code_types as ct " .
155 "ON b.code_type = ct.ct_key " .
156 "WHERE ct.ct_claim = '1' AND ct.ct_active = '1' AND " .
157 "b.encounter = '{$this->encounter_id}' AND b.pid = '{$this->pid}' AND " .
158 "b.activity = '1' ORDER BY b.date, b.id";
159 $res = sqlStatement($sql);
160 while ($row = sqlFetchArray($res)) {
161 // Save all diagnosis codes.
162 if ($row['ct_diag'] == '1') {
163 $this->diags[$row['code']] = $row['code'];
164 continue;
166 if (!$row['units']) $row['units'] = 1;
167 // Load prior payer data at the first opportunity in order to get
168 // the using_modifiers flag that is referenced below.
169 if (empty($this->procs)) $this->loadPayerInfo($row);
170 // Consolidate duplicate procedures.
171 foreach ($this->procs as $key => $trash) {
172 if (strcmp($this->procs[$key]['code'],$row['code']) == 0 &&
173 (strcmp($this->procs[$key]['modifier'],$row['modifier']) == 0 ||
174 !$this->using_modifiers))
176 $this->procs[$key]['units'] += $row['units'];
177 $this->procs[$key]['fee'] += $row['fee'];
178 continue 2; // skip to next table row
182 // If there is a row-specific provider then get its details.
183 if (!empty($row['provider_id'])) {
184 // Get service provider data for this row.
185 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
186 $row['provider'] = sqlQuery($sql);
187 // Get insurance numbers for this row's provider.
188 $sql = "SELECT * FROM insurance_numbers WHERE " .
189 "(insurance_company_id = '" . $row['payer_id'] .
190 "' OR insurance_company_id is NULL) AND " .
191 "provider_id = '" . $row['provider_id'] . "' " .
192 "ORDER BY insurance_company_id DESC LIMIT 1";
193 $row['insurance_numbers'] = sqlQuery($sql);
196 $this->procs[] = $row;
199 $resMoneyGot = sqlStatement("SELECT pay_amount as PatientPay,session_id as id,".
200 "date(post_time) as date FROM ar_activity where pid ='{$this->pid}' and encounter ='{$this->encounter_id}' ".
201 "and payer_type=0 and account_code='PCP'");
202 //new fees screen copay gives account_code='PCP'
203 while($rowMoneyGot = sqlFetchArray($resMoneyGot)){
204 $PatientPay=$rowMoneyGot['PatientPay']*-1;
205 $this->copay -= $PatientPay;
208 $sql = "SELECT * FROM x12_partners WHERE " .
209 "id = '" . $this->procs[0]['x12_partner_id'] . "'";
210 $this->x12_partner = sqlQuery($sql);
212 $sql = "SELECT * FROM facility WHERE " .
213 "id = '" . addslashes($this->encounter['facility_id']) . "' " .
214 "LIMIT 1";
215 $this->facility = sqlQuery($sql);
217 /*****************************************************************
218 $provider_id = $this->procs[0]['provider_id'];
219 *****************************************************************/
220 $provider_id = $this->encounter['provider_id'];
221 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
222 $this->provider = sqlQuery($sql);
223 // Selecting the billing facility assigned to the encounter. If none,
224 // try the first (and hopefully only) facility marked as a billing location.
225 if (empty($this->encounter['billing_facility'])) {
226 $sql = "SELECT * FROM facility " .
227 "ORDER BY billing_location DESC, id ASC LIMIT 1";
229 else {
230 $sql = "SELECT * FROM facility " .
231 " where id ='" . addslashes($this->encounter['billing_facility']) . "' ";
233 $this->billing_facility = sqlQuery($sql);
235 $sql = "SELECT * FROM insurance_numbers WHERE " .
236 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
237 "' OR insurance_company_id is NULL) AND " .
238 "provider_id = '$provider_id' " .
239 "ORDER BY insurance_company_id DESC LIMIT 1";
240 $this->insurance_numbers = sqlQuery($sql);
242 $sql = "SELECT * FROM patient_data WHERE " .
243 "pid = '{$this->pid}' " .
244 "ORDER BY id LIMIT 1";
245 $this->patient_data = sqlQuery($sql);
247 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
248 "ON fpa.id = forms.form_id WHERE " .
249 "forms.encounter = '{$this->encounter_id}' AND " .
250 "forms.pid = '{$this->pid}' AND " .
251 "forms.deleted = 0 AND " .
252 "forms.formdir = 'misc_billing_options' " .
253 "ORDER BY forms.date";
254 $this->billing_options = sqlQuery($sql);
256 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
257 $this->insurance_numbers['provider_number_type'] != '1C') ?
258 $this->patient_data['ref_providerID'] : $provider_id;
259 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
260 $this->referrer = sqlQuery($sql);
261 if (!$this->referrer) $this->referrer = array();
263 $supervisor_id = $this->encounter['supervisor_id'];
264 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
265 $this->supervisor = sqlQuery($sql);
266 if (!$this->supervisor) $this->supervisor = array();
268 $sql = "SELECT * FROM insurance_numbers WHERE " .
269 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
270 "' OR insurance_company_id is NULL) AND " .
271 "provider_id = '$supervisor_id' " .
272 "ORDER BY insurance_company_id DESC LIMIT 1";
273 $this->supervisor_numbers = sqlQuery($sql);
274 if (!$this->supervisor_numbers) $this->supervisor_numbers = array();
276 } // end constructor
278 // Return an array of adjustments from the designated prior payer for the
279 // designated procedure key (might be procedure:modifier), or for the claim
280 // level. For each adjustment give date, group code, reason code, amount.
281 // Note this will include "patient responsibility" adjustments which are
282 // not adjustments to OUR invoice, but they reduce the amount that the
283 // insurance company pays.
285 function payerAdjustments($ins, $code='Claim') {
286 $aadj = array();
288 // If we have no modifiers stored in SQL-Ledger for this claim,
289 // then we cannot use a modifier passed in with the key.
290 $tmp = strpos($code, ':');
291 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
293 // For payments, source always starts with "Ins" or "Pt".
294 // Nonzero adjustment reason examples:
295 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
296 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
297 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
298 // Ins1 adjust code A2 (Contractual adjustment)
299 // Ins adjust Ins1
300 // adjust code 45
301 // Zero adjustment reason examples:
302 // Co-pay: 25.00
303 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
304 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
305 // To copay Ins1 (manual entry)
306 // To ded'ble Ins1 (manual entry)
308 if (!empty($this->invoice[$code])) {
309 $date = '';
310 $deductible = 0;
311 $coinsurance = 0;
312 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
313 $insnumber = substr($inslabel, 3);
315 // Compute this procedure's patient responsibility amount as of this
316 // prior payer, which is the original charge minus all insurance
317 // payments and "hard" adjustments up to this payer.
318 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
319 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
320 if (isset($value['plv'])) {
321 // New method; plv (from ar_activity.payer_type) exists to
322 // indicate the payer level.
323 if (isset($value['pmt']) && $value['pmt'] != 0) {
324 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
325 $ptresp -= $value['pmt'];
327 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
328 // non-blank key indicates this is an adjustment and not a charge
329 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
330 $ptresp += $value['chg']; // adjustments are negative charges
333 $msp = isset( $value['msp'] ) ? $value['msp'] : null; // record the reason for adjustment
335 else {
336 // Old method: With SQL-Ledger payer level was stored in the memo.
337 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
338 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
340 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
341 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) || $tmp[1] <= $insnumber)
342 $ptresp += $value['chg']; // adjustments are negative charges
346 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
348 // Main loop, to extract adjustments for this payer and procedure.
349 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
350 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
351 if ($tmp) $date = $tmp;
352 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
353 $rsn = $value['rsn'];
354 $chg = 0 - $value['chg']; // adjustments are negative charges
356 $gcode = 'CO'; // default group code = contractual obligation
357 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
359 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
360 // From manual post. Take the defaults.
362 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
363 $coinsurance = $ptresp; // from manual post
364 continue;
366 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
367 $deductible = $ptresp; // from manual post
368 continue;
370 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
371 $coinsurance = $tmp[1]; // from 835 as of 6/2007
372 continue;
374 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
375 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
376 continue;
378 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
379 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
380 continue;
382 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
383 continue; // from 835 as of 6/2007
385 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
386 $rcode = $tmp[1]; // from 835
388 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
389 // Take the defaults.
391 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
392 continue; // it's for some other payer
394 else if ($insnumber == '1') {
395 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
396 $rcode = $tmp[1]; // from 835
398 else if ($chg) {
399 // Other adjustments default to Ins1.
401 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
402 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
403 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
404 continue;
406 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
407 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
408 continue;
410 else {
411 continue; // there is no adjustment amount
414 else {
415 continue; // it's for primary and that's not us
418 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
419 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
421 } // end if
422 } // end foreach
424 // If we really messed it up, at least avoid negative numbers.
425 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
426 if ($deductible > $ptresp) $deductible = $ptresp;
428 // Find out if this payer paid anything at all on this claim. This will
429 // help us allocate any unknown patient responsibility amounts.
430 $thispaidanything = 0;
431 foreach($this->invoice as $codekey => $codeval) {
432 foreach ($codeval['dtl'] as $key => $value) {
433 if (isset($value['plv'])) {
434 // New method; plv exists to indicate the payer level.
435 if ($value['plv'] == $insnumber) {
436 $thispaidanything += $value['pmt'];
439 else {
440 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
441 $thispaidanything += $value['pmt'];
447 // Allocate any unknown patient responsibility by guessing if the
448 // deductible has been satisfied.
449 if ($thispaidanything)
450 $coinsurance = $ptresp - $deductible;
451 else
452 $deductible = $ptresp - $coinsurance;
454 $deductible = sprintf('%.2f', $deductible);
455 $coinsurance = sprintf('%.2f', $coinsurance);
457 if ($date && $deductible != 0)
458 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
459 if ($date && $coinsurance != 0)
460 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
462 } // end if
464 return $aadj;
467 // Return date, total payments and total "hard" adjustments from the given
468 // prior payer. If $code is specified then only that procedure key is
469 // selected, otherwise it's for the whole claim.
471 function payerTotals($ins, $code='') {
472 // If we have no modifiers stored in SQL-Ledger for this claim,
473 // then we cannot use a modifier passed in with the key.
474 $tmp = strpos($code, ':');
475 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
477 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
478 $insnumber = substr($inslabel, 3);
479 $paytotal = 0;
480 $adjtotal = 0;
481 $date = '';
482 foreach($this->invoice as $codekey => $codeval) {
483 if ($code && strcmp($codekey,$code) != 0) continue;
484 foreach ($codeval['dtl'] as $key => $value) {
485 if (isset($value['plv'])) {
486 // New method; plv (from ar_activity.payer_type) exists to
487 // indicate the payer level.
488 if ($value['plv'] == $insnumber) {
489 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
490 $paytotal += $value['pmt'];
493 else {
494 // Old method: With SQL-Ledger payer level was stored in the memo.
495 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
496 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
497 $paytotal += $value['pmt'];
501 $aarr = $this->payerAdjustments($ins, $codekey);
502 foreach ($aarr as $a) {
503 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
504 if (!$date) $date = $a[0];
507 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
510 // Return the amount already paid by the patient.
512 function patientPaidAmount() {
513 // For primary claims $this->invoice is not loaded, so get the co-pay
514 // from the ar_activity table instead.
515 if (empty($this->invoice)) return $this->copay;
517 $amount = 0;
518 foreach($this->invoice as $codekey => $codeval) {
519 foreach ($codeval['dtl'] as $key => $value) {
520 if (isset($value['plv'])) {
521 // New method; plv exists to indicate the payer level.
522 if ($value['plv'] == 0) { // 0 indicates patient
523 $amount += $value['pmt'];
526 else {
527 // Old method: With SQL-Ledger payer level was stored in the memo.
528 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
529 $amount += $value['pmt'];
534 return sprintf('%.2f', $amount);
537 // Return invoice total, including adjustments but not payments.
539 function invoiceTotal() {
540 $amount = 0;
541 foreach($this->invoice as $codekey => $codeval) {
542 $amount += $codeval['chg'];
544 return sprintf('%.2f', $amount);
547 // Number of procedures in this claim.
548 function procCount() {
549 return count($this->procs);
552 // Number of payers for this claim. Ranges from 1 to 3.
553 function payerCount() {
554 return count($this->payers);
557 function x12gsversionstring() {
558 return x12clean(trim($this->x12_partner['x12_version']));
561 function x12gssenderid() {
562 $tmp = $this->x12_partner['x12_sender_id'];
563 while (strlen($tmp) < 15) $tmp .= " ";
564 return $tmp;
567 function x12gs03() {
569 * GS03: Application Receiver's Code
570 * Code Identifying Party Receiving Transmission
572 * In most cases, the ISA08 and GS03 are the same. However
574 * In some clearing houses ISA08 and GS03 are different
575 * Example: http://www.acs-gcro.com/downloads/DOL/DOL_CG_X12N_5010_837_v1_02.pdf - Page 18
576 * In this .pdf, the ISA08 is specified to be 100000 while the GS03 is specified to be 77044
578 * Therefore if the x12_gs03 segement is explicitly specified we use that value,
579 * otherwise we simply use the same receiver ID as specified for ISA03
581 if($this->x12_partner['x12_gs03'] !== '')
582 return $this->x12_partner['x12_gs03'];
583 else
584 return $this->x12_partner['x12_receiver_id'];
587 function x12gsreceiverid() {
588 $tmp = $this->x12_partner['x12_receiver_id'];
589 while (strlen($tmp) < 15) $tmp .= " ";
590 return $tmp;
593 function x12gsisa05() {
594 return $this->x12_partner['x12_isa05'];
596 //adding in functions for isa 01 - isa 04
598 function x12gsisa01() {
599 return $this->x12_partner['x12_isa01'];
602 function x12gsisa02() {
603 return $this->x12_partner['x12_isa02'];
606 function x12gsisa03() {
607 return $this->x12_partner['x12_isa03'];
609 function x12gsisa04() {
610 return $this->x12_partner['x12_isa04'];
613 /////////
614 function x12gsisa07() {
615 return $this->x12_partner['x12_isa07'];
618 function x12gsisa14() {
619 return $this->x12_partner['x12_isa14'];
622 function x12gsisa15() {
623 return $this->x12_partner['x12_isa15'];
626 function x12gsgs02() {
627 $tmp = $this->x12_partner['x12_gs02'];
628 if ($tmp === '') $tmp = $this->x12_partner['x12_sender_id'];
629 return $tmp;
632 function x12gsper06() {
633 return $this->x12_partner['x12_per06'];
636 function cliaCode() {
637 return x12clean(trim($this->facility['domain_identifier']));
640 function billingFacilityName() {
641 return x12clean(trim($this->billing_facility['name']));
644 function billingFacilityStreet() {
645 return x12clean(trim($this->billing_facility['street']));
648 function billingFacilityCity() {
649 return x12clean(trim($this->billing_facility['city']));
652 function billingFacilityState() {
653 return x12clean(trim($this->billing_facility['state']));
656 function billingFacilityZip() {
657 return x12clean(trim($this->billing_facility['postal_code']));
660 function billingFacilityETIN() {
661 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
664 function billingFacilityNPI() {
665 return x12clean(trim($this->billing_facility['facility_npi']));
668 function federalIdType() {
669 if ($this->billing_facility['tax_id_type'])
671 return $this->billing_facility['tax_id_type'];
673 else{
674 return null;
678 # The billing facility and the patient must both accept for this to return true.
679 function billingFacilityAssignment($ins=0) {
680 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
681 if (strcmp($tmp,'FALSE') == 0) return '0';
682 return !empty($this->billing_facility['accepts_assignment']);
685 function billingContactName() {
686 return x12clean(trim($this->billing_facility['attn']));
689 function billingContactPhone() {
690 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
691 $this->billing_facility['phone'], $tmp))
693 return $tmp[1] . $tmp[2] . $tmp[3];
695 return '';
698 function facilityName() {
699 return x12clean(trim($this->facility['name']));
702 function facilityStreet() {
703 return x12clean(trim($this->facility['street']));
706 function facilityCity() {
707 return x12clean(trim($this->facility['city']));
710 function facilityState() {
711 return x12clean(trim($this->facility['state']));
714 function facilityZip() {
715 return x12clean(trim($this->facility['postal_code']));
718 function facilityETIN() {
719 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
722 function facilityNPI() {
723 return x12clean(trim($this->facility['facility_npi']));
726 function facilityPOS() {
727 return sprintf('%02d', trim($this->facility['pos_code']));
730 function clearingHouseName() {
731 return x12clean(trim($this->x12_partner['name']));
734 function clearingHouseETIN() {
735 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
738 function providerNumberType($prockey=-1) {
739 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
740 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
741 return $tmp['provider_number_type'];
744 function providerNumber($prockey=-1) {
745 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
746 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
747 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
750 function providerGroupNumber($prockey=-1) {
751 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
752 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
753 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
756 // Returns 'P', 'S' or 'T'.
758 function payerSequence($ins=0) {
759 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
762 // Returns the HIPAA code of the patient-to-subscriber relationship.
764 function insuredRelationship($ins=0) {
765 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
766 if (strcmp($tmp,'self' ) == 0) return '18';
767 if (strcmp($tmp,'spouse') == 0) return '01';
768 if (strcmp($tmp,'child' ) == 0) return '19';
769 if (strcmp($tmp,'other' ) == 0) return 'G8';
770 return $tmp; // should not happen
773 function insuredTypeCode($ins=0) {
774 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
775 return $this->payers[$ins]['data']['policy_type'];
776 return '';
779 // Is the patient also the subscriber?
781 function isSelfOfInsured($ins=0) {
782 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
783 return (strcmp($tmp,'self') == 0);
786 function planName($ins=0) {
787 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
790 function policyNumber($ins=0) { // "ID"
791 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
794 function groupNumber($ins=0) {
795 return x12clean(trim($this->payers[$ins]['data']['group_number']));
798 function groupName($ins=0) {
799 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
802 // Claim types are:
803 // 16 Other HCFA
804 // MB Medicare Part B
805 // MC Medicaid
806 // CH ChampUSVA
807 // CH ChampUS
808 // BL Blue Cross Blue Shield
809 // 16 FECA
810 // 09 Self Pay
811 // 10 Central Certification
812 // 11 Other Non-Federal Programs
813 // 12 Preferred Provider Organization (PPO)
814 // 13 Point of Service (POS)
815 // 14 Exclusive Provider Organization (EPO)
816 // 15 Indemnity Insurance
817 // 16 Health Maintenance Organization (HMO) Medicare Risk
818 // AM Automobile Medical
819 // CI Commercial Insurance Co.
820 // DS Disability
821 // HM Health Maintenance Organization
822 // LI Liability
823 // LM Liability Medical
824 // OF Other Federal Program
825 // TV Title V
826 // VA Veterans Administration Plan
827 // WC Workers Compensation Health Plan
828 // ZZ Mutually Defined
830 function claimType($ins=0) {
831 if (empty($this->payers[$ins]['object'])) return '';
832 return $this->payers[$ins]['object']->get_freeb_claim_type();
835 function claimTypeRaw($ins=0) {
836 if (empty($this->payers[$ins]['object'])) return 0;
837 return $this->payers[$ins]['object']->get_freeb_type();
840 function insuredLastName($ins=0) {
841 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
844 function insuredFirstName($ins=0) {
845 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
848 function insuredMiddleName($ins=0) {
849 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
852 function insuredStreet($ins=0) {
853 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
856 function insuredCity($ins=0) {
857 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
860 function insuredState($ins=0) {
861 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
864 function insuredZip($ins=0) {
865 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
868 function insuredPhone($ins=0) {
869 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
870 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
871 return $tmp[1] . $tmp[2] . $tmp[3];
872 return '';
875 function insuredDOB($ins=0) {
876 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
879 function insuredSex($ins=0) {
880 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
883 function payerName($ins=0) {
884 return x12clean(trim($this->payers[$ins]['company']['name']));
887 function payerAttn($ins=0) {
888 return x12clean(trim($this->payers[$ins]['company']['attn']));
891 function payerStreet($ins=0) {
892 if (empty($this->payers[$ins]['object'])) return '';
893 $tmp = $this->payers[$ins]['object'];
894 $tmp = $tmp->get_address();
895 return x12clean(trim($tmp->get_line1()));
898 function payerCity($ins=0) {
899 if (empty($this->payers[$ins]['object'])) return '';
900 $tmp = $this->payers[$ins]['object'];
901 $tmp = $tmp->get_address();
902 return x12clean(trim($tmp->get_city()));
905 function payerState($ins=0) {
906 if (empty($this->payers[$ins]['object'])) return '';
907 $tmp = $this->payers[$ins]['object'];
908 $tmp = $tmp->get_address();
909 return x12clean(trim($tmp->get_state()));
912 function payerZip($ins=0) {
913 if (empty($this->payers[$ins]['object'])) return '';
914 $tmp = $this->payers[$ins]['object'];
915 $tmp = $tmp->get_address();
916 return x12clean(trim($tmp->get_zip()));
919 function payerID($ins=0) {
920 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
923 function payerAltID($ins=0) {
924 return x12clean(trim($this->payers[$ins]['company']['alt_cms_id']));
927 function patientLastName() {
928 return x12clean(trim($this->patient_data['lname']));
931 function patientFirstName() {
932 return x12clean(trim($this->patient_data['fname']));
935 function patientMiddleName() {
936 return x12clean(trim($this->patient_data['mname']));
939 function patientStreet() {
940 return x12clean(trim($this->patient_data['street']));
943 function patientCity() {
944 return x12clean(trim($this->patient_data['city']));
947 function patientState() {
948 return x12clean(trim($this->patient_data['state']));
951 function patientZip() {
952 return x12clean(trim($this->patient_data['postal_code']));
955 function patientPhone() {
956 $ptphone = $this->patient_data['phone_home'];
957 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
958 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
959 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
960 return $tmp[1] . $tmp[2] . $tmp[3];
961 return '';
964 function patientDOB() {
965 return str_replace('-', '', $this->patient_data['DOB']);
968 function patientSex() {
969 return strtoupper(substr($this->patient_data['sex'], 0, 1));
972 // Patient Marital Status: M = Married, S = Single, or something else.
973 function patientStatus() {
974 return strtoupper(substr($this->patient_data['status'], 0, 1));
977 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
978 // indicate employed.
979 function patientOccupation() {
980 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
983 function cptCode($prockey) {
984 return x12clean(trim($this->procs[$prockey]['code']));
987 function cptModifier($prockey) {
988 // Split on the colon or space and clean each modifier
989 $mods = array();
990 $cln_mods = array();
991 $mods = preg_split("/[: ]/",trim($this->procs[$prockey]['modifier']));
992 foreach ($mods as $mod) {
993 array_push($cln_mods, x12clean($mod));
995 return (implode (':', $cln_mods));
998 function cptNotecodes($prockey) {
999 return x12clean(trim($this->procs[$prockey]['notecodes']));
1002 // Returns the procedure code, followed by ":modifier" if there is one.
1003 function cptKey($prockey) {
1004 $tmp = $this->cptModifier($prockey);
1005 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
1008 function cptCharges($prockey) {
1009 return x12clean(trim($this->procs[$prockey]['fee']));
1012 function cptUnits($prockey) {
1013 if (empty($this->procs[$prockey]['units'])) return '1';
1014 return x12clean(trim($this->procs[$prockey]['units']));
1017 // NDC drug ID.
1018 function cptNDCID($prockey) {
1019 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1020 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1021 $ndc = $tmp[1];
1022 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
1023 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
1025 return x12clean($ndc); // format is bad but return it anyway
1027 return '';
1030 // NDC drug unit of measure code.
1031 function cptNDCUOM($prockey) {
1032 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1033 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
1034 return x12clean($tmp[2]);
1035 return '';
1038 // NDC drug number of units.
1039 function cptNDCQuantity($prockey) {
1040 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1041 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1042 return x12clean(ltrim($tmp[3], '0'));
1044 return '';
1047 function onsetDate() {
1048 return cleanDate($this->encounter['onset_date']);
1051 function onsetDateValid()
1053 return $this->onsetDate()!=='';
1056 function serviceDate() {
1057 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
1060 function priorAuth() {
1061 return x12clean(trim($this->billing_options['prior_auth_number']));
1064 function isRelatedEmployment() {
1065 return !empty($this->billing_options['employment_related']);
1068 function isRelatedAuto() {
1069 return !empty($this->billing_options['auto_accident']);
1072 function isRelatedOther() {
1073 return !empty($this->billing_options['other_accident']);
1076 function autoAccidentState() {
1077 return x12clean(trim($this->billing_options['accident_state']));
1080 function isUnableToWork() {
1081 return !empty($this->billing_options['is_unable_to_work']);
1084 function offWorkFrom() {
1085 return cleanDate($this->billing_options['off_work_from']);
1088 function offWorkTo() {
1089 return cleanDate($this->billing_options['off_work_to']);
1092 function isHospitalized() {
1093 return !empty($this->billing_options['is_hospitalized']);
1096 function hospitalizedFrom() {
1097 return cleanDate($this->billing_options['hospitalization_date_from']);
1100 function hospitalizedTo() {
1101 return cleanDate($this->billing_options['hospitalization_date_to']);
1104 function isOutsideLab() {
1105 return !empty($this->billing_options['outside_lab']);
1108 function outsideLabAmount() {
1109 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
1112 function medicaidResubmissionCode() {
1113 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
1116 function medicaidOriginalReference() {
1117 return x12clean(trim($this->billing_options['medicaid_original_reference']));
1120 function frequencyTypeCode() {
1121 return empty($this->billing_options['replacement_claim']) ? '1' : '7';
1124 function additionalNotes() {
1125 return x12clean(trim($this->billing_options['comments']));
1128 function dateInitialTreatment() {
1129 return cleanDate($this->billing_options['date_initial_treatment']);
1132 function box14qualifier()
1134 // If no box qualifier specified use "431" indicating Onset
1135 return empty($this->billing_options['box_14_date_qual']) ? '431' :
1136 $this->billing_options['box_14_date_qual'];
1139 function box15qualifier()
1141 // If no box qualifier specified use "454" indicating Initial Treatment
1142 return empty($this->billing_options['box_15_date_qual']) ? '454' :
1143 $this->billing_options['box_15_date_qual'];
1145 // Returns an array of unique diagnoses. Periods are stripped by default
1146 // Option to keep periods is to support HCFA 1500 02/12 version
1147 function diagArray($strip_periods=true) {
1148 $da = array();
1149 foreach ($this->procs as $row) {
1150 $atmp = explode(':', $row['justify']);
1151 foreach ($atmp as $tmp) {
1152 if (!empty($tmp)) {
1153 $code_data = explode('|',$tmp);
1155 // If there was a | in the code data, the the first part of the array is the type, and the second is the identifier
1156 if (!empty($code_data[1])) {
1158 // This is the simplest way to determine if the claim is using ICD9 or ICD10 codes
1159 // a mix of code types is generally not allowed as there is only one specifier for all diagnoses on HCFA-1500 form
1160 // and there would be ambiguity with E and V codes
1161 $this->diagtype=$code_data[0];
1163 //code is in the second part of the $code_data array.
1164 if($strip_periods==true)
1166 $diag = str_replace('.', '', $code_data[1]);
1169 else
1171 $diag=$code_data[1];
1175 else {
1176 //No prepended code type label
1177 if($strip_periods) {
1178 $diag = str_replace('.', '', $code_data[0]);
1180 else
1182 $diag=$code_data[0];
1185 $da[$diag] = $diag;
1189 // The above got all the diagnoses used for justification, in the order
1190 // used for justification. Next we go through all diagnoses, justified
1191 // or not, to make sure they all get into the claim. We do it this way
1192 // so that the more important diagnoses appear first.
1193 foreach ($this->diags as $diag) {
1194 if($strip_periods) {$diag = str_replace('.', '', $diag);}
1195 $da[$diag] = $diag;
1197 return $da;
1200 // Compute one 1-relative index in diagArray for the given procedure.
1201 // This function is obsolete, use diagIndexArray() instead.
1202 function diagIndex($prockey) {
1203 $da = $this->diagArray();
1204 $tmp = explode(':', $this->procs[$prockey]['justify']);
1205 if (empty($tmp)) return '';
1206 $diag = str_replace('.', '', $tmp[0]);
1207 $i = 0;
1208 foreach ($da as $value) {
1209 ++$i;
1210 if (strcmp($value,$diag) == 0) return $i;
1212 return '';
1215 // Compute array of 1-relative diagArray indices for the given procedure.
1216 function diagIndexArray($prockey) {
1217 $dia = array();
1218 $da = $this->diagArray();
1219 $atmp = explode(':', $this->procs[$prockey]['justify']);
1220 foreach ($atmp as $tmp) {
1221 if (!empty($tmp)) {
1222 $code_data = explode('|',$tmp);
1223 if (!empty($code_data[1])) {
1224 //Strip the prepended code type label
1225 $diag = str_replace('.', '', $code_data[1]);
1227 else {
1228 //No prepended code type label
1229 $diag = str_replace('.', '', $code_data[0]);
1231 $i = 0;
1232 foreach ($da as $value) {
1233 ++$i;
1234 if (strcmp($value,$diag) == 0) $dia[] = $i;
1238 return $dia;
1241 function providerLastName($prockey=-1) {
1242 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1243 $this->provider : $this->procs[$prockey]['provider'];
1244 return x12clean(trim($tmp['lname']));
1247 function providerFirstName($prockey=-1) {
1248 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1249 $this->provider : $this->procs[$prockey]['provider'];
1250 return x12clean(trim($tmp['fname']));
1253 function providerMiddleName($prockey=-1) {
1254 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1255 $this->provider : $this->procs[$prockey]['provider'];
1256 return x12clean(trim($tmp['mname']));
1259 function providerNPI($prockey=-1) {
1260 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1261 $this->provider : $this->procs[$prockey]['provider'];
1262 return x12clean(trim($tmp['npi']));
1265 function NPIValid($npi)
1267 // A NPI MUST be a 10 digit number
1268 if($npi==='') return false;
1269 if(strlen($npi)!=10) return false;
1270 if(!preg_match("/[0-9]*/",$npi)) return false;
1271 return true;
1274 function providerNPIValid($prockey=-1)
1276 return $this->NPIValid($this->providerNPI($prockey));
1279 function providerUPIN($prockey=-1) {
1280 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1281 $this->provider : $this->procs[$prockey]['provider'];
1282 return x12clean(trim($tmp['upin']));
1285 function providerSSN($prockey=-1) {
1286 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1287 $this->provider : $this->procs[$prockey]['provider'];
1288 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1291 function providerTaxonomy($prockey=-1) {
1292 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1293 $this->provider : $this->procs[$prockey]['provider'];
1294 if (empty($tmp['taxonomy'])) return '207Q00000X';
1295 return x12clean(trim($tmp['taxonomy']));
1298 function referrerLastName() {
1299 return x12clean(trim($this->referrer['lname']));
1302 function referrerFirstName() {
1303 return x12clean(trim($this->referrer['fname']));
1306 function referrerMiddleName() {
1307 return x12clean(trim($this->referrer['mname']));
1310 function referrerNPI() {
1311 return x12clean(trim($this->referrer['npi']));
1314 function referrerUPIN() {
1315 return x12clean(trim($this->referrer['upin']));
1318 function referrerSSN() {
1319 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1322 function referrerTaxonomy() {
1323 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1324 return x12clean(trim($this->referrer['taxonomy']));
1327 function supervisorLastName() {
1328 return x12clean(trim($this->supervisor['lname']));
1331 function supervisorFirstName() {
1332 return x12clean(trim($this->supervisor['fname']));
1335 function supervisorMiddleName() {
1336 return x12clean(trim($this->supervisor['mname']));
1339 function supervisorNPI() {
1340 return x12clean(trim($this->supervisor['npi']));
1343 function supervisorUPIN() {
1344 return x12clean(trim($this->supervisor['upin']));
1347 function supervisorSSN() {
1348 return x12clean(trim(str_replace('-', '', $this->supervisor['federaltaxid'])));
1351 function supervisorTaxonomy() {
1352 if (empty($this->supervisor['taxonomy'])) return '207Q00000X';
1353 return x12clean(trim($this->supervisor['taxonomy']));
1356 function supervisorNumberType() {
1357 return $this->supervisor_numbers['provider_number_type'];
1360 function supervisorNumber() {
1361 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers['provider_number'])));