3 * Inc file for the 270 / 271 creation and uploading
5 * This program creates the segments for the x12 270 eligibility file
6 * It also allows the reading and storing of the x12 271 file
9 * @link http://www.open-emr.org
10 * @author Terry Hill <terry@lilysystems.com>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @author Jerry Padgett <sjpadgett@gmail.com>
13 * @author Stephen Waite <stephen.waite@cmsvt.com>
14 * @copyright Copyright (c) 2010 MMF Systems, Inc
15 * @copyright Copyright (c) 2016 Terry Hill <terry@lillysystems.com>
16 * @copyright Copyright (c) 2017 Brady Miller <brady.g.miller@gmail.com>
17 * @copyright Copyright (c) 2019 Jerry Padgett <sjpadgett@gmail.com>
18 * @copyright Copyright (c) 2019 Stephen Waite <stephen.waite@cmsvt.com>
19 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
21 require_once("$srcdir/edihistory/codes/edih_271_code_class.php");
23 use OpenEMR\Common\Http\oeHttp;
24 use OpenEMR\Common\Utils\RandomGenUtils;
26 // @TODO global to become private var when this goes to a class.
30 // SEGMENT FUNCTION START
32 // ISA Segment - EDI-270 format
34 function create_ISA($row, $X12info, $segTer, $compEleSep)
37 $ISA[0] = "ISA"; // Interchange Control Header Segment ID
38 $ISA[1] = "00"; // Author Info Qualifier
39 $ISA[2] = str_pad("0000000", 10, " "); // Author Information
40 $ISA[3] = "00"; // Security Information Qualifier
41 // MEDI-CAL NOTE: For Leased-Line & Dial-Up use '01',
42 // for BATCH use '00'.
43 // '00' No Security Information Present
44 // (No Meaningful Information in I04)
45 $ISA[4] = str_pad("0000000000", 10, " "); // Security Information
46 $ISA[5] = str_pad($X12info['x12_isa05'], 2, " "); // Interchange ID Qualifier
47 $ISA[6] = str_pad($X12info['x12_sender_id'], 15, " "); // INTERCHANGE SENDER ID
48 $ISA[7] = str_pad($X12info['x12_isa07'], 2, " "); // Interchange ID Qualifier
49 $ISA[8] = str_pad($X12info['x12_receiver_id'], 15, " "); // INTERCHANGE RECEIVER ID
50 $ISA[9] = str_pad(date('ymd'), 6, " "); // Interchange Date (YYMMDD)
51 $ISA[10] = str_pad(date('Hi'), 4, " "); // Interchange Time (HHMM)
52 $ISA[11] = "^"; // Interchange Control Standards Identifier
53 $ISA[12] = str_pad("00501", 5, " "); // Interchange Control Version Number
54 $ISA[13] = str_pad("000000001", 9, " "); // INTERCHANGE CONTROL NUMBER
55 $ISA[14] = str_pad($X12info['x12_isa14'], 1, " "); // Acknowledgment Request [0= not requested, 1= requested]
56 $ISA[15] = str_pad($X12info['x12_isa15'], 1, " "); // Usage Indicator [ P = Production Data, T = Test Data ]
57 $ISA['Created'] = implode('*', $ISA); // Data Element Separator
58 $ISA['Created'] = $ISA['Created'] ."*";
59 $ISA['Created'] = $ISA ['Created'] . $compEleSep . $segTer;
61 return trim($ISA['Created']);
63 // GS Segment - EDI-270 format
64 function create_GS($row, $X12info, $segTer, $compEleSep)
67 $GS[0] = "GS"; // Functional Group Header Segment ID
68 $GS[1] = "HS"; // Functional ID Code [ HS = Eligibility, Coverage or Benefit Inquiry (270) ]
69 $GS[2] = $X12info['x12_sender_id']; // Application Sender's ID
70 $GS[3] = $X12info['x12_receiver_id']; // Application Receiver's ID
71 $GS[4] = date('Ymd'); // Date [CCYYMMDD]
72 $GS[5] = date('His'); // Time [HHMM] Group Creation Time
73 $GS[6] = "2"; // Group Control Number No zeros for 5010
74 $GS[7] = "X"; // Responsible Agency Code Accredited Standards Committee X12 ]
75 $GS[8] = "005010X279A1"; // Version Release / Industry[ Identifier Code Query 005010X279A1
76 $GS['Created'] = implode('*', $GS); // Data Element Separator
77 $GS['Created'] = $GS ['Created'] . $segTer; // change the information in the tag or change the tag
78 return trim($GS['Created']);
80 // ST Segment - EDI-270 format
81 function create_ST($row, $X12info, $segTer, $compEleSep)
84 $ST[0] = "ST"; // Transaction Set Header Segment ID
85 $ST[1] = "270"; // Transaction Set Identifier Code (Inquiry Request)
86 $ST[2] = "000000003"; // Transaction Set Control Number - Must match SE's
87 $ST[3] = "005010X279A1"; // Standard 005010X279A1 in $ST[3]
88 $ST['Created'] = implode('*', $ST); // Data Element Separator
89 $ST['Created'] = $ST ['Created'] . $segTer;
90 return trim($ST['Created']);
92 // BHT Segment - EDI-270 format
93 function create_BHT($row, $X12info, $segTer, $compEleSep)
96 $BHT[0] = "BHT"; // Beginning of Hierarchical Transaction Segment ID
97 $BHT[1] = "0022"; // Subscriber Structure Code
98 $BHT[2] = "13"; // Purpose Code - This is a Request
99 $BHT[3] = "PROVTest600"; // Submitter Transaction Identifier
100 // This information is required by the information Receiver when using Real Time transactions.
101 // For BATCH this can be used for optional information.
102 $BHT[4] = str_pad(date('Ymd'), 8, " "); // Date Transaction Set Created
103 $BHT[5] = str_pad(date('Hi'), 4, " "); // Time Transaction Set Created no space after and 1300 is plenty
104 $BHT['Created'] = implode('*', $BHT); // Data Element Separator
105 $BHT['Created'] = $BHT ['Created'] . $segTer;
106 return trim($BHT['Created']);
108 // HL Segment - EDI-270 format
109 function create_HL($row, $nHlCounter, $X12info, $segTer, $compEleSep)
112 $HL[0] = "HL"; // Hierarchical Level Segment ID
114 $HL[1] = $nHlCounter; // Hierarchical ID No.
115 if ($nHlCounter == 1) {
117 $HL[3] = 20; // Description: Identifies the payor, maintainer, or source of the information.
118 $HL[4] = 1; // 1 Additional Subordinate HL Data Segment in This Hierarchical Structure.
119 } else if ($nHlCounter == 2) {
120 $HL[2] = 1; // Hierarchical Parent ID Number
121 $HL[3] = 21; // Hierarchical Level Code. '21' Information Receiver
122 $HL[4] = 1; // 1 Additional Subordinate HL Data Segment in This Hierarchical Structure.
125 $HL[3] = 22; // Hierarchical Level Code.'22' Subscriber
126 $HL[4] = 0; // 0 no Additional Subordinate in the Hierarchical Structure.
128 $HL['Created'] = implode('*', $HL); // Data Element Separator
129 $HL['Created'] = $HL ['Created'] . $segTer;
130 return trim($HL['Created']);
132 // NM1 Segment - EDI-270 format
133 function create_NM1($row, $nm1Cast, $X12info, $segTer, $compEleSep)
136 $NM1[0] = "NM1"; // Subscriber Name Segment ID
137 if ($nm1Cast == 'PR') {
138 $NM1[1] = "PR"; // Entity ID Code - Payer [PR Payer]
139 $NM1[2] = "2"; // Entity Type - Non-Person
140 $NM1[3] = $row["payer_name"]; // Organizational Name
141 $NM1[4] = ""; // Data Element not required.
142 $NM1[5] = ""; // Data Element not required.
143 $NM1[6] = ""; // Data Element not required.
144 $NM1[7] = ""; // Data Element not required.
145 $NM1[8] = "PI"; // 5010 no longer uses "46"
146 if ($GLOBALS['enable_oa']) {
147 $payerId = $row['eligibility_id'];
149 $payerId = $row['cms_id'];
151 $NM1[9] = $payerId; // Application Sender's ID
152 } else if ($nm1Cast == 'FA') {
153 $NM1[1] = "FA"; // Entity ID Code - Facility [FA Facility]
154 $NM1[2] = "2"; // Entity Type - Non-Person
155 $NM1[3] = $row['facility_name']; // Organizational Name
156 $NM1[4] = ""; // Data Element not required.
157 $NM1[5] = ""; // Data Element not required.
158 $NM1[6] = ""; // Data Element not required.
159 $NM1[7] = ""; // Data Element not required.
161 $NM1[9] = $row['facility_npi'];
162 } else if ($nm1Cast == '1P') {
163 $NM1[1] = "1P"; // Entity ID Code - Provider
164 $NM1[2] = "2"; // Entity Type - Non-Person
165 $NM1[3] = $row['facility_name']; // Organizational Name
166 $NM1[4] = ""; // Data Element not required.
167 $NM1[5] = ""; // Data Element not required.
168 $NM1[6] = ""; // Data Element not required.
169 $NM1[7] = ""; // Data Element not required.
171 $NM1[9] = $row['provider_npi'];
172 } else if ($nm1Cast == 'IL') {
173 $NM1[1] = "IL"; // Insured or Subscriber
174 $NM1[2] = "1"; // Entity Type - Person
175 $NM1[3] = $row['lname']; // last Name
176 $NM1[4] = $row['fname']; // first Name
177 $NM1[5] = $row['mname']; // middle Name
178 $NM1[6] = ""; // data element
179 $NM1[7] = ""; // data element
180 $NM1[8] = "MI"; // Identification Code Qualifier
181 $NM1[9] = $row['policy_number']; // Identification Code
183 $NM1['Created'] = implode('*', $NM1); // Data Element Separator
184 $NM1['Created'] = $NM1['Created'] . $segTer;
185 return trim($NM1['Created']);
187 // REF Segment - EDI-270 format
188 function create_REF($row, $ref, $X12info, $segTer, $compEleSep)
191 $REF[0] = "REF"; // Subscriber Additional Identification does not want this for anything
193 $REF[1] = "4A"; // Reference Identification Qualifier
194 $REF[2] = $row['provider_pin']; // Provider Pin.
196 $REF[1] = "EJ"; // 'EJ' for Patient Account Number does not want this for patient
197 $REF[2] = $row['pid']; // Patient Account No.
199 $REF['Created'] = implode('*', $REF); // Data Element Separator
200 $REF['Created'] = $REF['Created'] . $segTer;
201 return trim($REF['Created']);
203 // TRN Segment - EDI-270 format
204 function create_TRN($row, $tracno, $refiden, $X12info, $segTer, $compEleSep)
207 $TRN[0] = "TRN"; // Subscriber Trace Number Segment ID
208 $TRN[1] = "1"; // Trace Type Code � Current Transaction Trace Numbers
209 $TRN[2] = $tracno; // Trace Number
210 $TRN[3] = "9000000000"; // Originating Company ID � must be 10 positions in length
211 $TRN[4] = $refiden; // Additional Entity Identifier (i.e. Subdivision)
212 $TRN['Created'] = implode('*', $TRN); // Data Element Separator
213 $TRN['Created'] = $TRN['Created'] . $segTer;
214 return trim($TRN['Created']);
216 // DMG Segment - EDI-270 format
217 function create_DMG($row, $X12info, $segTer, $compEleSep)
220 $DMG[0] = "DMG"; // Date or Time or Period Segment ID
221 $DMG[1] = "D8"; // Date Format Qualifier - (D8 means CCYYMMDD)
222 $DMG[2] = $row['dob']; // Subscriber's Birth date
223 $DMG[3] = strtoupper($row['sex'][0]);
224 $DMG['Created'] = implode('*', $DMG); // Data Element Separator
225 $DMG['Created'] = $DMG['Created'] . $segTer;
226 return trim($DMG['Created']);
228 // DTP Segment - EDI-270 format
229 function create_DTP($row, $qual, $X12info, $segTer, $compEleSep)
232 $DTP[0] = "DTP"; // Date or Time or Period Segment ID
233 $DTP[1] = $qual; // Qualifier - Date of Service
234 $DTP[2] = "D8"; // Date Format Qualifier - (D8 means CCYYMMDD)
235 if ($qual == '102') {
236 $DTP[3] = $row['date']; // Ins effective Date
238 switch ($X12info['x12_dtp03']) {
240 $dtp_date = !empty($row['pc_eventDate']) && $row['pc_eventDate'] > '20010101' ? $row['pc_eventDate'] : date("Ymd");
243 $dtp_date = !empty($row['date']) && $row['date'] > '20010101' ? $row['date'] : date("Ymd");
246 $dtp_date = date("Ymd");
248 $DTP[3] = $dtp_date; // Date of Service
250 $DTP['Created'] = implode('*', $DTP); // Data Element Separator
251 $DTP['Created'] = $DTP['Created'] . $segTer;
252 return trim($DTP['Created']);
254 // EQ Segment - EDI-270 format
255 function create_EQ($row, $X12info, $segTer, $compEleSep)
258 $EQ[0] = "EQ"; // Subscriber Eligibility or Benefit Inquiry Information
259 $EQ[1] = "30"; // Service Type Code
260 $EQ['Created'] = implode('*', $EQ); // Data Element Separator
261 $EQ['Created'] = $EQ['Created'] . $segTer;
262 return trim($EQ['Created']);
264 // SE Segment - EDI-270 format
265 function create_SE($row, $segmentcount, $X12info, $segTer, $compEleSep)
268 $SE[0] = "SE"; // Transaction Set Trailer Segment ID
269 $SE[1] = $segmentcount; // Segment Count
270 $SE[2] = "000000003"; // Transaction Set Control Number - Must match ST's
271 $SE['Created'] = implode('*', $SE); // Data Element Separator
272 $SE['Created'] = $SE['Created'] . $segTer;
273 return trim($SE['Created']);
275 // GE Segment - EDI-270 format
276 function create_GE($row, $X12info, $segTer, $compEleSep)
279 $GE[0] = "GE"; // Functional Group Trailer Segment ID
280 $GE[1] = "1"; // Number of included Transaction Sets
281 $GE[2] = "2"; // Group Control Number
282 $GE['Created'] = implode('*', $GE); // Data Element Separator
283 $GE['Created'] = $GE['Created'] . $segTer;
284 return trim($GE['Created']);
286 // IEA Segment - EDI-270 format
287 function create_IEA($row, $X12info, $segTer, $compEleSep)
290 $IEA[0] = "IEA"; // Interchange Control Trailer Segment ID
291 $IEA[1] = "1"; // Number of included Functional Groups
292 $IEA[2] = "000000001"; // Interchange Control Number
293 $IEA['Created'] = implode('*', $IEA);
294 $IEA['Created'] = $IEA['Created'] . $segTer;
295 return trim($IEA['Created']);
298 function translate_relationship($relationship)
300 switch ($relationship) {
313 // EDI-270 Batch file Generation
314 function print_elig($res, $X12info, $segTer, $compEleSep)
318 // For Header Segment
323 foreach ($res as $row) {
324 if ($nHlCounter == 1) {
326 $PATEDI = create_ISA($row, $X12info, $segTer, $compEleSep);
328 $PATEDI .= create_GS($row, $X12info, $segTer, $compEleSep);
330 $PATEDI .= create_ST($row, $X12info, $segTer, $compEleSep);
332 $PATEDI .= create_BHT($row, $X12info, $segTer, $compEleSep);
334 $PATEDI .= create_HL($row, 1, $X12info, $segTer, $compEleSep);
335 $PATEDI .= create_NM1($row, 'PR', $X12info, $segTer, $compEleSep);
336 // For Provider Segment
337 $PATEDI .= create_HL($row, 2, $X12info, $segTer, $compEleSep);
338 $PATEDI .= create_NM1($row, '1P', $X12info, $segTer, $compEleSep); // 5010 no longer uses FA
339 $nHlCounter = $nHlCounter + 2;
340 $segmentcount = 6; // segment counts - start from ST
342 // For Subscriber Segment
343 $PATEDI .= create_HL($row, $nHlCounter, $X12info, $segTer, $compEleSep);
344 $PATEDI .= create_NM1($row, 'IL', $X12info, $segTer, $compEleSep);
345 // send pid so we get it back in 271
346 $PATEDI .= create_REF($row, 'EJ', '', $segTer, '');
347 $PATEDI .= create_DMG($row, $X12info, $segTer, $compEleSep);
348 $PATEDI .= create_DTP($row, '291', $X12info, $segTer, $compEleSep);
349 $PATEDI .= create_EQ($row, $X12info, $segTer, $compEleSep);
350 $segmentcount = $segmentcount + 6;
351 $nHlCounter = $nHlCounter + 1;
352 $rowCount = $rowCount + 1;
354 $refiden = $refiden + 1;
355 if ($rowCount == count($res)) {
356 $segmentcount = $segmentcount + 1;
357 $PATEDI .= create_SE($row, $segmentcount, $X12info, $segTer, $compEleSep);
358 $PATEDI .= create_GE($row, $X12info, $segTer, $compEleSep);
359 $PATEDI .= create_IEA($row, $X12info, $segTer, $compEleSep);
365 function requestEligibleTransaction($pid = 0, $eFlag = false)
372 DATE_FORMAT(p.dob, '%Y%m%d') as dob,
380 i.provider as payer_id,
381 i.subscriber_relationship,
385 DATE_FORMAT(i.subscriber_DOB, '%Y%m%d') as subscriber_dob,
388 DATE_FORMAT(i.date, '%Y%m%d') as date,
389 d.lname as provider_lname,
390 d.fname as provider_fname,
391 d.npi as provider_npi,
392 d.upin as provider_pin,
393 f.federal_ein as federal_ein,
394 f.facility_npi as facility_npi,
395 f.name as facility_name,
397 c.eligibility_id as eligibility_id,
398 c.x12_default_eligibility_id as partner,
400 FROM patient_data AS p
401 LEFT JOIN users AS d on (p.providerID = d.id)
402 LEFT JOIN facility AS f on (f.id = d.facility_id)
403 LEFT JOIN insurance_data AS i ON (i.id =(SELECT id FROM insurance_data AS i WHERE pid = p.pid AND type = 'primary' ORDER BY date DESC LIMIT 1))
404 LEFT JOIN insurance_companies as c ON (c.id = i.provider)
406 $res = sqlStatement($query, array($pid));
408 $details = requestRealTimeEligible($res, '', "~", ':', true);
409 $isError = strpos($details, "Error:");
410 $isError = $isError !== false ? $isError : strpos($details, "AAA");
411 if ($isError !== false) {
412 $details = substr($details, $isError);
413 return "<div>" . nl2br(text($details)) . "</div>";
419 // EDI-270 RealTime Request & Response
420 // RealTime requires one transaction per request.
422 function requestRealTimeEligible($res, $X12info, $segTer, $compEleSep, $eFlag = false)
425 $totalCount = count($res);
426 $down_accum = $log = $error_accum = '';
427 foreach ($res as $row) {
429 $X12info = getX12Partner($row['partner']);
431 if ($row['providerID'] === 0 || !$row['provider_npi']) {
432 $error_accum .= xlt("Error") . ": " . xlt("Provider Missing Add one in Choices") . "\n";
434 if (!$row['eligibility_id']) {
435 $error_accum .= xlt("Error") . ": " . xlt("Missing Insurance Payer Id") . "\n";
437 if (!$row['policy_number'] || !$row['subscriber_dob']) {
438 $error_accum .= xlt("Error") . ": " . xlt("Missing Subscriber Policy Number or DOB") . "\n";
440 if (!empty($error_accum)) {
444 $PATEDI = create_ISA($row, $X12info, $segTer, $compEleSep);
446 $PATEDI .= create_GS($row, $X12info, $segTer, $compEleSep);
448 $PATEDI .= create_ST($row, $X12info, $segTer, $compEleSep);
450 $PATEDI .= create_BHT($row, $X12info, $segTer, $compEleSep);
452 $PATEDI .= create_HL($row, 1, $X12info, $segTer, $compEleSep);
453 $PATEDI .= create_NM1($row, 'PR', $X12info, $segTer, $compEleSep);
454 // For Provider Segment
455 $PATEDI .= create_HL($row, 2, $X12info, $segTer, $compEleSep);
456 //$PATEDI .= create_NM1($row, 'FA', $X12info, $segTer, $compEleSep); // unsure but this may have to be an option
457 $PATEDI .= create_NM1($row, '1P', $X12info, $segTer, $compEleSep);
458 // For Subscriber Segment
459 $PATEDI .= create_HL($row, 3, $X12info, $segTer, $compEleSep);
460 $PATEDI .= create_NM1($row, 'IL', $X12info, $segTer, $compEleSep);
461 // send pid so we get it back in 271
462 $PATEDI .= create_REF($row, 'EJ', '', $segTer, '');
463 $PATEDI .= create_DMG($row, $X12info, $segTer, $compEleSep);
465 $PATEDI .= create_DTP($row, '291', $X12info, $segTer, $compEleSep);
466 $PATEDI .= create_EQ($row, $X12info, $segTer, $compEleSep);
468 $PATEDI .= create_SE($row, 13, $X12info, $segTer, $compEleSep);
469 $PATEDI .= create_GE($row, $X12info, $segTer, $compEleSep);
470 $PATEDI .= create_IEA($row, $X12info, $segTer, $compEleSep);
472 $result = requestEligibility($X12info['id'], $PATEDI);
474 $e = strpos($result, "Error:");
476 $error_accum = $result;
478 $down_accum .= $result . "\n"; // delimit for next new request
481 $log .= "*** 270 " . xlt("Request Message") . " $rowCount of $totalCount\n" . $PATEDI . "\n" . $error_accum . "\n"; // keep a log.
484 // parse the 271 responses from 270 requests sent.
485 $process = parseEdi271($down_accum);
486 $log = xlt("List of ") . $rowCount . " " . xlt("Requests Sent") . ":\n" . $log . "\n" . $process;
495 function show_elig($res, $X12info, $segTer, $compEleSep)
499 echo " <div id='report_results'>
500 <table class='table table-striped table-hover'>
502 <th>". text(xl('Facility Name')) ."</th>
503 <th>". text(xl('Facility NPI')) ."</th>
504 <th>". text(xl('Insurance Comp')) ."</th>
505 <th>". text(xl('Appt Date')) ."</th>
506 <th>". text(xl('Policy No')) ."</th>
507 <th>". text(xl('Patient Name')) ."</th>
508 <th>". text(xl('DOB')) ."</th>
509 <th>". text(xl('Gender')) ."</th>
510 <th>". text(xl('SSN')) ."</th>
515 foreach ($res as $row) {
517 // what the heck is below for... looks abandoned.
519 $elig[0] = $row['facility_name']; // Inquiring Provider Name calendadr
520 $elig[1] = $row['facility_npi']; // Inquiring Provider NPI
521 $elig[2] = $row['payer_name']; // Payer Name our insurance co name
522 $elig[3] = $row['policy_number']; // Subscriber ID
523 $elig[4] = $row['subscriber_lname']; // Subscriber Last Name
524 $elig[5] = $row['subscriber_fname']; // Subscriber First Name
525 $elig[6] = $row['subscriber_mname']; // Subscriber Middle Initial
526 $elig[7] = $row['subscriber_dob']; // Subscriber Date of Birth
527 $elig[8] = substr($row['subscriber_sex'], 0, 1); // Subscriber Sex
528 $elig[9] = $row['subscriber_ss']; // Subscriber SSN
529 $elig[10] = translate_relationship($row['subscriber_relationship']); // Pt Relationship to insured
530 $elig[11] = $row['lname']; // Dependent Last Name
531 $elig[12] = $row['fname']; // Dependent First Name
532 $elig[13] = $row['mname']; // Dependent Middle Initial
533 $elig[14] = $row['dob']; // Dependent Date of Birth
534 $elig[15] = substr($row['sex'], 0, 1); // Dependent Sex
535 $elig[16] = $row['pc_eventDate']; // Date of service
536 $elig[17] = "30"; // Service Type
537 $elig[18] = $row['pubpid']; // Patient Account Number pubpid
539 echo " <tr id='PR".$i."_". text($row['policy_number'])."'>
540 <td class ='detail'>". text($row['facility_name']) ."</td>
541 <td class ='detail'>". text($row['facility_npi']) ."</td>
542 <td class ='detail'>". text($row['payer_name']) ."</td>
543 <td class ='detail'>". text(date("m/d/Y", strtotime($row['pc_eventDate']))) ."</td>
544 <td class ='detail'>". text($row['policy_number']) ."</td>
545 <td class ='detail'>". text($row['subscriber_lname']." ". $row['subscriber_fname']) ."</td>
546 <td class ='detail'>". text($row['subscriber_dob']) ."</td>
547 <td class ='detail'>". text($row['subscriber_sex']) ."</td>
548 <td class ='detail'>". text($row['subscriber_ss']) ."</td>
550 <img src=\"" . $GLOBALS['images_static_relative'] . "/deleteBtn.png\" title=" .text(xl('Delete Row')) . " style='cursor:pointer;cursor:hand;' onclick='deletetherow(\"" . $i."_". text($row['policy_number']) . "\")'>
554 unset($elig); // see ..
559 <td class='norecord' colspan=9>
560 <div style='padding:5px;font-family:arial;font-size:13px;text-align:center;'>". text(xl('No records found')) . "</div>
568 // To Show Eligibility Verification data
569 function show_eligibility_information($pid, $flag = false)
572 "SELECT eligr.*, eligv.insurance_id, eligv.copay, insd.pid, insc.name, " .
573 "Date_Format(eligv.eligibility_check_date, '%W %M %e, %Y %h:%i %p') AS verificationDate " .
574 "FROM eligibility_verification eligv " .
575 "INNER JOIN benefit_eligibility eligr ON eligr.verification_id = eligv.verification_id " .
576 "INNER JOIN insurance_data insd ON insd.id = eligv.insurance_id " .
577 "INNER JOIN insurance_companies insc ON insc.id = insd.provider " .
578 "WHERE insd.pid = ? AND eligv.eligibility_check_date = " .
579 "(SELECT Max(eligibility_verification.eligibility_check_date) FROM eligibility_verification ".
580 "WHERE eligibility_verification.insurance_id = eligv.insurance_id)";
581 $result = sqlStatement($query, array($pid));
583 $showString = "<div class='row'>";
586 while ($benefit = sqlFetchArray($result)) {
589 $showString .= "<div class='col col-sm-12'>\n";
590 $showString .= "<b>" . xlt('Insurance Provider') . ":</b> " . (!empty($benefit['name']) ? text($benefit['name']) : xlt('n/a')) . "<br/>\n";
591 $showString .= "<b>" . xlt('Verified On') . ":</b> " . text($benefit['verificationDate']) . "<br/><br/>\n";
592 $showString .= "</div><br/>\n";
594 $benefit['start_date'] = strpos($benefit['start_date'], "0000") === false ? $benefit['start_date'] : '';
595 $benefit['end_date'] = strpos($benefit['end_date'], "0000") === false ? $benefit['end_date'] : '';
597 switch ($benefit['type']) {
611 $color = "darkgreen";
614 $showString .= "\n<div class='col col-sm-6' >\n";
615 $showString .= !empty($benefit['benefit_type']) ? "<b style='color: $color'>" . xlt('Benefit Type') . ": " . text($benefit['benefit_type']) . "</b><br />\n" : '';
616 $showString .= !empty($benefit['start_date']) ? "<b>" . xlt('Start Date') . ":</b> " . text(date("m/d/Y", strtotime($benefit['start_date']))) . "<br />\n" : '';
617 $showString .= !empty($benefit['end_date']) ? "<b>" . xlt('End Date') . ":</b> " . text(date("m/d/Y", strtotime($benefit['end_date']))) . "<br />\n" : '';
618 $showString .= !empty($benefit['coverage_level']) ? "<b>" . xlt('Coverage Level') . ":</b> " .text($benefit['coverage_level']) . "<br />\n" : '';
619 $showString .= !empty($benefit['coverage_type']) ? "<b>" . xlt('Coverage Type') . ":</b> " . text($benefit['coverage_type']) . "<br />\n" : '';
620 $showString .= !empty($benefit['plan_type']) ? "<b>" . xlt('Plan Type') . ":</b> " . text($benefit['plan_type']) . "<br />\n" : '';
621 $showString .= !empty($benefit['plan_desc']) ? "<b>" . xlt('Plan Description') . ":</b> " . text(text($benefit['plan_desc'])) . "<br />\n" : '';
622 $showString .= !empty($benefit['coverage_period']) ? "<b>" . xlt('Coverage Period') . ":</b> " . text($benefit['coverage_period']) . "<br />\n" : '';
623 $showString .= !empty($benefit['amount']) ? "<b>" . xlt('Amount') . ":</b> " . text($benefit['amount']) . "<br />\n" : '';
624 $showString .= !empty($benefit['percent']) ? "<b>" . xlt('Percentage') . ":</b> " . text($benefit['percent']) . "<br />\n" : '';
625 $showString .= !empty($benefit['network_ind']) ? "<b>" . xlt('Network Indicator') . ":</b> " . text($benefit['network_ind']) . "<br />\n" : '';
626 $showString .= !empty($benefit['message']) ? "<b>" . xlt('Message') . ":</b> " . text($benefit['message']) . "<br />\n" : '';
627 $showString .= "</div>";
630 $showString .= "</div>\n<br/><div class='row'>\n";
636 $showString .= "</div>";
639 $showString = "<br><span><b>" . xlt("Nothing To Report") . "</b></span><br>";
641 $showString .= "</div>\n";
646 // Function to save the values in eligibility_verification table
647 function eligibility_verification_save($subscriber = [])
649 $verification_id = 0;
651 $partner_id = $subscriber['isa_sender_id'];
652 $patient_id = $subscriber['pid'];
654 $query = "SELECT id, copay FROM insurance_data WHERE type = 'primary' and pid = ?";
655 $insId = sqlQuery($query, array($patient_id));
656 if ($insId !== false) {
657 $insurance_id = $insId['id'];
658 $copay = $insId['copay'];
661 $query = "SELECT verification_id FROM eligibility_verification WHERE insurance_id = ?";
662 $resId = sqlQuery($query, array($insurance_id));
663 if ($resId !== false) {
664 $verification_id = $resId['verification_id'];
667 if (!empty($insurance_id)) {
668 $sqlBindArray = array();
669 $query = "REPLACE INTO eligibility_verification SET verification_id = ?, insurance_id = ?, response_id = ?, eligibility_check_date = now(), create_date = now()";
670 array_push($sqlBindArray, $verification_id, $insurance_id, $partner_id);
672 $res = sqlInsert($query, $sqlBindArray);
673 if (!$verification_id) {
674 $verification_id = $res;
678 $query = "DELETE FROM `benefit_eligibility` WHERE `benefit_eligibility`.`verification_id` = ?";
679 $res = sqlStatement($query, array($verification_id));
681 foreach ($subscriber['benefits'] as $benefit) {
688 $benefit['benefit_type'],
689 $benefit['start_date'],
690 $benefit['end_date'],
691 $benefit['coverage_level'],
692 $benefit['coverage_type'],
693 $benefit['plan_type'],
694 $benefit['plan_desc'],
695 $benefit['coverage_period'],
698 $benefit['network_ind'],
702 $query = "INSERT INTO `benefit_eligibility` " .
703 "(`verification_id`, `response_status`, `response_create_date`, `response_modify_date`, `type`, `benefit_type`, `start_date`, " .
704 "`end_date`, `coverage_level`, `coverage_type`, `plan_type`, `plan_description`, `coverage_period`, `amount`, `percent`, `network_ind`, `message`) " .
705 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
706 $res = sqlStatement($query, $bind);
710 // return array of X12 partners
711 // if id return just that id
712 function getX12Partner($id = 0)
714 // @TODO move to class
720 $returnval = sqlQuery("select * from x12_partners WHERE id = ?", array($id));
721 $X12info = $returnval;
723 $rez = sqlStatement("select * from x12_partners");
724 for ($iter = 0; $row = sqlFetchArray($rez); $iter++) {
725 $returnval[$iter] = $row;
732 // return array of provider usernames
733 function getUsernames()
735 $rez = sqlStatement("select distinct username, lname, fname,id from users " .
736 "where authorized = 1 and username != ''");
737 for ($iter = 0; $row = sqlFetchArray($rez); $iter++) {
738 $returnval[$iter] = $row;
744 // return formated array
746 function arrFormated(&$item, $key)
748 $item = strstr($item, '_');
749 $item = substr($item, 1, strlen($item) - 1);
753 function requestEligibility($partner = '', $x12_270 = '')
756 if (((int)$X12info['id'] !== (int)$partner) && (int)$partner > 0) {
757 $X12info = getX12Partner($partner);
760 $payloadId = "3b8c13f5-11e2-43bf-bc47-737cca04f3fe"; // a default fallback
761 if (function_exists('openssl_random_pseudo_bytes') === true) {
762 $data = openssl_random_pseudo_bytes(16);
763 $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
764 $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
765 $payloadId = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
768 $boundary = RandomGenUtils::createUniqueToken(12);
769 $rt_passwrd = $X12info['x12_isa04'];
770 $rt_user = $X12info['x12_isa02'];
771 $sender_id = $X12info['x12_sender_id'];
772 $receiver_id = $X12info['x12_receiver_id'];
773 $now_date = date("Y-m-d\TH:i:s\Z");
775 'Content-Type' => "multipart/form-data; boundary=$boundary",
776 'Host' => ' wsd.officeally.com'
779 // IMPORTANT: Do not change the format of $mime_body below.
780 // HTTP MIME Multipart is non-normative. LFs matter...
782 $mime_body = <<<MIMEBODY
784 Content-Disposition: form-data; name="ProcessingMode"
788 Content-Disposition: form-data; name="TimeStamp"
792 Content-Disposition: form-data; name="PayloadID"
796 Content-Disposition: form-data; name="CORERuleVersion"
800 Content-Disposition: form-data; name="ReceiverID"
804 Content-Disposition: form-data; name="SenderID"
808 Content-Disposition: form-data; name="PayloadType"
810 X12_270_Request_005010X279A1
812 Content-Disposition: form-data; name="UserName"
816 Content-Disposition: form-data; name="Password"
820 Content-Disposition: form-data; name="Payload"
826 $response = oeHttp::bodyFormat('body')
827 //->setDebug('5000')/* @todo uncomment and set proxy port to debug eg Fiddler */
828 ->usingHeaders($headers)
829 ->post('https://wsd.officeally.com/TransactionSite/rtx.aspx', $mime_body); // @TODO put request urls in x12 partner's for versatility.
831 $formBody = $response->body();
832 $contentType = $response->header('Content-Type')[0];
833 $hContentLength = (int)$response->header('Content-Length')[0];
834 $cksum = ($hContentLength - strlen($formBody)) === 0 ? true : false; // validate content size
835 $formData = mimeParse($formBody, $contentType);
839 $errors .= "Error:" . xlt("Request Content Fails Integrity Test");
841 if ($response->status() !== 200) {
842 $errors .= "\nError:" . xlt("Http Error") . ": " . $response->getReasonPhrase() . " : " . $response->status();
844 if ($formData['ErrorCode'] != "Success") {
845 $errors .= "\nError:" . $formData['ErrorCode'] . "\n" . $formData['ErrorMessage'];
848 $errors .= $formData['Payload'] ? "\nError:" . $formData['Payload'] : '';
852 $x12_271 = $formData['Payload'];
857 function mimeParse($formBody = '', $contentType)
859 $mimeBody = preg_replace('~\r\n?~', "\r", $formBody);
860 list($contentType, $bound, $cs) = explode(";", trim($contentType)); // $contentType & $cs are throwaways
861 $bound = explode("=", trim($bound, ' '))[1];
862 $mimeFields = preg_split("/-+$bound/", $mimeBody);
863 array_pop($mimeFields);
864 $hold = $isMatches = [];
865 foreach ($mimeFields as $id => $field) {
869 preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $field, $isMatches);
870 if (preg_match('/^(.*)\[\]$/i', $isMatches[1], $hold)) {
871 $mimeData[$hold[1]][] = $isMatches[2];
873 $mimeData[$isMatches[1]] = $isMatches[2];
879 function getPatientMatch($fn, $ln, $sex, $dob)
881 $fn = "%" . $fn . "%";
882 $ln = "%" . $ln . "%";
883 $sex = "%" . $sex . "%";
884 $dob = date("Y-m-d", strtotime($dob));
885 $sql = "SELECT pid FROM patient_data WHERE fname LIKE ? && lname LIKE ? && sex LIKE ? && DOB LIKE ?";
886 $rtn = sqlQuery($sql, array($fn, $ln, $sex, $dob));
888 return $rtn['pid'] ? $rtn['pid'] : 0;
891 function parseEdi271($content)
894 $codes = new edih_271_codes('*', '^');
895 $target = $GLOBALS['edi_271_file_path'];
897 // not sure if want to save yet
898 $target = $target . time();
900 $responses = explode("\n", $content);
901 if (empty($responses)) {
902 $responses = $content;
905 // Loop through each 271. '\n' delims records in batch.
906 foreach ($responses as $new) {
911 $subscribers = array();
921 $segments = explode("~", $new);
923 if (count($segments) < 6) {
924 if (file_exists($target)) {
930 foreach ($segments as $segment) {
931 $elements = explode("*", $segment);
932 $ecnt = count($elements);
934 for ($i = 0; $i < $ecnt; $i++) {
935 $elements[$i] = text(trim($elements[$i]));
937 // Switch Case for Segment
938 switch ($elements[0]) {
940 $loop[1] = $elements[2];
941 $in['isa_sender_id'] = $elements[6];
942 $in['isa_receiver_id'] = $elements[8];
943 $in['isa_control_number'] = $elements[13];
947 $loop['id'] = (int)$elements[1];
948 $loop['parent'] = (int)$elements[2];
949 $loop['error'] = (int)$elements[4];
953 if ($loop['id'] === 1) {//"PR" payer
954 $in['payer_org'] = $elements[3];
955 $in['payer_member_id'] = $elements[9];
956 } elseif ($loop['id'] === 2) { //"1P" or "FA"
957 $in['provider_org'] = $elements[3];
958 $in['provider_member_id'] = $elements[9];
959 } elseif ($elements[1] == "IL" || $loop['context'] == "TRN") { //"IL"
960 $in['trace'] = $trace;
961 $in['subscriber_lname'] = $elements[3];
962 $in['subscriber_fname'] = $elements[4];
963 $in['subscriber_mname'] = $elements[5];
964 $in['subscriber_member_id'] = $elements[9];
965 $in['verify_date'] = date('Y/m/d H:i:s');
967 $loop['context'] = $elements[0];
971 if ($elements[1] == "D8") {
972 $in['subscriber_dob'] = $elements[2];
974 $in['subscriber_sex'] = $elements[3];
975 $loop['context'] = $elements[0];
976 // 2100A-C should be done so get our patient id.
977 if (!(int)$in['pid']) {
978 $in['pid'] = (int)getPatientMatch(
979 $in['subscriber_fname'],
980 $in['subscriber_lname'],
981 $in['subscriber_sex'],
982 $in['subscriber_dob']
990 break; // subscriber not set yet
994 $in['benefits'] = $benefits ? $benefits : [];
995 array_push($subscribers, $in);
996 $loop['context'] = $elements[0];
1002 if ($elements[1] == "EJ") {
1003 $in['pid'] = (int)$elements[2];
1005 $in['pid'] = (int)getPatientMatch(
1006 $in['subscriber_fname'],
1007 $in['subscriber_lname'],
1008 $in['subscriber_sex'],
1009 $in['subscriber_dob']
1016 if ($elements[2] == "D8") {
1017 $loop['start_date'] = $elements[3] ? date("Y-m-d", strtotime($elements[3])) : '';
1018 $loop['end_date'] = '';
1019 } elseif ($elements[2] == "RD8") {
1020 $tmp = explode('-', $elements[3]);
1021 $loop['start_date'] = $tmp[0] ? date("Y-m-d", strtotime($tmp[0])) : '';
1022 $loop['end_date'] = !empty($tmp[1]) ? date("Y-m-d", strtotime($tmp[1])) : '';
1024 if ($loop['context'] == "EB") {
1025 $bcnt = count($benefits) - 1;
1026 $benefits[$bcnt]['start_date'] = $loop['start_date'];
1027 $benefits[$bcnt]['end_date'] = $loop['end_date'];
1033 'type' => $elements[1],
1034 'benefit_type' => $codes->get_271_code("EB01", $elements[1]) ? $codes->get_271_code("EB01", $elements[1]) : $elements[1],
1037 'coverage_level' => $elements[2] ? $codes->get_271_code("EB02", $elements[2]) :$elements[2],
1038 'coverage_type' => $elements[3] ? $codes->get_271_code("EB03", $elements[3]) :$elements[3],
1039 'plan_type' => $elements[4] ? $codes->get_271_code("EB04", $elements[4]) : $elements[4],
1040 'plan_description' => $elements[5],
1041 'coverage_period' => $elements[6] ? $codes->get_271_code("EB06", $elements[6]) : $elements[6],
1042 'amount' => $elements[7] ? number_format($elements[7], 2, '.', '') : '',
1043 'percent' => $elements[8],
1044 'network_ind' => $elements[12] ? $codes->get_271_code("EB12", $elements[12]) : $elements[12],
1045 'message' => '' // any MSG segments that may be assoc with this EB.
1047 $loop['context'] = "EB";
1048 array_push($benefits, $eb);
1053 'request_ind' => $elements[1],
1054 'reason_code' => $elements[3] . " : " . $codes->get_271_code("AAA03", $elements[3]),
1055 'follow_up' => $elements[4] . " : " . $codes->get_271_code("AAA04", $elements[4])
1057 array_push($AAA, $error);
1061 $bcnt = count($benefits) - 1;
1063 $benefits[$bcnt]['message'] = $benefits[$bcnt]['message'] ? $benefits[$bcnt]['message'] . ":" : '';
1064 $benefits[$bcnt]['message'] .= $elements[1];
1069 $in['benefits'] = $benefits ? $benefits : [];
1071 array_push($subscribers, $in);
1073 $loop['context'] = $elements[0];
1080 if (count($subscribers) < 1) {
1081 $elog = xlt("Error") . ": " . xlt("Unknown Transaction Error Maybe Subscriber Effective or DOB Dates");
1083 foreach ($subscribers as $subscriber) {
1084 eligibility_verification_save($subscriber);
1089 // some debug logging
1090 if (!$GLOBALS['disable_eligibility_log']) {
1091 $log .= "*------------------- " . xlt("271 Returned") . " --------------------*\n" . $new . "\n" . (isset($AAA[0]) ? (xlt("AAA Segments") . ":\n" . print_r($AAA, true)) : "\n") . $elog;
1092 $log .= makeEligibilityReport($subscribers);
1100 function makeEligibilityReport($subscribers = [])
1103 foreach ($subscribers as $subscriber) {
1105 xlt("Subscriber Member") . ": " . $subscriber['subscriber_fname'] . " " . $subscriber['subscriber_lname'] . " " . $subscriber['subscriber_mname'] .
1106 " " .xlt("Member Id") . ": " . $subscriber['subscriber_member_id'] . " ---*\n";
1107 $cnt = count($subscriber['benefits']);
1109 $binfo .= "*** " . xlt("Nothing returned to report") . " ***\n";
1111 foreach ($subscriber['benefits'] as $key => $benefits) {
1112 foreach ($benefits as $key => $benefit) {
1113 if ($key == 'type') {
1116 if (empty($benefit) || $key == 'type') {
1119 $binfo .= "\t" . ucwords(str_replace('_', ' ', $key)) . ": " . $benefit . "\n";