2 // Copyright (C) 2007-2009 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) {
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";
30 "*" . $claim->x12gssenderid() .
32 "*" . $claim->x12gsreceiverid() .
45 "*" . $claim->x12gssenderid() .
46 "*" . $claim->x12gsreceiverid() .
47 "*" . date('Ymd', $today) .
48 "*" . date('Hi', $today) .
51 "*" . $claim->x12gsversionstring() .
65 "*" . date('Ymd', $today) .
73 "*" . $claim->x12gsversionstring() .
77 $out .= "NM1" . // Loop 1000A Submitter
80 "*" . $claim->billingFacilityName() .
86 "*" . $claim->billingFacilityETIN() .
92 "*" . $claim->billingContactName() .
94 "*" . $claim->billingContactPhone() .
98 $out .= "NM1" . // Loop 1000B Receiver
101 "*" . $claim->clearingHouseName() .
107 "*" . $claim->clearingHouseETIN() .
113 $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop
120 $HLBillingPayToProvider = $HLcount++
;
123 $out .= "NM1" . // Loop 2010AA Billing Provider
126 "*" . $claim->billingFacilityName() .
131 if ($claim->billingFacilityNPI()) {
132 $out .= "*XX*" . $claim->billingFacilityNPI();
134 $log .= "*** Billing facility has no NPI.\n";
135 $out .= "*24*" . $claim->billingFacilityETIN();
141 "*" . $claim->billingFacilityStreet() .
146 "*" . $claim->billingFacilityCity() .
147 "*" . $claim->billingFacilityState() .
148 "*" . $claim->billingFacilityZip() .
151 // Add a REF*EI*<ein> segment if NPI was specified in the NM1 above.
152 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
156 "*" . $claim->billingFacilityETIN() .
160 if ($claim->providerNumberType() && $claim->providerNumber()) {
163 "*" . $claim->providerNumberType() .
164 "*" . $claim->providerNumber() .
167 else if ($claim->providerNumber()) {
168 $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
172 $out .= "NM1" . // Loop 2010AB Pay-To Provider
175 "*" . $claim->billingFacilityName() .
180 if ($claim->billingFacilityNPI())
181 $out .= "*XX*" . $claim->billingFacilityNPI();
183 $out .= "*24*" . $claim->billingFacilityETIN();
188 "*" . $claim->billingFacilityStreet() .
193 "*" . $claim->billingFacilityCity() .
194 "*" . $claim->billingFacilityState() .
195 "*" . $claim->billingFacilityZip() .
198 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
202 "*" . $claim->billingFacilityETIN() .
209 $out .= "HL" . // Loop 2000B Subscriber HL Loop
211 "*$HLBillingPayToProvider" .
216 $HLSubscriber = $HLcount++
;
218 if (!$claim->payerSequence()) {
219 $log .= "*** Error: Insurance information is missing!\n";
222 $out .= "SBR" . // Subscriber Information
223 "*" . $claim->payerSequence() .
224 "*" . $claim->insuredRelationship() .
225 "*" . $claim->groupNumber() .
226 "*" . $claim->groupName() .
227 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
231 "*" . $claim->claimType() . // Zirmed replaces this
235 $out .= "NM1" . // Loop 2010BA Subscriber
238 "*" . $claim->insuredLastName() .
239 "*" . $claim->insuredFirstName() .
240 "*" . $claim->insuredMiddleName() .
244 "*" . $claim->policyNumber() .
249 "*" . $claim->insuredStreet() .
254 "*" . $claim->insuredCity() .
255 "*" . $claim->insuredState() .
256 "*" . $claim->insuredZip() .
262 "*" . $claim->insuredDOB() .
263 "*" . $claim->insuredSex() .
267 $out .= "NM1" . // Loop 2010BB Payer
270 "*" . $claim->payerName() .
276 "*" . $claim->payerID() . // Zirmed ignores this if using Payer Name Matching.
279 // if (!$claim->payerID()) {
280 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
285 "*" . $claim->payerStreet() .
290 "*" . $claim->payerCity() .
291 "*" . $claim->payerState() .
292 "*" . $claim->payerZip() .
295 if (! $claim->isSelfOfInsured()) {
297 $out .= "HL" . // Loop 2000C Patient Information
308 "*" . $claim->insuredRelationship() .
312 $out .= "NM1" . // Loop 2010CA Patient
315 "*" . $claim->patientLastName() .
316 "*" . $claim->patientFirstName() .
317 "*" . $claim->patientMiddleName() .
322 "*" . $claim->patientStreet() .
327 "*" . $claim->patientCity() .
328 "*" . $claim->patientState() .
329 "*" . $claim->patientZip() .
335 "*" . $claim->patientDOB() .
336 "*" . $claim->patientSex() .
338 } // end of patient different from insured
340 $proccount = $claim->procCount();
342 $clm_total_charges = 0;
343 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
344 $clm_total_charges +
= $claim->cptCharges($prockey);
347 if (!$clm_total_charges) {
348 $log .= "*** This claim has no charges!\n";
352 $out .= "CLM" . // Loop 2300 Claim
354 "*" . sprintf("%.2f",$clm_total_charges) . // Zirmed computes and replaces this
357 "*" . $claim->facilityPOS() . "::" . $claim->frequencyTypeCode() .
360 "*" . ($claim->billingFacilityAssignment() ?
'Y' : 'N') .
366 $out .= "DTP" . // Date of Onset
369 "*" . $claim->onsetDate() .
372 if (strcmp($claim->facilityPOS(),'21') == 0) {
374 $out .= "DTP" . // Date of Hospitalization
377 "*" . $claim->onsetDate() .
381 $patientpaid = $claim->patientPaidAmount();
382 if ($patientpaid != 0) {
384 $out .= "AMT" . // Patient paid amount. Page 220.
390 if ($claim->priorAuth()) {
392 $out .= "REF" . // Prior Authorization Number
394 "*" . $claim->priorAuth() .
398 if ($claim->cliaCode()) {
399 // Required by Medicare when in-house labs are done.
401 $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
403 "*" . $claim->cliaCode() .
407 // Note: This would be the place to implement the NTE segment for loop 2300.
409 $da = $claim->diagArray();
411 $out .= "HI"; // Health Diagnosis Codes
412 $diag_type_code = 'BK';
413 foreach ($da as $diag) {
414 $out .= "*$diag_type_code:" . $diag;
415 $diag_type_code = 'BF';
419 if ($claim->referrerLastName()) {
420 // Medicare requires referring provider's name and UPIN.
422 $out .= "NM1" . // Loop 2310A Referring Provider
425 "*" . $claim->referrerLastName() .
426 "*" . $claim->referrerFirstName() .
427 "*" . $claim->referrerMiddleName() .
430 if ($claim->referrerNPI()) { $out .=
432 "*" . $claim->referrerNPI();
435 "*" . $claim->referrerSSN();
439 if ($claim->referrerTaxonomy()) {
442 "*RF" . // ReFerring provider
444 "*" . $claim->referrerTaxonomy() .
448 if ($claim->referrerUPIN()) {
450 $out .= "REF" . // Referring Provider Secondary Identification
452 "*" . $claim->referrerUPIN() .
458 $out .= "NM1" . // Loop 2310B Rendering Provider
461 "*" . $claim->providerLastName() .
462 "*" . $claim->providerFirstName() .
463 "*" . $claim->providerMiddleName() .
466 if ($claim->providerNPI()) { $out .=
468 "*" . $claim->providerNPI();
471 "*" . $claim->providerSSN();
472 $log .= "*** Rendering provider has no NPI.\n";
476 if ($claim->providerTaxonomy()) {
479 "*PE" . // PErforming provider
481 "*" . $claim->providerTaxonomy() .
485 // REF*1C is required here for the Medicare provider number if NPI was
486 // specified in NM109. Not sure if other payers require anything here.
487 if ($claim->providerNumber()) {
490 "*" . $claim->providerNumberType() .
491 "*" . $claim->providerNumber() .
495 // Loop 2310D is omitted in the case of home visits (POS=12).
496 if ($claim->facilityPOS() != 12) {
498 $out .= "NM1" . // Loop 2310D Service Location
501 if ($claim->facilityName() ||
$claim->facilityNPI() ||
$claim->facilityETIN()) { $out .=
502 "*" . $claim->facilityName();
504 if ($claim->facilityNPI() ||
$claim->facilityETIN()) { $out .=
509 if ($claim->facilityNPI()) { $out .=
510 "*XX*" . $claim->facilityNPI();
512 "*24*" . $claim->facilityETIN();
513 $log .= "*** Service location has no NPI.\n";
517 if ($claim->facilityStreet()) {
520 "*" . $claim->facilityStreet() .
523 if ($claim->facilityState()) {
526 "*" . $claim->facilityCity() .
527 "*" . $claim->facilityState() .
528 "*" . $claim->facilityZip() .
533 // Loop 2310E, Supervising Provider
535 if ($claim->supervisorLastName()) {
538 "*DQ" . // Supervising Physician
540 "*" . $claim->supervisorLastName() .
541 "*" . $claim->supervisorFirstName() .
542 "*" . $claim->supervisorMiddleName() .
543 "*" . // NM106 not used
545 if ($claim->supervisorNPI()) { $out .=
547 "*" . $claim->supervisorNPI();
550 "*" . $claim->supervisorSSN();
554 if ($claim->supervisorNumber()) {
557 "*" . $claim->supervisorNumberType() .
558 "*" . $claim->supervisorNumber() .
563 $prev_pt_resp = $clm_total_charges; // for computation below
565 // Loops 2320 and 2330*, other subscriber/payer information.
567 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
569 $tmp1 = $claim->claimType($ins);
570 $tmp2 = 'C1'; // Here a kludge. See page 321.
571 if ($tmp1 === 'CI') $tmp2 = 'C1';
572 if ($tmp1 === 'AM') $tmp2 = 'AP';
573 if ($tmp1 === 'HM') $tmp2 = 'HM';
574 if ($tmp1 === 'MB') $tmp2 = 'MB';
575 if ($tmp1 === 'MC') $tmp2 = 'MC';
576 if ($tmp1 === '09') $tmp2 = 'PP';
578 $out .= "SBR" . // Loop 2320, Subscriber Information - page 318
579 "*" . $claim->payerSequence($ins) .
580 "*" . $claim->insuredRelationship($ins) .
581 "*" . $claim->groupNumber($ins) .
582 "*" . $claim->groupName($ins) .
587 "*" . $claim->claimType($ins) .
590 // Things that apply only to previous payers, not future payers.
592 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
594 // Generate claim-level adjustments.
595 $aarr = $claim->payerAdjustments($ins);
596 foreach ($aarr as $a) {
598 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 323.
605 $payerpaid = $claim->payerTotals($ins);
607 $out .= "AMT" . // Previous payer's paid amount. Page 332.
609 "*" . $payerpaid[1] .
612 // Patient responsibility amount as of this previous payer.
613 $prev_pt_resp -= $payerpaid[1]; // reduce by payments
614 $prev_pt_resp -= $payerpaid[2]; // reduce by adjustments
617 $out .= "AMT" . // Patient responsibility amount per previous payer. Page 335.
619 "*" . sprintf('%.2f', $prev_pt_resp) .
622 } // End of things that apply only to previous payers.
625 $out .= "DMG" . // Other subscriber demographic information. Page 342.
627 "*" . $claim->insuredDOB($ins) .
628 "*" . $claim->insuredSex($ins) .
632 $out .= "OI" . // Other Insurance Coverage Information. Page 344.
642 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 350.
645 "*" . $claim->insuredLastName($ins) .
646 "*" . $claim->insuredFirstName($ins) .
647 "*" . $claim->insuredMiddleName($ins) .
651 "*" . $claim->policyNumber($ins) .
656 "*" . $claim->insuredStreet($ins) .
661 "*" . $claim->insuredCity($ins) .
662 "*" . $claim->insuredState($ins) .
663 "*" . $claim->insuredZip($ins) .
667 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 359.
670 "*" . $claim->payerName($ins) .
676 "*" . $claim->payerID($ins) .
679 // if (!$claim->payerID($ins)) {
680 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
683 // Payer address (N3 and N4) are added below so that Gateway EDI can
684 // auto-generate secondary claims. These do NOT appear in my copy of
685 // the spec! -- Rod 2008-06-12
687 if ($claim->x12gsreceiverid() == '431420764') { // if Gateway EDI
690 "*" . $claim->payerStreet($ins) .
695 "*" . $claim->payerCity($ins) .
696 "*" . $claim->payerState($ins) .
697 "*" . $claim->payerZip($ins) .
701 } // End loops 2320/2330*.
705 // Procedure loop starts here.
707 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
711 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
716 $out .= "SV1" . // Professional Service. Page 400.
717 "*HC:" . $claim->cptKey($prockey) .
718 "*" . sprintf('%.2f', $claim->cptCharges($prockey)) .
720 "*" . $claim->cptUnits($prockey) .
724 $dia = $claim->diagIndexArray($prockey);
726 foreach ($dia as $dindex) {
727 $out .= $separator . $dindex;
732 if (!$claim->cptCharges($prockey)) {
733 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
737 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
741 $out .= "DTP" . // Date of Service. Page 435.
744 "*" . $claim->serviceDate() .
747 // Loop 2410, Drug Information. Medicaid insurers seem to want this
750 $ndc = $claim->cptNDCID($prockey);
753 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
754 "*" . // Per addendum, LIN01 is not used.
759 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp)) {
760 $log .= "*** NDC code '$ndc' has invalid format!\n";
764 $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
765 if (!$tmpunits) $tmpunits = 1;
766 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
769 "*" . sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits) .
770 "*" . $claim->cptNDCQuantity($prockey) .
771 "*" . $claim->cptNDCUOM($prockey) .
775 // Loop 2420A, Rendering Provider (service-specific).
776 // Used if the rendering provider for this service line is different
777 // from that in loop 2310B.
779 if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
781 $out .= "NM1" . // Loop 2310B Rendering Provider
784 "*" . $claim->providerLastName($prockey) .
785 "*" . $claim->providerFirstName($prockey) .
786 "*" . $claim->providerMiddleName($prockey) .
789 if ($claim->providerNPI($prockey)) { $out .=
791 "*" . $claim->providerNPI($prockey);
794 "*" . $claim->providerSSN($prockey);
795 $log .= "*** Rendering provider has no NPI.\n";
799 if ($claim->providerTaxonomy($prockey)) {
802 "*PE" . // PErforming provider
804 "*" . $claim->providerTaxonomy($prockey) .
808 // REF*1C is required here for the Medicare provider number if NPI was
809 // specified in NM109. Not sure if other payers require anything here.
810 if ($claim->providerNumber($prockey)) {
813 "*" . $claim->providerNumberType($prockey) .
814 "*" . $claim->providerNumber($prockey) .
819 // Loop 2430, adjudication by previous payers.
821 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
822 if ($claim->payerSequence($ins) > $claim->payerSequence())
823 continue; // payer is future, not previous
825 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
826 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
828 if ($payerpaid[1] == 0 && !count($aarr)) {
829 $log .= "*** Procedure '" . $claim->cptKey($prockey) .
830 "' has no payments or adjustments from previous payer!\n";
835 $out .= "SVD" . // Service line adjudication. Page 554.
836 "*" . $claim->payerID($ins) .
837 "*" . $payerpaid[1] .
838 "*HC:" . $claim->cptKey($prockey) .
840 "*" . $claim->cptUnits($prockey) .
843 $tmpdate = $payerpaid[0];
844 foreach ($aarr as $a) {
846 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
851 if (!$tmpdate) $tmpdate = $a[0];
856 $out .= "DTP" . // Previous payer's line adjustment date. Page 566.
863 } // end this procedure
866 $out .= "SE" . // SE Trailer
871 $out .= "GE" . // GE Trailer
876 $out .= "IEA" . // IEA Trailer