///<summary>Generates all the xml up to the point where the first statement would go.</summary> public static void GeneratePracticeInfo(XmlWriter writer, long clinicNum) { Clinic clinic = Clinics.GetClinic(clinicNum); Ebill eBillClinic = Ebills.GetForClinic(clinicNum); if (eBillClinic == null) //Clinic specific Ebill doesn't exist, use the defaults. { eBillClinic = Ebills.GetForClinic(0); } writer.WriteProcessingInstruction("xml", "version = \"1.0\" standalone=\"yes\""); writer.WriteStartElement("StatementFile"); //sender address---------------------------------------------------------- writer.WriteStartElement("SenderAddress"); if (clinic == null) { writer.WriteElementString("Name", PrefC.GetString(PrefName.PracticeTitle)); } else { writer.WriteElementString("Name", clinic.Description); } WriteAddress(writer, eBillClinic.PracticeAddress, clinic); writer.WriteEndElement(); //SenderAddress writer.WriteStartElement("Statements"); }
//these are temporary: //private static string vendorID="68"; //private static string vendorPMScode="144"; //private static string clientAccountNumber="8011";//the dental office number set by EHG //private static string creditCardChoices="MC,D,V,A";//MasterCard,Discover,Visa,AmericanExpress //private static string userName=""; //private static string password=""; ///<summary>Returns empty list if no errors. Otherwise returns a list with error messages.</summary> public static List <string> Validate(long clinicNum) { List <string> listErrors = new List <string>(); Clinic clinic = Clinics.GetClinic(clinicNum); Ebill eBillClinic = Ebills.GetForClinic(clinicNum); Ebill eBillDefault = Ebills.GetForClinic(0); EHG_Address addressRemit = null; if (eBillClinic == null) { addressRemit = GetAddress(eBillDefault.RemitAddress, clinic); } else { addressRemit = GetAddress(eBillClinic.RemitAddress, clinic); } if (addressRemit.Address1.Trim().Length == 0 || addressRemit.City.Trim().Length == 0 || addressRemit.State.Trim().Length == 0 || addressRemit.Zip.Trim().Length == 0) { listErrors.Add(Lan.g("EHG_Statements", "invalid") + " " + Lan.g("EHG_Statements", addressRemit.Source)); } return(listErrors); }
///<summary>Generates all the xml up to the point where the first statement would go.</summary> public static void GeneratePracticeInfo(XmlWriter writer, long clinicNum) { Clinic clinic = Clinics.GetClinic(clinicNum); Ebill eBillClinic = Ebills.GetForClinic(clinicNum); Ebill eBillDefault = Ebills.GetForClinic(0); writer.WriteProcessingInstruction("xml", "version = \"1.0\" standalone=\"yes\""); writer.WriteStartElement("EISStatementFile"); writer.WriteAttributeString("VendorID", PrefC.GetString(PrefName.BillingElectVendorId)); writer.WriteAttributeString("OutputFormat", "StmOut_Blue6Col"); writer.WriteAttributeString("Version", "2"); writer.WriteElementString("SubmitDate", DateTime.Today.ToString("yyyy-MM-dd")); writer.WriteElementString("PrimarySubmitter", PrefC.GetString(PrefName.BillingElectVendorPMSCode)); writer.WriteElementString("Transmitter", "EHG"); writer.WriteStartElement("Practice"); string billingClientAccountNumber = eBillDefault.ClientAcctNumber; if (eBillClinic != null && eBillClinic.ClientAcctNumber != "") //clinic eBill entry exists, check the fields for overrides { billingClientAccountNumber = eBillClinic.ClientAcctNumber; } writer.WriteAttributeString("AccountNumber", billingClientAccountNumber); //sender address---------------------------------------------------------- writer.WriteStartElement("SenderAddress"); if (clinic == null) { writer.WriteElementString("Name", PrefC.GetString(PrefName.PracticeTitle)); } else { writer.WriteElementString("Name", clinic.Description); } if (eBillClinic == null) { WriteAddress(writer, eBillDefault.PracticeAddress, clinic); } else { WriteAddress(writer, eBillClinic.PracticeAddress, clinic); } writer.WriteEndElement(); //senderAddress //remit address---------------------------------------------------------- writer.WriteStartElement("RemitAddress"); if (clinic == null) { writer.WriteElementString("Name", PrefC.GetString(PrefName.PracticeTitle)); } else { writer.WriteElementString("Name", clinic.Description); } if (eBillClinic == null) { WriteAddress(writer, eBillDefault.RemitAddress, clinic); } else { WriteAddress(writer, eBillClinic.RemitAddress, clinic); } writer.WriteEndElement(); //remitAddress //Rendering provider------------------------------------------------------ Provider prov = Providers.GetProv(PrefC.GetLong(PrefName.PracticeDefaultProv)); writer.WriteStartElement("RenderingProvider"); writer.WriteElementString("Name", prov.GetFormalName()); writer.WriteElementString("LicenseNumber", prov.StateLicense); writer.WriteElementString("State", PrefC.GetString(PrefName.PracticeST)); writer.WriteEndElement(); //Rendering provider }
///<summary>Surround with try catch. The "data" is the previously constructed xml. If the internet connection is lost or unavailable, then the exception thrown will be a 404 error similar to the following: "The remote server returned an error: (404) Not Found"</summary> public static void Send(string data, long clinicNum) { //Validate the structure of the XML before sending. StringReader sr = new StringReader(data); try { XmlReader xmlr = XmlReader.Create(sr); while (xmlr.Read()) //Read every node an ensure that there are no exceptions thrown. { } } catch (Exception ex) { throw new ApplicationException("Invalid XML in statement batch: " + ex.Message); } finally { sr.Dispose(); } string strHistoryFile = ""; if (PrefC.GetBool(PrefName.BillingElectSaveHistory)) { string strHistoryDir = CodeBase.ODFileUtils.CombinePaths(ImageStore.GetPreferredAtoZpath(), "EHG_History"); if (!Directory.Exists(strHistoryDir)) { Directory.CreateDirectory(strHistoryDir); } strHistoryFile = CodeBase.ODFileUtils.CreateRandomFile(strHistoryDir, ".txt"); File.WriteAllText(strHistoryFile, data); } //Step 1: Post authentication request: Version myVersion = new Version(Application.ProductVersion); HttpWebRequest webReq; WebResponse response; StreamReader readStream; string str; string[] responseParams; string status = ""; string group = ""; string userid = ""; string authid = ""; string errormsg = ""; string alertmsg = ""; string curParam = ""; string serverName = "https://claimconnect.dentalxchange.com/dci/upload.svl";//live URL for claims (According to phone call with Dentalxchange) string serverNameOverride = PrefC.GetString(PrefName.BillingElectStmtUploadURL); if (!string.IsNullOrEmpty(serverNameOverride)) { serverName = serverNameOverride; } #if DEBUG //serverName="https://prelive.dentalxchange.com/dci/upload.svl"; //test URL for claims //serverName="https://claimconnect.dentalxchange.com/dci/upload.svl"; //live URL for claims //serverName="https://prelive.dentalxchange.com/dci/upload.svl"; //test URL for Stmts //serverName="https://billconnect.dentalxchange.com/dci/upload.svl"; //live URL for Stmts; probably the correct one to use. #endif webReq = (HttpWebRequest)WebRequest.Create(serverName); Ebill ebillDefault = Ebills.GetForClinic(0); string billingUserName = ebillDefault.ElectUserName; string billingPassword = ebillDefault.ElectPassword; if (PrefC.HasClinicsEnabled && clinicNum != 0) { Ebill eBill = Ebills.GetForClinic(clinicNum); if (eBill != null) //eBill entry exists, check the fields for overrides. { if (eBill.ElectUserName != "") { billingUserName = eBill.ElectUserName; } if (eBill.ElectPassword != "") { billingPassword = eBill.ElectPassword; } } } string postData = "Function=Auth" //CONSTANT; signifies that this is an authentication request + "&Source=STM" //CONSTANT; file format + "&UploaderName=OpenDental" //CONSTANT + "&UploaderVersion=" + myVersion.Major.ToString() + "." + myVersion.Minor.ToString() + "." + myVersion.Build.ToString() //eg 12.3.24 + "&Username="******"&Password="******"POST"; webReq.ContentType = "application/x-www-form-urlencoded"; webReq.ContentLength = postData.Length; ASCIIEncoding encoding = new ASCIIEncoding(); byte[] bytes = encoding.GetBytes(postData); Stream streamOut = webReq.GetRequestStream(); streamOut.Write(bytes, 0, bytes.Length); streamOut.Close(); response = webReq.GetResponse(); //Process the authentication response: readStream = new StreamReader(response.GetResponseStream(), Encoding.ASCII); str = readStream.ReadToEnd(); readStream.Close(); if (strHistoryFile != "") //Tack the response onto the end of the saved history file if one was created above. { File.AppendAllText(strHistoryFile, "\r\n\r\nCONNECTION REQUEST: postData.Length=" + postData.Length + " bytes.Length=" + bytes.Length + "==============\r\n" + " RESPONSE TO CONNECTION REQUEST================================================================\r\n" + str); } //Debug.WriteLine(str); //MessageBox.Show(str); responseParams = str.Split('&'); for (int i = 0; i < responseParams.Length; i++) { curParam = GetParam(responseParams[i]); switch (curParam) { case "Status": status = GetParamValue(responseParams[i]); break; case "GROUP": group = GetParamValue(responseParams[i]); break; case "UserID": userid = GetParamValue(responseParams[i]); break; case "AuthenticationID": authid = GetParamValue(responseParams[i]); break; case "ErrorMessage": errormsg = GetParamValue(responseParams[i]); break; case "AlertMessage": alertmsg = GetParamValue(responseParams[i]); break; default: throw new Exception("Unexpected parameter: " + curParam); } } //Process response for errors: if (alertmsg != "") { MessageBox.Show(alertmsg); } switch (status) { case "0": //MessageBox.Show("Authentication successful."); break; case "1": throw new Exception("Authentication failed. " + errormsg); case "2": throw new Exception("Cannot authenticate at this time. " + errormsg); case "3": throw new Exception("Invalid authentication request. " + errormsg); case "4": throw new Exception("Invalid program version. " + errormsg); case "5": throw new Exception("No customer contract. " + errormsg); default: //some as-yet-undefined error throw new Exception("Error " + status + ". " + errormsg); } //Step 2: Post upload request: //string fileName=Directory.GetFiles(clearhouse.ExportPath)[0]; string boundary = "------------7d13e425b00d0"; postData = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"Function\"\r\n" + "\r\n" + "Upload\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"Source\"\r\n" + "\r\n" + "STM\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"AuthenticationID\"\r\n" + "\r\n" + authid + "\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"File\"; filename=\"" + "stmt.xml" + "\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" //using(StreamReader sr=new StreamReader(fileName)) { // postData+=sr.ReadToEnd()+"\r\n" + data + "\r\n" + "--" + boundary + "--"; //} //Debug.WriteLine(postData); //MessageBox.Show(postData); webReq = (HttpWebRequest)WebRequest.Create(serverName); //Timeout documentation: https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout(v=vs.110).aspx. //Timeout: "Gets or sets the time-out value in milliseconds for the GetResponse and GetRequestStream methods." //Timeout default is 100 seconds, which should be sufficient in waiting for a reply from dentalxchange, since the reply is small. //ReadWriteTimeout documentation: https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.readwritetimeout%28v=vs.110%29.aspx //ReadWriteTimeout: "Gets or sets a time-out in milliseconds when writing to or reading from a stream." //ReadWriteTimeout default is 300 seconds (5 minutes). //Our message box that tells the user to wait up to 10 minutes for bills to send, therefore we need at least a 10 minute ReadWriteTimeout. //The user sees progress in the UI when sending. We can increase timeouts as much as we want without making the program look like it crashed. webReq.ReadWriteTimeout = 600000; //10 minutes = 10*60 seconds = 600 seconds = 600*1000 milliseconds = 600,000 milliseconds. webReq.KeepAlive = false; webReq.Method = "POST"; webReq.ContentType = "multipart/form-data; boundary=" + boundary; webReq.ContentLength = postData.Length; bytes = encoding.GetBytes(postData); streamOut = webReq.GetRequestStream(); streamOut.Write(bytes, 0, bytes.Length); streamOut.Close(); response = webReq.GetResponse(); //Process the response readStream = new StreamReader(response.GetResponseStream(), Encoding.ASCII); str = readStream.ReadToEnd(); readStream.Close(); if (strHistoryFile != "") //Tack the response onto the end of the saved history file if one was created above. { File.AppendAllText(strHistoryFile, "\r\n\r\nUPLOAD REQUEST: postData.Length=" + postData.Length + " bytes.Length=" + bytes.Length + "==============\r\n" + " RESPONSE TO DATA UPLOAD================================================================\r\n" + str); } errormsg = ""; status = ""; str = str.Replace("\r\n", ""); //Debug.Write(str); if (str.Length > 300) { throw new Exception("Unknown lengthy error message received."); } responseParams = str.Split('&'); for (int i = 0; i < responseParams.Length; i++) { curParam = GetParam(responseParams[i]); switch (curParam) { case "Status": status = GetParamValue(responseParams[i]); break; case "Error Message": case "ErrorMessage": errormsg = GetParamValue(responseParams[i]); break; case "Filename": case "Timestamp": break; case "": //errorMessage blank break; default: throw new Exception(str); //"Unexpected parameter: "+str);//curParam+"*"); } } switch (status) { case "0": //MessageBox.Show("Upload successful."); break; case "1": throw new Exception("Authentication failed. " + errormsg); case "2": throw new Exception("Cannot upload at this time. " + errormsg); } }