2 // Copyright (C) 2007-2011 Rod Roark <rod@sunsetsystems.com>
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once("Claim.class.php");
11 function gen_x12_837($pid, $encounter, &$log, $encounter_claim=false) {
15 $claim = new Claim($pid, $encounter);
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";
29 "*" . $claim->x12gsisa05() .
30 "*" . $claim->x12gssenderid() .
31 "*" . $claim->x12gsisa07() .
32 "*" . $claim->x12gsreceiverid() .
38 "*" . $claim->x12gsisa14() .
39 "*" . $claim->x12gsisa15() .
45 "*" . $claim->x12gsgs02() .
46 "*" . trim($claim->x12gsreceiverid()) .
47 "*" . date('Ymd', $today) .
48 "*" . date('Hi', $today) .
51 "*" . $claim->x12gsversionstring() .
65 "*" . date('Ymd', $today) .
67 ($encounter_claim ?
"*RP" : "*CH") .
73 "*" . $claim->x12gsversionstring() .
77 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
78 $billingFacilityName=substr($claim->billingFacilityName(),0,35);
79 $out .= "NM1" . // Loop 1000A Submitter
82 "*" . $billingFacilityName .
88 if (trim($claim->x12gsreceiverid()) == '470819582') { // if ECLAIMS EDI
89 $out .= "*" . $claim->clearingHouseETIN();
91 $out .= "*" . $claim->billingFacilityETIN();
98 "*" . $claim->billingContactName() .
100 "*" . $claim->billingContactPhone();
101 if ($claim->x12gsper06()) {
102 $out .= "*ED*" . $claim->x12gsper06();
107 $out .= "NM1" . // Loop 1000B Receiver
110 "*" . $claim->clearingHouseName() .
116 "*" . $claim->clearingHouseETIN() .
122 $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop
129 $HLBillingPayToProvider = $HLcount++
;
132 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
133 $billingFacilityName=substr($claim->billingFacilityName(),0,35);
134 $out .= "NM1" . // Loop 2010AA Billing Provider
137 "*" . $billingFacilityName .
142 if ($claim->billingFacilityNPI()) {
143 $out .= "*XX*" . $claim->billingFacilityNPI();
145 $log .= "*** Billing facility has no NPI.\n";
146 $out .= "*24*" . $claim->billingFacilityETIN();
152 "*" . $claim->billingFacilityStreet() .
157 "*" . $claim->billingFacilityCity() .
158 "*" . $claim->billingFacilityState() .
159 "*" . $claim->billingFacilityZip() .
162 // Add a REF*EI*<ein> segment if NPI was specified in the NM1 above.
163 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
166 if($claim->federalIdType()){
167 $out .= "*" . $claim->federalIdType();
170 $out .= "*EI";//For dealing with the situation before adding selection for TaxId type In facility ie default to EIN.
172 $out .= "*" . $claim->billingFacilityETIN() .
176 if ($claim->providerNumberType() && $claim->providerNumber()) {
179 "*" . $claim->providerNumberType() .
180 "*" . $claim->providerNumber() .
183 else if ($claim->providerNumber()) {
184 $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
188 //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
189 $billingFacilityName=substr($claim->billingFacilityName(),0,35);
190 $out .= "NM1" . // Loop 2010AB Pay-To Provider
193 "*" . $billingFacilityName .
198 if ($claim->billingFacilityNPI())
199 $out .= "*XX*" . $claim->billingFacilityNPI();
201 $out .= "*24*" . $claim->billingFacilityETIN();
206 "*" . $claim->billingFacilityStreet() .
211 "*" . $claim->billingFacilityCity() .
212 "*" . $claim->billingFacilityState() .
213 "*" . $claim->billingFacilityZip() .
216 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
220 "*" . $claim->billingFacilityETIN() .
227 $out .= "HL" . // Loop 2000B Subscriber HL Loop
229 "*$HLBillingPayToProvider" .
234 $HLSubscriber = $HLcount++
;
236 if (!$claim->payerSequence()) {
237 $log .= "*** Error: Insurance information is missing!\n";
240 $out .= "SBR" . // Subscriber Information
241 "*" . $claim->payerSequence() .
242 "*" . $claim->insuredRelationship() .
243 "*" . $claim->groupNumber() .
244 "*" . $claim->groupName() .
245 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
249 "*" . $claim->claimType() . // Zirmed replaces this
253 $out .= "NM1" . // Loop 2010BA Subscriber
256 "*" . $claim->insuredLastName() .
257 "*" . $claim->insuredFirstName() .
258 "*" . $claim->insuredMiddleName() .
262 "*" . $claim->policyNumber() .
267 "*" . $claim->insuredStreet() .
272 "*" . $claim->insuredCity() .
273 "*" . $claim->insuredState() .
274 "*" . $claim->insuredZip() .
280 "*" . $claim->insuredDOB() .
281 "*" . $claim->insuredSex() .
285 //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
286 $payerName=substr($claim->payerName(),0,35);
287 $out .= "NM1" . // Loop 2010BB Payer
296 // Zirmed ignores this if using payer name matching:
297 "*" . ($encounter_claim ?
$claim->payerAltID() : $claim->payerID()) .
300 // if (!$claim->payerID()) {
301 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
306 "*" . $claim->payerStreet() .
311 "*" . $claim->payerCity() .
312 "*" . $claim->payerState() .
313 "*" . $claim->payerZip() .
316 if (! $claim->isSelfOfInsured()) {
318 $out .= "HL" . // Loop 2000C Patient Information
329 "*" . $claim->insuredRelationship() .
333 $out .= "NM1" . // Loop 2010CA Patient
336 "*" . $claim->patientLastName() .
337 "*" . $claim->patientFirstName() .
338 "*" . $claim->patientMiddleName() .
343 "*" . $claim->patientStreet() .
348 "*" . $claim->patientCity() .
349 "*" . $claim->patientState() .
350 "*" . $claim->patientZip() .
356 "*" . $claim->patientDOB() .
357 "*" . $claim->patientSex() .
359 } // end of patient different from insured
361 $proccount = $claim->procCount();
363 $clm_total_charges = 0;
364 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
365 $clm_total_charges +
= $claim->cptCharges($prockey);
368 if (!$clm_total_charges) {
369 $log .= "*** This claim has no charges!\n";
374 $out .= "CLM" . // Loop 2300 Claim
376 "*" . sprintf("%.2f",$clm_total_charges) . // Zirmed computes and replaces this
379 "*" . sprintf('%02d', $claim->facilityPOS()) . "::" . $claim->frequencyTypeCode() . // Changed to correct single digit output
382 "*" . ($claim->billingFacilityAssignment() ?
'Y' : 'N') .
387 if ($claim->dateInitialTreatment()) {
389 $out .= "DTP" . // Date of Initial Treatment
392 "*" . $claim->dateInitialTreatment() .
397 $out .= "DTP" . // Date of Onset
400 "*" . $claim->onsetDate() .
403 if (strcmp($claim->facilityPOS(),'21') == 0) {
405 $out .= "DTP" . // Date of Hospitalization
408 "*" . $claim->onsetDate() .
412 $patientpaid = $claim->patientPaidAmount();
413 if ($patientpaid != 0) {
415 $out .= "AMT" . // Patient paid amount. Page 220.
421 if ($claim->priorAuth()) {
423 $out .= "REF" . // Prior Authorization Number
425 "*" . $claim->priorAuth() .
429 if ($claim->cliaCode() and $claim->claimType() === 'MB') {
430 // Required by Medicare when in-house labs are done.
432 $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
434 "*" . $claim->cliaCode() .
438 // Note: This would be the place to implement the NTE segment for loop 2300.
439 if ($claim->additionalNotes()) {
442 $out .= "NTE" . // comments box 19
444 "*" . $claim->additionalNotes() .
448 // Diagnoses, up to 8 per HI segment.
449 $da = $claim->diagArray();
450 $diag_type_code = 'BK';
452 foreach ($da as $diag) {
454 if ($tmp) $out .= "~\n";
456 $out .= "HI"; // Health Diagnosis Codes
458 $out .= "*$diag_type_code:" . $diag;
459 $diag_type_code = 'BF';
462 if ($tmp) $out .= "~\n";
464 if ($claim->referrerLastName()) {
465 // Medicare requires referring provider's name and UPIN.
467 $out .= "NM1" . // Loop 2310A Referring Provider
470 "*" . $claim->referrerLastName() .
471 "*" . $claim->referrerFirstName() .
472 "*" . $claim->referrerMiddleName() .
475 if ($claim->referrerNPI()) { $out .=
477 "*" . $claim->referrerNPI();
480 "*" . $claim->referrerSSN();
484 if ($claim->referrerTaxonomy()) {
487 "*RF" . // ReFerring provider
489 "*" . $claim->referrerTaxonomy() .
493 if ($claim->referrerUPIN()) {
495 $out .= "REF" . // Referring Provider Secondary Identification
497 "*" . $claim->referrerUPIN() .
503 $out .= "NM1" . // Loop 2310B Rendering Provider
506 "*" . $claim->providerLastName() .
507 "*" . $claim->providerFirstName() .
508 "*" . $claim->providerMiddleName() .
511 if ($claim->providerNPI()) { $out .=
513 "*" . $claim->providerNPI();
516 "*" . $claim->providerSSN();
517 $log .= "*** Rendering provider has no NPI.\n";
521 if ($claim->providerTaxonomy()) {
524 "*PE" . // PErforming provider
526 "*" . $claim->providerTaxonomy() .
530 // REF*1C is required here for the Medicare provider number if NPI was
531 // specified in NM109. Not sure if other payers require anything here.
532 // --- apparently ECLAIMS, INC wants the data in 2010 but NOT in 2310B - tony@mi-squared.com
534 if (trim($claim->x12gsreceiverid()) != '470819582') { // if NOT ECLAIMS EDI
535 if ($claim->providerNumber()) {
538 "*" . $claim->providerNumberType() .
539 "*" . $claim->providerNumber() .
544 // Loop 2310D is omitted in the case of home visits (POS=12).
545 if ($claim->facilityPOS() != 12) {
547 $out .= "NM1" . // Loop 2310D Service Location
550 //Field length is limited to 35. See nucc dataset page 77 www.nucc.org
551 $facilityName=substr($claim->facilityName(),0,35);
552 if ($claim->facilityName() ||
$claim->facilityNPI() ||
$claim->facilityETIN()) { $out .=
555 if ($claim->facilityNPI() ||
$claim->facilityETIN()) { $out .=
560 if ($claim->facilityNPI()) { $out .=
561 "*XX*" . $claim->facilityNPI();
563 "*24*" . $claim->facilityETIN();
564 $log .= "*** Service location has no NPI.\n";
568 if ($claim->facilityStreet()) {
571 "*" . $claim->facilityStreet() .
574 if ($claim->facilityState()) {
577 "*" . $claim->facilityCity() .
578 "*" . $claim->facilityState() .
579 "*" . $claim->facilityZip() .
584 // Loop 2310E, Supervising Provider
586 if ($claim->supervisorLastName()) {
589 "*DQ" . // Supervising Physician
591 "*" . $claim->supervisorLastName() .
592 "*" . $claim->supervisorFirstName() .
593 "*" . $claim->supervisorMiddleName() .
594 "*" . // NM106 not used
596 if ($claim->supervisorNPI()) { $out .=
598 "*" . $claim->supervisorNPI();
601 "*" . $claim->supervisorSSN();
605 if ($claim->supervisorNumber()) {
608 "*" . $claim->supervisorNumberType() .
609 "*" . $claim->supervisorNumber() .
614 $prev_pt_resp = $clm_total_charges; // for computation below
616 // Loops 2320 and 2330*, other subscriber/payer information.
618 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
620 $tmp1 = $claim->claimType($ins);
621 $tmp2 = 'C1'; // Here a kludge. See page 321.
622 if ($tmp1 === 'CI') $tmp2 = 'C1';
623 if ($tmp1 === 'AM') $tmp2 = 'AP';
624 if ($tmp1 === 'HM') $tmp2 = 'HM';
625 if ($tmp1 === 'MB') $tmp2 = 'MB';
626 if ($tmp1 === 'MC') $tmp2 = 'MC';
627 if ($tmp1 === '09') $tmp2 = 'PP';
629 $out .= "SBR" . // Loop 2320, Subscriber Information - page 318
630 "*" . $claim->payerSequence($ins) .
631 "*" . $claim->insuredRelationship($ins) .
632 "*" . $claim->groupNumber($ins) .
633 "*" . $claim->groupName($ins) .
638 "*" . $claim->claimType($ins) .
641 // Things that apply only to previous payers, not future payers.
643 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
645 // Generate claim-level adjustments.
646 $aarr = $claim->payerAdjustments($ins);
647 foreach ($aarr as $a) {
649 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 323.
656 $payerpaid = $claim->payerTotals($ins);
658 $out .= "AMT" . // Previous payer's paid amount. Page 332.
660 "*" . $payerpaid[1] .
663 // Patient responsibility amount as of this previous payer.
664 $prev_pt_resp -= $payerpaid[1]; // reduce by payments
665 $prev_pt_resp -= $payerpaid[2]; // reduce by adjustments
668 $out .= "AMT" . // Allowed amount per previous payer. Page 334.
670 "*" . sprintf('%.2f', $payerpaid[1] +
$prev_pt_resp) .
674 $out .= "AMT" . // Patient responsibility amount per previous payer. Page 335.
676 "*" . sprintf('%.2f', $prev_pt_resp) .
679 } // End of things that apply only to previous payers.
682 $out .= "DMG" . // Other subscriber demographic information. Page 342.
684 "*" . $claim->insuredDOB($ins) .
685 "*" . $claim->insuredSex($ins) .
689 $out .= "OI" . // Other Insurance Coverage Information. Page 344.
699 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 350.
702 "*" . $claim->insuredLastName($ins) .
703 "*" . $claim->insuredFirstName($ins) .
704 "*" . $claim->insuredMiddleName($ins) .
708 "*" . $claim->policyNumber($ins) .
713 "*" . $claim->insuredStreet($ins) .
718 "*" . $claim->insuredCity($ins) .
719 "*" . $claim->insuredState($ins) .
720 "*" . $claim->insuredZip($ins) .
724 //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
725 $payerName=substr($claim->payerName($ins),0,35);
726 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 359.
735 "*" . $claim->payerID($ins) .
738 // if (!$claim->payerID($ins)) {
739 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
742 // Payer address (N3 and N4) are added below so that Gateway EDI can
743 // auto-generate secondary claims. These do NOT appear in my copy of
744 // the spec! -- Rod 2008-06-12
746 if (trim($claim->x12gsreceiverid()) == '431420764') { // if Gateway EDI
749 "*" . $claim->payerStreet($ins) .
754 "*" . $claim->payerCity($ins) .
755 "*" . $claim->payerState($ins) .
756 "*" . $claim->payerZip($ins) .
760 } // End loops 2320/2330*.
764 // Procedure loop starts here.
766 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
770 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
775 $out .= "SV1" . // Professional Service. Page 400.
776 "*HC:" . $claim->cptKey($prockey) .
777 "*" . sprintf('%.2f', $claim->cptCharges($prockey)) .
779 "*" . $claim->cptUnits($prockey) .
783 $dia = $claim->diagIndexArray($prockey);
785 foreach ($dia as $dindex) {
788 if (++
$i >= 4) break;
792 if (!$claim->cptCharges($prockey)) {
793 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
797 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
801 $out .= "DTP" . // Date of Service. Page 435.
804 "*" . $claim->serviceDate() .
807 // AMT*AAE segment for Approved Amount from previous payer.
808 // Medicare secondaries seem to require this.
810 for ($ins = $claim->payerCount() - 1; $ins > 0; --$ins) {
811 if ($claim->payerSequence($ins) > $claim->payerSequence())
812 continue; // payer is future, not previous
813 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
815 $out .= "AMT" . // Approved amount per previous payer. Page 485.
817 "*" . sprintf('%.2f', $claim->cptCharges($prockey) - $payerpaid[2]) .
822 // Loop 2410, Drug Information. Medicaid insurers seem to want this
825 $ndc = $claim->cptNDCID($prockey);
828 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
829 "*" . // Per addendum, LIN01 is not used.
834 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp)) {
835 $log .= "*** NDC code '$ndc' has invalid format!\n";
839 $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
840 if (!$tmpunits) $tmpunits = 1;
841 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
844 "*" . sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits) .
845 "*" . $claim->cptNDCQuantity($prockey) .
846 "*" . $claim->cptNDCUOM($prockey) .
850 // Loop 2420A, Rendering Provider (service-specific).
851 // Used if the rendering provider for this service line is different
852 // from that in loop 2310B.
854 if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
856 $out .= "NM1" . // Loop 2310B Rendering Provider
859 "*" . $claim->providerLastName($prockey) .
860 "*" . $claim->providerFirstName($prockey) .
861 "*" . $claim->providerMiddleName($prockey) .
864 if ($claim->providerNPI($prockey)) { $out .=
866 "*" . $claim->providerNPI($prockey);
869 "*" . $claim->providerSSN($prockey);
870 $log .= "*** Rendering provider has no NPI.\n";
874 if ($claim->providerTaxonomy($prockey)) {
877 "*PE" . // PErforming provider
879 "*" . $claim->providerTaxonomy($prockey) .
883 // REF*1C is required here for the Medicare provider number if NPI was
884 // specified in NM109. Not sure if other payers require anything here.
885 if ($claim->providerNumber($prockey)) {
888 "*" . $claim->providerNumberType($prockey) .
889 "*" . $claim->providerNumber($prockey) .
894 // Loop 2430, adjudication by previous payers.
896 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
897 if ($claim->payerSequence($ins) > $claim->payerSequence())
898 continue; // payer is future, not previous
900 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
901 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
903 if ($payerpaid[1] == 0 && !count($aarr)) {
904 $log .= "*** Procedure '" . $claim->cptKey($prockey) .
905 "' has no payments or adjustments from previous payer!\n";
910 $out .= "SVD" . // Service line adjudication. Page 554.
911 "*" . $claim->payerID($ins) .
912 "*" . $payerpaid[1] .
913 "*HC:" . $claim->cptKey($prockey) .
915 "*" . $claim->cptUnits($prockey) .
918 $tmpdate = $payerpaid[0];
919 foreach ($aarr as $a) {
921 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
926 if (!$tmpdate) $tmpdate = $a[0];
929 $out .= "CAS02" . // Previous payer's adjustment reason
937 $out .= "DTP" . // Previous payer's line adjustment date. Page 566.
944 } // end this procedure
947 $out .= "SE" . // SE Trailer
952 $out .= "GE" . // GE Trailer
957 $out .= "IEA" . // IEA Trailer