Пример #1
0
			public override int CalcLength(CCDFieldInputter formData){
				CCDField lengthField=formData.GetFieldById(otherFieldId);
				if(lengthField==null) {
					return 0;
				}
				return valueWhenExists;
			}
Пример #2
0
			public override int CalcLength(CCDFieldInputter formData){
				CCDField lengthField=formData.GetFieldById(otherFieldId);
				if(lengthField==null){
					return -1;				
				}
				if(!Regex.IsMatch(lengthField.valuestr,"^[0-9]+$")){
					MessageBox.Show(this.ToString()+".CalcLength: Internal Error, cannot load field length from non-integer field value!");
					return -1;
				}
				//Now the value string of the field is garanteed to be a valid numerical string.
				return Convert.ToInt32(lengthField.valuestr);
			}
Пример #3
0
		public static string Run(int scriptNum,string responseExpected,Claim claim) {
			string retVal="";
			InsPlan insPlan=InsPlans.GetPlan(claim.PlanNum,null);
			InsSub insSub=InsSubs.GetOne(claim.InsSubNum);
			long etransNum=CanadianOutput.SendClaimReversal(claim,insPlan,insSub);
			Etrans etrans=Etranss.GetEtrans(etransNum);
			string message=EtransMessageTexts.GetMessageText(etrans.EtransMessageTextNum);
			CCDFieldInputter formData=new CCDFieldInputter(message);
			string responseStatus=formData.GetValue("G05");
			if(responseStatus!=responseExpected) {
			  return "G05 should be "+responseExpected+"\r\n";
			}
			retVal+="Reversal #"+scriptNum.ToString()+" successful.\r\n";
			return retVal;
		}
Пример #4
0
		public static string RunThree(bool showForms) {
			string retVal="";
			long provNum=ProviderC.ListShort[0].ProvNum;//dentist #1
			Patient pat=Patients.GetPat(PatientTC.PatNum6);//patient#6
			if(pat.PriProv!=provNum){
				Patient oldPat=pat.Copy();
				pat.PriProv=provNum;//this script uses the primary provider for the patient
				Patients.Update(pat,oldPat);
			}
			PatPlan patplan=PatPlans.GetPatPlan(pat.PatNum,2);
			InsSub sub=InsSubs.GetOne(patplan.InsSubNum);
			InsPlan plan=InsPlans.GetPlan(sub.PlanNum,new List<InsPlan>());
			long etransNum=CanadianOutput.SendElegibility(pat.PatNum,plan,new DateTime(1999,1,1),patplan.Relationship,patplan.PatID,showForms,sub);
			Etrans etrans=Etranss.GetEtrans(etransNum);
			string message=EtransMessageTexts.GetMessageText(etrans.EtransMessageTextNum);
			CCDFieldInputter formData=new CCDFieldInputter(message);
			string responseStatus=formData.GetValue("G05");
			if(responseStatus!="R") {
				throw new Exception("Should be R");
			}
			retVal+="Eligibility #3 successful.\r\n";
			return retVal;
		}
Пример #5
0
		///<summary>Checks to be sure that the value string set in this field meets the value requirements. This function can depend on the values of other form fields.</summary>
		public bool CheckValue(CCDFieldInputter formData,string value){
			if(value==null){
				return false;
			}
			//Db format is correct. Now check specific values.
			foreach(ValueRequirement valReq in valueRequirements){
				if(!valReq.MeetsRequirement(formData,value)){
					return false;
				}
			}
			return true;
		}
Пример #6
0
		///<summary>Returns the length required to input the field. This value may depend on other fields. Negative return value indicates error.</summary>
		public int GetRequiredLength(CCDFieldInputter formData){
			return lengthRequirement.CalcLength(formData);
		}
Пример #7
0
			public override int CalcLength(CCDFieldInputter formData){
				return len;
			}
Пример #8
0
			public override bool MeetsRequirement(CCDFieldInputter formData,string value) {
				CCDField otherField=formData.GetFieldById(otherFieldId);
				if(otherField==null){
					MessageBox.Show(this.ToString()+".MeetsRequirement: Internal error, field id "+otherFieldId+" does not exist!");
				}
				foreach(ValueMap valMap in valueMaps){
					foreach(string valstr in valMap.inputValues){
						if(otherField.valuestr==valstr){
							foreach(string str in valMap.discreteValues){
								if(value==str){
									return true;
								}
							}
						}
					}
				}
				return false;
			}
Пример #9
0
			public override bool MeetsRequirement(CCDFieldInputter formData,string value){
				return Regex.IsMatch(value,pattern);
			}
Пример #10
0
		public static string Run(int scriptNum,string responseExpected,string responseTypeExpected,Claim claim,bool showForms) {
			string retVal="";
			ClaimSendQueueItem queueItem=Claims.GetQueueList(claim.ClaimNum,claim.ClinicNum,0)[0];
			Eclaims.GetMissingData(queueItem);//,out warnings);
			if(queueItem.MissingData!="") {
				return "Cannot send claim until missing data is fixed:\r\n"+queueItem.MissingData+"\r\n";
			}
#if DEBUG
			Canadian.testNumber=scriptNum;
#endif
			long etransNum=Canadian.SendClaim(queueItem,showForms);
			Etrans etrans=Etranss.GetEtrans(etransNum);
			string message=EtransMessageTexts.GetMessageText(etrans.EtransMessageTextNum);
			CCDFieldInputter formData=new CCDFieldInputter(message);
			string responseType=formData.GetValue("A04");
			if(responseType!=responseTypeExpected) {
				return "Form type should be "+responseTypeExpected+"\r\n";
			}
			string responseStatus=formData.GetValue("G05");
			if(responseStatus!=responseExpected) {
				return "G05 should be "+responseExpected+"\r\n";
			}
			if(responseExpected=="R" && responseTypeExpected=="11") {
				//so far, only for #6.  We need some other way to test if successful transaction
				string errorMsgCount=formData.GetValue("G06");
				if(errorMsgCount=="00") {
					return "Wrong message count.\r\n";
				}
			}
			retVal+="Claim #"+scriptNum.ToString()+" successful.\r\n";
			return retVal;
		}
Пример #11
0
 ///<summary>Each payment reconciliation request can return up to 9 pages. This function will return one etrans ack for each page in the result, since each page must be requested individually. Only for version 04, no such transaction exists for version 02. The provTreat and provBilling must be validated as CDANet providers before calling this function.</summary>
 public static List<Etrans> GetPaymentReconciliations(Carrier carrier,Provider provTreat,Provider provBilling,DateTime reconciliationDate)
 {
     Clearinghouse clearhouse=Canadian.GetClearinghouse();
     if(clearhouse==null) {
         throw new ApplicationException("Canadian clearinghouse not found.");
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     List<Etrans> etransAcks=new List<Etrans>();
     int pageNumber=1;
     int totalPages=1;
     do{
         StringBuilder strb=new StringBuilder();
         if((carrier.CanadianSupportedTypes&CanSupTransTypes.RequestForPaymentReconciliation_06)!=CanSupTransTypes.RequestForPaymentReconciliation_06) {
             throw new ApplicationException("The carrier does not support payment reconciliation transactions.");
         }
         if(carrier.CanadianNetworkNum==0) {
             throw new ApplicationException("Carrier network not set.");
         }
         CanadianNetwork network=CanadianNetworks.GetNetwork(carrier.CanadianNetworkNum);
         Etrans etrans=Etranss.CreateCanadianOutput(0,carrier.CarrierNum,carrier.CanadianNetworkNum,clearhouse.ClearinghouseNum,EtransType.RequestPay_CA,0,0);
         //A01 transaction prefix 12 AN
         strb.Append(Canadian.TidyAN(network.CanadianTransactionPrefix,12));
         //A02 office sequence number 6 N
         strb.Append(Canadian.TidyN(etrans.OfficeSequenceNumber,6));
         //A03 format version number 2 N
         strb.Append("04");
         //A04 transaction code 2 N
         strb.Append("06");//payment reconciliation request
         //A05 carrier id number 6 N
         if(network.CanadianIsRprHandler) {
             strb.Append("999999");//Always 999999 if the network handles the RPR requests instead of the carriers in the network.
         }
         else {
             strb.Append(carrier.ElectID);//already validated as 6 digit number.
         }
         //A06 software system id 3 AN
         strb.Append(Canadian.SoftwareSystemId());
         //A10 encryption method 1 N
         if(carrier!=null) {
             strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
         }
         else {
             strb.Append("1");//No encryption when sending to a network.
         }
         //A07 message length N4
         strb.Append(Canadian.TidyN("77",5));
         //A09 carrier transaction counter 5 N
         strb.Append(Canadian.TidyN(etrans.CarrierTransCounter,5));
         //B01 CDA provider number 9 AN
         strb.Append(Canadian.TidyAN(provTreat.NationalProvID,9));//already validated
         //B02 (treating) provider office number 4 AN
         strb.Append(Canadian.TidyAN(provTreat.CanadianOfficeNum,4));//already validated
         //B03 billing provider number 9 AN
         //might need to account for possible 5 digit prov id assigned by carrier
         strb.Append(Canadian.TidyAN(provBilling.NationalProvID,9));//already validated
         //B04 billing provider office number 4 AN
         strb.Append(Canadian.TidyAN(provBilling.CanadianOfficeNum,4));//already validated
         //F33 Reconciliation Date 8 N
         strb.Append(reconciliationDate.ToString("yyyyMMdd"));
         //F38 Current Reconciliation Page Number N 1
         strb.Append(Canadian.TidyN(pageNumber,1));
         //End of message construction.
         string result="";
         bool resultIsError=false;
         try {
             result=Canadian.PassToIca(strb.ToString(),clearhouse);
         }
         catch(ApplicationException ex) {
             result=ex.Message;
             resultIsError=true;
             //Etranss.Delete(etrans.EtransNum);//we don't want to do this, because we want the incremented etrans.OfficeSequenceNumber to be saved
             //Attach an ack indicating failure.
         }
         //Attach an ack to the etrans
         Etrans etransAck=new Etrans();
         etransAck.PatNum=etrans.PatNum;
         etransAck.PlanNum=etrans.PlanNum;
         etransAck.InsSubNum=etrans.InsSubNum;
         etransAck.CarrierNum=etrans.CarrierNum;
         etransAck.DateTimeTrans=DateTime.Now;
         CCDFieldInputter fieldInputter=null;
         if(resultIsError) {
             etransAck.Etype=EtransType.AckError;
             etrans.Note="failed";
         }
         else {
             fieldInputter=new CCDFieldInputter(result);
             CCDField fieldG05=fieldInputter.GetFieldById("G05");
             if(fieldG05!=null) {
                 etransAck.AckCode=fieldG05.valuestr;
             }
             etransAck.Etype=fieldInputter.GetEtransType();
         }
         Etranss.Insert(etransAck);
         Etranss.SetMessage(etransAck.EtransNum,result);
         etrans.AckEtransNum=etransAck.EtransNum;
         Etranss.Update(etrans);
         Etranss.SetMessage(etrans.EtransNum,strb.ToString());
         etransAcks.Add(etransAck);
         if(resultIsError) {
             throw new ApplicationException(result);
         }
         CCDField fieldG62=fieldInputter.GetFieldById("G62");//Last reconciliation page number.
         totalPages=PIn.Int(fieldG62.valuestr);
         new FormCCDPrint(etrans,result,true);
         pageNumber++;
     } while(pageNumber<=totalPages);
     return etransAcks;
 }
Пример #12
0
		public static string Run(int scriptNum,string responseExpected,string responseTypeExpected,Claim claim,bool showForms,int pageNumber,int lastPageNumber,double firstExamFee,double diagnosticPhaseFee) {
		  string retVal="";
		  ClaimSendQueueItem queueItem=Claims.GetQueueList(claim.ClaimNum,claim.ClinicNum,0)[0];
		  Eclaims.GetMissingData(queueItem);//,out warnings);
			if(queueItem.MissingData!="") {
				return "Cannot send predetermination until missing data is fixed:\r\n"+queueItem.MissingData+"\r\n";
		  }
#if DEBUG
			Canadian.testNumber=scriptNum;
			claim.PreAuthString=""+pageNumber+","+lastPageNumber+","+firstExamFee+","+diagnosticPhaseFee;
#endif
		  long etransNum=Canadian.SendClaim(queueItem,showForms);
		  Etrans etrans=Etranss.GetEtrans(etransNum);
		  string message=EtransMessageTexts.GetMessageText(etrans.EtransMessageTextNum);
		  CCDFieldInputter formData=new CCDFieldInputter(message);
		  string responseType=formData.GetValue("A04");
		  if(responseType!=responseTypeExpected) {
		    return "Form type is '"+responseType+"' but should be '"+responseTypeExpected+"'\r\n";
		  }
		  string responseStatus=formData.GetValue("G05");
		  if(responseStatus!=responseExpected) {
		    return "G05 is '"+responseStatus+"' but should be '"+responseExpected+"'\r\n";
		  }
		  if(responseExpected=="R" && responseTypeExpected=="11") {
		    //so far, only for #6.  We need some other way to test if successful transaction
		    string errorMsgCount=formData.GetValue("G06");
		    if(errorMsgCount=="00") {
		      return "Wrong message count.\r\n";
		    }
		  }
		  retVal+="Predetermination #"+scriptNum+" page "+pageNumber+" of "+lastPageNumber+" successful.\r\n";
		  return retVal;
		}
Пример #13
0
			public abstract bool MeetsRequirement(CCDFieldInputter formData,string value);
Пример #14
0
		///<summary>Used for ITRANS and Claimstream. Takes a string, creates a file, and drops it into the clearinghouse export path.  Waits for the response, and then returns it as a string.  Will throw an exception if response not received in a reasonable amount of time.  </summary>
		public static string PassToIca(string msgText,Clearinghouse clearhouse) {
			if(clearhouse==null){
				throw new ApplicationException(Lan.g("Canadian","A CDAnet compatible clearinghouse could not be found."));
			}
			string saveFolder=clearhouse.ExportPath;
			if(!Directory.Exists(saveFolder)) {
				throw new ApplicationException(saveFolder+" not found.");
			}
			bool isItrans=(clearhouse.CommBridge==EclaimsCommBridge.ITRANS);
			bool isClaimstream=(clearhouse.CommBridge==EclaimsCommBridge.Claimstream);
			if(isClaimstream) {
				string certFilePath=ODFileUtils.CombinePaths(saveFolder,"OPENDENTAL.pem");
				if(!File.Exists(certFilePath)) {
					File.WriteAllBytes(certFilePath,Properties.Resources.OPENDENTAL_PEM);
				}
			}
			string officeSequenceNumber=msgText.Substring(12,6);//Field A02. Office Sequence Number is always part of every message type and is always in the same place.
			int fileNum=PIn.Int(officeSequenceNumber)%1000;
			//first, delete the result file from previous communication so that no such files can affect the loop logic below.
			string outputFile=ODFileUtils.CombinePaths(saveFolder,"output."+fileNum.ToString().PadLeft(3,'0'));
			if(File.Exists(outputFile)) {
				File.Delete(outputFile);//no exception thrown if file does not exist.
			}
			//create the input file with data:
			string tempInputFile=ODFileUtils.CombinePaths(saveFolder,"tempinput."+fileNum.ToString().PadLeft(3,'0'));
			//First, write to a temp file so that the clearinghouse sofware does not try to send the file while it is still being written.
			File.WriteAllText(tempInputFile,msgText,Encoding.GetEncoding(850));
			//Now that the file is completely written, rename it to the input format that the clearinghouse will recognize and process.
			string inputFile=ODFileUtils.CombinePaths(saveFolder,"input."+fileNum.ToString().PadLeft(3,'0'));
			File.Move(tempInputFile,inputFile);//The input file should not exist, because the clearinghouse software should process input files promptly, unless the clearinghouse service is off for 1000 transactions in a row. We want an exception to be thrown if this file already exists.
			DateTime start=DateTime.Now;
			while(DateTime.Now<start.AddSeconds(120)){//We wait for up to 120 seconds. Responses can take up to 95 seconds and we need some extra time to be sure.
				if(File.Exists(outputFile)){
					break;
				}
				Thread.Sleep(200);//1/10 second
				Application.DoEvents();
			}
			//The _nput.### file is just the input.### renamed after it is processed. The clearinghouse service renames the file so that it is not processed more than once.
			string nputFile=ODFileUtils.CombinePaths(saveFolder,"_nput."+fileNum.ToString().PadLeft(3,'0'));
			//We delete the intermediate file so that claim data is not just lying around.
			if(File.Exists(nputFile)) {//The file would not appear to exist if there was a permission issue.
				File.Delete(nputFile);//no exception thrown if file does not exist.
			}
			if(!File.Exists(outputFile)) {
				if(isItrans) {
					throw new ApplicationException("No response from iCAService. Ensure that the iCAService is started and the iCA folder has the necessary permissions.");
				}
				else if(isClaimstream) {
					throw new ApplicationException("No response from the CCDWS service. Ensure that the CCDWS service is started and the ccd folder has the necessary permissions.");
				}
				//Other clearinghouses, if we ever support them.
				throw new ApplicationException("No response from clearinghouse service. Ensure that the clearinghouse service is started and the export folder has the necessary permissions.");
			}
			byte[] resultBytes=File.ReadAllBytes(outputFile);
			string result=Encoding.GetEncoding(850).GetString(resultBytes);
			//strip the prefix.  Example prefix: 123456,0,000,
			string resultPrefix="";
			//Find position of third comma
			Match match=Regex.Match(result,@"^\d*,\d+,\d+,");
			if(match.Success){
				resultPrefix=result.Substring(0,match.Length);
				result=result.Substring(resultPrefix.Length);
			}
			//We delete the output file so that claim data is not just lying around.
			if(File.Exists(outputFile)) {//The file would not appear to exist if there was a permission issue.
				File.Delete(outputFile);//no exception thrown if file does not exist.
			}
			if(result.Length<42) {//The shortest message is a version 02 Request for Pended Claims with length 42. Any message shorter is considered to be an error message.
				//The only valid message less than 42 characters in length is an Outstanding Transactions Acknowledgement indicating that the mailbox is empty. 
				if(result.Length>=25 && result.Substring(12).StartsWith("NO MORE ITEMS")) {
					//Since the message does not have a well defined format, we skip the code below for parsing the result to check for a mailbox indicator.
					//Additionally, there is no need to check for a mailbox indicator because this message from ITRANS tells us that the mailbox is empty anyway.
					return result;
				}
				string[] responses=resultPrefix.Split(',');
				string errorCode=responses[1];
				string response="Error "+errorCode+"\r\n\r\n"+"Raw Response:\r\n"+resultPrefix+result+"\r\n\r\n";
				if(isItrans) {
					if(errorCode=="1013") {
						response+="The CDA digital certificate for the provider is either missing, not exportable, expired, or invalid.\r\n";
					}
					string errorFile=ODFileUtils.CombinePaths(Path.GetDirectoryName(clearhouse.ClientProgram),"ica.log");
					string errorlog="";
					if(File.Exists(errorFile)) {
						errorlog=File.ReadAllText(errorFile);
					}
					response+="\r\nPlease see http://goitrans.com/itrans_support/itrans_claim_support_error_codes.htm for more details.\r\n";
					response+="Error log:\r\n"+errorlog;
				}
				else if(isClaimstream) {					
					string errorDescription="";
					string errorMessage=GetErrorMessageForCodeClaimstream(errorCode,ref errorDescription);
					response+="Error Message: "+errorMessage+"\r\n";
					response+="Error Description: "+errorDescription+"\r\n\r\n";
					response+="For further error details, read the log file ccdws.log.";
				}
				throw new ApplicationException(response);
			}
			//We parse the result to look for a few things no matter what type of transaction we are dealing with.
			//string requestOfficeSequenceNumber=msgText.Substring(12,6);//Field A02 always exists on all messages and is always in the same location.
			//string responseOfficeSequenceNumber=result.Substring(12,6);//Field A02 always exists on all messages and is always in the same location.
			//string requestMessageType=msgText.Substring(20,2);//Field A04 always exists on all messages and is always in the same location.
			//string responseMessageType=result.Substring(20,2);//Field A04 always exists on all messages and is always in the same location.
			CCDFieldInputter messageData=new CCDFieldInputter(result);
			//if(responseOfficeSequenceNumber!=requestOfficeSequenceNumber) {
			//	CCDField responseStatus=messageData.GetFieldById("G05");
			//	bool isResponseRejection=false;
			//	if(responseStatus!=null && responseStatus.valuestr!=null && responseStatus.valuestr.ToUpper()=="R") {
			//		isResponseRejection=true;
			//	}
			//	if(isResponseRejection) {
			//		//We do not check office sequence numbers for rejection notices, due to the reason described in the following scenario.
			//		//Imagine that a claim is sent with request #000001 and a rejection notice is returned with the same response #000001.
			//		//No changes are made to the claim, and it is sent again, but this time with request #000002.
			//		//ITRANS will respond with the exact same message returned from the first rejection, including response #000001, not response #000002.
			//		//For any other transaction type, we have not seen any cases where the sequence number in the request and response do not match.
			//	}
			//	//Type 04 is an ROT, type 14 is an ROT response. Keep in mind that ROT transactions can get multiple types of responses back.
			//	else if(requestMessageType!="04" || (requestMessageType=="04" && responseMessageType=="14")) {
			//		if(isItrans) {
			//			throw new ApplicationException(Lan.g("Canadian","The office sequence number in the response from ITRANS is invalid. Response")+": "+result);
			//		}
			//		else if(isClaimstream) {
			//			throw new ApplicationException(Lan.g("Canadian","The office sequence number in the response from Claimstream is invalid. Response")+": "+result);
			//		}
			//		//Other clearinghouses, if we ever support them.
			//		throw new ApplicationException(Lan.g("Canadian","The office sequence number in the response from the clearinghouse is invalid. Response")+": "+result);
			//	}
			//}
			//We check the mailbox indicator here, only when the response is returned the first time instead of showing it in FormCCDPrint, because 
			//we do not want the mailbox indicator to be examined multiple times, which could happen if a transaction is viewed again using FormCCDPrint.
			CCDField mailboxIndicator=messageData.GetFieldById("A11");
			if(mailboxIndicator!=null) { //Field A11 should exist in all response types, but just in case.
				if(mailboxIndicator.valuestr.ToUpper()=="Y" || mailboxIndicator.valuestr.ToUpper()=="O") {
					MsgBox.Show("Canadian","NOTIFICATION: Items are waiting in the mailbox. Retrieve these items by going to the Manage module, click the Send Claims button, "
						+"then click the Outstanding button. This box will continue to show each time a claim is sent until the mailbox is cleared.");
				}
			}
			return result;
		}
Пример #15
0
 ///<summary>The result is the etransNum of the response message.  Or it might throw an exception if invalid data.  This class is also responsible for saving the returned message to the etrans table, and for printing out the required form.</summary>
 public static long SendElegibility(long patNum,InsPlan plan,DateTime date,Relat relat,string patID,bool doPrint,InsSub insSub)
 {
     //string electID,long patNum,string groupNumber,string divisionNo,
     //string subscriberID,string patID,Relat patRelat,long subscNum,string dentaideCardSequence)
     //Note: This might be the only class of this kind that returns a string.  It's a special situation.
     //We are simply not going to bother with language translation here.
     Carrier carrier=Carriers.GetCarrier(plan.CarrierNum);
     if(carrier==null) {
         throw new ApplicationException("Invalid carrier.");
     }
     if((carrier.CanadianSupportedTypes&CanSupTransTypes.EligibilityTransaction_08)!=CanSupTransTypes.EligibilityTransaction_08) {
         throw new ApplicationException("The carrier does not support eligibility transactions.");
     }
     if(carrier.CanadianNetworkNum==0) {
         throw new ApplicationException("Carrier network not set.");
     }
     CanadianNetwork network=CanadianNetworks.GetNetwork(carrier.CanadianNetworkNum);
     Patient patient=Patients.GetPat(patNum);
     Patient subscriber=Patients.GetPat(insSub.Subscriber);
     Provider provDefaultTreat=Providers.GetProv(PrefC.GetLong(PrefName.PracticeDefaultProv));
     Clearinghouse clearhouse=Canadian.GetClearinghouse();
     if(clearhouse==null){
         throw new ApplicationException("Canadian clearinghouse not found.");
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     //validate----------------------------------------------------------------------------------------------------
     string error="";
     //if(carrier.CanadianNetworkNum==0){
     //	if(error!="") error+=", ";
     //	error+="Carrier does not have network specified";
     //}
     if(!Regex.IsMatch(carrier.ElectID,@"^[0-9]{6}$")){//not necessary, but nice
         if(error!="") error+=", ";
         error+="CarrierId 6 digits";
     }
     if(!provDefaultTreat.IsCDAnet) {
         error+="Prov not setup as CDA provider";
     }
     if(provDefaultTreat.NationalProvID.Length!=9) {
         if(error!="")	error+=", ";
         error+="Prov CDA num 9 digits";
     }
     if(provDefaultTreat.CanadianOfficeNum.Length!=4) {
         if(error!="") error+=", ";
         error+="Prov office num 4 char";
     }
     //if(plan.GroupNum.Length==0 || groupNumber.Length>12 || groupNumber.Contains(" ")){
     //	if(error!="") error+=", ";
     //	error+="Plan Number";
     //}
     //if(subscriberID==""){//already validated.  And it's allowed to be blank sometimes
     //	if(error!="") error+=", ";
     //	error+="SubscriberID";
     //}
     if(patNum != insSub.Subscriber && relat==Relat.Self) {//if patient is not subscriber, and relat is self
         if(error!="") error+=", ";
         error+="Relationship cannot be self";
     }
     if(patient.Gender==PatientGender.Unknown){
         if(error!="") error+=", ";
         error+="Patient gender";
     }
     if(patient.Birthdate.Year<1880 || patient.Birthdate>DateTime.Today) {
         if(error!="") error+=", ";
         error+="Patient birthdate";
     }
     if(patient.LName=="") {
         if(error!="") error+=", ";
         error+="Patient lastname";
     }
     if(patient.FName=="") {
         if(error!="") error+=", ";
         error+="Patient firstname";
     }
     if(patient.CanadianEligibilityCode==0) {
         if(error!="") error+=", ";
         error+="Patient eligibility exception code";
     }
     if(subscriber.Birthdate.Year<1880 || subscriber.Birthdate>DateTime.Today) {
         if(error!="") error+=", ";
         error+="Subscriber birthdate";
     }
     if(subscriber.LName=="") {
         if(error!="") error+=", ";
         error+="Subscriber lastname";
     }
     if(subscriber.FName=="") {
         if(error!="") error+=", ";
         error+="Subscriber firstname";
     }
     if(error!="") {
         throw new ApplicationException(error);
     }
     Etrans etrans=Etranss.CreateCanadianOutput(patNum,carrier.CarrierNum,0,
         clearhouse.ClearinghouseNum,EtransType.Eligibility_CA,plan.PlanNum,insSub.InsSubNum);
     StringBuilder strb=new StringBuilder();
     //create message----------------------------------------------------------------------------------------------
     //A01 transaction prefix 12 AN
     strb.Append(Canadian.TidyAN(network.CanadianTransactionPrefix,12));
     //A02 office sequence number 6 N
     strb.Append(Canadian.TidyN(etrans.OfficeSequenceNumber,6));
     //A03 format version number 2 N
     strb.Append(carrier.CDAnetVersion);//eg. "04", validated in UI
     //A04 transaction code 2 N
     if(carrier.CDAnetVersion=="02"){
         strb.Append("00");//eligibility
     }
     else{
         strb.Append("08");//eligibility
     }
     //A05 carrier id number 6 N
     strb.Append(carrier.ElectID);//already validated as 6 digit number.
     //A06 software system id 3 AN
     strb.Append(Canadian.SoftwareSystemId());
     if(carrier.CDAnetVersion=="04") {
         //A10 encryption method 1 N
         strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
     }
     //A07 message length 5 N
     int len;
     bool C19PlanRecordPresent=false;
     if(carrier.CDAnetVersion=="02"){
         len=178;
         strb.Append(Canadian.TidyN(len,4));
     }
     else{
         len=214;
         if(plan.CanadianPlanFlag=="A"){// || plan.CanadianPlanFlag=="N"){
             C19PlanRecordPresent=true;
         }
         if(C19PlanRecordPresent){
             len+=30;
         }
         strb.Append(Canadian.TidyN(len,5));
         //A09 carrier transaction counter 5 N, only version 04
         strb.Append(Canadian.TidyN(etrans.CarrierTransCounter,5));
     }
     //B01 CDA provider number 9 AN
     strb.Append(Canadian.TidyAN(provDefaultTreat.NationalProvID,9));//already validated
     //B02 provider office number 4 AN
     strb.Append(Canadian.TidyAN(provDefaultTreat.CanadianOfficeNum,4));//already validated
     if(carrier.CDAnetVersion=="04"){
         //B03 billing provider number 9 AN
         Provider provBilling=Providers.GetProv(Providers.GetBillingProvNum(provDefaultTreat.ProvNum,patient.ClinicNum));
         strb.Append(Canadian.TidyAN(provBilling.NationalProvID,9));//already validated
     }
     if(carrier.CDAnetVersion=="02") {
         //C01 primary policy/plan number 8 AN (group number)
         //No special validation for version 02
         strb.Append(Canadian.TidyAN(plan.GroupNum,8));
     }
     else {
         //C01 primary policy/plan number 12 AN (group number)
         //only validated to ensure that it's not blank and is less than 12. Also that no spaces.
         strb.Append(Canadian.TidyAN(plan.GroupNum,12));
     }
     //C11 primary division/section number 10 AN
     strb.Append(Canadian.TidyAN(plan.DivisionNo,10));
     if(carrier.CDAnetVersion=="02") {
         //C02 subscriber id number 11 AN
         strb.Append(Canadian.TidyAN(insSub.SubscriberID.Replace("-",""),11));//no extra validation for version 02
     }
     else{
         //C02 subscriber id number 12 AN
         strb.Append(Canadian.TidyAN(insSub.SubscriberID.Replace("-",""),12));//validated
     }
     if(carrier.CDAnetVersion=="04") {
         //C17 primary dependant code 2 N. Optional
         strb.Append(Canadian.TidyN(patID,2));
     }
     //C03 relationship code 1 N
     //User interface does not only show Canadian options, but all options are handled.
     strb.Append(Canadian.GetRelationshipCode(relat));
     //C04 patient's sex 1 A
     //validated to not include "unknown"
     if(patient.Gender==PatientGender.Male) {
         strb.Append("M");
     }
     else {
         strb.Append("F");
     }
     //C05 patient birthday 8 N
     strb.Append(patient.Birthdate.ToString("yyyyMMdd"));//validated
     //C06 patient last name 25 AE
     strb.Append(Canadian.TidyAE(patient.LName,25,true));//validated
     //C07 patient first name 15 AE
     strb.Append(Canadian.TidyAE(patient.FName,15,true));//validated
     //C08 patient middle initial 1 AE
     strb.Append(Canadian.TidyAE(patient.MiddleI,1));
     //C09 eligibility exception code 1 N
     strb.Append(Canadian.GetEligibilityCode(patient.CanadianEligibilityCode,carrier.CDAnetVersion=="02"));//validated
     if(carrier.CDAnetVersion=="04") {
         //C12 plan flag 1 A
         strb.Append(Canadian.GetPlanFlag(plan.CanadianPlanFlag));
         //C18 plan record count 1 N
         if(C19PlanRecordPresent) {
             strb.Append("1");
         }
         else {
             strb.Append("0");
         }
         //C16 Eligibility date. 8 N.
         strb.Append(date.ToString("yyyyMMdd"));
     }
     //D01 subscriber birthday 8 N
     strb.Append(subscriber.Birthdate.ToString("yyyyMMdd"));//validated
     //D02 subscriber last name 25 AE
     strb.Append(Canadian.TidyAE(subscriber.LName,25,true));//validated
     //D03 subscriber first name 15 AE
     strb.Append(Canadian.TidyAE(subscriber.FName,15,true));//validated
     //D04 subscriber middle initial 1 AE
     strb.Append(Canadian.TidyAE(subscriber.MiddleI,1));
     if(carrier.CDAnetVersion=="04") {
         //D10 language of insured 1 A
         if(subscriber.Language=="fr") {
             strb.Append("F");
         }
         else {
             strb.Append("E");
         }
         //D11 card sequence/version number 2 N
         //Not validated against type of carrier.  Might need to check if Dentaide.
         strb.Append(Canadian.TidyN(plan.DentaideCardSequence,2));
         //C19 plan record 30 AN
         if(C19PlanRecordPresent) {
             //todo: what text goes here?  Not documented
             strb.Append(Canadian.TidyAN("",30));
         }
     }
     string result="";
     bool resultIsError=false;
     try {
         result=Canadian.PassToIca(strb.ToString(),clearhouse);
     }
     catch(ApplicationException ex) {
         result=ex.Message;
         resultIsError=true;
         //Etranss.Delete(etrans.EtransNum);//we don't want to do this, because we want the incremented etrans.OfficeSequenceNumber to be saved
         //Attach an ack indicating failure.
     }
     //Attach an ack to the etrans
     Etrans etransAck=new Etrans();
     etransAck.PatNum=etrans.PatNum;
     etransAck.PlanNum=etrans.PlanNum;
     etransAck.InsSubNum=etrans.InsSubNum;
     etransAck.CarrierNum=etrans.CarrierNum;
     etransAck.DateTimeTrans=DateTime.Now;
     CCDFieldInputter fieldInputter=null;
     if(resultIsError){
         etransAck.Etype=EtransType.AckError;
         etrans.Note="failed";
     }
     else{
         fieldInputter=new CCDFieldInputter(result);
         CCDField fieldG05=fieldInputter.GetFieldById("G05");
         if(fieldG05!=null) {
             etransAck.AckCode=fieldG05.valuestr;
         }
         etransAck.Etype=fieldInputter.GetEtransType();
     }
     Etranss.Insert(etransAck);
     Etranss.SetMessage(etransAck.EtransNum,result);
     etrans.AckEtransNum=etransAck.EtransNum;
     Etranss.Update(etrans);
     Etranss.SetMessage(etrans.EtransNum,strb.ToString());
     if(resultIsError){
         throw new ApplicationException(result);
     }
     if(doPrint) {
         new FormCCDPrint(etrans,result,true);//Print the form.
     }
     //Now we will process the 'result' here to extract the important data.  Basically Yes or No on the eligibility.
     //We might not do this for any other trans type besides eligibility.
     string strResponse="";//"Eligibility check on "+DateTime.Today.ToShortDateString()+"\r\n";
     //CCDField field=fieldInputter.GetFieldById("G05");//response status
     string valuestr=fieldInputter.GetValue("G05");//response status
     switch(valuestr){
         case "E":
             strResponse+="Patient is eligible.";
             break;
         case "R":
             strResponse+="Patient not eligible, or error in data.";
             break;
         case "M":
             strResponse+="Manual claimform should be submitted for employer certified plan.";
             break;
     }
     etrans=Etranss.GetEtrans(etrans.EtransNum);
     etrans.Note=strResponse;
     Etranss.Update(etrans);
     return etransAck.EtransNum;
     /*
     CCDField[] fields=fieldInputter.GetFieldsById("G08");//Error Codes
     for(int i=0;i<fields.Length;i++){
         retVal+="\r\n";
         retVal+=fields[i].valuestr;//todo: need to turn this into a readable string.
     }
     fields=fieldInputter.GetFieldsById("G32");//Display messages
     for(int i=0;i<fields.Length;i++) {
         retVal+="\r\n";
         retVal+=fields[i].valuestr;
     }
     return retVal;*/
 }
Пример #16
0
 ///<summary></summary>
 public static long SendClaimReversal(Claim claim,InsPlan plan,InsSub insSub)
 {
     StringBuilder strb=new StringBuilder();
     Clearinghouse clearhouse=Canadian.GetClearinghouse();
     if(clearhouse==null) {
         throw new ApplicationException(Lan.g("CanadianOutput","Canadian clearinghouse not found."));
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     Carrier carrier=Carriers.GetCarrier(plan.CarrierNum);
     if((carrier.CanadianSupportedTypes&CanSupTransTypes.ClaimReversal_02)!=CanSupTransTypes.ClaimReversal_02) {
         throw new ApplicationException(Lan.g("CanadianOutput","The carrier does not support reversal transactions."));
     }
     if(carrier.CanadianNetworkNum==0) {
         throw new ApplicationException("Carrier network not set.");
     }
     CanadianNetwork network=CanadianNetworks.GetNetwork(carrier.CanadianNetworkNum);
     Etrans etrans=Etranss.CreateCanadianOutput(claim.PatNum,carrier.CarrierNum,carrier.CanadianNetworkNum,
         clearhouse.ClearinghouseNum,EtransType.ClaimReversal_CA,plan.PlanNum,insSub.InsSubNum);
     etrans.ClaimNum=claim.ClaimNum;//We don't normally use a claim number with Etranss.CreateCanadianOutput(), but here we need the claim number so that we can show the claim reversal in the claim history.
     Etranss.Update(etrans);
     Patient patient=Patients.GetPat(claim.PatNum);
     Provider prov=Providers.GetProv(claim.ProvTreat);
     if(!prov.IsCDAnet) {
         throw new ApplicationException(Lan.g("CanadianOutput","Treating provider is not setup to use CDANet."));
     }
     Provider billProv=ProviderC.ListLong[Providers.GetIndexLong(claim.ProvBill)];
     if(!billProv.IsCDAnet) {
         throw new ApplicationException(Lan.g("CanadianOutput","Billing provider is not setup to use CDANet."));
     }
     InsPlan insPlan=InsPlans.GetPlan(claim.PlanNum,new List<InsPlan>());
     Patient subscriber=Patients.GetPat(insSub.Subscriber);
     //create message----------------------------------------------------------------------------------------------
     //A01 transaction prefix 12 AN
     strb.Append(Canadian.TidyAN(network.CanadianTransactionPrefix,12));
     //A02 office sequence number 6 N
     //We are required to use the same office sequence number as the original claim.
     etrans.OfficeSequenceNumber=0;//Clear the randomly generated office sequence number.
     List<Etrans> claimTransactions=Etranss.GetAllForOneClaim(claim.ClaimNum);
     DateTime originalEtransDateTime=DateTime.MinValue;//So we can get the latest matching transaction.
     for(int i=0;i<claimTransactions.Count;i++) {
         if(claimTransactions[i].Etype==EtransType.Claim_CA || claimTransactions[i].Etype==EtransType.ClaimCOB_CA) {
             Etrans ack=Etranss.GetEtrans(claimTransactions[i].AckEtransNum);
             if(ack==null) {//For those claims sent that didn't receive a response (i.e. when there is an exception while sending a claim).
                 continue;
             }
             string messageText=EtransMessageTexts.GetMessageText(ack.EtransMessageTextNum);
             CCDFieldInputter messageData=new CCDFieldInputter(messageText);
             CCDField transRefNum=messageData.GetFieldById("G01");
             if(transRefNum!=null && transRefNum.valuestr==claim.CanadaTransRefNum && claimTransactions[i].DateTimeTrans>originalEtransDateTime) {
                 etrans.OfficeSequenceNumber=PIn.Int(messageData.GetFieldById("A02").valuestr);
                 originalEtransDateTime=claimTransactions[i].DateTimeTrans;
             }
         }
     }
     DateTime serverDate=MiscData.GetNowDateTime().Date;
     if(originalEtransDateTime.Date!=serverDate) {
         throw new ApplicationException(Lan.g("CanadianOutput","Claims can only be reversed on the day that they were sent. The claim can only be manually reversed."));
     }
     strb.Append(Canadian.TidyN(etrans.OfficeSequenceNumber,6));
     //A03 format version number 2 N
     strb.Append(carrier.CDAnetVersion);//eg. "04", validated in UI
     //A04 transaction code 2 N
     strb.Append("02");//Same for both versions 02 and 04.
     //A05 carrier id number 6 N
     strb.Append(carrier.ElectID);//already validated as 6 digit number.
     //A06 software system id 3 AN
     strb.Append(Canadian.SoftwareSystemId());
     if(carrier.CDAnetVersion!="02") { //version 04
         //A10 encryption method 1 N
         strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
     }
     if(carrier.CDAnetVersion=="02") {
         //A07 message length N4
         strb.Append(Canadian.TidyN("133",4));
     }
     else { //version 04
         //A07 message length N 5
         strb.Append(Canadian.TidyN("164",5));
     }
     if(carrier.CDAnetVersion!="02") { //version 04
         //A09 carrier transaction counter 5 N
     #if DEBUG
         strb.Append("11111");
     #else
         strb.Append(Canadian.TidyN(etrans.CarrierTransCounter,5));
     #endif
     }
     //B01 CDA provider number 9 AN
     strb.Append(Canadian.TidyAN(prov.NationalProvID,9));//already validated
     //B02 provider office number 4 AN
     strb.Append(Canadian.TidyAN(prov.CanadianOfficeNum,4));//already validated
     if(carrier.CDAnetVersion!="02") { //version 04
         //B03 billing provider number 9 AN
         //might need to account for possible 5 digit prov id assigned by carrier
         strb.Append(Canadian.TidyAN(billProv.NationalProvID,9));//already validated
         //B04 billing provider office number 4 AN
         strb.Append(Canadian.TidyAN(billProv.CanadianOfficeNum,4));//already validated
     }
     if(carrier.CDAnetVersion=="02") {
         //C01 primary policy/plan number 8 AN
         //only validated to ensure that it's not blank and is less than 8. Also that no spaces.
         strb.Append(Canadian.TidyAN(insPlan.GroupNum,8));
     }
     else { //version 04
         //C01 primary policy/plan number 12 AN
         //only validated to ensure that it's not blank and is less than 12. Also that no spaces.
         strb.Append(Canadian.TidyAN(insPlan.GroupNum,12));
     }
     //C11 primary division/section number 10 AN
     strb.Append(Canadian.TidyAN(insPlan.DivisionNo,10));
     if(carrier.CDAnetVersion=="02") {
         //C02 subscriber id number 11 AN
         strb.Append(Canadian.TidyAN(insSub.SubscriberID.Replace("-",""),11));//validated
     }
     else { //version 04
         //C02 subscriber id number 12 AN
         strb.Append(Canadian.TidyAN(insSub.SubscriberID.Replace("-",""),12));//validated
     }
     //C03 relationship code 1 N
     //User interface does not only show Canadian options, but all options are handled.
     strb.Append(Canadian.GetRelationshipCode(claim.PatRelat));
     if(carrier.CDAnetVersion=="02") {
         //D02 subscriber last name 25 A
         strb.Append(Canadian.TidyA(subscriber.LName,25));//validated
     }
     else { //version 04
         //D02 subscriber last name 25 AE
         strb.Append(Canadian.TidyAE(subscriber.LName,25,true));//validated
     }
     if(carrier.CDAnetVersion=="02") {
         //D03 subscriber first name 15 A
         strb.Append(Canadian.TidyA(subscriber.FName,15));//validated
     }
     else { //version 04
         //D03 subscriber first name 15 AE
         strb.Append(Canadian.TidyAE(subscriber.FName,15,true));//validated
     }
     if(carrier.CDAnetVersion=="02") {
         //D04 subscriber middle initial 1 A
         strb.Append(Canadian.TidyA(subscriber.MiddleI,1));
     }
     else { //version 04
         //D04 subscriber middle initial 1 AE
         strb.Append(Canadian.TidyAE(subscriber.MiddleI,1));
     }
     if(carrier.CDAnetVersion!="02") { //version 04
         //For Future Use
         strb.Append("000000");
     }
     //G01 transaction reference number of original claim AN 14
     strb.Append(Canadian.TidyAN(claim.CanadaTransRefNum,14));
     string result="";
     bool resultIsError=false;
     try {
       result=Canadian.PassToIca(strb.ToString(),clearhouse);
     }
     catch(ApplicationException ex) {
       result=ex.Message;
       resultIsError=true;
       //Etranss.Delete(etrans.EtransNum);//we don't want to do this, because we want the incremented etrans.OfficeSequenceNumber to be saved
       //Attach an ack indicating failure.
     }
     //Attach an ack to the etrans
     Etrans etransAck=new Etrans();
     etransAck.PatNum=etrans.PatNum;
     etransAck.PlanNum=etrans.PlanNum;
     etransAck.InsSubNum=etrans.InsSubNum;
     etransAck.CarrierNum=etrans.CarrierNum;
     etransAck.DateTimeTrans=DateTime.Now;
     if(resultIsError) {
         etransAck.AckCode="R";//To allow the user to try and reverse the claim again.
       etransAck.Etype=EtransType.AckError;
       etrans.Note="failed";
     }
     else {
         try {
             CCDFieldInputter fieldInputter=new CCDFieldInputter(result);
             CCDField fieldG05=fieldInputter.GetFieldById("G05");
             if(fieldG05!=null) {
                 etransAck.AckCode=fieldG05.valuestr;
             }
             etransAck.Etype=fieldInputter.GetEtransType();
         }
         catch {
             etransAck.AckCode="R";//To allow the user to try and reverse the claim again.
             etransAck.Etype=EtransType.AckError;
             etrans.Note="Could not parse response from ITRANS.";
         }
     }
     Etranss.Insert(etransAck);
     Etranss.SetMessage(etransAck.EtransNum,result);
     etrans.AckEtransNum=etransAck.EtransNum;
     Etranss.Update(etrans);
     Etranss.SetMessage(etrans.EtransNum,strb.ToString());
     if(resultIsError) {
       throw new ApplicationException(result);
     }
     new FormCCDPrint(etrans,result,true);//Print the response.
     if(etrans.AckCode=="R") {
         throw new ApplicationException(Lan.g("CanadianOutput","Reversal was rejected by clearinghouse. The claim must be reversed manually."));
     }
     return etransAck.EtransNum;
 }
Пример #17
0
 //THIS TRANSACTION TYPE IS NOT USED BY ANY CANADIAN CARRIERS, AND IS NOT PART OF CERTIFICATION, EVEN THOUGH IT IS IN THE SPECIFICATIONS. WE NEED TO FIX BELOW COMMENTS AND MAKE THIS CODE FUNCTION MORE LIKE THE GetPaymentReconciliations() FUNCTION ABOVE.
 ///<summary>Does not exist in version 02 so only supported for version 04. Returns the request Etrans record. Usually pass in a carrier with network null.  If sending to a network, carrier will be null and we still don't see anywhere in the message format to specify network.  We expect to get clarification on this issue later. Validate provTreat as a CDANet provider before calling this function.</summary>
 public static Etrans GetSummaryReconciliation(Carrier carrier,CanadianNetwork network,Provider provTreat,DateTime reconciliationDate)
 {
     Clearinghouse clearhouse=Canadian.GetClearinghouse();
     if(clearhouse==null) {
         throw new ApplicationException("Canadian clearinghouse not found.");
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     StringBuilder strb=new StringBuilder();
     Etrans etrans=null;
     if(carrier!=null) {
         if((carrier.CanadianSupportedTypes&CanSupTransTypes.RequestForSummaryReconciliation_05)!=CanSupTransTypes.RequestForSummaryReconciliation_05) {
             throw new ApplicationException("The carrier does not support summary reconciliation transactions.");
         }
         etrans=Etranss.CreateCanadianOutput(0,carrier.CarrierNum,carrier.CanadianNetworkNum,
             clearhouse.ClearinghouseNum,EtransType.RequestSumm_CA,0,0);
     }
     else {//Assume network!=null
         etrans=Etranss.CreateCanadianOutput(0,0,network.CanadianNetworkNum,
             clearhouse.ClearinghouseNum,EtransType.RequestSumm_CA,0,0);
     }
     //A01 transaction prefix 12 AN
     strb.Append(Canadian.TidyAN(network.CanadianTransactionPrefix,12));
     //A02 office sequence number 6 N
     strb.Append(Canadian.TidyN(etrans.OfficeSequenceNumber,6));
     //A03 format version number 2 N
     strb.Append("04");
     //A04 transaction code 2 N
     strb.Append("05");//payment reconciliation request
     //A05 carrier id number 6 N
     if(carrier!=null) {
         strb.Append(carrier.ElectID);//already validated as 6 digit number.
     }
     else { //Assume network!=null
         strb.Append("999999");//Always 999999 when sending to a network.
     }
     //A06 software system id 3 AN
     strb.Append(Canadian.SoftwareSystemId());
     //A10 encryption method 1 N
     if(carrier!=null) {
         strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
     }
     else { //Assume network!=null
         strb.Append("1");//No encryption when sending to a network.
     }
     //A07 message length N4
     strb.Append(Canadian.TidyN("63",5));
     //A09 carrier transaction counter 5 N
     strb.Append(Canadian.TidyN(etrans.CarrierTransCounter,5));
     //B01 CDA provider number 9 AN
     strb.Append(Canadian.TidyAN(provTreat.NationalProvID,9));//already validated
     //B02 (treating) provider office number 4 AN
     strb.Append(Canadian.TidyAN(provTreat.CanadianOfficeNum,4));//already validated
     //F33 Reconciliation Date 8 N
     strb.Append(reconciliationDate.ToString("yyyyMMdd"));
     //End of message construction.
     string result="";
     bool resultIsError=false;
     try {
         if(carrier!=null) {
             result=Canadian.PassToIca(strb.ToString(),clearhouse);
         }
         else { //Assume network!=null
             result=Canadian.PassToIca(strb.ToString(),clearhouse);
         }
     }
     catch(ApplicationException ex) {
         result=ex.Message;
         resultIsError=true;
         //Etranss.Delete(etrans.EtransNum);//we don't want to do this, because we want the incremented etrans.OfficeSequenceNumber to be saved
         //Attach an ack indicating failure.
     }
     //Attach an ack to the etrans
     Etrans etransAck=new Etrans();
     etransAck.PatNum=etrans.PatNum;
     etransAck.PlanNum=etrans.PlanNum;
     etransAck.InsSubNum=etrans.InsSubNum;
     etransAck.CarrierNum=etrans.CarrierNum;
     etransAck.DateTimeTrans=DateTime.Now;
     CCDFieldInputter fieldInputter=null;
     if(resultIsError) {
         etransAck.Etype=EtransType.AckError;
         etrans.Note="failed";
     }
     else {
         fieldInputter=new CCDFieldInputter(result);
         CCDField fieldG05=fieldInputter.GetFieldById("G05");
         if(fieldG05!=null) {
             etransAck.AckCode=fieldG05.valuestr;
         }
         etransAck.Etype=fieldInputter.GetEtransType();
     }
     Etranss.Insert(etransAck);
     Etranss.SetMessage(etransAck.EtransNum,result);
     etrans.AckEtransNum=etransAck.EtransNum;
     Etranss.Update(etrans);
     Etranss.SetMessage(etrans.EtransNum,strb.ToString());
     if(resultIsError) {
         throw new ApplicationException(result);
     }
     new FormCCDPrint(etrans,result,true);
     return etrans;
 }
Пример #18
0
 ///<summary>Takes a string, creates a file, and drops it into the iCA folder.  Waits for the response, and then returns it as a string.  Will throw an exception if response not received in a reasonable amount of time.  </summary>
 public static string PassToIca(string msgText,Clearinghouse clearhouse)
 {
     if(clearhouse==null){
         throw new ApplicationException(Lan.g("Canadian","CDAnet Clearinghouse could not be found."));
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     string officeSequenceNumber=msgText.Substring(12,6);//Field A02. Office Sequence Number is always part of every message type and is always in the same place.
     int fileNum=PIn.Int(officeSequenceNumber)%1000;
     //first, delete the result file from previous communication so that no such files can affect the loop logic below.
     string outputFile=ODFileUtils.CombinePaths(saveFolder,"output."+fileNum.ToString().PadLeft(3,'0'));
     if(File.Exists(outputFile)) {
         File.Delete(outputFile);//no exception thrown if file does not exist.
     }
     //create the input file with data:
     string inputFile=ODFileUtils.CombinePaths(saveFolder,"input."+fileNum.ToString().PadLeft(3,'0'));
     File.WriteAllText(inputFile,msgText,Encoding.GetEncoding(850));
     DateTime start=DateTime.Now;
     while(DateTime.Now<start.AddSeconds(120)){//We wait for up to 120 seconds. Responses can take up to 95 seconds and we need some extra time to be sure.
         if(File.Exists(outputFile)){
             break;
         }
         Thread.Sleep(200);//1/10 second
         Application.DoEvents();
     }
     //The _nput.### file is just the input.### renamed after it is processed.
     string nputFile=ODFileUtils.CombinePaths(saveFolder,"_nput."+fileNum.ToString().PadLeft(3,'0'));
     //We delete the intermediate file so that claim data is not just lying around.
     if(File.Exists(nputFile)) {//The file would not appear to exist if there was a permission issue.
         File.Delete(nputFile);//no exception thrown if file does not exist.
     }
     if(!File.Exists(outputFile)) {
         throw new ApplicationException("No response from ITRANS. Either a folder share permission issue on the server or ITRANS is temporarily unreachable.");
     }
     byte[] resultBytes=File.ReadAllBytes(outputFile);
     string result=Encoding.GetEncoding(850).GetString(resultBytes);
     //strip the prefix.  Example prefix: 123456,0,000,
     string resultPrefix="";
     //Find position of third comma
     Match match=Regex.Match(result,@"^\d*,\d+,\d+,");
     if(match.Success){
         resultPrefix=result.Substring(0,match.Length);
         result=result.Substring(resultPrefix.Length);
     }
     //We delete the output file so that claim data is not just lying around.
     if(File.Exists(outputFile)) {//The file would not appear to exist if there was a permission issue.
         File.Delete(outputFile);//no exception thrown if file does not exist.
     }
     if(result.Length<42) {//The shortest message is a version 02 Request for Pended Claims with length 42. Any message shorter is considered to be an error message.
         string[] responses=resultPrefix.Split(',');
         string response="Error "+responses[1]+"\r\n\r\n"+"Raw Response:\r\n"+resultPrefix+result+"\r\n";
         if(responses[1]=="1013") {
             response+="iCA could not locate a valid digital certificate.\r\n";
         }
         string errorFile=ODFileUtils.CombinePaths(Path.GetDirectoryName(clearhouse.ClientProgram),"ica.log");
         string errorlog="";
         if(File.Exists(errorFile)){
             errorlog=File.ReadAllText(errorFile);
         }
         response+="\r\nPlease see http://goitrans.com/itrans_support/itrans_claim_support_error_codes.htm for more details.\r\n";
         response+="Error log:\r\n"+errorlog;
         throw new ApplicationException(response);
     }
     //We parse the result to look for a few things no matter what type of transaction we are dealing with.
     string requestOfficeSequenceNumber=msgText.Substring(12,6);//Field A02 always exists on all messages and is always in the same location.
     string responseOfficeSequenceNumber=result.Substring(12,6);//Field A02 always exists on all messages and is always in the same location.
     string requestMessageType=msgText.Substring(20,2);//Field A04 always exists on all messages and is always in the same location.
     string responseMessageType=result.Substring(20,2);//Field A04 always exists on all messages and is always in the same location.
     if(responseOfficeSequenceNumber!=requestOfficeSequenceNumber) {
         //Type 04 is an ROT, type 14 is an ROT response. Keep in mind that ROT transactions can get multiple types of responses back.
         if(requestMessageType!="04" || (requestMessageType=="04" && responseMessageType=="14")) {
             throw new ApplicationException(Lan.g("Canadian","The office sequence number in the response from CDAnet is invalid. Response")+": "+result);
         }
     }
     CCDFieldInputter messageData=new CCDFieldInputter(result);
     //We check the mailbox indicator here, only when the response is returned the first time instead of showing it in FormCCDPrint, because
     //we do not want the mailbox indicator to be examined multiple times, which could happen if a transaction is viewed again using FormCCDPrint.
     CCDField mailboxIndicator=messageData.GetFieldById("A11");
     if(mailboxIndicator!=null) { //Field A11 should exist in all response types, but just in case.
         if(mailboxIndicator.valuestr.ToUpper()=="Y" || mailboxIndicator.valuestr.ToUpper()=="O") {
             MsgBox.Show("Canadian","NOTIFICATION: Items are waiting in the mailbox. Retrieve these items by going to the Manage module, click the Send Claims button, "
                 +"then click the Outstanding button. This box will continue to show each time a claim is sent until the mailbox is cleared.");
         }
     }
     return result;
 }
Пример #19
0
		private void PrintDentaide(Graphics g){
			doc.standardFont=standardSmall;
			printPageNumbers=true;
			int headerHeight=(int)verticalLine;
			doc.bounds=new Rectangle(doc.bounds.X,doc.bounds.Y+headerHeight,doc.bounds.Width,
				doc.bounds.Height-headerHeight);//Reset the doc.bounds so that the page numbers are on a row alone.
			x=doc.StartElement();
			text=isFrench?"FORMULAIRE DENTAIDE":"DENTAIDE FORM";
			doc.DrawString(g,text,center-g.MeasureString(text,headingFont).Width/2,0,headingFont);
			x=doc.StartElement();
			text=DateToString(etrans.DateTimeTrans);
			SizeF size1=doc.DrawString(g,isFrench?"DATE: ":"DATE SUBMITTED: ",x,0);
			doc.DrawString(g,text,x+size1.Width,0);
			text="";
			CCDField fieldG01=formData.GetFieldById("G01");
			if(fieldG01!=null) {
				text=fieldG01.valuestr;
			}
			float rightMidCol=400.0f;
			doc.DrawField(g,isFrench?"NO DE TRANSACTION DE DENTAIDE":"DENTAIDE TRANSACTION NO",text,true,rightMidCol,0);
			x=doc.StartElement();
			string fieldText=isFrench?"NO DU PLAN DE TRAITEMENT":"PREDETERMINATION NO";
			if(transactionCode=="13") {//predetermination ACK
				text="";//We are not supposed to show a predetermination number for this transaction type.
			}
			else if(transactionCode=="23") {//predetermination EOB
				//The predetermination number is the same as the dentaide number. Don't change the text value.
			}
			else if(claim!=null) { //Claim ACK or EOB
				//Retreive the predetermination number for this claim from the transaction history.
				text="";//There may not be a predetermination, so we first clear the text.
				List<Etrans> etransHist=Etranss.GetAllForOneClaim(claim.ClaimNum);
				Etrans predetermEobTrans=null;
				for(int i=0;i<etransHist.Count;i++) {//Etrans entries are chronological, so we find the most recent one starting at the end of the list.
					if(etransHist[i].Etype==EtransType.PredetermEOB_CA) {//Predeterm EOBs only not Predeterm Acks, because only EOBs have been truely processed by the carrier.
						if(predetermEobTrans==null || etransHist[i].DateTimeTrans>predetermEobTrans.DateTimeTrans) {
							predetermEobTrans=etransHist[i];
						}
					}
				}
				if(predetermEobTrans!=null) {
					string predetermResponseMessage=EtransMessageTexts.GetMessageText(predetermEobTrans.EtransMessageTextNum);
					CCDFieldInputter predetermResponseFields=new CCDFieldInputter(predetermResponseMessage);
					CCDField predetermFieldG01=predetermResponseFields.GetFieldById("G01");
					if(predetermFieldG01!=null) {
						text=predetermFieldG01.valuestr;//finally retreive the predetermination number.
					}
				}
			}
			else { //For some reason in the tests they want us to print elegibilities on the Dentaide form. In this case there is not predetermination number.
				text="";
			}
			doc.DrawField(g,fieldText,text,false,x,0);
			PrintTreatmentProviderID(g,rightMidCol,0);
			x=doc.StartElement();
			PrintDentistName(g,x,0);
			PrintTreatmentProviderOfficeNumber(g,rightMidCol,0);
			x=doc.StartElement();
			PrintDentistAddress(g,x,0,0);
			size1=PrintDentistPhone(g,rightMidCol,0);
			//Dependant NO. should be same for both primary and secondary insurance, because it is the patient account number from within Open Dental.
			SizeF size2=PrintPrimaryDependantNo(g,rightMidCol,size1.Height,"PATIENT'S OFFICE ACCOUNT NO","NO DE DOSSIER DU PATIENT");
			SizeF size3=PrintOfficeSequenceNumber(g,rightMidCol,size1.Height+size2.Height);
			PrintPatientBirthday(g,rightMidCol,size1.Height+size2.Height+size3.Height);
			x=doc.StartElement();
			PrintPatientName(g,x,0);			
			PrintPatientSex(g,rightMidCol,0);
			x=doc.StartElement();
			PrintComment(g,x,0);
			x=doc.StartElement();
			float leftMidCol=290f;
			text=isFrench?"RENSEIGNEMENTS SUR L'ASSURANCE":"INSURANCE INFORMATION";
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Underline);
			size1=g.MeasureString(text,doc.standardFont);
			doc.DrawString(g,text,x+(leftMidCol-x-size1.Width)/2,0);
			text=isFrench?"PREMIER ASSUREUR":"PRIMARY COVERAGE";
			size1=g.MeasureString(text,doc.standardFont);
			float rightCol=leftMidCol+260f;
			doc.DrawString(g,text,leftMidCol+(rightCol-leftMidCol-size1.Width)/2,0);
			text=isFrench?"SECOND ASSUREUR":"SECONDARY COVERAGE";
			size1=g.MeasureString(text,doc.standardFont);
			doc.DrawString(g,text,rightCol+(doc.bounds.Right-rightCol-size1.Width)/2,0);
			x=doc.StartElement();
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Regular);
			text=isFrench?"ASSUREUR/ADMINIST. RÉGIME:":"CARRIER/PLAN ADMINISTRATOR:";
			doc.DrawString(g,text,x,0);
			text=primaryCarrier.CarrierName.ToUpper();//Field A05
			doc.DrawString(g,text,leftMidCol,0);
			if(claim!=null && insplan2!=null) {
				text=secondaryCarrier.CarrierName.ToUpper();
				doc.DrawString(g,text,rightCol,0);
			}
			x=doc.StartElement();
			text=isFrench?"NO DE POLICE:":"POLICY#:";
			doc.DrawString(g,text,x,0);
			text=insplan.GroupNum;//Field C01
			doc.DrawString(g,text,leftMidCol,0);
			doc.DrawString(g,isFrench?"DIV/SECTION:":"DIV/SECTION NO:",leftMidCol+86,0);
			doc.DrawString(g,insplan.DivisionNo,leftMidCol+190,0);
			if(claim!=null && insplan2!=null) {
				text=insplan2.GroupNum;//Field E02
				doc.DrawString(g,text,rightCol,0);
			}
			doc.DrawString(g,isFrench?"DIV/SECTION:":"DIV/SECTION NO:",rightCol+86,0);
			if(claim!=null && insplan2!=null) {
				doc.DrawString(g,insplan2.DivisionNo,rightCol+190,0);
			}
			x=doc.StartElement();
			text=isFrench?"TITULAIRE:":"INSURED/MEMBER NAME:";
			doc.DrawString(g,text,x,0);
			text=subscriber.GetNameFLFormal();
			doc.DrawString(g,text,leftMidCol,0);
			if(claim!=null && insplan2!=null) {
				text=subscriber2.GetNameFLFormal();
				doc.DrawString(g,text,rightCol,0);
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"ADRESSE:":"ADDRESS:",x,0);
			PrintSubscriberAddress(g,leftMidCol,0,true,rightCol-leftMidCol-10f);
			PrintSubscriberAddress(g,rightCol,0,false,doc.bounds.Right-rightCol-10f);
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"DATE DE NAISSANCE:":"BIRTHDATE:",x,0);
			doc.DrawString(g,DateToString(subscriber.Birthdate),leftMidCol,0);//Field D01
			if(subscriber2!=null){
				doc.DrawString(g,DateToString(subscriber2.Birthdate),rightCol,0);//Field E04
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"NO DU TITULAIRE/CERTIFICAT:":"INSURANCE/CERTIFICATE NO:",x,0);
			doc.DrawString(g,insSub.SubscriberID,leftMidCol,0);//Fields D01 and D11
			doc.DrawString(g,isFrench?"- SÉQUENCE:":"- SEQUENCE:",leftMidCol+86,0);
			doc.DrawString(g,insplan.DentaideCardSequence.ToString().PadLeft(2,'0'),leftMidCol+162,0);
			if(insplan2!=null){
				doc.DrawString(g,insSub2.SubscriberID,rightCol,0);//Fields E04 and E07
			}
			doc.DrawString(g,isFrench?"- SÉQUENCE:":"- SEQUENCE:",rightCol+86,0);
			if(insplan2!=null){
				doc.DrawString(g,insplan2.DentaideCardSequence.ToString().PadLeft(2,'0'),rightCol+162,0);
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"PARENTÉ AVEC PATIENT:":"RELATIONSHIP TO PATIENT:",x,0);
			text="";
			Relat relatPri=GetRelationshipToSubscriber();
			switch(Canadian.GetRelationshipCode(relatPri)){//Field C03
				case "1":
					text=isFrench?"Soi-même":"Self";
					break;
				case "2":
					text=isFrench?"Époux(se)":"Spouse";
					break;
				case "3":
					text="Parent";//Same in French and English.
					break;
				case "4":
					text=isFrench?"Conjoint(e)":"Common Law Spouse";
					break;
				case "5":
					text=isFrench?"Autre":"Other";
					break;
				default:
					break;
			}
			doc.DrawString(g,text,leftMidCol,0);
			if(claim!=null && insplan2!=null) {
				text="";
				switch(Canadian.GetRelationshipCode(patPlanSec.Relationship)){//Field E06
					case "1":
						text=isFrench?"Époux(se)":"Spouse";
						break;
					case "2":
						text=isFrench?"Époux(se)":"Spouse";
						break;
					case "3":
						text="Parent";//Same in French and English.
						break;
					//"4" does not apply.
					case "5":
						text=isFrench?"Autre":"Other";
						break;
					default:
						break;
				}
				doc.DrawString(g,text,rightCol,0);
			}
			//Spaces don't show up with underline, so we will have to underline manually.
			float underlineWidth=g.MeasureString("***",doc.standardFont).Width;
			string isStudent="   ";
			string isHandicapped="   ";
			if(relatPri==Relat.Child){
				text="";
				switch(patient.CanadianEligibilityCode){//Field C09
					case 1://Patient is a full-time student.
						isStudent=isFrench?"Oui":"Yes";
						text=patient.SchoolName;
						break;
					case 2://Patient is disabled.
						isHandicapped=isFrench?"Oui":"Yes";
						break;
					case 3://Patient is a disabled student.
						isStudent=isFrench?"Oui":"Yes";
						text=patient.SchoolName;
						isHandicapped=isFrench?"Oui":"Yes";
						break;
					default://Not applicable
						break;
				}
			}
			else if(patient.CanadianEligibilityCode==2) {
				isHandicapped=isFrench?"Oui":"Yes";
			}
			else if(patient.CanadianEligibilityCode==3) {
				isStudent=isFrench?"Oui":"Yes";
				isHandicapped=isFrench?"Oui":"Yes";
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"PERSONNE À CHARGE: HANDICAPÉ     ÉTUDIANT À PLEIN TEMPS     ; NOM DE L'INSTITUTION:":"IF DEPENDANT, INDICATE: HANDICAPPED     FULL-TIME STUDENT    ;NAME OF STUDENT'S SCHOOL:",x,0);
			doc.DrawString(g,isHandicapped,isFrench?(x+200):(x+248),0);
			doc.DrawString(g,isStudent,isFrench?(x+385):(x+397),0);
			x=doc.StartElement();
			doc.DrawString(g,patient.SchoolName,x,0);
			x=doc.StartElement();
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Underline);
			doc.DrawString(g,isFrench?"RENSEIGNEMENTS SUR LES SOINS:":"TREATMENT INFORMATION:",x,0);
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Regular);
			x=doc.StartElement();
			bullet=1;
			PrintAccidentBullet(g,isFrench?"Le traitement résulte-t-il d'un accident?":"Is treatment resulting from an accident?");
			x=doc.StartElement();
			CCDField initialPlacementUpperField=formData.GetFieldById("F15");
			string initialPlacementUpperStr="X";
			if(initialPlacementUpperField!=null) {
				initialPlacementUpperStr=initialPlacementUpperField.valuestr;
			}
			CCDField initialPlacementLowerField=formData.GetFieldById("F18");
			string initialPlacementLowerStr="X";
			if(initialPlacementLowerField!=null) {
				initialPlacementLowerStr=initialPlacementLowerField.valuestr;
			}			
			x+=doc.DrawString(g,bullet.ToString()+". ",x,0).Width;
			bullet++;
			doc.PushX(x);//Begin indentation.
			string initialPlacementUpperReadable=initialPlacementUpperStr=="X"?" ":(initialPlacementUpperStr=="N"?(isFrench?"Non":"No"):(isFrench?"Oui":"Yes"));
			string initialPlacementLowerReadable=initialPlacementLowerStr=="X"?" ":(initialPlacementLowerStr=="N"?(isFrench?"Non":"No"):(isFrench?"Oui":"Yes"));
			doc.DrawString(g,isFrench?
				"S'agit-il de la première mise en bouche d'une couronne, prothèse ou pont? Maxillaire:      Mandibule:":
				"Is this an initial placement for crown, denture or bridge?      Maxillary:      Mandibular:",x,0);
			doc.DrawString(g,initialPlacementUpperReadable,isFrench?(x+555):(x+510),0);
			doc.DrawString(g,initialPlacementLowerReadable,isFrench?(x+670):(x+625),0);
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"Si Non, indiquer le matériau de l'ancienne prothèse et la date de mise en bouche:":"If no, indicate the material used for the previous prosthesis and the date of insertion:",x,0);
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"Maxillaire:      Matériau:                              Date:":"Maxillary:       Material:                              Date:",x,0);
			doc.DrawString(g,initialPlacementUpperReadable,x+75,0);
			if(initialPlacementUpperStr=="N") {//Field F15 - Avoid invalid upper initial placement data.
				text=GetMaterialDescription(claim.CanadianMaxProsthMaterial);//Field F20
				doc.DrawString(g,text,x+180,0);
				text=DateToString(claim.CanadianDateInitialUpper);//Field F04
				doc.DrawString(g,text,x+420,0);
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"Mandibule:       Matériau:                              Date:":"Mandibular:      Material:                              Date:",x,0);
			doc.DrawString(g,initialPlacementLowerReadable,x+75,0);
			if(initialPlacementLowerStr=="N") {//Field F18 - Avoid invalid lower initial placement data.				
				text=GetMaterialDescription(claim.CanadianMandProsthMaterial);//Field F21
				doc.DrawString(g,text,x+180,0);
				text=DateToString(claim.CanadianDateInitialLower);//Field F19
				doc.DrawString(g,text,x+420,0);
			}
			x=doc.StartElement();
			doc.DrawString(g,isFrench?"Si Oui, indiquer le numéro des dents manquantes et la date des extractions.":"If yes, indicate the missing teeth numbers and the date(s) on which they were removed.",x,0);
			x=doc.StartElement();
			PrintMissingToothList(g);
			x=doc.PopX();//End first indentation.			
			x=doc.StartElement();
			size1=doc.DrawString(g,bullet.ToString()+". "+(isFrench?"S'agit-il d'un traitement en vue de soins d'orthodontie?":
				"Is treatment for orthodontic purposes? "),x,0);
			bullet++;
			if(claim!=null){
				if(claim.IsOrtho){//Field F05
					doc.DrawString(g,isFrench?"Oui":"Yes",x+size1.Width,0);
				}
				else{
					doc.DrawString(g,isFrench?"Non":"No",x+size1.Width,0);
				}
			}
			CCDField orthodonticRecordFlagField=formData.GetFieldById("F25");
			if(predetermination && orthodonticRecordFlagField!=null && orthodonticRecordFlagField.valuestr=="1"){
				x=doc.StartElement();
				x+=doc.DrawString(g,bullet.ToString()+". ",x,0).Width;
				bullet++;
				doc.PushX(x);//Begin indentation.
				doc.DrawString(g,isFrench?"S'il s'agit d'un plan de traitement d'orthodontie, indiquer":
					"For orthodontic treatment plan, please indicate:",x,0);
				x=doc.StartElement();
				text=formData.GetFieldById("F30").valuestr;//Duration of treatment in months.
				if(text!="00"){
					text=text.TrimStart('0');
					doc.DrawField(g,isFrench?"Durée du traitement: ":"Duration of treatment: ",text,true,x,0);
				}
				text=formData.GetFieldById("F26").valuestr;//First examination fee in raw form.
				if(text!="000000"){
					text=RawMoneyStrToDisplayMoney(text);
					doc.DrawField(g,isFrench?"Tarif du premier examen: ":"First examination fee: ",text,true,x,0);
				}
				text=formData.GetFieldById("F27").valuestr;//Diagnostic Phase Fee in raw form.
				if(text!="000000"){
					text=RawMoneyStrToDisplayMoney(text);
					doc.DrawField(g,isFrench?"Tarif de la phase diagnostique: ":"Diagnostic phase fee: ",text,true,x,0);
				}
				text=formData.GetFieldById("F28").valuestr;//Initial fee in raw form.
				if(text!="000000"){
					text=RawMoneyStrToDisplayMoney(text);
					doc.DrawField(g,isFrench?"Paiement initial: ":"Initial fee: ",text,true,x,0);
				}
				text=formData.GetFieldById("F29").valuestr;//Payment mode or expected payment cycle as an enumeration value.
				if(text!="0"){
					if(text=="1"){
						text=isFrench?"Mensuel":"Monthly";
					}
					else if(text=="2"){
						text=isFrench?"Bimestriel":"Bimonthly";
					}
					else if(text=="3"){
						text=isFrench?"Trimestriel":"Quarterly";
					}
					else{//4
						text=isFrench?"Aux quatre mois":"Every four months";
					}
					doc.DrawField(g,isFrench?"Mode de paiement: ":"Payment mode: ",text,true,x,0);
				}
				text=formData.GetFieldById("F31").valuestr;//Number of anticipated payments
				if(text!="00"){
					text=text.TrimStart('0');
					doc.DrawField(g,isFrench?"Nombre prévu de paiements: ":"Number of anticipated payments: ",text,true,x,0);
				}
				text=formData.GetFieldById("F32").valuestr;
				if(text!="000000"){
					text=RawMoneyStrToDisplayMoney(text);
					doc.DrawField(g,isFrench?"Montant prévu du paiement: ":"Anticipated payment amount: ",text,true,x,0);
				}				
				x=doc.StartElement(verticalLine);
				x=doc.PopX();//End indentation.
			}
			x=doc.StartElement();
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Underline);
			doc.DrawString(g,isFrench?"Validation du titulaire/employeur":"Policy holder / employer certification",x,0);
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Regular);
			x=doc.StartElement();
			underlineWidth=g.MeasureString("**************",doc.standardFont).Width;
			size1=doc.DrawString(g,isFrench?"1. Entrée en vigueur de couverture:":"1. Date coverage commenced:",x,0);
			doc.HorizontalLine(g,Pens.Black,x+size1.Width,x+size1.Width+underlineWidth,size1.Height);
			x=doc.StartElement();
			size1=doc.DrawString(g,isFrench?"2. Entrée en vigueur de personne à charge:":"2. Date dependant covered:",x,0);
			doc.HorizontalLine(g,Pens.Black,x+size1.Width,x+size1.Width+underlineWidth,size1.Height);
			x=doc.StartElement();
			size1=doc.DrawString(g,isFrench?"3. Terminaison:":"3. Date terminated:",x,0);
			doc.HorizontalLine(g,Pens.Black,x+size1.Width,x+size1.Width+underlineWidth,size1.Height);
			x=doc.StartElement(verticalLine);
			doc.PushX(x);
			x+=doc.DrawString(g,isFrench?"Signature de personne autorisée:":"Authorized signature:",x,0).Width;
			x+=doc.HorizontalLine(g,Pens.Black,x,x+150,verticalLine).Width+10;
			x+=doc.DrawString(g,isFrench?"Fonction:":"Position:",x,0).Width;
			x+=doc.HorizontalLine(g,Pens.Black,x,x+100,verticalLine).Width+10;
			x+=doc.DrawString(g,"Date:",x,0).Width;
			x+=doc.HorizontalLine(g,Pens.Black,x,x+150,verticalLine).Width+10;
			x=doc.PopX();
			x=doc.StartElement();
			float yoff=0;
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Underline);
			yoff+=doc.DrawString(g,isFrench?"Signature du patient et du dentiste":"Patient's and Dentist's signature",x,yoff).Height;
			doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size,FontStyle.Regular);
			yoff+=doc.DrawString(g,isFrench?"Je déclare qu’à ma connaissance les renseignements donnés sont "+
				"véridiques, exacts et complets. J’autorise la divulgation à l’assureur, ou au Centre "+
				"Dentaide, ou à leurs mandataires de tout renseignement ou dossier relatif à cette "+
				"demande de prestations. Je conviens de rebourser à l’assureur toute somme "+
				"débourséeindûment à mon égard. Je m;engage à verser au dentiste la portion non "+
				"assurée des frais et honoraires demandés pour les soins décrits ci-après.":
				"I certify that to my knowledge this information is truthful, accurate and complete. "+
				"I authorize the disclosure to the insurer, or Centre Dentaide, or their agents of any "+
				"information or file related to this claim. I agree to repay the insurer for any sum "+
				"paid on my behalf, and to pay the dentist the required fees for the uninsured portion "+
				"of the treatment described hereinafter.",x,yoff).Height;
			yoff+=2*verticalLine;
			yoff+=doc.HorizontalLine(g,Pens.Black,x,x+400,yoff).Height;
			yoff+=doc.DrawString(g,isFrench?"Signature du patient (ou parent/tuteur)":
				"Signature of patient (or parent/guardian)",x,yoff).Height;
			yoff+=verticalLine;
			yoff+=doc.DrawString(g,isFrench?"La présente constitue une description exacte des soins exécutés "+
				"et des honoraires demandés ou, dans le cas d'un plan de traitement, des traitements "+
				"devant être exécutés et des honoraires s'y rapportant, sauf erreur ou omission.":
				"The above and the treatments described below are an accurate statement of services "+
				"performed and fees charged, or of services to be performed and fees to be charged in "+
				"the case of a treatment plan, except errors and omissions.",x,yoff).Height;
			yoff+=verticalLine*2;
			yoff+=doc.HorizontalLine(g,Pens.Black,x,x+400,yoff).Height;
			yoff+=doc.DrawString(g,isFrench?"Signature du dentiste":"Dentist's signature",x,yoff).Height;
			x=doc.StartElement();
			size1=doc.DrawString(g,isFrench?"Date du traitement: ":"Date of Service: ",x,0);
			text=DateToString(etrans.DateTimeTrans);
			doc.DrawString(g,text,x+size1.Width,0);
			x=doc.StartElement(verticalLine);
			bool isEOB=transactionCode=="21" || transactionCode=="23";
			CCDField[] noteNumbers=formData.GetFieldsById("G45");//Used to looking up note reference numbers.
			if(claimprocs!=null) {
				List <Procedure> procListAll=Procedures.Refresh(claim.PatNum);
				float amountWidth=g.MeasureString("****.**",doc.standardFont).Width;
				//The rest of the CCDField[] object should all be the same length, since they come bundled together as part 
				//of each procedure.
				CCDField[] procedureLineNumbers=formData.GetFieldsById("F07");
				CCDField[] eligibleAmounts=formData.GetFieldsById("G12");
				CCDField[] deductibleAmounts=formData.GetFieldsById("G13");
				CCDField[] eligiblePercentages=formData.GetFieldsById("G14");
				CCDField[] dentaidePayAmounts=formData.GetFieldsById("G15");
				CCDField[] explainationNoteNumbers1=formData.GetFieldsById("G16");
				CCDField[] explainationNoteNumbers2=formData.GetFieldsById("G17");
				CCDField[] eligibleLabAmounts1=formData.GetFieldsById("G43");
				CCDField[] deductibleLabAmounts1=formData.GetFieldsById("G56");
				CCDField[] eligibleLabPercentage1=formData.GetFieldsById("G57");
				CCDField[] benefitLabAmount1=formData.GetFieldsById("G58");
				CCDField[] eligibleLabAmounts2=formData.GetFieldsById("G02");
				CCDField[] deductibleLabAmounts2=formData.GetFieldsById("G59");
				CCDField[] eligibleLabPercentage2=formData.GetFieldsById("G60");
				CCDField[] benefitLabAmount2=formData.GetFieldsById("G61");
				float noteCol=x;
				float noteColWidth=(isEOB?55:0);
				float procedureCol=noteCol+noteColWidth;
				float procedureColWidth=50;
				float toothCol=procedureCol+procedureColWidth;
				float toothColWidth=45;
				float surfaceCol=toothCol+toothColWidth;
				float surfaceColWidth=55;
				float feeCol=surfaceCol+surfaceColWidth;
				float feeColWidth=75;
				float eligibleFeeCol=feeCol+feeColWidth;
				float eligibleFeeColWidth=(isEOB?75:0);
				float labCol=eligibleFeeCol+eligibleFeeColWidth;
				float labColWidth=80;
				float eligibleLabCol=labCol+labColWidth;
				float eligibleLabColWidth=(isEOB?75:0);
				float deductibleCol=eligibleLabCol+eligibleLabColWidth;
				float deductibleColWidth=(isEOB?75:0);
				float percentCoveredCol=deductibleCol+deductibleColWidth;
				float percentCoveredColWidth=(isEOB?90:0);
				float dentaidePaysCol=percentCoveredCol+percentCoveredColWidth;
				float dentaidePaysColWidth=(isEOB?65:0);
				float endNoteCol=dentaidePaysCol+dentaidePaysColWidth;
				Font tempFont=doc.standardFont;
				doc.standardFont=standardSmall;
				double totalFee=0;
				double totalLab=0;
				double totalPaid=0;
				int numProcsPrinted=0;
				if(isEOB) {
					doc.DrawString(g,"Note",noteCol,0);
					doc.DrawString(g,isFrench?"Admissible":"Eligible",eligibleFeeCol,0);
					doc.DrawString(g,isFrench?"Admissible":"Eligible",eligibleLabCol,0);
					doc.DrawString(g,isFrench?"Franchise":"Deductible",deductibleCol,0);
					doc.DrawString(g,isFrench?"%Couvertrem.":"Percent\nCovered",percentCoveredCol,0);
					doc.DrawString(g,isFrench?"Dentaide":"Detaide\nPays",dentaidePaysCol,0);
				}
				doc.DrawString(g,isFrench?"Acte":"Proc",procedureCol,0);
				doc.DrawString(g,isFrench?"Dent":"Tooth",toothCol,0);
				doc.DrawString(g,"Surface",surfaceCol,0);
				doc.DrawString(g,isFrench?"Honoraires":"Fee",feeCol,0);
				doc.DrawString(g,isFrench?"Labo.":"Lab",labCol,0);
				for(int p=1;p<=claimprocs.Count;p++) {
					x=doc.StartElement();
					int procLineNum=numProcsPrinted+p;
					if(procLineNum>claimprocs.Count) {
						continue;
					}
					ClaimProc claimproc=null;
					for(int i=0;i<claimprocs.Count;i++) {
						if(claimprocs[i].LineNumber==procLineNum) {
							claimproc=claimprocs[i];
							break;
						}
					}
					Procedure proc=Procedures.GetOneProc(claimproc.ProcNum,true);
					text=claimproc.CodeSent.PadLeft(6,' ');//Field F08
					doc.DrawString(g,text,procedureCol,0);
					text=Tooth.ToInternat(proc.ToothNum);//Field F10
					doc.DrawString(g,text,toothCol,0);
					text=Tooth.SurfTidyForClaims(proc.Surf,proc.ToothNum);//Field F11
					doc.DrawString(g,text,surfaceCol,0);
					text=proc.ProcFee.ToString("F");//Field F12
					doc.DrawString(g,text,feeCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
					totalFee+=proc.ProcFee;
					if(isEOB) {
						List <Procedure> labProcs=Procedures.GetCanadianLabFees(proc.ProcNum,procListAll);
						for(int j=0;j<procedureLineNumbers.Length;j++) {
							if(Convert.ToInt32(procedureLineNumbers[j].valuestr)==procLineNum) {
								//Display the procedure information on its own line.
								//For any procLineNum>0, there will only be one matching carrier procedure, by definition.
								size1=new SizeF(0,0);
								int noteIndex=Convert.ToInt32(explainationNoteNumbers1[j].valuestr);
								if(noteIndex>0) {
									size1=doc.DrawString(g,noteNumbers[noteIndex].valuestr,noteCol,0);
								}
								noteIndex=Convert.ToInt32(explainationNoteNumbers2[j].valuestr);
								if(noteIndex>0) {
									doc.DrawString(g,Environment.NewLine+noteNumbers[noteIndex].valuestr,noteCol+size1.Width,0);
								}
								text=RawMoneyStrToDisplayMoney(eligibleAmounts[j].valuestr);
								doc.DrawString(g,text,eligibleFeeCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
								text=RawMoneyStrToDisplayMoney(deductibleAmounts[j].valuestr);
								doc.DrawString(g,text,deductibleCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
								text=RawPercentToDisplayPercent(eligiblePercentages[j].valuestr);
								doc.DrawString(g,text,percentCoveredCol,0);
								text=RawMoneyStrToDisplayMoney(dentaidePayAmounts[j].valuestr);
								doc.DrawString(g,text,dentaidePaysCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
								totalPaid+=Convert.ToDouble(text);
								if(labProcs.Count>0) {
									//Display the Lab1 information on its own line.
									x=doc.StartElement();
									text=ProcedureCodes.GetProcCodeFromDb(labProcs[0].CodeNum).ProcCode.PadLeft(6,' ');
									doc.DrawString(g,text,procedureCol,0);
									text=labProcs[0].ProcFee.ToString("F");
									totalLab+=labProcs[0].ProcFee;
									doc.DrawString(g,text,labCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawMoneyStrToDisplayMoney(eligibleLabAmounts1[j].valuestr);
									doc.DrawString(g,text,eligibleLabCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawMoneyStrToDisplayMoney(deductibleLabAmounts1[j].valuestr);
									doc.DrawString(g,text,deductibleCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawPercentToDisplayPercent(eligibleLabPercentage1[j].valuestr);
									doc.DrawString(g,text,percentCoveredCol,0);
									text=RawMoneyStrToDisplayMoney(benefitLabAmount1[j].valuestr);
									doc.DrawString(g,text,dentaidePaysCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
								}
								if(labProcs.Count>1) {
									//Display the Lab2 information on its own line.
									x=doc.StartElement();
									text=ProcedureCodes.GetProcCodeFromDb(labProcs[1].CodeNum).ProcCode.PadLeft(6,' ');
									doc.DrawString(g,text,procedureCol,0);
									text=labProcs[1].ProcFee.ToString("F");
									totalLab+=labProcs[1].ProcFee;
									doc.DrawString(g,text,labCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawMoneyStrToDisplayMoney(eligibleLabAmounts2[j].valuestr);
									doc.DrawString(g,text,eligibleLabCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawMoneyStrToDisplayMoney(deductibleLabAmounts2[j].valuestr);
									doc.DrawString(g,text,deductibleCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
									text=RawPercentToDisplayPercent(eligibleLabPercentage2[j].valuestr);
									doc.DrawString(g,text,percentCoveredCol,0);
									text=RawMoneyStrToDisplayMoney(benefitLabAmount2[j].valuestr);
									doc.DrawString(g,text,dentaidePaysCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
								}
							}
						}
					}
				}
				if(isEOB) {
					//List the carrier inserted procedures into the procedure list.
					CCDField[] carrierProcs=formData.GetFieldsById("G19");
					CCDField[] carrierEligibleAmts=formData.GetFieldsById("G20");
					CCDField[] carrierEligibleLabAmts=formData.GetFieldsById("G44");
					CCDField[] carrierDeductAmts=formData.GetFieldsById("G21");
					CCDField[] carrierAts=formData.GetFieldsById("G22");
					CCDField[] carrierBenefitAmts=formData.GetFieldsById("G23");
					CCDField[] carrierNotes1=formData.GetFieldsById("G24");
					CCDField[] carrierNotes2=formData.GetFieldsById("G25");
					for(int p=0;p<carrierProcs.Length;p++) {
						//Display the eligible proc info.
						x=doc.StartElement();
						text=carrierProcs[p].valuestr.PadLeft(6,' ');//Field G19
						doc.DrawString(g,text,procedureCol,0);
						text=ProcedureCodes.GetProcCode(ProcedureCodes.GetCodeNum(text)).Descript;
						doc.DrawString(g,text,procedureCol+procedureColWidth,0,doc.standardFont,(int)(toothCol-procedureCol-procedureColWidth-10));
						text=RawMoneyStrToDisplayMoney(carrierEligibleAmts[p].valuestr);//Field G20
						doc.DrawString(g,text,eligibleFeeCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
						text=RawMoneyStrToDisplayMoney(carrierDeductAmts[p].valuestr);//Field G21
						doc.DrawString(g,text,deductibleCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
						text=RawPercentToDisplayPercent(carrierAts[p].valuestr);//Field G22
						doc.DrawString(g,text,percentCoveredCol,0);
						text=RawMoneyStrToDisplayMoney(carrierBenefitAmts[p].valuestr);//Field G23
						doc.DrawString(g,text,dentaidePaysCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
						text="";
						if(carrierNotes1[p].valuestr!="00") {
							text+=carrierNotes1[p].valuestr;
						}
						if(carrierNotes2[p].valuestr!="00") {
							if(text.Length>0) {
								text+=",";
							}
							text+=carrierNotes2[p].valuestr;
						}
						doc.DrawString(g,text,endNoteCol,0);
						//Display the eligible lab info for the proc but on a separate line.
						x=doc.StartElement();
						text="LAB(S)";
						doc.DrawString(g,text,procedureCol,0);
						text=RawMoneyStrToDisplayMoney(carrierEligibleLabAmts[p].valuestr);
						doc.DrawString(g,text,eligibleLabCol+amountWidth-g.MeasureString(text,doc.standardFont).Width,0);
					}
				}
				doc.standardFont=new Font(doc.standardFont.FontFamily,doc.standardFont.Size+1,FontStyle.Bold);
				x=doc.StartElement(verticalLine);
				doc.DrawString(g,"TOTAL:",toothCol,0);
				if(isEOB) {
					CCDField totalPayable=formData.GetFieldById("G55");
					if(totalPayable!=null) {
						totalPaid=PIn.Double(RawMoneyStrToDisplayMoney(totalPayable.valuestr));
					}
					doc.DrawString(g,totalPaid.ToString("F"),dentaidePaysCol,0);
				}
				doc.DrawString(g,totalFee.ToString("F"),feeCol,0);
				doc.DrawString(g,totalLab.ToString("F"),labCol,0);
				doc.standardFont=tempFont;
			}
			if(isEOB){
				doc.StartElement();
				doc.HorizontalLine(g,breakLinePen,doc.bounds.Left,doc.bounds.Right,0);
				PrintNoteList(g);
			}
		}
Пример #20
0
		///<summary>Called directly instead of from Eclaims.SendBatches.  Includes one claim.  Sets claim status internally if successfully sent.  Returns the EtransNum of the ack.  Includes various user interaction such as displaying of messages, printing, triggering of COB claims, etc.  For a normal claim, primaryEOB will be blank.  But if this is a COB(type7), then we need to embed the primary EOB by passing it in. The queueItem.ClearinghouseNum must refer to a valid Canadian clearinghouse.</summary>
		public static long SendClaim(ClaimSendQueueItem queueItem,bool doPrint){
			Clearinghouse clearhouse=Clearinghouses.GetClearinghouse(queueItem.ClearinghouseNum);
//Warning: this path is not handled properly if trailing slash is missing:
			string saveFolder=clearhouse.ExportPath;
			if(!Directory.Exists(saveFolder)) {
				throw new ApplicationException(saveFolder+" not found.");
			}
			Etrans etrans;
			Claim claim;
			Clinic clinic;
			Provider billProv;
			Provider treatProv;
			InsPlan insPlan;
			InsSub insSub;
			Carrier carrier;
			InsPlan insPlan2=null;
			InsSub insSub2=null;
			Carrier carrier2=null;
			List <PatPlan> patPlansForPatient;
			Patient patient;
			Patient subscriber;
			List<ClaimProc> claimProcList;//all claimProcs for a patient.
			List<ClaimProc> claimProcsClaim;
			List<Procedure> procListAll;
			List<Procedure> extracted;
			List<Procedure> procListLabForOne;//Lab fees for one procedure
			Patient subscriber2=null;
			Procedure proc;
			ProcedureCode procCode;
			StringBuilder strb;
			string primaryEOBResponse="";
			string primaryClaimRequestMessage="";
			claim=Claims.GetClaim(queueItem.ClaimNum);
			claimProcList=ClaimProcs.Refresh(claim.PatNum);
			claimProcsClaim=ClaimProcs.GetForSendClaim(claimProcList,claim.ClaimNum);
			long planNum=claim.PlanNum;
			long insSubNum=claim.InsSubNum;
			Relat patRelat=claim.PatRelat;
			long planNum2=claim.PlanNum2;
			long insSubNum2=claim.InsSubNum2;
			Relat patRelat2=claim.PatRelat2;
			if(claim.ClaimType=="PreAuth") {
				etrans=Etranss.SetClaimSentOrPrinted(queueItem.ClaimNum,queueItem.PatNum,clearhouse.ClearinghouseNum,EtransType.Predeterm_CA,0);
			}
			else if(claim.ClaimType=="S") {//Secondary
				//We first need to verify that the claimprocs on the secondary/cob claim are the same as the claimprocs on the primary claim.
				etrans=Etranss.SetClaimSentOrPrinted(queueItem.ClaimNum,queueItem.PatNum,clearhouse.ClearinghouseNum,EtransType.ClaimCOB_CA,0);
				long claimNumPrimary=0;
				for(int i=0;i<claimProcsClaim.Count;i++) {
					List<ClaimProc> claimProcsForProc=ClaimProcs.GetForProc(claimProcList,claimProcsClaim[i].ProcNum);
					bool matchingPrimaryProc=false;
					for(int j=0;j<claimProcsForProc.Count;j++) {
						if(claimProcsForProc[j].ClaimNum!=0 && claimProcsForProc[j].ClaimNum!=claim.ClaimNum && (claimNumPrimary==0 || claimNumPrimary==claimProcsForProc[j].ClaimNum)) {
							claimNumPrimary=claimProcsForProc[j].ClaimNum;
							matchingPrimaryProc=true;
							break;
						}
					}
					if(!matchingPrimaryProc) {
						throw new ApplicationException(Lan.g("Canadian","The procedures attached to this COB claim must be the same as the procedures attached to the primary claim."));
					}
				}
				if(ClaimProcs.GetForSendClaim(claimProcList,claimNumPrimary).Count!=claimProcsClaim.Count) {
					throw new ApplicationException(Lan.g("Canadian","The procedures attached to this COB claim must be the same as the procedures attached to the primary claim."));
				}
				//Now ensure that the primary claim recieved an EOB response, or else we cannot send a COB.
				List <Etrans> etransPrimary=Etranss.GetHistoryOneClaim(claimNumPrimary);
				for(int i=0;i<etransPrimary.Count;i++) {
					primaryClaimRequestMessage=EtransMessageTexts.GetMessageText(etransPrimary[i].EtransMessageTextNum);
					Etrans etransPrimaryAck=Etranss.GetEtrans(etransPrimary[i].AckEtransNum);
					if(etransPrimaryAck.AckCode.ToUpper()=="R") {
						continue;
					}
					if(etransPrimaryAck!=null) {
						primaryEOBResponse=EtransMessageTexts.GetMessageText(etransPrimaryAck.EtransMessageTextNum);
					}
					break;
				}
				if(primaryEOBResponse=="") {
					throw new ApplicationException(Lan.g("Canadian","Cannot send secondary claim electronically until primary EOB has been received electronically."));
				}
				else if(primaryEOBResponse.Length<22) {
					throw new ApplicationException(Lan.g("Canadian","Cannot send secondary claim electronically, because primary claim electronic response is malformed. Try sending the primary claim again."));
				}
				else {//primaryEOBResponse.Length>=22
					string messageVersion=primaryEOBResponse.Substring(18,2);//Field A03 always exists on all messages and is always in the same location.
					string messageType=primaryEOBResponse.Substring(20,2);//Field A04 always exists on all messages and is always in the same location.
					if(messageVersion!="04") {
						throw new ApplicationException(Lan.g("Canadian","Cannot send secondary claim electronically, because primary claim electronic response is in an older format. The secondary claim must be printed instead."));
					}
					if(messageType!="21") {//message type 21 is EOB
						throw new ApplicationException(Lan.g("Canadian","Cannot send secondary claim electronically until primary EOB has been received electronically. The existing primary claim electronic response is not an EOB."));
					}
				}
				Claim claimPrimary=Claims.GetClaim(claimNumPrimary);
				planNum=claimPrimary.PlanNum;
				insSubNum=claimPrimary.InsSubNum;
				patRelat=claimPrimary.PatRelat;
				planNum2=claimPrimary.PlanNum2;
				insSubNum2=claimPrimary.InsSubNum2;
				patRelat2=claimPrimary.PatRelat2;
			}
			else { //primary claim
				etrans=Etranss.SetClaimSentOrPrinted(queueItem.ClaimNum,queueItem.PatNum,clearhouse.ClearinghouseNum,EtransType.Claim_CA,0);
			}
			claim=Claims.GetClaim(claim.ClaimNum);//Refresh the claim since the status might have changed above.
			clinic=Clinics.GetClinic(claim.ClinicNum);
			billProv=ProviderC.ListLong[Providers.GetIndexLong(claim.ProvBill)];
			treatProv=ProviderC.ListLong[Providers.GetIndexLong(claim.ProvTreat)];
			insPlan=InsPlans.GetPlan(planNum,new List <InsPlan> ());
			insSub=InsSubs.GetSub(insSubNum,new List<InsSub>());
			if(planNum2>0) {
				insPlan2=InsPlans.GetPlan(planNum2,new List<InsPlan>());
				insSub2=InsSubs.GetSub(insSubNum2,new List<InsSub>());
				carrier2=Carriers.GetCarrier(insPlan2.CarrierNum);
				subscriber2=Patients.GetPat(insSub2.Subscriber);
			}
			if(claim.ClaimType=="S") {
				carrier=Carriers.GetCarrier(insPlan2.CarrierNum);
			}
			else {
				carrier=Carriers.GetCarrier(insPlan.CarrierNum);
			}
			CanadianNetwork network=CanadianNetworks.GetNetwork(carrier.CanadianNetworkNum);
			patPlansForPatient=PatPlans.Refresh(claim.PatNum);
			patient=Patients.GetPat(claim.PatNum);
			subscriber=Patients.GetPat(insSub.Subscriber);
			procListAll=Procedures.Refresh(claim.PatNum);
			extracted=Procedures.GetCanadianExtractedTeeth(procListAll);
			strb=new StringBuilder();
			//A01 transaction prefix 12 AN
			strb.Append(TidyAN(network.CanadianTransactionPrefix,12));
			//A02 office sequence number 6 N
			strb.Append(TidyN(etrans.OfficeSequenceNumber,6));
			//A03 format version number 2 N
			if(carrier.CDAnetVersion=="") {
				strb.Append("04");
			}
			else {
				strb.Append(carrier.CDAnetVersion);
			}
			//A04 transaction code 2 N
			if(claim.ClaimType=="PreAuth") {
				strb.Append("03");//Predetermination
			}
			else {
				if(claim.ClaimType=="S") {
					strb.Append("07");//cob
				}
				else {
					strb.Append("01");//claim
				}
			}
			//A05 carrier id number 6 N
			strb.Append(carrier.ElectID);//already validated as 6 digit number.
			//A06 software system id 3 AN
			strb.Append(SoftwareSystemId());
			if(carrier.CDAnetVersion!="02") { //version 04
				//A10 encryption method 1 N
				strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
			}
			//A07 message length. 5 N in version 04, 4 N in version 02
			//We simply create a place holder here. We come back at the end of message construction and record the actual final message length.
			if(carrier.CDAnetVersion=="02") {
				strb.Append("0000");
			}
			else { //version 04
				strb.Append("00000");
			}
			if(carrier.CDAnetVersion=="02") {
				//A08 email flag 1 N
				if(claim.CanadianMaterialsForwarded=="") {
					strb.Append("0"); //no additional information
				}
				else if(claim.CanadianMaterialsForwarded.Contains("E")) {
					strb.Append("1"); //E-Mail to follow.
				}
				else {
					strb.Append("2"); //Letter to follow
				}
			}
			else { //version 04
				//A08 materials forwarded 1 AN
				strb.Append(GetMaterialsForwarded(claim.CanadianMaterialsForwarded));
			}
			if(carrier.CDAnetVersion!="02") { //version 04
				//A09 carrier transaction counter 5 N
#if DEBUG
				strb.Append("00001");
#else				
				strb.Append(TidyN(etrans.CarrierTransCounter,5));
#endif
			}
			//B01 CDA provider number 9 AN
			strb.Append(TidyAN(treatProv.NationalProvID,9));//already validated
			//B02 (treating) provider office number 4 AN
			strb.Append(TidyAN(treatProv.CanadianOfficeNum,4));//already validated	
			if(carrier.CDAnetVersion!="02") { //version 04
				//B03 billing provider number 9 AN
				//might need to account for possible 5 digit prov id assigned by carrier
				strb.Append(TidyAN(billProv.NationalProvID,9));//already validated
				//B04 billing provider office number 4 AN
				strb.Append(TidyAN(billProv.CanadianOfficeNum,4));//already validated	
				//B05 referring provider 10 AN
				strb.Append(TidyAN(claim.CanadianReferralProviderNum,10));
				//B06 referral reason 2 N
				strb.Append(TidyN(claim.CanadianReferralReason,2));
			}
			if(carrier.CDAnetVersion=="02") {
				//C01 primary policy/plan number 8 AN
				//only validated to ensure that it's not blank and is less than 8. Also that no spaces.
				strb.Append(TidyAN(insPlan.GroupNum,8));
			}
			else { //version 04
				//C01 primary policy/plan number 12 AN
				//only validated to ensure that it's not blank and is less than 12. Also that no spaces.
				strb.Append(TidyAN(insPlan.GroupNum,12));
			}
			//C11 primary division/section number 10 AN
			strb.Append(TidyAN(insPlan.DivisionNo,10));
			if(carrier.CDAnetVersion=="02") {
				//C02 subscriber id number 11 AN
				strb.Append(TidyAN(insSub.SubscriberID.Replace("-",""),11));//validated
			}
			else { //version 04
				//C02 subscriber id number 12 AN
				strb.Append(TidyAN(insSub.SubscriberID.Replace("-",""),12));//validated
			}
			if(carrier.CDAnetVersion!="02") { //version 04
				//C17 primary dependant code 2 N
				string patID="";
				for(int p=0;p<patPlansForPatient.Count;p++) {
					if(patPlansForPatient[p].InsSubNum==insSubNum) {
						patID=patPlansForPatient[p].PatID;
					}
				}
				strb.Append(TidyN(patID,2));
			}
			//C03 relationship code 1 N
			//User interface does not only show Canadian options, but all options are handled.
			strb.Append(GetRelationshipCode(patRelat));
			//C04 patient's sex 1 A
			//validated to not include "unknown"
			if(patient.Gender==PatientGender.Male){
				strb.Append("M");
			}
			else{
				strb.Append("F");
			}
			//C05 patient birthday 8 N
			strb.Append(patient.Birthdate.ToString("yyyyMMdd"));//validated
			if(carrier.CDAnetVersion=="02") {
				//C06 patient last name 25 A
				strb.Append(TidyA(patient.LName,25));//validated
			}
			else { //version 04
				//C06 patient last name 25 AE
				strb.Append(TidyAE(patient.LName,25,true));//validated
			}
			if(carrier.CDAnetVersion=="02") {
				//C07 patient first name 15 A
				strb.Append(TidyA(patient.FName,15));//validated
			}
			else { //version 04
				//C07 patient first name 15 AE
				strb.Append(TidyAE(patient.FName,15,true));//validated
			}
			if(carrier.CDAnetVersion=="02") {
				//C08 patient middle initial 1 A
				strb.Append(TidyA(patient.MiddleI,1));
			}
			else { //version 04
				//C08 patient middle initial 1 AE
				strb.Append(TidyAE(patient.MiddleI,1));
			}
			if(carrier.CDAnetVersion=="02") {
				//C09 eligibility exception code 1 N
				string exceptionCode=TidyN(patient.CanadianEligibilityCode,1);//Validated.
				if(exceptionCode=="4") {
					exceptionCode="0";//Code 4 in version 04 means "code not applicable", but in version 02, value 0 means "code not applicable".
				}
				strb.Append(exceptionCode);//validated
			}
			else { //version 04
				//C09 eligibility exception code 1 N
				strb.Append(TidyN((patient.CanadianEligibilityCode==0)?4:patient.CanadianEligibilityCode,1));//Validated. Use "code not applicable" when no value has been specified by the user.
			}
			if(carrier.CDAnetVersion=="02") {
				//C10 name of school 25 A
				//validated if patient 18yrs or older and full-time student (or disabled student)
				strb.Append(TidyA(patient.SchoolName,25));
			}
			else { //version 04
				//C10 name of school 25 AEN
				//validated if patient 18yrs or older and full-time student (or disabled student)
				strb.Append(TidyAEN(patient.SchoolName,25));
			}
			bool C19PlanRecordPresent=(insPlan.CanadianPlanFlag=="A" || insPlan.CanadianPlanFlag=="N" || insPlan.CanadianPlanFlag=="V");
			if(carrier.CDAnetVersion!="02") { //version 04
				//C12 plan flag 1 A
				strb.Append(Canadian.GetPlanFlag(insPlan.CanadianPlanFlag));
				//C18 plan record count 1 N
				if(C19PlanRecordPresent) {
					strb.Append("1");
				}
				else {
					strb.Append("0");
				}
			}
			CCDFieldInputter primaryClaimData=null;
			if(claim.ClaimType=="S") {
				primaryClaimData=new CCDFieldInputter(primaryClaimRequestMessage);
			}
			//D01 subscriber birthday 8 N
			strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D01").valuestr:subscriber.Birthdate.ToString("yyyyMMdd"));//validated
			if(carrier.CDAnetVersion=="02") {
				//D02 subscriber last name 25 A
				strb.Append(TidyA(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D02").valuestr:subscriber.LName,25));//validated
			}
			else { //version 04
				//D02 subscriber last name 25 AE
				strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D02").valuestr:subscriber.LName,25,true));//validated
			}
			if(carrier.CDAnetVersion=="02") {
				//D03 subscriber first name 15 A
				strb.Append(TidyA(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D03").valuestr:subscriber.FName,15));//validated
			}
			else { //version 04
				//D03 subscriber first name 15 AE
				strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D03").valuestr:subscriber.FName,15,true));//validated
			}
			if(carrier.CDAnetVersion=="02") {
				//D04 subscriber middle initial 1 A
				strb.Append(TidyA(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D04").valuestr:subscriber.MiddleI,1));
			}
			else { //version 04
				//D04 subscriber middle initial 1 AE
				strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D04").valuestr:subscriber.MiddleI,1));
			}
			if(carrier.CDAnetVersion=="02") {
				//D05 subscriber address line one 30 AN
				strb.Append(TidyAN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D05").valuestr:subscriber.Address,30,true));//validated
			}
			else { //version 04
				//D05 subscriber address line one 30 AEN
				strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D05").valuestr:subscriber.Address,30,true));//validated
			}
			if(carrier.CDAnetVersion=="02") {
				//D06 subscriber address line two 30 AN
				strb.Append(TidyAN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D06").valuestr:subscriber.Address2,30,true));
			}
			else { //version 04
				//D06 subscriber address line two 30 AEN
				strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D06").valuestr:subscriber.Address2,30,true));
			}
			if(carrier.CDAnetVersion=="02") {
				//D07 subscriber city 20 A
				strb.Append(TidyA(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D07").valuestr:subscriber.City,20));//validated
			}
			else { //version 04
				//D07 subscriber city 20 AEN
				strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D07").valuestr:subscriber.City,20,true));//validated
			}
			//D08 subscriber province/state 2 A
			strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D08").valuestr:subscriber.State);//very throroughly validated previously
			if(carrier.CDAnetVersion=="02") {
				//D09 subscriber postal/zip code 6 AN
				strb.Append(TidyAN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D09").valuestr:subscriber.Zip.Replace("-","").Replace(" ",""),6));//validated.
			}
			else { //version 04
				//D09 subscriber postal/zip code 9 AN
				strb.Append(TidyAN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D09").valuestr:subscriber.Zip.Replace("-","").Replace(" ",""),9));//validated.
			}
			//D10 language of insured 1 A
			strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("D10").valuestr:(subscriber.Language=="fr"?"F":"E"));
			bool orthoRecordFlag=false;
			if(carrier.CDAnetVersion!="02") { //version 04
				//D11 card sequence/version number 2 N
				//Not validated against type of carrier yet. Might need to check if Dentaide.
				strb.Append(TidyN(insPlan.DentaideCardSequence,2));
				//E18 secondary coverage flag 1 A
				if(planNum2>0) {
					strb.Append("Y");
				}
				else {
					strb.Append("N");
				}
				//E20 secondary record count 1 N
				if(planNum2==0) {
					strb.Append("0");
				}
				else {
					strb.Append("1");
				}
				//F06 number of procedures performed 1 N. Must be between 1 and 7.  UI prevents attaching more than 7.
				strb.Append(TidyN(claimProcsClaim.Count,1));//number validated
				//F22 extracted teeth count 2 N
				strb.Append(TidyN(extracted.Count,2));//validated against matching prosthesis
				if(claim.ClaimType=="PreAuth") {
					orthoRecordFlag=(claim.CanadaEstTreatStartDate.Year>1880 || claim.CanadaInitialPayment!=0 || claim.CanadaPaymentMode!=0 ||
						claim.CanadaTreatDuration!=0 || claim.CanadaNumAnticipatedPayments!=0 || claim.CanadaAnticipatedPayAmount!=0);
					//F25 Orthodontic Record Flag 1 N
					if(orthoRecordFlag) {
						strb.Append("1");
					}
					else {
						strb.Append("0");
					}
				}
				if(claim.ClaimType=="S") { //cob
					//G39 Embedded Transaction Length N 4
					strb.Append(Canadian.TidyN(primaryEOBResponse.Length,4));
				}
			}
			//Secondary carrier fields (E19 to E07) ONLY included if E20=1----------------------------------------------------
			if(planNum2!=0) {
				if(carrier.CDAnetVersion!="02") { //version 04
					//E19 secondary carrier transaction number 6 N
					strb.Append(TidyN(etrans.CarrierTransCounter2,6));
				}
				//E01 sec carrier id number 6 N
				strb.Append(carrier2.ElectID);//already validated as 6 digit number.
				if(carrier.CDAnetVersion=="02") {
					//E02 sec carrier policy/plan num 8 AN
					//only validated to ensure that it's not blank and is less than 8. Also that no spaces.
					//We might later allow 999999 if sec carrier is unlisted or unknown.
					strb.Append(TidyAN(insPlan2.GroupNum,8));
				}
				else { //version 04
					//E02 sec carrier policy/plan num 12 AN
					//only validated to ensure that it's not blank and is less than 12. Also that no spaces.
					//We might later allow 999999 if sec carrier is unlisted or unknown.
					strb.Append(TidyAN(insPlan2.GroupNum,12));
				}
				//E05 sec division/section num 10 AN
				strb.Append(TidyAN(insPlan2.DivisionNo,10));
				if(carrier.CDAnetVersion=="02") {
					//E03 sec plan subscriber id 11 AN
					strb.Append(TidyAN(insSub2.SubscriberID.Replace("-",""),11));//validated
				}
				else { //version 04
					//E03 sec plan subscriber id 12 AN
					strb.Append(TidyAN(insSub2.SubscriberID.Replace("-",""),12));//validated
				}
				if(carrier.CDAnetVersion!="02") { //version 04
					//E17 sec dependent code 2 N
					string patID="";
					for(int p=0;p<patPlansForPatient.Count;p++) {
						if(patPlansForPatient[p].InsSubNum==insSubNum2) {
							patID=patPlansForPatient[p].PatID;
						}
					}
					strb.Append(TidyN(patID,2));
					//E06 sec relationship code 1 N
					//User interface does not only show Canadian options, but all options are handled.
					strb.Append(GetRelationshipCode(patRelat2));
				}
				//E04 sec subscriber birthday 8 N
				strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E04").valuestr:subscriber2.Birthdate.ToString("yyyyMMdd"));//validated
				if(carrier.CDAnetVersion!="02") { //version 04
					//E08 sec subscriber last name 25 AE
					strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E08").valuestr:subscriber2.LName,25,true));//validated
					//E09 sec subscriber first name 15 AE
					strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E09").valuestr:subscriber2.FName,15,true));//validated
					//E10 sec subscriber middle initial 1 AE
					strb.Append(TidyAE(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E10").valuestr:subscriber2.MiddleI,1));
					//E11 sec subscriber address one 30 AEN
					strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E11").valuestr:subscriber2.Address,30,true));//validated
					//E12 sec subscriber address two 30 AEN
					strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E12").valuestr:subscriber2.Address2,30,true));
					//E13 sec subscriber city 20 AEN
					strb.Append(TidyAEN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E13").valuestr:subscriber2.City,20,true));//validated
					//E14 sec subscriber province/state 2 A
					strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E14").valuestr:subscriber2.State);//very throroughly validated previously
					//E15 sec subscriber postal/zip code 9 AN
					strb.Append(TidyAN(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E15").valuestr:subscriber2.Zip.Replace("-","").Replace(" ",""),9));//validated
					//E16 sec language 1 A
					strb.Append(claim.ClaimType=="S"?primaryClaimData.GetFieldById("E16").valuestr:(subscriber2.Language=="fr"?"F":"E"));
					//E07 sec card sequence/version num 2 N
					//todo Not validated yet.
					strb.Append(TidyN(claim.ClaimType=="S"?PIn.Int(primaryClaimData.GetFieldById("E07").valuestr):insPlan2.DentaideCardSequence,2));
				}
				//End of secondary subscriber fields---------------------------------------------------------------------------
			}
			else { //There is no secondary plan.
				if(carrier.CDAnetVersion=="02") { 
					//Secondary subscriber fields are always available in version 2. Since there is no plan, put blank data as a filler.
					//E01 N 6
					strb.Append("000000");
					//E02 AN 8
					strb.Append("        ");
					//E05 AN 10
					strb.Append("          ");
					//E03 AN 11
					strb.Append("           ");
					//E04 N 8
					strb.Append("00000000");
				}
			}
			if(claim.ClaimType!="PreAuth") {
				//F01 payee code 1 N
				if((claim.ClaimType!="S" && insSub.AssignBen) || (claim.ClaimType=="S" && insSub2.AssignBen)) {
					if(carrier.CDAnetVersion=="02") {
						strb.Append("0");//pay dentist
					}
					else { //version 04
						strb.Append("4");//pay dentist
					}
				}
				else {
					strb.Append("1");//pay subscriber
				}
			}
			//F02 accident date 8 N
			if(claim.AccidentDate.Year>1900){//if accident related
				strb.Append(claim.AccidentDate.ToString("yyyyMMdd"));//validated
			}
			else{
				strb.Append(TidyN(0,8));
			}
			if(claim.ClaimType!="PreAuth") {
				//F03 predetermination number 14 AN
				strb.Append(TidyAN(claim.PreAuthString,14));
			}
			if(carrier.CDAnetVersion=="02") {
				//F15 Is this an Initial Replacement? A 1
				string initialPlacement="Y";
				DateTime initialPlacementDate=DateTime.MinValue;
				if(claim.CanadianIsInitialUpper=="Y"){
					initialPlacement="Y";
					initialPlacementDate=claim.CanadianDateInitialUpper;
				}
				else if(claim.CanadianIsInitialLower=="Y"){
					initialPlacement="Y";
					initialPlacementDate=claim.CanadianDateInitialLower;
				}
				else if(claim.CanadianIsInitialUpper=="N") {
					initialPlacement="N";
					initialPlacementDate=claim.CanadianDateInitialUpper;
				}
				else if(claim.CanadianIsInitialLower=="N"){
					initialPlacement="N";
					initialPlacementDate=claim.CanadianDateInitialLower;
				}
				strb.Append(initialPlacement);
				//F04 date of initial placement 8 N
				if(initialPlacementDate.Year>1900) {
					strb.Append(initialPlacementDate.ToString("yyyyMMdd"));
				}
				else {
					strb.Append("00000000");
				}
				//F05 tx req'd for ortho purposes 1 A
				if(claim.IsOrtho) {
					strb.Append("Y");
				}
				else {
					strb.Append("N");
				}
				//F06 number of procedures performed 1 N. Must be between 1 and 7.  UI prevents attaching more than 7.
				strb.Append(TidyN(claimProcsClaim.Count,1));//number validated
			}
			else { //version 04
				//F15 initial placement upper 1 A  Y or N or X
				strb.Append(Canadian.TidyA(claim.CanadianIsInitialUpper,1));//validated
				//F04 date of initial placement upper 8 N
				if(claim.CanadianDateInitialUpper.Year>1900) {
					strb.Append(claim.CanadianDateInitialUpper.ToString("yyyyMMdd"));
				}
				else {
					strb.Append("00000000");
				}
				//F18 initial placement lower 1 A
				strb.Append(Canadian.TidyA(claim.CanadianIsInitialLower,1));//validated
				//F19 date of initial placement lower 8 N
				if(claim.CanadianDateInitialLower.Year>1900) {
					strb.Append(claim.CanadianDateInitialLower.ToString("yyyyMMdd"));
				}
				else {
					strb.Append("00000000");
				}
				//F05 tx req'd for ortho purposes 1 A
				if(claim.IsOrtho) {
					strb.Append("Y");
				}
				else {
					strb.Append("N");
				}
				//F20 max prosth material 1 N
				if(claim.CanadianMaxProsthMaterial==7) {//our fake crown code
					strb.Append("0");
				}
				else {
					strb.Append(claim.CanadianMaxProsthMaterial.ToString());//validated
				}
				//F21 mand prosth material 1 N
				if(claim.CanadianMandProsthMaterial==7) {//our fake crown code
					strb.Append("0");
				}
				else {
					strb.Append(claim.CanadianMandProsthMaterial.ToString());//validated
				}
			}
			if(carrier.CDAnetVersion!="02") { //version 04
				//If F22 is non-zero. Repeat for the number of times specified by F22.----------------------------------------------
				for(int t=0;t<extracted.Count;t++) {
					//F23 extracted tooth num 2 N
					//todo: check validation
					strb.Append(TidyN(Tooth.ToInternat(extracted[t].ToothNum),2));//validated
					//F24 extraction date 8 N
					//todo: check validation
					strb.Append(extracted[t].ProcDate.ToString("yyyyMMdd"));//validated
				}
			}
			if(carrier.CDAnetVersion!="02") { //version 04
				if(claim.ClaimType=="PreAuth") {
#if DEBUG
					//We are required to test multi-page (up to 7 procs per page) predeterminations for certification. We do not actually do this in the real world.
					//We will use the claim.PreAuthString here to pass these useless numbers in for testing purposes, since this field is not used for predetermination claims for any other reason.
					int currentPredeterminationPageNumber=1;
					int lastPredeterminationPageNumber=1;
					if(claim.PreAuthString!="") {
						string[] predetermNums=claim.PreAuthString.Split(new char[] { ',' });
						currentPredeterminationPageNumber=PIn.Int(predetermNums[0]);
						lastPredeterminationPageNumber=PIn.Int(predetermNums[1]);
					}
					//G46 Current Predetermination Page Number N 1
					strb.Append(Canadian.TidyN(currentPredeterminationPageNumber,1));
					//G47 Last Predetermination Page Number N 1
					strb.Append(Canadian.TidyN(lastPredeterminationPageNumber,1));
#else
					//G46 Current Predetermination Page Number N 1
					strb.Append("1");//Always 1 page, because UI prevents attaching more than 7 procedures per claim in Canadian mode.
					//G47 Last Predetermination Page Number N 1
					strb.Append("1");//Always 1 page, because UI prevents attaching more than 7 procedures per claim in Canadian mode.
#endif
					if(orthoRecordFlag) { //F25 is set
						//F37 Estimated Treatment Starting Date N 8
						strb.Append(Canadian.TidyN(claim.CanadaEstTreatStartDate.ToString("yyyyMMdd"),8));
						double firstExamFee=0;
						double diagnosticPhaseFee=0;
#if DEBUG //Fields F26 and F27 are not required in the real world, but there are a few certification tests that require this information in order for the test to pass.
						if(claim.PreAuthString!="") {
							string[] preauthData=claim.PreAuthString.Split(new char[] { ',' });
							if(preauthData.Length>2) {
								firstExamFee=PIn.Double(preauthData[2]);
							}
							if(preauthData.Length>3) {
								diagnosticPhaseFee=PIn.Double(preauthData[3]);
							}
						}
#endif
						//F26 First Examination Fee D 6
						strb.Append(Canadian.TidyD(firstExamFee,6));//optional
						//F27 Diagnostic Phase Fee D 6
						strb.Append(Canadian.TidyD(diagnosticPhaseFee,6));//optional
						//F28 Initial Payment D 6
						strb.Append(Canadian.TidyD(claim.CanadaInitialPayment,6));
						//F29 Payment Mode N 1
						strb.Append(Canadian.TidyN(claim.CanadaPaymentMode,1));//Validated in UI.
						//F30 Treatment Duration N 2
						strb.Append(Canadian.TidyN(claim.CanadaTreatDuration,2));
						//F31 Number of Anticipated Payments N 2
						strb.Append(Canadian.TidyN(claim.CanadaNumAnticipatedPayments,2));
						//F32 Anticipated Payment Amount D 6
						strb.Append(Canadian.TidyD(claim.CanadaAnticipatedPayAmount,6));
					}
				}
			}
			//Procedures: Repeat for number of times specified by F06.----------------------------------------------------------
			for(int p=0;p<claimProcsClaim.Count;p++) {
				//claimProcsClaim already excludes any claimprocs with ProcNum=0, so no payments etc.
				proc=Procedures.GetProcFromList(procListAll,claimProcsClaim[p].ProcNum);
				procCode=ProcedureCodes.GetProcCode(proc.CodeNum);
				procListLabForOne=Procedures.GetCanadianLabFees(proc.ProcNum,procListAll);
				//F07 proc line number 1 N
				strb.Append((p+1).ToString());
				if(carrier.CDAnetVersion=="02") {
					//F08 procedure code 5 N
					strb.Append(TidyN(claimProcsClaim[p].CodeSent,5).Trim().PadLeft(5,'0'));
				}
				else { //version 04
					//F08 procedure code 5 AN
					strb.Append(TidyAN(claimProcsClaim[p].CodeSent,5).Trim().PadLeft(5,'0'));
				}
				if(claim.ClaimType!="PreAuth") {
					//F09 date of service 8 N
					strb.Append(claimProcsClaim[p].ProcDate.ToString("yyyyMMdd"));//validated
				}
				//F10 international tooth, sextant, quad, or arch 2 N
				strb.Append(GetToothQuadOrArch(proc,procCode));
				//F11 tooth surface 5 A
				//the SurfTidy function is very thorough, so it's OK to use TidyAN
				if(procCode.TreatArea==TreatmentArea.Surf) {
#if DEBUG
					//since the scripts use impossible surfaces, we need to just use raw database here
					strb.Append(TidyAN(proc.Surf,5));
#else
					strb.Append(TidyAN(Tooth.SurfTidyForClaims(proc.Surf,proc.ToothNum),5));
#endif
				}
				else {
					strb.Append("     ");
				}
				//F12 dentist's fee claimed 6 D
				strb.Append(TidyD(claimProcsClaim[p].FeeBilled,6));
				if(carrier.CDAnetVersion!="02") { //version 04
					//F34 lab procedure code #1 5 AN
					if(procListLabForOne.Count>0) {
						strb.Append(TidyAN(ProcedureCodes.GetProcCode(procListLabForOne[0].CodeNum).ProcCode,5).Trim().PadLeft(5,'0'));
					}
					else {
						strb.Append("     ");
					}
				}
				//F13 lab procedure fee #1 6 D
				if(procListLabForOne.Count>0){
					strb.Append(TidyD(procListLabForOne[0].ProcFee,6));
				}
				else{
					strb.Append("000000");
				}
				if(carrier.CDAnetVersion=="02") {
					//F14 Unit of Time D 4
					//This is a somewhat deprecated field becacuse it no longer exists in version 04. Some carriers reject claims 
					//if there is a time specified for a procedure that does not require a time. It is safest for now to just set
					//this value to zero always.
					double procHours=0;
					//procHours=(PrefC.GetInt(PrefName.AppointmentTimeIncrement)*procCode.ProcTime.Length)/60.0;
					strb.Append(TidyD(procHours,4));
				}
				else { //version 04
					//F35 lab procedure code #2 5 AN
					if(procListLabForOne.Count>1) {
						strb.Append(TidyAN(ProcedureCodes.GetProcCode(procListLabForOne[1].CodeNum).ProcCode,5).Trim().PadLeft(5,'0'));
					}
					else {
						strb.Append("     ");
					}
					//F36 lab procedure fee #2 6 D
					if(procListLabForOne.Count>1) {
						strb.Append(TidyD(procListLabForOne[1].ProcFee,6));
					}
					else {
						strb.Append("000000");
					}
					//F16 procedure type codes 5 A
					strb.Append(TidyA((proc.CanadianTypeCodes==null || proc.CanadianTypeCodes=="")?"X":proc.CanadianTypeCodes,5));
					//F17 remarks code 2 N
					//optional.  PMP field.  See C12. Zeros when not used.
					strb.Append("00");
				}
			}
			if(carrier.CDAnetVersion!="02") { //version 04
				//C19 plan record 30 AN
				if(C19PlanRecordPresent) {
					if(insPlan.CanadianPlanFlag=="A") {
						//insPlan.CanadianDiagnosticCode and insPlan.CanadianInstitutionCode are validated in the UI.
						strb.Append(Canadian.TidyAN(Canadian.TidyAN(insPlan.CanadianDiagnosticCode,6,true)+Canadian.TidyAN(insPlan.CanadianInstitutionCode,6,true),30,true));
					}
					else { //N or V. These two plan flags are not yet in use. Presumably, for future use.
						strb.Append(Canadian.TidyAN("",30));
					}
				}
			}
			//We are required to append the primary EOB. This is not a data dictionary field.
			if(claim.ClaimType=="S") {
				strb.Append(ConvertEOBVersion(primaryEOBResponse,carrier.CDAnetVersion));
			}
			//Now we go back and fill in the actual message length now that we know the number for sure.
			if(carrier.CDAnetVersion=="02") {
				strb.Replace("0000",Canadian.TidyN(strb.Length,4),31,4);
			}
			else { //version 04
				strb.Replace("00000",Canadian.TidyN(strb.Length,5),32,5);
			}
			//end of creating the message
			//this is where we attempt the actual sending:
			string result="";
			bool resultIsError=false;
			try{
#if DEBUG
				if(claim.ClaimType=="PreAuth") { //Predeterminations
					if(testNumber==3) { //Predetermination test #3
						strb.Replace("Y","N",563,1);//We use claim.IsOrtho for fields F05 and F25, but for some reason in this example the values expected are opposite. We think this is a problem with the CDANet test.
						strb.Replace("00000000","35000025",577,8);//These are optional fields (F26 and F27), so we have not implemented them, but the test does not work without them for some reason.
					}
				}
#endif
				result=PassToIca(strb.ToString(),clearhouse);
			}
			catch(ApplicationException ex) {
				result=ex.Message;
				resultIsError=true;
			}
			//Attach an ack to the etrans
			Etrans etransAck=new Etrans();
			etransAck.PatNum=etrans.PatNum;
			etransAck.PlanNum=etrans.PlanNum;
			etransAck.InsSubNum=etrans.InsSubNum;
			etransAck.CarrierNum=etrans.CarrierNum;
			etransAck.ClaimNum=etrans.ClaimNum;
			etransAck.DateTimeTrans=DateTime.Now;
			CCDFieldInputter fieldInputter=null;
			if(resultIsError){
				etransAck.Etype=EtransType.AckError;
				etrans.Note="failed";
			}
			else{
				fieldInputter=new CCDFieldInputter(result);
				CCDField fieldG05=fieldInputter.GetFieldById("G05");
				if(fieldG05!=null) {
					etransAck.AckCode=fieldG05.valuestr;
					if(etransAck.AckCode=="M") { //Manually print the claim form.
						PrintCdaClaimForm(claim);
					}
				}
				etransAck.Etype=fieldInputter.GetEtransType();
			}
			Etranss.Insert(etransAck);
			Etranss.SetMessage(etransAck.EtransNum,result);
			etrans.AckEtransNum=etransAck.EtransNum;
			Etranss.Update(etrans);
			Etranss.SetMessage(etrans.EtransNum,strb.ToString());
			if(resultIsError) {
				throw new ApplicationException(result);
			}
			if(claim.ClaimType!="PreAuth") {
				Claims.SetCanadianClaimSent(queueItem.ClaimNum);//when called from ClaimEdit, that window will close immediately, so we're directly changing the db.
				CCDField fieldTransRefNum=fieldInputter.GetFieldById("G01");
				if(fieldTransRefNum!=null) {
					if(etransAck.AckCode!="R") {
						claim.CanadaTransRefNum=fieldTransRefNum.valuestr;
						Claims.Update(claim);
					}
				}
			}
			if(doPrint) {
				new FormCCDPrint(etrans,result,true);//Physically print the form.
			}
			if(claim.ClaimType!="PreAuth" && claim.ClaimType!="S" && etransAck.Etype==EtransType.ClaimEOB_CA && planNum2>0) {//if an eob was returned and patient has secondary insurance.
				//if an EOB is returned, there are two possibilities.
				//1. The EOB contains an embedded EOB because the same carrier is both pri and sec.  Both got printed above.
				//2. The EOB does not contain an embedded EOB, indicating that a COB claim needs to be created and sent.
				//That is done here, automatically.
				//UI already prevents the initial automatic creation of the secondary claim for Canada.
				string embeddedLength=fieldInputter.GetValue("G39");
				if(embeddedLength=="" || embeddedLength=="0000") {//no embedded message
					Claim claim2=new Claim();
					claim2.PatNum=claim.PatNum;
					claim2.DateService=claim.DateService;
					claim2.DateSent=DateTime.Today;
					claim2.ClaimStatus="W";
					claim2.PlanNum=planNum2;
					claim2.InsSubNum=insSubNum2;
					claim2.PatRelat=patRelat2;
					claim2.PlanNum2=planNum;
					claim2.InsSubNum2=insSubNum;
					claim2.PatRelat2=patRelat;
					claim2.ClaimType="S";
					claim2.ProvTreat=claim.ProvTreat;
					claim2.IsProsthesis="N";
					claim2.ProvBill=claim.ProvBill;
					claim2.EmployRelated=YN.No;
					claim2.AccidentDate=claim.AccidentDate;
					claim2.IsOrtho=claim.IsOrtho;
					claim2.CanadianDateInitialLower=claim.CanadianDateInitialLower;
					claim2.CanadianDateInitialUpper=claim.CanadianDateInitialUpper;
					claim2.CanadianIsInitialLower=claim.CanadianIsInitialLower;
					claim2.CanadianIsInitialUpper=claim.CanadianIsInitialUpper;
					claim2.CanadianMandProsthMaterial=claim.CanadianMandProsthMaterial;
					claim2.CanadianMaterialsForwarded=claim.CanadianMaterialsForwarded;
					claim2.CanadianMaxProsthMaterial=claim.CanadianMaxProsthMaterial;
					claim2.CanadianReferralProviderNum=claim.CanadianReferralProviderNum;
					claim2.CanadianReferralReason=claim.CanadianReferralReason;
					Claims.Insert(claim2);//to retreive a key for new Claim.ClaimNum
					ClaimProc[] claimProcsClaim2=new ClaimProc[claimProcsClaim.Count];
					long procNum;
					for(int i=0;i<claimProcsClaim.Count;i++) {//loop through the procs from claim 1
						//and try to find an estimate that can be used
						procNum=claimProcsClaim[i].ProcNum;
						claimProcsClaim2[i]=Procedures.GetClaimProcEstimate(procNum,claimProcList,insPlan2,claim2.InsSubNum2);
					}
					for(int i=0;i<claimProcsClaim2.Length;i++) {//loop through each claimProc
						//and create any missing estimates just in case
						if(claimProcsClaim2[i]==null) {
							claimProcsClaim2[i]=new ClaimProc();
							//claimProcsClaim and claimProcsClaim2 already exclude any claimprocs with ProcNum=0, so no payments etc.
							proc=Procedures.GetProcFromList(procListAll,claimProcsClaim[i].ProcNum);
							ClaimProcs.CreateEst(claimProcsClaim2[i],proc,insPlan2,insSub2);
						}
					}
					for(int i=0;i<claimProcsClaim2.Length;i++) {
						//claimProcsClaim and claimProcsClaim2 already exclude any claimprocs with ProcNum=0, so no payments etc.
						proc=Procedures.GetProcFromList(procListAll,claimProcsClaim2[i].ProcNum);//1:1
						claimProcsClaim2[i].ClaimNum=claim2.ClaimNum;
						claimProcsClaim2[i].Status=ClaimProcStatus.NotReceived;
						claimProcsClaim2[i].CodeSent=claimProcsClaim[i].CodeSent;
						claimProcsClaim2[i].LineNumber=(byte)(i+1);
						ClaimProcs.Update(claimProcsClaim2[i]);
					}
					claimProcList=ClaimProcs.Refresh(claim2.PatNum);
					Family fam=Patients.GetFamily(claim2.PatNum);
					List<InsSub> subList=InsSubs.RefreshForFam(fam);
					List<InsPlan> planList=InsPlans.RefreshForSubList(subList);
					List<Benefit> benefitList=Benefits.Refresh(patPlansForPatient,subList);
					ClaimL.CalculateAndUpdate(procListAll,planList,claim2,patPlansForPatient,benefitList,patient.Age,subList);
					ClaimSendQueueItem queueItem2=Claims.GetQueueList(claim2.ClaimNum,claim2.ClinicNum,0)[0];
					string responseMessageVersion=result.Substring(18,2);//Field A03 always exists on all messages and is always in the same location.
					//ok to skip validation
					//We can only send an electronic secondary claim when the EOB received from the primary insurance is a version 04 message and when
					//the secondary carrier accepts secondary claims electronically (COBs). Otherwise, the user must send the claim by paper.
					if(responseMessageVersion!="02" && (carrier2.CanadianSupportedTypes&CanSupTransTypes.CobClaimTransaction_07)==CanSupTransTypes.CobClaimTransaction_07) {
						long etransNum=SendClaim(queueItem2,doPrint);//recursive
						return etransNum;//for now, we'll return the etransnum of the secondary ack.
					}
					//The secondary carrier does not support COB claim transactions. We must print a manual claim form.
					if(doPrint) {
						PrintCdaClaimForm(claim2);
					}
				}
				else {//an embedded message exists
					//string embeddedMsg=fieldInputter.GetValue("G40");
					//MsgBoxCopyPaste msgbox=new MsgBoxCopyPaste(embeddedMsg);
					//msgbox.Show();
					//actually, nothing to do here because already printed above.
				}
			}
			return etransAck.EtransNum;
		}
Пример #21
0
 ///<summary>Returns the list of etrans requests. The etrans.AckEtransNum can be used to get the etrans ack. The following are the only possible formats that can be returned in the acks: 21 EOB Response, 11 Claim Ack, 14 Outstanding Transactions Response, 23 Predetermination EOB, 13 Predetermination Ack, 24 E-Mail Response. Set version2 to true if version 02 request and false for version 04 request. Set sendToItrans to true only when sending to carrier 999999 representing the entire ITRANS network. When version2 is false and sendToItrans is false then carrier must be set to a valid Canadian carrier, otherwise it can be set to null. Prov must be validated as a CDANet provider before calling this function.</summary>
 public static List<Etrans> GetOutstandingTransactions(bool version2,bool sendToItrans,Carrier carrier,Provider prov)
 {
     List<Etrans> etransAcks=new List<Etrans>();
     Clearinghouse clearhouse=Canadian.GetClearinghouse();
     if(clearhouse==null) {
         throw new ApplicationException("Canadian clearinghouse not found.");
     }
     string saveFolder=clearhouse.ExportPath;
     if(!Directory.Exists(saveFolder)) {
         throw new ApplicationException(saveFolder+" not found.");
     }
     //We are required to send the request for outstanding transactions over and over until we get back an outstanding transactions ack format (Transaction type 14), because
     //there may be more than one item in the mailbox and we can only get one item at time.
     bool exit=false;
     do {
         StringBuilder strb=new StringBuilder();
         Etrans etrans=null;
         if(version2 || sendToItrans) {
             etrans=Etranss.CreateCanadianOutput(0,0,0,clearhouse.ClearinghouseNum,EtransType.RequestOutstand_CA,0,0);
         }
         else {
             if((carrier.CanadianSupportedTypes&CanSupTransTypes.RequestForOutstandingTrans_04)!=CanSupTransTypes.RequestForOutstandingTrans_04) {
                 throw new ApplicationException("The carrier does not support request for outstanding transactions.");
             }
             etrans=Etranss.CreateCanadianOutput(0,carrier.CarrierNum,carrier.CanadianNetworkNum,
                 clearhouse.ClearinghouseNum,EtransType.RequestOutstand_CA,0,0);
         }
         //A01 transaction prefix 12 AN
         if(version2 || sendToItrans) {
             strb.Append("            ");
         }
         else {
             if(carrier.CanadianNetworkNum==0) {
                 throw new ApplicationException("Carrier network not set.");
             }
             CanadianNetwork network=CanadianNetworks.GetNetwork(carrier.CanadianNetworkNum);
             strb.Append(Canadian.TidyAN(network.CanadianTransactionPrefix,12));
         }
         //A02 office sequence number 6 N
         strb.Append(Canadian.TidyN(etrans.OfficeSequenceNumber,6));
         //A03 format version number 2 N
         if(version2) {
             strb.Append("02");
         }
         else {
             strb.Append("04");
         }
         //A04 transaction code 2 N
         strb.Append("04");//outstanding transactions request
         if(!version2) {//version 04
             //A05 carrier id number 6 N
             if(sendToItrans) {
                 strb.Append("999999");
             }
             else {
                 strb.Append(carrier.ElectID);//already validated as 6 digit number.
             }
         }
         //A06 software system id 3 AN
         strb.Append(Canadian.SoftwareSystemId());
         if(!version2) { //version 04
             //A10 encryption method 1 N
             if(sendToItrans) {
                 strb.Append("1");
             }
             else {
                 strb.Append(carrier.CanadianEncryptionMethod);//validated in UI
             }
         }
         //A07 message length N4
         if(!version2) { //version 04
             strb.Append(Canadian.TidyN("64",5));
         }
         else {
             strb.Append(Canadian.TidyN("42",4));
         }
         if(!version2) { //version 04
             //A09 carrier transaction counter 5 N
             strb.Append(Canadian.TidyN(etrans.CarrierTransCounter,5));
         }
         //According to the documentation for the outstanding transactions ack format, B01 only has to be a valid provider for the practice,
         //and that will trigger acknowledgements for all providers of the practice. I am assuming here that the same is true for the
         //billing provider in field B03, because there is no real reason to limit the request to any particular provider.
         //B01 CDA provider number 9 AN
         strb.Append(Canadian.TidyAN(prov.NationalProvID,9));//already validated
         //B02 (treating) provider office number 4 AN
         strb.Append(Canadian.TidyAN(prov.CanadianOfficeNum,4));//already validated
         if(!version2) { //version 04
             //B03 billing provider number 9 AN
             //might need to account for possible 5 digit prov id assigned by carrier
             strb.Append(Canadian.TidyAN(prov.NationalProvID,9));//already validated
         }
         string result="";
         bool resultIsError=false;
         try {
             result=Canadian.PassToIca(strb.ToString(),clearhouse);
         }
         catch(ApplicationException ex) {
             result=ex.Message;
             resultIsError=true;
             //Etranss.Delete(etrans.EtransNum);//we don't want to do this, because we want the incremented etrans.OfficeSequenceNumber to be saved
             //Attach an ack indicating failure.
         }
         //Attach an ack to the etrans
         Etrans etransAck=new Etrans();
         etransAck.PatNum=etrans.PatNum;
         etransAck.PlanNum=etrans.PlanNum;
         etransAck.InsSubNum=etrans.InsSubNum;
         etransAck.CarrierNum=etrans.CarrierNum;
         etransAck.DateTimeTrans=DateTime.Now;
         CCDFieldInputter fieldInputter=null;
         if(resultIsError) {
             etransAck.Etype=EtransType.AckError;
             etrans.Note="failed";
         }
         else {
             if(result.Substring(12).StartsWith("NO MORE ITEMS")) {
                 etransAck.Etype=EtransType.OutstandingAck_CA;
                 exit=true;
             }
             else {
                 fieldInputter=new CCDFieldInputter(result);
                 CCDField fieldG05=fieldInputter.GetFieldById("G05");
                 if(fieldG05!=null) {
                     etransAck.AckCode=fieldG05.valuestr;
                 }
                 etransAck.Etype=fieldInputter.GetEtransType();
             }
         }
         Etranss.Insert(etransAck);
         Etranss.SetMessage(etransAck.EtransNum,result);
         etrans.AckEtransNum=etransAck.EtransNum;
         Etranss.Update(etrans);
         Etranss.SetMessage(etrans.EtransNum,strb.ToString());
         etransAcks.Add(etransAck);
         if(resultIsError) {
             throw new ApplicationException(result);
         }
         if(fieldInputter==null) { //happens in version 02 when a terminating message containing the text "NO MORE ITEMS" is received.
             break;
         }
         CCDField fieldA04=fieldInputter.GetFieldById("A04");//message format
         if(version2) {
             //In this case, there are only 4 possible responses: EOB, Claim Ack, Claim Ack with an error code, or Claim Ack with literal "NO MORE ITEMS" starting at character 13.
             if(fieldA04.valuestr=="11") {
                 CCDField fieldG08=fieldInputter.GetFieldById("G08");
                 if(fieldG08!=null && (fieldG08.valuestr=="004" || fieldG08.valuestr=="049")) { //Exit conditions specified in the documentation.
                     etransAck.Etype=EtransType.OutstandingAck_CA;
                     exit=true;
                 }
             }
         }
         else { //version 04
             //Remember, the only allowed response transaction types are: 21 EOB Response, 11 Claim Ack, 14 Outstanding Transactions Response, 23 Predetermination EOB, 13 Predetermination Ack, 24 E-Mail Response
             if(fieldA04.valuestr=="14") {//Outstanding Transaction Ack Format
                 CCDField fieldG05=fieldInputter.GetFieldById("G05");//response status
                 if(fieldG05.valuestr=="R") { //We only expect the result to be 'R' or 'X' as specified in the documentation.
                     CCDField fieldG07=fieldInputter.GetFieldById("G07");//disposition message
                     CCDField fieldG08=fieldInputter.GetFieldById("G08");//error code
                     MessageBox.Show(Lan.g("","Failed to receive outstanding transactions. Messages from CDANet")+": "+Environment.NewLine+
                         fieldG07.valuestr.Trim()+Environment.NewLine+((fieldG08!=null)?CCDerror.message(Convert.ToInt32(fieldG08.valuestr),false):""));
                 }
                 etransAck.Etype=EtransType.OutstandingAck_CA;
                 exit=true;
             }
         }
         //Field A02 exists in all of the possible formats (21,11,14,23,13,24).
         CCDField fieldA02=fieldInputter.GetFieldById("A02");//office sequence number
         //We use the Office Sequence Number to find the original etrans entry so that we can discover which patient the response is referring to.
         Etrans etranOriginal=Etranss.GetForSequenceNumberCanada(fieldA02.valuestr);
         if(etranOriginal!=null) { //Null will happen when testing, but should not happen in production.
             etrans.PatNum=etranOriginal.PatNum;
             etrans.PlanNum=etranOriginal.PlanNum;
             etrans.InsSubNum=etranOriginal.InsSubNum;
             etrans.ClaimNum=etranOriginal.ClaimNum;
             Etranss.Update(etrans);
             etransAck.PatNum=etranOriginal.PatNum;
             etransAck.PlanNum=etranOriginal.PlanNum;
             etransAck.InsSubNum=etranOriginal.InsSubNum;
             etransAck.ClaimNum=etranOriginal.ClaimNum;
             Etranss.Update(etransAck);
             if(!exit) {
                 if(etransAck.ClaimNum!=0) {
                     Claim claim=Claims.GetClaim(etransAck.ClaimNum);
                     if(etransAck.AckCode=="A") {
                         claim.ClaimStatus="R";
                         claim.DateReceived=MiscData.GetNowDateTime();
                     }
                     else if(etransAck.AckCode=="H" || etransAck.AckCode=="B" || etransAck.AckCode=="C" || etransAck.AckCode=="N") {
                         claim.ClaimStatus="S";
                     }
                     else if(etransAck.AckCode=="M") {
                         Canadian.PrintManualClaimForm(claim);
                     }
                     Claims.Update(claim);
                 }
             }
         }
         if(!exit) {
             try {
                 new FormCCDPrint(etrans,result,true);
             }
             catch(Exception ex) {
                 MessageBox.Show(Lan.g("CanadianOutput","Failed to display one of the ROT responses")+": "+Environment.NewLine+ex.ToString());
             }
         }
     } while(!exit);
     return etransAcks;
 }
Пример #22
0
			public override bool MeetsRequirement(CCDFieldInputter formData,string value) {
				for(int i=0;i<acceptedValues.Length;i++){
					if(value==acceptedValues[i]){
						return true;
					}
				}
				return false;
			}
Пример #23
0
		private static string ConvertEOBVersion(string primaryEOB,string versionTo) {
			try {
				CCDFieldInputter primaryEOBfields=new CCDFieldInputter(primaryEOB);
				CCDField fieldA03=primaryEOBfields.GetFieldById("A03");
				if(fieldA03!=null && fieldA03.valuestr!=versionTo) {
					StringBuilder strb=new StringBuilder(primaryEOB);//todo: perform format conversion here.
					return strb.ToString();
				}
			}
			catch {
				//There was an error converting the primary EOB to the correct version. Just return the original and hope it works.
			}
			return primaryEOB;//No conversion necessary/possible.
		}
Пример #24
0
		private void Init(){
			InitializeComponent();
			breakLinePen.Width=2;
			if(etrans.PatNum!=0) { //Some transactions are not patient specific.
				patient=Patients.GetPat(etrans.PatNum);
				patPlansForPat=PatPlans.Refresh(etrans.PatNum);
				claim=Claims.GetClaim(etrans.ClaimNum);
				primaryCarrier=Carriers.GetCarrier(etrans.CarrierNum);
				if(claim==null) {//for eligibility
					//Get primary info
					insSub=InsSubs.GetSub(etrans.InsSubNum,new List<InsSub>());
					subscriber=Patients.GetPat(insSub.Subscriber);
					insplan=InsPlans.GetPlan(etrans.PlanNum,new List<InsPlan>());
					patPlanPri=PatPlans.GetFromList(patPlansForPat,insSub.InsSubNum);
				}
				else {
					//Get primary info
					insSub=InsSubs.GetSub(claim.InsSubNum,new List<InsSub>());
					subscriber=Patients.GetPat(insSub.Subscriber);
					insplan=InsPlans.GetPlan(claim.PlanNum,new List<InsPlan>());
					patPlanPri=PatPlans.GetFromList(patPlansForPat,insSub.InsSubNum);
					//Get secondary info
					if(claim.InsSubNum2!=0) {
						patPlanSec=PatPlans.GetFromList(patPlansForPat,claim.InsSubNum2);
						insSub2=InsSubs.GetSub(claim.InsSubNum2,new List<InsSub>());
						subscriber2=Patients.GetPat(insSub2.Subscriber);
						insplan2=InsPlans.GetPlan(claim.PlanNum2,new List<InsPlan>());
						secondaryCarrier=Carriers.GetCarrier(insplan2.CarrierNum);
					}
					//Provider info
					provTreat=Providers.GetProv(claim.ProvTreat);
					provBill=Providers.GetProv(claim.ProvBill);
					//Claim related info
					claimprocs=ClaimProcs.RefreshForClaim(claim.ClaimNum);
					long clinicNum=0;
					for(int i=0;i<claimprocs.Count;i++) {
						if(claimprocs[i].ClinicNum!=0) {
							clinicNum=claimprocs[i].ClinicNum;
							break;
						}
					}
					if(clinicNum!=0) {
						clinic=Clinics.GetClinic(clinicNum);
					}
					else if(!PrefC.GetBool(PrefName.EasyNoClinics) && Clinics.List.Length>0) {
						clinic=Clinics.List[0];
					}
				}
				if(provTreat==null) {
					provTreat=Providers.GetProv(Patients.GetProvNum(patient));
				}
				if(provBill==null) {
					provBill=Providers.GetProv(Patients.GetProvNum(patient));
				}
				List<Procedure> procsAll=Procedures.Refresh(etrans.PatNum);
				extracted=Procedures.GetCanadianExtractedTeeth(procsAll);
			}
			if(MessageText==null || MessageText.Length<23) {
				throw new Exception("CCD message format too short: "+MessageText);
			}
			formData=new CCDFieldInputter(MessageText);//Input the fields of the given message.
			CCDField languageOfInsured=formData.GetFieldById("G27");
			if(languageOfInsured!=null) {
				if(languageOfInsured.valuestr=="F") {
					isFrench=true;
				}
			}
			else if(subscriber!=null && subscriber.Language=="fr") {
				isFrench=true;
			}
			formatVersionNumber=formData.GetFieldById("A03").valuestr;//Must always exist so no error checking here.
			transactionCode=formData.GetFieldById("A04").valuestr;//Must always exist so no error checking here.
			if(formatVersionNumber=="04") {//FormId field does not exist in version 02 in any of the message texts.
				CCDField formIdField=formData.GetFieldById("G42");//Usually exists in version 04 response messages.
				//Only a few response transactions don't define field G42. So far, those are transactions 15 (Summary Reconciliation), 16 (Payment Reconciliation) and 24 (Email).
				//In these cases, we simply do not use the formId field later on in the display code.
				if(formIdField!=null) {
					formId=formIdField.valuestr;
				}
			}
			else {//Version 02
				//Since there is no FormID field in version 02, we figure out what the formId should be based on the transaction type.
				if(transactionCode=="10") {//Eligibility Response.
					formId="08";//Eligibility Form
				}
				else if(transactionCode=="11") {//Claim Response.
					formId="03";//Claim Acknowledgement Form
				}
				else if(transactionCode=="21") {//EOB
					formId="01";//EOB Form
					CCDField g02=formData.GetFieldById("G02");
					if(g02!=null && g02.valuestr=="Y") {
						formId="04";//Employer Certified.
					}
				}
				else if(transactionCode=="13") {//Response to Pre-Determination.
					formId="06";//Pre-Determination Acknowledgement Form
				}
				else if(transactionCode=="12") { //Reversal response
					//There is no standard form for a reversal response, but we print the reversal response later on based on the transactioncode so we don't need to do anything here.
				}
				else {
					throw new Exception("Unhandled transactionCode '"+transactionCode+"' for version 02 message.");
				}
			}
			CCDField status=formData.GetFieldById("G05");
			if(status!=null && status.valuestr!=null) {
				responseStatus=status.valuestr.ToUpper();
			}
			transactionCode=formData.GetFieldById("A04").valuestr;
			predetermination=(transactionCode=="23"||transactionCode=="13");//Be sure to list all predetermination response types here!
			if(copiesToPrint<=0) { //Show the form on screen if there are no copies to print.
				ShowDisplayMessages();
				CCDField fieldPayTo=formData.GetFieldById("F01");
				if(fieldPayTo!=null) {
					bool paySubscriber=(fieldPayTo.valuestr=="1");//same for version 02 and version 04
					//Typically, insurance companies in Canada prefer to pay the subscriber instead of the dentist.
					if(AssignmentOfBenefits()) {//The insurance plan is set to pay the dentist
						if(paySubscriber) {//The carrier has decided to pay the subscriber.
							MsgBox.Show("Canadian","INFORMATION: The carrier changed the payee from the dentist to the subscriber.");//This check was required for certification.
						}
					}
					else {//The insurance plan is set to pay the subscriber
						if(!paySubscriber) {//The carrier has decided to pay the dentist.
							MsgBox.Show("Canadian","INFORMATION: The carrier changed the payee from the subscriber to the dentist.");//This check was required for certification.
						}
					}
				}
				CCDField paymentAdjustmentAmount=formData.GetFieldById("G33");
				if(paymentAdjustmentAmount!=null) {
					if(paymentAdjustmentAmount.valuestr.Substring(1)!="000000") {
						MessageBox.Show(Lan.g(this,"Payment adjustment amount")+": "+RawMoneyStrToDisplayMoney(paymentAdjustmentAmount.valuestr));
					}
				}
				if(autoPrint) {
					if(responseStatus!="R") { //We are not required to automatically print rejection notices.
						//Automatically print a patient copy only. We are never required to autoprint a dentist copy, but it can be done manually instead.
						new FormCCDPrint(etrans.Copy(),MessageText,1,false,true);
					}
				}
				if(formId=="05") { //Manual claim form
					Canadian.ShowManualClaimForm(claim);
					Close();
				}
				else {
					pd=CreatePrintDocument();
					printPreviewControl1.Document=pd;//Setting the document causes system to call pd_PrintPage, which will print the document in the preview window.
					ShowDialog();
				}
			}
			else {
				pd=CreatePrintDocument();
				if(formId=="05") { //Manual claim form
#if DEBUG
					Canadian.ShowManualClaimForm(claim);
#else
					Canadian.PrintManualClaimForm(claim);//Send the print job to the physical printer.
#endif
				}
				else {
#if DEBUG
					new FormCCDPrint(etrans.Copy(),MessageText,0,false,patientCopy);//Show the form on the screen.
#else
					pd.Print();//Send the print job to the physical printer.
#endif
				}
				//Print the remaining copies recursively.
				if(copiesToPrint>=2) {
					new FormCCDPrint(etrans.Copy(),MessageText,copiesToPrint-1,false,patientCopy);
				}
			}
			CCDField embeddedTransaction=formData.GetFieldById("G40");
			if(embeddedTransaction!=null) {
				new FormCCDPrint(etrans.Copy(),embeddedTransaction.valuestr,copiesToPrint,autoPrint,patientCopy);
			}
		}
Пример #25
0
			///<summary>Return the length required to input this field in bytes. Negative value indicates error.</summary>
			public abstract int CalcLength(CCDFieldInputter formData);
Пример #26
0
			public override bool MeetsRequirement(CCDFieldInputter formData,string value) {
				if(!Regex.IsMatch(value,"^[0-9]+$")) {
					MessageBox.Show(this.ToString()+".MeetsRequirements: Internal Error, cannot check range requirement against "+
													"non-integer value '"+value+"'");
					return false;
				}
				int numVal=Convert.ToInt32(value);
				return(numVal >= minVal && numVal <= maxVal);
			}
Пример #27
0
        ///<summary>The result is a string which can be dropped into the insplan.BenefitNotes.  Or it might throw an exception if invalid data.  This class is also responsible for saving the returned message to the etrans table and printing out the required form.</summary>
        public static string SendElegibility(string electID, int patNum, string groupNumber, string divisionNo,
                                             string subscriberID, string patID, Relat patRelat, int subscNum, string dentaideCardSequence)
        {
            //Note: This might be the only class of this kind that returns a string.  It's a special situation.
            //We are simply not going to bother with language translation here.
            //determine carrier.
            Carrier carrier = Carriers.GetCanadian(electID);          //this also happens to validate missing or short value

            if (carrier == null)
            {
                throw new ApplicationException("Invalid carrier EDI code.");
            }
            Clearinghouse clearhouse = Canadian.GetClearinghouse();

            if (clearhouse == null)
            {
                throw new ApplicationException("Canadian clearinghouse not found.");
            }
            string saveFolder = clearhouse.ExportPath;

            if (!Directory.Exists(saveFolder))
            {
                throw new ApplicationException(saveFolder + " not found.");
            }
            //Initialize objects-----------------------------------------------------------------------------------------------
            Patient  patient    = Patients.GetPat(patNum);
            Patient  subscriber = Patients.GetPat(subscNum);
            Provider treatProv  = Providers.GetProv(Patients.GetProvNum(patient));
            Provider billProv   = Providers.GetProv(Providers.GetBillingProvNum(treatProv.ProvNum));
            //I had to use a dialog box to get the eligibility code.

            //validate any missing info----------------------------------------------------------------------------------
            string error = "";

            if (carrier.CanadianNetworkNum == 0)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Carrier does not have network specified";
            }
            if (!Regex.IsMatch(carrier.ElectID, @"^[0-9]{6}$"))           //not necessary, but nice
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "CarrierId 6 digits";
            }
            if (treatProv.NationalProvID.Length != 9)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "TreatingProv CDA num 9 digits";
            }
            if (treatProv.CanadianOfficeNum.Length != 4)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "TreatingProv office num 4 char";
            }
            if (billProv.NationalProvID.Length != 9)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "BillingProv CDA num 9 digits";
            }
            if (groupNumber.Length == 0 || groupNumber.Length > 12 || groupNumber.Contains(" "))
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Plan Number";
            }
            if (subscriberID == "")
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "SubscriberID";
            }
            if (patNum != subscNum && patRelat == Relat.Self)           //if patient is not subscriber, and relat is self
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Relationship cannot be self";
            }
            if (patient.Gender == PatientGender.Unknown)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Patient gender";
            }
            if (patient.Birthdate.Year < 1880 || patient.Birthdate > DateTime.Today)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Patient birthdate";
            }
            if (patient.LName == "")
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Patient lastname";
            }
            if (patient.FName == "")
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Patient firstname";
            }
            if (subscriber.Birthdate.Year < 1880 || subscriber.Birthdate > DateTime.Today)
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Subscriber birthdate";
            }
            if (subscriber.LName == "")
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Subscriber lastname";
            }
            if (subscriber.FName == "")
            {
                if (error != "")
                {
                    error += ", ";
                }
                error += "Subscriber firstname";
            }
            if (error != "")
            {
                throw new ApplicationException(error);
            }
            FormCanadianEligibility FormElig = new FormCanadianEligibility();

            FormElig.ShowDialog();
            if (FormElig.DialogResult != DialogResult.OK)
            {
                throw new ApplicationException("Eligibility Code or Date missing.");
            }
            //eligiblity code guaranteed to not be 0 at this point.  Also date will be between 1980 and 10 years from now.
            Etrans etrans = Etranss.CreateCanadianOutput(patNum, carrier.CarrierNum, carrier.CanadianNetworkNum,
                                                         clearhouse.ClearinghouseNum, EtransType.Eligibility_CA);
            string txt = "";

            //create message----------------------------------------------------------------------------------------------
            //A01 transaction prefix 12 AN
//todo
            txt += "123456789012";          //To be later provided by the individual network.
            //A02 office sequence number 6 N
            txt += Canadian.TidyN(etrans.OfficeSequenceNumber, 6);
            //A03 format version number 2 N
            txt += "04";
            //A04 transaction code 2 N
            txt += "08";            //eligibility
            //A05 carrier id number 6 N
            txt += carrier.ElectID; //already validated as 6 digit number.
            //A06 software system id 3 AN  The third character is for version of OD.
//todo
            txt += "OD1";          //To be later supplied by CDAnet staff to uniquely identify OD.
            //A10 encryption method 1 N
//todo
            txt += "1";
            //A07 message length 5 N
            int len = 214;

//todo does not account for C19. Possibly 30 more.
            //if(C19 is used, Plan Record){
            //len+=30;
            //}
            txt += Canadian.TidyN(len, 5);
            //A09 carrier transaction counter 5 N
            txt += Canadian.TidyN(etrans.CarrierTransCounter, 5);
            //B01 CDA provider number 9 AN
            txt += Canadian.TidyAN(treatProv.NationalProvID, 9);         //already validated
            //B02 (treating) provider office number 4 AN
            txt += Canadian.TidyAN(treatProv.CanadianOfficeNum, 4);      //already validated
            //B03 billing provider number 9 AN
//todo, need to account for possible 5 digit prov id assigned by carrier
            txt += Canadian.TidyAN(billProv.NationalProvID, 9);         //already validated
            //C01 primary policy/plan number 12 AN (group number)
            //only validated to ensure that it's not blank and is less than 12. Also that no spaces.
            txt += Canadian.TidyAN(groupNumber, 12);
            //C11 primary division/section number 10 AN
            txt += Canadian.TidyAN(divisionNo, 10);
            //C02 subscriber id number 12 AN
            txt += Canadian.TidyAN(subscriberID.Replace("-", ""), 12);        //validated
            //C17 primary dependant code 2 N. Optional
            txt += Canadian.TidyN(patID, 2);
            //C03 relationship code 1 N
            //User interface does not only show Canadian options, but all options are handled.
            txt += Canadian.GetRelationshipCode(patRelat);
            //C04 patient's sex 1 A
            //validated to not include "unknown"
            if (patient.Gender == PatientGender.Male)
            {
                txt += "M";
            }
            else
            {
                txt += "F";
            }
            //C05 patient birthday 8 N
            txt += patient.Birthdate.ToString("yyyyMMdd");          //validated
            //C06 patient last name 25 AE
            txt += Canadian.TidyAE(patient.LName, 25, true);        //validated
            //C07 patient first name 15 AE
            txt += Canadian.TidyAE(patient.FName, 15, true);        //validated
            //C08 patient middle initial 1 AE
            txt += Canadian.TidyAE(patient.MiddleI, 1);
            //C09 eligibility exception code 1 N
            txt += Canadian.TidyN(FormElig.EligibilityCode, 1);         //validated
            //C12 plan flag 1 A
//todo
            //might not be carrier.IsPMP.  Might have to do with plan, not carrier. See F17.
            txt += " ";
            //C18 plan record count 1 N
//todo
            txt += "0";
            //C16 Eligibility date. 8 N.
            txt += FormElig.AsOfDate.ToString("yyyyMMdd");          //validated
            //D01 subscriber birthday 8 N
            txt += subscriber.Birthdate.ToString("yyyyMMdd");       //validated
            //D02 subscriber last name 25 AE
            txt += Canadian.TidyAE(subscriber.LName, 25, true);     //validated
            //D03 subscriber first name 15 AE
            txt += Canadian.TidyAE(subscriber.FName, 15, true);     //validated
            //D04 subscriber middle initial 1 AE
            txt += Canadian.TidyAE(subscriber.MiddleI, 1);
            //D10 language of insured 1 A
            if (subscriber.Language == "fr")
            {
                txt += "F";
            }
            else
            {
                txt += "E";
            }
            //D11 card sequence/version number 2 N
//todo: Not validated against type of carrier yet.  Need to check if Dentaide.
            txt += Canadian.TidyN(dentaideCardSequence, 2);
//todo If C18=1, then the following field would appear
            //C19 plan record 30 AN
            string result = "";

            try {
                result = Canadian.PassToCCD(txt, carrier.CanadianNetworkNum, clearhouse);
            }
            catch (ApplicationException ex) {
                Etranss.Delete(etrans.EtransNum);
                throw new ApplicationException(ex.Message);
            }
            Etranss.SetMessage(etrans.EtransNum, txt);
            etrans.MessageText = txt;
            FormCCDPrint FormP = new FormCCDPrint(etrans);          //Print the form.

            FormP.ShowDialog();
            //Now we will process the 'result' here to extract the important data.  Basically Yes or No on the eligibility.
            //We might not do this for any other trans type besides eligibility.
            string           retVal        = "Eligibility check on " + DateTime.Today.ToShortDateString() + "\r\n";
            CCDFieldInputter fieldInputter = new CCDFieldInputter(result);
            CCDField         field         = fieldInputter.GetFieldById("G05");//response status

            //CCDFieldInputter could really use a GetValue(string fieldId) method so I don't have to use a field object.
            switch (field.valuestr)
            {
            case "E":
                retVal += "Patient is eligible.";
                break;

            case "R":
                retVal += "Patient not eligible, or error in data.";
                break;

            case "M":
                retVal += "Manual claimform should be submitted for employer certified plan.";
                break;
            }
            CCDField[] fields = fieldInputter.GetFieldsById("G08");          //Error Codes
            for (int i = 0; i < fields.Length; i++)
            {
                retVal += "\r\n";
                retVal += fields[i].valuestr;              //todo: need to turn this into a readable string.
            }
            fields = fieldInputter.GetFieldsById("G32");   //Display messages
            for (int i = 0; i < fields.Length; i++)
            {
                retVal += "\r\n";
                retVal += fields[i].valuestr;
            }
            return(retVal);
        }