Ejemplo n.º 1
0
function gen_x12_837($pid, $encounter, &$log, $encounter_claim = false)
{
    $today = time();
    $out = '';
    $claim = new Claim($pid, $encounter);
    $edicount = 0;
    $log .= "Generating claim {$pid}-{$encounter} for " . $claim->patientFirstName() . ' ' . $claim->patientMiddleName() . ' ' . $claim->patientLastName() . ' on ' . date('Y-m-d H:i', $today) . ".\n";
    $out .= "ISA" . "*00" . "*          " . "*00" . "*          " . "*" . $claim->x12gsisa05() . "*" . $claim->x12gssenderid() . "*" . $claim->x12gsisa07() . "*" . $claim->x12gsreceiverid() . "*030911" . "*1630" . "*U" . "*00401" . "*000000001" . "*" . $claim->x12gsisa14() . "*" . $claim->x12gsisa15() . "*:" . "~\n";
    $out .= "GS" . "*HC" . "*" . $claim->x12gsgs02() . "*" . trim($claim->x12gsreceiverid()) . "*" . date('Ymd', $today) . "*" . date('Hi', $today) . "*1" . "*X" . "*" . $claim->x12gsversionstring() . "~\n";
    ++$edicount;
    $out .= "ST" . "*837" . "*0021" . "~\n";
    ++$edicount;
    $out .= "BHT" . "*0019" . "*00" . "*0123" . "*" . date('Ymd', $today) . "*1023" . ($encounter_claim ? "*RP" : "*CH") . "~\n";
    ++$edicount;
    $out .= "REF" . "*87" . "*" . $claim->x12gsversionstring() . "~\n";
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
    $billingFacilityName = substr($claim->billingFacilityName(), 0, 35);
    $out .= "NM1" . "*41" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*" . "*46";
    if (trim($claim->x12gsreceiverid()) == '470819582') {
        // if ECLAIMS EDI
        $out .= "*" . $claim->clearingHouseETIN();
    } else {
        $out .= "*" . $claim->billingFacilityETIN();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "PER" . "*IC" . "*" . $claim->billingContactName() . "*TE" . "*" . $claim->billingContactPhone();
    if ($claim->x12gsper06()) {
        $out .= "*ED*" . $claim->x12gsper06();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "NM1" . "*40" . "*2" . "*" . $claim->clearingHouseName() . "*" . "*" . "*" . "*" . "*46" . "*" . $claim->clearingHouseETIN() . "~\n";
    $HLcount = 1;
    ++$edicount;
    $out .= "HL" . "*{$HLcount}" . "*" . "*20" . "*1" . "~\n";
    $HLBillingPayToProvider = $HLcount++;
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
    $billingFacilityName = substr($claim->billingFacilityName(), 0, 35);
    $out .= "NM1" . "*85" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*";
    if ($claim->billingFacilityNPI()) {
        $out .= "*XX*" . $claim->billingFacilityNPI();
    } else {
        $log .= "*** Billing facility has no NPI.\n";
        $out .= "*24*" . $claim->billingFacilityETIN();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "N3" . "*" . $claim->billingFacilityStreet() . "~\n";
    ++$edicount;
    $out .= "N4" . "*" . $claim->billingFacilityCity() . "*" . $claim->billingFacilityState() . "*" . $claim->billingFacilityZip() . "~\n";
    // Add a REF*EI*<ein> segment if NPI was specified in the NM1 above.
    if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
        ++$edicount;
        $out .= "REF";
        if ($claim->federalIdType()) {
            $out .= "*" . $claim->federalIdType();
        } else {
            $out .= "*EI";
            //For dealing with the situation before adding selection for TaxId type In facility ie default to EIN.
        }
        $out .= "*" . $claim->billingFacilityETIN() . "~\n";
    }
    if ($claim->providerNumberType() && $claim->providerNumber()) {
        ++$edicount;
        $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
    } else {
        if ($claim->providerNumber()) {
            $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
        }
    }
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
    $billingFacilityName = substr($claim->billingFacilityName(), 0, 35);
    $out .= "NM1" . "*87" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*";
    if ($claim->billingFacilityNPI()) {
        $out .= "*XX*" . $claim->billingFacilityNPI();
    } else {
        $out .= "*24*" . $claim->billingFacilityETIN();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "N3" . "*" . $claim->billingFacilityStreet() . "~\n";
    ++$edicount;
    $out .= "N4" . "*" . $claim->billingFacilityCity() . "*" . $claim->billingFacilityState() . "*" . $claim->billingFacilityZip() . "~\n";
    if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
        ++$edicount;
        $out .= "REF" . "*EI" . "*" . $claim->billingFacilityETIN() . "~\n";
    }
    $PatientHL = 0;
    ++$edicount;
    $out .= "HL" . "*{$HLcount}" . "*{$HLBillingPayToProvider}" . "*22" . "*{$PatientHL}" . "~\n";
    $HLSubscriber = $HLcount++;
    if (!$claim->payerSequence()) {
        $log .= "*** Error: Insurance information is missing!\n";
    }
    ++$edicount;
    $out .= "SBR" . "*" . $claim->payerSequence() . "*" . $claim->insuredRelationship() . "*" . $claim->groupNumber() . "*" . $claim->groupName() . "*" . $claim->insuredTypeCode() . "*" . "*" . "*" . "*" . $claim->claimType() . "~\n";
    ++$edicount;
    $out .= "NM1" . "*IL" . "*1" . "*" . $claim->insuredLastName() . "*" . $claim->insuredFirstName() . "*" . $claim->insuredMiddleName() . "*" . "*" . "*MI" . "*" . $claim->policyNumber() . "~\n";
    ++$edicount;
    $out .= "N3" . "*" . $claim->insuredStreet() . "~\n";
    ++$edicount;
    $out .= "N4" . "*" . $claim->insuredCity() . "*" . $claim->insuredState() . "*" . $claim->insuredZip() . "~\n";
    ++$edicount;
    $out .= "DMG" . "*D8" . "*" . $claim->insuredDOB() . "*" . $claim->insuredSex() . "~\n";
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
    $payerName = substr($claim->payerName(), 0, 35);
    $out .= "NM1" . "*PR" . "*2" . "*" . $payerName . "*" . "*" . "*" . "*" . "*PI" . "*" . ($encounter_claim ? $claim->payerAltID() : $claim->payerID()) . "~\n";
    // if (!$claim->payerID()) {
    //   $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
    // }
    ++$edicount;
    $out .= "N3" . "*" . $claim->payerStreet() . "~\n";
    ++$edicount;
    $out .= "N4" . "*" . $claim->payerCity() . "*" . $claim->payerState() . "*" . $claim->payerZip() . "~\n";
    if (!$claim->isSelfOfInsured()) {
        ++$edicount;
        $out .= "HL" . "*{$HLcount}" . "*{$HLSubscriber}" . "*23" . "*0" . "~\n";
        $HLcount++;
        ++$edicount;
        $out .= "PAT" . "*" . $claim->insuredRelationship() . "~\n";
        ++$edicount;
        $out .= "NM1" . "*QC" . "*1" . "*" . $claim->patientLastName() . "*" . $claim->patientFirstName() . "*" . $claim->patientMiddleName() . "~\n";
        ++$edicount;
        $out .= "N3" . "*" . $claim->patientStreet() . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->patientCity() . "*" . $claim->patientState() . "*" . $claim->patientZip() . "~\n";
        ++$edicount;
        $out .= "DMG" . "*D8" . "*" . $claim->patientDOB() . "*" . $claim->patientSex() . "~\n";
    }
    // end of patient different from insured
    $proccount = $claim->procCount();
    $clm_total_charges = 0;
    for ($prockey = 0; $prockey < $proccount; ++$prockey) {
        $clm_total_charges += $claim->cptCharges($prockey);
    }
    if (!$clm_total_charges) {
        $log .= "*** This claim has no charges!\n";
    }
    ++$edicount;
    $out .= "CLM" . "*{$pid}-{$encounter}" . "*" . sprintf("%.2f", $clm_total_charges) . "*" . "*" . "*" . sprintf('%02d', $claim->facilityPOS()) . "::" . $claim->frequencyTypeCode() . "*Y" . "*A" . "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') . "*Y" . "*C" . "~\n";
    ++$edicount;
    $out .= "DTP" . "*431" . "*D8" . "*" . $claim->onsetDate() . "~\n";
    if (strcmp($claim->facilityPOS(), '21') == 0) {
        ++$edicount;
        $out .= "DTP" . "*435" . "*D8" . "*" . $claim->onsetDate() . "~\n";
    }
    $patientpaid = $claim->patientPaidAmount();
    if ($patientpaid != 0) {
        ++$edicount;
        $out .= "AMT" . "*F5" . "*" . $patientpaid . "~\n";
    }
    if ($claim->priorAuth()) {
        ++$edicount;
        $out .= "REF" . "*G1" . "*" . $claim->priorAuth() . "~\n";
    }
    if ($claim->cliaCode() and $claim->claimType() === 'MB') {
        // Required by Medicare when in-house labs are done.
        ++$edicount;
        $out .= "REF" . "*X4" . "*" . $claim->cliaCode() . "~\n";
    }
    // Note: This would be the place to implement the NTE segment for loop 2300.
    if ($claim->additionalNotes()) {
        // Claim note.
        ++$edicount;
        $out .= "NTE" . "*" . "*" . $claim->additionalNotes() . "~\n";
    }
    // Diagnoses, up to 8 per HI segment.
    $da = $claim->diagArray();
    $diag_type_code = 'BK';
    $tmp = 0;
    foreach ($da as $diag) {
        if ($tmp % 8 == 0) {
            if ($tmp) {
                $out .= "~\n";
            }
            ++$edicount;
            $out .= "HI";
            // Health Diagnosis Codes
        }
        $out .= "*{$diag_type_code}:" . $diag;
        $diag_type_code = 'BF';
        ++$tmp;
    }
    if ($tmp) {
        $out .= "~\n";
    }
    if ($claim->referrerLastName()) {
        // Medicare requires referring provider's name and UPIN.
        ++$edicount;
        $out .= "NM1" . "*DN" . "*1" . "*" . $claim->referrerLastName() . "*" . $claim->referrerFirstName() . "*" . $claim->referrerMiddleName() . "*" . "*";
        if ($claim->referrerNPI()) {
            $out .= "*XX" . "*" . $claim->referrerNPI();
        } else {
            $out .= "*34" . "*" . $claim->referrerSSN();
        }
        $out .= "~\n";
        if ($claim->referrerTaxonomy()) {
            ++$edicount;
            $out .= "PRV" . "*RF" . "*ZZ" . "*" . $claim->referrerTaxonomy() . "~\n";
        }
        if ($claim->referrerUPIN()) {
            ++$edicount;
            $out .= "REF" . "*1G" . "*" . $claim->referrerUPIN() . "~\n";
        }
    }
    ++$edicount;
    $out .= "NM1" . "*82" . "*1" . "*" . $claim->providerLastName() . "*" . $claim->providerFirstName() . "*" . $claim->providerMiddleName() . "*" . "*";
    if ($claim->providerNPI()) {
        $out .= "*XX" . "*" . $claim->providerNPI();
    } else {
        $out .= "*34" . "*" . $claim->providerSSN();
        $log .= "*** Rendering provider has no NPI.\n";
    }
    $out .= "~\n";
    if ($claim->providerTaxonomy()) {
        ++$edicount;
        $out .= "PRV" . "*PE" . "*ZZ" . "*" . $claim->providerTaxonomy() . "~\n";
    }
    // REF*1C is required here for the Medicare provider number if NPI was
    // specified in NM109.  Not sure if other payers require anything here.
    // --- apparently ECLAIMS, INC wants the data in 2010 but NOT in 2310B - tony@mi-squared.com
    if (trim($claim->x12gsreceiverid()) != '470819582') {
        // if NOT ECLAIMS EDI
        if ($claim->providerNumber()) {
            ++$edicount;
            $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
        }
    }
    // Loop 2310D is omitted in the case of home visits (POS=12).
    if ($claim->facilityPOS() != 12) {
        ++$edicount;
        $out .= "NM1" . "*77" . "*2";
        //Field length is limited to 35. See nucc dataset page 77 www.nucc.org
        $facilityName = substr($claim->facilityName(), 0, 35);
        if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) {
            $out .= "*" . $facilityName;
        }
        if ($claim->facilityNPI() || $claim->facilityETIN()) {
            $out .= "*" . "*" . "*" . "*";
            if ($claim->facilityNPI()) {
                $out .= "*XX*" . $claim->facilityNPI();
            } else {
                $out .= "*24*" . $claim->facilityETIN();
                $log .= "*** Service location has no NPI.\n";
            }
        }
        $out .= "~\n";
        if ($claim->facilityStreet()) {
            ++$edicount;
            $out .= "N3" . "*" . $claim->facilityStreet() . "~\n";
        }
        if ($claim->facilityState()) {
            ++$edicount;
            $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . $claim->facilityZip() . "~\n";
        }
    }
    // Loop 2310E, Supervising Provider
    //
    if ($claim->supervisorLastName()) {
        ++$edicount;
        $out .= "NM1" . "*DQ" . "*1" . "*" . $claim->supervisorLastName() . "*" . $claim->supervisorFirstName() . "*" . $claim->supervisorMiddleName() . "*" . "*";
        // Name Suffix
        if ($claim->supervisorNPI()) {
            $out .= "*XX" . "*" . $claim->supervisorNPI();
        } else {
            $out .= "*34" . "*" . $claim->supervisorSSN();
        }
        $out .= "~\n";
        if ($claim->supervisorNumber()) {
            ++$edicount;
            $out .= "REF" . "*" . $claim->supervisorNumberType() . "*" . $claim->supervisorNumber() . "~\n";
        }
    }
    $prev_pt_resp = $clm_total_charges;
    // for computation below
    // Loops 2320 and 2330*, other subscriber/payer information.
    //
    for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
        $tmp1 = $claim->claimType($ins);
        $tmp2 = 'C1';
        // Here a kludge. See page 321.
        if ($tmp1 === 'CI') {
            $tmp2 = 'C1';
        }
        if ($tmp1 === 'AM') {
            $tmp2 = 'AP';
        }
        if ($tmp1 === 'HM') {
            $tmp2 = 'HM';
        }
        if ($tmp1 === 'MB') {
            $tmp2 = 'MB';
        }
        if ($tmp1 === 'MC') {
            $tmp2 = 'MC';
        }
        if ($tmp1 === '09') {
            $tmp2 = 'PP';
        }
        ++$edicount;
        $out .= "SBR" . "*" . $claim->payerSequence($ins) . "*" . $claim->insuredRelationship($ins) . "*" . $claim->groupNumber($ins) . "*" . $claim->groupName($ins) . "*" . $tmp2 . "*" . "*" . "*" . "*" . $claim->claimType($ins) . "~\n";
        // Things that apply only to previous payers, not future payers.
        //
        if ($claim->payerSequence($ins) < $claim->payerSequence()) {
            // Generate claim-level adjustments.
            $aarr = $claim->payerAdjustments($ins);
            foreach ($aarr as $a) {
                ++$edicount;
                $out .= "CAS" . "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
            }
            $payerpaid = $claim->payerTotals($ins);
            ++$edicount;
            $out .= "AMT" . "*D" . "*" . $payerpaid[1] . "~\n";
            // Patient responsibility amount as of this previous payer.
            $prev_pt_resp -= $payerpaid[1];
            // reduce by payments
            $prev_pt_resp -= $payerpaid[2];
            // reduce by adjustments
            ++$edicount;
            $out .= "AMT" . "*B6" . "*" . sprintf('%.2f', $payerpaid[1] + $prev_pt_resp) . "~\n";
            ++$edicount;
            $out .= "AMT" . "*F2" . "*" . sprintf('%.2f', $prev_pt_resp) . "~\n";
        }
        // End of things that apply only to previous payers.
        ++$edicount;
        $out .= "DMG" . "*D8" . "*" . $claim->insuredDOB($ins) . "*" . $claim->insuredSex($ins) . "~\n";
        ++$edicount;
        $out .= "OI" . "*" . "*" . "*Y" . "*B" . "*" . "*Y" . "~\n";
        ++$edicount;
        $out .= "NM1" . "*IL" . "*1" . "*" . $claim->insuredLastName($ins) . "*" . $claim->insuredFirstName($ins) . "*" . $claim->insuredMiddleName($ins) . "*" . "*" . "*MI" . "*" . $claim->policyNumber($ins) . "~\n";
        ++$edicount;
        $out .= "N3" . "*" . $claim->insuredStreet($ins) . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->insuredCity($ins) . "*" . $claim->insuredState($ins) . "*" . $claim->insuredZip($ins) . "~\n";
        ++$edicount;
        //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
        $payerName = substr($claim->payerName($ins), 0, 35);
        $out .= "NM1" . "*PR" . "*2" . "*" . $payerName . "*" . "*" . "*" . "*" . "*PI" . "*" . $claim->payerID($ins) . "~\n";
        // if (!$claim->payerID($ins)) {
        //   $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
        // }
        // Payer address (N3 and N4) are added below so that Gateway EDI can
        // auto-generate secondary claims.  These do NOT appear in my copy of
        // the spec!  -- Rod 2008-06-12
        if (trim($claim->x12gsreceiverid()) == '431420764') {
            // if Gateway EDI
            ++$edicount;
            $out .= "N3" . "*" . $claim->payerStreet($ins) . "~\n";
            //
            ++$edicount;
            $out .= "N4" . "*" . $claim->payerCity($ins) . "*" . $claim->payerState($ins) . "*" . $claim->payerZip($ins) . "~\n";
        }
        // end Gateway EDI
    }
    // End loops 2320/2330*.
    $loopcount = 0;
    // Procedure loop starts here.
    //
    for ($prockey = 0; $prockey < $proccount; ++$prockey) {
        ++$loopcount;
        ++$edicount;
        $out .= "LX" . "*{$loopcount}" . "~\n";
        ++$edicount;
        $out .= "SV1" . "*HC:" . $claim->cptKey($prockey) . "*" . sprintf('%.2f', $claim->cptCharges($prockey)) . "*UN" . "*" . $claim->cptUnits($prockey) . "*" . "*" . "*";
        $dia = $claim->diagIndexArray($prockey);
        $i = 0;
        foreach ($dia as $dindex) {
            if ($i) {
                $out .= ':';
            }
            $out .= $dindex;
            if (++$i >= 4) {
                break;
            }
        }
        $out .= "~\n";
        if (!$claim->cptCharges($prockey)) {
            $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
        }
        if (empty($dia)) {
            $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
        }
        ++$edicount;
        $out .= "DTP" . "*472" . "*D8" . "*" . $claim->serviceDate() . "~\n";
        // AMT*AAE segment for Approved Amount from previous payer.
        // Medicare secondaries seem to require this.
        //
        for ($ins = $claim->payerCount() - 1; $ins > 0; --$ins) {
            if ($claim->payerSequence($ins) > $claim->payerSequence()) {
                continue;
            }
            // payer is future, not previous
            $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
            ++$edicount;
            $out .= "AMT" . "*AAE" . "*" . sprintf('%.2f', $claim->cptCharges($prockey) - $payerpaid[2]) . "~\n";
            break;
        }
        // Loop 2410, Drug Information. Medicaid insurers seem to want this
        // with HCPCS codes.
        //
        $ndc = $claim->cptNDCID($prockey);
        if ($ndc) {
            ++$edicount;
            $out .= "LIN" . "*" . "*N4" . "*" . $ndc . "~\n";
            if (!preg_match('/^\\d\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d$/', $ndc, $tmp)) {
                $log .= "*** NDC code '{$ndc}' has invalid format!\n";
            }
            ++$edicount;
            $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
            if (!$tmpunits) {
                $tmpunits = 1;
            }
            $out .= "CTP" . "*" . "*" . "*" . sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits) . "*" . $claim->cptNDCQuantity($prockey) . "*" . $claim->cptNDCUOM($prockey) . "~\n";
        }
        // Loop 2420A, Rendering Provider (service-specific).
        // Used if the rendering provider for this service line is different
        // from that in loop 2310B.
        //
        if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
            ++$edicount;
            $out .= "NM1" . "*82" . "*1" . "*" . $claim->providerLastName($prockey) . "*" . $claim->providerFirstName($prockey) . "*" . $claim->providerMiddleName($prockey) . "*" . "*";
            if ($claim->providerNPI($prockey)) {
                $out .= "*XX" . "*" . $claim->providerNPI($prockey);
            } else {
                $out .= "*34" . "*" . $claim->providerSSN($prockey);
                $log .= "*** Rendering provider has no NPI.\n";
            }
            $out .= "~\n";
            if ($claim->providerTaxonomy($prockey)) {
                ++$edicount;
                $out .= "PRV" . "*PE" . "*ZZ" . "*" . $claim->providerTaxonomy($prockey) . "~\n";
            }
            // REF*1C is required here for the Medicare provider number if NPI was
            // specified in NM109.  Not sure if other payers require anything here.
            if ($claim->providerNumber($prockey)) {
                ++$edicount;
                $out .= "REF" . "*" . $claim->providerNumberType($prockey) . "*" . $claim->providerNumber($prockey) . "~\n";
            }
        }
        // Loop 2430, adjudication by previous payers.
        //
        for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
            if ($claim->payerSequence($ins) > $claim->payerSequence()) {
                continue;
            }
            // payer is future, not previous
            $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
            $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
            if ($payerpaid[1] == 0 && !count($aarr)) {
                $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no payments or adjustments from previous payer!\n";
                continue;
            }
            ++$edicount;
            $out .= "SVD" . "*" . $claim->payerID($ins) . "*" . $payerpaid[1] . "*HC:" . $claim->cptKey($prockey) . "*" . "*" . $claim->cptUnits($prockey) . "~\n";
            $tmpdate = $payerpaid[0];
            foreach ($aarr as $a) {
                ++$edicount;
                $out .= "CAS" . "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
                if (!$tmpdate) {
                    $tmpdate = $a[0];
                }
            }
            if ($tmpdate) {
                ++$edicount;
                $out .= "DTP" . "*573" . "*D8" . "*{$tmpdate}" . "~\n";
            }
        }
        // end loop 2430
    }
    // end this procedure
    ++$edicount;
    $out .= "SE" . "*{$edicount}" . "*0021" . "~\n";
    $out .= "GE" . "*1" . "*1" . "~\n";
    $out .= "IEA" . "*1" . "*000000001" . "~\n";
    $log .= "\n";
    return $out;
}
Ejemplo n.º 2
0
function gen_x12_837($pid, $encounter, &$log, $encounter_claim = false)
{
    $today = time();
    $out = '';
    $claim = new Claim($pid, $encounter);
    $edicount = 0;
    // This is true for the 5010 standard, false for 4010.
    // x12gsversionstring() should be "005010X222A1" or "004010X098A1".
    $CMS_5010 = strpos($claim->x12gsversionstring(), '5010') !== false;
    $log .= "Generating claim {$pid}-{$encounter} for " . $claim->patientFirstName() . ' ' . $claim->patientMiddleName() . ' ' . $claim->patientLastName() . ' on ' . date('Y-m-d H:i', $today) . ".\n";
    $out .= "ISA" . "*00" . "*          " . "*00" . "*          " . "*" . $claim->x12gsisa05() . "*" . $claim->x12gssenderid() . "*" . $claim->x12gsisa07() . "*" . $claim->x12gsreceiverid() . "*030911" . "*1630" . "*" . ($CMS_5010 ? "^" : "U") . "*" . ($CMS_5010 ? "00501" : "00401") . "*000000001" . "*" . $claim->x12gsisa14() . "*" . $claim->x12gsisa15() . "*:" . "~\n";
    $out .= "GS" . "*HC" . "*" . $claim->x12gsgs02() . "*" . trim($claim->x12gs03()) . "*" . date('Ymd', $today) . "*" . date('Hi', $today) . "*1" . "*X" . "*" . $claim->x12gsversionstring() . "~\n";
    ++$edicount;
    $out .= "ST" . "*837" . "*0021" . ($CMS_5010 ? "*" . $claim->x12gsversionstring() : "") . "~\n";
    ++$edicount;
    $out .= "BHT" . "*0019" . "*00" . "*0123" . "*" . date('Ymd', $today) . "*" . date('Hi', $today) . ($encounter_claim ? "*RP" : "*CH") . "~\n";
    if (!$CMS_5010) {
        // This segment was deleted for 5010.
        ++$edicount;
        $out .= "REF" . "*87" . "*" . $claim->x12gsversionstring() . "~\n";
    }
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
    $billingFacilityName = substr($claim->billingFacilityName(), 0, $CMS_5010 ? 60 : 35);
    $out .= "NM1" . "*41" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*" . "*46";
    if (trim($claim->x12gsreceiverid()) == '470819582') {
        // if ECLAIMS EDI
        $out .= "*" . $claim->clearingHouseETIN();
    } else {
        $out .= "*" . $claim->billingFacilityETIN();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "PER" . "*IC" . "*" . $claim->billingContactName() . "*TE" . "*" . $claim->billingContactPhone();
    if (!$CMS_5010 && $claim->x12gsper06()) {
        $out .= "*ED*" . $claim->x12gsper06();
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "NM1" . "*40" . "*2" . "*" . $claim->clearingHouseName() . "*" . "*" . "*" . "*" . "*46" . "*" . $claim->clearingHouseETIN() . "~\n";
    $HLcount = 1;
    ++$edicount;
    $out .= "HL" . "*{$HLcount}" . "*" . "*20" . "*1" . "~\n";
    $HLBillingPayToProvider = $HLcount++;
    // Situational PRV segment (for provider taxonomy code) omitted here.
    // Situational CUR segment (foreign currency information) omitted here.
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 63 www.nucc.org
    $billingFacilityName = substr($claim->billingFacilityName(), 0, $CMS_5010 ? 60 : 35);
    $out .= "NM1" . "*85" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*";
    if ($claim->billingFacilityNPI()) {
        $out .= "*XX*" . $claim->billingFacilityNPI();
    } else {
        $log .= "*** Billing facility has no NPI.\n";
        if ($CMS_5010) {
            $out .= "*XX*";
        } else {
            $out .= "*24*" . $claim->billingFacilityETIN();
        }
    }
    $out .= "~\n";
    ++$edicount;
    $out .= "N3" . "*" . $claim->billingFacilityStreet() . "~\n";
    ++$edicount;
    $out .= "N4" . "*" . $claim->billingFacilityCity() . "*" . $claim->billingFacilityState() . "*" . stripZipCode($claim->billingFacilityZip()) . "~\n";
    if ($CMS_5010 || $claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
        ++$edicount;
        $out .= "REF";
        if ($claim->federalIdType()) {
            $out .= "*" . $claim->federalIdType();
        } else {
            $out .= "*EI";
            // For dealing with the situation before adding TaxId type In facility.
        }
        $out .= "*" . $claim->billingFacilityETIN() . "~\n";
    }
    if ($claim->providerNumberType() && $claim->providerNumber() && !($CMS_5010 && $claim->billingFacilityNPI())) {
        ++$edicount;
        $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
    } else {
        if ($claim->providerNumber() && !$claim->providerNumberType()) {
            $log .= "*** Payer-specific provider insurance number is present but has no type assigned.\n";
        }
    }
    // Situational PER*1C segment omitted.
    // Pay-To Address defaults to billing provider and is no longer required in 5010.
    if (!$CMS_5010) {
        ++$edicount;
        // Field length is limited to 35. See nucc dataset page 63 www.nucc.org
        $billingFacilityName = substr($claim->billingFacilityName(), 0, $CMS_5010 ? 60 : 35);
        $out .= "NM1" . "*87" . "*2" . "*" . $billingFacilityName . "*" . "*" . "*" . "*";
        if ($claim->billingFacilityNPI()) {
            $out .= "*XX*" . $claim->billingFacilityNPI();
        } else {
            $out .= "*24*" . $claim->billingFacilityETIN();
        }
        $out .= "~\n";
        ++$edicount;
        $out .= "N3" . "*" . $claim->billingFacilityStreet() . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->billingFacilityCity() . "*" . $claim->billingFacilityState() . "*" . stripZipCode($claim->billingFacilityZip()) . "~\n";
        if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) {
            ++$edicount;
            $out .= "REF" . "*EI" . "*" . $claim->billingFacilityETIN() . "~\n";
        }
    }
    // Loop 2010AC Pay-To Plan Name omitted.  Includes:
    // NM1*PE, N3, N4, REF*2U, REF*EI
    $PatientHL = $claim->isSelfOfInsured() ? 0 : 1;
    $HLSubscriber = $HLcount++;
    ++$edicount;
    $out .= "HL" . "*{$HLSubscriber}" . "*{$HLBillingPayToProvider}" . "*22" . "*{$PatientHL}" . "~\n";
    if (!$claim->payerSequence()) {
        $log .= "*** Error: Insurance information is missing!\n";
    }
    ++$edicount;
    $out .= "SBR" . "*" . $claim->payerSequence() . "*" . ($claim->isSelfOfInsured() ? '18' : '') . "*" . $claim->groupNumber() . "*" . ($CMS_5010 && $claim->groupNumber() ? '' : $claim->groupName()) . "*" . $claim->insuredTypeCode() . "*" . "*" . "*" . "*" . $claim->claimType() . "~\n";
    // Segment PAT omitted.
    ++$edicount;
    $out .= "NM1" . "*IL" . "*1" . "*" . $claim->insuredLastName() . "*" . $claim->insuredFirstName() . "*" . $claim->insuredMiddleName() . "*" . "*" . "*MI" . "*" . $claim->policyNumber() . "~\n";
    // For 5010, further subscriber info is sent only if they are the patient.
    if (!$CMS_5010 || $claim->isSelfOfInsured()) {
        ++$edicount;
        $out .= "N3" . "*" . $claim->insuredStreet() . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->insuredCity() . "*" . $claim->insuredState() . "*" . stripZipCode($claim->insuredZip()) . "~\n";
        ++$edicount;
        $out .= "DMG" . "*D8" . "*" . $claim->insuredDOB() . "*" . $claim->insuredSex() . "~\n";
    }
    // Segment REF*SY (Subscriber Secondary Identification) omitted.
    // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
    // Segment PER*IC (Property and Casualty Subscriber Contact Information) omitted.
    ++$edicount;
    //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
    $payerName = substr($claim->payerName(), 0, $CMS_5010 ? 60 : 35);
    $out .= "NM1" . "*PR" . "*2" . "*" . $payerName . "*" . "*" . "*" . "*" . "*PI" . "*" . ($encounter_claim ? $claim->payerAltID() : $claim->payerID()) . "~\n";
    // if (!$claim->payerID()) {
    //   $log .= "*** CMS ID is missing for payer '" . $claim->payerName() . "'.\n";
    // }
    if (true) {
        // !$CMS_5010
        // The 5010 spec says:
        // "Required when the payer address is available and the submitter intends
        // for the claim to be printed on paper at the next EDI location (for example, a
        // clearinghouse). If not required by this implementation guide, do not send."
        ++$edicount;
        $out .= "N3" . "*" . $claim->payerStreet() . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->payerCity() . "*" . $claim->payerState() . "*" . stripZipCode($claim->payerZip()) . "~\n";
    }
    // Segment REF (Payer Secondary Identification) omitted.
    // Segment REF (Billing Provider Secondary Identification) omitted.
    if (!$claim->isSelfOfInsured()) {
        ++$edicount;
        $out .= "HL" . "*{$HLcount}" . "*{$HLSubscriber}" . "*23" . "*0" . "~\n";
        $HLcount++;
        ++$edicount;
        $out .= "PAT" . "*" . $claim->insuredRelationship() . "~\n";
        ++$edicount;
        $out .= "NM1" . "*QC" . "*1" . "*" . $claim->patientLastName() . "*" . $claim->patientFirstName() . "*" . $claim->patientMiddleName() . "~\n";
        ++$edicount;
        $out .= "N3" . "*" . $claim->patientStreet() . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->patientCity() . "*" . $claim->patientState() . "*" . stripZipCode($claim->patientZip()) . "~\n";
        ++$edicount;
        $out .= "DMG" . "*D8" . "*" . $claim->patientDOB() . "*" . $claim->patientSex() . "~\n";
        // Segment REF*Y4 (Property and Casualty Claim Number) omitted.
        // Segment REF (Property and Casualty Patient Identifier) omitted.
        // Segment PER (Property and Casualty Patient Contact Information) omitted.
    }
    // end of patient different from insured
    $proccount = $claim->procCount();
    $clm_total_charges = 0;
    for ($prockey = 0; $prockey < $proccount; ++$prockey) {
        $clm_total_charges += $claim->cptCharges($prockey);
    }
    if (!$clm_total_charges) {
        $log .= "*** This claim has no charges!\n";
    }
    ++$edicount;
    $out .= "CLM" . "*{$pid}-{$encounter}" . "*" . sprintf("%.2f", $clm_total_charges) . "*" . "*" . "*" . sprintf('%02d', $claim->facilityPOS()) . ":" . ($CMS_5010 ? "B" : "") . ":" . $claim->frequencyTypeCode() . "*Y" . "*A" . "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') . "*Y" . ($CMS_5010 ? "" : "*C") . "~\n";
    if ($claim->onsetDate() && $claim->onsetDate() !== $claim->serviceDate() && $claim->onsetDateValid()) {
        ++$edicount;
        $out .= "DTP" . "*431" . "*D8" . "*" . $claim->onsetDate() . "~\n";
    }
    if ($claim->dateInitialTreatment() && $claim->onsetDateValid()) {
        ++$edicount;
        $out .= "DTP" . "*454" . "*D8" . "*" . $claim->dateInitialTreatment() . "~\n";
    }
    // Segment DTP*304 (Last Seen Date) omitted.
    // Segment DTP*453 (Acute Manifestation Date) omitted.
    // Segment DTP*439 (Accident Date) omitted.
    // Segment DTP*484 (Last Menstrual Period Date) omitted.
    // Segment DTP*455 (Last X-Ray Date) omitted.
    // Segment DTP*471 (Hearing and Vision Prescription Date) omitted.
    // Segments DTP (Disability Dates) omitted.
    // Segment DTP*297 (Last Worked Date) omitted.
    // Segment DTP*296 (Authorized Return to Work Date) omitted.
    if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->onsetDateValid()) {
        ++$edicount;
        $out .= "DTP" . "*435" . "*D8" . "*" . $claim->onsetDate() . "~\n";
    }
    // Segment DTP*096 (Discharge Date) omitted.
    // Segments DTP (Assumed and Relinquished Care Dates) omitted.
    // Segment DTP*444 (Property and Casualty Date of First Contact) omitted.
    // Segment DTP*050 (Repricer Received Date) omitted.
    // Segment PWK (Claim Supplemental Information) omitted.
    // Segment CN1 (Contract Information) omitted.
    $patientpaid = $claim->patientPaidAmount();
    if ($patientpaid != 0) {
        ++$edicount;
        $out .= "AMT" . "*F5" . "*" . $patientpaid . "~\n";
    }
    // Segment REF*4N (Service Authorization Exception Code) omitted.
    // Segment REF*F5 (Mandatory Medicare Crossover Indicator) omitted.
    // Segment REF*EW (Mammography Certification Number) omitted.
    // Segment REF*9F (Referral Number) omitted.
    if ($claim->priorAuth()) {
        ++$edicount;
        $out .= "REF" . "*G1" . "*" . $claim->priorAuth() . "~\n";
    }
    // Segment REF*F8 (Payer Claim Control Number) omitted.
    if ($claim->cliaCode() && ($CMS_5010 || $claim->claimType() === 'MB')) {
        // Required by Medicare when in-house labs are done.
        ++$edicount;
        $out .= "REF" . "*X4" . "*" . $claim->cliaCode() . "~\n";
    }
    // Segment REF*9A (Repriced Claim Number) omitted.
    // Segment REF*9C (Adjusted Repriced Claim Number) omitted.
    // Segment REF*LX (Investigational Device Exemption Number) omitted.
    // Segment REF*D9 (Claim Identifier for Transmission Intermediaries) omitted.
    // Segment REF*EA (Medical Record Number) omitted.
    // Segment REF*P4 (Demonstration Project Identifier) omitted.
    // Segment REF*1J (Care Plan Oversight) omitted.
    // Segment K3 (File Information) omitted.
    if ($claim->additionalNotes()) {
        // Claim note.
        ++$edicount;
        $out .= "NTE" . "*" . ($CMS_5010 ? "ADD" : "") . "*" . $claim->additionalNotes() . "~\n";
    }
    // Segment CR1 (Ambulance Transport Information) omitted.
    // Segment CR2 (Spinal Manipulation Service Information) omitted.
    // Segment CRC (Ambulance Certification) omitted.
    // Segment CRC (Patient Condition Information: Vision) omitted.
    // Segment CRC (Homebound Indicator) omitted.
    // Segment CRC (EPSDT Referral) omitted.
    // Diagnoses, up to $max_per_seg per HI segment.
    $max_per_seg = $CMS_5010 ? 12 : 8;
    $da = $claim->diagArray();
    $diag_type_code = 'BK';
    $tmp = 0;
    foreach ($da as $diag) {
        if ($tmp % $max_per_seg == 0) {
            if ($tmp) {
                $out .= "~\n";
            }
            ++$edicount;
            $out .= "HI";
            // Health Diagnosis Codes
        }
        $out .= "*{$diag_type_code}:" . $diag;
        $diag_type_code = 'BF';
        ++$tmp;
    }
    if ($tmp) {
        $out .= "~\n";
    }
    // Segment HI*BP (Anesthesia Related Procedure) omitted.
    // Segment HI*BG (Condition Information) omitted.
    // Segment HCP (Claim Pricing/Repricing Information) omitted.
    if ($claim->referrerLastName()) {
        // Medicare requires referring provider's name and UPIN.
        ++$edicount;
        $out .= "NM1" . "*DN" . "*1" . "*" . $claim->referrerLastName() . "*" . $claim->referrerFirstName() . "*" . $claim->referrerMiddleName() . "*" . "*";
        if ($CMS_5010 || $claim->referrerNPI()) {
            $out .= "*XX" . "*" . $claim->referrerNPI();
        } else {
            $out .= "*34" . "*" . $claim->referrerSSN();
        }
        $out .= "~\n";
        if (!$CMS_5010 && $claim->referrerTaxonomy()) {
            ++$edicount;
            $out .= "PRV" . "*RF" . "*ZZ" . "*" . $claim->referrerTaxonomy() . "~\n";
        }
        if (!CMS_5010 && $claim->referrerUPIN()) {
            ++$edicount;
            $out .= "REF" . "*1G" . "*" . $claim->referrerUPIN() . "~\n";
        }
    }
    /* Per the implementation guide lines, only include this information if it is different 
     * than the Loop 2010AA information
     */
    if (!$CMS_5010 || $claim->providerNPIValid() && $claim->billingFacilityNPI() !== $claim->providerNPI()) {
        ++$edicount;
        $out .= "NM1" . "*82" . "*1" . "*" . $claim->providerLastName() . "*" . $claim->providerFirstName() . "*" . $claim->providerMiddleName() . "*" . "*";
        if ($CMS_5010 || $claim->providerNPI()) {
            $out .= "*XX" . "*" . $claim->providerNPI();
        } else {
            $out .= "*34" . "*" . $claim->providerSSN();
            $log .= "*** Rendering provider has no NPI.\n";
        }
        $out .= "~\n";
        if ($claim->providerTaxonomy()) {
            ++$edicount;
            $out .= "PRV" . "*PE" . "*" . ($CMS_5010 ? "PXC" : "ZZ") . "*" . $claim->providerTaxonomy() . "~\n";
        }
        // End of Loop 2310B
    } else {
        // This loop can only get skipped if we are generating a 5010 claim
        if (!$claim->providerNPIValid()) {
            /* If the loop was skipped because the provider NPI was invalid, generate
             * a warning for the log.*/
            $log .= "*** Skipping 2310B because " . $claim->providerLastName() . "," . $claim->providerFirstName() . " has invalid NPI.\n";
        }
        /* Skipping this segment because the providerNPI and the billingFacilityNPI are identical
         * is a normal condition, so no need to warn.
         */
    }
    // 4010: REF*1C is required here for the Medicare provider number if NPI was
    // specified in NM109.  Not sure if other payers require anything here.
    // --- apparently ECLAIMS, INC wants the data in 2010 but NOT in 2310B - tony@mi-squared.com
    //
    // 5010 spec says nothing here if NPI was specified.
    //
    if ($CMS_5010 && !$claim->providerNPI() && in_array($claim->providerNumberType(), array('0B', '1G', 'G2', 'LU')) || !$CMS_5010 && trim($claim->x12gsreceiverid()) != '470819582') {
        if ($claim->providerNumber()) {
            ++$edicount;
            $out .= "REF" . "*" . $claim->providerNumberType() . "*" . $claim->providerNumber() . "~\n";
        }
    }
    // Loop 2310D is omitted in the case of home visits (POS=12).
    if ($claim->facilityPOS() != 12 && (!$CMS_5010 || $claim->facilityNPI() != $claim->billingFacilityNPI())) {
        ++$edicount;
        $out .= "NM1" . "*77" . "*2";
        //Field length is limited to 35. See nucc dataset page 77 www.nucc.org
        $facilityName = substr($claim->facilityName(), 0, $CMS_5010 ? 60 : 35);
        if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) {
            $out .= "*" . $facilityName;
        }
        if ($claim->facilityNPI() || $claim->facilityETIN()) {
            $out .= "*" . "*" . "*" . "*";
            if ($CMS_5010 || $claim->facilityNPI()) {
                $out .= "*XX*" . $claim->facilityNPI();
            } else {
                $out .= "*24*" . $claim->facilityETIN();
            }
            if (!$claim->facilityNPI()) {
                $log .= "*** Service location has no NPI.\n";
            }
        }
        $out .= "~\n";
        if ($claim->facilityStreet()) {
            ++$edicount;
            $out .= "N3" . "*" . $claim->facilityStreet() . "~\n";
        }
        if ($claim->facilityState()) {
            ++$edicount;
            $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . stripZipCode($claim->facilityZip()) . "~\n";
        }
    }
    // Segment REF (Service Facility Location Secondary Identification) omitted.
    // Segment PER (Service Facility Contact Information) omitted.
    // Loop 2310E, Supervising Provider
    //
    if ($claim->supervisorLastName()) {
        ++$edicount;
        $out .= "NM1" . "*DQ" . "*1" . "*" . $claim->supervisorLastName() . "*" . $claim->supervisorFirstName() . "*" . $claim->supervisorMiddleName() . "*" . "*";
        // Name Suffix
        if ($CMS_5010 || $claim->supervisorNPI()) {
            $out .= "*XX" . "*" . $claim->supervisorNPI();
        } else {
            $out .= "*34" . "*" . $claim->supervisorSSN();
        }
        if (!$claim->supervisorNPI()) {
            $log .= "*** Supervising Provider has no NPI.\n";
        }
        $out .= "~\n";
        if ($claim->supervisorNumber()) {
            ++$edicount;
            $out .= "REF" . "*" . $claim->supervisorNumberType() . "*" . $claim->supervisorNumber() . "~\n";
        }
    }
    // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
    // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
    $prev_pt_resp = $clm_total_charges;
    // for computation below
    // Loops 2320 and 2330*, other subscriber/payer information.
    // Remember that insurance index 0 is always for the payer being billed
    // by this claim, and 1 and above are always for the "other" payers.
    //
    for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
        $tmp1 = $claim->claimType($ins);
        $tmp2 = 'C1';
        // Here a kludge. See page 321.
        if ($tmp1 === 'CI') {
            $tmp2 = 'C1';
        }
        if ($tmp1 === 'AM') {
            $tmp2 = 'AP';
        }
        if ($tmp1 === 'HM') {
            $tmp2 = 'HM';
        }
        if ($tmp1 === 'MB') {
            $tmp2 = 'MB';
        }
        if ($tmp1 === 'MC') {
            $tmp2 = 'MC';
        }
        if ($tmp1 === '09') {
            $tmp2 = 'PP';
        }
        ++$edicount;
        $out .= "SBR" . "*" . $claim->payerSequence($ins) . "*" . $claim->insuredRelationship($ins) . "*" . $claim->groupNumber($ins) . "*" . ($CMS_5010 && $claim->groupNumber($ins) ? '' : $claim->groupName($ins)) . "*" . ($CMS_5010 ? $claim->insuredTypeCode($ins) : $tmp2) . "*" . "*" . "*" . "*" . $claim->claimType($ins) . "~\n";
        // Things that apply only to previous payers, not future payers.
        //
        if ($claim->payerSequence($ins) < $claim->payerSequence()) {
            // Generate claim-level adjustments.
            $aarr = $claim->payerAdjustments($ins);
            foreach ($aarr as $a) {
                ++$edicount;
                $out .= "CAS" . "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
            }
            $payerpaid = $claim->payerTotals($ins);
            ++$edicount;
            $out .= "AMT" . "*D" . "*" . $payerpaid[1] . "~\n";
            // Segment AMT*A8 (COB Total Non-Covered Amount) omitted.
            // Segment AMT*EAF (Remaining Patient Liability) omitted.
            if (!$CMS_5010) {
                // Patient responsibility amount as of this previous payer.
                $prev_pt_resp -= $payerpaid[1];
                // reduce by payments
                $prev_pt_resp -= $payerpaid[2];
                // reduce by adjustments
                ++$edicount;
                $out .= "AMT" . "*B6" . "*" . sprintf('%.2f', $payerpaid[1] + $prev_pt_resp) . "~\n";
                ++$edicount;
                $out .= "AMT" . "*F2" . "*" . sprintf('%.2f', $prev_pt_resp) . "~\n";
            }
        }
        // End of things that apply only to previous payers.
        if (!$CMS_5010) {
            ++$edicount;
            $out .= "DMG" . "*D8" . "*" . $claim->insuredDOB($ins) . "*" . $claim->insuredSex($ins) . "~\n";
        }
        ++$edicount;
        $out .= "OI" . "*" . "*" . "*" . ($claim->billingFacilityAssignment($ins) ? 'Y' : 'N') . "*" . ($CMS_5010 ? '' : 'B') . "*" . "*Y" . "~\n";
        // Segment MOA (Medicare Outpatient Adjudication) omitted.
        ++$edicount;
        $out .= "NM1" . "*IL" . "*1" . "*" . $claim->insuredLastName($ins) . "*" . $claim->insuredFirstName($ins) . "*" . $claim->insuredMiddleName($ins) . "*" . "*" . "*MI" . "*" . $claim->policyNumber($ins) . "~\n";
        ++$edicount;
        $out .= "N3" . "*" . $claim->insuredStreet($ins) . "~\n";
        ++$edicount;
        $out .= "N4" . "*" . $claim->insuredCity($ins) . "*" . $claim->insuredState($ins) . "*" . stripZipCode($claim->insuredZip($ins)) . "~\n";
        // Segment REF (Other Subscriber Secondary Identification) omitted.
        ++$edicount;
        //Field length is limited to 35. See nucc dataset page 81 www.nucc.org
        $payerName = substr($claim->payerName($ins), 0, $CMS_5010 ? 60 : 35);
        $out .= "NM1" . "*PR" . "*2" . "*" . $payerName . "*" . "*" . "*" . "*" . "*PI" . "*" . $claim->payerID($ins) . "~\n";
        // if (!$claim->payerID($ins)) {
        //   $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n";
        // }
        // Payer address (N3 and N4) are added below so that Gateway EDI can
        // auto-generate secondary claims.  These do NOT appear in my copy of
        // the spec!  -- Rod 2008-06-12
        if ($CMS_5010 || trim($claim->x12gsreceiverid()) == '431420764') {
            // if Gateway EDI
            ++$edicount;
            $out .= "N3" . "*" . $claim->payerStreet($ins) . "~\n";
            //
            ++$edicount;
            $out .= "N4" . "*" . $claim->payerCity($ins) . "*" . $claim->payerState($ins) . "*" . stripZipCode($claim->payerZip($ins)) . "~\n";
        }
        // end Gateway EDI
        // Segment DTP*573 (Claim Check or Remittance Date) omitted.
        // Segment REF (Other Payer Secondary Identifier) omitted.
        // Segment REF*G1 (Other Payer Prior Authorization Number) omitted.
        // Segment REF*9F (Other Payer Referral Number) omitted.
        // Segment REF*T4 (Other Payer Claim Adjustment Indicator) omitted.
        // Segment REF*F8 (Other Payer Claim Control Number) omitted.
        // Segment NM1 (Other Payer Referring Provider) omitted.
        // Segment REF (Other Payer Referring Provider Secondary Identification) omitted.
        // Segment NM1 (Other Payer Rendering Provider) omitted.
        // Segment REF (Other Payer Rendering Provider Secondary Identification) omitted.
        // Segment NM1 (Other Payer Service Facility Location) omitted.
        // Segment REF (Other Payer Service Facility Location Secondary Identification) omitted.
        // Segment NM1 (Other Payer Supervising Provider) omitted.
        // Segment REF (Other Payer Supervising Provider Secondary Identification) omitted.
        // Segment NM1 (Other Payer Billing Provider) omitted.
        // Segment REF (Other Payer Billing Provider Secondary Identification) omitted.
    }
    // End loops 2320/2330*.
    $loopcount = 0;
    // Procedure loop starts here.
    //
    for ($prockey = 0; $prockey < $proccount; ++$prockey) {
        ++$loopcount;
        ++$edicount;
        $out .= "LX" . "*{$loopcount}" . "~\n";
        ++$edicount;
        $out .= "SV1" . "*HC:" . $claim->cptKey($prockey) . "*" . sprintf('%.2f', $claim->cptCharges($prockey)) . "*UN" . "*" . $claim->cptUnits($prockey) . "*" . "*" . "*";
        $dia = $claim->diagIndexArray($prockey);
        $i = 0;
        foreach ($dia as $dindex) {
            if ($i) {
                $out .= ':';
            }
            $out .= $dindex;
            if (++$i >= 4) {
                break;
            }
        }
        $out .= "~\n";
        if (!$claim->cptCharges($prockey)) {
            $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no charges!\n";
        }
        if (empty($dia)) {
            $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' is not justified!\n";
        }
        // Segment SV5 (Durable Medical Equipment Service) omitted.
        // Segment PWK (Line Supplemental Information) omitted.
        // Segment PWK (Durable Medical Equipment Certificate of Medical Necessity Indicator) omitted.
        // Segment CR1 (Ambulance Transport Information) omitted.
        // Segment CR3 (Durable Medical Equipment Certification) omitted.
        // Segment CRC (Ambulance Certification) omitted.
        // Segment CRC (Hospice Employee Indicator) omitted.
        // Segment CRC (Condition Indicator / Durable Medical Equipment) omitted.
        ++$edicount;
        $out .= "DTP" . "*472" . "*D8" . "*" . $claim->serviceDate() . "~\n";
        $testnote = rtrim($claim->cptNotecodes($prockey));
        if (!empty($testnote)) {
            ++$edicount;
            $out .= "NTE" . "*ADD" . "*" . $claim->cptNotecodes($prockey) . "~\n";
        }
        // Segment DTP*471 (Prescription Date) omitted.
        // Segment DTP*607 (Revision/Recertification Date) omitted.
        // Segment DTP*463 (Begin Therapy Date) omitted.
        // Segment DTP*461 (Last Certification Date) omitted.
        // Segment DTP*304 (Last Seen Date) omitted.
        // Segment DTP (Test Date) omitted.
        // Segment DTP*011 (Shipped Date) omitted.
        // Segment DTP*455 (Last X-Ray Date) omitted.
        // Segment DTP*454 (Initial Treatment Date) omitted.
        // Segment QTY (Ambulance Patient Count) omitted.
        // Segment QTY (Obstetric Anesthesia Additional Units) omitted.
        // Segment MEA (Test Result) omitted.
        // Segment CN1 (Contract Information) omitted.
        // Segment REF*9B (Repriced Line Item Reference Number) omitted.
        // Segment REF*9D (Adjusted Repriced Line Item Reference Number) omitted.
        // Segment REF*G1 (Prior Authorization) omitted.
        // Segment REF*6R (Line Item Control Number) omitted.
        //   (Really oughta have this for robust 835 posting!)
        // Segment REF*EW (Mammography Certification Number) omitted.
        // Segment REF*X4 (CLIA Number) omitted.
        // Segment REF*F4 (Referring CLIA Facility Identification) omitted.
        // Segment REF*BT (Immunization Batch Number) omitted.
        // Segment REF*9F (Referral Number) omitted.
        // Segment AMT*T (Sales Tax Amount) omitted.
        // Segment AMT*F4 (Postage Claimed Amount) omitted.
        // Segment K3 (File Information) omitted.
        // Segment NTE (Line Note) omitted.
        // Segment NTE (Third Party Organization Notes) omitted.
        // Segment PS1 (Purchased Service Information) omitted.
        // Segment HCP (Line Pricing/Repricing Information) omitted.
        if (!$CMS_5010) {
            // This segment was deleted for 5010.
            //
            // AMT*AAE segment for Approved Amount from previous payer.
            // Medicare secondaries seem to require this.
            //
            for ($ins = $claim->payerCount() - 1; $ins > 0; --$ins) {
                if ($claim->payerSequence($ins) > $claim->payerSequence()) {
                    continue;
                }
                // payer is future, not previous
                $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
                ++$edicount;
                $out .= "AMT" . "*AAE" . "*" . sprintf('%.2f', $claim->cptCharges($prockey) - $payerpaid[2]) . "~\n";
                break;
            }
        }
        // Loop 2410, Drug Information. Medicaid insurers seem to want this
        // with HCPCS codes.
        //
        $ndc = $claim->cptNDCID($prockey);
        if ($ndc) {
            ++$edicount;
            $out .= "LIN" . "*" . "*N4" . "*" . $ndc . "~\n";
            if (!preg_match('/^\\d\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d$/', $ndc, $tmp) && !preg_match('/^\\d{11}$/', $ndc)) {
                $log .= "*** NDC code '{$ndc}' has invalid format!\n";
            }
            ++$edicount;
            $tmpunits = $claim->cptNDCQuantity($prockey) * $claim->cptUnits($prockey);
            if (!$tmpunits) {
                $tmpunits = 1;
            }
            $out .= "CTP" . "*" . "*" . "*" . ($CMS_5010 ? '' : sprintf('%.2f', $claim->cptCharges($prockey) / $tmpunits)) . "*" . $claim->cptNDCQuantity($prockey) . "*" . $claim->cptNDCUOM($prockey) . "~\n";
        }
        // Segment REF (Prescription or Compound Drug Association Number) omitted.
        // Loop 2420A, Rendering Provider (service-specific).
        // Used if the rendering provider for this service line is different
        // from that in loop 2310B.
        //
        if ($claim->providerNPI() != $claim->providerNPI($prockey)) {
            ++$edicount;
            $out .= "NM1" . "*82" . "*1" . "*" . $claim->providerLastName($prockey) . "*" . $claim->providerFirstName($prockey) . "*" . $claim->providerMiddleName($prockey) . "*" . "*";
            if ($CMS_5010 || $claim->providerNPI($prockey)) {
                $out .= "*XX" . "*" . $claim->providerNPI($prockey);
            } else {
                $out .= "*34" . "*" . $claim->providerSSN($prockey);
            }
            if (!$claim->providerNPI($prockey)) {
                $log .= "*** Rendering provider has no NPI.\n";
            }
            $out .= "~\n";
            if ($claim->providerTaxonomy($prockey)) {
                ++$edicount;
                $out .= "PRV" . "*PE" . "*" . ($CMS_5010 ? "PXC" : "ZZ") . "*" . $claim->providerTaxonomy($prockey) . "~\n";
            }
            // Segment PRV*PE (Rendering Provider Specialty Information) omitted.
            // Segment REF (Rendering Provider Secondary Identification) omitted.
            // Segment NM1 (Purchased Service Provider Name) omitted.
            // Segment REF (Purchased Service Provider Secondary Identification) omitted.
            // Segment NM1,N3,N4 (Service Facility Location) omitted.
            // Segment REF (Service Facility Location Secondary Identification) omitted.
            // Segment NM1 (Supervising Provider Name) omitted.
            // Segment REF (Supervising Provider Secondary Identification) omitted.
            // Segment NM1,N3,N4 (Ordering Provider) omitted.
            // Segment REF (Ordering Provider Secondary Identification) omitted.
            // Segment PER (Ordering Provider Contact Information) omitted.
            // Segment NM1 (Referring Provider Name) omitted.
            // Segment REF (Referring Provider Secondary Identification) omitted.
            // Segments NM1*PW, N3, N4 (Ambulance Pick-Up Location) omitted.
            // Segments NM1*45, N3, N4 (Ambulance Drop-Off Location) omitted.
            // REF*1C is required here for the Medicare provider number if NPI was
            // specified in NM109.  Not sure if other payers require anything here.
            if (!$CMS_5010 && $claim->providerNumber($prockey)) {
                ++$edicount;
                $out .= "REF" . "*" . $claim->providerNumberType($prockey) . "*" . $claim->providerNumber($prockey) . "~\n";
            }
        }
        // Loop 2430, adjudication by previous payers.
        //
        for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
            if ($claim->payerSequence($ins) > $claim->payerSequence()) {
                continue;
            }
            // payer is future, not previous
            $payerpaid = $claim->payerTotals($ins, $claim->cptKey($prockey));
            $aarr = $claim->payerAdjustments($ins, $claim->cptKey($prockey));
            if ($payerpaid[1] == 0 && !count($aarr)) {
                $log .= "*** Procedure '" . $claim->cptKey($prockey) . "' has no payments or adjustments from previous payer!\n";
                continue;
            }
            ++$edicount;
            $out .= "SVD" . "*" . $claim->payerID($ins) . "*" . $payerpaid[1] . "*HC:" . $claim->cptKey($prockey) . "*" . "*" . $claim->cptUnits($prockey) . "~\n";
            $tmpdate = $payerpaid[0];
            foreach ($aarr as $a) {
                ++$edicount;
                $out .= "CAS" . "*" . $a[1] . "*" . $a[2] . "*" . $a[3] . "~\n";
                if (!$tmpdate) {
                    $tmpdate = $a[0];
                }
                // WTH is this??
                /*************************************************************
                   if ( isset($a[4]) &&
                   	$a[4] != null ) {
                   	$out .= "CAS02" . // Previous payer's adjustment reason
                      "*" . $a[4] .
                      "~\n";	
                   }
                   *************************************************************/
            }
            if ($tmpdate) {
                ++$edicount;
                $out .= "DTP" . "*573" . "*D8" . "*{$tmpdate}" . "~\n";
            }
            // Segment AMT*EAF (Remaining Patient Liability) omitted.
            // Segment LQ (Form Identification Code) omitted.
            // Segment FRM (Supporting Documentation) omitted.
        }
        // end loop 2430
    }
    // end this procedure
    ++$edicount;
    $out .= "SE" . "*{$edicount}" . "*0021" . "~\n";
    $out .= "GE" . "*1" . "*1" . "~\n";
    $out .= "IEA" . "*1" . "*000000001" . "~\n";
    // Remove any trailing empty fields (delimiters) from each segment.
    $out = preg_replace('/\\*+~/', '~', $out);
    $log .= "\n";
    return $out;
}