graphic.
[openemr.git] / library / gen_x12_837.inc.php
blob5537a132c36afe11607e11eaa7a395b913490349
1 <?php
2 // Copyright (C) 2007-2008 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once("Claim.class.php");
11 function gen_x12_837($pid, $encounter, &$log) {
13 $today = time();
14 $out = '';
15 $claim = new Claim($pid, $encounter);
16 $edicount = 0;
18 $log .= "Generating claim $pid-$encounter for " .
19 $claim->patientFirstName() . ' ' .
20 $claim->patientMiddleName() . ' ' .
21 $claim->patientLastName() . ' on ' .
22 date('Y-m-d H:i', $today) . ".\n";
24 $out .= "ISA" .
25 "*00" .
26 "* " .
27 "*00" .
28 "* " .
29 "*ZZ" .
30 "*" . $claim->x12gssenderid() .
31 "*ZZ" .
32 "*" . $claim->x12gsreceiverid() .
33 "*030911" .
34 "*1630" .
35 "*U" .
36 "*00401" .
37 "*000000001" .
38 "*0" .
39 "*P" .
40 "*:" .
41 "~\n";
43 $out .= "GS" .
44 "*HC" .
45 "*" . $claim->x12gssenderid() .
46 "*" . $claim->x12gsreceiverid() .
47 "*" . date('Ymd', $today) .
48 "*" . date('Hi', $today) .
49 "*1" .
50 "*X" .
51 "*" . $claim->x12gsversionstring() .
52 "~\n";
54 ++$edicount;
55 $out .= "ST" .
56 "*837" .
57 "*0021" .
58 "~\n";
60 ++$edicount;
61 $out .= "BHT" .
62 "*0019" .
63 "*00" .
64 "*0123" .
65 "*" . date('Ymd', $today) .
66 "*1023" .
67 "*CH" .
68 "~\n";
70 ++$edicount;
71 $out .= "REF" .
72 "*87" .
73 "*" . $claim->x12gsversionstring() .
74 "~\n";
76 ++$edicount;
77 $out .= "NM1" . // Loop 1000A Submitter
78 "*41" .
79 "*2" .
80 "*" . $claim->billingFacilityName() .
81 "*" .
82 "*" .
83 "*" .
84 "*" .
85 "*46" .
86 "*" . $claim->billingFacilityETIN() .
87 "~\n";
89 ++$edicount;
90 $out .= "PER" .
91 "*IC" .
92 "*" . $claim->billingContactName() .
93 "*TE" .
94 "*" . $claim->billingContactPhone() .
95 "~\n";
97 ++$edicount;
98 $out .= "NM1" . // Loop 1000B Receiver
99 "*40" .
100 "*2" .
101 "*" . $claim->clearingHouseName() .
102 "*" .
103 "*" .
104 "*" .
105 "*" .
106 "*46" .
107 "*" . $claim->clearingHouseETIN() .
108 "~\n";
110 $HLcount = 1;
112 ++$edicount;
113 $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop
114 "*$HLcount" .
115 "*" .
116 "*20" .
117 "*1" .
118 "~\n";
120 $HLBillingPayToProvider = $HLcount++;
122 ++$edicount;
123 $out .= "NM1" . // Loop 2010AA Billing Provider
124 "*85" .
125 "*2" .
126 "*" . $claim->billingFacilityName() .
127 "*" .
128 "*" .
129 "*" .
130 "*";
131 if ($claim->billingFacilityNPI()) {
132 $out .= "*XX*" . $claim->billingFacilityNPI();
133 } else {
134 $log .= "*** Billing facility has no NPI.\n";
135 $out .= "*24*" . $claim->billingFacilityETIN();
137 $out .= "~\n";
139 ++$edicount;
140 $out .= "N3" .
141 "*" . $claim->billingFacilityStreet() .
142 "~\n";
144 ++$edicount;
145 $out .= "N4" .
146 "*" . $claim->billingFacilityCity() .
147 "*" . $claim->billingFacilityState() .
148 "*" . $claim->billingFacilityZip() .
149 "~\n";
151 // Add a REF*EI*<ein> segment if NPI was specified in the NM1 above.
152 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
153 ++$edicount;
154 $out .= "REF" .
155 "*EI" .
156 "*" . $claim->billingFacilityETIN() .
157 "~\n";
160 ++$edicount;
161 $out .= "REF" .
162 "*" . $claim->providerNumberType() .
163 "*" . $claim->providerNumber() .
164 "~\n";
166 ++$edicount;
167 $out .= "NM1" . // Loop 2010AB Pay-To Provider
168 "*87" .
169 "*2" .
170 "*" . $claim->billingFacilityName() .
171 "*" .
172 "*" .
173 "*" .
174 "*";
175 if ($claim->billingFacilityNPI())
176 $out .= "*XX*" . $claim->billingFacilityNPI();
177 else
178 $out .= "*24*" . $claim->billingFacilityETIN();
179 $out .= "~\n";
181 ++$edicount;
182 $out .= "N3" .
183 "*" . $claim->billingFacilityStreet() .
184 "~\n";
186 ++$edicount;
187 $out .= "N4" .
188 "*" . $claim->billingFacilityCity() .
189 "*" . $claim->billingFacilityState() .
190 "*" . $claim->billingFacilityZip() .
191 "~\n";
193 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
194 ++$edicount;
195 $out .= "REF" .
196 "*EI" .
197 "*" . $claim->billingFacilityETIN() .
198 "~\n";
201 $PatientHL = 0;
203 ++$edicount;
204 $out .= "HL" . // Loop 2000B Subscriber HL Loop
205 "*$HLcount" .
206 "*$HLBillingPayToProvider" .
207 "*22" .
208 "*$PatientHL" .
209 "~\n";
211 $HLSubscriber = $HLcount++;
213 if (!$claim->payerSequence()) {
214 $log .= "*** Error: Insurance information is missing!\n";
216 ++$edicount;
217 $out .= "SBR" . // Subscriber Information
218 "*" . $claim->payerSequence() .
219 "*" . $claim->insuredRelationship() .
220 "*" . $claim->groupNumber() .
221 "*" . $claim->groupName() .
222 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
223 "*" .
224 "*" .
225 "*" .
226 "*" . $claim->claimType() . // Zirmed replaces this
227 "~\n";
229 ++$edicount;
230 $out .= "NM1" . // Loop 2010BA Subscriber
231 "*IL" .
232 "*1" .
233 "*" . $claim->insuredLastName() .
234 "*" . $claim->insuredFirstName() .
235 "*" . $claim->insuredMiddleName() .
236 "*" .
237 "*" .
238 "*MI" .
239 "*" . $claim->policyNumber() .
240 "~\n";
242 ++$edicount;
243 $out .= "N3" .
244 "*" . $claim->insuredStreet() .
245 "~\n";
247 ++$edicount;
248 $out .= "N4" .
249 "*" . $claim->insuredCity() .
250 "*" . $claim->insuredState() .
251 "*" . $claim->insuredZip() .
252 "~\n";
254 ++$edicount;
255 $out .= "DMG" .
256 "*D8" .
257 "*" . $claim->insuredDOB() .
258 "*" . $claim->insuredSex() .
259 "~\n";
261 ++$edicount;
262 $out .= "NM1" . // Loop 2010BB Payer
263 "*PR" .
264 "*2" .
265 "*" . $claim->payerName() .
266 "*" .
267 "*" .
268 "*" .
269 "*" .
270 "*PI" .
271 "*" . $claim->payerID() . // Zirmed ignores this if using Payer Name Matching.
272 "~\n";
274 // if (!$claim->payerID()) {
275 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
276 // }
278 ++$edicount;
279 $out .= "N3" .
280 "*" . $claim->payerStreet() .
281 "~\n";
283 ++$edicount;
284 $out .= "N4" .
285 "*" . $claim->payerCity() .
286 "*" . $claim->payerState() .
287 "*" . $claim->payerZip() .
288 "~\n";
290 if (! $claim->isSelfOfInsured()) {
291 ++$edicount;
292 $out .= "HL" . // Loop 2000C Patient Information
293 "*$HLcount" .
294 "*$HLSubscriber" .
295 "*23" .
296 "*0" .
297 "~\n";
299 $HLcount++;
301 ++$edicount;
302 $out .= "PAT" .
303 "*" . $claim->insuredRelationship() .
304 "~\n";
306 ++$edicount;
307 $out .= "NM1" . // Loop 2010CA Patient
308 "*QC" .
309 "*1" .
310 "*" . $claim->patientLastName() .
311 "*" . $claim->patientFirstName() .
312 "*" . $claim->patientMiddleName() .
313 "~\n";
315 ++$edicount;
316 $out .= "N3" .
317 "*" . $claim->patientStreet() .
318 "~\n";
320 ++$edicount;
321 $out .= "N4" .
322 "*" . $claim->patientCity() .
323 "*" . $claim->patientState() .
324 "*" . $claim->patientZip() .
325 "~\n";
327 ++$edicount;
328 $out .= "DMG" .
329 "*D8" .
330 "*" . $claim->patientDOB() .
331 "*" . $claim->patientSex() .
332 "~\n";
333 } // end of patient different from insured
335 $proccount = $claim->procCount();
337 $clm_total_charges = 0;
338 for ($prockey = 0; $prockey < $proccount; ++$prockey) {
339 $clm_total_charges += $claim->cptCharges($prockey);
342 if (!$clm_total_charges) {
343 $log .= "*** This claim has no charges!\n";
346 ++$edicount;
347 $out .= "CLM" . // Loop 2300 Claim
348 "*$pid-$encounter" .
349 "*" . sprintf("%.2f",$clm_total_charges) . // Zirmed computes and replaces this
350 "*" .
351 "*" .
352 "*" . $claim->facilityPOS() . "::1" .
353 "*Y" .
354 "*A" .
355 "*Y" .
356 "*Y" .
357 "*C" .
358 "~\n";
360 ++$edicount;
361 $out .= "DTP" . // Date of Onset
362 "*431" .
363 "*D8" .
364 "*" . $claim->onsetDate() .
365 "~\n";
367 if (strcmp($claim->facilityPOS(),'21') == 0) {
368 ++$edicount;
369 $out .= "DTP" . // Date of Hospitalization
370 "*435" .
371 "*D8" .
372 "*" . $claim->onsetDate() .
373 "~\n";
376 $patientpaid = $claim->patientPaidAmount();
377 if ($patientpaid != 0) {
378 ++$edicount;
379 $out .= "AMT" . // Patient paid amount. Page 220.
380 "*F5" .
381 "*" . $patientpaid .
382 "~\n";
385 if ($claim->priorAuth()) {
386 ++$edicount;
387 $out .= "REF" . // Prior Authorization Number
388 "*G1" .
389 "*" . $claim->priorAuth() .
390 "~\n";
393 if ($claim->cliaCode()) {
394 // Required by Medicare when in-house labs are done.
395 ++$edicount;
396 $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
397 "*X4" .
398 "*" . $claim->cliaCode() .
399 "~\n";
402 $da = $claim->diagArray();
403 ++$edicount;
404 $out .= "HI"; // Health Diagnosis Codes
405 $diag_type_code = 'BK';
406 foreach ($da as $diag) {
407 $out .= "*$diag_type_code:" . $diag;
408 $diag_type_code = 'BF';
410 $out .= "~\n";
412 if ($claim->referrerLastName()) {
413 // Medicare requires referring provider's name and UPIN.
414 ++$edicount;
415 $out .= "NM1" . // Loop 2310A Referring Provider
416 "*DN" .
417 "*1" .
418 "*" . $claim->referrerLastName() .
419 "*" . $claim->referrerFirstName() .
420 "*" . $claim->referrerMiddleName() .
421 "*" .
422 "*";
423 if ($claim->referrerNPI()) { $out .=
424 "*XX" .
425 "*" . $claim->referrerNPI();
426 } else { $out .=
427 "*34" .
428 "*" . $claim->referrerSSN();
430 $out .= "~\n";
432 ++$edicount;
433 $out .= "REF" . // Referring Provider Secondary Identification
434 "*1G" .
435 "*" . $claim->referrerUPIN() .
436 "~\n";
439 ++$edicount;
440 $out .= "NM1" . // Loop 2310B Rendering Provider
441 "*82" .
442 "*1" .
443 "*" . $claim->providerLastName() .
444 "*" . $claim->providerFirstName() .
445 "*" . $claim->providerMiddleName() .
446 "*" .
447 "*";
448 if ($claim->providerNPI()) { $out .=
449 "*XX" .
450 "*" . $claim->providerNPI();
451 } else { $out .=
452 "*34" .
453 "*" . $claim->providerSSN();
454 $log .= "*** Rendering provider has no NPI.\n";
456 $out .= "~\n";
458 ++$edicount;
459 $out .= "PRV" . // Rendering Provider Information
460 "*PE" .
461 "*ZZ" .
462 "*207Q00000X" .
463 "~\n";
465 // REF*1C is required here for the Medicare provider number if NPI was
466 // specified in NM109. Not sure if other payers require anything here.
467 if ($claim->providerNumber()) {
468 ++$edicount;
469 $out .= "REF" .
470 "*" . $claim->providerNumberType() .
471 "*" . $claim->providerNumber() .
472 "~\n";
475 ++$edicount;
476 $out .= "NM1" . // Loop 2310D Service Location
477 "*77" .
478 "*2" .
479 "*" . $claim->facilityName() .
480 "*" .
481 "*" .
482 "*" .
483 "*";
484 if ($claim->facilityNPI()) { $out .=
485 "*XX*" . $claim->facilityNPI();
486 } else { $out .=
487 "*24*" . $claim->facilityETIN();
488 $log .= "*** Service location has no NPI.\n";
490 $out .= "~\n";
492 ++$edicount;
493 $out .= "N3" .
494 "*" . $claim->facilityStreet() .
495 "~\n";
497 ++$edicount;
498 $out .= "N4" .
499 "*" . $claim->facilityCity() .
500 "*" . $claim->facilityState() .
501 "*" . $claim->facilityZip() .
502 "~\n";
504 $prev_pt_resp = $clm_total_charges; // for computation below
506 // Loops 2320 and 2330*, other subscriber/payer information.
508 for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
510 ++$edicount;
511 $out .= "SBR" . // Loop 2320, Subscriber Information - page 318
512 "*" . $claim->payerSequence($ins) .
513 "*" . $claim->insuredRelationship($ins) .
514 "*" . $claim->groupNumber($ins) .
515 "*" . $claim->groupName($ins) .
516 "*" .
517 "*" .
518 "*" .
519 "*" .
520 "*" . $claim->claimType($ins) .
521 "~\n";
523 // Things that apply only to previous payers, not future payers.
525 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
527 // Generate claim-level adjustments.
528 $aarr = $claim->payerAdjustments($ins);
529 foreach ($aarr as $a) {
530 ++$edicount;
531 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 323.
532 "*" . $a[1] .
533 "*" . $a[2] .
534 "*" . $a[3] .
535 "~\n";
538 $payerpaid = $claim->payerTotals($ins);
539 ++$edicount;
540 $out .= "AMT" . // Previous payer's paid amount. Page 332.
541 "*D" .
542 "*" . $payerpaid[1] .
543 "~\n";
545 // Patient responsibility amount as of this previous payer.
546 $prev_pt_resp -= $payerpaid[1]; // reduce by payments
547 $prev_pt_resp -= $payerpaid[2]; // reduce by adjustments
549 ++$edicount;
550 $out .= "AMT" . // Patient responsibility amount per previous payer. Page 335.
551 "*F2" .
552 "*" . sprintf('%.2f', $prev_pt_resp) .
553 "~\n";
555 } // End of things that apply only to previous payers.
557 ++$edicount;
558 $out .= "DMG" . // Other subscriber demographic information. Page 342.
559 "*D8" .
560 "*" . $claim->insuredDOB($ins) .
561 "*" . $claim->insuredSex($ins) .
562 "~\n";
564 ++$edicount;
565 $out .= "OI" . // Other Insurance Coverage Information. Page 344.
566 "*" .
567 "*" .
568 "*Y" .
569 "*B" .
570 "*" .
571 "*Y" .
572 "~\n";
574 ++$edicount;
575 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 350.
576 "*IL" .
577 "*1" .
578 "*" . $claim->insuredLastName($ins) .
579 "*" . $claim->insuredFirstName($ins) .
580 "*" . $claim->insuredMiddleName($ins) .
581 "*" .
582 "*" .
583 "*MI" .
584 "*" . $claim->policyNumber($ins) .
585 "~\n";
587 ++$edicount;
588 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 359.
589 "*PR" .
590 "*2" .
591 "*" . $claim->payerName($ins) .
592 "*" .
593 "*" .
594 "*" .
595 "*" .
596 "*PI" .
597 "*" . $claim->payerID($ins) .
598 "~\n";
600 // if (!$claim->payerID($ins)) {
601 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
602 // }
604 } // End loops 2320/2330*.
606 $loopcount = 0;
608 // Procedure loop starts here.
610 for ($prockey = 0; $prockey < $proccount; ++$prockey) {
611 ++$loopcount;
613 ++$edicount;
614 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
615 "*$loopcount" .
616 "~\n";
618 ++$edicount;
619 $out .= "SV1" . // Professional Service. Page 400.
620 "*HC:" . $claim->cptKey($prockey) .
621 "*" . sprintf('%.2f', $claim->cptCharges($prockey)) .
622 "*UN" .
623 "*" . $claim->cptUnits($prockey) .
624 "*" .
625 "*" .
626 "*";
627 $dia = $claim->diagIndexArray($prockey);
628 $separator = '';
629 foreach ($dia as $dindex) {
630 $out .= $separator . $dindex;
631 $separator = ':';
633 $out .= "~\n";
635 if (!$claim->cptCharges($prockey)) {
636 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
639 if (empty($dia)) {
640 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
643 ++$edicount;
644 $out .= "DTP" . // Date of Service. Page 435.
645 "*472" .
646 "*D8" .
647 "*" . $claim->serviceDate() .
648 "~\n";
650 // Loop 2410, Drug Information. Medicaid insurers seem to want this
651 // with HCPCS codes.
653 $ndc = $claim->cptNDCID($prockey);
654 if ($ndc) {
655 ++$edicount;
656 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
657 "*4" .
658 "*N4" .
659 "*" . $ndc .
660 "~\n";
662 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp)) {
663 $log .= "*** NDC code '$ndc' has invalid format!\n";
666 ++$edicount;
667 $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
668 if (!$tmpunits) $tmpunits = 1;
669 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
670 "*" .
671 "*" .
672 "*" . sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits) .
673 "*" . $claim->cptNDCQuantity($prockey) .
674 "*" . $claim->cptNDCUOM($prockey) .
675 "~\n";
678 // Loop 2430, adjudication by previous payers.
680 for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
681 if ($claim->payerSequence($ins) > $claim->payerSequence())
682 continue; // payer is future, not previous
684 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
685 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
687 if ($payerpaid[1] == 0 && !count($aarr)) {
688 $log .= "*** Procedure '" . $claim->cptKey($prockey) .
689 "' has no payments or adjustments from previous payer!\n";
690 continue;
693 ++$edicount;
694 $out .= "SVD" . // Service line adjudication. Page 554.
695 "*" . $claim->payerID($ins) .
696 "*" . $payerpaid[1] .
697 "*HC:" . $claim->cptKey($prockey) .
698 "*" .
699 "*" . $claim->cptUnits($prockey) .
700 "~\n";
702 $tmpdate = $payerpaid[0];
703 foreach ($aarr as $a) {
704 ++$edicount;
705 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
706 "*" . $a[1] .
707 "*" . $a[2] .
708 "*" . $a[3] .
709 "~\n";
710 if (!$tmpdate) $tmpdate = $a[0];
713 if ($tmpdate) {
714 ++$edicount;
715 $out .= "DTP" . // Previous payer's line adjustment date. Page 566.
716 "*573" .
717 "*D8" .
718 "*$tmpdate" .
719 "~\n";
721 } // end loop 2430
722 } // end this procedure
724 ++$edicount;
725 $out .= "SE" . // SE Trailer
726 "*$edicount" .
727 "*0021" .
728 "~\n";
730 $out .= "GE" . // GE Trailer
731 "*1" .
732 "*1" .
733 "~\n";
735 $out .= "IEA" . // IEA Trailer
736 "*1" .
737 "*000000001" .
738 "~\n";
740 $log .= "\n";
741 return $out;