Wednesday, 28 August 2013

HL7 Parsing–My Own Parser

In my previous post, i showed how to use HAPI for parsing HL7 File. But for my current assignment , i am not able to go further on HAPI. Here is my requirement

1. I need to parse Patient lab result ORU^R01 Message type
2. Here is the structure of the message

MSH
PID
ORC
IN1
IN2...IN4
DG1....DG4
OBR (N times, under each OBR, there can be N number of OBXs)
----> OBX1....OBXn
----------NTEs(Some time OBX may also contain NTEs
NTE(Some times, OBR may not have OBX, but they have NTEs

3. And here is the challenge, upfront i will not know how many DG, OBR and under each OBR, how many OBXs are present in the result file. So without any looping mechanism, we cannot parse the file and convert into human readable text.
4. I tried with lot with HAPI to achieve the desired result, but no luck. May be because of the following reasons
            a) HAPI Does not have much examples to show the power of the parser of each Message type
            b) I do not have time to read their API and form the example Smile

            c) Finally HAPI does not have ability to parse the multiple OBR and OBX


So i decided to drop HAPI and started my own parser. I have been working in Health care industry for more than 14 years, so understanding the terminology/domain is not a problem for me. Writing our own parser is fun for me, because i have the done the same thing in VB6 successfully.  But this time with java. Here is the detail code.

package hl7.parse;

import java.util.HashMap;
import java.util.Map;

public class MyParser {

String message;
private Long totalLines = (long) 0;
private Long MSHCount = (long) 0;
private Long PIDCount = (long) 0;
private Long ORCCount = (long) 0;
private Long totalDGs = (long) 0;
private Long OBRCounter = (long) 0;
private Long OBXCounter = (long) 0;
private String MSH;
private String PID;
private String ORC;
private Map<String, String> element = new HashMap<String, String>();
private String currentOBR;

public Long getTotalDGs() {
return totalDGs;
}

public Long getTotalOBRs() {
return OBRCounter;
}

public Long getORCCount() {
return ORCCount;
}

public String getORC() {
return ORC;
}

public String getMSH() {
return MSH;
}

public String getPID() {
return PID;
}

public Long getPIDCount() {
return PIDCount;
}

public Long getMSHCount() {
return MSHCount;
}

public Long getTotalLines() {
return totalLines;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public MyParser(String message) {
this.message = message;
}

public String getValue(String key) {
return element.get(key);
}

public void parseIt() {
String delimiter = "\\r";
String[] temp;
temp = message.split(delimiter);
for (int i = 0; i < temp.length; i++) {
// Store the lines if developer wants it
element.put("Segment-" + i, temp[i]);
parseSegment(temp[i]);
totalLines++;
}

// Write the final one
// Store the total OBX Count -- OBR[1]OBX-count
if (this.currentOBR != null) {
String key = "OBR[" + OBRCounter + "]" + "OBX-count";
element.put(key, String.valueOf(OBXCounter));

}
}

private void parseSegment(String line) {
String segment;
segment = line.substring(0, 3);
if (segment.equalsIgnoreCase("MSH"))
parseMSH(line);

if (segment.equalsIgnoreCase("PID"))
parsePID(line);

if (segment.equalsIgnoreCase("ORC"))
parseORC(line);

if (segment.equalsIgnoreCase("DG1") || segment.equalsIgnoreCase("DG2")
|| segment.equalsIgnoreCase("DG3")
|| segment.equalsIgnoreCase("DG4"))
parseDGs(line);

if (segment.equalsIgnoreCase("OBR"))
parseOBRs(line);

if (segment.equalsIgnoreCase("OBX"))
parseOBXs(line);

}

private void parseDGs(String line) {

String delimiter = "\\|";
String[] temp;
temp = line.split(delimiter);
totalDGs++;
element.put("DG-" + totalDGs, line);
element.put("DG-" + totalDGs + "-count", String.valueOf(temp.length));
for (int i = 0; i < temp.length; i++) {
element.put("DG[" + totalDGs + "]-" + i, temp[i]);
}

}

private void parseOBRs(String line) {

String delimiter = "\\|";
String[] temp;
temp = line.split(delimiter);

// Store the total OBX Count -- OBR[1]OBX-count
if (this.currentOBR != null) {
String key = "OBR[" + OBRCounter + "]" + "OBX-count";
element.put(key, String.valueOf(OBXCounter));
}
OBRCounter++;
element.put("OBR[" + OBRCounter + "]", line);
element.put("OBR[" + OBRCounter + "]" + "-count",
String.valueOf(temp.length));
this.currentOBR = "OBR[" + OBRCounter + "]";
// Reset the OBX counter
OBXCounter = (long) 0;
for (int i = 0; i < temp.length; i++) {
element.put("OBR[" + OBRCounter + "]-" + i, temp[i]);
if (i == 4 || i == 16) //
{
parseSubComponent(temp[i], "OBR[" + OBRCounter + "]-" + i);
}
}

}

private void parseOBXs(String line) {

// Here we need to carefull, OBX are under OBR, so we need to attach to
// the correct OBR
String delimiter = "\\|";
String[] temp;
String key;
temp = line.split(delimiter);
OBXCounter++;
key = this.currentOBR + "OBX[" + OBXCounter + "]";
// Very first thing is, We need to Print the OBX Line under OBR as
// OBR[1]OBX[1]
element.put(key, line);
// OBR[1]OBX[1]-count
element.put(key + "-count", String.valueOf(temp.length));
// OBR[1]OBX[1]-1, // OBR[1]OBX[1]-2,etc
for (int i = 0; i < temp.length; i++) {
element.put(key + "-" + i, temp[i]);
}
}

private void parseMSH(String line) {

String delimiter = "\\|";
String[] temp;
temp = line.split(delimiter);
this.MSH = line;
for (int i = 0; i < temp.length; i++) {
element.put("MSH-" + i, temp[i]);
MSHCount++;
}

}

private void parseORC(String line) {

String delimiter = "\\|";
String[] temp;
temp = line.split(delimiter);
this.ORC = line;
for (int i = 0; i < temp.length; i++) {
element.put("ORC-" + i, temp[i]);
if (i == 12 || i == 17 || i == 18) //
{
parseSubComponent(temp[i], "ORC-" + i);
}
ORCCount++;
}

}

private void parsePID(String line) {

String delimiter = "\\|";
String[] temp;
temp = line.split(delimiter);
this.PID = line;
for (int i = 0; i < temp.length; i++) {

element.put("PID-" + i, temp[i]);
if (i == 5 || i == 11) // Let us parse patient Name
{
parseSubComponent(temp[i], "PID-" + i);
}
PIDCount++;
}

}

private void parseSubComponent(String line, String mapKey) {

String delimiter = "\\^";
String[] temp;
temp = line.split(delimiter);
int j;
for (int i = 0; i < temp.length; i++) {
j = i + 1;
element.put(mapKey + "-" + j, temp[i]);
}

}

}





public void onParse() {
String element;
String value;
String hl7Message = getStringFromInputStream("c:\\temp\\example1.hl7");
System.out.println("HL7 Content is " + hl7Message);
myParser = new MyParser(hl7Message);
myParser.parseIt();

element = "Receiving Facility";
value = myParser.getValue("MSH-5");
System.out.println(element + " : " + value);

element = "Patient No";
value = myParser.getValue("PID-2");
System.out.println(element + " : " + value);

element = "Lab Accession Number";
value = myParser.getValue("PID-4");
System.out.println(element + " : " + value);

element = "Patient Last Name";
value = myParser.getValue("PID-5-1");
System.out.println(element + " : " + value);

element = "Patient First Name";
value = myParser.getValue("PID-5-2");
System.out.println(element + " : " + value);

element = "Patient Middle Name";
value = myParser.getValue("PID-5-3");
System.out.println(element + " : " + value);

element = "Patient DOB";
value = myParser.getValue("PID-7");
System.out.println(element + " : " + value);

element = "Patient Sex";
value = myParser.getValue("PID-8");
System.out.println(element + " : " + value);

element = "Provider NPI";
value = myParser.getValue("ORC-12-1");
System.out.println(element + " : " + value);

element = "Provider Last Name";
value = myParser.getValue("ORC-12-2");
System.out.println(element + " : " + value);

element = "Provider First Name";
value = myParser.getValue("ORC-12-3");
System.out.println(element + " : " + value);

element = "Provider Initial";
value = myParser.getValue("ORC-12-5");
System.out.println(element + " : " + value);

element = "Account No";
value = myParser.getValue("ORC-17-1");
System.out.println(element + " : " + value);

element = "Account Name";
value = myParser.getValue("ORC-17-2");
System.out.println(element + " : " + value);

element = "Account Address";
value = myParser.getValue("ORC-18-1");
System.out.println(element + " : " + value);

element = "Account City";
value = myParser.getValue("ORC-18-2");
System.out.println(element + " : " + value);

element = "Account State";
value = myParser.getValue("ORC-18-3");
System.out.println(element + " : " + value);

element = "Account Zip";
value = myParser.getValue("ORC-18-4");
System.out.println(element + " : " + value);

element = "Account Phone";
value = myParser.getValue("ORC-18-5");
System.out.println(element + " : " + value);

System.out
.println("*********************************ALL ABOUT MSH ***********************");
// Let us print the MSH Values
for (int i = 0; i < myParser.getMSHCount(); i++) {
System.out.println("MSH-" + i + ": "
+ myParser.getValue("MSH-" + i));
}

// Ofcouse If you want to you get particular value also as follows
System.out.println("Please give me MSH[3] "
+ myParser.getValue("MSH-3"));

// Ofcourse You can also print MSH String
System.out.println("Please give me MSH Line..." + myParser.getMSH());

System.out
.println("*********************************ALL ABOUT PID ***********************");
// Ofcouse If you want to you get particular value also as follows
System.out.println("Please give me PID[3] "
+ myParser.getValue("PID-3"));

// Ofcourse You can also print PID String
System.out.println("Please give me PID Line..." + myParser.getPID());

// Component 1 [ST] family name (last name)
// Component 2 [ST] given name (first name)
// Component 3 [ST] middle initial or name
// Component 4 [ST] suffix (jr, sr, etc)
// Component 5 [ST] prefix (Mr, Mrs, Dr etc)
// Component 6 [ST] degree (MD, PHD etc)
// Component 7 [ID] name type code

// Ofcouse You can print Patient Last Name as follows
System.out.println("Please give me Patient Last Name "
+ myParser.getValue("PID-5-1"));

// Ofcouse You can print Patient First Name as follows
System.out.println("Please give me Patient First Name "
+ myParser.getValue("PID-5-2"));

// Ofcouse You can print Patient Address as follows
System.out.println("Please give me Patient Address "
+ myParser.getValue("PID-11-1"));

// Ofcouse You can print Patient Address as follows
System.out.println("Please give me Patient City "
+ myParser.getValue("PID-11-2"));

// Ofcouse You can print Patient Address as follows
System.out.println("Please give me Patient State "
+ myParser.getValue("PID-11-3"));

// Ofcouse You can print Patient Address as follows
System.out.println("Please give me Patient Zip "
+ myParser.getValue("PID-11-4"));

// Let us print the PID Values
for (int i = 0; i < myParser.getPIDCount(); i++) {
System.out.println("PID-" + i + ": "
+ myParser.getValue("PID-" + i));
}

System.out
.println("*********************************ALL ABOUT ORC***********************");
// Let us print the ORC Values
for (int i = 0; i < myParser.getORCCount(); i++) {
System.out.println("ORC-" + i + ": "
+ myParser.getValue("ORC-" + i));
}
// Ofcouse If you want to you get particular value also as follows
System.out.println("Please give me ORC[2] "
+ myParser.getValue("ORC-2"));

// Ofcourse You can also print ORC String
System.out.println("Please give me ORC Line..." + myParser.getORC());

System.out
.println("*********************************ALL ABOUT DG***********************");
// Total Number of DGs
System.out.println("Please give me Total DGs " + myParser.getTotalDGs());
// Let us print the DGs
for (int i = 1; i <= myParser.getTotalDGs(); i++) {
System.out.println("DG-" + i + ": " + myParser.getValue("DG-" + i));
}

for (int i = 1; i <= myParser.getTotalDGs(); i++) {
int total = Integer.parseInt(myParser
.getValue("DG-" + i + "-count"));
for (int j = 1; j < total; j++) {
System.out.println("DG[" + i + "]-" + j + " : "
+ myParser.getValue("DG[" + i + "]-" + j));
}

}

System.out
.println("*********************************ALL ABOUT OBR***********************");
// Total Number of OBRs
System.out.println("Please give me Total OBR " + myParser.getTotalOBRs());
// Let us print the OBRs
String key;
for (int i = 1; i <= myParser.getTotalOBRs(); i++) {
key = "OBR[" + i + "]";
System.out.println(key + " : " + myParser.getValue(key));
// We can also retieve the total elements in each OBR
System.out.println("Total Elements in " + key + " is "
+ myParser.getValue(key + "-count"));
}

for (int i = 1; i <= myParser.getTotalOBRs(); i++) {
key = "OBR[" + i + "]-count";
int total = Integer.parseInt(myParser.getValue(key));
for (int j = 1; j < total; j++) {
key = "OBR[" + i + "]-" + j;
System.out.println(key + " : " + myParser.getValue(key));
}

}

System.out
.println("*********************************ALL ABOUT OBX***********************");

key = "OBR[1]OBX-count";
// For given OBR, You can find how many OBXs are there as follows
System.out.println("Please give me Total OBXs in OBR[1] "
+ myParser.getValue(key));

key = "OBR[2]OBX-count";
System.out.println("Please give me Total OBXs in OBR[2] "
+ myParser.getValue(key));

key = "OBR[1]OBX[1]";
// You can also Print the OBX Line in each OBR as follows
System.out.println("Please give me OBX[1] in OBR[1] " + myParser.getValue(key));
key = "OBR[1]OBX-count";
int total = Integer.parseInt(myParser.getValue(key));

// You can also Print all OBXs in OBR[1]
for (int i = 1; i <= total; i++) {
key = "OBR[1]OBX[" + i + "]";
System.out.println("OBR[1]OBX[" + i + "] => " + myParser.getValue(key));
}

// Dont know how many OBRs and how many OBXs in each OBRs, no problem,
// we can put them in the loop
for (int i = 1; i <= myParser.getTotalOBRs(); i++) {
key = "OBR[" + i + "]";
System.out.println(key + " : " + myParser.getValue(key));
key = key + "OBX-count";
total = Integer.parseInt(myParser.getValue(key));
for (int j = 1; j <= total; j++) {
key = "OBR[" + i + "]" + "OBX[" + j + "]";
System.out.println("----->" + key + " : " + myParser.getValue(key));
}
}
}

// convert InputStream to String
private static String getStringFromInputStream(String fileName) {

BufferedReader br = null;
StringBuilder sb = new StringBuilder();

String line;
try {
br = new BufferedReader(new FileReader(fileName));
while ((line = br.readLine()) != null) {
sb.append(line + "\r");
}

} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

return sb.toString();

}

Sample HL7 File


MSH|^~\&|mas|MY XXXX LAB|mas|100|201303201430||ORU^R01||P|2.3|
PID|1|K2TE154871|^^^MRN~^^^CID~^^^ACCN|234567892|K2^TEST^||19640512|M|||^^FL^|||||U|||||||0||
ORC|1|000133423|||CM||||201206132103|PHH|201206141230|^^^^     ^^^^^|||||261^XXXX CARE CENTER|31231 IN.STATE ROAD 17 SUITE 102^LAUDERHILL^NY^22313^95455848444|3|PHH|
IN1|1|09102|MCARE|MEDICARE PART B|PO BOX 44117^^JACKSONVILLE^FL^322310018||8664549007||MEDICARE PART B|||||||K2^TEST^|01|00000000|^^NY^||||||||||||||||||||||||||||T|
DG1|1|ICD|319|UNSPECIFIED MENTAL RETARDATION|||||||||||||BUIR154337^000039106|
DG2|1|ICD|30490|UNSPECIFIED DRUG DEPENDENCE, UNSPECIFIED USE|||||||||||||HOJA159610^000039110|
OBR|1|234567892||22014^K2S|||201211151512|201211151514||||||201211151512||^^^^     ^^^^^||A|GENERAL CHEMISTRY||||||X|
OBX|1|TX|22014^K2S|  ^A^A|X||-||||X||9|201211150000||...||
OBR|2|234567892||2521S^K2S|||201211151512|201211181214|||A|||201211151512||^^^^     ^^^^^||D|DRUGS||||||F|
OBX|1|TX|2521S^K2S|  ^A^A|NEGATIVE|CUTOFF 20 ng/ml|-||||F||9|201211181214||MAM||
OBR|3|234567892||4814^GABA|||201211151512|201212061207|||A|||201211151512||^^^^     ^^^^^||D|DRUGS||||||C|
OBX|1|TX|4814^GABA|  ^D^D|X||-||||X||9|201212060949||RBJ|MERCY^^^^^^|
NTE|1|LD|     A THERAPEUTIC RANGE FOR GABAPENTIN HAS NOT BEEN ESTABLISHED. THE RANGE OF|
NTE|2|LD|     THE ASSAY IS 0.75 TO 40.00 ug/mL.REPORTABLE RESULT BELOW THIS RANGE IS|
NTE|3|LD|     <0.75 ug/mL AND ABOVE THIS RANGE IS >40.0 ug/ml.|
OBR|4|234567892||9632^GABA|||201211151512|201211191214|||A|||201211151512||^^^^     ^^^^^||A|GENERAL CHEMISTRY||||||X|
OBX|1|TX|9632^GABA|  ^A^A|X|ng/ml|NEGATIVE-|*|||X||9|201211191214||MAM||



Here is the output

Receiving Facility : 100
Patient No : K2TE154871
Lab Accession Number : 234567892
Patient Last Name : K2
Patient First Name : TEST
Patient Middle Name : null
Patient DOB : 19640512
Patient Sex : M
Provider NPI :
Provider Last Name :
Provider First Name :
Provider Initial :
Account No : 261
Account Name : XXXX CARE CENTER
Account Address : 31231 IN.STATE ROAD 17 SUITE 102
Account City : LAUDERHILL
Account State : NY
Account Zip : 22313
Account Phone : 95455848444
*********************************ALL ABOUT MSH ***********************
MSH-0: MSH
MSH-1: ^~\&
MSH-2: mas
MSH-3: MY XXXX LAB
MSH-4: mas
MSH-5: 100
MSH-6: 201303201430
MSH-7:
MSH-8: ORU^R01
MSH-9:
MSH-10: P
MSH-11: 2.3
Please give me MSH[3] MY XXXX LAB
Please give me MSH Line...MSH|^~\&|mas|MY XXXX LAB|mas|100|201303201430||ORU^R01||P|2.3|
*********************************ALL ABOUT PID ***********************
Please give me PID[3] ^^^MRN~^^^CID~^^^ACCN
Please give me PID Line...PID|1|K2TE154871|^^^MRN~^^^CID~^^^ACCN|234567892|K2^TEST^||19640512|M|||^^FL^|||||U|||||||0||
Please give me Patient Last Name K2
Please give me Patient First Name TEST
Please give me Patient Address
Please give me Patient City
Please give me Patient State FL
Please give me Patient Zip null
PID-0: PID
PID-1: 1
PID-2: K2TE154871
PID-3: ^^^MRN~^^^CID~^^^ACCN
PID-4: 234567892
PID-5: K2^TEST^
PID-6:
PID-7: 19640512
PID-8: M
PID-9:
PID-10:
PID-11: ^^FL^
PID-12:
PID-13:
PID-14:
PID-15:
PID-16: U
PID-17:
PID-18:
PID-19:
PID-20:
PID-21:
PID-22:
PID-23: 0
*********************************ALL ABOUT ORC***********************
ORC-0: ORC
ORC-1: 1
ORC-2: 000133423
ORC-3:
ORC-4:
ORC-5: CM
ORC-6:
ORC-7:
ORC-8:
ORC-9: 201206132103
ORC-10: PHH
ORC-11: 201206141230
ORC-12: ^^^^ ^^^^^
ORC-13:
ORC-14:
ORC-15:
ORC-16:
ORC-17: 261^XXXX CARE CENTER
ORC-18: 31231 IN.STATE ROAD 17 SUITE 102^LAUDERHILL^NY^22313^95455848444
ORC-19: 3
ORC-20: PHH
Please give me ORC[2] 000133423
Please give me ORC Line...ORC|1|000133423|||CM||||201206132103|PHH|201206141230|^^^^ ^^^^^|||||261^XXXX CARE CENTER|31231 IN.STATE ROAD 17 SUITE 102^LAUDERHILL^NY^22313^95455848444|3|PHH|
*********************************ALL ABOUT DG***********************
Please give me Total DGs 2
DG-1: DG1|1|ICD|319|UNSPECIFIED MENTAL RETARDATION|||||||||||||BUIR154337^000039106|
DG-2: DG1|1|ICD|30490|UNSPECIFIED DRUG DEPENDENCE, UNSPECIFIED USE|||||||||||||HOJA159610^000039110|
DG[1]-1 : 1
DG[1]-2 : ICD
DG[1]-3 : 319
DG[1]-4 : UNSPECIFIED MENTAL RETARDATION
DG[1]-5 :
DG[1]-6 :
DG[1]-7 :
DG[1]-8 :
DG[1]-9 :
DG[1]-10 :
DG[1]-11 :
DG[1]-12 :
DG[1]-13 :
DG[1]-14 :
DG[1]-15 :
DG[1]-16 :
DG[1]-17 : BUIR154337^000039106
DG[2]-1 : 1
DG[2]-2 : ICD
DG[2]-3 : 30490
DG[2]-4 : UNSPECIFIED DRUG DEPENDENCE, UNSPECIFIED USE
DG[2]-5 :
DG[2]-6 :
DG[2]-7 :
DG[2]-8 :
DG[2]-9 :
DG[2]-10 :
DG[2]-11 :
DG[2]-12 :
DG[2]-13 :
DG[2]-14 :
DG[2]-15 :
DG[2]-16 :
DG[2]-17 : HOJA159610^000039110
*********************************ALL ABOUT OBR***********************
Please give me Total OBR 4
OBR[1] : OBR|1|234567892||22014^K2S|||201211151512|201211151514||||||201211151512||^^^^ ^^^^^||A|GENERAL CHEMISTRY||||||X|
Total Elements in OBR[1] is 26
OBR[2] : OBR|2|234567892||2521S^K2S|||201211151512|201211181214|||A|||201211151512||^^^^ ^^^^^||D|DRUGS||||||F|
Total Elements in OBR[2] is 26
OBR[3] : OBR|3|234567892||4814^GABA|||201211151512|201212061207|||A|||201211151512||^^^^ ^^^^^||D|DRUGS||||||C|
Total Elements in OBR[3] is 26
OBR[4] : OBR|4|234567892||9632^GABA|||201211151512|201211191214|||A|||201211151512||^^^^ ^^^^^||A|GENERAL CHEMISTRY||||||X|
Total Elements in OBR[4] is 26
OBR[1]-1 : 1
OBR[1]-2 : 234567892
OBR[1]-3 :
OBR[1]-4 : 22014^K2S
OBR[1]-5 :
OBR[1]-6 :
OBR[1]-7 : 201211151512
OBR[1]-8 : 201211151514
OBR[1]-9 :
OBR[1]-10 :
OBR[1]-11 :
OBR[1]-12 :
OBR[1]-13 :
OBR[1]-14 : 201211151512
OBR[1]-15 :
OBR[1]-16 : ^^^^ ^^^^^
OBR[1]-17 :
OBR[1]-18 : A
OBR[1]-19 : GENERAL CHEMISTRY
OBR[1]-20 :
OBR[1]-21 :
OBR[1]-22 :
OBR[1]-23 :
OBR[1]-24 :
OBR[1]-25 : X
OBR[2]-1 : 2
OBR[2]-2 : 234567892
OBR[2]-3 :
OBR[2]-4 : 2521S^K2S
OBR[2]-5 :
OBR[2]-6 :
OBR[2]-7 : 201211151512
OBR[2]-8 : 201211181214
OBR[2]-9 :
OBR[2]-10 :
OBR[2]-11 : A
OBR[2]-12 :
OBR[2]-13 :
OBR[2]-14 : 201211151512
OBR[2]-15 :
OBR[2]-16 : ^^^^ ^^^^^
OBR[2]-17 :
OBR[2]-18 : D
OBR[2]-19 : DRUGS
OBR[2]-20 :
OBR[2]-21 :
OBR[2]-22 :
OBR[2]-23 :
OBR[2]-24 :
OBR[2]-25 : F
OBR[3]-1 : 3
OBR[3]-2 : 234567892
OBR[3]-3 :
OBR[3]-4 : 4814^GABA
OBR[3]-5 :
OBR[3]-6 :
OBR[3]-7 : 201211151512
OBR[3]-8 : 201212061207
OBR[3]-9 :
OBR[3]-10 :
OBR[3]-11 : A
OBR[3]-12 :
OBR[3]-13 :
OBR[3]-14 : 201211151512
OBR[3]-15 :
OBR[3]-16 : ^^^^ ^^^^^
OBR[3]-17 :
OBR[3]-18 : D
OBR[3]-19 : DRUGS
OBR[3]-20 :
OBR[3]-21 :
OBR[3]-22 :
OBR[3]-23 :
OBR[3]-24 :
OBR[3]-25 : C
OBR[4]-1 : 4
OBR[4]-2 : 234567892
OBR[4]-3 :
OBR[4]-4 : 9632^GABA
OBR[4]-5 :
OBR[4]-6 :
OBR[4]-7 : 201211151512
OBR[4]-8 : 201211191214
OBR[4]-9 :
OBR[4]-10 :
OBR[4]-11 : A
OBR[4]-12 :
OBR[4]-13 :
OBR[4]-14 : 201211151512
OBR[4]-15 :
OBR[4]-16 : ^^^^ ^^^^^
OBR[4]-17 :
OBR[4]-18 : A
OBR[4]-19 : GENERAL CHEMISTRY
OBR[4]-20 :
OBR[4]-21 :
OBR[4]-22 :
OBR[4]-23 :
OBR[4]-24 :
OBR[4]-25 : X
*********************************ALL ABOUT OBX***********************
Please give me Total OBXs in OBR[1] 1
Please give me Total OBXs in OBR[2] 1
Please give me OBX[1] in OBR[1] OBX|1|TX|22014^K2S| ^A^A|X||-||||X||9|201211150000||...||
OBR[1]OBX[1] => OBX|1|TX|22014^K2S| ^A^A|X||-||||X||9|201211150000||...||
OBR[1] : OBR|1|234567892||22014^K2S|||201211151512|201211151514||||||201211151512||^^^^ ^^^^^||A|GENERAL CHEMISTRY||||||X|
----->OBR[1]OBX[1] : OBX|1|TX|22014^K2S| ^A^A|X||-||||X||9|201211150000||...||
OBR[2] : OBR|2|234567892||2521S^K2S|||201211151512|201211181214|||A|||201211151512||^^^^ ^^^^^||D|DRUGS||||||F|
----->OBR[2]OBX[1] : OBX|1|TX|2521S^K2S| ^A^A|NEGATIVE|CUTOFF 20 ng/ml|-||||F||9|201211181214||MAM||
OBR[3] : OBR|3|234567892||4814^GABA|||201211151512|201212061207|||A|||201211151512||^^^^ ^^^^^||D|DRUGS||||||C|
----->OBR[3]OBX[1] : OBX|1|TX|4814^GABA| ^D^D|X||-||||X||9|201212060949||RBJ|MERCY^^^^^^|
OBR[4] : OBR|4|234567892||9632^GABA|||201211151512|201211191214|||A|||201211151512||^^^^ ^^^^^||A|GENERAL CHEMISTRY||||||X|
----->OBR[4]OBX[1] : OBX|1|TX|9632^GABA| ^A^A|X|ng/ml|NEGATIVE-|*|||X||9|201211191214||MAM||

Saturday, 24 August 2013

Preauthorization

Preauthorization


Before you can schedule certain healthcare services you may need to get preapproval from your insurance company. This is called preauthorization. (The terms precertification, prior authorization, and prior approval are also used, and they all basically mean the same thing.) For example, services that may require pre-certification include outpatient and inpatient hospital services, observation services, invasive procedures, CT, MRI and PET scans, and colonoscopies.


In the medical billing world, preauthorization, prior authorization, precertification, and notification are terms that may be used interchangeably to mean that for certain situations and procedures, providers have to contact insurers in advance and obtain a certification number in order to be reimbursed properly (or at all) for services. Insurance verification and insurance authorization services play a vital role in revenue cycle management. In fact, most claim denials happen when a patient is ineligible for services billed by the provider.


If an authorization is required you can usually obtain it from the insurance company over the phone. Some companies have special forms that they want faxed in to them. Usually they will issue you a number or a series of numbers and letters which you will enter on the insurance claim in box 23. Authorizations are usually given for a certain number of visits over a certain period of time.


Typical Screen in PMS Software to store Pre Authorization Numbers by Payer.



image

Friday, 23 August 2013

ZK Dropupload example

This is another example for ZK Drag and Drop files in the zul. Apart from the official example, i have shown how to add muliple files and store in the DB and also, if browser does not support HTML 5, then user can still use button to upload their files.

image

ChosenboxViewModel.java

package demo.combobox.chosenbox;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.io.Files;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.UploadEvent;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

import demo.data.EmailLabels;

public class ChosenboxViewModel {

private ListModelList<String> contactsModel = new ListModelList<String>();
private ListModel<String> labelsModel = new ListModelList<String>(
EmailLabels.getLabels());
private List<String> filesList = new ArrayList<String>();



public List<String> getFilesList() {
return filesList;
}

public void setFilesList(List<String> filesList) {
this.filesList = filesList;
}

@Init
public void init() {

}

@Command("newContact")
public void newContact(@BindingParam("contact") String contact) {
contactsModel.add(contact);
contactsModel.addToSelection(contact);
}

public ListModel<String> getContactsModel() {
return contactsModel;
}

public ListModel<String> getLabelsModel() {
return labelsModel;
}

@Command
@NotifyChange("filesList")
public void doUpload(@ContextParam(ContextType.BIND_CONTEXT) BindContext ctx) throws IOException {

UploadEvent upEvent = null;
Object objUploadEvent = ctx.getTriggerEvent();
if (objUploadEvent != null && (objUploadEvent instanceof UploadEvent)) {
upEvent = (UploadEvent) objUploadEvent;
}
if (upEvent != null) {
org.zkoss.util.media.Media[] medias = upEvent.getMedias();
String pathToStore = getDestinationFolder();
for (org.zkoss.util.media.Media m : medias) {
filesList.add(m.getName());
Files.copy(new File(pathToStore + m.getName()),
m.getStreamData());
}

}
}

private String getDestinationFolder() {

String returnPath = null;
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH); // Note: zero based!
int day = now.get(Calendar.DAY_OF_MONTH);
returnPath = Executions.getCurrent().getDesktop().getWebApp()
.getRealPath("/");
String yearPath = "\\" + "myFiles" + "\\" + year + "\\" + month + "\\"
+ day + "\\";
returnPath = returnPath + yearPath;
File baseDir = new File(returnPath);
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return returnPath;
}

@Command
@NotifyChange("filesList")
public void onDelete(@BindingParam("currentFile") String curFile)
{
filesList.remove(curFile);
}
}


EmailContacts.java


package demo.data;

import java.util.Arrays;
import java.util.Collection;

public class EmailContacts {

public static Collection<? extends String> getContacts() {
return Arrays.asList("Adam (adam@company.org)",
"Chris (chris@company.org)", "Daniel (daniel@company.org)",
"Eve(eve@company.org)", "Fritz (fritz@company.org)",
"Mary (mary@company.org)", "Max (max@company.org)",
"John (john@company.org)", "Peter (peter@company.org)");
}

}

EmailLabels.java


package demo.data;

import java.util.Arrays;
import java.util.Collection;

public class EmailLabels {

public static Collection<? extends String> getLabels() {
return Arrays.asList("accounts", "friends", "information", "personal",
"products", "projects", "support", "work");
}

}



chosenbox.zul


<zk>
<style src="/widgets/combobox/chosenbox/style.css" />
<vlayout hflex="1" apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.combobox.chosenbox.ChosenboxViewModel')">
<image sclass="maillogo"
src="/widgets/combobox/chosenbox/img/logo_zkmail.png" />
<vlayout sclass="mail" hflex="1">
<hbox sclass="mailformrow" hflex="1" align="center">
<label sclass="maillabel" value="Label">To</label>
<chosenbox sclass="mailinput" hflex="1"
model="@load(vm.contactsModel)"
emptyMessage="type or select contacts (existing or new ones)"
creatable="true" createMessage="Create new contact '{0}'"
onSearch="@command('newContact', contact=event.value)"
open="false" />
</hbox>
<hbox sclass="mailformrow" hflex="1" align="center">
<label sclass="maillabel" value="Label"></label>
<chosenbox sclass="mailinput" hflex="1"
model="@load(vm.labelsModel)"
emptyMessage="choose one or more labels" />
</hbox>
<textbox id="hereplease" sclass="mailinput" multiline="true"
hflex="1" height="100px">
</textbox>
</vlayout>
<window title="Attachment(s) You can drage and Drop here"
id="attachwin" border="normal" height="1 80%" width="50%">
<vlayout>
<hlayout>
<dropupload maxsize="5120" detection="self"
width="570px" height="30px"
onUpload="@command('doUpload',upEvent=event)"
content="Drop the files here">
</dropupload>
<button upload="true,maxsize=5120"
onUpload="@command('doUpload',upEvent=event)" label="Add Files">
</button>

</hlayout>
<listbox height="200px" id=""
model="@load(vm.filesList)">
<listhead sizable="true">
<listheader label="FileName" />
<listheader label="Delete" />
</listhead>
<template name="model">
<listitem>
<listcell label="@bind(each)" />
<listcell>
<button image="DeleteRecord.png"
onClick="@command('onDelete', currentFile=each)" />
</listcell>
</listitem>
</template>
</listbox>
</vlayout>
</window>
</vlayout>


</zk>





ChosenboxViewModel.java


package demo.combobox.chosenbox;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.io.Files;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.UploadEvent;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

import demo.data.EmailLabels;

public class ChosenboxViewModel {

private ListModelList<String> contactsModel = new ListModelList<String>();
private ListModel<String> labelsModel = new ListModelList<String>(
EmailLabels.getLabels());
private List<String> filesList = new ArrayList<String>();



public List<String> getFilesList() {
return filesList;
}

public void setFilesList(List<String> filesList) {
this.filesList = filesList;
}

@Init
public void init() {

}

@Command("newContact")
public void newContact(@BindingParam("contact") String contact) {
contactsModel.add(contact);
contactsModel.addToSelection(contact);
}

public ListModel<String> getContactsModel() {
return contactsModel;
}

public ListModel<String> getLabelsModel() {
return labelsModel;
}

@Command
@NotifyChange("filesList")
public void doUpload(@ContextParam(ContextType.BIND_CONTEXT) BindContext ctx) throws IOException {

UploadEvent upEvent = null;
Object objUploadEvent = ctx.getTriggerEvent();
if (objUploadEvent != null && (objUploadEvent instanceof UploadEvent)) {
upEvent = (UploadEvent) objUploadEvent;
}
if (upEvent != null) {
org.zkoss.util.media.Media[] medias = upEvent.getMedias();
String pathToStore = getDestinationFolder();
for (org.zkoss.util.media.Media m : medias) {
filesList.add(m.getName());
Files.copy(new File(pathToStore + m.getName()),
m.getStreamData());
}

}
}

private String getDestinationFolder() {

String returnPath = null;
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH); // Note: zero based!
int day = now.get(Calendar.DAY_OF_MONTH);
returnPath = Executions.getCurrent().getDesktop().getWebApp()
.getRealPath("/");
String yearPath = "\\" + "myFiles" + "\\" + year + "\\" + month + "\\"
+ day + "\\";
returnPath = returnPath + yearPath;
File baseDir = new File(returnPath);
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return returnPath;
}

@Command
@NotifyChange("filesList")
public void onDelete(@BindingParam("currentFile") String curFile)
{
filesList.remove(curFile);
}
}

Thursday, 22 August 2013

Hibernate Custom validator to validate Multiple email ids

In my current project, i need to validate email ids where user will be enter more email id in one text area with semicolon as separator. Before storing into DB, i need to check whether all the email ids are valid one. So i decided to create custom Hibernate validator constraint. For more examples and understanding, please check here

package com.hibernate;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = ValidateEmailValidator.class)
@Documented
public @interface ValidateEmails {

String message() default "{default message}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}



package com.hibernate;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ValidateEmailValidator implements
ConstraintValidator<ValidateEmails, String> {

String emailPattern = ".+@.+\\.[a-z]+";
private final Pattern EMAIL_PATTERN = Pattern.compile(emailPattern);

@Override
public void initialize(ValidateEmails arg0) {

}

@Override
public boolean isValid(String value,
ConstraintValidatorContext constraintContext) {
if (value == null) {
return true;
}
boolean isValid = true;
String delimiter = ";";
String[] temp;
temp = value.split(delimiter);
for (int i = 0; i < temp.length; i++) {
if (validateEmail(temp[i]) == false) {
return false;
}
}
return isValid;
}

private boolean validateEmail(String email) {

return validatePattern(email, EMAIL_PATTERN);
}

private boolean validatePattern(String toBeValidated, Pattern pattern) {
if (toBeValidated == null)
return false;
Matcher m = pattern.matcher(toBeValidated);
return m.matches();
}
}


Example Usage:

@ValidateEmails(message = "Some email ids are invalid for Send result to email.")
private String resultEmails;

ZK List box inline Editing with Add New and Delete action

In my previous example, i showed how to add New record and delete existing record in the inline Editing of grid. This one is same as previous, but i have used ZK List box. Only zul file has been changed, rest all the same code.

<zk>
<style>
.z-label { display:block; } tr.z-row td.z-row-inner { padding:
2px 5px; } .z-row-cnt, .z-column-cnt { text-align: center; }

</style>
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.grid.inline_editing.InlineEditingViewModel')">
<separator></separator>
<separator></separator>
<button onClick="@command('onAddNew')" label="Add New" />
<separator></separator>
<listbox id="demoGrid"
model="@load(vm.allBooks) @template((vm.displayEdit and each.editingStatus) ? 'editable' : 'noneditable')">
<listhead sizable="true">
<listheader label="Title" />
<listheader label="Author" />
<listheader visible="@load(vm.displayEdit)" />
<listheader visible="@load(vm.displayEdit)" />
</listhead>
<template name="editable">
<listitem>
<listcell>
<textbox cols="60" id="titletext"
value="@load(each.title) @save(each.title, before='confirm')" />
</listcell>
<listcell>
<textbox
value="@load(each.author) @save(each.author, before='confirm')" />
</listcell>
<listcell>
<div>
<button image="pencil-small.png"
onClick="@command('confirm', currentBook=each)" />
<button image="cross-small.png"
onClick="@command('changeEditableStatus', currentBook=each )" />
</div>
</listcell>
</listitem>
</template>
<template name="noneditable">
<listitem>
<listcell>
<label value="@load(each.title)" />
</listcell>
<listcell>
<label value="@load(each.author)" />
</listcell>
<listcell>
<button image="pencil-small.png"
onClick="@command('changeEditableStatus', currentBook=each )" />
</listcell>
<listcell>
<button image="DeleteRecord.png"
onClick="@command('onDelete', currentBook=each )" />
</listcell>
</listitem>
</template>
</listbox>
</div>
</zk>

ZK Example for inline Editing with Add New and Delete

I am quite impressed on this demo from ZK. But adding new record and delete existing record is missing as part of typical CRUD. So i thought that we can add that and see how it works. So here is the example on that

image
ZK Version : 6.5.2

Book.java

package demo.grid.inline_editing;

public class Book {
private String author, title ;
private boolean editingStatus;

public Book(String author, String title, boolean editingStatus) {
this.author = author;
this.title = title;
this.editingStatus = editingStatus;

}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}


public boolean getEditingStatus() {
return editingStatus;
}

public void setEditingStatus(boolean editingStatus) {
this.editingStatus = editingStatus;
}

}


BookData.java



package demo.grid.inline_editing;

import java.util.ArrayList;
import java.util.List;

public class BookData {

private final List<Book> allBooks = new ArrayList<Book>();

public BookData(){
allBooks.add(new Book("Philip Hensher", "The Fit",false ));
allBooks.add(new Book("Philip Hensher", "Kitchen Venom",false));
allBooks.add(new Book("Michael Greenberg", "Hurry Down Sunshine",false));
allBooks.add(new Book("Michael Greenberg", "Painless Vocabulary",false));
allBooks.add(new Book("Rick Perlstein", "Nixonland: The Rise of a President and the Fracturing",false));
allBooks.add(new Book("Rick Perlstein", "Nixonland",false));
}

public List<Book> getAllBooks() {
return allBooks;
}

}

Zul File

<zk>
<style>
.z-label { display:block; } tr.z-row td.z-row-inner { padding:
2px 5px; } .z-row-cnt, .z-column-cnt { text-align: center; }

</style>
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.grid.inline_editing.InlineEditingViewModel')">
<separator></separator>
<separator></separator>
<button onClick="@command('onAddNew')" label="Add New" />
<separator></separator>
<grid id="demoGrid"
model="@load(vm.allBooks) @template((vm.displayEdit and each.editingStatus) ? 'editable' : 'noneditable')">
<columns>
<column width="350px">Title</column>
<column width="650px">Author</column>
<column width="120px" visible="@load(vm.displayEdit)">
Edit
</column>
<column width="120px" visible="@load(vm.displayEdit)">
Delete
</column>
</columns>
<template name="editable">
<row>
<textbox cols="60" id="titletext"
value="@load(each.title) @save(each.title, before='confirm')" />
<textbox
value="@load(each.author) @save(each.author, before='confirm')" />

<div>
<button image="pencil-small.png"
onClick="@command('confirm', currentBook=each)" />
<button image="cross-small.png"
onClick="@command('changeEditableStatus', currentBook=each )" />
</div>
</row>
</template>
<template name="noneditable">
<row>
<label value="@load(each.title)" />
<label value="@load(each.author)" />
<button image="pencil-small.png"
onClick="@command('changeEditableStatus', currentBook=each )" />
<button image="DeleteRecord.png"
onClick="@command('onDelete', currentBook=each )" />
</row>
</template>
</grid>
</div>
</zk>





View Model

package demo.grid.inline_editing;

import java.util.List;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zhtml.Messagebox;

public class InlineEditingViewModel {

private BookData data = new BookData();

private boolean displayEdit = true;
private boolean isAddNew = false;

public boolean isDisplayEdit() {
return displayEdit;
}

@NotifyChange({ "allBooks", "displayEdit" })
public void setDisplayEdit(boolean displayEdit) {
this.displayEdit = displayEdit;
}

public List<Book> getAllBooks() {
return data.getAllBooks();
}

@Command
public void changeEditableStatus(@BindingParam("currentBook") Book book) {
if (isAddNew == true) {
data.getAllBooks().remove(book);
isAddNew = false;
} else
book.setEditingStatus(!book.getEditingStatus());
refreshRowTemplate(book);

}

@Command
public void confirm(@BindingParam("currentBook") Book book) {
if (isAddNew == true) {
if (book.getAuthor().equalsIgnoreCase("")
|| book.getTitle().equalsIgnoreCase("")) {
Messagebox.show(" Please enter the values");
return;
} else
isAddNew = false;
}
book.setEditingStatus(!book.getEditingStatus());
refreshRowTemplate(book);
}

public void refreshRowTemplate(Book lcs) {
/*
* This code is special and notifies ZK that the bean's value has
* changed as it is used in the template mechanism. This stops the
* entire Grid's data from being refreshed
*/
BindUtils.postNotifyChange(null, null, lcs, "editingStatus");
}

@Command
@NotifyChange({ "allBooks", "displayEdit" })
public void onAddNew() {
data.getAllBooks().add(0, new Book("", "", true));
isAddNew = true;
}

@Command
@NotifyChange({ "allBooks", "displayEdit" })
public void onDelete(@BindingParam("currentBook") Book book)
{
data.getAllBooks().remove(book);
}
}


Output :
image

Video Demo
http://screencast.com/t/Lwe5GJkN3fQa


You can download the Source here

Patient Demographics and Patient cases

1



2


3


4


5


6



7

Wednesday, 21 August 2013

How to get Current Date and Time and store in the bean

In some cases, we always want to show the current date and time while adding new order or item, etc in the screen. Here is an example for the same.

Assume that you have the Hibernate POJO as follows

	import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

@Temporal(TemporalType.DATE)
private Date collectedDate;

private Time collectedTime;


public Date getCollectedDate() {
return collectedDate;
}

public void setCollectedDate(Date collectedDate) {
this.collectedDate = collectedDate;
}

public Time getCollectedTime() {
return collectedTime;
}

public void setCollectedTime(Time collectedTime) {
this.collectedTime = collectedTime;
}

Now you can use the following statement to set current date and time

this.selectedOrder.setCollectedDate(new Date());
        this.selectedOrder.setCollectedTime(new java.sql.Time(System
                .currentTimeMillis()));

Friday, 16 August 2013

Maintaining Patient Insurance



Many patients only have one insurance plan but it is possible for a patient to have two or three medical insurance policies. The first insurance billed would be the primary insurance. The next one billed would be the secondary, and the last would be the tertiary.


After payment is received from the primary insurance, the secondary is then billed on a new claim with the information regarding payment from the primary insurance in the form of a photocopy of the explanation of benefits. If there is a tertiary insurance, it would be billed after payment from both the primary and secondary insurances is received. Copies of both
the primary and secondary eobs would be attached to a new claim with the tertiary insurance information on it.


Another example of a person having two insurances is when both the husband and the wife work and both are eligible for health benefits from their employers. Their own policy would be primary and the spouse’s would be secondary. If they have children there are rules that determine which policy is prime for the children.


How patient insurances are maintained  in the EMR/PMS software's ?

Today, software's are maintaining the patient insurance in difference ways. Each method has its own cons and pros. I will list those methods which i found in my past experience.


Before getting into detail, let us see the terms "Primary",  Secondary, Tertiary and 4th Insurance". In some software, instead of calling 4th Insurance, they will call as Quaternary Insurance.

Actually, there is no defined process or method to identify which is patient primary insurance , and which is patient secondary insurance, and so on. For more details,
please download this article and you will know how it has been identified. Since there is no defined way, most of the time, reception people will enter the secondary insurance information into primary and vice versa. That's the reason, all the software's providing swapping option for the insurance

And also, we should know how secondary insurance billed and get paid. Please download this document to know more on that.
Now let us see how software's are maintaining the patient insurance.

Method 1 : Maintain 2 or 4 insurance at the Patient Level

Here you can always maintain 2 or 4 insurance at active state. Give important to the word "Active" here. But, HIPAA EDI 837 Transaction allow up to  11 insurance.  So what happens, at one point of time, the existing insurance get expired and patient got new insurance ? Well, you cannot remove  that insurance from the system it because it is tightly linked to Billing (Claims) Module. So only the option is to de activate the existing primary insurance and add new insurance in the active state as primary Insurance.

The advantages in this method is : Insurance are maintained only at one place i.e. at the patient level. So any changes done here, it will impact all the claims linked to that.  This might be useful when there is error in the data entry and after the correction, they want to re submit all the claims linked to that insurance.

And also, at one point of time, there will be huge number in the inactive state, and no idea which claims are linked to that.
But what happen, if the patient is coming for two different visit types : For example, you may have a patient that is being treated for injuries sustained   from an auto accident that is covered under one insurance policy; yet that same patient may receive treatment during the same visit for a condition    unrelated to the auto accident where a different policy may be billed. So in this case, you need to maintain two primary insurance dependent on the visit type.In the above method, it is not possible, so that is one of the disadvantage.

Method : 2

Another way is to clone the patient demographics insurance while claim is created and therein after maintain the copy of all insurance at the claim  level. 
 
In this method, initially, the insurance are maintained at the patient level. But when the claim is created, software will take a copy of all insurance and will maintain along with part of the claim details. So here, after the claim is created, the insurance at the patient level is plugged off and will not have tightly linked with the claim. If any error in the policy details, of course, first we should correct at the claim level and then at the patient level for error free future claims.
if the patient is coming for two different visit types, then we can easily handle here because, we need to change only at the claim level.

Method : 3

And final method is patient case. This method not only to maintain the insurance, but it can also provide a template kind of stuff to create same  kind of claim data for the same patient again  and again to save the time.
In order understand, simply you can think patient case is nothing but to maintain group of insurances.  Let us see detail now.

Sunday, 11 August 2013

EDI Transactions


The HIPAA transactions and code set standards are rules that standardize the electronic exchange of health-related administrative information, such as claims forms. The rules are based on electronic data interchange (EDI) standards, which allow for the exchange of information from computer-to-computer without human involvement.

A "transaction" is an electronic business document. Under HIPAA, a handful of standardized transactions will replace hundreds of proprietary, non-standard transactions currently in use. For example, the HCFA 1500 claims form/file will be replaced by the X12 837 claim/encounter transaction. Each of the HIPAA standard transactions has a name, a number, and a business or administrative use. Those of importance in a medical practice are listed in the table below.


Transaction Number Business use
Claim/encounter X12 837 For submitting claim to health plan, insurer, or other payer
Eligibility inquiry and response X12 270 and 271 For inquiring of a health plan the status of a patient.s eligibility for benefits and details regarding the types of services covered, and for receiving information in response from the health plan or payer.
Claim status inquiry and response X12 276 and 277 For inquiring about and monitoring outstanding claims (where is the claim? Why haven.t you paid us?) and for receiving information in response from the health plan or payer. Claims status codes are now standardized for all payers.
Referrals and prior authorizations X12 278 For obtaining referrals and authorizations accurately and quickly, and for receiving prior authorization responses from the payer or utilization management organization (UMO) used by a payer.
Health care payment and remittance advice X12 835 For replacing paper EOB/EOPs and explaining all adjustment data from payers. Also, permits auto-posting of payments to accounts receivable system.
Health claims attachments (proposed) X12 275 For sending detailed clinical information in support of claims, in response to payment denials, and other similar uses.


How EDI Works
Doctor diagnosis the patient and provide the treatment for the identified disease. Billing Team prepare the bill(claim) and the claim is transmitted into an EDI Document format called as 837 Health care claim. Then the EDI 837 Document securely transmitted to the insurance company via clearing house.

Then the Insurance company processes the claim which comes in the electronic format and provide the necessary reimbursement for the provider for the treatment given to the patient.

Why You Need EDI – the Benefits
  • Lower costs
  • Higher efficiency
  • Improved accuracy
  • Enhanced security
  • Greater management information
Interest to see some sample EDI Documents. Please check here.

837 Professional

Professional billing is responsible for the billing of claims generated for work performed by physicians, suppliers and other non-institutional providers for both outpatient and inpatient services. Professional charges are billed on a CMS-1500 form. The electronic version of the CMS-1500 is called the 837-P, the P standing for the professional format.

837 Institutional
Institutional billing is responsible for the billing of claims generated for work performed by hospitals and skilled nursing facilities. Institutional charges are billed on a UB-04.

Both sets of 837 specifications are same. The only differences would be claim specific data that pertains to a single transaction. All three transactions contain ISA, GS and ST segments but some data and qualifying codes are specific to the type of 837. Another way to quickly identify which type of 837 is being encountered is by the codes sent in the GS-08 or in the ST-03. Professionals use a 005010X222, Institutional uses a 005010X223 and Dental uses a 005010X224.


For 837 Institutional sample, please check here

        

Questions or feedback are always welcome. You can email me at vbsenthilinnet@gmail.com. 

Friday, 9 August 2013

Health care Coding system

There are different coding system used in Healthcare industry. Let us see one by one now. Let us again recall our first definition as follows

"Medical Billing is a Process of Submission of Bills/Claims to the Insurance Company in a specified format for the service rendered (for the treatment given) by the doctor for the patient."

Pay more attention to the word "treatment given by the doctor to the patient". How that information passed to the insurance company ? in plain text via email ? in audio file ? ???

The information passed by means of coding system called Current Procedural Terminology (CPT), simply by numbers, so that other end (insurance company) easily understand what the doctor did for the patient.

Say for example, For example, if you cut your finger and the doctor repairs the cut(treatment) , there is a procedure code(CPT) to put on the bill/claim to collect the payment from the insurance company. So what is that number ? The number is 12001. Search in the Google with "CPT Code 12001" for more stories/articles/information on that :).

Ok, Next who is defining all these numbers ? Well procedure codes owned, copyrighted and developed by the American Medical Association
(http://www.ama-assn.org/ama).  Next question ? AMA also define the price (reimbursement amount) for each procedure ? Yes. But that
is a another big story in healthcare about price(charges) for each procedure code. We will see later about that.

Basically there are three types of CPT which as classified as Category 1 , Category II and category III.
Category I codes include surgery, Anaesthesiology, Vaccines, etc.
Category II is used for performance measurement. Say for an example, if you are smoker, and if the doctor gives counselling to stop smoking,then that is also treated as treatment and identify by the procedure number 0002F .
Category III codes represent temporary codes for new diseases.

HCPCS Codes

HCPCS (HCFA Common Procedural Coding System) is a series of codes developed by the federal government and specifically the Center for Medicare and Medicaid Services (CMS) (formerly known as the Health Care Finance Administration (HCFA)). CMS uses these codes primarily for Medicare and Medicaid to describe procedures or items not listed in the CPT manual

What is the difference between CPT codes and HCPCS?'
CPT codes are owned and copyrighted by the American Medical Association. They describe common procedures used in the course of health care delivery and are oriented to physician use in one way or another. HCPCS are codes generated by the federal government to describe procedures that have special significance to either the Medicaid or Medicare programs.


International classification of diseases (ICD)

ICD is developed and copyrighted by the World Health Organization. ICD is a set of 3, or 5 digit codes that convert a disease, an injury
or a history of a medical condition to a number or numbers. For example, Chicken Pox or Varicella has an ICD-9 code of 052.9. We use numbers so computers can recognize them quickly because insurance companies process millions of claims daily. The 9 means the 9th revision being used. Soon ICD-9 will be eliminated due to being out-dated and because there are so many current codes that there will no longer be any room for new codes. ICD-9 will be replaced by ICD-10. Every year, in October, the codes are released and are effective. There are new codes, codes with different descriptions, and codes that are eliminated. Why do we have new codes? New diseases and injuries are identified every day.

Most commonly used ICD Codes

ICD Code

Description

789

Abdominal Pain

538.8

Stomach Disorder – Acid Reflux

305

ALCOHOL ABUSE

477.9

ALLERGIC RHINITIS CAUSE UNSPECIFIED

308.9

UNSPECIFIED ACUTE REACTION TO STRESS

401.9

UNSPECIFIED ESSENTIAL HYPERTENSION

796.2

BLOOD PRESSURE

787.91

Diarrhea

250.00

Diabetes

786.2

Cough

460

Common Cold

786.5

Chest Pain

723.1

Neck Pain

346.1

Migraine

782.4

Jaundice

783.2

Weight Loss

339.00

Cluster headache syndrome, unspecified

339.10

Tension-type headache, unspecified

780.5

Sleep Disturbance

571.90

Liver disease - chronic, unspecified

493.90

Asthma, unspecified


Relationship between an ICD and a CPT code

CPT codes describe what you do, and ICD codes describe why you do it.


The critical relationship between an ICD-9 code and a CPT code is that the diagnosis supports the medical necessity of the procedure. Since both ICD-9 and CPT are numeric codes, health care consulting firms, the government, and insurers have all designed software that compares the codes for a logical relationship. For example, a bill for CPT 31256, nasal/sinus endoscopy would not be supported by ICD-9 826.0, closed fracture of a phalanges of the foot. Such a claim would be quickly identified and rejected.

Each service you provide becomes a line item (a CPT code) on an insurance claim form. Although your level of reimbursement is linked to a claim's CPT codes, you need to record a symptom, diagnosis or complaint (an ICD-9 code) to establish the “medical necessity” of each service. Showing medical necessity basically means that you justify your treatment choice (CPT code) by linking it to an appropriate diagnosis, symptom or complaint (ICD-9 code). Up to four ICD-9 codes can be linked to each CPT code on a HCFA-1500 form.

For example, a patient in the office for routine diabetes monitoring also complains of chest pain suggesting angina pectoris. As part of the work-up that day, you perform an ECG in your office. On your claim form, however, you list only the ICD-9 code for diabetes. In all likelihood, the insurer won't pay for the ECG because it's not clear from the claim form why the test was medically necessary. The ICD-9 code for chest pain or angina pectoris should also have been listed to indicate the medical necessity for the ECG Link the diagnosis code (ICD-9) to the service code (CPT) on the insurance claim form to identify why the service was rendered, thereby establishing medical necessity.

Here is an another example
if you cut your finger and the doctor repairs the cut, there is a procedure code to put on the claim form. The code is recognized by coders, and insurance company claims software. Let’s look at the cut on the finger. To convert the repair to a CPT code, you need to know the length of the wound, in centimetres so you can select the correct CPT code. For the purpose of this example. You have a 1cm simple cut on your index finger. The repair of this cut would be 12001. Every procedure performed MUST be supported by a correct diagnosis code or ICD-9 code. The diagnosis or ICD-9 code for an unspecified wound of the finger would be 883.0. Now, if you saw ICD-9 code 042 used with CPT code 12001, you would be confused. That would be like saying the doctor sutured the patient’s finger cut because the patient had AIDS. Therefore it doesn’t make sense to suture a wound if there is no open wound diagnosis.