Пример #1
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;
		}
Пример #2
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;*/
 }
Пример #3
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;
 }
Пример #4
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;
 }
Пример #5
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;
 }
Пример #6
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;
 }