3 * @link http://www.open-emr.org
4 * @author Rod Roark <rod@sunsetsystems.com>
5 * @copyright Copyright (c) 2009 Rod Roark <rod@sunsetsystems.com>
6 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
9 require_once(dirname(__FILE__
) . "/invoice_summary.inc.php");
11 // This enforces the X12 Basic Character Set. Page A2.
13 function x12clean($str)
15 return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?= ]/', '', strtoupper($str));
18 // Make sure dates have no formatting and zero filled becomes blank
19 // Handles date time stamp formats as well
21 function cleanDate($date_field)
23 $cleandate = str_replace('-', '', substr($date_field, 0, 10));
25 if (substr_count($cleandate, '0')==8) {
35 var $pid; // patient id
36 var $encounter_id; // encounter id
37 var $procs; // array of procedure rows from billing table
38 var $diags; // array of icd9 codes from billing table
39 var $diagtype= "ICD10"; // diagnosis code_type; safe to assume ICD10 now
40 var $x12_partner; // row from x12_partners table
41 var $encounter; // row from form_encounter table
42 var $facility; // row from facility table
43 var $billing_facility; // row from facility table
44 var $provider; // row from users table (rendering provider)
45 var $referrer; // row from users table (referring provider)
46 var $supervisor; // row from users table (supervising provider)
47 var $insurance_numbers; // row from insurance_numbers table for current payer
48 var $supervisor_numbers;// row from insurance_numbers table for current payer
49 var $patient_data; // row from patient_data table
50 var $billing_options; // row from form_misc_billing_options table
51 var $invoice; // result from get_invoice_summary()
52 var $payers; // array of arrays, for all payers
53 var $copay; // total of copays from the ar_activity table
56 function loadPayerInfo(&$billrow)
59 $encounter_date = substr($this->encounter
['date'], 0, 10);
61 // Create the $payers array. This contains data for all insurances
62 // with the current one always at index 0, and the others in payment
63 // order starting at index 1.
65 $this->payers
= array();
66 $this->payers
[0] = array();
67 $query = "SELECT * FROM insurance_data WHERE " .
68 "pid = '{$this->pid}' AND " .
69 "date <= '$encounter_date' " .
70 "ORDER BY type ASC, date DESC";
71 $dres = sqlStatement($query);
73 while ($drow = sqlFetchArray($dres)) {
74 if (strcmp($prevtype, $drow['type']) == 0) {
78 $prevtype = $drow['type'];
79 // Very important to look at entries with a missing provider because
80 // they indicate no insurance as of the given date.
81 if (empty($drow['provider'])) {
85 $ins = count($this->payers
);
86 if ($drow['provider'] == $billrow['payer_id'] && empty($this->payers
[0]['data'])) {
90 $crow = sqlQuery("SELECT * FROM insurance_companies WHERE " .
91 "id = '" . $drow['provider'] . "'");
92 $orow = new InsuranceCompany($drow['provider']);
93 $this->payers
[$ins] = array();
94 $this->payers
[$ins]['data'] = $drow;
95 $this->payers
[$ins]['company'] = $crow;
96 $this->payers
[$ins]['object'] = $orow;
99 // This kludge hands most cases of a rare ambiguous situation, where
100 // the primary insurance company is the same as the secondary. It seems
101 // nobody planned for that!
103 for ($i = 1; $i < count($this->payers
); ++
$i) {
104 if ($billrow['process_date'] &&
105 $this->payers
[0]['data']['provider'] == $this->payers
[$i]['data']['provider']) {
106 $tmp = $this->payers
[0];
107 $this->payers
[0] = $this->payers
[$i];
108 $this->payers
[$i] = $tmp;
112 $this->using_modifiers
= true;
114 // Get payment and adjustment details if there are any previous payers.
116 $this->invoice
= array();
117 if ($this->payerSequence() != 'P') {
118 $this->invoice
= ar_get_invoice_summary($this->pid
, $this->encounter_id
, true);
119 // Secondary claims might not have modifiers in SQL-Ledger data.
120 // In that case, note that we should not try to match on them.
121 $this->using_modifiers
= false;
122 foreach ($this->invoice
as $key => $trash) {
123 if (strpos($key, ':')) {
124 $this->using_modifiers
= true;
130 // Constructor. Loads relevant database information.
132 function __construct($pid, $encounter_id)
135 $this->encounter_id
= $encounter_id;
136 $this->procs
= array();
137 $this->diags
= array();
140 $this->facilityService
= new \services\
FacilityService();
142 // We need the encounter date before we can identify the payers.
143 $sql = "SELECT * FROM form_encounter WHERE " .
144 "pid = '{$this->pid}' AND " .
145 "encounter = '{$this->encounter_id}'";
146 $this->encounter
= sqlQuery($sql);
148 // Sort by procedure timestamp in order to get some consistency.
149 $sql = "SELECT b.id, b.date, b.code_type, b.code, b.pid, b.provider_id, " .
150 "b.user, b.groupname, b.authorized, b.encounter, b.code_text, b.billed, " .
151 "b.activity, b.payer_id, b.bill_process, b.bill_date, b.process_date, " .
152 "b.process_file, b.modifier, b.units, b.fee, b.justify, b.target, b.x12_partner_id, " .
153 "b.ndc_info, b.notecodes, ct.ct_diag " .
154 "FROM billing as b INNER JOIN code_types as ct " .
155 "ON b.code_type = ct.ct_key " .
156 "WHERE ct.ct_claim = '1' AND ct.ct_active = '1' AND " .
157 "b.encounter = '{$this->encounter_id}' AND b.pid = '{$this->pid}' AND " .
158 "b.activity = '1' ORDER BY b.date, b.id";
159 $res = sqlStatement($sql);
160 while ($row = sqlFetchArray($res)) {
161 // Save all diagnosis codes.
162 if ($row['ct_diag'] == '1') {
163 $this->diags
[$row['code']] = $row['code'];
167 if (!$row['units']) {
171 // Load prior payer data at the first opportunity in order to get
172 // the using_modifiers flag that is referenced below.
173 if (empty($this->procs
)) {
174 $this->loadPayerInfo($row);
177 // The consolidate duplicate procedures, which was previously here, was removed
178 // from codebase on 12/9/15. Reason: Some insurance companies decline consolidated
179 // procedures, and this can be left up to the billing coder when they input the items.
181 // If there is a row-specific provider then get its details.
182 if (!empty($row['provider_id'])) {
183 // Get service provider data for this row.
184 $sql = "SELECT * FROM users WHERE id = '" . $row['provider_id'] . "'";
185 $row['provider'] = sqlQuery($sql);
186 // Get insurance numbers for this row's provider.
187 $sql = "SELECT * FROM insurance_numbers WHERE " .
188 "(insurance_company_id = '" . $row['payer_id'] .
189 "' OR insurance_company_id is NULL) AND " .
190 "provider_id = '" . $row['provider_id'] . "' " .
191 "ORDER BY insurance_company_id DESC LIMIT 1";
192 $row['insurance_numbers'] = sqlQuery($sql);
195 $this->procs
[] = $row;
198 $resMoneyGot = sqlStatement("SELECT pay_amount as PatientPay,session_id as id,".
199 "date(post_time) as date FROM ar_activity where pid ='{$this->pid}' and encounter ='{$this->encounter_id}' ".
200 "and payer_type=0 and account_code='PCP'");
201 //new fees screen copay gives account_code='PCP'
202 while ($rowMoneyGot = sqlFetchArray($resMoneyGot)) {
203 $PatientPay=$rowMoneyGot['PatientPay']*-1;
204 $this->copay
-= $PatientPay;
207 $sql = "SELECT * FROM x12_partners WHERE " .
208 "id = '" . $this->procs
[0]['x12_partner_id'] . "'";
209 $this->x12_partner
= sqlQuery($sql);
211 $this->facility
= $this->facilityService
->getById($this->encounter
['facility_id']);
213 /*****************************************************************
214 $provider_id = $this->procs[0]['provider_id'];
215 *****************************************************************/
216 $provider_id = $this->encounter
['provider_id'];
217 $sql = "SELECT * FROM users WHERE id = '$provider_id'";
218 $this->provider
= sqlQuery($sql);
219 // Selecting the billing facility assigned to the encounter. If none,
220 // try the first (and hopefully only) facility marked as a billing location.
221 if (empty($this->encounter
['billing_facility'])) {
222 $this->billing_facility
= $this->facilityService
->getPrimaryBillingLocation();
224 $this->billing_facility
= $this->facilityService
->getById($this->encounter
['billing_facility']);
227 $sql = "SELECT * FROM insurance_numbers WHERE " .
228 "(insurance_company_id = '" . $this->procs
[0]['payer_id'] .
229 "' OR insurance_company_id is NULL) AND " .
230 "provider_id = '$provider_id' " .
231 "ORDER BY insurance_company_id DESC LIMIT 1";
232 $this->insurance_numbers
= sqlQuery($sql);
234 $sql = "SELECT * FROM patient_data WHERE " .
235 "pid = '{$this->pid}' " .
236 "ORDER BY id LIMIT 1";
237 $this->patient_data
= sqlQuery($sql);
239 $sql = "SELECT fpa.* FROM forms JOIN form_misc_billing_options AS fpa " .
240 "ON fpa.id = forms.form_id WHERE " .
241 "forms.encounter = '{$this->encounter_id}' AND " .
242 "forms.pid = '{$this->pid}' AND " .
243 "forms.deleted = 0 AND " .
244 "forms.formdir = 'misc_billing_options' " .
245 "ORDER BY forms.date";
246 $this->billing_options
= sqlQuery($sql);
248 $referrer_id = (empty($GLOBALS['MedicareReferrerIsRenderer']) ||
249 $this->insurance_numbers
['provider_number_type'] != '1C') ?
250 $this->patient_data
['ref_providerID'] : $provider_id;
251 $sql = "SELECT * FROM users WHERE id = '$referrer_id'";
252 $this->referrer
= sqlQuery($sql);
253 if (!$this->referrer
) {
254 $this->referrer
= array();
257 $supervisor_id = $this->encounter
['supervisor_id'];
258 $sql = "SELECT * FROM users WHERE id = '$supervisor_id'";
259 $this->supervisor
= sqlQuery($sql);
260 if (!$this->supervisor
) {
261 $this->supervisor
= array();
264 $billing_options_id = $this->billing_options
['provider_id'];
265 $sql = "SELECT * FROM users WHERE id = ?";
266 $this->billing_prov_id
= sqlQuery($sql, array($billing_options_id));
267 if (!$this->billing_prov_id
) {
268 $this->billing_prov_id
= 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
) {
278 $this->supervisor_numbers
= array();
282 // Return an array of adjustments from the designated prior payer for the
283 // designated procedure key (might be procedure:modifier), or for the claim
284 // level. For each adjustment give date, group code, reason code, amount.
285 // Note this will include "patient responsibility" adjustments which are
286 // not adjustments to OUR invoice, but they reduce the amount that the
287 // insurance company pays.
289 function payerAdjustments($ins, $code = 'Claim')
293 // If we have no modifiers stored in SQL-Ledger for this claim,
294 // then we cannot use a modifier passed in with the key.
295 $tmp = strpos($code, ':');
296 if ($tmp && !$this->using_modifiers
) {
297 $code = substr($code, 0, $tmp);
300 // For payments, source always starts with "Ins" or "Pt".
301 // Nonzero adjustment reason examples:
302 // Ins1 adjust code 42 (Charges exceed ... (obsolete))
303 // Ins1 adjust code 45 (Charges exceed your contracted/ legislated fee arrangement)
304 // Ins1 adjust code 97 (Payment is included in the allowance for another service/procedure)
305 // Ins1 adjust code A2 (Contractual adjustment)
308 // Zero adjustment reason examples:
310 // Coinsurance: 11.46 (code 2) Note: fix remits to identify insurance
311 // To deductible: 0.22 (code 1) Note: fix remits to identify insurance
312 // To copay Ins1 (manual entry)
313 // To ded'ble Ins1 (manual entry)
315 if (!empty($this->invoice
[$code])) {
319 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
320 $insnumber = substr($inslabel, 3);
322 // Compute this procedure's patient responsibility amount as of this
323 // prior payer, which is the original charge minus all insurance
324 // payments and "hard" adjustments up to this payer.
325 $ptresp = $this->invoice
[$code]['chg'] +
$this->invoice
[$code]['adj'];
326 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
327 // plv (from ar_activity.payer_type) exists to
328 // indicate the payer level.
329 if (isset($value['pmt']) && $value['pmt'] != 0) {
330 if ($value['plv'] > 0 && $value['plv'] <= $insnumber) {
331 $ptresp -= $value['pmt'];
333 } else if (isset($value['chg']) && trim(substr($key, 0, 10))) {
334 // non-blank key indicates this is an adjustment and not a charge
335 if ($value['plv'] > 0 && $value['plv'] <= $insnumber) {
336 $ptresp +
= $value['chg']; // adjustments are negative charges
340 $msp = isset($value['msp']) ?
$value['msp'] : null; // record the reason for adjustment
344 $ptresp = 0; // we may be insane but try to hide it
347 // Main loop, to extract adjustments for this payer and procedure.
348 foreach ($this->invoice
[$code]['dtl'] as $key => $value) {
349 $tmp = str_replace('-', '', trim(substr($key, 0, 10)));
354 if ($tmp && $value['pmt'] == 0) { // not original charge and not a payment
355 $rsn = $value['rsn'];
356 $chg = 0 - $value['chg']; // adjustments are negative charges
358 $gcode = 'CO'; // default group code = contractual obligation
359 $rcode = '45'; // default reason code = max fee exceeded (code 42 is obsolete)
361 if (preg_match("/Ins adjust $inslabel/i", $rsn, $tmp)) {
362 // From manual post. Take the defaults.
363 } else if (preg_match("/To copay $inslabel/i", $rsn, $tmp) && !$chg) {
364 $coinsurance = $ptresp; // from manual post
366 } else if (preg_match("/To ded'ble $inslabel/i", $rsn, $tmp) && !$chg) {
367 $deductible = $ptresp; // from manual post
369 } else if (preg_match("/$inslabel copay: (\S+)/i", $rsn, $tmp) && !$chg) {
370 $coinsurance = $tmp[1]; // from 835 as of 6/2007
372 } else if (preg_match("/$inslabel coins: (\S+)/i", $rsn, $tmp) && !$chg) {
373 $coinsurance = $tmp[1]; // from 835 and manual post as of 6/2007
375 } else if (preg_match("/$inslabel dedbl: (\S+)/i", $rsn, $tmp) && !$chg) {
376 $deductible = $tmp[1]; // from 835 and manual post as of 6/2007
378 } else if (preg_match("/$inslabel ptresp: (\S+)/i", $rsn, $tmp) && !$chg) {
379 continue; // from 835 as of 6/2007
380 } else if (preg_match("/$inslabel adjust code (\S+)/i", $rsn, $tmp)) {
381 $rcode = $tmp[1]; // from 835
382 } else if (preg_match("/$inslabel/i", $rsn, $tmp)) {
383 // Take the defaults.
384 } else if (preg_match('/Ins(\d)/i', $rsn, $tmp) && $tmp[1] != $insnumber) {
385 continue; // it's for some other payer
386 } else if ($insnumber == '1') {
387 if (preg_match("/\$\s*adjust code (\S+)/i", $rsn, $tmp)) {
388 $rcode = $tmp[1]; // from 835
390 // Other adjustments default to Ins1.
391 } else if (preg_match("/Co-pay: (\S+)/i", $rsn, $tmp) ||
392 preg_match("/Coinsurance: (\S+)/i", $rsn, $tmp)) {
393 $coinsurance = 0 +
$tmp[1]; // from 835 before 6/2007
395 } else if (preg_match("/To deductible: (\S+)/i", $rsn, $tmp)) {
396 $deductible = 0 +
$tmp[1]; // from 835 before 6/2007
399 continue; // there is no adjustment amount
402 continue; // it's for primary and that's not us
405 if ($rcode == '42') {
406 $rcode= '45'; // reason 42 is obsolete
409 $aadj[] = array($date, $gcode, $rcode, sprintf('%.2f', $chg));
413 // If we really messed it up, at least avoid negative numbers.
414 if ($coinsurance > $ptresp) {
415 $coinsurance = $ptresp;
418 if ($deductible > $ptresp) {
419 $deductible = $ptresp;
422 // Find out if this payer paid anything at all on this claim. This will
423 // help us allocate any unknown patient responsibility amounts.
424 $thispaidanything = 0;
425 foreach ($this->invoice
as $codekey => $codeval) {
426 foreach ($codeval['dtl'] as $key => $value) {
427 // plv exists to indicate the payer level.
428 if ($value['plv'] == $insnumber) {
429 $thispaidanything +
= $value['pmt'];
434 // Allocate any unknown patient responsibility by guessing if the
435 // deductible has been satisfied.
436 if ($thispaidanything) {
437 $coinsurance = $ptresp - $deductible;
439 $deductible = $ptresp - $coinsurance;
442 $deductible = sprintf('%.2f', $deductible);
443 $coinsurance = sprintf('%.2f', $coinsurance);
445 if ($date && $deductible != 0) {
446 $aadj[] = array($date, 'PR', '1', $deductible, $msp);
449 if ($date && $coinsurance != 0) {
450 $aadj[] = array($date, 'PR', '2', $coinsurance, $msp);
457 // Return date, total payments and total "hard" adjustments from the given
458 // prior payer. If $code is specified then only that procedure key is
459 // selected, otherwise it's for the whole claim.
461 function payerTotals($ins, $code = '')
463 // If we have no modifiers stored in SQL-Ledger for this claim,
464 // then we cannot use a modifier passed in with the key.
465 $tmp = strpos($code, ':');
466 if ($tmp && !$this->using_modifiers
) {
467 $code = substr($code, 0, $tmp);
470 $inslabel = ($this->payerSequence($ins) == 'S') ?
'Ins2' : 'Ins1';
471 $insnumber = substr($inslabel, 3);
475 foreach ($this->invoice
as $codekey => $codeval) {
476 if ($code && strcmp($codekey, $code) != 0) {
480 foreach ($codeval['dtl'] as $key => $value) {
481 // plv (from ar_activity.payer_type) exists to
482 // indicate the payer level.
483 if ($value['plv'] == $insnumber) {
485 $date = str_replace('-', '', trim(substr($key, 0, 10)));
488 $paytotal +
= $value['pmt'];
492 $aarr = $this->payerAdjustments($ins, $codekey);
493 foreach ($aarr as $a) {
494 if (strcmp($a[1], 'PR') != 0) {
504 return array($date, sprintf('%.2f', $paytotal), sprintf('%.2f', $adjtotal));
507 // Return the amount already paid by the patient.
509 function patientPaidAmount()
511 // For primary claims $this->invoice is not loaded, so get the co-pay
512 // from the ar_activity table instead.
513 if (empty($this->invoice
)) {
519 foreach ($this->invoice
as $codekey => $codeval) {
520 foreach ($codeval['dtl'] as $key => $value) {
521 // plv exists to indicate the payer level.
522 if ($value['plv'] == 0) { // 0 indicates patient
523 $amount +
= $value['pmt'];
528 return sprintf('%.2f', $amount);
531 // Return invoice total, including adjustments but not payments.
533 function invoiceTotal()
536 foreach ($this->invoice
as $codekey => $codeval) {
537 $amount +
= $codeval['chg'];
540 return sprintf('%.2f', $amount);
543 // Number of procedures in this claim.
546 return count($this->procs
);
549 // Number of payers for this claim. Ranges from 1 to 3.
550 function payerCount()
552 return count($this->payers
);
555 function x12gsversionstring()
557 return x12clean(trim($this->x12_partner
['x12_version']));
560 function x12gssenderid()
562 $tmp = $this->x12_partner
['x12_sender_id'];
563 while (strlen($tmp) < 15) {
573 * GS03: Application Receiver's Code
574 * Code Identifying Party Receiving Transmission
576 * In most cases, the ISA08 and GS03 are the same. However
578 * In some clearing houses ISA08 and GS03 are different
579 * Example: http://www.acs-gcro.com/downloads/DOL/DOL_CG_X12N_5010_837_v1_02.pdf - Page 18
580 * In this .pdf, the ISA08 is specified to be 100000 while the GS03 is specified to be 77044
582 * Therefore if the x12_gs03 segement is explicitly specified we use that value,
583 * otherwise we simply use the same receiver ID as specified for ISA03
585 if ($this->x12_partner
['x12_gs03'] !== '') {
586 return $this->x12_partner
['x12_gs03'];
588 return $this->x12_partner
['x12_receiver_id'];
592 function x12gsreceiverid()
594 $tmp = $this->x12_partner
['x12_receiver_id'];
595 while (strlen($tmp) < 15) {
602 function x12gsisa05()
604 return $this->x12_partner
['x12_isa05'];
606 //adding in functions for isa 01 - isa 04
608 function x12gsisa01()
610 return $this->x12_partner
['x12_isa01'];
613 function x12gsisa02()
615 return $this->x12_partner
['x12_isa02'];
618 function x12gsisa03()
620 return $this->x12_partner
['x12_isa03'];
622 function x12gsisa04()
624 return $this->x12_partner
['x12_isa04'];
628 function x12gsisa07()
630 return $this->x12_partner
['x12_isa07'];
633 function x12gsisa14()
635 return $this->x12_partner
['x12_isa14'];
638 function x12gsisa15()
640 return $this->x12_partner
['x12_isa15'];
645 $tmp = $this->x12_partner
['x12_gs02'];
647 $tmp = $this->x12_partner
['x12_sender_id'];
653 function x12gsper06()
655 return $this->x12_partner
['x12_per06'];
660 return x12clean(trim($this->facility
['domain_identifier']));
663 function billingFacilityName()
665 return x12clean(trim($this->billing_facility
['name']));
668 function billingFacilityStreet()
670 return x12clean(trim($this->billing_facility
['street']));
673 function billingFacilityCity()
675 return x12clean(trim($this->billing_facility
['city']));
678 function billingFacilityState()
680 return x12clean(trim($this->billing_facility
['state']));
683 function billingFacilityZip()
685 return x12clean(trim($this->billing_facility
['postal_code']));
688 function billingFacilityETIN()
690 return x12clean(trim(str_replace('-', '', $this->billing_facility
['federal_ein'])));
693 function billingFacilityNPI()
695 return x12clean(trim($this->billing_facility
['facility_npi']));
698 function federalIdType()
700 if ($this->billing_facility
['tax_id_type']) {
701 return $this->billing_facility
['tax_id_type'];
707 # The billing facility and the patient must both accept for this to return true.
708 function billingFacilityAssignment($ins = 0)
710 $tmp = strtoupper($this->payers
[$ins]['data']['accept_assignment']);
711 if (strcmp($tmp, 'FALSE') == 0) {
715 return !empty($this->billing_facility
['accepts_assignment']);
718 function billingContactName()
720 return x12clean(trim($this->billing_facility
['attn']));
723 function billingContactPhone()
726 "/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
727 $this->billing_facility
['phone'],
730 return $tmp[1] . $tmp[2] . $tmp[3];
736 function facilityName()
738 return x12clean(trim($this->facility
['name']));
741 function facilityStreet()
743 return x12clean(trim($this->facility
['street']));
746 function facilityCity()
748 return x12clean(trim($this->facility
['city']));
751 function facilityState()
753 return x12clean(trim($this->facility
['state']));
756 function facilityZip()
758 return x12clean(trim($this->facility
['postal_code']));
761 function facilityETIN()
763 return x12clean(trim(str_replace('-', '', $this->facility
['federal_ein'])));
766 function facilityNPI()
768 return x12clean(trim($this->facility
['facility_npi']));
771 function facilityPOS()
773 if ($this->encounter
['pos_code']) {
774 return sprintf('%02d', trim($this->encounter
['pos_code']));
776 return sprintf('%02d', trim($this->facility
['pos_code']));
780 function clearingHouseName()
782 return x12clean(trim($this->x12_partner
['name']));
785 function clearingHouseETIN()
787 return x12clean(trim(str_replace('-', '', $this->x12_partner
['id_number'])));
790 function providerNumberType($prockey = -1)
792 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
793 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
794 return $tmp['provider_number_type'];
797 function providerNumber($prockey = -1)
799 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
800 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
801 return x12clean(trim(str_replace('-', '', $tmp['provider_number'])));
804 function providerGroupNumber($prockey = -1)
806 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
807 $this->insurance_numbers
: $this->procs
[$prockey]['insurance_numbers'];
808 return x12clean(trim(str_replace('-', '', $tmp['group_number'])));
811 // Returns 'P', 'S' or 'T'.
813 function payerSequence($ins = 0)
815 return strtoupper(substr($this->payers
[$ins]['data']['type'], 0, 1));
818 // Returns the HIPAA code of the patient-to-subscriber relationship.
820 function insuredRelationship($ins = 0)
822 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
823 if (strcmp($tmp, 'self') == 0) {
827 if (strcmp($tmp, 'spouse') == 0) {
831 if (strcmp($tmp, 'child') == 0) {
835 if (strcmp($tmp, 'other') == 0) {
839 return $tmp; // should not happen
842 function insuredTypeCode($ins = 0)
844 if (strcmp($this->claimType($ins), 'MB') == 0 && $this->payerSequence($ins) != 'P') {
845 return $this->payers
[$ins]['data']['policy_type'];
851 // Is the patient also the subscriber?
853 function isSelfOfInsured($ins = 0)
855 $tmp = strtolower($this->payers
[$ins]['data']['subscriber_relationship']);
856 return (strcmp($tmp, 'self') == 0);
859 function planName($ins = 0)
861 return x12clean(trim($this->payers
[$ins]['data']['plan_name']));
864 function policyNumber($ins = 0)
867 return x12clean(trim($this->payers
[$ins]['data']['policy_number']));
870 function groupNumber($ins = 0)
872 return x12clean(trim($this->payers
[$ins]['data']['group_number']));
875 function groupName($ins = 0)
877 return x12clean(trim($this->payers
[$ins]['data']['subscriber_employer']));
882 // MB Medicare Part B
886 // BL Blue Cross Blue Shield
889 // 10 Central Certification
890 // 11 Other Non-Federal Programs
891 // 12 Preferred Provider Organization (PPO)
892 // 13 Point of Service (POS)
893 // 14 Exclusive Provider Organization (EPO)
894 // 15 Indemnity Insurance
895 // 16 Health Maintenance Organization (HMO) Medicare Risk
896 // AM Automobile Medical
897 // CI Commercial Insurance Co.
899 // HM Health Maintenance Organization
901 // LM Liability Medical
902 // OF Other Federal Program
904 // VA Veterans Administration Plan
905 // WC Workers Compensation Health Plan
906 // ZZ Mutually Defined
908 function claimType($ins = 0)
910 if (empty($this->payers
[$ins]['object'])) {
914 return $this->payers
[$ins]['object']->get_ins_claim_type();
917 function claimTypeRaw($ins = 0)
919 if (empty($this->payers
[$ins]['object'])) {
923 return $this->payers
[$ins]['object']->get_ins_type_code();
926 function insuredLastName($ins = 0)
928 return x12clean(trim($this->payers
[$ins]['data']['subscriber_lname']));
931 function insuredFirstName($ins = 0)
933 return x12clean(trim($this->payers
[$ins]['data']['subscriber_fname']));
936 function insuredMiddleName($ins = 0)
938 return x12clean(trim($this->payers
[$ins]['data']['subscriber_mname']));
941 function insuredStreet($ins = 0)
943 return x12clean(trim($this->payers
[$ins]['data']['subscriber_street']));
946 function insuredCity($ins = 0)
948 return x12clean(trim($this->payers
[$ins]['data']['subscriber_city']));
951 function insuredState($ins = 0)
953 return x12clean(trim($this->payers
[$ins]['data']['subscriber_state']));
956 function insuredZip($ins = 0)
958 return x12clean(trim($this->payers
[$ins]['data']['subscriber_postal_code']));
961 function insuredPhone($ins = 0)
964 "/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/",
965 $this->payers
[$ins]['data']['subscriber_phone'],
968 return $tmp[1] . $tmp[2] . $tmp[3];
974 function insuredDOB($ins = 0)
976 return str_replace('-', '', $this->payers
[$ins]['data']['subscriber_DOB']);
979 function insuredSex($ins = 0)
981 return strtoupper(substr($this->payers
[$ins]['data']['subscriber_sex'], 0, 1));
984 function payerName($ins = 0)
986 return x12clean(trim($this->payers
[$ins]['company']['name']));
989 function payerAttn($ins = 0)
991 return x12clean(trim($this->payers
[$ins]['company']['attn']));
994 function payerStreet($ins = 0)
996 if (empty($this->payers
[$ins]['object'])) {
1000 $tmp = $this->payers
[$ins]['object'];
1001 $tmp = $tmp->get_address();
1002 return x12clean(trim($tmp->get_line1()));
1005 function payerCity($ins = 0)
1007 if (empty($this->payers
[$ins]['object'])) {
1011 $tmp = $this->payers
[$ins]['object'];
1012 $tmp = $tmp->get_address();
1013 return x12clean(trim($tmp->get_city()));
1016 function payerState($ins = 0)
1018 if (empty($this->payers
[$ins]['object'])) {
1022 $tmp = $this->payers
[$ins]['object'];
1023 $tmp = $tmp->get_address();
1024 return x12clean(trim($tmp->get_state()));
1027 function payerZip($ins = 0)
1029 if (empty($this->payers
[$ins]['object'])) {
1033 $tmp = $this->payers
[$ins]['object'];
1034 $tmp = $tmp->get_address();
1035 return x12clean(trim($tmp->get_zip()));
1038 function payerID($ins = 0)
1040 return x12clean(trim($this->payers
[$ins]['company']['cms_id']));
1043 function payerAltID($ins = 0)
1045 return x12clean(trim($this->payers
[$ins]['company']['alt_cms_id']));
1048 function patientLastName()
1050 return x12clean(trim($this->patient_data
['lname']));
1053 function patientFirstName()
1055 return x12clean(trim($this->patient_data
['fname']));
1058 function patientMiddleName()
1060 return x12clean(trim($this->patient_data
['mname']));
1063 function patientStreet()
1065 return x12clean(trim($this->patient_data
['street']));
1068 function patientCity()
1070 return x12clean(trim($this->patient_data
['city']));
1073 function patientState()
1075 return x12clean(trim($this->patient_data
['state']));
1078 function patientZip()
1080 return x12clean(trim($this->patient_data
['postal_code']));
1083 function patientPhone()
1085 $ptphone = $this->patient_data
['phone_home'];
1087 $ptphone = $this->patient_data
['phone_biz'];
1091 $ptphone = $this->patient_data
['phone_cell'];
1094 if (preg_match("/([2-9]\d\d)\D*(\d\d\d)\D*(\d\d\d\d)/", $ptphone, $tmp)) {
1095 return $tmp[1] . $tmp[2] . $tmp[3];
1101 function patientDOB()
1103 return str_replace('-', '', $this->patient_data
['DOB']);
1106 function patientSex()
1108 return strtoupper(substr($this->patient_data
['sex'], 0, 1));
1111 // Patient Marital Status: M = Married, S = Single, or something else.
1112 function patientStatus()
1114 return strtoupper(substr($this->patient_data
['status'], 0, 1));
1117 // This should be UNEMPLOYED, STUDENT, PT STUDENT, or anything else to
1118 // indicate employed.
1119 function patientOccupation()
1121 return strtoupper(x12clean(trim($this->patient_data
['occupation'])));
1124 function cptCode($prockey)
1126 return x12clean(trim($this->procs
[$prockey]['code']));
1129 function cptModifier($prockey)
1131 // Split on the colon or space and clean each modifier
1133 $cln_mods = array();
1134 $mods = preg_split("/[: ]/", trim($this->procs
[$prockey]['modifier']));
1135 foreach ($mods as $mod) {
1136 array_push($cln_mods, x12clean($mod));
1139 return (implode(':', $cln_mods));
1142 function cptNotecodes($prockey)
1144 return x12clean(trim($this->procs
[$prockey]['notecodes']));
1147 // Returns the procedure code, followed by ":modifier" if there is one.
1148 function cptKey($prockey)
1150 $tmp = $this->cptModifier($prockey);
1151 return $this->cptCode($prockey) . ($tmp ?
":$tmp" : "");
1154 function cptCharges($prockey)
1156 return x12clean(trim($this->procs
[$prockey]['fee']));
1159 function cptUnits($prockey)
1161 if (empty($this->procs
[$prockey]['units'])) {
1165 return x12clean(trim($this->procs
[$prockey]['units']));
1169 function cptNDCID($prockey)
1171 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
1172 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1174 if (preg_match('/^(\d+)-(\d+)-(\d+)$/', $ndc, $tmp)) {
1175 return sprintf('%05d%04d%02d', $tmp[1], $tmp[2], $tmp[3]);
1178 return x12clean($ndc); // format is bad but return it anyway
1184 // NDC drug unit of measure code.
1185 function cptNDCUOM($prockey)
1187 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
1188 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1189 return x12clean($tmp[2]);
1195 // NDC drug number of units.
1196 function cptNDCQuantity($prockey)
1198 $ndcinfo = $this->procs
[$prockey]['ndc_info'];
1199 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndcinfo, $tmp)) {
1200 return x12clean(ltrim($tmp[3], '0'));
1206 function onsetDate()
1208 return cleanDate($this->encounter
['onset_date']);
1211 function onsetDateValid()
1213 return $this->onsetDate()!=='';
1216 function serviceDate()
1218 return str_replace('-', '', substr($this->encounter
['date'], 0, 10));
1221 function priorAuth()
1223 return x12clean(trim($this->billing_options
['prior_auth_number']));
1226 function isRelatedEmployment()
1228 return !empty($this->billing_options
['employment_related']);
1231 function isRelatedAuto()
1233 return !empty($this->billing_options
['auto_accident']);
1236 function isRelatedOther()
1238 return !empty($this->billing_options
['other_accident']);
1241 function autoAccidentState()
1243 return x12clean(trim($this->billing_options
['accident_state']));
1246 function isUnableToWork()
1248 return !empty($this->billing_options
['is_unable_to_work']);
1251 function offWorkFrom()
1253 return cleanDate($this->billing_options
['off_work_from']);
1256 function offWorkTo()
1258 return cleanDate($this->billing_options
['off_work_to']);
1261 function isHospitalized()
1263 return !empty($this->billing_options
['is_hospitalized']);
1266 function hospitalizedFrom()
1268 return cleanDate($this->billing_options
['hospitalization_date_from']);
1271 function hospitalizedFromDateValid()
1273 return $this->hospitalizedFrom()!=='';
1276 function hospitalizedTo()
1278 return cleanDate($this->billing_options
['hospitalization_date_to']);
1280 function hospitalizedToDateValid()
1282 return $this->hospitalizedTo()!=='';
1285 function isOutsideLab()
1287 return !empty($this->billing_options
['outside_lab']);
1290 function outsideLabAmount()
1292 return sprintf('%.2f', 0 +
$this->billing_options
['lab_amount']);
1295 function medicaidReferralCode()
1297 return x12clean(trim($this->billing_options
['medicaid_referral_code']));
1300 function epsdtFlag()
1302 return x12clean(trim($this->billing_options
['epsdt_flag']));
1305 function medicaidResubmissionCode()
1307 return x12clean(trim($this->billing_options
['medicaid_resubmission_code']));
1310 function medicaidOriginalReference()
1312 return x12clean(trim($this->billing_options
['medicaid_original_reference']));
1315 function frequencyTypeCode()
1317 return ($this->billing_options
['replacement_claim'] == 1) ?
'7' : '1';
1320 function icnResubmissionNumber()
1322 return x12clean($this->billing_options
['icn_resubmission_number']);
1325 function additionalNotes()
1327 return x12clean(trim($this->billing_options
['comments']));
1330 function miscOnsetDate()
1332 return cleanDate($this->billing_options
['onset_date']);
1335 function miscOnsetDateValid()
1337 return $this->miscOnsetDate()!=='';
1340 function dateInitialTreatment()
1342 return cleanDate($this->billing_options
['date_initial_treatment']);
1345 function dateInitialTreatmentValid()
1347 return $this->dateInitialTreatment()!=='';
1350 function box14Qualifier()
1352 // If no box qualifier specified use "431" indicating Onset
1353 return empty($this->billing_options
['box_14_date_qual']) ?
'431' :
1354 $this->billing_options
['box_14_date_qual'];
1357 function box15Qualifier()
1359 // If no box qualifier specified use "454" indicating Initial Treatment
1360 return empty($this->billing_options
['box_15_date_qual']) ?
'454' :
1361 $this->billing_options
['box_15_date_qual'];
1363 // Returns an array of unique diagnoses. Periods are stripped by default
1364 // Option to keep periods is to support HCFA 1500 02/12 version
1365 function diagArray($strip_periods = true)
1368 foreach ($this->procs
as $row) {
1369 $atmp = explode(':', $row['justify']);
1370 foreach ($atmp as $tmp) {
1372 $code_data = explode('|', $tmp);
1374 // If there was a | in the code data, the the first part of the array is the type, and the second is the identifier
1375 if (!empty($code_data[1])) {
1376 // This is the simplest way to determine if the claim is using ICD9 or ICD10 codes
1377 // a mix of code types is generally not allowed as there is only one specifier for all diagnoses on HCFA-1500 form
1378 // and there would be ambiguity with E and V codes
1379 $this->diagtype
=$code_data[0];
1381 //code is in the second part of the $code_data array.
1382 if ($strip_periods==true) {
1383 $diag = str_replace('.', '', $code_data[1]);
1385 $diag=$code_data[1];
1388 //No prepended code type label
1389 if ($strip_periods) {
1390 $diag = str_replace('.', '', $code_data[0]);
1392 $diag=$code_data[0];
1401 // The above got all the diagnoses used for justification, in the order
1402 // used for justification. Next we go through all diagnoses, justified
1403 // or not, to make sure they all get into the claim. We do it this way
1404 // so that the more important diagnoses appear first.
1405 foreach ($this->diags
as $diag) {
1406 if ($strip_periods) {
1407 $diag = str_replace('.', '', $diag);
1416 // Compute one 1-relative index in diagArray for the given procedure.
1417 // This function is obsolete, use diagIndexArray() instead.
1418 function diagIndex($prockey)
1420 $da = $this->diagArray();
1421 $tmp = explode(':', $this->procs
[$prockey]['justify']);
1426 $diag = str_replace('.', '', $tmp[0]);
1428 foreach ($da as $value) {
1430 if (strcmp($value, $diag) == 0) {
1438 // Compute array of 1-relative diagArray indices for the given procedure.
1439 function diagIndexArray($prockey)
1442 $da = $this->diagArray();
1443 $atmp = explode(':', $this->procs
[$prockey]['justify']);
1444 foreach ($atmp as $tmp) {
1446 $code_data = explode('|', $tmp);
1447 if (!empty($code_data[1])) {
1448 //Strip the prepended code type label
1449 $diag = str_replace('.', '', $code_data[1]);
1451 //No prepended code type label
1452 $diag = str_replace('.', '', $code_data[0]);
1456 foreach ($da as $value) {
1458 if (strcmp($value, $diag) == 0) {
1468 function providerLastName($prockey = -1)
1470 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1471 $this->provider
: $this->procs
[$prockey]['provider'];
1472 return x12clean(trim($tmp['lname']));
1475 function providerFirstName($prockey = -1)
1477 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1478 $this->provider
: $this->procs
[$prockey]['provider'];
1479 return x12clean(trim($tmp['fname']));
1482 function providerMiddleName($prockey = -1)
1484 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1485 $this->provider
: $this->procs
[$prockey]['provider'];
1486 return x12clean(trim($tmp['mname']));
1489 function providerNPI($prockey = -1)
1491 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1492 $this->provider
: $this->procs
[$prockey]['provider'];
1493 return x12clean(trim($tmp['npi']));
1496 function NPIValid($npi)
1498 // A NPI MUST be a 10 digit number
1503 if (strlen($npi)!=10) {
1507 if (!preg_match("/[0-9]*/", $npi)) {
1513 function providerNPIValid($prockey = -1)
1515 return $this->NPIValid($this->providerNPI($prockey));
1518 function providerUPIN($prockey = -1)
1520 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1521 $this->provider
: $this->procs
[$prockey]['provider'];
1522 return x12clean(trim($tmp['upin']));
1525 function providerSSN($prockey = -1)
1527 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1528 $this->provider
: $this->procs
[$prockey]['provider'];
1529 return x12clean(trim(str_replace('-', '', $tmp['federaltaxid'])));
1532 function providerTaxonomy($prockey = -1)
1534 $tmp = ($prockey < 0 ||
empty($this->procs
[$prockey]['provider_id'])) ?
1535 $this->provider
: $this->procs
[$prockey]['provider'];
1536 if (empty($tmp['taxonomy'])) {
1537 return '207Q00000X';
1540 return x12clean(trim($tmp['taxonomy']));
1543 function referrerLastName()
1545 return x12clean(trim($this->referrer
['lname']));
1548 function referrerFirstName()
1550 return x12clean(trim($this->referrer
['fname']));
1553 function referrerMiddleName()
1555 return x12clean(trim($this->referrer
['mname']));
1558 function referrerNPI()
1560 return x12clean(trim($this->referrer
['npi']));
1563 function referrerUPIN()
1565 return x12clean(trim($this->referrer
['upin']));
1568 function referrerSSN()
1570 return x12clean(trim(str_replace('-', '', $this->referrer
['federaltaxid'])));
1573 function referrerTaxonomy()
1575 if (empty($this->referrer
['taxonomy'])) {
1576 return '207Q00000X';
1579 return x12clean(trim($this->referrer
['taxonomy']));
1582 function supervisorLastName()
1584 return x12clean(trim($this->supervisor
['lname']));
1587 function supervisorFirstName()
1589 return x12clean(trim($this->supervisor
['fname']));
1592 function supervisorMiddleName()
1594 return x12clean(trim($this->supervisor
['mname']));
1597 function supervisorNPI()
1599 return x12clean(trim($this->supervisor
['npi']));
1602 function supervisorUPIN()
1604 return x12clean(trim($this->supervisor
['upin']));
1607 function supervisorSSN()
1609 return x12clean(trim(str_replace('-', '', $this->supervisor
['federaltaxid'])));
1612 function supervisorTaxonomy()
1614 if (empty($this->supervisor
['taxonomy'])) {
1615 return '207Q00000X';
1618 return x12clean(trim($this->supervisor
['taxonomy']));
1621 function supervisorNumberType()
1623 return $this->supervisor_numbers
['provider_number_type'];
1626 function supervisorNumber()
1628 return x12clean(trim(str_replace('-', '', $this->supervisor_numbers
['provider_number'])));
1631 function billingProviderLastName()
1633 return x12clean(trim($this->billing_prov_id
['lname']));
1636 function billingProviderFirstName()
1638 return x12clean(trim($this->billing_prov_id
['fname']));
1641 function billingProviderMiddleName()
1643 return x12clean(trim($this->billing_prov_id
['mname']));
1646 function billingProviderNPI()
1648 return x12clean(trim($this->billing_prov_id
['npi']));
1651 function billingProviderUPIN()
1653 return x12clean(trim($this->billing_prov_id
['upin']));
1656 function billingProviderSSN()
1658 return x12clean(trim(str_replace('-', '', $this->billing_prov_id
['federaltaxid'])));
1661 function billingProviderTaxonomy()
1663 if (empty($this->billing_prov_id
['taxonomy'])) {
1664 return '207Q00000X';
1667 return x12clean(trim($this->billing_prov_id
['taxonomy']));