2 // Copyright (C) 2007 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() .
162 "*" . $claim->providerNumberType() .
163 "*" . $claim->providerNumber() .
167 $out .= "NM1" . // Loop 2010AB Pay-To Provider
170 "*" . $claim->billingFacilityName() .
175 if ($claim->billingFacilityNPI())
176 $out .= "*XX*" . $claim->billingFacilityNPI();
178 $out .= "*24*" . $claim->billingFacilityETIN();
183 "*" . $claim->billingFacilityStreet() .
188 "*" . $claim->billingFacilityCity() .
189 "*" . $claim->billingFacilityState() .
190 "*" . $claim->billingFacilityZip() .
193 if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
197 "*" . $claim->billingFacilityETIN() .
204 $out .= "HL" . // Loop 2000B Subscriber HL Loop
206 "*$HLBillingPayToProvider" .
211 $HLSubscriber = $HLcount++
;
214 $out .= "SBR" . // Subscriber Information
215 "*" . $claim->payerSequence() .
216 "*" . $claim->insuredRelationship() .
217 "*" . $claim->groupNumber() .
218 "*" . $claim->groupName() .
219 "*" . $claim->insuredTypeCode() . // applies for secondary medicare
223 "*" . $claim->claimType() . // Zirmed replaces this
227 $out .= "NM1" . // Loop 2010BA Subscriber
230 "*" . $claim->insuredLastName() .
231 "*" . $claim->insuredFirstName() .
232 "*" . $claim->insuredMiddleName() .
236 "*" . $claim->policyNumber() .
241 "*" . $claim->insuredStreet() .
246 "*" . $claim->insuredCity() .
247 "*" . $claim->insuredState() .
248 "*" . $claim->insuredZip() .
254 "*" . $claim->insuredDOB() .
255 "*" . $claim->insuredSex() .
259 $out .= "NM1" . // Loop 2010BB Payer
262 "*" . $claim->payerName() .
268 "*" . $claim->payerID() . // Zirmed ignores this if using Payer Name Matching.
271 // if (!$claim->payerID()) {
272 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
277 "*" . $claim->payerStreet() .
282 "*" . $claim->payerCity() .
283 "*" . $claim->payerState() .
284 "*" . $claim->payerZip() .
287 if (! $claim->isSelfOfInsured()) {
289 $out .= "HL" . // Loop 2000C Patient Information
300 "*" . $claim->insuredRelationship() .
304 $out .= "NM1" . // Loop 2010CA Patient
307 "*" . $claim->patientLastName() .
308 "*" . $claim->patientFirstName() .
309 "*" . $claim->patientMiddleName() .
314 "*" . $claim->patientStreet() .
319 "*" . $claim->patientCity() .
320 "*" . $claim->patientState() .
321 "*" . $claim->patientZip() .
327 "*" . $claim->patientDOB() .
328 "*" . $claim->patientSex() .
330 } // end of patient different from insured
332 $proccount = $claim->procCount();
334 $clm_total_charges = 0;
335 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
336 $clm_total_charges +
= $claim->cptCharges($prockey);
339 if (!$clm_total_charges) {
340 $log .= "*** This claim has no charges!\n";
344 $out .= "CLM" . // Loop 2300 Claim
346 "*" . sprintf("%.2f",$clm_total_charges) . // Zirmed computes and replaces this
349 "*" . $claim->facilityPOS() . "::1" .
358 $out .= "DTP" . // Date of Onset
361 "*" . $claim->onsetDate() .
364 if (strcmp($claim->facilityPOS(),'21') == 0) {
366 $out .= "DTP" . // Date of Hospitalization
369 "*" . $claim->onsetDate() .
373 $patientpaid = $claim->patientPaidAmount();
374 if ($patientpaid != 0) {
376 $out .= "AMT" . // Patient paid amount. Page 220.
382 if ($claim->priorAuth()) {
384 $out .= "REF" . // Prior Authorization Number
386 "*" . $claim->priorAuth() .
390 if ($claim->cliaCode()) {
391 // Required by Medicare when in-house labs are done.
393 $out .= "REF" . // Clinical Laboratory Improvement Amendment Number
395 "*" . $claim->cliaCode() .
399 $da = $claim->diagArray();
401 $out .= "HI"; // Health Diagnosis Codes
402 $diag_type_code = 'BK';
403 foreach ($da as $diag) {
404 $out .= "*$diag_type_code:" . $diag;
405 $diag_type_code = 'BF';
409 if ($claim->referrerLastName()) {
410 // Medicare requires referring provider's name and UPIN.
412 $out .= "NM1" . // Loop 2310A Referring Provider
415 "*" . $claim->referrerLastName() .
416 "*" . $claim->referrerFirstName() .
417 "*" . $claim->referrerMiddleName() .
420 if ($claim->referrerNPI()) { $out .=
422 "*" . $claim->referrerNPI();
425 "*" . $claim->referrerSSN();
430 $out .= "REF" . // Referring Provider Secondary Identification
432 "*" . $claim->referrerUPIN() .
437 $out .= "NM1" . // Loop 2310B Rendering Provider
440 "*" . $claim->providerLastName() .
441 "*" . $claim->providerFirstName() .
442 "*" . $claim->providerMiddleName() .
445 if ($claim->providerNPI()) { $out .=
447 "*" . $claim->providerNPI();
450 "*" . $claim->providerSSN();
451 $log .= "*** Rendering provider has no NPI.\n";
456 $out .= "PRV" . // Rendering Provider Information
462 // REF*1C is required here for the Medicare provider number if NPI was
463 // specified in NM109. Not sure if other payers require anything here.
464 if ($claim->providerNumber()) {
467 "*" . $claim->providerNumberType() .
468 "*" . $claim->providerNumber() .
473 $out .= "NM1" . // Loop 2310D Service Location
476 "*" . $claim->facilityName() .
481 if ($claim->facilityNPI()) { $out .=
482 "*XX*" . $claim->facilityNPI();
484 "*24*" . $claim->facilityETIN();
485 $log .= "*** Service location has no NPI.\n";
491 "*" . $claim->facilityStreet() .
496 "*" . $claim->facilityCity() .
497 "*" . $claim->facilityState() .
498 "*" . $claim->facilityZip() .
501 $prev_pt_resp = $clm_total_charges; // for computation below
503 // Loops 2320 and 2330*, other subscriber/payer information.
505 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
508 $out .= "SBR" . // Loop 2320, Subscriber Information - page 318
509 "*" . $claim->payerSequence($ins) .
510 "*" . $claim->insuredRelationship($ins) .
511 "*" . $claim->groupNumber($ins) .
512 "*" . $claim->groupName($ins) .
517 "*" . $claim->claimType($ins) .
520 // Things that apply only to previous payers, not future payers.
522 if ($claim->payerSequence($ins) < $claim->payerSequence()) {
524 // Generate claim-level adjustments.
525 $aarr = $claim->payerAdjustments($ins);
526 foreach ($aarr as $a) {
528 $out .= "CAS" . // Previous payer's claim-level adjustments. Page 323.
535 $payerpaid = $claim->payerTotals($ins);
537 $out .= "AMT" . // Previous payer's paid amount. Page 332.
539 "*" . $payerpaid[1] .
542 // Patient responsibility amount as of this previous payer.
543 $prev_pt_resp -= $payerpaid[1]; // reduce by payments
544 $prev_pt_resp -= $payerpaid[2]; // reduce by adjustments
547 $out .= "AMT" . // Patient responsibility amount per previous payer. Page 335.
549 "*" . sprintf('%.2f', $prev_pt_resp) .
552 } // End of things that apply only to previous payers.
555 $out .= "DMG" . // Other subscriber demographic information. Page 342.
557 "*" . $claim->insuredDOB($ins) .
558 "*" . $claim->insuredSex($ins) .
562 $out .= "OI" . // Other Insurance Coverage Information. Page 344.
572 $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 350.
575 "*" . $claim->insuredLastName($ins) .
576 "*" . $claim->insuredFirstName($ins) .
577 "*" . $claim->insuredMiddleName($ins) .
581 "*" . $claim->policyNumber($ins) .
585 $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 359.
588 "*" . $claim->payerName($ins) .
594 "*" . $claim->payerID($ins) .
597 // if (!$claim->payerID($ins)) {
598 // $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
601 } // End loops 2320/2330*.
605 // Procedure loop starts here.
607 for ($prockey = 0; $prockey < $proccount; ++
$prockey) {
611 $out .= "LX" . // Loop 2400 LX Service Line. Page 398.
616 $out .= "SV1" . // Professional Service. Page 400.
617 "*HC:" . $claim->cptKey($prockey) .
618 "*" . sprintf('%.2f', $claim->cptCharges($prockey)) .
620 "*" . $claim->cptUnits($prockey) .
623 "*" . $claim->diagIndex($prockey) .
626 if (!$claim->cptCharges($prockey)) {
627 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
630 if (!$claim->diagIndex($prockey)) {
631 $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
635 $out .= "DTP" . // Date of Service. Page 435.
638 "*" . $claim->serviceDate() .
641 // Loop 2410, Drug Information. Medicaid insurers seem to want this
644 $ndc = $claim->cptNDCID($prockey);
647 $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71).
653 if (!preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp)) {
654 $log .= "*** NDC code '$ndc' has invalid format!\n";
658 $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
659 if (!$tmpunits) $tmpunits = 1;
660 $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74).
663 "*" . sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits) .
664 "*" . $claim->cptNDCQuantity($prockey) .
665 "*" . $claim->cptNDCUOM($prockey) .
669 // Loop 2430, adjudication by previous payers.
671 for ($ins = 1; $ins < $claim->payerCount(); ++
$ins) {
672 if ($claim->payerSequence($ins) > $claim->payerSequence())
673 continue; // payer is future, not previous
675 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
676 $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
678 if ($payerpaid[1] == 0 && !count($aarr)) {
679 $log .= "*** Procedure '" . $claim->cptKey($prockey) .
680 "' has no payments or adjustments from previous payer!\n";
685 $out .= "SVD" . // Service line adjudication. Page 554.
686 "*" . $claim->payerID($ins) .
687 "*" . $payerpaid[1] .
688 "*HC:" . $claim->cptKey($prockey) .
690 "*" . $claim->cptUnits($prockey) .
693 $tmpdate = $payerpaid[0];
694 foreach ($aarr as $a) {
696 $out .= "CAS" . // Previous payer's line level adjustments. Page 558.
701 if (!$tmpdate) $tmpdate = $a[0];
706 $out .= "DTP" . // Previous payer's line adjustment date. Page 566.
713 } // end this procedure
716 $out .= "SE" . // SE Trailer
721 $out .= "GE" . // GE Trailer
726 $out .= "IEA" . // IEA Trailer