Put $GLOBALS['webroot'] in path
[openemr.git] / library / Claim.class.php
blob6f15615dbe225456e1feed7d0263fbcb9b819077
1 <?php
2 // Copyright (C) 2007-2008 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once(dirname(__FILE__) . "/classes/Address.class.php");
10 require_once(dirname(__FILE__) . "/classes/InsuranceCompany.class.php");
11 require_once(dirname(__FILE__) . "/sql-ledger.inc");
12 require_once(dirname(__FILE__) . "/invoice_summary.inc.php");
14 // This enforces the X12 Basic Character Set. Page A2.
16 function x12clean($str) {
17 return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?= ]/', '', strtoupper($str));
20 class Claim {
22 var $pid; // patient id
23 var $encounter_id; // encounter id
24 var $procs; // array of procedure rows from billing table
25 var $x12_partner; // row from x12_partners table
26 var $encounter; // row from form_encounter table
27 var $facility; // row from facility table
28 var $billing_facility; // row from facility table
29 var $provider; // row from users table (rendering provider)
30 var $referrer; // row from users table (referring provider)
31 var $insurance_numbers; // row from insurance_numbers table for current payer
32 var $patient_data; // row from patient_data table
33 var $billing_options; // row from form_misc_billing_options table
34 var $invoice; // result from get_invoice_summary()
35 var $payers; // array of arrays, for all payers
36 var $copay; // total of copays from the billing table
38 function loadPayerInfo(&$billrow) {
39 global $sl_err;
40 $encounter_date = substr($this->encounter['date'], 0, 10);
42 // Create the $payers array. This contains data for all insurances
43 // with the current one always at index 0, and the others in payment
44 // order starting at index 1.
46 $this->payers = array();
47 $this->payers[0] = array();
48 $query = "SELECT * FROM insurance_data WHERE " .
49 "pid = '{$this->pid}' AND " .
50 "date <= '$encounter_date' " .
51 "ORDER BY type ASC, date DESC";
52 $dres = sqlStatement($query);
53 $prevtype = '';
54 while ($drow = sqlFetchArray($dres)) {
55 if (strcmp($prevtype, $drow['type']) == 0) continue;
56 $prevtype = $drow['type'];
57 // Very important to look at entries with a missing provider because
58 // they indicate no insurance as of the given date.
59 if (empty($drow['provider'])) continue;
60 $ins = count($this->payers);
61 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers[0]['data'])) $ins = 0;
62 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
63 "id = '" . $drow['provider'] . "'");
64 $orow = new InsuranceCompany($drow['provider']);
65 $this->payers[$ins] = array();
66 $this->payers[$ins]['data'] = $drow;
67 $this->payers[$ins]['company'] = $crow;
68 $this->payers[$ins]['object'] = $orow;
71 // This kludge hands most cases of a rare ambiguous situation, where
72 // the primary insurance company is the same as the secondary. It seems
73 // nobody planned for that!
75 for ($i = 1; $i < count($this->payers); ++$i) {
76 if ($billrow['process_date'] &&
77 $this->payers[0]['data']['provider'] == $this->payers[$i]['data']['provider'])
79 $tmp = $this->payers[0];
80 $this->payers[0] = $this->payers[$i];
81 $this->payers[$i] = $tmp;
85 $this->using_modifiers = true;
87 // Get payment and adjustment details if there are any previous payers.
89 $this->invoice = array();
90 if ($this->payerSequence() != 'P') {
91 if ($GLOBALS['oer_config']['ws_accounting']['enabled'] === 2) {
92 $this->invoice = ar_get_invoice_summary($this->pid, $this->encounter_id, true);
94 else if ($GLOBALS['oer_config']['ws_accounting']['enabled']) {
95 SLConnect();
96 $arres = SLQuery("select id from ar where invnumber = " .
97 "'{$this->pid}.{$this->encounter_id}'");
98 if ($sl_err) die($sl_err);
99 $arrow = SLGetRow($arres, 0);
100 if ($arrow) {
101 $this->invoice = get_invoice_summary($arrow['id'], true);
103 SLClose();
105 // Secondary claims might not have modifiers in SQL-Ledger data.
106 // In that case, note that we should not try to match on them.
107 $this->using_modifiers = false;
108 foreach ($this->invoice as $key => $trash) {
109 if (strpos($key, ':')) $this->using_modifiers = true;
114 // Constructor. Loads relevant database information.
116 function Claim($pid, $encounter_id) {
117 $this->pid = $pid;
118 $this->encounter_id = $encounter_id;
119 $this->procs = array();
120 $this->copay = 0;
122 // We need the encounter date before we can identify the payers.
123 $sql = "SELECT * FROM form_encounter WHERE " .
124 "pid = '{$this->pid}' AND " .
125 "encounter = '{$this->encounter_id}'";
126 $this->encounter = sqlQuery($sql);
128 // Sort by procedure timestamp in order to get some consistency. In particular
129 // we determine the provider from the first procedure in this array.
130 $sql = "SELECT * FROM billing WHERE " .
131 "encounter = '{$this->encounter_id}' AND pid = '{$this->pid}' AND " .
132 "(code_type = 'CPT4' OR code_type = 'HCPCS' OR code_type = 'COPAY') AND " .
133 "activity = '1' ORDER BY date, id";
134 $res = sqlStatement($sql);
135 while ($row = sqlFetchArray($res)) {
136 if ($row['code_type'] == 'COPAY') {
137 $this->copay -= $row['fee'];
138 continue;
140 if (!$row['units']) $row['units'] = 1;
141 // Load prior payer data at the first opportunity in order to get
142 // the using_modifiers flag that is referenced below.
143 if (empty($this->procs)) $this->loadPayerInfo($row);
144 // Consolidate duplicate procedures.
145 foreach ($this->procs as $key => $trash) {
146 if (strcmp($this->procs[$key]['code'],$row['code']) == 0 &&
147 (strcmp($this->procs[$key]['modifier'],$row['modifier']) == 0 ||
148 !$this->using_modifiers))
150 $this->procs[$key]['units'] += $row['units'];
151 $this->procs[$key]['fee'] += $row['fee'];
152 continue 2; // skip to next table row
155 $this->procs[] = $row;
158 $sql = "SELECT * FROM x12_partners WHERE " .
159 "id = '" . $this->procs[0]['x12_partner_id'] . "'";
160 $this->x12_partner = sqlQuery($sql);
162 $sql = "SELECT * FROM facility WHERE " .
163 "id = '" . addslashes($this->encounter['facility_id']) . "' " .
164 "LIMIT 1";
165 $this->facility = sqlQuery($sql);
167 $provider_id = $this->procs[0]['provider_id'];
168 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
169 $this->provider = sqlQuery($sql);
171 $sql = "SELECT * FROM facility " .
172 "ORDER BY billing_location DESC, id ASC LIMIT 1";
173 $this->billing_facility = sqlQuery($sql);
175 $sql = "SELECT * FROM insurance_numbers WHERE " .
176 "(insurance_company_id = '" . $this->procs[0]['payer_id'] .
177 "' OR insurance_company_id is NULL) AND " .
178 "provider_id = '" . $this->provider['id'] .
179 "' order by insurance_company_id DESC LIMIT 1";
180 $this->insurance_numbers = sqlQuery($sql);
182 $sql = "SELECT * FROM patient_data WHERE " .
183 "pid = '{$this->pid}' " .
184 "ORDER BY id LIMIT 1";
185 $this->patient_data = sqlQuery($sql);
187 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
188 "ON fpa.id = forms.form_id WHERE " .
189 "forms.encounter = '{$this->encounter_id}' AND " .
190 "forms.pid = '{$this->pid}' AND " .
191 "forms.formdir = 'misc_billing_options' " .
192 "ORDER BY forms.date";
193 $this->billing_options = sqlQuery($sql);
195 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
196 $this->insurance_numbers['provider_number_type'] != '1C') ?
197 $this->patient_data['providerID'] : $provider_id;
198 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
199 $this->referrer = sqlQuery($sql);
200 if (!$this->referrer) $this->referrer = array();
202 } // end constructor
204 // Return an array of adjustments from the designated prior payer for the
205 // designated procedure key (might be procedure:modifier), or for the claim
206 // level. For each adjustment give date, group code, reason code, amount.
207 // Note this will include "patient responsibility" adjustments which are
208 // not adjustments to OUR invoice, but they reduce the amount that the
209 // insurance company pays.
211 function payerAdjustments($ins, $code='Claim') {
212 $aadj = array();
214 // If we have no modifiers stored in SQL-Ledger for this claim,
215 // then we cannot use a modifier passed in with the key.
216 $tmp = strpos($code, ':');
217 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
219 // For payments, source always starts with "Ins" or "Pt".
220 // Nonzero adjustment reason examples:
221 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
222 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
223 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
224 // Ins1 adjust code A2 (Contractual adjustment)
225 // Ins adjust Ins1
226 // adjust code 45
227 // Zero adjustment reason examples:
228 // Co-pay: 25.00
229 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
230 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
231 // To copay Ins1 (manual entry)
232 // To ded'ble Ins1 (manual entry)
234 if (!empty($this->invoice[$code])) {
235 $date = '';
236 $deductible = 0;
237 $coinsurance = 0;
238 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
239 $insnumber = substr($inslabel, 3);
241 // Compute this procedure's patient responsibility amount as of this
242 // prior payer, which is the original charge minus all insurance
243 // payments and "hard" adjustments up to this payer.
244 $ptresp = $this->invoice[$code]['chg'] + $this->invoice[$code]['adj'];
245 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
246 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
247 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
249 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
250 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) || $tmp[1] <= $insnumber)
251 $ptresp += $value['chg']; // adjustments are negative charges
254 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
256 // Main loop, to extract adjustments for this payer and procedure.
257 foreach ($this->invoice[$code]['dtl'] as $key => $value) {
258 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
259 if ($tmp) $date = $tmp;
260 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
261 $rsn = $value['rsn'];
262 $chg = 0 - $value['chg']; // adjustments are negative charges
264 $gcode = 'CO'; // default group code = contractual obligation
265 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
267 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
268 // From manual post. Take the defaults.
270 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
271 $coinsurance = $ptresp; // from manual post
272 continue;
274 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
275 $deductible = $ptresp; // from manual post
276 continue;
278 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
279 $coinsurance = $tmp[1]; // from 835 as of 6/2007
280 continue;
282 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
283 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
284 continue;
286 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
287 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
288 continue;
290 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
291 continue; // from 835 as of 6/2007
293 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
294 $rcode = $tmp[1]; // from 835
296 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
297 // Take the defaults.
299 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
300 continue; // it's for some other payer
302 else if ($insnumber == '1') {
303 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
304 $rcode = $tmp[1]; // from 835
306 else if ($chg) {
307 // Other adjustments default to Ins1.
309 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
310 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
311 $coinsurance = 0 + $tmp[1]; // from 835 before 6/2007
312 continue;
314 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
315 $deductible = 0 + $tmp[1]; // from 835 before 6/2007
316 continue;
318 else {
319 continue; // there is no adjustment amount
322 else {
323 continue; // it's for primary and that's not us
326 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
327 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
329 } // end if
330 } // end foreach
332 // If we really messed it up, at least avoid negative numbers.
333 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
334 if ($deductible > $ptresp) $deductible = $ptresp;
336 // Find out if this payer paid anything at all on this claim. This will
337 // help us allocate any unknown patient responsibility amounts.
338 $thispaidanything = 0;
339 foreach($this->invoice as $codekey => $codeval) {
340 foreach ($codeval['dtl'] as $key => $value) {
341 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
342 $thispaidanything += $value['pmt'];
347 // Allocate any unknown patient responsibility by guessing if the
348 // deductible has been satisfied.
349 if ($thispaidanything)
350 $coinsurance = $ptresp - $deductible;
351 else
352 $deductible = $ptresp - $coinsurance;
354 if ($date && $deductible != 0)
355 $aadj[] = array($date, 'PR', '1', sprintf('%.2f', $deductible));
356 if ($date && $coinsurance != 0)
357 $aadj[] = array($date, 'PR', '2', sprintf('%.2f', $coinsurance));
359 } // end if
361 return $aadj;
364 // Return date, total payments and total "hard" adjustments from the given
365 // prior payer. If $code is specified then only that procedure key is
366 // selected, otherwise it's for the whole claim.
368 function payerTotals($ins, $code='') {
369 // If we have no modifiers stored in SQL-Ledger for this claim,
370 // then we cannot use a modifier passed in with the key.
371 $tmp = strpos($code, ':');
372 if ($tmp && !$this->using_modifiers) $code = substr($code, 0, $tmp);
374 $inslabel = ($this->payerSequence($ins) == 'S') ? 'Ins2' : 'Ins1';
375 $paytotal = 0;
376 $adjtotal = 0;
377 $date = '';
378 foreach($this->invoice as $codekey => $codeval) {
379 if ($code && strcmp($codekey,$code) != 0) continue;
380 foreach ($codeval['dtl'] as $key => $value) {
381 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
382 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
383 $paytotal += $value['pmt'];
386 $aarr = $this->payerAdjustments($ins, $codekey);
387 foreach ($aarr as $a) {
388 if (strcmp($a[1],'PR') != 0) $adjtotal += $a[3];
389 if (!$date) $date = $a[0];
392 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
395 // Return the amount already paid by the patient.
397 function patientPaidAmount() {
398 // For primary claims $this->invoice is not loaded, so get the co-pay
399 // from the billing table instead.
400 if (empty($this->invoice)) return $this->copay;
402 $amount = 0;
403 foreach($this->invoice as $codekey => $codeval) {
404 foreach ($codeval['dtl'] as $key => $value) {
405 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
406 $amount += $value['pmt'];
410 return sprintf('%.2f', $amount);
413 // Return invoice total, including adjustments but not payments.
415 function invoiceTotal() {
416 $amount = 0;
417 foreach($this->invoice as $codekey => $codeval) {
418 $amount += $codeval['chg'];
420 return sprintf('%.2f', $amount);
423 // Number of procedures in this claim.
424 function procCount() {
425 return count($this->procs);
428 // Number of payers for this claim. Ranges from 1 to 3.
429 function payerCount() {
430 return count($this->payers);
433 function x12gsversionstring() {
434 return x12clean(trim($this->x12_partner['x12_version']));
437 function x12gssenderid() {
438 $tmp = $this->x12_partner['x12_sender_id'];
439 while (strlen($tmp) < 15) $tmp .= " ";
440 return $tmp;
443 function x12gsreceiverid() {
444 $tmp = $this->x12_partner['x12_receiver_id'];
445 while (strlen($tmp) < 15) $tmp .= " ";
446 return $tmp;
449 function cliaCode() {
450 return x12clean(trim($this->facility['domain_identifier']));
453 function billingFacilityName() {
454 return x12clean(trim($this->billing_facility['name']));
457 function billingFacilityStreet() {
458 return x12clean(trim($this->billing_facility['street']));
461 function billingFacilityCity() {
462 return x12clean(trim($this->billing_facility['city']));
465 function billingFacilityState() {
466 return x12clean(trim($this->billing_facility['state']));
469 function billingFacilityZip() {
470 return x12clean(trim($this->billing_facility['postal_code']));
473 function billingFacilityETIN() {
474 return x12clean(trim(str_replace('-', '', $this->billing_facility['federal_ein'])));
477 function billingFacilityNPI() {
478 return x12clean(trim($this->billing_facility['facility_npi']));
481 # The billing facility and the patient must both accept for this to return true.
482 function billingFacilityAssignment($ins=0) {
483 $tmp = strtoupper($this->payers[$ins]['data']['accept_assignment']);
484 if (strcmp($tmp,'FALSE') == 0) return '0';
485 return !empty($this->billing_facility['accepts_assignment']);
488 function billingContactName() {
489 return x12clean(trim($this->billing_facility['attn']));
492 function billingContactPhone() {
493 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
494 $this->billing_facility['phone'], $tmp))
496 return $tmp[1] . $tmp[2] . $tmp[3];
498 return '';
501 function facilityName() {
502 return x12clean(trim($this->facility['name']));
505 function facilityStreet() {
506 return x12clean(trim($this->facility['street']));
509 function facilityCity() {
510 return x12clean(trim($this->facility['city']));
513 function facilityState() {
514 return x12clean(trim($this->facility['state']));
517 function facilityZip() {
518 return x12clean(trim($this->facility['postal_code']));
521 function facilityETIN() {
522 return x12clean(trim(str_replace('-', '', $this->facility['federal_ein'])));
525 function facilityNPI() {
526 return x12clean(trim($this->facility['facility_npi']));
529 function facilityPOS() {
530 return x12clean(trim($this->facility['pos_code']));
533 function clearingHouseName() {
534 return x12clean(trim($this->x12_partner['name']));
537 function clearingHouseETIN() {
538 return x12clean(trim(str_replace('-', '', $this->x12_partner['id_number'])));
541 function providerNumberType() {
542 return $this->insurance_numbers['provider_number_type'];
545 function providerNumber() {
546 return x12clean(trim(str_replace('-', '', $this->insurance_numbers['provider_number'])));
549 function providerGroupNumber() {
550 return x12clean(trim(str_replace('-', '', $this->insurance_numbers['group_number'])));
553 // Returns 'P', 'S' or 'T'.
555 function payerSequence($ins=0) {
556 return strtoupper(substr($this->payers[$ins]['data']['type'], 0, 1));
559 // Returns the HIPAA code of the patient-to-subscriber relationship.
561 function insuredRelationship($ins=0) {
562 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
563 if (strcmp($tmp,'self' ) == 0) return '18';
564 if (strcmp($tmp,'spouse') == 0) return '01';
565 if (strcmp($tmp,'child' ) == 0) return '19';
566 if (strcmp($tmp,'other' ) == 0) return 'G8';
567 return $tmp; // should not happen
570 function insuredTypeCode($ins=0) {
571 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
572 return '12'; // medicare secondary working aged beneficiary or
573 // spouse with employer group health plan
574 return '';
577 // Is the patient also the subscriber?
579 function isSelfOfInsured($ins=0) {
580 $tmp = strtolower($this->payers[$ins]['data']['subscriber_relationship']);
581 return (strcmp($tmp,'self') == 0);
584 function planName($ins=0) {
585 return x12clean(trim($this->payers[$ins]['data']['plan_name']));
588 function policyNumber($ins=0) { // "ID"
589 return x12clean(trim($this->payers[$ins]['data']['policy_number']));
592 function groupNumber($ins=0) {
593 return x12clean(trim($this->payers[$ins]['data']['group_number']));
596 function groupName($ins=0) {
597 return x12clean(trim($this->payers[$ins]['data']['subscriber_employer']));
600 // Claim types are:
601 // 16 Other HCFA
602 // MB Medicare Part B
603 // MC Medicaid
604 // CH ChampUSVA
605 // CH ChampUS
606 // BL Blue Cross Blue Shield
607 // 16 FECA
608 // 09 Self Pay
609 // 10 Central Certification
610 // 11 Other Non-Federal Programs
611 // 12 Preferred Provider Organization (PPO)
612 // 13 Point of Service (POS)
613 // 14 Exclusive Provider Organization (EPO)
614 // 15 Indemnity Insurance
615 // 16 Health Maintenance Organization (HMO) Medicare Risk
616 // AM Automobile Medical
617 // CI Commercial Insurance Co.
618 // DS Disability
619 // HM Health Maintenance Organization
620 // LI Liability
621 // LM Liability Medical
622 // OF Other Federal Program
623 // TV Title V
624 // VA Veterans Administration Plan
625 // WC Workers Compensation Health Plan
626 // ZZ Mutually Defined
628 function claimType($ins=0) {
629 if (empty($this->payers[$ins]['object'])) return '';
630 return $this->payers[$ins]['object']->get_freeb_claim_type();
633 function insuredLastName($ins=0) {
634 return x12clean(trim($this->payers[$ins]['data']['subscriber_lname']));
637 function insuredFirstName($ins=0) {
638 return x12clean(trim($this->payers[$ins]['data']['subscriber_fname']));
641 function insuredMiddleName($ins=0) {
642 return x12clean(trim($this->payers[$ins]['data']['subscriber_mname']));
645 function insuredStreet($ins=0) {
646 return x12clean(trim($this->payers[$ins]['data']['subscriber_street']));
649 function insuredCity($ins=0) {
650 return x12clean(trim($this->payers[$ins]['data']['subscriber_city']));
653 function insuredState($ins=0) {
654 return x12clean(trim($this->payers[$ins]['data']['subscriber_state']));
657 function insuredZip($ins=0) {
658 return x12clean(trim($this->payers[$ins]['data']['subscriber_postal_code']));
661 function insuredPhone($ins=0) {
662 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
663 $this->payers[$ins]['data']['subscriber_phone'], $tmp))
664 return $tmp[1] . $tmp[2] . $tmp[3];
665 return '';
668 function insuredDOB($ins=0) {
669 return str_replace('-', '', $this->payers[$ins]['data']['subscriber_DOB']);
672 function insuredSex($ins=0) {
673 return strtoupper(substr($this->payers[$ins]['data']['subscriber_sex'], 0, 1));
676 function payerName($ins=0) {
677 return x12clean(trim($this->payers[$ins]['company']['name']));
680 function payerAttn($ins=0) {
681 return x12clean(trim($this->payers[$ins]['company']['attn']));
684 function payerStreet($ins=0) {
685 if (empty($this->payers[$ins]['object'])) return '';
686 $tmp = $this->payers[$ins]['object'];
687 $tmp = $tmp->get_address();
688 return x12clean(trim($tmp->get_line1()));
691 function payerCity($ins=0) {
692 if (empty($this->payers[$ins]['object'])) return '';
693 $tmp = $this->payers[$ins]['object'];
694 $tmp = $tmp->get_address();
695 return x12clean(trim($tmp->get_city()));
698 function payerState($ins=0) {
699 if (empty($this->payers[$ins]['object'])) return '';
700 $tmp = $this->payers[$ins]['object'];
701 $tmp = $tmp->get_address();
702 return x12clean(trim($tmp->get_state()));
705 function payerZip($ins=0) {
706 if (empty($this->payers[$ins]['object'])) return '';
707 $tmp = $this->payers[$ins]['object'];
708 $tmp = $tmp->get_address();
709 return x12clean(trim($tmp->get_zip()));
712 function payerID($ins=0) {
713 return x12clean(trim($this->payers[$ins]['company']['cms_id']));
716 function patientLastName() {
717 return x12clean(trim($this->patient_data['lname']));
720 function patientFirstName() {
721 return x12clean(trim($this->patient_data['fname']));
724 function patientMiddleName() {
725 return x12clean(trim($this->patient_data['mname']));
728 function patientStreet() {
729 return x12clean(trim($this->patient_data['street']));
732 function patientCity() {
733 return x12clean(trim($this->patient_data['city']));
736 function patientState() {
737 return x12clean(trim($this->patient_data['state']));
740 function patientZip() {
741 return x12clean(trim($this->patient_data['postal_code']));
744 function patientPhone() {
745 $ptphone = $this->patient_data['phone_home'];
746 if (!$ptphone) $ptphone = $this->patient_data['phone_biz'];
747 if (!$ptphone) $ptphone = $this->patient_data['phone_cell'];
748 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
749 return $tmp[1] . $tmp[2] . $tmp[3];
750 return '';
753 function patientDOB() {
754 return str_replace('-', '', $this->patient_data['DOB']);
757 function patientSex() {
758 return strtoupper(substr($this->patient_data['sex'], 0, 1));
761 // Patient Marital Status: M = Married, S = Single, or something else.
762 function patientStatus() {
763 return strtoupper(substr($this->patient_data['status'], 0, 1));
766 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
767 // indicate employed.
768 function patientOccupation() {
769 return strtoupper(x12clean(trim($this->patient_data['occupation'])));
772 function cptCode($prockey) {
773 return x12clean(trim($this->procs[$prockey]['code']));
776 function cptModifier($prockey) {
777 return x12clean(trim($this->procs[$prockey]['modifier']));
780 // Returns the procedure code, followed by ":modifier" if there is one.
781 function cptKey($prockey) {
782 $tmp = $this->cptModifier($prockey);
783 return $this->cptCode($prockey) . ($tmp ? ":$tmp" : "");
786 function cptCharges($prockey) {
787 return x12clean(trim($this->procs[$prockey]['fee']));
790 function cptUnits($prockey) {
791 if (empty($this->procs[$prockey]['units'])) return '1';
792 return x12clean(trim($this->procs[$prockey]['units']));
795 // NDC drug ID.
796 function cptNDCID($prockey) {
797 $ndcinfo = $this->procs[$prockey]['ndc_info'];
798 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
799 $ndc = $tmp[1];
800 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
801 return sprintf('%05d-%04d-%02d', $tmp[1], $tmp[2], $tmp[3]);
803 return x12clean($ndc); // format is bad but return it anyway
805 return '';
808 // NDC drug unit of measure code.
809 function cptNDCUOM($prockey) {
810 $ndcinfo = $this->procs[$prockey]['ndc_info'];
811 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
812 return x12clean($tmp[2]);
813 return '';
816 // NDC drug number of units.
817 function cptNDCQuantity($prockey) {
818 $ndcinfo = $this->procs[$prockey]['ndc_info'];
819 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
820 return x12clean(ltrim($tmp[3], '0'));
822 return '';
825 function onsetDate() {
826 return str_replace('-', '', substr($this->encounter['onset_date'], 0, 10));
829 function serviceDate() {
830 return str_replace('-', '', substr($this->encounter['date'], 0, 10));
833 function priorAuth() {
834 return x12clean(trim($this->billing_options['prior_auth_number']));
837 function isRelatedEmployment() {
838 return !empty($this->billing_options['employment_related']);
841 function isRelatedAuto() {
842 return !empty($this->billing_options['auto_accident']);
845 function isRelatedOther() {
846 return !empty($this->billing_options['other_accident']);
849 function autoAccidentState() {
850 return x12clean(trim($this->billing_options['accident_state']));
853 function isUnableToWork() {
854 return !empty($this->billing_options['is_unable_to_work']);
857 function offWorkFrom() {
858 return str_replace('-', '', substr($this->billing_options['off_work_from'], 0, 10));
861 function offWorkTo() {
862 return str_replace('-', '', substr($this->billing_options['off_work_to'], 0, 10));
865 function isHospitalized() {
866 return !empty($this->billing_options['is_hospitalized']);
869 function hospitalizedFrom() {
870 return str_replace('-', '', substr($this->billing_options['hospitalization_date_from'], 0, 10));
873 function hospitalizedTo() {
874 return str_replace('-', '', substr($this->billing_options['hospitalization_date_to'], 0, 10));
877 function isOutsideLab() {
878 return !empty($this->billing_options['outside_lab']);
881 function outsideLabAmount() {
882 return sprintf('%.2f', 0 + $this->billing_options['lab_amount']);
885 function medicaidResubmissionCode() {
886 return x12clean(trim($this->billing_options['medicaid_resubmission_code']));
889 function medicaidOriginalReference() {
890 return x12clean(trim($this->billing_options['medicaid_original_reference']));
893 function frequencyTypeCode() {
894 return empty($this->billing_options['replacement_claim']) ? '1' : '7';
897 function additionalNotes() {
898 return x12clean(trim($this->billing_options['comments']));
901 // Returns an array of unique diagnoses. Periods are stripped.
902 function diagArray() {
903 $da = array();
904 foreach ($this->procs as $row) {
905 $atmp = explode(':', $row['justify']);
906 foreach ($atmp as $tmp) {
907 if (!empty($tmp)) {
908 $diag = str_replace('.', '', $tmp);
909 $da[$diag] = $diag;
913 return $da;
916 // Compute one 1-relative index in diagArray for the given procedure.
917 // This function is obsolete, use diagIndexArray() instead.
918 function diagIndex($prockey) {
919 $da = $this->diagArray();
920 $tmp = explode(':', $this->procs[$prockey]['justify']);
921 if (empty($tmp)) return '';
922 $diag = str_replace('.', '', $tmp[0]);
923 $i = 0;
924 foreach ($da as $value) {
925 ++$i;
926 if (strcmp($value,$diag) == 0) return $i;
928 return '';
931 // Compute array of 1-relative diagArray indices for the given procedure.
932 function diagIndexArray($prockey) {
933 $dia = array();
934 $da = $this->diagArray();
935 $atmp = explode(':', $this->procs[$prockey]['justify']);
936 foreach ($atmp as $tmp) {
937 if (!empty($tmp)) {
938 $diag = str_replace('.', '', $tmp);
939 $i = 0;
940 foreach ($da as $value) {
941 ++$i;
942 if (strcmp($value,$diag) == 0) $dia[] = $i;
946 return $dia;
949 function providerLastName() {
950 return x12clean(trim($this->provider['lname']));
953 function providerFirstName() {
954 return x12clean(trim($this->provider['fname']));
957 function providerMiddleName() {
958 return x12clean(trim($this->provider['mname']));
961 function providerNPI() {
962 return x12clean(trim($this->provider['npi']));
965 function providerUPIN() {
966 return x12clean(trim($this->provider['upin']));
969 function providerSSN() {
970 return x12clean(trim(str_replace('-', '', $this->provider['federaltaxid'])));
973 function providerTaxonomy() {
974 if (empty($this->provider['taxonomy'])) return '207Q00000X';
975 return x12clean(trim($this->provider['taxonomy']));
978 function referrerLastName() {
979 return x12clean(trim($this->referrer['lname']));
982 function referrerFirstName() {
983 return x12clean(trim($this->referrer['fname']));
986 function referrerMiddleName() {
987 return x12clean(trim($this->referrer['mname']));
990 function referrerNPI() {
991 return x12clean(trim($this->referrer['npi']));
994 function referrerUPIN() {
995 return x12clean(trim($this->referrer['upin']));
998 function referrerSSN() {
999 return x12clean(trim(str_replace('-', '', $this->referrer['federaltaxid'])));
1002 function referrerTaxonomy() {
1003 if (empty($this->referrer['taxonomy'])) return '207Q00000X';
1004 return x12clean(trim($this->referrer['taxonomy']));