added ending dates of service
[openemr.git] / library / gen_hcfa_1500.inc.php
blob395586ac77b59df6dd58bc893ef9849c67fb4415
1 <?php
2 // Copyright (C) 2008 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 require_once("Claim.class.php");
11 $hcfa_curr_line = 1;
12 $hcfa_curr_col = 1;
13 $hcfa_data = '';
14 $hcfa_proc_index = 0;
16 function put_hcfa($line, $col, $maxlen, $data) {
17 global $hcfa_curr_line, $hcfa_curr_col, $hcfa_data;
18 if ($line < $hcfa_curr_line)
19 die("Data item at ($line, $col) precedes current line.");
20 while ($hcfa_curr_line < $line) {
21 $hcfa_data .= "\n";
22 ++$hcfa_curr_line;
23 $hcfa_curr_col = 1;
25 if ($col < $hcfa_curr_col)
26 die("Data item at ($line, $col) precedes current column.");
27 while ($hcfa_curr_col < $col) {
28 $hcfa_data .= " ";
29 ++$hcfa_curr_col;
31 $data = preg_replace('/[.#]/', '', strtoupper($data));
32 $len = min(strlen($data), $maxlen);
33 $hcfa_data .= substr($data, 0, $len);
34 $hcfa_curr_col += $len;
37 function gen_hcfa_1500($pid, $encounter, &$log) {
38 global $hcfa_data, $hcfa_proc_index;
40 $hcfa_data = '';
41 $hcfa_proc_index = 0;
43 $today = time();
44 $claim = new Claim($pid, $encounter);
46 $log .= "Generating HCFA claim $pid-$encounter for " .
47 $claim->patientFirstName() . ' ' .
48 $claim->patientMiddleName() . ' ' .
49 $claim->patientLastName() . ' on ' .
50 date('Y-m-d H:i', $today) . ".\n";
52 while ($hcfa_proc_index < $claim->procCount()) {
53 if ($hcfa_proc_index) $hcfa_data .= "\014"; // append form feed for new page
54 gen_hcfa_1500_page($pid, $encounter, &$log, &$claim);
57 $log .= "\n";
58 return $hcfa_data;
61 function gen_hcfa_1500_page($pid, $encounter, &$log, &$claim) {
62 global $hcfa_curr_line, $hcfa_curr_col, $hcfa_data, $hcfa_proc_index;
64 $hcfa_curr_line = 1;
65 $hcfa_curr_col = 1;
67 // Payer name, attn, street.
68 put_hcfa(2, 41, 31, $claim->payerName());
69 put_hcfa(3, 41, 31, $claim->payerAttn());
70 put_hcfa(4, 41, 31, $claim->payerStreet());
72 // Payer city, state, zip.
73 $tmp = $claim->payerCity() ? ($claim->payerCity() . ', ') : '';
74 put_hcfa(5, 41, 31, $tmp . $claim->payerState() . ' ' . $claim->payerZip());
76 // Box 1. Insurance Type
77 $ct = $claim->claimType();
78 $tmpcol = 45; // Other
79 if ($ct === 'MB') $tmpcol = 1; // Medicare
80 else if ($ct === 'MC') $tmpcol = 8; // Medicaid
81 else if ($ct === 'CH') $tmpcol = 15; // Champus
82 else if ($ct === 'CH') $tmpcol = 24; // Champus VA (why same code?)
83 else if ($ct === 'BL') $tmpcol = 31; // Group Health Plan (only BCBS?)
84 else if ($ct === '16') $tmpcol = 39; // FECA
85 put_hcfa(8, $tmpcol, 1, 'X');
87 // Box 1a. Insured's ID Number
88 put_hcfa(8, 50, 17, $claim->policyNumber());
90 // Box 2. Patient's Name
91 $tmp = $claim->patientLastName() . ', ' . $claim->patientFirstName();
92 if ($claim->patientMiddleName())
93 $tmp .= ', ' . substr($claim->patientMiddleName(),0,1);
94 put_hcfa(10, 1, 28, $tmp);
96 // Box 3. Patient's Birth Date and Sex
97 $tmp = $claim->patientDOB();
98 put_hcfa(10, 31, 2, substr($tmp,4,2));
99 put_hcfa(10, 34, 2, substr($tmp,6,2));
100 put_hcfa(10, 37, 4, substr($tmp,0,4));
101 put_hcfa(10, $claim->patientSex() == 'M' ? 42 : 47, 1, 'X');
103 // Box 4. Insured's Name
104 $tmp = $claim->insuredLastName() . ', ' . $claim->insuredFirstName();
105 if ($claim->insuredMiddleName())
106 $tmp .= ', ' . substr($claim->insuredMiddleName(),0,1);
107 put_hcfa(10, 50, 28, $tmp);
109 // Box 5. Patient's Address
110 put_hcfa(12, 1, 28, $claim->patientStreet());
112 // Box 6. Patient Relationship to Insured
113 $tmp = $claim->insuredRelationship();
114 $tmpcol = 47; // Other
115 if ($tmp === '18') $tmpcol = 33; // self
116 else if ($tmp === '01') $tmpcol = 38; // spouse
117 else if ($tmp === '19') $tmpcol = 42; // child
118 put_hcfa(12, $tmpcol, 1, 'X');
120 // Box 7. Insured's Address
121 put_hcfa(12, 50, 28, $claim->insuredStreet());
123 // Box 5 continued. Patient's City and State
124 put_hcfa(14, 1, 20, $claim->patientCity());
125 put_hcfa(14, 26, 2, $claim->patientState());
127 // Box 8. Patient (Marital) Status
128 $tmp = $claim->patientStatus();
129 $tmpcol = 47; // Other
130 if ($tmp === 'S') $tmpcol = 35; // Single
131 else if ($tmp === 'M') $tmpcol = 41; // Married
132 put_hcfa(14, $tmpcol, 1, 'X');
134 // Box 7 continued. Insured's City and State
135 put_hcfa(14, 50, 20, $claim->insuredCity());
136 put_hcfa(14, 74, 2, $claim->insuredState());
138 // Box 5 continued. Patient's Zip Code and Telephone
139 put_hcfa(16, 1, 10, $claim->patientZip());
140 $tmp = $claim->patientPhone();
141 put_hcfa(16, 15, 3, substr($tmp,0,3));
142 put_hcfa(16, 19, 7, substr($tmp,3));
144 // Box 8 continued. Patient (Employment) Status
145 $tmp = $claim->patientOccupation();
146 if ($tmp === 'STUDENT' ) put_hcfa(16, 41, 1, 'X');
147 else if ($tmp === 'PT STUDENT') put_hcfa(16, 47, 1, 'X');
148 else if ($tmp !== 'UNEMPLOYED') put_hcfa(16, 35, 1, 'X');
150 // Box 7 continued. Insured's Zip Code and Telephone
151 put_hcfa(16, 50, 10, $claim->insuredZip());
152 $tmp = $claim->insuredPhone();
153 put_hcfa(16, 65, 3, substr($tmp,0,3));
154 put_hcfa(16, 69, 7, substr($tmp,3));
156 // Box 9. Other Insured's Name
157 if ($claim->payerCount() > 1) {
158 $tmp = $claim->insuredLastName(1) . ', ' . $claim->insuredFirstName(1);
159 if ($claim->insuredMiddleName(1))
160 $tmp .= ', ' . substr($claim->insuredMiddleName(1),0,1);
161 put_hcfa(18, 1, 28, $tmp);
164 // Box 11. Insured's Group Number
165 put_hcfa(18, 50, 30, $claim->groupNumber());
167 // Box 9a. Other Insured's Policy or Group Number
168 if ($claim->payerCount() > 1) {
169 put_hcfa(20, 1, 28, $claim->policyNumber(1));
172 // Box 10a. Employment Related
173 put_hcfa(20, $claim->isRelatedEmployment() ? 35 : 41, 1, 'X');
175 // Box 11a. Insured's Birth Date and Sex
176 $tmp = $claim->insuredDOB();
177 put_hcfa(20, 53, 2, substr($tmp,4,2));
178 put_hcfa(20, 56, 2, substr($tmp,6,2));
179 put_hcfa(20, 59, 4, substr($tmp,0,4));
180 put_hcfa(20, $claim->insuredSex() == 'M' ? 68 : 75, 1, 'X');
182 // Box 9b. Other Insured's Birth Date and Sex
183 if ($claim->payerCount() > 1) {
184 $tmp = $claim->insuredDOB(1);
185 put_hcfa(22, 2, 2, substr($tmp,4,2));
186 put_hcfa(22, 5, 2, substr($tmp,6,2));
187 put_hcfa(22, 8, 4, substr($tmp,0,4));
188 put_hcfa(22, $claim->insuredSex(1) == 'M' ? 18 : 24, 1, 'X');
191 // Box 10b. Auto Accident
192 put_hcfa(22, $claim->isRelatedAuto() ? 35 : 41, 1, 'X');
193 if ($claim->isRelatedAuto())
194 put_hcfa(22, 45, 2, $claim->autoAccidentState());
196 // Box 11b. Insured's Employer/School Name
197 put_hcfa(22, 50, 30, $claim->groupName());
199 // Box 9c. Other Insured's Employer/School Name
200 if ($claim->payerCount() > 1) {
201 put_hcfa(24, 1, 28, $claim->groupName(1));
204 // Box 10c. Other Accident
205 put_hcfa(24, $claim->isRelatedOther() ? 35 : 41, 1, 'X');
207 // Box 11c. Insurance Plan Name or Program Name
208 put_hcfa(24, 50, 30, $claim->planName());
210 // Box 9d. Other Insurance Plan Name or Program Name
211 if ($claim->payerCount() > 1) {
212 put_hcfa(26, 1, 28, $claim->planName(1));
215 // Box 11d. Is There Another Health Benefit Plan
216 put_hcfa(26, $claim->payerCount() > 1 ? 52 : 57, 1, 'X');
218 // Box 12. Patient's or Authorized Person's Signature
219 put_hcfa(29, 7, 17, 'Signature on File');
220 // Note: Date does not apply unless the person physically signs the form.
222 // Box 13. Insured's or Authorized Person's Signature
223 put_hcfa(29, 55, 17, 'Signature on File');
225 // Box 14. Date of Current Illness/Injury/Pregnancy
226 $tmp = $claim->onsetDate();
227 put_hcfa(32, 2, 2, substr($tmp,4,2));
228 put_hcfa(32, 5, 2, substr($tmp,6,2));
229 put_hcfa(32, 8, 4, substr($tmp,0,4));
231 // Box 15. Date of First Occurrence
232 // Not currently supported.
234 // Box 16. Dates Patient Unable to Work in Current Occupation
235 if ($claim->isUnableToWork()) {
236 $tmp = $claim->offWorkFrom();
237 put_hcfa(32, 54, 2, substr($tmp,4,2));
238 put_hcfa(32, 57, 2, substr($tmp,6,2));
239 put_hcfa(32, 60, 4, substr($tmp,0,4));
240 $tmp = $claim->offWorkTo();
241 put_hcfa(32, 68, 2, substr($tmp,4,2));
242 put_hcfa(32, 71, 2, substr($tmp,6,2));
243 put_hcfa(32, 74, 4, substr($tmp,0,4));
246 // Referring provider stuff. Reports are that for primary care doctors,
247 // Medicare forbids an entry here and other payers require one.
248 // There is still confusion over this.
250 if ($claim->referrerLastName() &&
251 (empty($GLOBALS['MedicareReferrerIsRenderer']) || $claim->claimType() != 'MB'))
253 // Box 17a. Referring Provider Alternate Identifier
254 if ($claim->referrerUPIN()) {
255 put_hcfa(33, 30, 2, '1G');
256 put_hcfa(33, 33, 15, $claim->referrerUPIN());
259 // Box 17. Name of Referring Provider or Other Source
260 $tmp = $claim->referrerLastName() . ', ' . $claim->referrerFirstName();
261 if ($claim->referrerMiddleName())
262 $tmp .= ', ' . substr($claim->referrerMiddleName(),0,1);
263 put_hcfa(34, 1, 25, $tmp);
265 // Box 17b. Referring Provider NPI
266 if ($claim->referrerNPI()) {
267 put_hcfa(34, 33, 15, $claim->referrerNPI());
271 // Box 18. Hospitalization Dates Related to Current Services
272 if ($claim->isHospitalized()) {
273 $tmp = $claim->hospitalizedFrom();
274 put_hcfa(34, 54, 2, substr($tmp,4,2));
275 put_hcfa(34, 57, 2, substr($tmp,6,2));
276 put_hcfa(34, 60, 4, substr($tmp,0,4));
277 $tmp = $claim->hospitalizedTo();
278 put_hcfa(34, 68, 2, substr($tmp,4,2));
279 put_hcfa(34, 71, 2, substr($tmp,6,2));
280 put_hcfa(34, 74, 4, substr($tmp,0,4));
283 // Box 19. Reserved for Local Use
284 // Not currently supported.
286 // Box 20. Outside Lab
287 put_hcfa(36, $claim->isOutsideLab() ? 52 : 57, 1, 'X');
288 if ($claim->isOutsideLab()) {
289 // Note here that put_hcfa strips the decimal point, as required.
290 // We right-justify this amount (ending in col. 69).
291 put_hcfa(36, 63, 8, sprintf('%8s', $claim->outsideLabAmount()));
294 // Box 21. Diagnoses
295 $tmp = $claim->diagArray();
296 $diags = array();
297 foreach ($tmp as $diag) $diags[] = $diag;
298 if (!empty($diags[0])) {
299 put_hcfa(38, 3, 3, substr($diags[0], 0, 3));
300 put_hcfa(38, 7, 2, substr($diags[0], 3));
302 if (!empty($diags[2])) {
303 put_hcfa(38, 30, 3, substr($diags[2], 0, 3));
304 put_hcfa(38, 34, 2, substr($diags[2], 3));
307 // Box 22. Medicaid Resubmission Code and Original Ref. No.
308 put_hcfa(38, 50, 10, $claim->medicaidResubmissionCode());
309 put_hcfa(38, 62, 10, $claim->medicaidOriginalReference());
311 // Box 21 continued. Diagnoses
312 if (!empty($diags[1])) {
313 put_hcfa(40, 3, 3, substr($diags[1], 0, 3));
314 put_hcfa(40, 7, 2, substr($diags[1], 3));
316 if (!empty($diags[3])) {
317 put_hcfa(40, 30, 3, substr($diags[3], 0, 3));
318 put_hcfa(40, 34, 2, substr($diags[3], 3));
321 // Box 23. Prior Authorization Number
322 put_hcfa(40, 50, 28, $claim->priorAuth());
324 $proccount = $claim->procCount(); // number of procedures
326 // Charges, adjustments and payments are accumulated by line item so that
327 // each page of a multi-page claim will stand alone. Payments include the
328 // co-pay for the first page only.
329 $clm_total_charges = 0;
330 $clm_amount_adjusted = 0;
331 $clm_amount_paid = $hcfa_proc_index ? 0 : $claim->patientPaidAmount();
333 // Procedure loop starts here.
335 for ($svccount = 0; $svccount < 6 && $hcfa_proc_index < $proccount; ++$hcfa_proc_index) {
336 $dia = $claim->diagIndexArray($hcfa_proc_index);
338 if (!$claim->cptCharges($hcfa_proc_index)) {
339 $log .= "*** Procedure '" . $claim->cptKey($hcfa_proc_index) .
340 "' has no charges!\n";
343 if (empty($dia)) {
344 $log .= "*** Procedure '" . $claim->cptKey($hcfa_proc_index) .
345 "' is not justified!\n";
348 $clm_total_charges += $claim->cptCharges($hcfa_proc_index);
350 // Compute prior payments and "hard" adjustments.
351 for ($ins = 1; $ins < $claim->payerCount(); ++$ins) {
352 if ($claim->payerSequence($ins) > $claim->payerSequence())
353 continue; // skip future payers
354 $payerpaid = $claim->payerTotals($ins, $claim->cptKey($hcfa_proc_index));
355 $clm_amount_paid += $payerpaid[1];
356 $clm_amount_adjusted += $payerpaid[2];
359 ++$svccount;
360 $lino = $svccount * 2 + 41;
362 // Drug Information. Medicaid insurers want this with HCPCS codes.
364 $ndc = $claim->cptNDCID($hcfa_proc_index);
365 if ($ndc) {
366 if (preg_match('/^\d\d\d\d\d-\d\d\d\d-\d\d$/', $ndc, $tmp)) {
367 $ndc = $tmp[1] . $tmp[2] . $tmp[3];
368 } else {
369 $log .= "*** NDC code '$ndc' has invalid format!\n";
371 put_hcfa($lino, 1, 50, "N4$ndc " . $claim->cptNDCUOM($hcfa_proc_index) .
372 $claim->cptNDCQuantity($hcfa_proc_index));
375 // 24i and 24j Top. ID Qualifier and Rendering Provider ID
376 if ($claim->providerNumber()) {
377 put_hcfa($lino, 65, 2, $claim->providerNumberType());
378 put_hcfa($lino, 68, 10, $claim->providerNumber());
381 ++$lino;
383 // 24a. Date of Service
384 $tmp = $claim->serviceDate();
385 put_hcfa($lino, 1, 2, substr($tmp,4,2));
386 put_hcfa($lino, 4, 2, substr($tmp,6,2));
387 put_hcfa($lino, 7, 2, substr($tmp,2,2));
388 put_hcfa($lino,10, 2, substr($tmp,4,2));
389 put_hcfa($lino,13, 2, substr($tmp,6,2));
390 put_hcfa($lino,16, 2, substr($tmp,2,2));
392 // 24b. Place of Service
393 put_hcfa($lino, 19, 2, $claim->facilityPOS());
395 // 24c. EMG
396 // Not currently supported.
398 // 24d. Procedures, Services or Supplies
399 put_hcfa($lino, 25, 7, $claim->cptCode($hcfa_proc_index));
400 put_hcfa($lino, 33, 6, $claim->cptModifier($hcfa_proc_index));
402 // 24e. Diagnosis Pointer
403 $tmp = '';
404 foreach ($claim->diagIndexArray($hcfa_proc_index) as $value) $tmp .= $value;
405 put_hcfa($lino, 45, 4, $tmp);
407 // 24f. Charges
408 put_hcfa($lino, 50, 8, str_replace('.', ' ',
409 sprintf('%8.2f', $claim->cptCharges($hcfa_proc_index))));
411 // 24g. Days or Units
412 put_hcfa($lino, 59, 3, $claim->cptUnits($hcfa_proc_index));
414 // 24h. EPSDT Family Plan
415 // Not currently supported.
417 // 24j. Rendering Provider NPI
418 put_hcfa($lino, 68, 10, $claim->providerNPI());
421 // 25. Federal Tax ID Number
422 // Using the Billing Facility EIN because that's what FreeB did.
423 put_hcfa(56, 1, 15, $claim->billingFacilityETIN());
424 put_hcfa(56, 19, 1, 'X'); // The EIN checkbox
426 // 26. Patient's Account No.
427 // Instructions say hyphens are not allowed, but freeb used them.
428 put_hcfa(56, 23, 15, "$pid-$encounter");
430 // 27. Accept Assignment
431 put_hcfa(56, $claim->billingFacilityAssignment() ? 38 : 43, 1, 'X');
433 // 28. Total Charge
434 put_hcfa(56, 52, 8, str_replace('.',' ',sprintf('%8.2f',$clm_total_charges)));
435 if (!$clm_total_charges) {
436 $log .= "*** This claim has no charges!\n";
439 // 29. Amount Paid
440 put_hcfa(56, 62, 8, str_replace('.',' ',sprintf('%8.2f',$clm_amount_paid)));
442 // 30. Balance Due
443 // For secondary payers this reflects primary "contracted rate" adjustments,
444 // so in general box 30 will not equal box 28 minus box 29.
445 put_hcfa(56, 71, 8, str_replace('.',' ',sprintf('%8.2f',
446 $clm_total_charges - $clm_amount_paid - $clm_amount_adjusted)));
448 // 33. Billing Provider: Phone Number
449 $tmp = $claim->billingContactPhone();
450 put_hcfa(57, 66, 3, substr($tmp,0,3));
451 put_hcfa(57, 70, 7, substr($tmp,3));
453 // 32. Service Facility Location Information: Name
454 put_hcfa(58, 23, 25, $claim->facilityName());
456 // 33. Billing Provider: Name
457 put_hcfa(58, 50, 25, $claim->billingFacilityName());
459 // 32. Service Facility Location Information: Street
460 put_hcfa(59, 23, 25, $claim->facilityStreet());
462 // 33. Billing Provider: Name
463 put_hcfa(59, 50, 25, $claim->billingFacilityStreet());
465 // 31. Signature of Physician or Supplier
466 // FreeB printed the rendering provider's name and the current date here,
467 // but according to my instructions it must be a real signature and date,
468 // or else "Signature on File" or "SOF".
469 put_hcfa(60, 1, 20, 'Signature on File');
471 // $tmp = $claim->providerFirstName();
472 // if ($claim->providerMiddleName()) $tmp .= ' ' . substr($claim->providerMiddleName(),0,1);
473 // put_hcfa(60, 1, 20, $tmp . ' ' . $claim->providerLastName);
475 // 32. Service Facility Location Information: City State Zip
476 $tmp = $claim->facilityCity() ? ($claim->facilityCity() . ' ') : '';
477 put_hcfa(60, 23, 25, $tmp . $claim->facilityState() . ' ' .
478 $claim->facilityZip());
480 // 32. Billing Provider: City State Zip
481 $tmp = $claim->billingFacilityCity() ? ($claim->billingFacilityCity() . ' ') : '';
482 put_hcfa(60, 50, 25, $tmp . $claim->billingFacilityState() . ' ' .
483 $claim->billingFacilityZip());
485 // 32a. Service Facility NPI
486 put_hcfa(61, 24, 10, $claim->facilityNPI());
488 // 32b. Service Facility Other ID
489 // Note that Medicare does NOT want this any more.
490 if ($claim->groupNumber()) {
491 put_hcfa(61, 36, 2, $claim->providerNumberType());
492 put_hcfa(61, 39, 11, $claim->groupNumber());
495 // 33a. Billing Facility NPI
496 put_hcfa(61, 51, 10, $claim->billingFacilityNPI());
498 // 33b. Billing Facility Other ID
499 // Note that Medicare does NOT want this any more.
500 if ($claim->groupNumber()) {
501 put_hcfa(61, 63, 2, $claim->providerNumberType());
502 put_hcfa(61, 65, 14, $claim->groupNumber());
505 return;