MedEx Updates
[openemr.git] / library / gen_x12_837i.inc.php
blobbf14343b62ad9f21c538a83fbf7ae7a36d950315
1 <?php
2 /**
3 * X12 837I
5 * @package OpenEMR
6 * @link http://www.open-emr.org
7 * @author Jerry Padgett <sjpadgett@gmail.com>
8 * @copyright Copyright (c) 2017 Jerry Padgett <sjpadgett@gmail.com>
9 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
12 require_once(dirname(__FILE__) . "/invoice_summary.inc.php");
14 use OpenEMR\Billing\Claim;
16 function x12date($frmdate)
18 return ('20' . substr($frmdate, 4, 2) . substr($frmdate, 0, 2) . substr($frmdate, 2, 2));
21 $ub04id = array();
22 function generate_x12_837I($pid, $encounter, &$log, $ub04id)
24 $today = time();
25 $out = '';
26 $claim = new Claim($pid, $encounter);
27 $edicount = 0;
29 $log .= "Generating 837I claim $pid-$encounter for " .
30 $claim->patientFirstName() . ' ' .
31 $claim->patientMiddleName() . ' ' .
32 $claim->patientLastName() . ' on ' .
33 date('Y-m-d H:i', $today) . ".\n";
34 $out .= "ISA" .
35 "*" . $claim->x12gsisa01() .
36 "*" . $claim->x12gsisa02() .
37 "*" . $claim->x12gsisa03() .
38 "*" . $claim->x12gsisa04() .
39 "*" . $claim->x12gsisa05() .
40 "*" . $claim->x12gssenderid() .
41 "*" . $claim->x12gsisa07() .
42 "*" . $claim->x12gsreceiverid() .
43 // date of transmission "*030911" .
44 "*" . date('Ymd', $today) .
45 //Time of transmission "*1630" .
46 "*" . date('Hi', $today) .
47 "*" . "^" .
48 "*" . "00501" .
49 "*" . "000000001" .
50 "*" . $claim->x12gsisa14() .
51 "*" . $claim->x12gsisa15() .
52 "*:" .
53 "~\n";
54 $out .= "GS" .
55 "*HC" .
56 "*" . $claim->x12gsgs02() .
57 "*" . trim($claim->x12gs03()) .
58 "*" . date('Ymd', $today) .
59 "*" . date('Hi', $today) .
60 "*1" .
61 "*X" .
62 // "*" . $claim->x12gsversionstring() .
63 "*" . "005010X223A2".
64 "~\n";
65 ++$edicount;
66 $out .= "ST" .
67 "*" . "837" .
68 "*" . "0021" .
69 // "*" . $claim->x12gsversionstring() .
70 "*" . "005010X223A2".
71 "~\n";
72 ++$edicount;
73 $out .= "BHT" .
74 "*" . "0019" . // 0019 is required here
75 "*" . "00" . // 00 = original transmission
76 "*" . "0123" . // reference identification
77 "*" . date('Ymd', $today) . // transaction creation date
78 "*" . date('Hi', $today) . // transaction creation time
79 ($encounter_claim ? "*RP" : "*CH") . // RP = reporting, CH = chargeable
80 "~\n";
82 ++$edicount;
83 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
84 $billingFacilityName = substr($claim->billingFacilityName(), 0, 60);
85 if ($billingFacilityName == '') {
86 $log .= "*** billing facility name in 1000A loop is empty\n";
88 $out .= "NM1" . // Loop 1000A Submitter stays in the 837I
89 "*" . "41" .
90 "*" . "2" .
91 "*" . $billingFacilityName .
92 "*" .
93 "*" .
94 "*" .
95 "*" .
96 "*" . "46";
98 $out .= "*" . $claim->billingFacilityETIN();
99 $out .= "~\n";
100 ++$edicount;
102 $out .= "PER" .
103 "*IC" .
104 "*" . $claim->billingContactName() .
105 "*TE" .
106 "*" . $claim->billingContactPhone();
107 $out .= "~\n";
109 ++$edicount;
111 $out .= "NM1" . // Loop 1000B Receiver stays in the 837I
112 "*" . "40" .
113 "*" . "2" .
114 "*" . $claim->clearingHouseName() .
115 "*" .
116 "*" .
117 "*" .
118 "*" .
119 "*" . "46" .
120 "*" . $claim->clearingHouseETIN() .
121 "~\n";
122 $HLcount = 1;
123 ++$edicount;
125 $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop
126 "*" . "$HLcount" .
127 "*" .
128 "*" . "20" .
129 "*" . "1" . // 1 indicates there are child segments
130 "~\n";
131 $HLBillingPayToProvider = $HLcount++;
132 // Situational PRV segment (for provider taxonomy code) omitted here.
133 // Situational CUR segment (foreign currency information) omitted here.
134 ++$edicount;
135 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
136 $billingFacilityName = substr($claim->billingFacilityName(), 0, 60);
137 $out .= "NM1" . // Loop 2010AA Billing Provider stays in the 837I
138 "*" . "85" .
139 "*" . "2" .
140 "*" . $billingFacilityName .
141 "*" .
142 "*" .
143 "*" .
144 "*";
145 if ($claim->billingFacilityNPI()) {
146 $out .= "*XX*" . $claim->billingFacilityNPI();
147 } else {
148 $log .= "*** Billing facility has no NPI.\n";
149 $out .= "*XX*";
151 $out .= "~\n";
152 ++$edicount;
153 $out .= "N3" .
154 "*" . $claim->billingFacilityStreet() .
155 "~\n";
156 ++$edicount;
157 $out .= "N4" .
158 "*" . $claim->billingFacilityCity() .
159 "*" . $claim->billingFacilityState() .
160 "*" . stripZipCode($claim->billingFacilityZip()) .
161 "~\n";
162 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
163 ++$edicount;
164 $out .= "REF";
165 if ($claim->federalIdType()) {
166 $out .= "*" . $claim->federalIdType();
167 } else {
168 $out .= "*EI"; // For dealing with the situation before adding TaxId type In facility.
170 $out .= "*" . $claim->billingFacilityETIN() . "~\n";
171 } else {
172 $log .= "*** No billing facility NPI and/or ETIN.\n";
174 if ($claim->providerNumberType() && $claim->providerNumber() && !$claim->billingFacilityNPI()) {
175 ++$edicount;
176 $out .= "REF" .
177 "*" . $claim->providerNumberType() .
178 "*" . $claim->providerNumber() .
179 "~\n";
180 } else if ($claim->providerNumber() && !$claim->providerNumberType()) {
181 $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
183 // Situational PER*1C segment.
184 ++$edicount;
185 $out .= "PER" .
186 "*" . "IC" .
187 "*" . $claim->billingContactName() .
188 "*" . "TE" .
189 "*" . $claim->billingContactPhone();
190 $out .= "~\n";
192 // This is also excluded in the 837I
193 // Loop 2010AC Pay-To Plan Name omitted. Includes:
194 // NM1*PE, N3, N4, REF*2U, REF*EI
195 $PatientHL = $claim->isSelfOfInsured() ? 0 : 1;
196 $HLSubscriber = $HLcount++;
197 ++$edicount;
199 // loop 2000B
200 $out .= "HL" . // Loop 2000B Subscriber HL Loop
201 "*$HLSubscriber" .
202 "*$HLBillingPayToProvider" .
203 "*" . "22" .
204 "*$PatientHL" .
205 "~\n";
206 if (!$claim->payerSequence()) {
207 $log .= "*** Error: Insurance information is missing!\n";
209 ++$edicount;
211 //SBR01 is either a P or S SBR02 for care is always 18 "patient" SBR09 is always MA
212 $out .= "SBR" . // Subscriber Information
213 "*" . $claim->payerSequence() .
214 "*" . ($claim->isSelfOfInsured() ? '18' : '') .
215 "*" . $claim->groupNumber() .
216 "*" . (($claim->groupNumber()) ? '' : $claim->groupName()) .
217 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
218 "*" .
219 "*" .
220 "*" .
221 "*" . $claim->claimType() . // Zirmed replaces this
222 "~\n";
223 // 2000C Segment PAT omitted.
224 ++$edicount;
225 $out .= "NM1" . // Loop 2010BA Subscriber same in 837I
226 "*IL" .
227 "*" . "1" . // 1 = person, 2 = non-person
228 "*" . $claim->insuredLastName() .
229 "*" . $claim->insuredFirstName() .
230 "*" . $claim->insuredMiddleName() .
231 "*" .
232 "*" . // Name Suffix
233 "*MI" .
234 // "MI" = Member Identification Number
235 // "II" = Standard Unique Health Identifier, "Required if the
236 // HIPAA Individual Patient Identifier is mandated use."
237 // Here we presume that is not true yet.
238 "*" . $claim->policyNumber() .
239 "~\n";
240 // For 5010, further subscriber info is sent only if they are the patient.
241 if ($claim->isSelfOfInsured()) {
242 ++$edicount;
243 $out .= "N3" .
244 "*" . $claim->insuredStreet() .
245 "~\n";
246 ++$edicount;
247 $out .= "N4" .
248 "*" . $claim->insuredCity() .
249 "*" . $claim->insuredState() .
250 "*" . stripZipCode($claim->insuredZip()) .
251 "~\n";
252 ++$edicount;
253 $out .= "DMG" .
254 "*D8" .
255 "*" . $claim->insuredDOB() .
256 "*" . $claim->insuredSex() .
257 "~\n";
259 // Segment REF*SY (Subscriber Secondary Identification) omitted.
260 // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
261 // Segment PER*IC (Property and Casualty Subscriber Contact Information) omitted.
262 ++$edicount;
263 $payerName = substr($claim->payerName(), 0, 60);
264 $out .= "NM1" . // Loop 2010BB Payer
265 "*PR" .
266 "*" . "2" .
267 "*" . $payerName .
268 "*" .
269 "*" .
270 "*" .
271 "*" .
272 "*" . "PI" .
273 "*" . ($encounter_claim ? $claim->payerAltID() : $claim->payerID()) .
274 "~\n";
275 if (!$claim->payerID()) {
276 $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
279 ++$edicount;
280 $out .= "N3" .
281 "*" . $claim->payerStreet() .
282 "~\n";
283 ++$edicount;
284 $out .= "N4" .
285 "*" . $claim->payerCity() .
286 "*" . $claim->payerState() .
287 "*" . stripZipCode($claim->payerZip()) .
288 "~\n";
290 // Segment REF (Payer Secondary Identification) omitted.
291 // Segment REF (Billing Provider Secondary Identification) omitted.
294 if (! $claim->isSelfOfInsured()) {
295 ++$edicount;
296 $out .= "HL" . // Loop 2000C Patient Information
297 "*" . "$HLcount" .
298 "*$HLSubscriber" .
299 "*23" .
300 "*0" .
301 "~\n";
302 $HLcount++;
303 ++$edicount;
304 $out .= "PAT" .
305 "*" . $claim->insuredRelationship() .
306 "~\n";
307 ++$edicount;
308 $out .= "NM1" . // Loop 2010CA Patient may need this elsed in to the loop 2000C
309 "*QC" .
310 "*1" .
311 "*" . $claim->patientLastName() .
312 "*" . $claim->patientFirstName();
313 if ($claim->patientMiddleName() !== '') {
314 $out .= "*" . $claim->patientMiddleName();
316 $out .= "~\n";
317 ++$edicount;
318 $out .= "N3" .
319 "*" . $claim->patientStreet() .
320 "~\n";
321 ++$edicount;
322 $out .= "N4" .
323 "*" . $claim->patientCity() .
324 "*" . $claim->patientState() .
325 "*" . stripZipCode($claim->patientZip()) .
326 "~\n";
327 ++$edicount;
328 $out .= "DMG" .
329 "*D8" .
330 "*" . $claim->patientDOB() .
331 "*" . $claim->patientSex() .
332 "~\n";
333 // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
334 // Segment REF (Property and Casualty Patient Identifier) omitted.
335 // Segment PER (Property and Casualty Patient Contact Information) omitted.
336 } // end of patient different from insured
337 $proccount = $claim->procCount();
338 $clm_total_charges = 0;
339 for ($prockey = 0; $prockey < $proccount; ++$prockey) {
340 $clm_total_charges += $claim->cptCharges($prockey);
342 if (!$clm_total_charges) {
343 $log .= "*** This claim has no charges!\n";
345 ++$edicount;
346 $out .= "CLM" . // Loop 2300 Claim
347 "*" . $pid . "-" . $encounter .
348 "*" . sprintf("%.2f", $clm_total_charges) . // Zirmed computes and replaces this
349 "*" .
350 "*" ;
351 // Service location this need to be bill type from ub form type_of_bill
352 if (strlen($ub04id[7]) >= 3) {
353 $out .= "*" . substr($ub04id[7], 1, 1) . ":" . substr($ub04id[7], 2, 1) . ":" . substr($ub04id[7], 3, 1);
356 $out .= "*" .
357 "*" . "A" .
358 "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') .
359 "*" . "Y" .
360 "~\n";
361 // discharge hour
362 if ($ub04id[29]) {
363 ++ $edicount;
364 $out .= "DTP" . // Loop 2300
365 "*" . "096" .
366 "*" . "TM" .
367 "*" . $ub04id[29] .
368 "~\n";
371 // Statment Dates
372 // DTP 434 RD8 (Statment from OR to date)
374 if ($ub04id[13]) {
375 ++ $edicount;
377 $tmp = x12date($ub04id[13]);
378 $tmp1 = x12date($ub04id[14]);
379 $out .= "DTP" . // Loop 2300
380 "*434" . "*" . "RD8" . "*" . $tmp . '-' . $tmp1 . "~\n";
383 if ($ub04id[13]) {
384 ++ $edicount;
385 $tmp = x12date($ub04id[25]);
386 $out .= "DTP" . // Loop 2300
387 "*435" . "*" . "DT" . "*" . $tmp . $ub04id[26] . "~\n";
390 if (strlen(trim($ub04id[13])) == 0) {
391 $log .= "*** Error: No Admission Date Entered!\n";
394 // Repricer Received Date
395 // DTP 050 D8 (Admission Date and Hour from form)
397 // Institutional Claim Code
398 // CL1 (Admission Type Code) (Admission Source Code) (Patient Status Code)
400 if ($ub04id[27] != "014X") { // Type of bill
401 ++ $edicount;
402 $out .= "CL1" . // Loop 2300
403 "*" . $ub04id[27] . "*" . $ub04id[28] . "*" . $ub04id[30] . "~\n";
406 // Segment PWK (Claim Supplemental Information) omitted.
408 // Segment CN1 (Contract Information) omitted.
410 // Patient Estimated Amount Due
411 // Check logic
413 // $patientpaid = $claim->patientPaidAmount();
414 // if ($patientpaid != 0) {
415 // ++$edicount;
416 // $out .= "AMT" . // Patient paid amount. Page 190/220.
417 // "*F5" .
418 // "*" . $patientpaid .
419 // "~\n";
420 // }
422 // Segment REF*4N (Service Authorization Exception Code) omitted.
423 // Segment REF*9F (Referral Number) omitted.
425 // Prior Authorization
427 if ($claim->priorAuth()) {
428 ++ $edicount;
429 $out .= "REF" . // Prior Authorization Number
430 "*G1" . "*" . $claim->priorAuth() . "~\n";
433 // Segment REF*F8 (Payer Claim Control Number) omitted.
435 // This may be needed for the UB04 Claim if so change the 'MB' to 'MA'
436 // if ($claim->cliaCode() && ($CMS_5010 || $claim->claimType() === 'MB')) {
437 // Required by Medicare when in-house labs are done.
438 // ++$edicount;
439 // $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
440 // "*X4" .
441 // "*" . $claim->cliaCode() .
442 // "~\n";
443 // }
445 // Segment REF*9A (Repriced Claim Number) omitted.
446 // Segment REF*9C (Adjusted Repriced Claim Number) omitted.
447 // Segment REF*LX (Investigational Device Exemption Number) omitted.
448 // Segment REF*S9 (Claim Identifier for Transmission Intermediaries) omitted.
449 // Segment REF*LU (Auto Accident State) omitted.
450 // Segment REF*EA (Medical Record Number) omitted.
451 // Segment REF*P4 (Demonstration Project Identifier) omitted.
452 // Segment REF*G4 (Peer Review Organization PRO Approval Number) omitted.
453 // Segment K3 (File Information) omitted.
455 if ($claim->additionalNotes()) {
456 // Claim Note
457 // Has a list of valid codes. Required when PROVIDER deems necessary
459 // Billing note.
460 // Check to verify I am getting this information on the ub04 form
462 ++ $edicount;
463 $out .= "NTE" . // comments box 19
464 "*" . "ADD" . "*" . $claim->additionalNotes() . "~\n";
467 // Segment CRC (EPSDT Referral) omitted.
468 // Diagnoses, up to $max_per_seg per HI segment. Check this
469 $max_per_seg = 18;
470 $da = $claim->diagArray();
471 $diag_type_code = 'ABK'; // ICD10
472 $tmp = 0;
473 foreach ($da as $diag) {
474 if ($tmp == 1) {
475 continue;
477 if ($tmp % $max_per_seg == 0) {
478 if ($tmp) {
479 $out .= "~\n";
481 ++ $edicount;
482 $out .= "HI"; // Health Diagnosis Codes
484 $out .= "*" . $diag_type_code . ":" . $diag;
485 if ($claim->diagtype == "ICD9") {
486 $diag_type_code = 'BF';
487 } else {
488 $diag_type_code = 'ABF';
491 ++ $tmp;
493 if ($tmp) {
494 $out .= "~\n";
497 // Segment HI*BI (Occurrence Span Information).
498 // HI BI (Occurrence Span Code 1) RD8 (Occurrence Span Code Associated Date)
499 if ($ub04id[52]) {
500 $max_per_seg = 4;
501 $diag_type_code = 'BI';
502 $tmp = 0;
503 $os = 52;
504 for ($i = 0; $i <= 3;) {
505 if ($tmp % $max_per_seg == 0) {
506 if ($tmp) {
507 $out .= "~\n";
509 ++ $edicount;
510 $out .= "HI"; // Health Diagnosis Codes
512 if ($ub04id[$os]) {
513 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++] . ":" . x12date($ub04id[$os ++]) . ":" . x12date($ub04id[$os ++]);
514 $diag_type_code = 'BI';
516 if ($os >= 57) {
517 $os = 67;
519 ++ $tmp;
520 ++ $i;
522 if ($tmp) {
523 $out .= "~\n";
527 // Segment HI*BH (Occurrence Information).
528 // HI BH (Occurrence Code 1) D8 (Occurrence Code Associated Date)
530 if ($ub04id[44]) {
531 $max_per_seg = 8;
532 $diag_type_code = 'BH';
533 $tmp = 0;
534 $os = 44;
535 for ($i = 0; $i <= 7;) {
536 if ($tmp % $max_per_seg == 0) {
537 if ($tmp) {
538 $out .= "~\n";
540 ++ $edicount;
541 $out .= "HI"; // Health Diagnosis Codes
543 if ($ub04id[$os]) {
544 $out .= "*" . $diag_type_code . ":" . $ub04id[$os] . ":D8" . ":" . x12date($ub04id[$os ++]);
545 $diag_type_code = 'BH';
547 if ($os >= 51) {
548 $os = 59;
550 ++ $tmp;
551 ++ $i;
553 if ($tmp) {
554 $out .= "~\n";
558 // Segment HI*BE (Value Information).
559 // HI BE (Value Code 1) *.* (Value Code Amount)
561 if ($ub04id[74]) {
562 $max_per_seg = 12;
563 $diag_type_code = 'BE';
564 $os = 74;
565 $tmp = 0;
566 for ($i = 0; $i <= 11;) {
567 if ($tmp % $max_per_seg == 0) {
568 if ($tmp) {
569 $out .= "~\n";
571 ++ $edicount;
572 $out .= "HI"; // Health Diagnosis Codes
574 if ($ub04id[$os]) {
575 // if ($i=1) {
576 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++] . ":" . ":" . x12date($ub04id[$os ++]);
577 $diag_type_code = 'BE';
578 // }
580 ++ $tmp;
581 ++ $i;
583 if ($tmp) {
584 $out .= "~\n";
588 // Segment HI*BG (Condition Information).
589 // HI BG (Condition Code 1)
591 if ($ub04id[31]) {
592 $max_per_seg = 11;
593 $diag_type_code = 'BG';
594 $os = 31;
595 $tmp = 0;
596 for ($i = 0; $i <= 10;) {
597 if ($tmp % $max_per_seg == 0) {
598 if ($tmp) {
599 $out .= "~\n";
601 ++ $edicount;
602 $out .= "HI"; // Health Diagnosis Codes
604 if ($ub04id[$os]) {
605 // if ($i=1) {
606 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++];
607 $diag_type_code = 'BG';
608 // }
611 ++ $tmp;
612 ++ $i;
614 if ($tmp) {
615 $out .= "~\n";
619 // Segment HI*TC (Treatment Code Information).
620 // HI TC (Treatment Code 1)
621 /* 63a. TREATMENT AUTHORIZATION CODES - PRIMARY PLAN */
622 if ($ub04id[319]) {
623 $max_per_seg = 3;
624 $diag_type_code = 'TC';
625 $tmp = 0;
627 for ($i = 0; $i <= 2;) {
628 if ($tmp % $max_per_seg == 0) {
629 if ($tmp) {
630 $out .= "~\n";
632 ++ $edicount;
633 $out .= "HI"; // Health Diagnosis Codes
636 if ($ub04id[319]) {
637 $out .= "*" . $diag_type_code . ":" . $ub04id[319];
638 $diag_type_code = 'TC';
640 if ($i = 1) {
641 if ($ub04id[322]) {
642 $out .= "*" . $diag_type_code . ":" . $ub04id[322];
643 $diag_type_code = 'TC';
646 if ($i = 2) {
647 if ($ub04id[325]) {
648 $out .= "*" . $diag_type_code . ":" . $ub04id[325];
649 $diag_type_code = 'TC';
653 ++ $tmp;
654 ++ $i;
656 if ($tmp) {
657 $out .= "~\n";
661 // Segment HCP (Claim Pricing/Repricing Information) omitted.
663 // This needs to allow Attending Physician 2310A, Operating Physician Name 2310B, Other Operating Physician Name 2310C
664 // and Rendering Provider Name (Rendering Provider Name is futher down)
666 if ($ub04id[388]) {
667 ++ $edicount;
668 // Loop 2310A Attending Physician
669 $out .= "NM1" . "*71" . "*1" . "*" . $ub04id[388] . "*" . $ub04id[389] . "*" . "*";
670 if ($ub04id[379]) { // NPI
671 $out .= "*" . "XX" . "*" . $ub04id[379];
672 } else {
673 $out .= "*" . "*";
674 $log .= "*** Attending Physician has no NPI.\n";
676 $out .= "~\n";
678 if ($ub04id[380]) {
679 ++ $edicount;
680 $out .= "REF" . // Attending Physician Secondary Identification
681 "*" . $ub04id[380] . "*" . $ub04id[381] . "~\n";
685 // 2310B
687 if ($ub04id[400]) {
688 ++ $edicount;
690 $out .= "NM1" . // Loop 2310B operating Physician
691 "*72" . "*1" . "*" . $ub04id[400] . "*" . $ub04id[400] . "*" . "*";
692 if ($ub04id[390]) {
693 $out .= "*" . "XX" . "*" . $ub04id[390];
694 } else {
695 $out .= "*" . "*";
696 $log .= "*** Operating Physician has no NPI qualifier.\n";
698 $out .= "~\n";
700 if ($ub04id[391]) {
701 ++ $edicount;
702 $out .= "REF" . // operating Physician Secondary Identification
703 "*" . $claim->$ub04id[391] . "*" . $ub04id[392] . "~\n";
707 // 2310C
709 if ($ub04id[413]) {
710 ++ $edicount;
712 $out .= "NM1" . // Loop 2310C other operating Physician
713 "*73" . "*1" . "*" . $ub04id[413] . "*" . $ub04id[414] . "*" . "*";
714 if ($ub04id[405]) {
715 $out .= "*" . $ub04id[405] . "*" . $ub04id[406];
716 } else {
717 $out .= "*" . "*";
718 $log .= "*** Other Operating Physician has no NPI.\n";
720 $out .= "~\n";
722 if ($ub04id[407]) {
723 ++ $edicount;
724 $out .= "REF" . // other operating Physician Secondary Identification
725 "*" . $ub04id[407] . "*" . $ub04id[408] . "~\n";
728 if ($ub04id[427]) {
729 ++ $edicount;
731 $out .= "NM1" . // Loop 2310C other operating Physician
732 "*73" . "*1" . "*" . $ub04id[427] . "*" . $ub04id[428] . "*" . "*";
733 if ($ub04id[420]) {
734 $out .= "*" . $ub04id[419] . "*" . $ub04id[420];
735 } else {
736 $out .= "*" . "*";
737 $log .= "*** Other Operating Physician has no NPI.\n";
739 $out .= "~\n";
741 if ($ub04id[422]) {
742 ++ $edicount;
743 $out .= "REF" . // other operating Physician Secondary Identification
744 "*" . $ub04id[422] . "*" . $ub04id[421] . "~\n";
748 * Per the implementation guide lines, only include this information if it is different
749 * than the Loop 2010AA information
751 if ($claim->providerNPIValid() && $claim->billingFacilityNPI() !== $claim->providerNPI()) {
752 ++ $edicount;
753 $out .= "NM1" . // Loop 2310D Rendering Provider
754 "*82" . "*1" . "*" . $claim->providerLastName() . "*" . $claim->providerFirstName() . "*" . $claim->providerMiddleName() . "*" . "*";
755 if ($claim->providerNPI()) {
756 $out .= "*XX" . "*" . $claim->providerNPI();
757 } else {
758 $log .= "*** Rendering provider has no NPI.\n";
760 $out .= "~\n";
762 // End of Loop 2310D
763 } else {
764 // This loop can only get skipped if we are generating a 5010 claim
765 if (! ($claim->providerNPIValid())) {
766 //If the loop was skipped because the provider NPI was invalid, generate a warning for the log.
767 $log .= "*** Skipping 2310B because " . $claim->providerLastName() . "," . $claim->providerFirstName() . " has invalid NPI.\n";
770 * Skipping this segment because the providerNPI and the billingFacilityNPI are identical
771 * is a normal condition, so no need to warn.
775 // 5010 spec says nothing here if NPI was specified.
777 if (! $claim->providerNPI() && in_array($claim->providerNumberType(), array('0B', '1G', 'G2', 'LU'))) {
778 if ($claim->providerNumber()) {
779 ++ $edicount;
780 $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
784 // Loop 2310D is omitted in the case of home visits (POS=12).
785 if ($claim->facilityPOS() != 12 && ($claim->facilityNPI() != $claim->billingFacilityNPI())) {
786 ++ $edicount;
788 // Service Facility Name
790 $out .= "NM1" . // Loop 2310E Service Location
791 "*77" . "*2";
792 $facilityName = substr($claim->facilityName(), 0, 60);
793 if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) {
794 $out .= "*" . $facilityName;
796 if ($claim->facilityNPI() || $claim->facilityETIN()) {
797 $out .= "*" . "*" . "*" . "*";
798 if ($claim->facilityNPI()) {
799 $out .= "*XX*" . $claim->facilityNPI();
800 } else {
801 $log .= "*** Service location has no NPI.\n";
804 $out .= "~\n";
805 if ($claim->facilityStreet()) {
806 ++ $edicount;
807 $out .= "N3" . "*" . $claim->facilityStreet() . "~\n";
809 if ($claim->facilityState()) {
810 ++ $edicount;
811 $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . stripZipCode($claim->facilityZip()) . "~\n";
815 // Segment REF (Service Facility Location Secondary Identification) omitted.
816 // Segment PER (Service Facility Contact Information) omitted.
818 // Loop 2310F Referring Provider
820 if ($claim->referrerLastName()) {
821 // Medicare requires referring provider's name and UPIN.
822 ++ $edicount;
824 $out .= "NM1" . // Loop 2310F Referring Provider this needs to change position
825 "*DN" . "*1" . "*" . $claim->referrerLastName() . "*" . $claim->referrerFirstName() . "*" . $claim->referrerMiddleName() . "*" . "*";
826 if ($claim->referrerNPI()) {
827 $out .= "*XX" . "*" . $claim->referrerNPI();
828 } else {
829 $log .= "*** Referrer has no NPI.\n";
831 $out .= "~\n";
834 // Loop 2310E, Supervising Provider
835 // Omitted
837 // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
838 // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
840 $prev_pt_resp = $clm_total_charges; // for computation below
842 // Loops 2320 and 2330*, other subscriber/payer information.
843 // Remember that insurance index 0 is always for the payer being billed
844 // by this claim, and 1 and above are always for the "other" payers.
846 for ($ins = 1; $ins < $claim->payerCount(); ++ $ins) {
847 $tmp1 = $claim->claimType($ins);
848 $tmp2 = 'C1'; // Here a kludge. See page 321.
849 if ($tmp1 === 'CI') {
850 $tmp2 = 'C1';
852 if ($tmp1 === 'AM') {
853 $tmp2 = 'AP';
855 if ($tmp1 === 'HM') {
856 $tmp2 = 'HM';
858 if ($tmp1 === 'MB') {
859 $tmp2 = 'MB';
861 if ($tmp1 === 'MA') {
862 $tmp2 = 'MA';
864 if ($tmp1 === 'MC') {
865 $tmp2 = 'MC';
867 if ($tmp1 === '09') {
868 $tmp2 = 'PP';
870 ++ $edicount;
872 $out .= "SBR" . // Loop 2320, Subscriber Information - page 297/318
873 "*" . $claim->payerSequence($ins) .
874 "*" . $claim->insuredRelationship($ins) .
875 "*" . $claim->groupNumber($ins) .
876 "*" . (($claim->groupNumber($ins)) ? '' : $claim->groupName($ins)) .
877 "*" . ($claim->insuredTypeCode($ins) ? $claim->insuredTypeCode($ins) : $tmp2) .
878 "*" .
879 "*" .
880 "*" .
881 "*" . $claim->claimType($ins) .
882 "~\n";
884 // Things that apply only to previous payers, not future payers.
886 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
887 // Generate claim-level adjustments.
888 $aarr = $claim->payerAdjustments($ins);
889 foreach ($aarr as $a) {
890 ++ $edicount;
891 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 301/323.
892 "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
895 $payerpaid = $claim->payerTotals($ins);
896 ++ $edicount;
897 $out .= "AMT" . // Previous payer's paid amount. Page 307/332.
898 "*D" . "*" . $payerpaid[1] . "~\n";
900 // Segment AMT*A8 (COB Total Non-Covered Amount) omitted.
901 // Segment AMT*EAF (Remaining Patient Liability) omitted.
902 } // End of things that apply only to previous payers.
904 ++ $edicount;
905 $out .= "OI" . // Other Insurance Coverage Information. Page 310/344.
906 "*" . "*" . "*" . ($claim->billingFacilityAssignment($ins) ? 'Y' : 'N') .
907 // For this next item, the 5010 example in the spec does not match its
908 // description. So this might be wrong.
909 "*" .
910 "*" .
911 "*" .
912 "Y" .
913 "~\n";
915 // Segment MOA (Medicare Outpatient Adjudication) omitted.
916 ++$edicount;
917 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 315/350.
918 "*" . "IL" .
919 "*" . "1" .
920 "*" . $claim->insuredLastName($ins) .
921 "*" . $claim->insuredFirstName($ins) .
922 "*" . $claim->insuredMiddleName($ins) .
923 "*" .
924 "*" .
925 "*" . "MI" .
926 "*" . $claim->policyNumber($ins) .
927 "~\n";
929 ++$edicount;
930 $out .= "N3" .
931 "*" . $claim->insuredStreet($ins) .
932 "~\n";
934 ++$edicount;
935 $out .= "N4" .
936 "*" . $claim->insuredCity($ins) .
937 "*" . $claim->insuredState($ins) .
938 "*" . stripZipCode($claim->insuredZip($ins)) .
939 "~\n";
941 // Segment REF (Other Subscriber Secondary Identification) omitted.
942 ++$edicount;
943 $payerName = substr($claim->payerName($ins), 0, 60);
944 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 322/359.
945 "*" . "PR" .
946 "*" . "2" .
947 "*" . $payerName .
948 "*" .
949 "*" .
950 "*" .
951 "*" .
952 "*" . "PI" .
953 "*" . $claim->payerID($ins) .
954 "~\n";
956 if (!$claim->payerID($ins)) {
957 $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
960 ++$edicount;
961 $out .= "N3" .
962 "*" . $claim->payerStreet($ins) .
963 "~\n";
965 ++$edicount;
966 $out .= "N4" .
967 "*" . $claim->payerCity($ins) .
968 "*" . $claim->payerState($ins) .
969 "*" . stripZipCode($claim->payerZip($ins)) .
970 "~\n";
972 // Segment DTP*573 (Claim Check or Remittance Date) omitted.
973 // Segment REF (Other Payer Secondary Identifier) omitted.
974 // Segment REF*G1 (Other Payer Prior Authorization Number) omitted.
975 // Segment REF*9F (Other Payer Referral Number) omitted.
976 // Segment REF*T4 (Other Payer Claim Adjustment Indicator) omitted.
977 // Segment REF*F8 (Other Payer Claim Control Number) omitted.
978 // 2330C-I loops Omitted
979 } // End loops 2320/2330*.
981 $loopcount = 0;
983 // Procedure loop starts here.
986 for ($tlh = 0; $tlh < $proccount; ++ $tlh) {
987 $tmp = $claim->procs[$tlh][code_text];
989 if ($claim->procs[$tlh][code_type] == 'HCPCS') {
990 $tmpcode = '3';
991 } else {
992 $tmpcode = '1';
994 $getrevcd = $claim->cptCode($tlh);
995 $sql = "SELECT * FROM codes WHERE code_type = ? and code = ? ORDER BY revenue_code DESC";
996 $revcode[$tlh] = sqlQuery($sql, array(
997 $tmpcode,
998 $getrevcd
1003 for ($prockey = 0; $prockey < $proccount; ++ $prockey) {
1004 $os = 99 + ($loopcount * 8); // Form revenue code offset
1005 $dosos = 102 + ($loopcount * 8); // Procedure date of service form start offset-add 8 for loop
1006 ++ $loopcount;
1008 ++ $edicount;
1009 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
1010 "*" . "$loopcount" . "~\n";
1012 ++ $edicount;
1014 // Revenue code from form
1016 $tmp = $ub04id[$os]; //$revcode[$prockey][revenue_code];
1017 if (empty($tmp)) {
1018 $log .= "*** Error: Missing Revenue Code for " . $claim->cptKey($prockey) . "!\n";
1020 // Institutional Service Line.
1022 $out .= "SV2" . "*" . $tmp . // revenue code
1024 "*" . "HC:" . $claim->cptKey($prockey) . "*" . sprintf('%.2f', $claim->cptCharges($prockey)) . "*UN" . "*" . $claim->cptUnits($prockey) . "*" . "*" . "*";
1026 $out .= "~\n";
1028 if (! $claim->cptCharges($prockey)) {
1029 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
1032 // Segment SV5 (Durable Medical Equipment Service) omitted.
1033 // Segment PWK (Line Supplemental Information) omitted.
1034 // Segment PWK (Durable Medical Equipment Certificate of Medical Necessity Indicator) omitted.
1035 // Segment CR1 (Ambulance Transport Information) omitted.
1036 // Segment CR3 (Durable Medical Equipment Certification) omitted.
1037 // Segment CRC (Ambulance Certification) omitted.
1038 // Segment CRC (Hospice Employee Indicator) omitted.
1039 // Segment CRC (Condition Indicator / Durable Medical Equipment) omitted.
1041 ++ $edicount;
1043 $out .= "DTP" . // Date of Service. Needs to be when service preformed.
1044 "*" . "472" . "*" . "D8" . "*" . $ub04id[$dosos] . "~\n"; //$claim->serviceDate()
1046 $testnote = rtrim($claim->cptNotecodes($prockey));
1047 if (! empty($testnote)) {
1048 ++ $edicount;
1049 $out .= "NTE" . // Explain Unusual Circumstances.
1050 "*ADD" . "*" . $claim->cptNotecodes($prockey) . "~\n";
1053 // Segment DTP*471 (Prescription Date) omitted.
1054 // Segment DTP*607 (Revision/Recertification Date) omitted.
1055 // Segment DTP*463 (Begin Therapy Date) omitted.
1056 // Segment DTP*461 (Last Certification Date) omitted.
1057 // Segment DTP*304 (Last Seen Date) omitted.
1058 // Segment DTP (Test Date) omitted.
1059 // Segment DTP*011 (Shipped Date) omitted.
1060 // Segment DTP*455 (Last X-Ray Date) omitted.
1061 // Segment DTP*454 (Initial Treatment Date) omitted.
1062 // Segment QTY (Ambulance Patient Count) omitted.
1063 // Segment QTY (Obstetric Anesthesia Additional Units) omitted.
1064 // Segment MEA (Test Result) omitted.
1065 // Segment CN1 (Contract Information) omitted.
1066 // Segment REF*9B (Repriced Line Item Reference Number) omitted.
1067 // Segment REF*9D (Adjusted Repriced Line Item Reference Number) omitted.
1068 // Segment REF*G1 (Prior Authorization) omitted.
1069 // Segment REF*6R (Line Item Control Number) omitted.
1070 // (Really oughta have this for robust 835 posting!)
1071 // Segment REF*EW (Mammography Certification Number) omitted.
1072 // Segment REF*X4 (CLIA Number) omitted.
1073 // Segment REF*F4 (Referring CLIA Facility Identification) omitted.
1074 // Segment REF*BT (Immunization Batch Number) omitted.
1075 // Segment REF*9F (Referral Number) omitted.
1076 // Segment AMT*GT (Sales Tax Amount) omitted.
1077 // Segment AMT*F4 (Postage Claimed Amount) omitted.
1078 // Segment K3 (File Information) omitted.
1079 // Segment NTE (Line Note) omitted.
1080 // Segment NTE (Third Party Organization Notes) omitted.
1081 // Segment PS1 (Purchased Service Information) omitted.
1082 // Segment HCP (Line Pricing/Repricing Information) omitted.
1084 // Loop 2410, Drug Information. Medicaid insurers seem to want this
1085 // with HCPCS codes.
1087 $ndc = $claim->cptNDCID($prockey);
1089 if ($ndc) {
1090 ++$edicount;
1091 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
1092 "*" . // Per addendum, LIN01 is not used.
1093 "*" . "N4" .
1094 "*" . $ndc .
1095 "~\n";
1097 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp) && !preg_match('/^\d{11}$/', $ndc)) {
1098 $log .= "*** NDC code '$ndc' has invalid format!\n";
1101 ++$edicount;
1102 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
1103 "*" .
1104 "*" .
1105 "*" .
1106 "*" . $claim->cptNDCQuantity($prockey) .
1107 "*" . $claim->cptNDCUOM($prockey) .
1108 // Note: 5010 documents "ME" (Milligrams) as an additional unit of measure.
1109 "~\n";
1112 // Segment REF (Prescription or Compound Drug Association Number) omitted.
1114 // Loop 2420A, Rendering Provider (service-specific). (Operating Physician Name for 837I)
1115 // Used if the rendering provider for this service line is different
1116 // from that in loop 2310B.
1118 if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
1119 ++$edicount;
1120 $out .= "NM1" . // Loop 2310B Rendering Provider
1121 "*" . "82" .
1122 "*" . "1" .
1123 "*" . $claim->providerLastName($prockey) .
1124 "*" . $claim->providerFirstName($prockey) .
1125 "*" . $claim->providerMiddleName($prockey) .
1126 "*" .
1127 "*";
1128 if ($claim->providerNPI($prockey)) {
1129 $out .=
1130 "*" . "XX" .
1131 "*" . $claim->providerNPI($prockey);
1132 } else {
1133 $log .= "*** Rendering provider has no NPI.\n";
1135 $out .= "~\n";
1137 if ($claim->providerTaxonomy($prockey)) {
1138 ++$edicount;
1139 $out .= "PRV" .
1140 "*" . "PE" . // PErforming provider
1141 "*" . "PXC" .
1142 "*" . $claim->providerTaxonomy($prockey) .
1143 "~\n";
1146 // Segment PRV*PE (Rendering Provider Specialty Information) omitted.
1147 // Segment REF (Rendering Provider Secondary Identification) omitted.
1148 // Segment NM1 (Purchased Service Provider Name) omitted.
1149 // Segment REF (Purchased Service Provider Secondary Identification) omitted.
1150 // Segment NM1,N3,N4 (Service Facility Location) omitted.
1151 // Segment REF (Service Facility Location Secondary Identification) omitted.
1152 // Segment NM1 (Supervising Provider Name) omitted.
1153 // Segment REF (Supervising Provider Secondary Identification) omitted.
1154 // Segment NM1,N3,N4 (Ordering Provider) omitted.
1155 // Segment REF (Ordering Provider Secondary Identification) omitted.
1156 // Segment PER (Ordering Provider Contact Information) omitted.
1157 // Segment NM1 (Referring Provider Name) omitted.
1158 // Segment REF (Referring Provider Secondary Identification) omitted.
1159 // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
1160 // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
1162 // REF*1C is required here for the Medicare provider number if NPI was
1163 // specified in NM109. Not sure if other payers require anything here.
1164 if ($claim->providerNumberType($prockey) == "G2") {
1165 ++$edicount; $out .= "REF" . "*" . $claim->providerNumberType($prockey) .
1166 "*" . $claim->providerNumber($prockey) . "~\n";
1168 } // provider exception
1170 // Loop 2430, adjudication by previous payers.
1172 for ($ins = 1; $ins < $claim->payerCount(); ++ $ins) {
1173 if ($claim->payerSequence($ins) > $claim->payerSequence()) {
1174 continue; // payer is future, not previous
1177 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
1178 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
1180 if ($payerpaid[1] == 0 && ! count($aarr)) {
1181 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no payments or adjustments from previous payer!\n";
1182 continue;
1185 ++ $edicount;
1186 $out .= "SVD" . // Service line adjudication. Page 554.
1187 "*" . $claim->payerID($ins) . "*" . $payerpaid[1] . "*HC:" . $claim->cptKey($prockey) . "*" . "*" . $claim->cptUnits($prockey) . "~\n";
1189 $tmpdate = $payerpaid[0];
1190 foreach ($aarr as $a) {
1191 ++ $edicount;
1192 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
1193 "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
1194 if (! $tmpdate) {
1195 $tmpdate = $a[0];
1199 if ($tmpdate) {
1200 ++ $edicount;
1201 $out .= "DTP" . // Previous payer's line adjustment date. Page 493/566.
1202 "*573" . "*D8" . "*$tmpdate" . "~\n";
1205 // Segment AMT*EAF (Remaining Patient Liability) omitted.
1206 // Segment LQ (Form Identification Code) omitted.
1207 // Segment FRM (Supporting Documentation) omitted.
1208 } // end loop 2430
1209 } // end this procedure
1211 ++ $edicount;
1212 $out .= "SE" . // SE Trailer
1213 "*$edicount" . "*0021" . "~\n";
1215 $out .= "GE" . // GE Trailer
1216 "*1" . "*1" . "~\n";
1218 $out .= "IEA" . // IEA Trailer
1219 "*1" . "*000000001" . "~\n";
1221 // Remove any trailing empty fields (delimiters) from each segment.
1222 $out = preg_replace('/\*+~/', '~', $out);
1224 $log .= "\n";
1225 return $out;