Also included Payment link in Fees left_nav menu and renamed the the Payments to...
[openemr.git] / library / Claim.class.php
blob7e49e0dd102c0c11e688a5a7b14813799c52fb95
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 $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 billing 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 if ($GLOBALS['oer_config']['ws_accounting']['enabled'] === 2) {
110 $this->invoice = ar_get_invoice_summary($this->pid, $this->encounter_id, true);
112 else if ($GLOBALS['oer_config']['ws_accounting']['enabled']) {
113 SLConnect();
114 $arres = SLQuery("select id from ar where invnumber = " .
115 "'{$this->pid}.{$this->encounter_id}'");
116 if ($sl_err) die($sl_err);
117 $arrow = SLGetRow($arres, 0);
118 if ($arrow) {
119 $this->invoice = get_invoice_summary($arrow['id'], true);
121 SLClose();
123 // Secondary claims might not have modifiers in SQL-Ledger data.
124 // In that case, note that we should not try to match on them.
125 $this->using_modifiers = false;
126 foreach ($this->invoice as $key => $trash) {
127 if (strpos($key, ':')) $this->using_modifiers = true;
132 // Constructor. Loads relevant database information.
134 function Claim($pid, $encounter_id) {
135 $this->pid = $pid;
136 $this->encounter_id = $encounter_id;
137 $this->procs = array();
138 $this->diags = array();
139 $this->copay = 0;
141 // We need the encounter date before we can identify the payers.
142 $sql = "SELECT * FROM form_encounter WHERE " .
143 "pid = '{$this->pid}' AND " .
144 "encounter = '{$this->encounter_id}'";
145 $this->encounter = sqlQuery($sql);
147 // Sort by procedure timestamp in order to get some consistency.
148 $sql = "SELECT b.id, b.date, b.code_type, b.code, b.pid, b.provider_id, " .
149 "b.user, b.groupname, b.authorized, b.encounter, b.code_text, b.billed, " .
150 "b.activity, b.payer_id, b.bill_process, b.bill_date, b.process_date, " .
151 "b.process_file, b.modifier, b.units, b.fee, b.justify, b.target, b.x12_partner_id, " .
152 "b.ndc_info, b.notecodes, ct.ct_diag " .
153 "FROM billing as b INNER JOIN code_types as ct " .
154 "ON b.code_type = ct.ct_key " .
155 "WHERE ct.ct_claim = '1' AND ct.ct_active = '1' AND " .
156 "b.encounter = '{$this->encounter_id}' AND b.pid = '{$this->pid}' AND " .
157 "b.activity = '1' ORDER BY b.date, b.id";
158 $res = sqlStatement($sql);
159 while ($row = sqlFetchArray($res)) {
160 if ($row['code_type'] == 'COPAY') {
161 $this->copay -= $row['fee'];
162 continue;
164 // Save all diagnosis codes.
165 if ($row['ct_diag'] == '1') {
166 $this->diags[$row['code']] = $row['code'];
167 continue;
169 if (!$row['units']) $row['units'] = 1;
170 // Load prior payer data at the first opportunity in order to get
171 // the using_modifiers flag that is referenced below.
172 if (empty($this->procs)) $this->loadPayerInfo($row);
173 // Consolidate duplicate procedures.
174 foreach ($this->procs as $key => $trash) {
175 if (strcmp($this->procs[$key]['code'],$row['code']) == 0 &&
176 (strcmp($this->procs[$key]['modifier'],$row['modifier']) == 0 ||
177 !$this->using_modifiers))
179 $this->procs[$key]['units'] += $row['units'];
180 $this->procs[$key]['fee'] += $row['fee'];
181 continue 2; // skip to next table row
185 // If there is a row-specific provider then get its details.
186 if (!empty($row['provider_id'])) {
187 // Get service provider data for this row.
188 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
189 $row['provider'] = sqlQuery($sql);
190 // Get insurance numbers for this row's provider.
191 $sql = "SELECT * FROM insurance_numbers WHERE " .
192 "(insurance_company_id = '" . $row['payer_id'] .
193 "' OR insurance_company_id is NULL) AND " .
194 "provider_id = '" . $row['provider_id'] . "' " .
195 "ORDER BY insurance_company_id DESC LIMIT 1";
196 $row['insurance_numbers'] = sqlQuery($sql);
199 $this->procs[] = $row;
202 $resMoneyGot = sqlStatement("SELECT pay_amount as PatientPay,session_id as id,".
203 "date(post_time) as date FROM ar_activity where pid ='{$this->pid}' and encounter ='{$this->encounter_id}' ".
204 "and payer_type=0 and account_code='PCP'");
205 //new fees screen copay gives account_code='PCP'
206 while($rowMoneyGot = sqlFetchArray($resMoneyGot)){
207 $PatientPay=$rowMoneyGot['PatientPay']*-1;
208 $this->copay -= $PatientPay;
211 $sql = "SELECT * FROM x12_partners WHERE " .
212 "id = '" . $this->procs[0]['x12_partner_id'] . "'";
213 $this->x12_partner = sqlQuery($sql);
215 $sql = "SELECT * FROM facility WHERE " .
216 "id = '" . addslashes($this->encounter['facility_id']) . "' " .
217 "LIMIT 1";
218 $this->facility = sqlQuery($sql);
220 /*****************************************************************
221 $provider_id = $this->procs[0]['provider_id'];
222 *****************************************************************/
223 $provider_id = $this->encounter['provider_id'];
224 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
225 $this->provider = sqlQuery($sql);
226 // Selecting the billing facility assigned to the encounter. If none,
227 // try the first (and hopefully only) facility marked as a billing location.
228 if (empty($this->encounter['billing_facility'])) {
229 $sql = "SELECT * FROM facility " .
230 "ORDER BY billing_location DESC, id ASC LIMIT 1";
232 else {
233 $sql = "SELECT * FROM facility " .
234 " where id ='" . addslashes($this->encounter['billing_facility']) . "' ";
236 $this->billing_facility = sqlQuery($sql);
238 $sql = "SELECT * FROM insurance_numbers WHERE " .
239 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
240 "' OR insurance_company_id is NULL) AND " .
241 "provider_id = '$provider_id' " .
242 "ORDER BY insurance_company_id DESC LIMIT 1";
243 $this->insurance_numbers = sqlQuery($sql);
245 $sql = "SELECT * FROM patient_data WHERE " .
246 "pid = '{$this->pid}' " .
247 "ORDER BY id LIMIT 1";
248 $this->patient_data = sqlQuery($sql);
250 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
251 "ON fpa.id = forms.form_id WHERE " .
252 "forms.encounter = '{$this->encounter_id}' AND " .
253 "forms.pid = '{$this->pid}' AND " .
254 "forms.deleted = 0 AND " .
255 "forms.formdir = 'misc_billing_options' " .
256 "ORDER BY forms.date";
257 $this->billing_options = sqlQuery($sql);
259 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
260 $this->insurance_numbers['provider_number_type'] != '1C') ?
261 $this->patient_data['ref_providerID'] : $provider_id;
262 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
263 $this->referrer = sqlQuery($sql);
264 if (!$this->referrer) $this->referrer = array();
266 $supervisor_id = $this->encounter['supervisor_id'];
267 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
268 $this->supervisor = sqlQuery($sql);
269 if (!$this->supervisor) $this->supervisor = array();
271 $sql = "SELECT * FROM insurance_numbers WHERE " .
272 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
273 "' OR insurance_company_id is NULL) AND " .
274 "provider_id = '$supervisor_id' " .
275 "ORDER BY insurance_company_id DESC LIMIT 1";
276 $this->supervisor_numbers = sqlQuery($sql);
277 if (!$this->supervisor_numbers) $this->supervisor_numbers = array();
279 } // end constructor
281 // Return an array of adjustments from the designated prior payer for the
282 // designated procedure key (might be procedure:modifier), or for the claim
283 // level. For each adjustment give date, group code, reason code, amount.
284 // Note this will include "patient responsibility" adjustments which are
285 // not adjustments to OUR invoice, but they reduce the amount that the
286 // insurance company pays.
288 function payerAdjustments($ins, $code='Claim') {
289 $aadj = array();
291 // If we have no modifiers stored in SQL-Ledger for this claim,
292 // then we cannot use a modifier passed in with the key.
293 $tmp = strpos($code, ':');
294 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
296 // For payments, source always starts with "Ins" or "Pt".
297 // Nonzero adjustment reason examples:
298 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
299 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
300 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
301 // Ins1 adjust code A2 (Contractual adjustment)
302 // Ins adjust Ins1
303 // adjust code 45
304 // Zero adjustment reason examples:
305 // Co-pay: 25.00
306 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
307 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
308 // To copay Ins1 (manual entry)
309 // To ded'ble Ins1 (manual entry)
311 if (!empty($this->invoice[$code])) {
312 $date = '';
313 $deductible = 0;
314 $coinsurance = 0;
315 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
316 $insnumber = substr($inslabel, 3);
318 // Compute this procedure's patient responsibility amount as of this
319 // prior payer, which is the original charge minus all insurance
320 // payments and "hard" adjustments up to this payer.
321 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
322 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
323 if (isset($value['plv'])) {
324 // New method; plv (from ar_activity.payer_type) exists to
325 // indicate the payer level.
326 if (isset($value['pmt']) && $value['pmt'] != 0) {
327 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
328 $ptresp -= $value['pmt'];
330 else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
331 // non-blank key indicates this is an adjustment and not a charge
332 if ($value['plv'] > 0 && $value['plv'] <= $insnumber)
333 $ptresp += $value['chg']; // adjustments are negative charges
336 $msp = isset( $value['msp'] ) ? $value['msp'] : null; // record the reason for adjustment
338 else {
339 // Old method: With SQL-Ledger payer level was stored in the memo.
340 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
341 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
343 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
344 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) || $tmp[1] <= $insnumber)
345 $ptresp += $value['chg']; // adjustments are negative charges
349 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
351 // Main loop, to extract adjustments for this payer and procedure.
352 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
353 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
354 if ($tmp) $date = $tmp;
355 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
356 $rsn = $value['rsn'];
357 $chg = 0 - $value['chg']; // adjustments are negative charges
359 $gcode = 'CO'; // default group code = contractual obligation
360 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
362 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
363 // From manual post. Take the defaults.
365 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
366 $coinsurance = $ptresp; // from manual post
367 continue;
369 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
370 $deductible = $ptresp; // from manual post
371 continue;
373 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
374 $coinsurance = $tmp[1]; // from 835 as of 6/2007
375 continue;
377 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
378 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
379 continue;
381 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
382 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
383 continue;
385 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
386 continue; // from 835 as of 6/2007
388 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
389 $rcode = $tmp[1]; // from 835
391 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
392 // Take the defaults.
394 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
395 continue; // it's for some other payer
397 else if ($insnumber == '1') {
398 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
399 $rcode = $tmp[1]; // from 835
401 else if ($chg) {
402 // Other adjustments default to Ins1.
404 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
405 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
406 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
407 continue;
409 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
410 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
411 continue;
413 else {
414 continue; // there is no adjustment amount
417 else {
418 continue; // it's for primary and that's not us
421 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
422 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
424 } // end if
425 } // end foreach
427 // If we really messed it up, at least avoid negative numbers.
428 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
429 if ($deductible > $ptresp) $deductible = $ptresp;
431 // Find out if this payer paid anything at all on this claim. This will
432 // help us allocate any unknown patient responsibility amounts.
433 $thispaidanything = 0;
434 foreach($this->invoice as $codekey => $codeval) {
435 foreach ($codeval['dtl'] as $key => $value) {
436 if (isset($value['plv'])) {
437 // New method; plv exists to indicate the payer level.
438 if ($value['plv'] == $insnumber) {
439 $thispaidanything += $value['pmt'];
442 else {
443 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
444 $thispaidanything += $value['pmt'];
450 // Allocate any unknown patient responsibility by guessing if the
451 // deductible has been satisfied.
452 if ($thispaidanything)
453 $coinsurance = $ptresp - $deductible;
454 else
455 $deductible = $ptresp - $coinsurance;
457 $deductible = sprintf('%.2f', $deductible);
458 $coinsurance = sprintf('%.2f', $coinsurance);
460 if ($date && $deductible != 0)
461 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
462 if ($date && $coinsurance != 0)
463 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
465 } // end if
467 return $aadj;
470 // Return date, total payments and total "hard" adjustments from the given
471 // prior payer. If $code is specified then only that procedure key is
472 // selected, otherwise it's for the whole claim.
474 function payerTotals($ins, $code='') {
475 // If we have no modifiers stored in SQL-Ledger for this claim,
476 // then we cannot use a modifier passed in with the key.
477 $tmp = strpos($code, ':');
478 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
480 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
481 $insnumber = substr($inslabel, 3);
482 $paytotal = 0;
483 $adjtotal = 0;
484 $date = '';
485 foreach($this->invoice as $codekey => $codeval) {
486 if ($code && strcmp($codekey,$code) != 0) continue;
487 foreach ($codeval['dtl'] as $key => $value) {
488 if (isset($value['plv'])) {
489 // New method; plv (from ar_activity.payer_type) exists to
490 // indicate the payer level.
491 if ($value['plv'] == $insnumber) {
492 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
493 $paytotal += $value['pmt'];
496 else {
497 // Old method: With SQL-Ledger payer level was stored in the memo.
498 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
499 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
500 $paytotal += $value['pmt'];
504 $aarr = $this->payerAdjustments($ins, $codekey);
505 foreach ($aarr as $a) {
506 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
507 if (!$date) $date = $a[0];
510 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
513 // Return the amount already paid by the patient.
515 function patientPaidAmount() {
516 // For primary claims $this->invoice is not loaded, so get the co-pay
517 // from the billing table instead.
518 if (empty($this->invoice)) return $this->copay;
520 $amount = 0;
521 foreach($this->invoice as $codekey => $codeval) {
522 foreach ($codeval['dtl'] as $key => $value) {
523 if (isset($value['plv'])) {
524 // New method; plv exists to indicate the payer level.
525 if ($value['plv'] == 0) { // 0 indicates patient
526 $amount += $value['pmt'];
529 else {
530 // Old method: With SQL-Ledger payer level was stored in the memo.
531 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
532 $amount += $value['pmt'];
537 return sprintf('%.2f', $amount);
540 // Return invoice total, including adjustments but not payments.
542 function invoiceTotal() {
543 $amount = 0;
544 foreach($this->invoice as $codekey => $codeval) {
545 $amount += $codeval['chg'];
547 return sprintf('%.2f', $amount);
550 // Number of procedures in this claim.
551 function procCount() {
552 return count($this->procs);
555 // Number of payers for this claim. Ranges from 1 to 3.
556 function payerCount() {
557 return count($this->payers);
560 function x12gsversionstring() {
561 return x12clean(trim($this->x12_partner['x12_version']));
564 function x12gssenderid() {
565 $tmp = $this->x12_partner['x12_sender_id'];
566 while (strlen($tmp) < 15) $tmp .= " ";
567 return $tmp;
570 function x12gs03() {
572 * GS03: Application Receiver's Code
573 * Code Identifying Party Receiving Transmission
575 * In most cases, the ISA08 and GS03 are the same. However
577 * In some clearing houses ISA08 and GS03 are different
578 * Example: http://www.acs-gcro.com/downloads/DOL/DOL_CG_X12N_5010_837_v1_02.pdf - Page 18
579 * In this .pdf, the ISA08 is specified to be 100000 while the GS03 is specified to be 77044
581 * Therefore if the x12_gs03 segement is explicitly specified we use that value,
582 * otherwise we simply use the same receiver ID as specified for ISA03
584 if($this->x12_partner['x12_gs03'] !== '')
585 return $this->x12_partner['x12_gs03'];
586 else
587 return $this->x12_partner['x12_receiver_id'];
590 function x12gsreceiverid() {
591 $tmp = $this->x12_partner['x12_receiver_id'];
592 while (strlen($tmp) < 15) $tmp .= " ";
593 return $tmp;
596 function x12gsisa05() {
597 return $this->x12_partner['x12_isa05'];
599 //adding in functions for isa 01 - isa 04
601 function x12gsisa01() {
602 return $this->x12_partner['x12_isa01'];
605 function x12gsisa02() {
606 return $this->x12_partner['x12_isa02'];
609 function x12gsisa03() {
610 return $this->x12_partner['x12_isa03'];
612 function x12gsisa04() {
613 return $this->x12_partner['x12_isa04'];
616 /////////
617 function x12gsisa07() {
618 return $this->x12_partner['x12_isa07'];
621 function x12gsisa14() {
622 return $this->x12_partner['x12_isa14'];
625 function x12gsisa15() {
626 return $this->x12_partner['x12_isa15'];
629 function x12gsgs02() {
630 $tmp = $this->x12_partner['x12_gs02'];
631 if ($tmp === '') $tmp = $this->x12_partner['x12_sender_id'];
632 return $tmp;
635 function x12gsper06() {
636 return $this->x12_partner['x12_per06'];
639 function cliaCode() {
640 return x12clean(trim($this->facility['domain_identifier']));
643 function billingFacilityName() {
644 return x12clean(trim($this->billing_facility['name']));
647 function billingFacilityStreet() {
648 return x12clean(trim($this->billing_facility['street']));
651 function billingFacilityCity() {
652 return x12clean(trim($this->billing_facility['city']));
655 function billingFacilityState() {
656 return x12clean(trim($this->billing_facility['state']));
659 function billingFacilityZip() {
660 return x12clean(trim($this->billing_facility['postal_code']));
663 function billingFacilityETIN() {
664 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
667 function billingFacilityNPI() {
668 return x12clean(trim($this->billing_facility['facility_npi']));
671 function federalIdType() {
672 if ($this->billing_facility['tax_id_type'])
674 return $this->billing_facility['tax_id_type'];
676 else{
677 return null;
681 # The billing facility and the patient must both accept for this to return true.
682 function billingFacilityAssignment($ins=0) {
683 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
684 if (strcmp($tmp,'FALSE') == 0) return '0';
685 return !empty($this->billing_facility['accepts_assignment']);
688 function billingContactName() {
689 return x12clean(trim($this->billing_facility['attn']));
692 function billingContactPhone() {
693 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
694 $this->billing_facility['phone'], $tmp))
696 return $tmp[1] . $tmp[2] . $tmp[3];
698 return '';
701 function facilityName() {
702 return x12clean(trim($this->facility['name']));
705 function facilityStreet() {
706 return x12clean(trim($this->facility['street']));
709 function facilityCity() {
710 return x12clean(trim($this->facility['city']));
713 function facilityState() {
714 return x12clean(trim($this->facility['state']));
717 function facilityZip() {
718 return x12clean(trim($this->facility['postal_code']));
721 function facilityETIN() {
722 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
725 function facilityNPI() {
726 return x12clean(trim($this->facility['facility_npi']));
729 function facilityPOS() {
730 return sprintf('%02d', trim($this->facility['pos_code']));
733 function clearingHouseName() {
734 return x12clean(trim($this->x12_partner['name']));
737 function clearingHouseETIN() {
738 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
741 function providerNumberType($prockey=-1) {
742 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
743 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
744 return $tmp['provider_number_type'];
747 function providerNumber($prockey=-1) {
748 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
749 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
750 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
753 function providerGroupNumber($prockey=-1) {
754 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
755 $this->insurance_numbers : $this->procs[$prockey]['insurance_numbers'];
756 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
759 // Returns 'P', 'S' or 'T'.
761 function payerSequence($ins=0) {
762 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
765 // Returns the HIPAA code of the patient-to-subscriber relationship.
767 function insuredRelationship($ins=0) {
768 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
769 if (strcmp($tmp,'self' ) == 0) return '18';
770 if (strcmp($tmp,'spouse') == 0) return '01';
771 if (strcmp($tmp,'child' ) == 0) return '19';
772 if (strcmp($tmp,'other' ) == 0) return 'G8';
773 return $tmp; // should not happen
776 function insuredTypeCode($ins=0) {
777 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
778 return $this->payers[$ins]['data']['policy_type'];
779 return '';
782 // Is the patient also the subscriber?
784 function isSelfOfInsured($ins=0) {
785 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
786 return (strcmp($tmp,'self') == 0);
789 function planName($ins=0) {
790 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
793 function policyNumber($ins=0) { // "ID"
794 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
797 function groupNumber($ins=0) {
798 return x12clean(trim($this->payers[$ins]['data']['group_number']));
801 function groupName($ins=0) {
802 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
805 // Claim types are:
806 // 16 Other HCFA
807 // MB Medicare Part B
808 // MC Medicaid
809 // CH ChampUSVA
810 // CH ChampUS
811 // BL Blue Cross Blue Shield
812 // 16 FECA
813 // 09 Self Pay
814 // 10 Central Certification
815 // 11 Other Non-Federal Programs
816 // 12 Preferred Provider Organization (PPO)
817 // 13 Point of Service (POS)
818 // 14 Exclusive Provider Organization (EPO)
819 // 15 Indemnity Insurance
820 // 16 Health Maintenance Organization (HMO) Medicare Risk
821 // AM Automobile Medical
822 // CI Commercial Insurance Co.
823 // DS Disability
824 // HM Health Maintenance Organization
825 // LI Liability
826 // LM Liability Medical
827 // OF Other Federal Program
828 // TV Title V
829 // VA Veterans Administration Plan
830 // WC Workers Compensation Health Plan
831 // ZZ Mutually Defined
833 function claimType($ins=0) {
834 if (empty($this->payers[$ins]['object'])) return '';
835 return $this->payers[$ins]['object']->get_freeb_claim_type();
838 function insuredLastName($ins=0) {
839 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
842 function insuredFirstName($ins=0) {
843 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
846 function insuredMiddleName($ins=0) {
847 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
850 function insuredStreet($ins=0) {
851 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
854 function insuredCity($ins=0) {
855 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
858 function insuredState($ins=0) {
859 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
862 function insuredZip($ins=0) {
863 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
866 function insuredPhone($ins=0) {
867 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
868 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
869 return $tmp[1] . $tmp[2] . $tmp[3];
870 return '';
873 function insuredDOB($ins=0) {
874 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
877 function insuredSex($ins=0) {
878 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
881 function payerName($ins=0) {
882 return x12clean(trim($this->payers[$ins]['company']['name']));
885 function payerAttn($ins=0) {
886 return x12clean(trim($this->payers[$ins]['company']['attn']));
889 function payerStreet($ins=0) {
890 if (empty($this->payers[$ins]['object'])) return '';
891 $tmp = $this->payers[$ins]['object'];
892 $tmp = $tmp->get_address();
893 return x12clean(trim($tmp->get_line1()));
896 function payerCity($ins=0) {
897 if (empty($this->payers[$ins]['object'])) return '';
898 $tmp = $this->payers[$ins]['object'];
899 $tmp = $tmp->get_address();
900 return x12clean(trim($tmp->get_city()));
903 function payerState($ins=0) {
904 if (empty($this->payers[$ins]['object'])) return '';
905 $tmp = $this->payers[$ins]['object'];
906 $tmp = $tmp->get_address();
907 return x12clean(trim($tmp->get_state()));
910 function payerZip($ins=0) {
911 if (empty($this->payers[$ins]['object'])) return '';
912 $tmp = $this->payers[$ins]['object'];
913 $tmp = $tmp->get_address();
914 return x12clean(trim($tmp->get_zip()));
917 function payerID($ins=0) {
918 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
921 function payerAltID($ins=0) {
922 return x12clean(trim($this->payers[$ins]['company']['alt_cms_id']));
925 function patientLastName() {
926 return x12clean(trim($this->patient_data['lname']));
929 function patientFirstName() {
930 return x12clean(trim($this->patient_data['fname']));
933 function patientMiddleName() {
934 return x12clean(trim($this->patient_data['mname']));
937 function patientStreet() {
938 return x12clean(trim($this->patient_data['street']));
941 function patientCity() {
942 return x12clean(trim($this->patient_data['city']));
945 function patientState() {
946 return x12clean(trim($this->patient_data['state']));
949 function patientZip() {
950 return x12clean(trim($this->patient_data['postal_code']));
953 function patientPhone() {
954 $ptphone = $this->patient_data['phone_home'];
955 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
956 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
957 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
958 return $tmp[1] . $tmp[2] . $tmp[3];
959 return '';
962 function patientDOB() {
963 return str_replace('-', '', $this->patient_data['DOB']);
966 function patientSex() {
967 return strtoupper(substr($this->patient_data['sex'], 0, 1));
970 // Patient Marital Status: M = Married, S = Single, or something else.
971 function patientStatus() {
972 return strtoupper(substr($this->patient_data['status'], 0, 1));
975 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
976 // indicate employed.
977 function patientOccupation() {
978 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
981 function cptCode($prockey) {
982 return x12clean(trim($this->procs[$prockey]['code']));
985 function cptModifier($prockey) {
986 // Split on the colon or space and clean each modifier
987 $mods = array();
988 $cln_mods = array();
989 $mods = preg_split("/[: ]/",trim($this->procs[$prockey]['modifier']));
990 foreach ($mods as $mod) {
991 array_push($cln_mods, x12clean($mod));
993 return (implode (':', $cln_mods));
996 function cptNotecodes($prockey) {
997 return x12clean(trim($this->procs[$prockey]['notecodes']));
1000 // Returns the procedure code, followed by ":modifier" if there is one.
1001 function cptKey($prockey) {
1002 $tmp = $this->cptModifier($prockey);
1003 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
1006 function cptCharges($prockey) {
1007 return x12clean(trim($this->procs[$prockey]['fee']));
1010 function cptUnits($prockey) {
1011 if (empty($this->procs[$prockey]['units'])) return '1';
1012 return x12clean(trim($this->procs[$prockey]['units']));
1015 // NDC drug ID.
1016 function cptNDCID($prockey) {
1017 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1018 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1019 $ndc = $tmp[1];
1020 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
1021 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
1023 return x12clean($ndc); // format is bad but return it anyway
1025 return '';
1028 // NDC drug unit of measure code.
1029 function cptNDCUOM($prockey) {
1030 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1031 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
1032 return x12clean($tmp[2]);
1033 return '';
1036 // NDC drug number of units.
1037 function cptNDCQuantity($prockey) {
1038 $ndcinfo = $this->procs[$prockey]['ndc_info'];
1039 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1040 return x12clean(ltrim($tmp[3], '0'));
1042 return '';
1045 function onsetDate() {
1046 return cleanDate($this->encounter['onset_date']);
1049 function onsetDateValid()
1051 return $this->onsetDate()!=='';
1054 function serviceDate() {
1055 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
1058 function priorAuth() {
1059 return x12clean(trim($this->billing_options['prior_auth_number']));
1062 function isRelatedEmployment() {
1063 return !empty($this->billing_options['employment_related']);
1066 function isRelatedAuto() {
1067 return !empty($this->billing_options['auto_accident']);
1070 function isRelatedOther() {
1071 return !empty($this->billing_options['other_accident']);
1074 function autoAccidentState() {
1075 return x12clean(trim($this->billing_options['accident_state']));
1078 function isUnableToWork() {
1079 return !empty($this->billing_options['is_unable_to_work']);
1082 function offWorkFrom() {
1083 return cleanDate($this->billing_options['off_work_from']);
1086 function offWorkTo() {
1087 return cleanDate($this->billing_options['off_work_to']);
1090 function isHospitalized() {
1091 return !empty($this->billing_options['is_hospitalized']);
1094 function hospitalizedFrom() {
1095 return cleanDate($this->billing_options['hospitalization_date_from']);
1098 function hospitalizedTo() {
1099 return cleanDate($this->billing_options['hospitalization_date_to']);
1102 function isOutsideLab() {
1103 return !empty($this->billing_options['outside_lab']);
1106 function outsideLabAmount() {
1107 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
1110 function medicaidResubmissionCode() {
1111 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
1114 function medicaidOriginalReference() {
1115 return x12clean(trim($this->billing_options['medicaid_original_reference']));
1118 function frequencyTypeCode() {
1119 return empty($this->billing_options['replacement_claim']) ? '1' : '7';
1122 function additionalNotes() {
1123 return x12clean(trim($this->billing_options['comments']));
1126 function dateInitialTreatment() {
1127 return cleanDate($this->billing_options['date_initial_treatment']);
1130 // Returns an array of unique diagnoses. Periods are stripped.
1131 function diagArray() {
1132 $da = array();
1133 foreach ($this->procs as $row) {
1134 $atmp = explode(':', $row['justify']);
1135 foreach ($atmp as $tmp) {
1136 if (!empty($tmp)) {
1137 $code_data = explode('|',$tmp);
1138 if (!empty($code_data[1])) {
1139 //Strip the prepended code type label
1140 $diag = str_replace('.', '', $code_data[1]);
1142 else {
1143 //No prepended code type label
1144 $diag = str_replace('.', '', $code_data[0]);
1146 $da[$diag] = $diag;
1150 // The above got all the diagnoses used for justification, in the order
1151 // used for justification. Next we go through all diagnoses, justified
1152 // or not, to make sure they all get into the claim. We do it this way
1153 // so that the more important diagnoses appear first.
1154 foreach ($this->diags as $diag) {
1155 $diag = str_replace('.', '', $diag);
1156 $da[$diag] = $diag;
1158 return $da;
1161 // Compute one 1-relative index in diagArray for the given procedure.
1162 // This function is obsolete, use diagIndexArray() instead.
1163 function diagIndex($prockey) {
1164 $da = $this->diagArray();
1165 $tmp = explode(':', $this->procs[$prockey]['justify']);
1166 if (empty($tmp)) return '';
1167 $diag = str_replace('.', '', $tmp[0]);
1168 $i = 0;
1169 foreach ($da as $value) {
1170 ++$i;
1171 if (strcmp($value,$diag) == 0) return $i;
1173 return '';
1176 // Compute array of 1-relative diagArray indices for the given procedure.
1177 function diagIndexArray($prockey) {
1178 $dia = array();
1179 $da = $this->diagArray();
1180 $atmp = explode(':', $this->procs[$prockey]['justify']);
1181 foreach ($atmp as $tmp) {
1182 if (!empty($tmp)) {
1183 $code_data = explode('|',$tmp);
1184 if (!empty($code_data[1])) {
1185 //Strip the prepended code type label
1186 $diag = str_replace('.', '', $code_data[1]);
1188 else {
1189 //No prepended code type label
1190 $diag = str_replace('.', '', $code_data[0]);
1192 $i = 0;
1193 foreach ($da as $value) {
1194 ++$i;
1195 if (strcmp($value,$diag) == 0) $dia[] = $i;
1199 return $dia;
1202 function providerLastName($prockey=-1) {
1203 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1204 $this->provider : $this->procs[$prockey]['provider'];
1205 return x12clean(trim($tmp['lname']));
1208 function providerFirstName($prockey=-1) {
1209 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1210 $this->provider : $this->procs[$prockey]['provider'];
1211 return x12clean(trim($tmp['fname']));
1214 function providerMiddleName($prockey=-1) {
1215 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1216 $this->provider : $this->procs[$prockey]['provider'];
1217 return x12clean(trim($tmp['mname']));
1220 function providerNPI($prockey=-1) {
1221 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1222 $this->provider : $this->procs[$prockey]['provider'];
1223 return x12clean(trim($tmp['npi']));
1226 function NPIValid($npi)
1228 // A NPI MUST be a 10 digit number
1229 if($npi==='') return false;
1230 if(strlen($npi)!=10) return false;
1231 if(!preg_match("/[0-9]*/",$npi)) return false;
1232 return true;
1235 function providerNPIValid($prockey=-1)
1237 return $this->NPIValid($this->providerNPI($prockey));
1240 function providerUPIN($prockey=-1) {
1241 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1242 $this->provider : $this->procs[$prockey]['provider'];
1243 return x12clean(trim($tmp['upin']));
1246 function providerSSN($prockey=-1) {
1247 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1248 $this->provider : $this->procs[$prockey]['provider'];
1249 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1252 function providerTaxonomy($prockey=-1) {
1253 $tmp = ($prockey < 0 || empty($this->procs[$prockey]['provider_id'])) ?
1254 $this->provider : $this->procs[$prockey]['provider'];
1255 if (empty($tmp['taxonomy'])) return '207Q00000X';
1256 return x12clean(trim($tmp['taxonomy']));
1259 function referrerLastName() {
1260 return x12clean(trim($this->referrer['lname']));
1263 function referrerFirstName() {
1264 return x12clean(trim($this->referrer['fname']));
1267 function referrerMiddleName() {
1268 return x12clean(trim($this->referrer['mname']));
1271 function referrerNPI() {
1272 return x12clean(trim($this->referrer['npi']));
1275 function referrerUPIN() {
1276 return x12clean(trim($this->referrer['upin']));
1279 function referrerSSN() {
1280 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1283 function referrerTaxonomy() {
1284 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1285 return x12clean(trim($this->referrer['taxonomy']));
1288 function supervisorLastName() {
1289 return x12clean(trim($this->supervisor['lname']));
1292 function supervisorFirstName() {
1293 return x12clean(trim($this->supervisor['fname']));
1296 function supervisorMiddleName() {
1297 return x12clean(trim($this->supervisor['mname']));
1300 function supervisorNPI() {
1301 return x12clean(trim($this->supervisor['npi']));
1304 function supervisorUPIN() {
1305 return x12clean(trim($this->supervisor['upin']));
1308 function supervisorSSN() {
1309 return x12clean(trim(str_replace('-', '', $this->supervisor['federaltaxid'])));
1312 function supervisorTaxonomy() {
1313 if (empty($this->supervisor['taxonomy'])) return '207Q00000X';
1314 return x12clean(trim($this->supervisor['taxonomy']));
1317 function supervisorNumberType() {
1318 return $this->supervisor_numbers['provider_number_type'];
1321 function supervisorNumber() {
1322 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers['provider_number'])));