2 // Copyright (C) 2007-2008 Rod Roark <rod@sunsetsystems.com>
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));
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) {
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 provider != '' AND " .
50 "date <= '$encounter_date' " .
51 "ORDER BY type ASC, date DESC";
52 $dres = sqlStatement($query);
54 while ($drow = sqlFetchArray($dres)) {
55 if (strcmp($prevtype, $drow['type']) == 0) continue;
56 $prevtype = $drow['type'];
57 $ins = count($this->payers
);
58 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers
[0]['data'])) $ins = 0;
59 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
60 "id = '" . $drow['provider'] . "'");
61 $orow = new InsuranceCompany($drow['provider']);
62 $this->payers
[$ins] = array();
63 $this->payers
[$ins]['data'] = $drow;
64 $this->payers
[$ins]['company'] = $crow;
65 $this->payers
[$ins]['object'] = $orow;
68 // This kludge hands most cases of a rare ambiguous situation, where
69 // the primary insurance company is the same as the secondary. It seems
70 // nobody planned for that!
72 for ($i = 1; $i < count($this->payers
); ++
$i) {
73 if ($billrow['process_date'] &&
74 $this->payers
[0]['data']['provider'] == $this->payers
[$i]['data']['provider'])
76 $tmp = $this->payers
[0];
77 $this->payers
[0] = $this->payers
[$i];
78 $this->payers
[$i] = $tmp;
82 $this->using_modifiers
= true;
84 // Get payment and adjustment details if there are any previous payers.
86 $this->invoice
= array();
87 if ($this->payerSequence() != 'P') {
88 if ($GLOBALS['oer_config']['ws_accounting']['enabled']) {
90 $arres = SLQuery("select id from ar where invnumber = " .
91 "'{$this->pid}.{$this->encounter_id}'");
92 if ($sl_err) die($sl_err);
93 $arrow = SLGetRow($arres, 0);
95 $this->invoice
= get_invoice_summary($arrow['id'], true);
99 // Secondary claims might not have modifiers in SQL-Ledger data.
100 // In that case, note that we should not try to match on them.
101 $this->using_modifiers
= false;
102 foreach ($this->invoice
as $key => $trash) {
103 if (strpos($key, ':')) $this->using_modifiers
= true;
108 // Constructor. Loads relevant database information.
110 function Claim($pid, $encounter_id) {
112 $this->encounter_id
= $encounter_id;
113 $this->procs
= array();
116 // We need the encounter date before we can identify the payers.
117 $sql = "SELECT * FROM form_encounter WHERE " .
118 "pid = '{$this->pid}' AND " .
119 "encounter = '{$this->encounter_id}'";
120 $this->encounter
= sqlQuery($sql);
122 // Sort by procedure timestamp in order to get some consistency. In particular
123 // we determine the provider from the first procedure in this array.
124 $sql = "SELECT * FROM billing WHERE " .
125 "encounter = '{$this->encounter_id}' AND pid = '{$this->pid}' AND " .
126 "(code_type = 'CPT4' OR code_type = 'HCPCS' OR code_type = 'COPAY') AND " .
127 "activity = '1' ORDER BY date, id";
128 $res = sqlStatement($sql);
129 while ($row = sqlFetchArray($res)) {
130 if ($row['code_type'] == 'COPAY') {
131 $this->copay
-= $row['fee'];
134 if (!$row['units']) $row['units'] = 1;
135 // Load prior payer data at the first opportunity in order to get
136 // the using_modifiers flag that is referenced below.
137 if (empty($this->procs
)) $this->loadPayerInfo($row);
138 // Consolidate duplicate procedures.
139 foreach ($this->procs
as $key => $trash) {
140 if (strcmp($this->procs
[$key]['code'],$row['code']) == 0 &&
141 (strcmp($this->procs
[$key]['modifier'],$row['modifier']) == 0 ||
142 !$this->using_modifiers
))
144 $this->procs
[$key]['units'] +
= $row['units'];
145 $this->procs
[$key]['fee'] +
= $row['fee'];
146 continue 2; // skip to next table row
149 $this->procs
[] = $row;
152 $sql = "SELECT * FROM x12_partners WHERE " .
153 "id = '" . $this->procs
[0]['x12_partner_id'] . "'";
154 $this->x12_partner
= sqlQuery($sql);
156 $sql = "SELECT * FROM facility WHERE " .
157 "id = '" . addslashes($this->encounter
['facility_id']) . "' " .
159 $this->facility
= sqlQuery($sql);
161 $provider_id = $this->procs
[0]['provider_id'];
162 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
163 $this->provider
= sqlQuery($sql);
165 $sql = "SELECT * FROM facility " .
166 "ORDER BY billing_location DESC, id ASC LIMIT 1";
167 $this->billing_facility
= sqlQuery($sql);
169 $sql = "SELECT * FROM insurance_numbers WHERE " .
170 "(insurance_company_id = '" . $this->procs
[0]['payer_id'] .
171 "' OR insurance_company_id is NULL) AND " .
172 "provider_id = '" . $this->provider
['id'] .
173 "' order by insurance_company_id DESC LIMIT 1";
174 $this->insurance_numbers
= sqlQuery($sql);
176 $sql = "SELECT * FROM patient_data WHERE " .
177 "pid = '{$this->pid}' " .
178 "ORDER BY id LIMIT 1";
179 $this->patient_data
= sqlQuery($sql);
181 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
182 "ON fpa.id = forms.form_id WHERE " .
183 "forms.encounter = '{$this->encounter_id}' AND " .
184 "forms.pid = '{$this->pid}' AND " .
185 "forms.formdir = 'misc_billing_options' " .
186 "ORDER BY forms.date";
187 $this->billing_options
= sqlQuery($sql);
189 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
190 $this->insurance_numbers
['provider_number_type'] != '1C') ?
191 $this->patient_data
['providerID'] : $provider_id;
192 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
193 $this->referrer
= sqlQuery($sql);
194 if (!$this->referrer
) $this->referrer
= array();
198 // Return an array of adjustments from the designated prior payer for the
199 // designated procedure key (might be procedure:modifier), or for the claim
200 // level. For each adjustment give date, group code, reason code, amount.
201 // Note this will include "patient responsibility" adjustments which are
202 // not adjustments to OUR invoice, but they reduce the amount that the
203 // insurance company pays.
205 function payerAdjustments($ins, $code='Claim') {
208 // If we have no modifiers stored in SQL-Ledger for this claim,
209 // then we cannot use a modifier passed in with the key.
210 $tmp = strpos($code, ':');
211 if ($tmp && !$this->using_modifiers
) $code = substr($code, 0, $tmp);
213 // For payments, source always starts with "Ins" or "Pt".
214 // Nonzero adjustment reason examples:
215 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
216 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
217 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
218 // Ins1 adjust code A2 (Contractual adjustment)
221 // Zero adjustment reason examples:
223 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
224 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
225 // To copay Ins1 (manual entry)
226 // To ded'ble Ins1 (manual entry)
228 if (!empty($this->invoice
[$code])) {
232 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
233 $insnumber = substr($inslabel, 3);
235 // Compute this procedure's patient responsibility amount as of this
236 // prior payer, which is the original charge minus all insurance
237 // payments and "hard" adjustments up to this payer.
238 $ptresp = $this->invoice
[$code]['chg'] +
$this->invoice
[$code]['adj'];
239 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
240 if (preg_match("/^Ins(\d)/i", $value['src'], $tmp)) {
241 if ($tmp[1] <= $insnumber) $ptresp -= $value['pmt'];
243 else if (trim(substr($key, 0, 10))) { // not an adjustment if no date
244 if (!preg_match("/Ins(\d)/i", $value['rsn'], $tmp) ||
$tmp[1] <= $insnumber)
245 $ptresp +
= $value['chg']; // adjustments are negative charges
248 if ($ptresp < 0) $ptresp = 0; // we may be insane but try to hide it
250 // Main loop, to extract adjustments for this payer and procedure.
251 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
252 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
253 if ($tmp) $date = $tmp;
254 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
255 $rsn = $value['rsn'];
256 $chg = 0 - $value['chg']; // adjustments are negative charges
258 $gcode = 'CO'; // default group code = contractual obligation
259 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
261 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
262 // From manual post. Take the defaults.
264 else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
265 $coinsurance = $ptresp; // from manual post
268 else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
269 $deductible = $ptresp; // from manual post
272 else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
273 $coinsurance = $tmp[1]; // from 835 as of 6/2007
276 else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
277 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
280 else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
281 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
284 else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
285 continue; // from 835 as of 6/2007
287 else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
288 $rcode = $tmp[1]; // from 835
290 else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
291 // Take the defaults.
293 else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
294 continue; // it's for some other payer
296 else if ($insnumber == '1') {
297 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
298 $rcode = $tmp[1]; // from 835
301 // Other adjustments default to Ins1.
303 else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
304 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
305 $coinsurance = 0 +
$tmp[1]; // from 835 before 6/2007
308 else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
309 $deductible = 0 +
$tmp[1]; // from 835 before 6/2007
313 continue; // there is no adjustment amount
317 continue; // it's for primary and that's not us
320 if ($rcode == '42') $rcode= '45'; // reason 42 is obsolete
321 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
326 // If we really messed it up, at least avoid negative numbers.
327 if ($coinsurance > $ptresp) $coinsurance = $ptresp;
328 if ($deductible > $ptresp) $deductible = $ptresp;
330 // Find out if this payer paid anything at all on this claim. This will
331 // help us allocate any unknown patient responsibility amounts.
332 $thispaidanything = 0;
333 foreach($this->invoice
as $codekey => $codeval) {
334 foreach ($codeval['dtl'] as $key => $value) {
335 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
336 $thispaidanything +
= $value['pmt'];
341 // Allocate any unknown patient responsibility by guessing if the
342 // deductible has been satisfied.
343 if ($thispaidanything)
344 $coinsurance = $ptresp - $deductible;
346 $deductible = $ptresp - $coinsurance;
348 if ($date && $deductible != 0)
349 $aadj[] = array($date, 'PR', '1', sprintf('%.2f', $deductible));
350 if ($date && $coinsurance != 0)
351 $aadj[] = array($date, 'PR', '2', sprintf('%.2f', $coinsurance));
358 // Return date, total payments and total "hard" adjustments from the given
359 // prior payer. If $code is specified then only that procedure key is
360 // selected, otherwise it's for the whole claim.
362 function payerTotals($ins, $code='') {
363 // If we have no modifiers stored in SQL-Ledger for this claim,
364 // then we cannot use a modifier passed in with the key.
365 $tmp = strpos($code, ':');
366 if ($tmp && !$this->using_modifiers
) $code = substr($code, 0, $tmp);
368 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
372 foreach($this->invoice
as $codekey => $codeval) {
373 if ($code && strcmp($codekey,$code) != 0) continue;
374 foreach ($codeval['dtl'] as $key => $value) {
375 if (preg_match("/$inslabel/i", $value['src'], $tmp)) {
376 if (!$date) $date = str_replace('-', '', trim(substr($key, 0, 10)));
377 $paytotal +
= $value['pmt'];
380 $aarr = $this->payerAdjustments($ins, $codekey);
381 foreach ($aarr as $a) {
382 if (strcmp($a[1],'PR') != 0) $adjtotal +
= $a[3];
383 if (!$date) $date = $a[0];
386 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
389 // Return the amount already paid by the patient.
391 function patientPaidAmount() {
392 // For primary claims $this->invoice is not loaded, so get the co-pay
393 // from the billing table instead.
394 if (empty($this->invoice
)) return $this->copay
;
397 foreach($this->invoice
as $codekey => $codeval) {
398 foreach ($codeval['dtl'] as $key => $value) {
399 if (!preg_match("/Ins/i", $value['src'], $tmp)) {
400 $amount +
= $value['pmt'];
404 return sprintf('%.2f', $amount);
407 // Return invoice total, including adjustments but not payments.
409 function invoiceTotal() {
411 foreach($this->invoice
as $codekey => $codeval) {
412 $amount +
= $codeval['chg'];
414 return sprintf('%.2f', $amount);
417 // Number of procedures in this claim.
418 function procCount() {
419 return count($this->procs
);
422 // Number of payers for this claim. Ranges from 1 to 3.
423 function payerCount() {
424 return count($this->payers
);
427 function x12gsversionstring() {
428 return x12clean(trim($this->x12_partner
['x12_version']));
431 function x12gssenderid() {
432 $tmp = $this->x12_partner
['x12_sender_id'];
433 while (strlen($tmp) < 15) $tmp .= " ";
437 function x12gsreceiverid() {
438 $tmp = $this->x12_partner
['x12_receiver_id'];
439 while (strlen($tmp) < 15) $tmp .= " ";
443 function cliaCode() {
444 return x12clean(trim($this->facility
['domain_identifier']));
447 function billingFacilityName() {
448 return x12clean(trim($this->billing_facility
['name']));
451 function billingFacilityStreet() {
452 return x12clean(trim($this->billing_facility
['street']));
455 function billingFacilityCity() {
456 return x12clean(trim($this->billing_facility
['city']));
459 function billingFacilityState() {
460 return x12clean(trim($this->billing_facility
['state']));
463 function billingFacilityZip() {
464 return x12clean(trim($this->billing_facility
['postal_code']));
467 function billingFacilityETIN() {
468 return x12clean(trim(str_replace('-', '', $this->billing_facility
['federal_ein'])));
471 function billingFacilityNPI() {
472 return x12clean(trim($this->billing_facility
['facility_npi']));
475 function billingFacilityAssignment() {
476 return !empty($this->billing_facility
['accepts_assignment']);
479 function billingContactName() {
480 return x12clean(trim($this->billing_facility
['attn']));
483 function billingContactPhone() {
484 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
485 $this->billing_facility
['phone'], $tmp))
487 return $tmp[1] . $tmp[2] . $tmp[3];
492 function facilityName() {
493 return x12clean(trim($this->facility
['name']));
496 function facilityStreet() {
497 return x12clean(trim($this->facility
['street']));
500 function facilityCity() {
501 return x12clean(trim($this->facility
['city']));
504 function facilityState() {
505 return x12clean(trim($this->facility
['state']));
508 function facilityZip() {
509 return x12clean(trim($this->facility
['postal_code']));
512 function facilityETIN() {
513 return x12clean(trim(str_replace('-', '', $this->facility
['federal_ein'])));
516 function facilityNPI() {
517 return x12clean(trim($this->facility
['facility_npi']));
520 function facilityPOS() {
521 return x12clean(trim($this->facility
['pos_code']));
524 function clearingHouseName() {
525 return x12clean(trim($this->x12_partner
['name']));
528 function clearingHouseETIN() {
529 return x12clean(trim(str_replace('-', '', $this->x12_partner
['id_number'])));
532 function providerNumberType() {
533 return $this->insurance_numbers
['provider_number_type'];
536 function providerNumber() {
537 return x12clean(trim(str_replace('-', '', $this->insurance_numbers
['provider_number'])));
540 // Returns 'P', 'S' or 'T'.
542 function payerSequence($ins=0) {
543 return strtoupper(substr($this->payers
[$ins]['data']['type'], 0, 1));
546 // Returns the HIPAA code of the patient-to-subscriber relationship.
548 function insuredRelationship($ins=0) {
549 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
550 if (strcmp($tmp,'self' ) == 0) return '18';
551 if (strcmp($tmp,'spouse') == 0) return '01';
552 if (strcmp($tmp,'child' ) == 0) return '19';
553 if (strcmp($tmp,'other' ) == 0) return 'G8';
554 return $tmp; // should not happen
557 function insuredTypeCode($ins=0) {
558 if (strcmp($this->claimType($ins),'MB') == 0 && $this->payerSequence($ins) != 'P')
559 return '12'; // medicare secondary working aged beneficiary or
560 // spouse with employer group health plan
564 // Is the patient also the subscriber?
566 function isSelfOfInsured($ins=0) {
567 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
568 return (strcmp($tmp,'self') == 0);
571 function planName($ins=0) {
572 return x12clean(trim($this->payers
[$ins]['data']['plan_name']));
575 function policyNumber($ins=0) { // "ID"
576 return x12clean(trim($this->payers
[$ins]['data']['policy_number']));
579 function groupNumber($ins=0) {
580 return x12clean(trim($this->payers
[$ins]['data']['group_number']));
583 function groupName($ins=0) {
584 return x12clean(trim($this->payers
[$ins]['data']['subscriber_employer']));
589 // MB Medicare Part B
593 // BL Blue Cross Blue Shield
596 // 10 Central Certification
597 // 11 Other Non-Federal Programs
598 // 12 Preferred Provider Organization (PPO)
599 // 13 Point of Service (POS)
600 // 14 Exclusive Provider Organization (EPO)
601 // 15 Indemnity Insurance
602 // 16 Health Maintenance Organization (HMO) Medicare Risk
603 // AM Automobile Medical
604 // CI Commercial Insurance Co.
606 // HM Health Maintenance Organization
608 // LM Liability Medical
609 // OF Other Federal Program
611 // VA Veterans Administration Plan
612 // WC Workers Compensation Health Plan
613 // ZZ Mutually Defined
615 function claimType($ins=0) {
616 if (empty($this->payers
[$ins]['object'])) return '';
617 return $this->payers
[$ins]['object']->get_freeb_claim_type();
620 function insuredLastName($ins=0) {
621 return x12clean(trim($this->payers
[$ins]['data']['subscriber_lname']));
624 function insuredFirstName($ins=0) {
625 return x12clean(trim($this->payers
[$ins]['data']['subscriber_fname']));
628 function insuredMiddleName($ins=0) {
629 return x12clean(trim($this->payers
[$ins]['data']['subscriber_mname']));
632 function insuredStreet($ins=0) {
633 return x12clean(trim($this->payers
[$ins]['data']['subscriber_street']));
636 function insuredCity($ins=0) {
637 return x12clean(trim($this->payers
[$ins]['data']['subscriber_city']));
640 function insuredState($ins=0) {
641 return x12clean(trim($this->payers
[$ins]['data']['subscriber_state']));
644 function insuredZip($ins=0) {
645 return x12clean(trim($this->payers
[$ins]['data']['subscriber_postal_code']));
648 function insuredPhone($ins=0) {
649 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
650 $this->payers
[$ins]['data']['subscriber_phone'], $tmp))
651 return $tmp[1] . $tmp[2] . $tmp[3];
655 function insuredDOB($ins=0) {
656 return str_replace('-', '', $this->payers
[$ins]['data']['subscriber_DOB']);
659 function insuredSex($ins=0) {
660 return strtoupper(substr($this->payers
[$ins]['data']['subscriber_sex'], 0, 1));
663 function payerName($ins=0) {
664 return x12clean(trim($this->payers
[$ins]['company']['name']));
667 function payerAttn($ins=0) {
668 return x12clean(trim($this->payers
[$ins]['company']['attn']));
671 function payerStreet($ins=0) {
672 if (empty($this->payers
[$ins]['object'])) return '';
673 $tmp = $this->payers
[$ins]['object'];
674 $tmp = $tmp->get_address();
675 return x12clean(trim($tmp->get_line1()));
678 function payerCity($ins=0) {
679 if (empty($this->payers
[$ins]['object'])) return '';
680 $tmp = $this->payers
[$ins]['object'];
681 $tmp = $tmp->get_address();
682 return x12clean(trim($tmp->get_city()));
685 function payerState($ins=0) {
686 if (empty($this->payers
[$ins]['object'])) return '';
687 $tmp = $this->payers
[$ins]['object'];
688 $tmp = $tmp->get_address();
689 return x12clean(trim($tmp->get_state()));
692 function payerZip($ins=0) {
693 if (empty($this->payers
[$ins]['object'])) return '';
694 $tmp = $this->payers
[$ins]['object'];
695 $tmp = $tmp->get_address();
696 return x12clean(trim($tmp->get_zip()));
699 function payerID($ins=0) {
700 return x12clean(trim($this->payers
[$ins]['company']['cms_id']));
703 function patientLastName() {
704 return x12clean(trim($this->patient_data
['lname']));
707 function patientFirstName() {
708 return x12clean(trim($this->patient_data
['fname']));
711 function patientMiddleName() {
712 return x12clean(trim($this->patient_data
['mname']));
715 function patientStreet() {
716 return x12clean(trim($this->patient_data
['street']));
719 function patientCity() {
720 return x12clean(trim($this->patient_data
['city']));
723 function patientState() {
724 return x12clean(trim($this->patient_data
['state']));
727 function patientZip() {
728 return x12clean(trim($this->patient_data
['postal_code']));
731 function patientPhone() {
732 $ptphone = $this->patient_data
['phone_home'];
733 if (!$ptphone) $ptphone = $this->patient_data
['phone_biz'];
734 if (!$ptphone) $ptphone = $this->patient_data
['phone_cell'];
735 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp))
736 return $tmp[1] . $tmp[2] . $tmp[3];
740 function patientDOB() {
741 return str_replace('-', '', $this->patient_data
['DOB']);
744 function patientSex() {
745 return strtoupper(substr($this->patient_data
['sex'], 0, 1));
748 // Patient Marital Status: M = Married, S = Single, or something else.
749 function patientStatus() {
750 return strtoupper(substr($this->patient_data
['status'], 0, 1));
753 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
754 // indicate employed.
755 function patientOccupation() {
756 return strtoupper(x12clean(trim($this->patient_data
['occupation'])));
759 function cptCode($prockey) {
760 return x12clean(trim($this->procs
[$prockey]['code']));
763 function cptModifier($prockey) {
764 return x12clean(trim($this->procs
[$prockey]['modifier']));
767 // Returns the procedure code, followed by ":modifier" if there is one.
768 function cptKey($prockey) {
769 $tmp = $this->cptModifier($prockey);
770 return $this->cptCode($prockey) . ($tmp ?
":$tmp" : "");
773 function cptCharges($prockey) {
774 return x12clean(trim($this->procs
[$prockey]['fee']));
777 function cptUnits($prockey) {
778 if (empty($this->procs
[$prockey]['units'])) return '1';
779 return x12clean(trim($this->procs
[$prockey]['units']));
783 function cptNDCID($prockey) {
784 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
785 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
787 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
788 return sprintf('%05d-%04d-%02d', $tmp[1], $tmp[2], $tmp[3]);
790 return x12clean($ndc); // format is bad but return it anyway
795 // NDC drug unit of measure code.
796 function cptNDCUOM($prockey) {
797 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
798 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp))
799 return x12clean($tmp[2]);
803 // NDC drug number of units.
804 function cptNDCQuantity($prockey) {
805 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
806 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
807 return x12clean(ltrim($tmp[3], '0'));
812 function onsetDate() {
813 return str_replace('-', '', substr($this->encounter
['onset_date'], 0, 10));
816 function serviceDate() {
817 return str_replace('-', '', substr($this->encounter
['date'], 0, 10));
820 function priorAuth() {
821 return x12clean(trim($this->billing_options
['prior_auth_number']));
824 function isRelatedEmployment() {
825 return !empty($this->billing_options
['employment_related']);
828 function isRelatedAuto() {
829 return !empty($this->billing_options
['auto_accident']);
832 function isRelatedOther() {
833 return !empty($this->billing_options
['other_accident']);
836 function autoAccidentState() {
837 return x12clean(trim($this->billing_options
['accident_state']));
840 function isUnableToWork() {
841 return !empty($this->billing_options
['is_unable_to_work']);
844 function offWorkFrom() {
845 return str_replace('-', '', substr($this->billing_options
['off_work_from'], 0, 10));
848 function offWorkTo() {
849 return str_replace('-', '', substr($this->billing_options
['off_work_to'], 0, 10));
852 function isHospitalized() {
853 return !empty($this->billing_options
['is_hospitalized']);
856 function hospitalizedFrom() {
857 return str_replace('-', '', substr($this->billing_options
['hospitalization_date_from'], 0, 10));
860 function hospitalizedTo() {
861 return str_replace('-', '', substr($this->billing_options
['hospitalization_date_to'], 0, 10));
864 function isOutsideLab() {
865 return !empty($this->billing_options
['outside_lab']);
868 function outsideLabAmount() {
869 return sprintf('%.2f', 0 +
$this->billing_options
['lab_amount']);
872 function medicaidResubmissionCode() {
873 return x12clean(trim($this->billing_options
['medicaid_resubmission_code']));
876 function medicaidOriginalReference() {
877 return x12clean(trim($this->billing_options
['medicaid_original_reference']));
880 // Returns an array of unique diagnoses. Periods are stripped.
881 function diagArray() {
883 foreach ($this->procs
as $row) {
884 $atmp = explode(':', $row['justify']);
885 foreach ($atmp as $tmp) {
887 $diag = str_replace('.', '', $tmp);
895 // Compute one 1-relative index in diagArray for the given procedure.
896 // This function is obsolete, use diagIndexArray() instead.
897 function diagIndex($prockey) {
898 $da = $this->diagArray();
899 $tmp = explode(':', $this->procs
[$prockey]['justify']);
900 if (empty($tmp)) return '';
901 $diag = str_replace('.', '', $tmp[0]);
903 foreach ($da as $value) {
905 if (strcmp($value,$diag) == 0) return $i;
910 // Compute array of 1-relative diagArray indices for the given procedure.
911 function diagIndexArray($prockey) {
913 $da = $this->diagArray();
914 $atmp = explode(':', $this->procs
[$prockey]['justify']);
915 foreach ($atmp as $tmp) {
917 $diag = str_replace('.', '', $tmp);
919 foreach ($da as $value) {
921 if (strcmp($value,$diag) == 0) $dia[] = $i;
928 function providerLastName() {
929 return x12clean(trim($this->provider
['lname']));
932 function providerFirstName() {
933 return x12clean(trim($this->provider
['fname']));
936 function providerMiddleName() {
937 return x12clean(trim($this->provider
['mname']));
940 function providerNPI() {
941 return x12clean(trim($this->provider
['npi']));
944 function providerUPIN() {
945 return x12clean(trim($this->provider
['upin']));
948 function providerSSN() {
949 return x12clean(trim(str_replace('-', '', $this->provider
['federaltaxid'])));
952 function providerTaxonomy() {
953 if (empty($this->provider
['taxonomy'])) return '207Q00000X';
954 return x12clean(trim($this->provider
['taxonomy']));
957 function referrerLastName() {
958 return x12clean(trim($this->referrer
['lname']));
961 function referrerFirstName() {
962 return x12clean(trim($this->referrer
['fname']));
965 function referrerMiddleName() {
966 return x12clean(trim($this->referrer
['mname']));
969 function referrerNPI() {
970 return x12clean(trim($this->referrer
['npi']));
973 function referrerUPIN() {
974 return x12clean(trim($this->referrer
['upin']));
977 function referrerSSN() {
978 return x12clean(trim(str_replace('-', '', $this->referrer
['federaltaxid'])));
981 function referrerTaxonomy() {
982 if (empty($this->referrer
['taxonomy'])) return '207Q00000X';
983 return x12clean(trim($this->referrer
['taxonomy']));