From 58097315ea042e525f3e244c0247ca9ac75f2ea8 Mon Sep 17 00:00:00 2001 From: stephen waite Date: Sat, 3 Nov 2018 17:46:30 -0400 Subject: [PATCH] migrating x12 gen 837 p to a class (#1934) --- interface/billing/billing_process.php | 33 +- library/billing/src/Claim.php | 19 +- library/billing/src/X12837P.php | 1241 ++++++++++++++++++++++++++++++++ library/gen_x12_837.inc.php | 1245 --------------------------------- library/gen_x12_837i.inc.php | 14 +- 5 files changed, 1276 insertions(+), 1276 deletions(-) create mode 100644 library/billing/src/X12837P.php delete mode 100644 library/gen_x12_837.inc.php diff --git a/interface/billing/billing_process.php b/interface/billing/billing_process.php index 32b401ede..06a1171c5 100644 --- a/interface/billing/billing_process.php +++ b/interface/billing/billing_process.php @@ -2,36 +2,29 @@ /* * Billing process Program * - * This program process data for claims generation - * - * Copyright (C) 2016 Terry Hill - * Copyright (C) 2014 Brady Miller - * Copyright (C) 2017 Jerry Padgett - * - * LICENSE: This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 3 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://opensource.org/licenses/gpl-license.php. + * This program processes data for claims generation * * @package OpenEMR - * @author Terry Hill * @author Brady Miller + * @author Terry Hill * @author Jerry Padgett - * @link http://www.open-emr.org + * @author Stephen Waite + * @copyright Copyright (c) 2014 Brady Miller + * @copyright Copyright (c) 2016 Terry Hill + * @copyright Copyright (C) 2017 Jerry Padgett + * @copyright Copyright (c) 2018 Stephen Waite + * @link https://github.com/openemr/openemr/tree/master + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ + require_once("../globals.php"); require_once("$srcdir/patient.inc"); require_once("$srcdir/billrep.inc"); require_once("$srcdir/billing.inc"); -require_once("$srcdir/gen_x12_837.inc.php"); require_once("$srcdir/gen_hcfa_1500.inc.php"); +use OpenEMR\Billing\X12837P; + if (!verifyCsrfToken($_POST["csrf_token_form"])) { csrfNotVerified(); } @@ -235,7 +228,7 @@ function process_form($ar) $bill_info[] = xl("Claim ") . $claimid . xl(" has been re-opened.") . "\n"; } elseif (isset($ar['bn_x12']) || isset($ar['bn_x12_encounter'])) { $log = ''; - $segs = explode("~\n", gen_x12_837($patient_id, $encounter, $log, isset($ar['bn_x12_encounter']))); + $segs = explode("~\n", X12837P::gen_x12_837($patient_id, $encounter, $log, isset($ar['bn_x12_encounter']))); fwrite($hlog, $log); append_claim($segs); if (! updateClaim(false, $patient_id, $encounter, - 1, - 1, 2, 2, $bat_filename)) { diff --git a/library/billing/src/Claim.php b/library/billing/src/Claim.php index c93e71fba..afb6a2ffc 100644 --- a/library/billing/src/Claim.php +++ b/library/billing/src/Claim.php @@ -41,14 +41,25 @@ class Claim public $facilityService; // via matthew.vita orm work :) // This enforces the X12 Basic Character Set. Page A2. - private function x12Clean($str) + public function x12Clean($str) { return preg_replace('/[^A-Z0-9!"\\&\'()+,\\-.\\/;?=@ ]/', '', strtoupper($str)); } + + // X12 likes 9 digit zip codes also moving this from gen_x12 + // to pursue PSR-0 and PSR-4 + public function x12Zip($zip) + { + $zip = $this->x12Clean($zip); + if (strlen($zip) == 5) { + return $zip . "9999"; + } else { + return $zip; + } + } -// Make sure dates have no formatting and zero filled becomes blank -// Handles date time stamp formats as well - + // Make sure dates have no formatting and zero filled becomes blank + // Handles date time stamp formats as well public function cleanDate($date_field) { $cleandate = str_replace('-', '', substr($date_field, 0, 10)); diff --git a/library/billing/src/X12837P.php b/library/billing/src/X12837P.php new file mode 100644 index 000000000..61fd6f9e1 --- /dev/null +++ b/library/billing/src/X12837P.php @@ -0,0 +1,1241 @@ + + * @author Stephen Waite + * @copyright Copyright (c) 2009 Rod Roark + * @copyright Copyright (c) 2018 Stephen Waite + * @link https://github.com/openemr/openemr/tree/master + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + +namespace OpenEMR\Billing; + +require_once(dirname(__FILE__) . "/../../invoice_summary.inc.php"); + +use OpenEMR\Billing\Claim; + +class X12837P +{ + public static function gen_x12_837($pid, $encounter, &$log, $encounter_claim = false) + { + $today = time(); + $out = ''; + $claim = new Claim($pid, $encounter); + $edicount = 0; + $HLcount = 0; + + $log .= "Generating claim $pid" . "-" . $encounter . " for " . + $claim->patientFirstName() . ' ' . + $claim->patientMiddleName() . ' ' . + $claim->patientLastName() . ' on ' . + date('Y-m-d H:i', $today) . ".\n"; + + $out .= "ISA" . + "*" . $claim->x12gsisa01() . + "*" . $claim->x12gsisa02() . + "*" . $claim->x12gsisa03() . + "*" . $claim->x12gsisa04() . + "*" . $claim->x12gsisa05() . + "*" . $claim->x12gssenderid() . + "*" . $claim->x12gsisa07() . + "*" . $claim->x12gsreceiverid() . + "*" . "030911" . // dummy data replace by billing_process.php + "*" . "1630" . // ditto + "*" . "^" . + "*" . "00501" . + "*" . "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" . + "*" . $claim->x12gsversionstring() . + "~\n"; + + ++$edicount; + $out .= "BHT" . + "*" . "0019" . // 0019 is required here + "*" . "00" . // 00 = original transmission + "*" . "0123" . // reference identification + "*" . date('Ymd', $today) . // transaction creation date + "*" . date('Hi', $today) . // transaction creation time + "*" . ($encounter_claim ? "RP" : "CH") . // RP = reporting, CH = chargeable + "~\n"; + + ++$edicount; + if ($claim->federalIdType() == "SY") { // check entity type for NM*102 1 == person, 2 == non-person entity + $firstName = $claim->providerFirstName(); + $lastName = $claim->providerLastName(); + $middleName = $claim->providerMiddleName(); + $suffixName = $claim->providerSuffixName(); + $out .= "NM1" . // Loop 1000A Submitter + "*" . "41" . + "*" . "1" . + "*" . $lastName . + "*" . $firstName . + "*" . $middleName . + "*" . // Name Prefix not used + "*" . $suffixName . + "*" . "46"; + } else { + $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); + if ($billingFacilityName == '') { + $log .= "*** billing facility name in 1000A loop is empty\n"; + } + $out .= "NM1" . + "*" . "41" . + "*" . "2" . + "*" . $billingFacilityName . + "*" . + "*" . + "*" . + "*" . + "*" . "46"; + } + $out .= "*" . $claim->billingFacilityETIN(); + $out .= "~\n"; + + ++$edicount; + $out .= "PER" . // Loop 1000A, Submitter EDI contact information + "*" . "IC" . + "*" . $claim->billingContactName() . + "*" . "TE" . + "*" . $claim->billingContactPhone() . + "*" . "EM" . + "*" . $claim->billingContactEmail(); + $out .= "~\n"; + + ++$edicount; + $out .= "NM1" . // Loop 1000B Receiver + "*" . "40" . + "*" . "2" . + "*" . $claim->clearingHouseName() . + "*" . + "*" . + "*" . + "*" . + "*" . "46" . + "*" . $claim->clearingHouseETIN() . + "~\n"; + + ++$HLcount; + ++$edicount; + $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop + "*" . $HLcount . + "*" . + "*" . "20" . + "*" . "1" . // 1 indicates there are child segments + "~\n"; + + $HLBillingPayToProvider = $HLcount++; + + // Situational PRV segment for provider taxonomy. + if ($claim->facilityTaxonomy()) { + ++$edicount; + $out .= "PRV" . + "*" . "BI" . + "*" . "PXC" . + "*" . $claim->facilityTaxonomy() . + "~\n"; + } + + // Situational CUR segment (foreign currency information) omitted here. + ++$edicount; + if ($claim->federalIdType() == "SY") { // check for entity type like in 1000A + $firstName = $claim->providerFirstName(); + $lastName = $claim->providerLastName(); + $middleName = $claim->providerMiddleName(); + $out .= "NM1" . + "*" . "85" . + "*" . "1" . + "*" . $lastName . + "*" . $firstName . + "*" . $middleName . + "*" . // Name Prefix not used + "*"; + } else { + $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); + if ($billingFacilityName == '') { + $log .= "*** billing facility name in 2010A loop is empty\n"; + } + $out .= "NM1" . // Loop 2010AA Billing Provider + "*" . "85" . + "*" . "2" . + "*" . $billingFacilityName . + "*" . + "*" . + "*" . + "*"; + } + if ($claim->billingFacilityNPI()) { + $out .= "*XX*" . $claim->billingFacilityNPI(); + } else { + $log .= "*** Billing facility has no NPI.\n"; + } + $out .= "~\n"; + + ++$edicount; + $out .= "N3" . + "*" . $claim->billingFacilityStreet() . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->billingFacilityCity() . + "*" . $claim->billingFacilityState() . + "*" . $claim->x12Zip($claim->billingFacilityZip()) . + "~\n"; + + if ($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"; + } else { + $log .= "*** No billing facility NPI and/or ETIN.\n"; + } + if ($claim->providerNumberType() && $claim->providerNumber() && !$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 but may be useful + if ($claim->facilityStreet() != $claim->billingFacilityStreet()) { + ++$edicount; + $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); + $out .= "NM1" . // Loop 2010AB Pay-To Provider + "*" . "87" . + "*" . "2" . + "*" . $billingFacilityName . + "*" . + "*" . + "*" . + "*"; + if ($claim->billingFacilityNPI()) { + $out .= "*XX*" . $claim->billingFacilityNPI(); + } + $out .= "~\n"; + + ++$edicount; + $out .= "N3" . + "*" . $claim->billingFacilityStreet() . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->billingFacilityCity() . + "*" . $claim->billingFacilityState() . + "*" . $claim->x12Zip($claim->billingFacilityZip()) . + "~\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" . // Loop 2000B Subscriber HL Loop + "*" . $HLSubscriber . + "*" . $HLBillingPayToProvider . + "*" . "22" . + "*" . $PatientHL . + "~\n"; + + if (!$claim->payerSequence()) { + $log .= "*** Error: Insurance information is missing!\n"; + } + + ++$edicount; + $out .= "SBR" . // Subscriber Information + "*" . $claim->payerSequence() . + "*" . ($claim->isSelfOfInsured() ? '18' : '') . + "*" . $claim->groupNumber() . + "*" . ($claim->groupNumber() ? '' : $claim->groupName()) . // if groupNumber no groupName + "*" . $claim->insuredTypeCode() . // applies for secondary medicare + "*" . + "*" . + "*" . + "*" . $claim->claimType() . + "~\n"; + + // Segment PAT omitted. + + ++$edicount; + $out .= "NM1" . // Loop 2010BA Subscriber + "*" . "IL" . + "*" . "1" . // 1 = person, 2 = non-person + "*" . $claim->insuredLastName() . + "*" . $claim->insuredFirstName() . + "*" . $claim->insuredMiddleName() . + "*" . + "*" . // Name Suffix not used + "*" . "MI" . + // "MI" = Member Identification Number + // "II" = Standard Unique Health Identifier, "Required if the + // HIPAA Individual Patient Identifier is mandated use." + // Here we presume that is not true yet. + "*" . $claim->policyNumber() . + "~\n"; + + // For 5010, further subscriber info is sent only if they are the patient. + if ($claim->isSelfOfInsured()) { + ++$edicount; + $out .= "N3" . + "*" . $claim->insuredStreet() . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->insuredCity() . + "*" . $claim->insuredState() . + "*" . $claim->x12Zip($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; + $payerName = substr($claim->payerName(), 0, 60); + $out .= "NM1" . // Loop 2010BB Payer + "*" . "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->x12Zip($claim->payerZip()) . + "~\n"; + + // Segment REF (Payer Secondary Identification) omitted. + // Segment REF (Billing Provider Secondary Identification) omitted. + + if (!$claim->isSelfOfInsured()) { + ++$edicount; + $out .= "HL" . // Loop 2000C Patient Information + "*" . $HLcount . + "*" . $HLSubscriber . + "*" . "23" . + "*" . "0" . + "~\n"; + + $HLcount++; + ++$edicount; + $out .= "PAT" . + "*" . $claim->insuredRelationship() . + "~\n"; + + ++$edicount; + $out .= "NM1" . // Loop 2010CA Patient + "*" . "QC" . + "*" . "1" . + "*" . $claim->patientLastName() . + "*" . $claim->patientFirstName(); + + if ($claim->patientMiddleName() !== '') { + $out .= "*" . $claim->patientMiddleName(); + } + + $out .= "~\n"; + + ++$edicount; + $out .= "N3" . + "*" . $claim->patientStreet() . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->patientCity() . + "*" . $claim->patientState() . + "*" . $claim->x12Zip($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" . // Loop 2300 Claim + "*" . $pid . "-" . $encounter . + "*" . sprintf("%.2f", $clm_total_charges) . + "*" . + "*" . + "*" . sprintf('%02d', $claim->facilityPOS()) . ":" . "B" . ":" . $claim->frequencyTypeCode() . + "*" . "Y" . + "*" . "A" . + "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') . + "*" . "Y" . + "~\n"; + + // above is for historical use of encounter onset date, now in misc_billing_options + // Segment DTP*431 (Onset of Current Symptoms or Illness) + // Segment DTP*484 (Last Menstrual Period Date) + + if ($claim->onsetDate() && ($claim->onsetDate() !== $claim->serviceDate()) && ($claim->onsetDateValid())) { + ++$edicount; + $out .= "DTP" . // Date of Onset + "*" . "431" . + "*" . "D8" . + "*" . $claim->onsetDate() . + "~\n"; + } else if ($claim->miscOnsetDate() && ($claim->miscOnsetDate() !== $claim->serviceDate()) + && ($claim->box14Qualifier()) && ($claim->miscOnsetDateValid())) { + ++$edicount; + $out .= "DTP" . + "*" . $claim->box14Qualifier() . + "*" . "D8" . + "*" . $claim->miscOnsetDate() . + "~\n"; + } + + // Segment DTP*304 (Last Seen Date) + // Segment DTP*453 (Acute Manifestation Date) + // Segment DTP*439 (Accident Date) + // Segment DTP*455 (Last X-Ray Date) + // Segment DTP*471 (Hearing and Vision Prescription Date) + // Segment DTP*314 (Disability) omitted. + // Segment DTP*360 (Initial Disability Period Start) omitted. + // Segment DTP*361 (Initial Disability Period End) omitted. + // Segment DTP*297 (Last Worked Date) + // Segment DTP*296 (Authorized Return to Work Date) + + // Segment DTP*454 (Initial Treatment Date) + + if ($claim->dateInitialTreatment() && ($claim->box15Qualifier()) && ($claim->dateInitialTreatmentValid())) { + ++$edicount; + $out .= "DTP" . // Date Last Seen + "*" . $claim->box15Qualifier() . + "*" . "D8" . + "*" . $claim->dateInitialTreatment() . + "~\n"; + } + + if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->onsetDateValid()) { + ++$edicount; + $out .= "DTP" . // Date of Hospitalization + "*" . "435" . + "*" . "D8" . + "*" . $claim->onsetDate() . + "~\n"; + } + + // above is for historical use of encounter onset date, now in misc_billing_options + if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->hospitalizedFromDateValid()) { + ++$edicount; + $out .= "DTP" . // Date of Admission + "*" . "435" . + "*" . "D8" . + "*" . $claim->hospitalizedFrom() . + "~\n"; + } + + // Segment DTP*096 (Discharge Date) + if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->hospitalizedToDateValid()) { + ++$edicount; + $out .= "DTP" . // Date of Discharge + "*" . "96" . + "*" . "D8" . + "*" . $claim->hospitalizedTo() . + "~\n"; + } + + // 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" . // Patient paid amount. Page 190/220. + "*" . "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" . // Prior Authorization Number + "*" . "G1" . + "*" . $claim->priorAuth() . + "~\n"; + } + + // Segment REF*F8 Payer Claim Control Number for claim re-submission.icn_resubmission_number + if (trim($claim->billing_options['icn_resubmission_number']) > 3) { + ++$edicount; + error_log("Method 1: " . $claim->billing_options['icn_resubmission_number'], 0); + $out .= "REF" . + "*" . "F8" . + "*" . $claim->icnResubmissionNumber() . + "~\n"; + } + + if ($claim->cliaCode() && ($claim->claimType() === 'MB')) { + // Required by Medicare when in-house labs are done. + ++$edicount; + $out .= "REF" . // Clinical Laboratory Improvement Amendment Number + "*" . "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" . // comments box 19 + "*" . "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). + if ($claim->epsdtFlag()) { + ++$edicount; + $out .= "CRC" . + "*" . "ZZ" . + "*" . "Y" . + "*" . $claim->medicaidReferralCode() . + "~\n"; + } + + // Diagnoses, up to $max_per_seg per HI segment. + $max_per_seg = 12; + $da = $claim->diagArray(); + if ($claim->diagtype == "ICD9") { + $diag_type_code = 'BK'; + } else { + $diag_type_code = 'ABK'; + } + $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; + if ($claim->diagtype == "ICD9") { + $diag_type_code = 'BF'; + } else { + $diag_type_code = 'ABF'; + } + ++$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" . // Loop 2310A Referring Provider + "*" . "DN" . + "*" . "1" . + "*" . $claim->referrerLastName() . + "*" . $claim->referrerFirstName() . + "*" . $claim->referrerMiddleName() . + "*" . + "*"; + if ($claim->referrerNPI()) { + $out .= + "*" . "XX" . + "*" . $claim->referrerNPI(); + } else { + $log .= "*** Referring provider has no NPI.\n"; + } + $out .= "~\n"; + } + + // Per the implementation guide lines, only include this information if it is different + // than the Loop 2010AA information + if ($claim->providerNPIValid() && ($claim->billingFacilityNPI() !== $claim->providerNPI())) { + ++$edicount; + $out .= "NM1" . // Loop 2310B Rendering Provider + "*" . "82" . + "*" . "1" . + "*" . $claim->providerLastName() . + "*" . $claim->providerFirstName() . + "*" . $claim->providerMiddleName() . + "*" . + "*"; + if ($claim->providerNPI()) { + $out .= + "*" . "XX" . + "*" . $claim->providerNPI(); + } else { + $log .= "*** Rendering provider has no NPI.\n"; + } + $out .= "~\n"; + + if ($claim->providerTaxonomy()) { + ++$edicount; + $out .= "PRV" . + "*" . "PE" . // Performing provider + "*" . "PXC" . + "*" . $claim->providerTaxonomy() . + "~\n"; + } else { + $log .= "*** Performing provider has no taxonomy code.\n"; + } + } else { + $log .= "*** Rendering provider is billing under a group.\n"; + } + 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"; + } + + if (!$claim->providerNPI() && in_array($claim->providerNumberType(), array('0B', '1G', 'G2', 'LU'))) { + if ($claim->providerNumber()) { + ++$edicount; + $out .= "REF" . + "*" . $claim->providerNumberType() . + "*" . $claim->providerNumber() . + "~\n"; + } + } + // End of Loop 2310B + + // Loop 2310C is omitted in the case of home visits (POS=12). + if ($claim->facilityPOS() != 12 && ($claim->facilityNPI() != $claim->billingFacilityNPI())) { + ++$edicount; + $out .= "NM1" . // Loop 2310C Service Location + "*" . "77" . + "*" . "2"; + $facilityName = substr($claim->facilityName(), 0, 60); + if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) { + $out .= + "*" . $facilityName; + } else { + $log .= "*** Check for invalid facility name, NPI, and/or tax id.\n"; + } + if ($claim->facilityNPI() || $claim->facilityETIN()) { + $out .= + "*" . + "*" . + "*" . + "*"; + if ($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() . + "*" . $claim->x12Zip($claim->facilityZip()) . + "~\n"; + } + } + // Segment REF (Service Facility Location Secondary Identification) omitted. + // Segment PER (Service Facility Contact Information) omitted. + + // Loop 2310D, Supervising Provider + if (! empty($claim->supervisorLastName())) { + ++$edicount; + $out .= "NM1" . + "*" . "DQ" . // Supervising Physician + "*" . "1" . // Person + "*" . $claim->supervisorLastName() . + "*" . $claim->supervisorFirstName() . + "*" . $claim->supervisorMiddleName() . + "*" . // NM106 not used + "*"; // Name Suffix not used + if ($claim->supervisorNPI()) { + $out .= + "*" . "XX" . + "*" . $claim->supervisorNPI(); + } else { + $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. + + // 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" . // Loop 2320, Subscriber Information - page 297/318 + "*" . $claim->payerSequence($ins) . + "*" . $claim->insuredRelationship($ins) . + "*" . $claim->groupNumber($ins) . + "*" . ($claim->groupNumber() ? '' : $claim->groupName()) . + "*" . $claim->insuredTypeCode($ins) . + "*" . + "*" . + "*" . + "*" . $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" . // Previous payer's claim-level adjustments. Page 301/323. + "*" . $a[1] . + "*" . $a[2] . + "*" . $a[3] . + "~\n"; + } + + $payerpaid = $claim->payerTotals($ins); + ++$edicount; + $out .= "AMT" . // Previous payer's paid amount. Page 307/332. + "*" . "D" . + "*" . $payerpaid[1] . + "~\n"; + // Segment AMT*A8 (COB Total Non-Covered Amount) omitted. + // Segment AMT*EAF (Remaining Patient Liability) omitted. + } // End of things that apply only to previous payers. + + ++$edicount; + $out .= "OI" . // Other Insurance Coverage Information. Page 310/344. + "*" . + "*" . + "*" . ($claim->billingFacilityAssignment($ins) ? 'Y' : 'N') . + // For this next item, the 5010 example in the spec does not match its + // description. So this might be wrong. + "*" . + "*" . + "*" . + "Y" . + "~\n"; + + // Segment MOA (Medicare Outpatient Adjudication) omitted. + ++$edicount; + $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 315/350. + "*" . "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->x12Zip($claim->insuredZip($ins)) . + "~\n"; + + // Segment REF (Other Subscriber Secondary Identification) omitted. + ++$edicount; + $payerName = substr($claim->payerName($ins), 0, 60); + $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 322/359. + "*" . "PR" . + "*" . "2" . + "*" . $payerName . + "*" . + "*" . + "*" . + "*" . + "*" . "PI" . + "*" . $claim->payerID($ins) . + "~\n"; + + if (!$claim->payerID($ins)) { + $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n"; + } + + ++$edicount; + $out .= "N3" . + "*" . $claim->payerStreet($ins) . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->payerCity($ins) . + "*" . $claim->payerState($ins) . + "*" . $claim->x12Zip($claim->payerZip($ins)) . + "~\n"; + // 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; + + // Loop 2400 Procedure Loop. + // + + for ($prockey = 0; $prockey < $proccount; ++$prockey) { + ++$loopcount; + ++$edicount; + $out .= "LX" . // Segment LX, Service Line. Page 398. + "*" . $loopcount . + "~\n"; + + ++$edicount; + $out .= "SV1" . // Segment SV1, Professional Service. Page 400. + "*" . "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; + } + } + + # needed for epstd + if ($claim->epsdtFlag()) { + $out .= "*" . + "*" . + "*" . + "*" . "Y" . + "~\n"; + } else { + $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 PWK01 (Line Supplemental Information) 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" . // Date of Service. Page 435. + "*" . "472" . + "*" . "D8" . + "*" . $claim->serviceDate() . + "~\n"; + + $testnote = rtrim($claim->cptNotecodes($prockey)); + if (!empty($testnote)) { + ++$edicount; + $out .= "NTE" . // Explain Unusual Circumstances. + "*" . "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. + + // Loop 2410, Drug Information. Medicaid insurers seem to want this + // with HCPCS codes. + // + $ndc = $claim->cptNDCID($prockey); + + if ($ndc) { + ++$edicount; + $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71). + "*" . // Per addendum, LIN01 is not used. + "*" . "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; + $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74). + "*" . + "*" . + "*" . + "*" . $claim->cptNDCQuantity($prockey) . + "*" . $claim->cptNDCUOM($prockey) . + // Note: 5010 documents "ME" (Milligrams) as an additional unit of measure. + "~\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" . // Loop 2420A Rendering Provider + "*" . "82" . + "*" . "1" . + "*" . $claim->providerLastName($prockey) . + "*" . $claim->providerFirstName($prockey) . + "*" . $claim->providerMiddleName($prockey) . + "*" . + "*"; + if ($claim->providerNPI($prockey)) { + $out .= + "*" . "XX" . + "*" . $claim->providerNPI($prockey); + } else { + $log .= "*** Rendering provider has no NPI.\n"; + } + $out .= "~\n"; + + // Segment PRV*PE (Rendering Provider Specialty Information) . + + if ($claim->providerTaxonomy($prockey)) { + ++$edicount; + $out .= "PRV" . + "*" . "PE" . // PErforming provider + "*" . "PXC" . + "*" . $claim->providerTaxonomy($prockey) . + "~\n"; + } + + // Segment REF (Rendering Provider Secondary Identification). + // 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->providerNumberType($prockey) == "G2") { + ++$edicount; $out .= "REF" . "*" . $claim->providerNumberType($prockey) . + "*" . $claim->providerNumber($prockey) . "~\n"; + } + } // end provider exception + + // Segment NM1 (Loop 2420B Purchased Service Provider Name) omitted. + // Segment REF (Loop 2420B Purchased Service Provider Secondary Identification) omitted. + // Segment NM1,N3,N4 (Loop 2420C Service Facility Location) omitted. + // Segment REF (Loop 2420C Service Facility Location Secondary Identification) omitted. + // Segment NM1 (Loop 2420D Supervising Provider Name) omitted. + // Segment REF (Loop 2420D Supervising Provider Secondary Identification) omitted. + + // Loop 2420E, Ordering Provider. + // for Medicare DME claims esp @joe on chat.open-emr.org :) + + if ($claim->Box17Qualifier() == "DK" && ($claim->claimType() === 'MB')) { + ++$edicount; + $out .= "NM1" . + "*" . $claim->Box17Qualifier() . + "*" . "1" . + "*" . $claim->billingProviderLastName() . + "*" . $claim->billingProviderFirstName() . + "*" . $claim->billingProviderMiddleName() . + "*" . + "*"; + if ($claim->billingProviderNPI()) { + $out .= + "*" . "XX" . + "*" . $claim->billingProviderNPI(); + } else { + $log .= "*** Ordering provider has no NPI.\n"; + } + $out .= "~\n"; + + ++$edicount; + $out .= "N3" . + "*" . $claim->billingProviderStreet() . + "*" . $claim->billingProviderStreetB() . + "~\n"; + + ++$edicount; + $out .= "N4" . + "*" . $claim->billingProviderCity() . + "*" . $claim->billingProviderState() . + "*" . $claim->x12Zip($claim->billingProviderZip()) . + "~\n"; + // 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. + + // 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" . // Service line adjudication. Page 554. + "*" . $claim->payerID($ins) . + "*" . $payerpaid[1] . + "*" . "HC:" . $claim->cptKey($prockey) . + "*" . + "*" . $claim->cptUnits($prockey) . + "~\n"; + + $tmpdate = $payerpaid[0]; + foreach ($aarr as $a) { + ++$edicount; + $out .= "CAS" . // Previous payer's line level adjustments. Page 558. + "*" . $a[1] . + "*" . $a[2] . + "*" . $a[3] . + "~\n"; + if (!$tmpdate) { + $tmpdate = $a[0]; + } + } + + if ($tmpdate) { + ++$edicount; + $out .= "DTP" . // Previous payer's line adjustment date. Page 493/566. + "*" . "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" . // SE Trailer + "*" . $edicount . + "*" . "0021" . + "~\n"; + + $out .= "GE" . // GE Trailer + "*" . "1" . + "*" . "1" . + "~\n"; + + $out .= "IEA" . // IEA Trailer + "*" . "1" . + "*" . "000000001" . + "~\n"; + + // Remove any trailing empty fields (delimiters) from each segment. + $out = preg_replace('/\*+~/', '~', $out); + + $log .= "\n"; + return $out; + } +} diff --git a/library/gen_x12_837.inc.php b/library/gen_x12_837.inc.php deleted file mode 100644 index 1578fe9be..000000000 --- a/library/gen_x12_837.inc.php +++ /dev/null @@ -1,1245 +0,0 @@ - - * @author Stephen Waite - * @copyright Copyright (c) 2009 Rod Roark - * @copyright Copyright (c) 2018 Stephen Waite - * @link https://github.com/openemr/openemr/tree/master - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 - */ - -require_once(dirname(__FILE__) . "/invoice_summary.inc.php"); - -use OpenEMR\Billing\Claim; - -function stripZipCode($zip) -{ - $temp = preg_replace('/[-\s]*/', '', $zip); - if (strlen($temp) == 5) { - return $temp . "9999"; - } else { - return $temp; - } -} - -function gen_x12_837($pid, $encounter, &$log, $encounter_claim = false) -{ - $today = time(); - $out = ''; - $claim = new Claim($pid, $encounter); - $edicount = 0; - $HLcount = 0; - - $log .= "Generating claim $pid" . "-" . $encounter . " for " . - $claim->patientFirstName() . ' ' . - $claim->patientMiddleName() . ' ' . - $claim->patientLastName() . ' on ' . - date('Y-m-d H:i', $today) . ".\n"; - - $out .= "ISA" . - "*" . $claim->x12gsisa01() . - "*" . $claim->x12gsisa02() . - "*" . $claim->x12gsisa03() . - "*" . $claim->x12gsisa04() . - "*" . $claim->x12gsisa05() . - "*" . $claim->x12gssenderid() . - "*" . $claim->x12gsisa07() . - "*" . $claim->x12gsreceiverid() . - "*" . "030911" . // dummy data replace by billing_process.php - "*" . "1630" . // ditto - "*" . "^" . - "*" . "00501" . - "*" . "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" . - "*" . $claim->x12gsversionstring() . - "~\n"; - - ++$edicount; - $out .= "BHT" . - "*" . "0019" . // 0019 is required here - "*" . "00" . // 00 = original transmission - "*" . "0123" . // reference identification - "*" . date('Ymd', $today) . // transaction creation date - "*" . date('Hi', $today) . // transaction creation time - "*" . ($encounter_claim ? "RP" : "CH") . // RP = reporting, CH = chargeable - "~\n"; - - ++$edicount; - if ($claim->federalIdType() == "SY") { // check entity type for NM*102 1 == person, 2 == non-person entity - $firstName = $claim->providerFirstName(); - $lastName = $claim->providerLastName(); - $middleName = $claim->providerMiddleName(); - $suffixName = $claim->providerSuffixName(); - $out .= "NM1" . // Loop 1000A Submitter - "*" . "41" . - "*" . "1" . - "*" . $lastName . - "*" . $firstName . - "*" . $middleName . - "*" . // Name Prefix not used - "*" . $suffixName . - "*" . "46"; - } else { - $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); - if ($billingFacilityName == '') { - $log .= "*** billing facility name in 1000A loop is empty\n"; - } - $out .= "NM1" . - "*" . "41" . - "*" . "2" . - "*" . $billingFacilityName . - "*" . - "*" . - "*" . - "*" . - "*" . "46"; - } - $out .= "*" . $claim->billingFacilityETIN(); - $out .= "~\n"; - - ++$edicount; - $out .= "PER" . // Loop 1000A, Submitter EDI contact information - "*" . "IC" . - "*" . $claim->billingContactName() . - "*" . "TE" . - "*" . $claim->billingContactPhone() . - "*" . "EM" . - "*" . $claim->billingContactEmail(); - $out .= "~\n"; - - ++$edicount; - $out .= "NM1" . // Loop 1000B Receiver - "*" . "40" . - "*" . "2" . - "*" . $claim->clearingHouseName() . - "*" . - "*" . - "*" . - "*" . - "*" . "46" . - "*" . $claim->clearingHouseETIN() . - "~\n"; - - ++$HLcount; - ++$edicount; - $out .= "HL" . // Loop 2000A Billing/Pay-To Provider HL Loop - "*" . $HLcount . - "*" . - "*" . "20" . - "*" . "1" . // 1 indicates there are child segments - "~\n"; - - $HLBillingPayToProvider = $HLcount++; - - // Situational PRV segment for provider taxonomy. - if ($claim->facilityTaxonomy()) { - ++$edicount; - $out .= "PRV" . - "*" . "BI" . - "*" . "PXC" . - "*" . $claim->facilityTaxonomy() . - "~\n"; - } - - // Situational CUR segment (foreign currency information) omitted here. - ++$edicount; - if ($claim->federalIdType() == "SY") { // check for entity type like in 1000A - $firstName = $claim->providerFirstName(); - $lastName = $claim->providerLastName(); - $middleName = $claim->providerMiddleName(); - $out .= "NM1" . - "*" . "85" . - "*" . "1" . - "*" . $lastName . - "*" . $firstName . - "*" . $middleName . - "*" . // Name Prefix not used - "*"; - } else { - $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); - if ($billingFacilityName == '') { - $log .= "*** billing facility name in 2010A loop is empty\n"; - } - $out .= "NM1" . // Loop 2010AA Billing Provider - "*" . "85" . - "*" . "2" . - "*" . $billingFacilityName . - "*" . - "*" . - "*" . - "*"; - } - if ($claim->billingFacilityNPI()) { - $out .= "*XX*" . $claim->billingFacilityNPI(); - } else { - $log .= "*** Billing facility has no NPI.\n"; - } - $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"; - if ($claim->federalIdType()) { - $out .= "*" . $claim->federalIdType(); - } else { - $out .= "*EI"; // For dealing with the situation before adding TaxId type In facility. - } - $out .= "*" . $claim->billingFacilityETIN() . "~\n"; - } else { - $log .= "*** No billing facility NPI and/or ETIN.\n"; - } - if ($claim->providerNumberType() && $claim->providerNumber() && !$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 but may be useful - if ($claim->facilityStreet() != $claim->billingFacilityStreet()) { - ++$edicount; - $billingFacilityName = substr($claim->billingFacilityName(), 0, 60); - $out .= "NM1" . // Loop 2010AB Pay-To Provider - "*" . "87" . - "*" . "2" . - "*" . $billingFacilityName . - "*" . - "*" . - "*" . - "*"; - if ($claim->billingFacilityNPI()) { - $out .= "*XX*" . $claim->billingFacilityNPI(); - } - $out .= "~\n"; - - ++$edicount; - $out .= "N3" . - "*" . $claim->billingFacilityStreet() . - "~\n"; - - ++$edicount; - $out .= "N4" . - "*" . $claim->billingFacilityCity() . - "*" . $claim->billingFacilityState() . - "*" . stripZipCode($claim->billingFacilityZip()) . - "~\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" . // Loop 2000B Subscriber HL Loop - "*" . $HLSubscriber . - "*" . $HLBillingPayToProvider . - "*" . "22" . - "*" . $PatientHL . - "~\n"; - - if (!$claim->payerSequence()) { - $log .= "*** Error: Insurance information is missing!\n"; - } - - ++$edicount; - $out .= "SBR" . // Subscriber Information - "*" . $claim->payerSequence() . - "*" . ($claim->isSelfOfInsured() ? '18' : '') . - "*" . $claim->groupNumber() . - "*" . ($claim->groupNumber() ? '' : $claim->groupName()) . // if groupNumber no groupName - "*" . $claim->insuredTypeCode() . // applies for secondary medicare - "*" . - "*" . - "*" . - "*" . $claim->claimType() . - "~\n"; - - // Segment PAT omitted. - - ++$edicount; - $out .= "NM1" . // Loop 2010BA Subscriber - "*" . "IL" . - "*" . "1" . // 1 = person, 2 = non-person - "*" . $claim->insuredLastName() . - "*" . $claim->insuredFirstName() . - "*" . $claim->insuredMiddleName() . - "*" . - "*" . // Name Suffix not used - "*" . "MI" . - // "MI" = Member Identification Number - // "II" = Standard Unique Health Identifier, "Required if the - // HIPAA Individual Patient Identifier is mandated use." - // Here we presume that is not true yet. - "*" . $claim->policyNumber() . - "~\n"; - - // For 5010, further subscriber info is sent only if they are the patient. - if ($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; - $payerName = substr($claim->payerName(), 0, 60); - $out .= "NM1" . // Loop 2010BB Payer - "*" . "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() . - "*" . stripZipCode($claim->payerZip()) . - "~\n"; - - // Segment REF (Payer Secondary Identification) omitted. - // Segment REF (Billing Provider Secondary Identification) omitted. - - if (!$claim->isSelfOfInsured()) { - ++$edicount; - $out .= "HL" . // Loop 2000C Patient Information - "*" . $HLcount . - "*" . $HLSubscriber . - "*" . "23" . - "*" . "0" . - "~\n"; - - $HLcount++; - ++$edicount; - $out .= "PAT" . - "*" . $claim->insuredRelationship() . - "~\n"; - - ++$edicount; - $out .= "NM1" . // Loop 2010CA Patient - "*" . "QC" . - "*" . "1" . - "*" . $claim->patientLastName() . - "*" . $claim->patientFirstName(); - - if ($claim->patientMiddleName() !== '') { - $out .= "*" . $claim->patientMiddleName(); - } - - $out .= "~\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" . // Loop 2300 Claim - "*" . $pid . "-" . $encounter . - "*" . sprintf("%.2f", $clm_total_charges) . - "*" . - "*" . - "*" . sprintf('%02d', $claim->facilityPOS()) . ":" . "B" . ":" . $claim->frequencyTypeCode() . - "*" . "Y" . - "*" . "A" . - "*" . ($claim->billingFacilityAssignment() ? 'Y' : 'N') . - "*" . "Y" . - "~\n"; - - // above is for historical use of encounter onset date, now in misc_billing_options - // Segment DTP*431 (Onset of Current Symptoms or Illness) - // Segment DTP*484 (Last Menstrual Period Date) - - if ($claim->onsetDate() && ($claim->onsetDate() !== $claim->serviceDate()) && ($claim->onsetDateValid())) { - ++$edicount; - $out .= "DTP" . // Date of Onset - "*" . "431" . - "*" . "D8" . - "*" . $claim->onsetDate() . - "~\n"; - } else if ($claim->miscOnsetDate() && ($claim->miscOnsetDate() !== $claim->serviceDate()) - && ($claim->box14Qualifier()) && ($claim->miscOnsetDateValid())) { - ++$edicount; - $out .= "DTP" . - "*" . $claim->box14Qualifier() . - "*" . "D8" . - "*" . $claim->miscOnsetDate() . - "~\n"; - } - - // Segment DTP*304 (Last Seen Date) - // Segment DTP*453 (Acute Manifestation Date) - // Segment DTP*439 (Accident Date) - // Segment DTP*455 (Last X-Ray Date) - // Segment DTP*471 (Hearing and Vision Prescription Date) - // Segment DTP*314 (Disability) omitted. - // Segment DTP*360 (Initial Disability Period Start) omitted. - // Segment DTP*361 (Initial Disability Period End) omitted. - // Segment DTP*297 (Last Worked Date) - // Segment DTP*296 (Authorized Return to Work Date) - - // Segment DTP*454 (Initial Treatment Date) - - if ($claim->dateInitialTreatment() && ($claim->box15Qualifier()) && ($claim->dateInitialTreatmentValid())) { - ++$edicount; - $out .= "DTP" . // Date Last Seen - "*" . $claim->box15Qualifier() . - "*" . "D8" . - "*" . $claim->dateInitialTreatment() . - "~\n"; - } - - if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->onsetDateValid()) { - ++$edicount; - $out .= "DTP" . // Date of Hospitalization - "*" . "435" . - "*" . "D8" . - "*" . $claim->onsetDate() . - "~\n"; - } - - // above is for historical use of encounter onset date, now in misc_billing_options - if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->hospitalizedFromDateValid()) { - ++$edicount; - $out .= "DTP" . // Date of Admission - "*" . "435" . - "*" . "D8" . - "*" . $claim->hospitalizedFrom() . - "~\n"; - } - - // Segment DTP*096 (Discharge Date) - if (strcmp($claim->facilityPOS(), '21') == 0 && $claim->hospitalizedToDateValid()) { - ++$edicount; - $out .= "DTP" . // Date of Discharge - "*" . "96" . - "*" . "D8" . - "*" . $claim->hospitalizedTo() . - "~\n"; - } - - // 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" . // Patient paid amount. Page 190/220. - "*" . "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" . // Prior Authorization Number - "*" . "G1" . - "*" . $claim->priorAuth() . - "~\n"; - } - - // Segment REF*F8 Payer Claim Control Number for claim re-submission.icn_resubmission_number - if (trim($claim->billing_options['icn_resubmission_number']) > 3) { - ++$edicount; - error_log("Method 1: " . $claim->billing_options['icn_resubmission_number'], 0); - $out .= "REF" . - "*" . "F8" . - "*" . $claim->icnResubmissionNumber() . - "~\n"; - } - - if ($claim->cliaCode() && ($claim->claimType() === 'MB')) { - // Required by Medicare when in-house labs are done. - ++$edicount; - $out .= "REF" . // Clinical Laboratory Improvement Amendment Number - "*" . "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" . // comments box 19 - "*" . "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). - if ($claim->epsdtFlag()) { - ++$edicount; - $out .= "CRC" . - "*" . "ZZ" . - "*" . "Y" . - "*" . $claim->medicaidReferralCode() . - "~\n"; - } - - // Diagnoses, up to $max_per_seg per HI segment. - $max_per_seg = 12; - $da = $claim->diagArray(); - if ($claim->diagtype == "ICD9") { - $diag_type_code = 'BK'; - } else { - $diag_type_code = 'ABK'; - } - $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; - if ($claim->diagtype == "ICD9") { - $diag_type_code = 'BF'; - } else { - $diag_type_code = 'ABF'; - } - ++$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" . // Loop 2310A Referring Provider - "*" . "DN" . - "*" . "1" . - "*" . $claim->referrerLastName() . - "*" . $claim->referrerFirstName() . - "*" . $claim->referrerMiddleName() . - "*" . - "*"; - if ($claim->referrerNPI()) { - $out .= - "*" . "XX" . - "*" . $claim->referrerNPI(); - } else { - $log .= "*** Referring provider has no NPI.\n"; - } - $out .= "~\n"; - } - - // Per the implementation guide lines, only include this information if it is different - // than the Loop 2010AA information - if ($claim->providerNPIValid() && ($claim->billingFacilityNPI() !== $claim->providerNPI())) { - ++$edicount; - $out .= "NM1" . // Loop 2310B Rendering Provider - "*" . "82" . - "*" . "1" . - "*" . $claim->providerLastName() . - "*" . $claim->providerFirstName() . - "*" . $claim->providerMiddleName() . - "*" . - "*"; - if ($claim->providerNPI()) { - $out .= - "*" . "XX" . - "*" . $claim->providerNPI(); - } else { - $log .= "*** Rendering provider has no NPI.\n"; - } - $out .= "~\n"; - - if ($claim->providerTaxonomy()) { - ++$edicount; - $out .= "PRV" . - "*" . "PE" . // Performing provider - "*" . "PXC" . - "*" . $claim->providerTaxonomy() . - "~\n"; - } else { - $log .= "*** Performing provider has no taxonomy code.\n"; - } - } else { - $log .= "*** Rendering provider is billing under a group.\n"; - } - 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"; - } - - if (!$claim->providerNPI() && in_array($claim->providerNumberType(), array('0B', '1G', 'G2', 'LU'))) { - if ($claim->providerNumber()) { - ++$edicount; - $out .= "REF" . - "*" . $claim->providerNumberType() . - "*" . $claim->providerNumber() . - "~\n"; - } - } - // End of Loop 2310B - - // Loop 2310C is omitted in the case of home visits (POS=12). - if ($claim->facilityPOS() != 12 && ($claim->facilityNPI() != $claim->billingFacilityNPI())) { - ++$edicount; - $out .= "NM1" . // Loop 2310C Service Location - "*" . "77" . - "*" . "2"; - $facilityName = substr($claim->facilityName(), 0, 60); - if ($claim->facilityName() || $claim->facilityNPI() || $claim->facilityETIN()) { - $out .= - "*" . $facilityName; - } else { - $log .= "*** Check for invalid facility name, NPI, and/or tax id.\n"; - } - if ($claim->facilityNPI() || $claim->facilityETIN()) { - $out .= - "*" . - "*" . - "*" . - "*"; - if ($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 2310D, Supervising Provider - if (! empty($claim->supervisorLastName())) { - ++$edicount; - $out .= "NM1" . - "*" . "DQ" . // Supervising Physician - "*" . "1" . // Person - "*" . $claim->supervisorLastName() . - "*" . $claim->supervisorFirstName() . - "*" . $claim->supervisorMiddleName() . - "*" . // NM106 not used - "*"; // Name Suffix not used - if ($claim->supervisorNPI()) { - $out .= - "*" . "XX" . - "*" . $claim->supervisorNPI(); - } else { - $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. - - // 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" . // Loop 2320, Subscriber Information - page 297/318 - "*" . $claim->payerSequence($ins) . - "*" . $claim->insuredRelationship($ins) . - "*" . $claim->groupNumber($ins) . - "*" . ($claim->groupNumber() ? '' : $claim->groupName()) . - "*" . $claim->insuredTypeCode($ins) . - "*" . - "*" . - "*" . - "*" . $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" . // Previous payer's claim-level adjustments. Page 301/323. - "*" . $a[1] . - "*" . $a[2] . - "*" . $a[3] . - "~\n"; - } - - $payerpaid = $claim->payerTotals($ins); - ++$edicount; - $out .= "AMT" . // Previous payer's paid amount. Page 307/332. - "*" . "D" . - "*" . $payerpaid[1] . - "~\n"; - // Segment AMT*A8 (COB Total Non-Covered Amount) omitted. - // Segment AMT*EAF (Remaining Patient Liability) omitted. - } // End of things that apply only to previous payers. - - ++$edicount; - $out .= "OI" . // Other Insurance Coverage Information. Page 310/344. - "*" . - "*" . - "*" . ($claim->billingFacilityAssignment($ins) ? 'Y' : 'N') . - // For this next item, the 5010 example in the spec does not match its - // description. So this might be wrong. - "*" . - "*" . - "*" . - "Y" . - "~\n"; - - // Segment MOA (Medicare Outpatient Adjudication) omitted. - ++$edicount; - $out .= "NM1" . // Loop 2330A Subscriber info for other insco. Page 315/350. - "*" . "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; - $payerName = substr($claim->payerName($ins), 0, 60); - $out .= "NM1" . // Loop 2330B Payer info for other insco. Page 322/359. - "*" . "PR" . - "*" . "2" . - "*" . $payerName . - "*" . - "*" . - "*" . - "*" . - "*" . "PI" . - "*" . $claim->payerID($ins) . - "~\n"; - - if (!$claim->payerID($ins)) { - $log .= "*** CMS ID is missing for payer '" . $claim->payerName($ins) . "'.\n"; - } - - ++$edicount; - $out .= "N3" . - "*" . $claim->payerStreet($ins) . - "~\n"; - - ++$edicount; - $out .= "N4" . - "*" . $claim->payerCity($ins) . - "*" . $claim->payerState($ins) . - "*" . stripZipCode($claim->payerZip($ins)) . - "~\n"; - // 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; - - // Loop 2400 Procedure Loop. - // - - for ($prockey = 0; $prockey < $proccount; ++$prockey) { - ++$loopcount; - ++$edicount; - $out .= "LX" . // Segment LX, Service Line. Page 398. - "*" . $loopcount . - "~\n"; - - ++$edicount; - $out .= "SV1" . // Segment SV1, Professional Service. Page 400. - "*" . "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; - } - } - - # needed for epstd - if ($claim->epsdtFlag()) { - $out .= "*" . - "*" . - "*" . - "*" . "Y" . - "~\n"; - } else { - $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 PWK01 (Line Supplemental Information) 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" . // Date of Service. Page 435. - "*" . "472" . - "*" . "D8" . - "*" . $claim->serviceDate() . - "~\n"; - - $testnote = rtrim($claim->cptNotecodes($prockey)); - if (!empty($testnote)) { - ++$edicount; - $out .= "NTE" . // Explain Unusual Circumstances. - "*" . "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. - - // Loop 2410, Drug Information. Medicaid insurers seem to want this - // with HCPCS codes. - // - $ndc = $claim->cptNDCID($prockey); - - if ($ndc) { - ++$edicount; - $out .= "LIN" . // Drug Identification. Page 500+ (Addendum pg 71). - "*" . // Per addendum, LIN01 is not used. - "*" . "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; - $out .= "CTP" . // Drug Pricing. Page 500+ (Addendum pg 74). - "*" . - "*" . - "*" . - "*" . $claim->cptNDCQuantity($prockey) . - "*" . $claim->cptNDCUOM($prockey) . - // Note: 5010 documents "ME" (Milligrams) as an additional unit of measure. - "~\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" . // Loop 2420A Rendering Provider - "*" . "82" . - "*" . "1" . - "*" . $claim->providerLastName($prockey) . - "*" . $claim->providerFirstName($prockey) . - "*" . $claim->providerMiddleName($prockey) . - "*" . - "*"; - if ($claim->providerNPI($prockey)) { - $out .= - "*" . "XX" . - "*" . $claim->providerNPI($prockey); - } else { - $log .= "*** Rendering provider has no NPI.\n"; - } - $out .= "~\n"; - - // Segment PRV*PE (Rendering Provider Specialty Information) . - - if ($claim->providerTaxonomy($prockey)) { - ++$edicount; - $out .= "PRV" . - "*" . "PE" . // PErforming provider - "*" . "PXC" . - "*" . $claim->providerTaxonomy($prockey) . - "~\n"; - } - - // Segment REF (Rendering Provider Secondary Identification). - // 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->providerNumberType($prockey) == "G2") { - ++$edicount; $out .= "REF" . "*" . $claim->providerNumberType($prockey) . - "*" . $claim->providerNumber($prockey) . "~\n"; - } - } // end provider exception - - // Segment NM1 (Loop 2420B Purchased Service Provider Name) omitted. - // Segment REF (Loop 2420B Purchased Service Provider Secondary Identification) omitted. - // Segment NM1,N3,N4 (Loop 2420C Service Facility Location) omitted. - // Segment REF (Loop 2420C Service Facility Location Secondary Identification) omitted. - // Segment NM1 (Loop 2420D Supervising Provider Name) omitted. - // Segment REF (Loop 2420D Supervising Provider Secondary Identification) omitted. - - // Loop 2420E, Ordering Provider. - // for Medicare DME claims esp @joe on chat.open-emr.org :) - - if ($claim->Box17Qualifier() == "DK" && ($claim->claimType() === 'MB')) { - ++$edicount; - $out .= "NM1" . - "*" . $claim->Box17Qualifier() . - "*" . "1" . - "*" . $claim->billingProviderLastName() . - "*" . $claim->billingProviderFirstName() . - "*" . $claim->billingProviderMiddleName() . - "*" . - "*"; - if ($claim->billingProviderNPI()) { - $out .= - "*" . "XX" . - "*" . $claim->billingProviderNPI(); - } else { - $log .= "*** Ordering provider has no NPI.\n"; - } - $out .= "~\n"; - - ++$edicount; - $out .= "N3" . - "*" . $claim->billingProviderStreet() . - "*" . $claim->billingProviderStreetB() . - "~\n"; - - ++$edicount; - $out .= "N4" . - "*" . $claim->billingProviderCity() . - "*" . $claim->billingProviderState() . - "*" . stripZipCode($claim->billingProviderZip()) . - "~\n"; - // 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. - - // 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" . // Service line adjudication. Page 554. - "*" . $claim->payerID($ins) . - "*" . $payerpaid[1] . - "*" . "HC:" . $claim->cptKey($prockey) . - "*" . - "*" . $claim->cptUnits($prockey) . - "~\n"; - - $tmpdate = $payerpaid[0]; - foreach ($aarr as $a) { - ++$edicount; - $out .= "CAS" . // Previous payer's line level adjustments. Page 558. - "*" . $a[1] . - "*" . $a[2] . - "*" . $a[3] . - "~\n"; - if (!$tmpdate) { - $tmpdate = $a[0]; - } - } - - if ($tmpdate) { - ++$edicount; - $out .= "DTP" . // Previous payer's line adjustment date. Page 493/566. - "*" . "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" . // SE Trailer - "*" . $edicount . - "*" . "0021" . - "~\n"; - - $out .= "GE" . // GE Trailer - "*" . "1" . - "*" . "1" . - "~\n"; - - $out .= "IEA" . // IEA Trailer - "*" . "1" . - "*" . "000000001" . - "~\n"; - - // Remove any trailing empty fields (delimiters) from each segment. - $out = preg_replace('/\*+~/', '~', $out); - - $log .= "\n"; - return $out; -} diff --git a/library/gen_x12_837i.inc.php b/library/gen_x12_837i.inc.php index bf14343b6..1782345c5 100644 --- a/library/gen_x12_837i.inc.php +++ b/library/gen_x12_837i.inc.php @@ -157,7 +157,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->billingFacilityCity() . "*" . $claim->billingFacilityState() . - "*" . stripZipCode($claim->billingFacilityZip()) . + "*" . $claim->x12Zip($claim->billingFacilityZip()) . "~\n"; if ($claim->billingFacilityNPI() && $claim->billingFacilityETIN()) { ++$edicount; @@ -247,7 +247,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->insuredCity() . "*" . $claim->insuredState() . - "*" . stripZipCode($claim->insuredZip()) . + "*" . $claim->x12Zip($claim->insuredZip()) . "~\n"; ++$edicount; $out .= "DMG" . @@ -284,7 +284,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->payerCity() . "*" . $claim->payerState() . - "*" . stripZipCode($claim->payerZip()) . + "*" . $claim->x12Zip($claim->payerZip()) . "~\n"; // Segment REF (Payer Secondary Identification) omitted. @@ -322,7 +322,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->patientCity() . "*" . $claim->patientState() . - "*" . stripZipCode($claim->patientZip()) . + "*" . $claim->x12Zip($claim->patientZip()) . "~\n"; ++$edicount; $out .= "DMG" . @@ -808,7 +808,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) } if ($claim->facilityState()) { ++ $edicount; - $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . stripZipCode($claim->facilityZip()) . "~\n"; + $out .= "N4" . "*" . $claim->facilityCity() . "*" . $claim->facilityState() . "*" . $claim->x12Zip($claim->facilityZip()) . "~\n"; } } @@ -935,7 +935,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->insuredCity($ins) . "*" . $claim->insuredState($ins) . - "*" . stripZipCode($claim->insuredZip($ins)) . + "*" . $claim->x12Zip($claim->insuredZip($ins)) . "~\n"; // Segment REF (Other Subscriber Secondary Identification) omitted. @@ -966,7 +966,7 @@ function generate_x12_837I($pid, $encounter, &$log, $ub04id) $out .= "N4" . "*" . $claim->payerCity($ins) . "*" . $claim->payerState($ins) . - "*" . stripZipCode($claim->payerZip($ins)) . + "*" . $claim->x12Zip($claim->payerZip($ins)) . "~\n"; // Segment DTP*573 (Claim Check or Remittance Date) omitted. -- 2.11.4.GIT