some portal work
[openemr.git] / library / gen_x12_837i.inc.php
blob1d0b7b0ebf77e4ab05af189b3839be72eebfc864
1 <?php
2 /**
3 * X12 837I
5 * @package OpenEMR
6 * @link https://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 use OpenEMR\Billing\Claim;
14 function x12date($frmdate)
16 return ('20' . substr($frmdate, 4, 2) . substr($frmdate, 0, 2) . substr($frmdate, 2, 2));
19 $ub04id = array();
20 function generate_x12_837I($pid, $encounter, &$log, $ub04id)
22 $today = time();
23 $out = '';
24 $claim = new Claim($pid, $encounter);
25 $edicount = 0;
27 $log .= "Generating 837I claim $pid-$encounter for " .
28 $claim->patientFirstName() . ' ' .
29 $claim->patientMiddleName() . ' ' .
30 $claim->patientLastName() . ' on ' .
31 date('Y-m-d H:i', $today) . ".\n";
32 $out .= "ISA" .
33 "*" . $claim->x12gsisa01() .
34 "*" . $claim->x12gsisa02() .
35 "*" . $claim->x12gsisa03() .
36 "*" . $claim->x12gsisa04() .
37 "*" . $claim->x12gsisa05() .
38 "*" . $claim->x12gssenderid() .
39 "*" . $claim->x12gsisa07() .
40 "*" . $claim->x12gsreceiverid() .
41 // date of transmission "*030911" .
42 "*" . date('Ymd', $today) .
43 //Time of transmission "*1630" .
44 "*" . date('Hi', $today) .
45 "*" . "^" .
46 "*" . "00501" .
47 "*" . "000000001" .
48 "*" . $claim->x12gsisa14() .
49 "*" . $claim->x12gsisa15() .
50 "*:" .
51 "~\n";
52 $out .= "GS" .
53 "*HC" .
54 "*" . $claim->x12gsgs02() .
55 "*" . trim($claim->x12gs03()) .
56 "*" . date('Ymd', $today) .
57 "*" . date('Hi', $today) .
58 "*1" .
59 "*X" .
60 // "*" . $claim->x12gsversionstring() .
61 "*" . "005010X223A2".
62 "~\n";
63 ++$edicount;
64 $out .= "ST" .
65 "*" . "837" .
66 "*" . "0021" .
67 // "*" . $claim->x12gsversionstring() .
68 "*" . "005010X223A2".
69 "~\n";
70 ++$edicount;
71 $out .= "BHT" .
72 "*" . "0019" . // 0019 is required here
73 "*" . "00" . // 00 = original transmission
74 "*" . "0123" . // reference identification
75 "*" . date('Ymd', $today) . // transaction creation date
76 "*" . date('Hi', $today) . // transaction creation time
77 ($encounter_claim ? "*RP" : "*CH") . // RP = reporting, CH = chargeable
78 "~\n";
80 ++$edicount;
81 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
82 $billingFacilityName = substr($claim->billingFacilityName(), 0, 60);
83 if ($billingFacilityName == '') {
84 $log .= "*** billing facility name in 1000A loop is empty\n";
86 $out .= "NM1" . // Loop 1000A Submitter stays in the 837I
87 "*" . "41" .
88 "*" . "2" .
89 "*" . $billingFacilityName .
90 "*" .
91 "*" .
92 "*" .
93 "*" .
94 "*" . "46";
96 $out .= "*" . $claim->billingFacilityETIN();
97 $out .= "~\n";
98 ++$edicount;
100 $out .= "PER" .
101 "*IC" .
102 "*" . $claim->billingContactName() .
103 "*TE" .
104 "*" . $claim->billingContactPhone();
105 $out .= "~\n";
107 ++$edicount;
109 $out .= "NM1" . // Loop 1000B Receiver stays in the 837I
110 "*" . "40" .
111 "*" . "2" .
112 "*" . $claim->clearingHouseName() .
113 "*" .
114 "*" .
115 "*" .
116 "*" .
117 "*" . "46" .
118 "*" . $claim->clearingHouseETIN() .
119 "~\n";
120 $HLcount = 1;
121 ++$edicount;
123 $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop
124 "*" . "$HLcount" .
125 "*" .
126 "*" . "20" .
127 "*" . "1" . // 1 indicates there are child segments
128 "~\n";
129 $HLBillingPayToProvider = $HLcount++;
130 // Situational PRV segment (for provider taxonomy code) omitted here.
131 // Situational CUR segment (foreign currency information) omitted here.
132 ++$edicount;
133 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
134 $billingFacilityName = substr($claim->billingFacilityName(), 0, 60);
135 $out .= "NM1" . // Loop 2010AA Billing Provider stays in the 837I
136 "*" . "85" .
137 "*" . "2" .
138 "*" . $billingFacilityName .
139 "*" .
140 "*" .
141 "*" .
142 "*";
143 if ($claim->billingFacilityNPI()) {
144 $out .= "*XX*" . $claim->billingFacilityNPI();
145 } else {
146 $log .= "*** Billing facility has no NPI.\n";
147 $out .= "*XX*";
149 $out .= "~\n";
150 ++$edicount;
151 $out .= "N3" .
152 "*" . $claim->billingFacilityStreet() .
153 "~\n";
154 ++$edicount;
155 $out .= "N4" .
156 "*" . $claim->billingFacilityCity() .
157 "*" . $claim->billingFacilityState() .
158 "*" . $claim->x12Zip($claim->billingFacilityZip()) .
159 "~\n";
160 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
161 ++$edicount;
162 $out .= "REF";
163 if ($claim->federalIdType()) {
164 $out .= "*" . $claim->federalIdType();
165 } else {
166 $out .= "*EI"; // For dealing with the situation before adding TaxId type In facility.
168 $out .= "*" . $claim->billingFacilityETIN() . "~\n";
169 } else {
170 $log .= "*** No billing facility NPI and/or ETIN.\n";
172 if ($claim->providerNumberType() && $claim->providerNumber() && !$claim->billingFacilityNPI()) {
173 ++$edicount;
174 $out .= "REF" .
175 "*" . $claim->providerNumberType() .
176 "*" . $claim->providerNumber() .
177 "~\n";
178 } else if ($claim->providerNumber() && !$claim->providerNumberType()) {
179 $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
181 // Situational PER*1C segment.
182 ++$edicount;
183 $out .= "PER" .
184 "*" . "IC" .
185 "*" . $claim->billingContactName() .
186 "*" . "TE" .
187 "*" . $claim->billingContactPhone();
188 $out .= "~\n";
190 // This is also excluded in the 837I
191 // Loop 2010AC Pay-To Plan Name omitted. Includes:
192 // NM1*PE, N3, N4, REF*2U, REF*EI
193 $PatientHL = $claim->isSelfOfInsured() ? 0 : 1;
194 $HLSubscriber = $HLcount++;
195 ++$edicount;
197 // loop 2000B
198 $out .= "HL" . // Loop 2000B Subscriber HL Loop
199 "*$HLSubscriber" .
200 "*$HLBillingPayToProvider" .
201 "*" . "22" .
202 "*$PatientHL" .
203 "~\n";
204 if (!$claim->payerSequence()) {
205 $log .= "*** Error: Insurance information is missing!\n";
207 ++$edicount;
209 //SBR01 is either a P or S SBR02 for care is always 18 "patient" SBR09 is always MA
210 $out .= "SBR" . // Subscriber Information
211 "*" . $claim->payerSequence() .
212 "*" . ($claim->isSelfOfInsured() ? '18' : '') .
213 "*" . $claim->groupNumber() .
214 "*" . (($claim->groupNumber()) ? '' : $claim->groupName()) .
215 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
216 "*" .
217 "*" .
218 "*" .
219 "*" . $claim->claimType() . // Zirmed replaces this
220 "~\n";
221 // 2000C Segment PAT omitted.
222 ++$edicount;
223 $out .= "NM1" . // Loop 2010BA Subscriber same in 837I
224 "*IL" .
225 "*" . "1" . // 1 = person, 2 = non-person
226 "*" . $claim->insuredLastName() .
227 "*" . $claim->insuredFirstName() .
228 "*" . $claim->insuredMiddleName() .
229 "*" .
230 "*" . // Name Suffix
231 "*MI" .
232 // "MI" = Member Identification Number
233 // "II" = Standard Unique Health Identifier, "Required if the
234 // HIPAA Individual Patient Identifier is mandated use."
235 // Here we presume that is not true yet.
236 "*" . $claim->policyNumber() .
237 "~\n";
238 // For 5010, further subscriber info is sent only if they are the patient.
239 if ($claim->isSelfOfInsured()) {
240 ++$edicount;
241 $out .= "N3" .
242 "*" . $claim->insuredStreet() .
243 "~\n";
244 ++$edicount;
245 $out .= "N4" .
246 "*" . $claim->insuredCity() .
247 "*" . $claim->insuredState() .
248 "*" . $claim->x12Zip($claim->insuredZip()) .
249 "~\n";
250 ++$edicount;
251 $out .= "DMG" .
252 "*D8" .
253 "*" . $claim->insuredDOB() .
254 "*" . $claim->insuredSex() .
255 "~\n";
257 // Segment REF*SY (Subscriber Secondary Identification) omitted.
258 // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
259 // Segment PER*IC (Property and Casualty Subscriber Contact Information) omitted.
260 ++$edicount;
261 $payerName = substr($claim->payerName(), 0, 60);
262 $out .= "NM1" . // Loop 2010BB Payer
263 "*PR" .
264 "*" . "2" .
265 "*" . $payerName .
266 "*" .
267 "*" .
268 "*" .
269 "*" .
270 "*" . "PI" .
271 "*" . ($encounter_claim ? $claim->payerAltID() : $claim->payerID()) .
272 "~\n";
273 if (!$claim->payerID()) {
274 $log .= "*** Payer ID is missing for payer '" . $claim->payerName() . "'.\n";
277 ++$edicount;
278 $out .= "N3" .
279 "*" . $claim->payerStreet() .
280 "~\n";
281 ++$edicount;
282 $out .= "N4" .
283 "*" . $claim->payerCity() .
284 "*" . $claim->payerState() .
285 "*" . $claim->x12Zip($claim->payerZip()) .
286 "~\n";
288 // Segment REF (Payer Secondary Identification) omitted.
289 // Segment REF (Billing Provider Secondary Identification) omitted.
292 if (! $claim->isSelfOfInsured()) {
293 ++$edicount;
294 $out .= "HL" . // Loop 2000C Patient Information
295 "*" . "$HLcount" .
296 "*$HLSubscriber" .
297 "*23" .
298 "*0" .
299 "~\n";
300 $HLcount++;
301 ++$edicount;
302 $out .= "PAT" .
303 "*" . $claim->insuredRelationship() .
304 "~\n";
305 ++$edicount;
306 $out .= "NM1" . // Loop 2010CA Patient may need this elsed in to the loop 2000C
307 "*QC" .
308 "*1" .
309 "*" . $claim->patientLastName() .
310 "*" . $claim->patientFirstName();
311 if ($claim->patientMiddleName() !== '') {
312 $out .= "*" . $claim->patientMiddleName();
314 $out .= "~\n";
315 ++$edicount;
316 $out .= "N3" .
317 "*" . $claim->patientStreet() .
318 "~\n";
319 ++$edicount;
320 $out .= "N4" .
321 "*" . $claim->patientCity() .
322 "*" . $claim->patientState() .
323 "*" . $claim->x12Zip($claim->patientZip()) .
324 "~\n";
325 ++$edicount;
326 $out .= "DMG" .
327 "*D8" .
328 "*" . $claim->patientDOB() .
329 "*" . $claim->patientSex() .
330 "~\n";
331 // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
332 // Segment REF (Property and Casualty Patient Identifier) omitted.
333 // Segment PER (Property and Casualty Patient Contact Information) omitted.
334 } // end of patient different from insured
335 $proccount = $claim->procCount();
336 $clm_total_charges = 0;
337 for ($prockey = 0; $prockey < $proccount; ++$prockey) {
338 $clm_total_charges += $claim->cptCharges($prockey);
340 if (!$clm_total_charges) {
341 $log .= "*** This claim has no charges!\n";
343 ++$edicount;
344 $out .= "CLM" . // Loop 2300 Claim
345 "*" . $pid . "-" . $encounter .
346 "*" . sprintf("%.2f", $clm_total_charges) . // Zirmed computes and replaces this
347 "*" .
348 "*" ;
349 // Service location this need to be bill type from ub form type_of_bill
350 if (strlen($ub04id[7]) >= 3) {
351 $out .= "*" . substr($ub04id[7], 1, 1) . ":" . substr($ub04id[7], 2, 1) . ":" . substr($ub04id[7], 3, 1);
354 $out .= "*" .
355 "*" . "A" .
356 "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') .
357 "*" . "Y" .
358 "~\n";
359 // discharge hour
360 if ($ub04id[29]) {
361 ++ $edicount;
362 $out .= "DTP" . // Loop 2300
363 "*" . "096" .
364 "*" . "TM" .
365 "*" . $ub04id[29] .
366 "~\n";
369 // Statment Dates
370 // DTP 434 RD8 (Statment from OR to date)
372 if ($ub04id[13]) {
373 ++ $edicount;
375 $tmp = x12date($ub04id[13]);
376 $tmp1 = x12date($ub04id[14]);
377 $out .= "DTP" . // Loop 2300
378 "*434" . "*" . "RD8" . "*" . $tmp . '-' . $tmp1 . "~\n";
381 if ($ub04id[13]) {
382 ++ $edicount;
383 $tmp = x12date($ub04id[25]);
384 $out .= "DTP" . // Loop 2300
385 "*435" . "*" . "DT" . "*" . $tmp . $ub04id[26] . "~\n";
388 if (strlen(trim($ub04id[13])) == 0) {
389 $log .= "*** Error: No Admission Date Entered!\n";
392 // Repricer Received Date
393 // DTP 050 D8 (Admission Date and Hour from form)
395 // Institutional Claim Code
396 // CL1 (Admission Type Code) (Admission Source Code) (Patient Status Code)
398 if ($ub04id[27] != "014X") { // Type of bill
399 ++ $edicount;
400 $out .= "CL1" . // Loop 2300
401 "*" . $ub04id[27] . "*" . $ub04id[28] . "*" . $ub04id[30] . "~\n";
404 // Segment PWK (Claim Supplemental Information) omitted.
406 // Segment CN1 (Contract Information) omitted.
408 // Patient Estimated Amount Due
409 // Check logic
411 // $patientpaid = $claim->patientPaidAmount();
412 // if ($patientpaid != 0) {
413 // ++$edicount;
414 // $out .= "AMT" . // Patient paid amount. Page 190/220.
415 // "*F5" .
416 // "*" . $patientpaid .
417 // "~\n";
418 // }
420 // Segment REF*4N (Service Authorization Exception Code) omitted.
421 // Segment REF*9F (Referral Number) omitted.
423 // Prior Authorization
425 if ($claim->priorAuth()) {
426 ++ $edicount;
427 $out .= "REF" . // Prior Authorization Number
428 "*G1" . "*" . $claim->priorAuth() . "~\n";
431 // Segment REF*F8 (Payer Claim Control Number) omitted.
433 // This may be needed for the UB04 Claim if so change the 'MB' to 'MA'
434 // if ($claim->cliaCode() && ($CMS_5010 || $claim->claimType() === 'MB')) {
435 // Required by Medicare when in-house labs are done.
436 // ++$edicount;
437 // $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
438 // "*X4" .
439 // "*" . $claim->cliaCode() .
440 // "~\n";
441 // }
443 // Segment REF*9A (Repriced Claim Number) omitted.
444 // Segment REF*9C (Adjusted Repriced Claim Number) omitted.
445 // Segment REF*LX (Investigational Device Exemption Number) omitted.
446 // Segment REF*S9 (Claim Identifier for Transmission Intermediaries) omitted.
447 // Segment REF*LU (Auto Accident State) omitted.
448 // Segment REF*EA (Medical Record Number) omitted.
449 // Segment REF*P4 (Demonstration Project Identifier) omitted.
450 // Segment REF*G4 (Peer Review Organization PRO Approval Number) omitted.
451 // Segment K3 (File Information) omitted.
453 if ($claim->additionalNotes()) {
454 // Claim Note
455 // Has a list of valid codes. Required when PROVIDER deems necessary
457 // Billing note.
458 // Check to verify I am getting this information on the ub04 form
460 ++ $edicount;
461 $out .= "NTE" . // comments box 19
462 "*" . "ADD" . "*" . $claim->additionalNotes() . "~\n";
465 // Segment CRC (EPSDT Referral) omitted.
466 // Diagnoses, up to $max_per_seg per HI segment. Check this
467 $max_per_seg = 18;
468 $da = $claim->diagArray();
469 $diag_type_code = 'ABK'; // ICD10
470 $tmp = 0;
471 foreach ($da as $diag) {
472 if ($tmp == 1) {
473 continue;
475 if ($tmp % $max_per_seg == 0) {
476 if ($tmp) {
477 $out .= "~\n";
479 ++ $edicount;
480 $out .= "HI"; // Health Diagnosis Codes
482 $out .= "*" . $diag_type_code . ":" . $diag;
483 if ($claim->diagtype == "ICD9") {
484 $diag_type_code = 'BF';
485 } else {
486 $diag_type_code = 'ABF';
489 ++ $tmp;
491 if ($tmp) {
492 $out .= "~\n";
495 // Segment HI*BI (Occurrence Span Information).
496 // HI BI (Occurrence Span Code 1) RD8 (Occurrence Span Code Associated Date)
497 if ($ub04id[52]) {
498 $max_per_seg = 4;
499 $diag_type_code = 'BI';
500 $tmp = 0;
501 $os = 52;
502 for ($i = 0; $i <= 3;) {
503 if ($tmp % $max_per_seg == 0) {
504 if ($tmp) {
505 $out .= "~\n";
507 ++ $edicount;
508 $out .= "HI"; // Health Diagnosis Codes
510 if ($ub04id[$os]) {
511 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++] . ":" . x12date($ub04id[$os ++]) . ":" . x12date($ub04id[$os ++]);
512 $diag_type_code = 'BI';
514 if ($os >= 57) {
515 $os = 67;
517 ++ $tmp;
518 ++ $i;
520 if ($tmp) {
521 $out .= "~\n";
525 // Segment HI*BH (Occurrence Information).
526 // HI BH (Occurrence Code 1) D8 (Occurrence Code Associated Date)
528 if ($ub04id[44]) {
529 $max_per_seg = 8;
530 $diag_type_code = 'BH';
531 $tmp = 0;
532 $os = 44;
533 for ($i = 0; $i <= 7;) {
534 if ($tmp % $max_per_seg == 0) {
535 if ($tmp) {
536 $out .= "~\n";
538 ++ $edicount;
539 $out .= "HI"; // Health Diagnosis Codes
541 if ($ub04id[$os]) {
542 $out .= "*" . $diag_type_code . ":" . $ub04id[$os] . ":D8" . ":" . x12date($ub04id[$os ++]);
543 $diag_type_code = 'BH';
545 if ($os >= 51) {
546 $os = 59;
548 ++ $tmp;
549 ++ $i;
551 if ($tmp) {
552 $out .= "~\n";
556 // Segment HI*BE (Value Information).
557 // HI BE (Value Code 1) *.* (Value Code Amount)
559 if ($ub04id[74]) {
560 $max_per_seg = 12;
561 $diag_type_code = 'BE';
562 $os = 74;
563 $tmp = 0;
564 for ($i = 0; $i <= 11;) {
565 if ($tmp % $max_per_seg == 0) {
566 if ($tmp) {
567 $out .= "~\n";
569 ++ $edicount;
570 $out .= "HI"; // Health Diagnosis Codes
572 if ($ub04id[$os]) {
573 // if ($i=1) {
574 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++] . ":" . ":" . x12date($ub04id[$os ++]);
575 $diag_type_code = 'BE';
576 // }
578 ++ $tmp;
579 ++ $i;
581 if ($tmp) {
582 $out .= "~\n";
586 // Segment HI*BG (Condition Information).
587 // HI BG (Condition Code 1)
589 if ($ub04id[31]) {
590 $max_per_seg = 11;
591 $diag_type_code = 'BG';
592 $os = 31;
593 $tmp = 0;
594 for ($i = 0; $i <= 10;) {
595 if ($tmp % $max_per_seg == 0) {
596 if ($tmp) {
597 $out .= "~\n";
599 ++ $edicount;
600 $out .= "HI"; // Health Diagnosis Codes
602 if ($ub04id[$os]) {
603 // if ($i=1) {
604 $out .= "*" . $diag_type_code . ":" . $ub04id[$os ++];
605 $diag_type_code = 'BG';
606 // }
609 ++ $tmp;
610 ++ $i;
612 if ($tmp) {
613 $out .= "~\n";
617 // Segment HI*TC (Treatment Code Information).
618 // HI TC (Treatment Code 1)
619 /* 63a. TREATMENT AUTHORIZATION CODES - PRIMARY PLAN */
620 if ($ub04id[319]) {
621 $max_per_seg = 3;
622 $diag_type_code = 'TC';
623 $tmp = 0;
625 for ($i = 0; $i <= 2;) {
626 if ($tmp % $max_per_seg == 0) {
627 if ($tmp) {
628 $out .= "~\n";
630 ++ $edicount;
631 $out .= "HI"; // Health Diagnosis Codes
634 if ($ub04id[319]) {
635 $out .= "*" . $diag_type_code . ":" . $ub04id[319];
636 $diag_type_code = 'TC';
638 if ($i = 1) {
639 if ($ub04id[322]) {
640 $out .= "*" . $diag_type_code . ":" . $ub04id[322];
641 $diag_type_code = 'TC';
644 if ($i = 2) {
645 if ($ub04id[325]) {
646 $out .= "*" . $diag_type_code . ":" . $ub04id[325];
647 $diag_type_code = 'TC';
651 ++ $tmp;
652 ++ $i;
654 if ($tmp) {
655 $out .= "~\n";
659 // Segment HCP (Claim Pricing/Repricing Information) omitted.
661 // This needs to allow Attending Physician 2310A, Operating Physician Name 2310B, Other Operating Physician Name 2310C
662 // and Rendering Provider Name (Rendering Provider Name is futher down)
664 if ($ub04id[388]) {
665 ++ $edicount;
666 // Loop 2310A Attending Physician
667 $out .= "NM1" . "*71" . "*1" . "*" . $ub04id[388] . "*" . $ub04id[389] . "*" . "*";
668 if ($ub04id[379]) { // NPI
669 $out .= "*" . "XX" . "*" . $ub04id[379];
670 } else {
671 $out .= "*" . "*";
672 $log .= "*** Attending Physician has no NPI.\n";
674 $out .= "~\n";
676 if ($ub04id[380]) {
677 ++ $edicount;
678 $out .= "REF" . // Attending Physician Secondary Identification
679 "*" . $ub04id[380] . "*" . $ub04id[381] . "~\n";
683 // 2310B
685 if ($ub04id[400]) {
686 ++ $edicount;
688 $out .= "NM1" . // Loop 2310B operating Physician
689 "*72" . "*1" . "*" . $ub04id[400] . "*" . $ub04id[400] . "*" . "*";
690 if ($ub04id[390]) {
691 $out .= "*" . "XX" . "*" . $ub04id[390];
692 } else {
693 $out .= "*" . "*";
694 $log .= "*** Operating Physician has no NPI qualifier.\n";
696 $out .= "~\n";
698 if ($ub04id[391]) {
699 ++ $edicount;
700 $out .= "REF" . // operating Physician Secondary Identification
701 "*" . $claim->$ub04id[391] . "*" . $ub04id[392] . "~\n";
705 // 2310C
707 if ($ub04id[413]) {
708 ++ $edicount;
710 $out .= "NM1" . // Loop 2310C other operating Physician
711 "*73" . "*1" . "*" . $ub04id[413] . "*" . $ub04id[414] . "*" . "*";
712 if ($ub04id[405]) {
713 $out .= "*" . $ub04id[405] . "*" . $ub04id[406];
714 } else {
715 $out .= "*" . "*";
716 $log .= "*** Other Operating Physician has no NPI.\n";
718 $out .= "~\n";
720 if ($ub04id[407]) {
721 ++ $edicount;
722 $out .= "REF" . // other operating Physician Secondary Identification
723 "*" . $ub04id[407] . "*" . $ub04id[408] . "~\n";
726 if ($ub04id[427]) {
727 ++ $edicount;
729 $out .= "NM1" . // Loop 2310C other operating Physician
730 "*73" . "*1" . "*" . $ub04id[427] . "*" . $ub04id[428] . "*" . "*";
731 if ($ub04id[420]) {
732 $out .= "*" . $ub04id[419] . "*" . $ub04id[420];
733 } else {
734 $out .= "*" . "*";
735 $log .= "*** Other Operating Physician has no NPI.\n";
737 $out .= "~\n";
739 if ($ub04id[422]) {
740 ++ $edicount;
741 $out .= "REF" . // other operating Physician Secondary Identification
742 "*" . $ub04id[422] . "*" . $ub04id[421] . "~\n";
746 * Per the implementation guide lines, only include this information if it is different
747 * than the Loop 2010AA information
749 if ($claim->providerNPIValid() && $claim->billingFacilityNPI() !== $claim->providerNPI()) {
750 ++ $edicount;
751 $out .= "NM1" . // Loop 2310D Rendering Provider
752 "*82" . "*1" . "*" . $claim->providerLastName() . "*" . $claim->providerFirstName() . "*" . $claim->providerMiddleName() . "*" . "*";
753 if ($claim->providerNPI()) {
754 $out .= "*XX" . "*" . $claim->providerNPI();
755 } else {
756 $log .= "*** Rendering provider has no NPI.\n";
758 $out .= "~\n";
760 // End of Loop 2310D
761 } else {
762 // This loop can only get skipped if we are generating a 5010 claim
763 if (! ($claim->providerNPIValid())) {
764 //If the loop was skipped because the provider NPI was invalid, generate a warning for the log.
765 $log .= "*** Skipping 2310B because " . $claim->providerLastName() . "," . $claim->providerFirstName() . " has invalid NPI.\n";
768 * Skipping this segment because the providerNPI and the billingFacilityNPI are identical
769 * is a normal condition, so no need to warn.
773 // 5010 spec says nothing here if NPI was specified.
775 if (! $claim->providerNPI() && in_array($claim->providerNumberType(), array('0B', '1G', 'G2', 'LU'))) {
776 if ($claim->providerNumber()) {
777 ++ $edicount;
778 $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
782 // Loop 2310D is omitted in the case of home visits (POS=12).
783 if ($claim->facilityPOS() != 12 && ($claim->facilityNPI() != $claim->billingFacilityNPI())) {
784 ++ $edicount;
786 // Service Facility Name
788 $out .= "NM1" . // Loop 2310E Service Location
789 "*77" . "*2";
790 $facilityName = substr($claim->facilityName(), 0, 60);
791 if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) {
792 $out .= "*" . $facilityName;
794 if ($claim->facilityNPI() || $claim->facilityETIN()) {
795 $out .= "*" . "*" . "*" . "*";
796 if ($claim->facilityNPI()) {
797 $out .= "*XX*" . $claim->facilityNPI();
798 } else {
799 $log .= "*** Service location has no NPI.\n";
802 $out .= "~\n";
803 if ($claim->facilityStreet()) {
804 ++ $edicount;
805 $out .= "N3" . "*" . $claim->facilityStreet() . "~\n";
807 if ($claim->facilityState()) {
808 ++ $edicount;
809 $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . $claim->x12Zip($claim->facilityZip()) . "~\n";
813 // Segment REF (Service Facility Location Secondary Identification) omitted.
814 // Segment PER (Service Facility Contact Information) omitted.
816 // Loop 2310F Referring Provider
818 if ($claim->referrerLastName()) {
819 // Medicare requires referring provider's name and UPIN.
820 ++ $edicount;
822 $out .= "NM1" . // Loop 2310F Referring Provider this needs to change position
823 "*DN" . "*1" . "*" . $claim->referrerLastName() . "*" . $claim->referrerFirstName() . "*" . $claim->referrerMiddleName() . "*" . "*";
824 if ($claim->referrerNPI()) {
825 $out .= "*XX" . "*" . $claim->referrerNPI();
826 } else {
827 $log .= "*** Referrer has no NPI.\n";
829 $out .= "~\n";
832 // Loop 2310E, Supervising Provider
833 // Omitted
835 // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
836 // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
838 $prev_pt_resp = $clm_total_charges; // for computation below
840 // Loops 2320 and 2330*, other subscriber/payer information.
841 // Remember that insurance index 0 is always for the payer being billed
842 // by this claim, and 1 and above are always for the "other" payers.
844 for ($ins = 1; $ins < $claim->payerCount(); ++ $ins) {
845 $tmp1 = $claim->claimType($ins);
846 $tmp2 = 'C1'; // Here a kludge. See page 321.
847 if ($tmp1 === 'CI') {
848 $tmp2 = 'C1';
850 if ($tmp1 === 'AM') {
851 $tmp2 = 'AP';
853 if ($tmp1 === 'HM') {
854 $tmp2 = 'HM';
856 if ($tmp1 === 'MB') {
857 $tmp2 = 'MB';
859 if ($tmp1 === 'MA') {
860 $tmp2 = 'MA';
862 if ($tmp1 === 'MC') {
863 $tmp2 = 'MC';
865 if ($tmp1 === '09') {
866 $tmp2 = 'PP';
868 ++ $edicount;
870 $out .= "SBR" . // Loop 2320, Subscriber Information - page 297/318
871 "*" . $claim->payerSequence($ins) .
872 "*" . $claim->insuredRelationship($ins) .
873 "*" . $claim->groupNumber($ins) .
874 "*" . (($claim->groupNumber($ins)) ? '' : $claim->groupName($ins)) .
875 "*" . ($claim->insuredTypeCode($ins) ? $claim->insuredTypeCode($ins) : $tmp2) .
876 "*" .
877 "*" .
878 "*" .
879 "*" . $claim->claimType($ins) .
880 "~\n";
882 // Things that apply only to previous payers, not future payers.
884 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
885 // Generate claim-level adjustments.
886 $aarr = $claim->payerAdjustments($ins);
887 foreach ($aarr as $a) {
888 ++ $edicount;
889 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 301/323.
890 "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
893 $payerpaid = $claim->payerTotals($ins);
894 ++ $edicount;
895 $out .= "AMT" . // Previous payer's paid amount. Page 307/332.
896 "*D" . "*" . $payerpaid[1] . "~\n";
898 // Segment AMT*A8 (COB Total Non-Covered Amount) omitted.
899 // Segment AMT*EAF (Remaining Patient Liability) omitted.
900 } // End of things that apply only to previous payers.
902 ++ $edicount;
903 $out .= "OI" . // Other Insurance Coverage Information. Page 310/344.
904 "*" . "*" . "*" . ($claim->billingFacilityAssignment($ins) ? 'Y' : 'N') .
905 // For this next item, the 5010 example in the spec does not match its
906 // description. So this might be wrong.
907 "*" .
908 "*" .
909 "*" .
910 "Y" .
911 "~\n";
913 // Segment MOA (Medicare Outpatient Adjudication) omitted.
914 ++$edicount;
915 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 315/350.
916 "*" . "IL" .
917 "*" . "1" .
918 "*" . $claim->insuredLastName($ins) .
919 "*" . $claim->insuredFirstName($ins) .
920 "*" . $claim->insuredMiddleName($ins) .
921 "*" .
922 "*" .
923 "*" . "MI" .
924 "*" . $claim->policyNumber($ins) .
925 "~\n";
927 ++$edicount;
928 $out .= "N3" .
929 "*" . $claim->insuredStreet($ins) .
930 "~\n";
932 ++$edicount;
933 $out .= "N4" .
934 "*" . $claim->insuredCity($ins) .
935 "*" . $claim->insuredState($ins) .
936 "*" . $claim->x12Zip($claim->insuredZip($ins)) .
937 "~\n";
939 // Segment REF (Other Subscriber Secondary Identification) omitted.
940 ++$edicount;
941 $payerName = substr($claim->payerName($ins), 0, 60);
942 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 322/359.
943 "*" . "PR" .
944 "*" . "2" .
945 "*" . $payerName .
946 "*" .
947 "*" .
948 "*" .
949 "*" .
950 "*" . "PI" .
951 "*" . $claim->payerID($ins) .
952 "~\n";
954 if (!$claim->payerID($ins)) {
955 $log .= "*** Payer ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
958 ++$edicount;
959 $out .= "N3" .
960 "*" . $claim->payerStreet($ins) .
961 "~\n";
963 ++$edicount;
964 $out .= "N4" .
965 "*" . $claim->payerCity($ins) .
966 "*" . $claim->payerState($ins) .
967 "*" . $claim->x12Zip($claim->payerZip($ins)) .
968 "~\n";
970 // Segment DTP*573 (Claim Check or Remittance Date) omitted.
971 // Segment REF (Other Payer Secondary Identifier) omitted.
972 // Segment REF*G1 (Other Payer Prior Authorization Number) omitted.
973 // Segment REF*9F (Other Payer Referral Number) omitted.
974 // Segment REF*T4 (Other Payer Claim Adjustment Indicator) omitted.
975 // Segment REF*F8 (Other Payer Claim Control Number) omitted.
976 // 2330C-I loops Omitted
977 } // End loops 2320/2330*.
979 $loopcount = 0;
981 // Procedure loop starts here.
984 for ($tlh = 0; $tlh < $proccount; ++ $tlh) {
985 $tmp = $claim->procs[$tlh][code_text];
987 if ($claim->procs[$tlh][code_type] == 'HCPCS') {
988 $tmpcode = '3';
989 } else {
990 $tmpcode = '1';
992 $getrevcd = $claim->cptCode($tlh);
993 $sql = "SELECT * FROM codes WHERE code_type = ? and code = ? ORDER BY revenue_code DESC";
994 $revcode[$tlh] = sqlQuery($sql, array(
995 $tmpcode,
996 $getrevcd
1001 for ($prockey = 0; $prockey < $proccount; ++ $prockey) {
1002 $os = 99 + ($loopcount * 8); // Form revenue code offset
1003 $dosos = 102 + ($loopcount * 8); // Procedure date of service form start offset-add 8 for loop
1004 ++ $loopcount;
1006 ++ $edicount;
1007 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
1008 "*" . "$loopcount" . "~\n";
1010 ++ $edicount;
1012 // Revenue code from form
1014 $tmp = $ub04id[$os]; //$revcode[$prockey][revenue_code];
1015 if (empty($tmp)) {
1016 $log .= "*** Error: Missing Revenue Code for " . $claim->cptKey($prockey) . "!\n";
1018 // Institutional Service Line.
1020 $out .= "SV2" . "*" . $tmp . // revenue code
1022 "*" . "HC:" . $claim->cptKey($prockey) . "*" . sprintf('%.2f', $claim->cptCharges($prockey)) . "*UN" . "*" . $claim->cptUnits($prockey) . "*" . "*" . "*";
1024 $out .= "~\n";
1026 if (! $claim->cptCharges($prockey)) {
1027 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
1030 // Segment SV5 (Durable Medical Equipment Service) omitted.
1031 // Segment PWK (Line Supplemental Information) omitted.
1032 // Segment PWK (Durable Medical Equipment Certificate of Medical Necessity Indicator) omitted.
1033 // Segment CR1 (Ambulance Transport Information) omitted.
1034 // Segment CR3 (Durable Medical Equipment Certification) omitted.
1035 // Segment CRC (Ambulance Certification) omitted.
1036 // Segment CRC (Hospice Employee Indicator) omitted.
1037 // Segment CRC (Condition Indicator / Durable Medical Equipment) omitted.
1039 ++ $edicount;
1041 $out .= "DTP" . // Date of Service. Needs to be when service preformed.
1042 "*" . "472" . "*" . "D8" . "*" . $ub04id[$dosos] . "~\n"; //$claim->serviceDate()
1044 $testnote = rtrim($claim->cptNotecodes($prockey));
1045 if (! empty($testnote)) {
1046 ++ $edicount;
1047 $out .= "NTE" . // Explain Unusual Circumstances.
1048 "*ADD" . "*" . $claim->cptNotecodes($prockey) . "~\n";
1051 // Segment DTP*471 (Prescription Date) omitted.
1052 // Segment DTP*607 (Revision/Recertification Date) omitted.
1053 // Segment DTP*463 (Begin Therapy Date) omitted.
1054 // Segment DTP*461 (Last Certification Date) omitted.
1055 // Segment DTP*304 (Last Seen Date) omitted.
1056 // Segment DTP (Test Date) omitted.
1057 // Segment DTP*011 (Shipped Date) omitted.
1058 // Segment DTP*455 (Last X-Ray Date) omitted.
1059 // Segment DTP*454 (Initial Treatment Date) omitted.
1060 // Segment QTY (Ambulance Patient Count) omitted.
1061 // Segment QTY (Obstetric Anesthesia Additional Units) omitted.
1062 // Segment MEA (Test Result) omitted.
1063 // Segment CN1 (Contract Information) omitted.
1064 // Segment REF*9B (Repriced Line Item Reference Number) omitted.
1065 // Segment REF*9D (Adjusted Repriced Line Item Reference Number) omitted.
1066 // Segment REF*G1 (Prior Authorization) omitted.
1067 // Segment REF*6R (Line Item Control Number) omitted.
1068 // (Really oughta have this for robust 835 posting!)
1069 // Segment REF*EW (Mammography Certification Number) omitted.
1070 // Segment REF*X4 (CLIA Number) omitted.
1071 // Segment REF*F4 (Referring CLIA Facility Identification) omitted.
1072 // Segment REF*BT (Immunization Batch Number) omitted.
1073 // Segment REF*9F (Referral Number) omitted.
1074 // Segment AMT*GT (Sales Tax Amount) omitted.
1075 // Segment AMT*F4 (Postage Claimed Amount) omitted.
1076 // Segment K3 (File Information) omitted.
1077 // Segment NTE (Line Note) omitted.
1078 // Segment NTE (Third Party Organization Notes) omitted.
1079 // Segment PS1 (Purchased Service Information) omitted.
1080 // Segment HCP (Line Pricing/Repricing Information) omitted.
1082 // Loop 2410, Drug Information. Medicaid insurers seem to want this
1083 // with HCPCS codes.
1085 $ndc = $claim->cptNDCID($prockey);
1087 if ($ndc) {
1088 ++$edicount;
1089 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
1090 "*" . // Per addendum, LIN01 is not used.
1091 "*" . "N4" .
1092 "*" . $ndc .
1093 "~\n";
1095 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp) && !preg_match('/^\d{11}$/', $ndc)) {
1096 $log .= "*** NDC code '$ndc' has invalid format!\n";
1099 ++$edicount;
1100 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
1101 "*" .
1102 "*" .
1103 "*" .
1104 "*" . $claim->cptNDCQuantity($prockey) .
1105 "*" . $claim->cptNDCUOM($prockey) .
1106 // Note: 5010 documents "ME" (Milligrams) as an additional unit of measure.
1107 "~\n";
1110 // Segment REF (Prescription or Compound Drug Association Number) omitted.
1112 // Loop 2420A, Rendering Provider (service-specific). (Operating Physician Name for 837I)
1113 // Used if the rendering provider for this service line is different
1114 // from that in loop 2310B.
1116 if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
1117 ++$edicount;
1118 $out .= "NM1" . // Loop 2310B Rendering Provider
1119 "*" . "82" .
1120 "*" . "1" .
1121 "*" . $claim->providerLastName($prockey) .
1122 "*" . $claim->providerFirstName($prockey) .
1123 "*" . $claim->providerMiddleName($prockey) .
1124 "*" .
1125 "*";
1126 if ($claim->providerNPI($prockey)) {
1127 $out .=
1128 "*" . "XX" .
1129 "*" . $claim->providerNPI($prockey);
1130 } else {
1131 $log .= "*** Rendering provider has no NPI.\n";
1133 $out .= "~\n";
1135 if ($claim->providerTaxonomy($prockey)) {
1136 ++$edicount;
1137 $out .= "PRV" .
1138 "*" . "PE" . // PErforming provider
1139 "*" . "PXC" .
1140 "*" . $claim->providerTaxonomy($prockey) .
1141 "~\n";
1144 // Segment PRV*PE (Rendering Provider Specialty Information) omitted.
1145 // Segment REF (Rendering Provider Secondary Identification) omitted.
1146 // Segment NM1 (Purchased Service Provider Name) omitted.
1147 // Segment REF (Purchased Service Provider Secondary Identification) omitted.
1148 // Segment NM1,N3,N4 (Service Facility Location) omitted.
1149 // Segment REF (Service Facility Location Secondary Identification) omitted.
1150 // Segment NM1 (Supervising Provider Name) omitted.
1151 // Segment REF (Supervising Provider Secondary Identification) omitted.
1152 // Segment NM1,N3,N4 (Ordering Provider) omitted.
1153 // Segment REF (Ordering Provider Secondary Identification) omitted.
1154 // Segment PER (Ordering Provider Contact Information) omitted.
1155 // Segment NM1 (Referring Provider Name) omitted.
1156 // Segment REF (Referring Provider Secondary Identification) omitted.
1157 // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
1158 // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
1160 // REF*1C is required here for the Medicare provider number if NPI was
1161 // specified in NM109. Not sure if other payers require anything here.
1162 if ($claim->providerNumberType($prockey) == "G2") {
1163 ++$edicount; $out .= "REF" . "*" . $claim->providerNumberType($prockey) .
1164 "*" . $claim->providerNumber($prockey) . "~\n";
1166 } // provider exception
1168 // Loop 2430, adjudication by previous payers.
1170 for ($ins = 1; $ins < $claim->payerCount(); ++ $ins) {
1171 if ($claim->payerSequence($ins) > $claim->payerSequence()) {
1172 continue; // payer is future, not previous
1175 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
1176 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
1178 if ($payerpaid[1] == 0 && ! count($aarr)) {
1179 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no payments or adjustments from previous payer!\n";
1180 continue;
1183 ++ $edicount;
1184 $out .= "SVD" . // Service line adjudication. Page 554.
1185 "*" . $claim->payerID($ins) . "*" . $payerpaid[1] . "*HC:" . $claim->cptKey($prockey) . "*" . "*" . $claim->cptUnits($prockey) . "~\n";
1187 $tmpdate = $payerpaid[0];
1188 foreach ($aarr as $a) {
1189 ++ $edicount;
1190 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
1191 "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
1192 if (! $tmpdate) {
1193 $tmpdate = $a[0];
1197 if ($tmpdate) {
1198 ++ $edicount;
1199 $out .= "DTP" . // Previous payer's line adjustment date. Page 493/566.
1200 "*573" . "*D8" . "*$tmpdate" . "~\n";
1203 // Segment AMT*EAF (Remaining Patient Liability) omitted.
1204 // Segment LQ (Form Identification Code) omitted.
1205 // Segment FRM (Supporting Documentation) omitted.
1206 } // end loop 2430
1207 } // end this procedure
1209 ++ $edicount;
1210 $out .= "SE" . // SE Trailer
1211 "*$edicount" . "*0021" . "~\n";
1213 $out .= "GE" . // GE Trailer
1214 "*1" . "*1" . "~\n";
1216 $out .= "IEA" . // IEA Trailer
1217 "*1" . "*000000001" . "~\n";
1219 // Remove any trailing empty fields (delimiters) from each segment.
1220 $out = preg_replace('/\*+~/', '~', $out);
1222 $log .= "\n";
1223 return $out;