using System; using System.Windows.Forms; using GZTW.AyaNova.BLL; using Interop.QBFC7; using System.Data; using System.Drawing; using System.Reflection; using System.Collections; using System.Text; using System.Threading; using System.Collections.Generic; using System.Text.RegularExpressions; namespace AyaNova.PlugIn.QBI { /// /// Summary description for Util. /// public class Util { public enum pfstat { OK = 0, Failed = 1, Cancel = 2 } #region Attributes and properties public static Global GlobalSettings = null; // public static GZTW.AyaNova.BLL.Region RegionalSettings = null; public static LocalizedTextTable LocaleText = null; public static Integration QBI = null; public static QBIDataEx QDat = null; public static string QCountry = "US"; public static double QVersion = 1.1; public static string QCompanyFile = ""; public static string QCompanyName = ""; public static string sLastRequestXML = ""; public static System.Resources.ResourceManager AyaResource = null; //case 1062 public static bool bMainWindowOpen = false; /// /// The official and unchanging integration ID for /// QBI /// public static Guid QBID { get { return new Guid("{0F8AC430-D304-4726-8361-3FDA33233E68}"); } } #endregion static Util() { } /// /// Are you sure prompt /// /// /// /// public static bool AreYouSure(string msg, string title) { if (MessageBox.Show( msg, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) return true; return false; } #region General helper methods static public Image AyaImage(string sImageResourceName) { if (AyaResource == null) return new Bitmap(10,10);//this is strictly for design mode because forms based on setbase throw exception during design return (Image)AyaResource.GetObject(sImageResourceName); } static public Icon AyaIcon(string sImageResourceName) { return (Icon)AyaResource.GetObject(sImageResourceName); } static public void OpenWebURL(object oUrl) { if (oUrl == null) return; string sUrl = oUrl.ToString(); if (sUrl == "") return; if (sUrl.ToLower().StartsWith("http://")) System.Diagnostics.Process.Start(sUrl); else System.Diagnostics.Process.Start("http://" + sUrl); } /// /// Gets icon image from assembly /// /// /// static public Bitmap xImage(string ImageName) { // if(log.IsDebugEnabled) // //case 1039 //log.Debug("Image(" + ImageName + ")"); //string [] s=Assembly.GetExecutingAssembly().GetManifestResourceNames(); return new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("AyaNovaQBI." + ImageName)); } /// /// Invert a color so it's readable against the passed /// in color /// /// /// static public Color InvertColor(Color col) { // if(log.IsDebugEnabled) // //case 1039 //log.Debug("InvertColor("+col.ToString()+")"); int nSourceColor = col.R + col.G + col.B; int r = 255 - col.R; int g = 255 - col.G; int b = 255 - col.B; int nInvertColor = r + g + b; Color invert; if (nSourceColor - nInvertColor < 28) invert = Color.White; else invert = Color.FromArgb(r, g, b); return invert; } /// /// Checks to see if an item is linked already /// /// /// static public bool IsLinked(Guid AyaNovaID) { return (QBI.Maps[AyaNovaID]!=null); } #endregion #region Pre flight check /// /// Pre flight check, prompt user as required to repair /// or add missing settings / failed ones /// /// This method is called AFTER login. /// /// public static pfstat PreFlightCheck() { //Go through each item sequentially, keep calling it //while return status if fail, this way user keeps at it //until the situation is corrected or cancels if (QBValidate() == pfstat.Cancel) { IntegrationLog.Log(QBID, "PFC: Unable to validate QuickBooks connection, user selected cancel"); return pfstat.Cancel; } else { IntegrationLog.Log(QBID, "PFC: QB validated Country=" + QCountry + ", QBVersion=" + QVersion.ToString() + ", Companyfile=" + QCompanyFile); PopulateQBListCache(); PopulateAyaListCache(); } IntegrationObjectCheck(); if (ValidateSettings(false) == pfstat.Cancel) { IntegrationLog.Log(QBID, "PFC: User settings not completed, user selected cancel"); return pfstat.Cancel; } //Added: 18-Nov-2006 CASE 163 //check that linked items in integration map exist in QB if (QBI.Maps.Count == 0) return pfstat.OK; //Missing links table: DataTable dtTemp = new DataTable(); dtTemp.Columns.Add("MAPID", typeof(Guid)); dtTemp.Columns.Add("Name", typeof(string)); bool present = true; foreach (IntegrationMap m in QBI.Maps) { present = true; switch (m.RootObjectType) { case RootObjectTypes.Client: present = QBClients.Rows.Contains(m.ForeignID); break; case RootObjectTypes.Vendor: present = QBVendors.Rows.Contains(m.ForeignID); break; case RootObjectTypes.Rate: case RootObjectTypes.Part: present = QBItems.Rows.Contains(m.ForeignID); break; } if (!present) dtTemp.Rows.Add(new object[] { m.ID, m.RootObjectType.ToString() + ": " + m.Name }); } if (dtTemp.Rows.Count > 0) { if (dtTemp.Rows.Count == QBI.Maps.Count) { //None of the items mapped match offer to remove them all #region Nothing matches IntegrationLog.Log(QBID, "PFC: No integration maps match qb database objects!"); DialogResult dr = MessageBox.Show("None of the mapped items in AyaNova were found in the \r\n" + "Currently open QuickBooks database.\r\n" + "It's possible you have the wrong database open.\r\n\r\n" + "Do you want to remove all mappings from AyaNova?", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Yes) { dr = MessageBox.Show("If you select YES all mappings will be removed from AyaNova.", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Yes) { IntegrationLog.Log(QBID, "PFC: User opted to remove all mappings after double warning."); foreach (DataRow row in dtTemp.Rows) { QBI.Maps.Remove(row["MAPID"].ToString()); } QBI = (Integration)QBI.Save(); return pfstat.Cancel; } } #endregion } else { //some items match so iterate them and offer to delete one by one IntegrationLog.Log(QBID, "PFC: Some integration maps do not match qb database objects"); foreach (DataRow row in dtTemp.Rows) { DialogResult dr = MessageBox.Show("Linked object: " + row["Name"].ToString() + "\r\n" + "Is missing or set Inactive in QuickBooks.\r\n\r\nRemove it's link from AyaNova?", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Cancel) return pfstat.Cancel; if (dr == DialogResult.Yes) QBI.Maps.Remove(row["MAPID"].ToString()); } QBI = (Integration)QBI.Save(); } } return pfstat.OK; } #region PFC AyaNova side /// /// Check if integration object is present in database, if not then /// add it. /// /// public static pfstat IntegrationObjectCheck() { if (QBI == null) { if (Integration.IntegrationExists(QBID)) { QBI = Integration.GetItem(QBID); //Get the QuickBooks settings object //Block below completely modified for //Case 299 QDat = new QBIDataEx(); if (QBI.AIObject == null || QBI.AIObject.ToString() == "") { QBI.AIObject = QDat.XMLData; QBI = (Integration)QBI.Save(); } else { if (QBI.AIObject is QBIData) { QDat.convertFromObjectFormat(QBI.AIObject); QBI.AIObject = QDat.XMLData; QBI = (Integration)QBI.Save(); } else { //All normal, parse xml and move on QDat.XMLData = (string)QBI.AIObject; } } //Old pre case 299 block // QDat = (QBIData)QBI.AIObject; //if (QDat == null) //{ // QDat = new QBIData(); // QBI.AIObject = QDat; // QBI = (Integration)QBI.Save(); //} } else { QBI = Integration.NewItem(QBID); QBI.Active = true; QBI.AppVersion = "3.x"; QBI.Name = "AyaNova QBI - QuickBooks integration"; //Case 299 QDat = new QBIDataEx(); QBI.AIObject = QDat.XMLData; QBI = (Integration)QBI.Save(); IntegrationLog.Log(QBID, "PFC: QBI Integration object created"); } } return pfstat.OK; } #endregion #region PFC QB side /// /// Open QB connection /// gather info required for future /// transactions /// public static pfstat QBValidate() { // We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; bool bConnected = false; // Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); while (!booSessionBegun) { try { sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); bConnected = true; sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; } catch (System.Runtime.InteropServices.COMException ex) { if (bConnected) sessionManager.CloseConnection(); if (ex.ErrorCode == -2147220458 || ex.ErrorCode == -2147220472 || ex.Message.Contains("Could not start")) { if (MessageBox.Show( "QuickBooks doesn't appear to be running on this computer.\r\n" + "Start QuickBooks and open your company file now before proceeding.", "AyaNova QBI: Pre flight check", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == DialogResult.Cancel) return pfstat.Cancel; } else { IntegrationLog.Log(QBID, "PFC: QBValidate connect unanticipated exception: " + ex.Message + "\r\nError code:" + ex.ErrorCode.ToString()); MessageBox.Show(ex.Message + "\r\nError code:" + string.Format("(HRESULT:0x{0:X8})", ex.ErrorCode)); return pfstat.Cancel; } } } try { //Get the country and latest version supported QVersion = 0; QCountry = "US";//default string[] versions = sessionManager.QBXMLVersionsForSession; double vers = 0; Regex rxVersion = new Regex("[0-9.,]+", RegexOptions.Multiline | RegexOptions.Compiled); foreach (string s in versions) { if (s.StartsWith("CA") || s.StartsWith("ca")) { QCountry = "CA"; } else if (s.StartsWith("UK") || s.StartsWith("uk")) { QCountry = "UK"; } //case 262 //strip out only numeric bit regardless of what text is in there //including commas if it's french canadian and using a comma instead of a decimal point //(the safe to double will handle the comma if present) string strVersionNumber = rxVersion.Match(s).Value; vers = SafeToDouble(strVersionNumber); if (vers > QVersion) { QVersion = vers; } } if (QVersion < 6.0) { IntegrationLog.Log(QBID, "PFC: Failed, QuickBooks found is too old, 2008 or higher is required, prompted user to run QB update utility to be able to proceed"); CopyableMessageBox cp=new CopyableMessageBox( "You seem to be running QuickBooks older than 2008\r\n" + "You must update to 2008 or higher before you can use AyaNova QBI.\r\n\r\n"+ "(If you are running QuickBooks 2008 or higher and still getting this error, ensure that you are also using QBFC7 or higher)\r\n\r\n"+ "VERSION FOUND = " + QVersion ); cp.ShowDialog(); return pfstat.Failed; } //Get the company file to open QCompanyFile = sessionManager.GetCurrentCompanyFileName(); // if(QCountry=="US") // { //Get company data // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ICompanyQuery cq = requestSet.AppendCompanyQueryRq(); IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); IResponse response = responseSet.ResponseList.GetAt(0); if (response.StatusCode != 0) { IntegrationLog.Log(QBID, "PFC: Failed Company query:" + response.StatusMessage + ", " + response.StatusCode.ToString()); throw new ApplicationException("PFC: Failed Company query:" + response.StatusMessage + ", " + response.StatusCode.ToString()); } ICompanyRet cl = response.Detail as ICompanyRet; QCompanyName = ProcessQBString(cl.CompanyName); requestSet.ClearRequests(); //---------------- // } // else // QCompanyName=QCompanyFile; // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); return pfstat.OK; } catch (Exception ex) { //MessageBox.Show(ex.Message.ToString() + "\nStack Trace: \n" + ex.StackTrace + "\nExiting the application"); IntegrationLog.Log(QBID, "PFC: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion #region PFC Check if user settings are completed and valid //case 3268 public static string TRANSACTION_CLASS_NO_CLASS_SELECTED = ""; /// /// Validate the users preferences /// if any are missing or invalid prompt for them /// /// public static pfstat ValidateSettings(bool ForceReset) { bool SetEverything = false; if (ForceReset) SetEverything = true; //Display user friendly dialog //explaining that configuration needs to be set if (QDat.NothingSet) { SetEverything = true; MessageBox.Show( "AyaNova QBI has now connected sucessfully to both AyaNova and QuickBooks.\r\n\r\n" + "The next step is to set preferences for how AyaNova QBI will operate.\r\n\r\n" + "AyaNova QBI will now step through each setting and get your preference\r\n" + "in order to integrate AyaNova with QuickBooks.\r\n\r\n" + "These settings can be changed later.", "Setup wizard", MessageBoxButtons.OK, MessageBoxIcon.Information); } #region confirm company file ApproveCompanyFile s0 = new ApproveCompanyFile(); s0.QBCompanyName = QCompanyName.Replace("&", "&&"); s0.QBCompanyPath = QCompanyFile; if (s0.ShowDialog() == DialogResult.Cancel) { IntegrationLog.Log(QBID, "PFC: User cancelled when shown company file currently open - " + QCompanyFile); return pfstat.Cancel; } #endregion #region WO Pre status //Validate any existing status if (SetEverything == false && QDat.PreWOStatus != Guid.Empty) { if (WorkorderStatus.Exists(QDat.PreWOStatus)) goto PRESTATUSOK; } else { //Empty pre status is valid if not first //time setup as user can opt for selecting //workorders of any status if (SetEverything == false) goto PRESTATUSOK; } //We've arrived here because there is no valid setting for Pre workorder status //or it's the first time through and needs to be selected on way or another SetWOStatus s1 = new SetWOStatus(); s1.DialogTitle = "AyaNova QBI setup - Choose billable Workorder Status"; s1.OptionTitle = "Billable workorder status"; s1.OptionDescription = "One of AyaNova QBI's tasks is to look for work orders in AyaNova \r\n" + "that are ready to be billed out and put them in a list for your selection. \r\n" + " \r\n" + "By default QBI will consider work orders that are set to service completed \r\n" + "and are not closed as it's selection criteria. \r\n" + " \r\n" + "In addition, you can further refine the types of work orders that QBI \r\n" + "considers ready for billing by specifying here a particular workorder Status \r\n" + "you want to include in addition to the default criteria. "; s1.SelectedStatus = QDat.PreWOStatus; s1.PreStatus = true; if (s1.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.PreWOStatus = s1.SelectedStatus; PRESTATUSOK: #endregion #region WO POST status //Validate any existing status if (SetEverything == false && QDat.PostWOStatus != Guid.Empty) { if (WorkorderStatus.Exists(QDat.PostWOStatus)) goto POSTSTATUSOK; } else { //Empty post status is valid if not first //time setup if (SetEverything == false) goto POSTSTATUSOK; } //We've arrived here because there is no valid setting for POST workorder status //or it's the first time through and needs to be selected on way or another s1 = new SetWOStatus(); s1.DialogTitle = "AyaNova QBI setup - Choose post billed Workorder Status"; s1.OptionTitle = "Post billing workorder status"; s1.OptionDescription = "After QBI has billed out a work order, it can change the \r\n" + "work order status for you automatically if desired."; s1.SelectedStatus = QDat.PostWOStatus; s1.PreStatus = false; if (s1.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.PostWOStatus = s1.SelectedStatus; s1.Dispose(); s1 = null; POSTSTATUSOK: #endregion #region Outside service charge as //Validate any existing status if (SetEverything == false && QDat.OutsideServiceChargeAs != null && QDat.OutsideServiceChargeAs != "") { if (QBItems.Rows.Contains(QDat.OutsideServiceChargeAs)) goto OUTSIDESERVICECHARGEASOK; else { MessageBox.Show("The QuickBooks Item previously set for invoicing Outside Service items\r\n" + "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + "QuickBooks Item."); } } //We've arrived here because there is no valid setting for OutsideServiceChargeAs SetQBChargeAs s2 = new SetQBChargeAs(); s2.DialogTitle = "AyaNova QBI setup - Charge outside service as?"; s2.OptionTitle = "Outside service"; s2.OptionDescription = "QBI needs to know what QuickBooks Item you want \r\n" + "to use when invoicing the AyaNova \"Outside service\" portion of a work order.\r\n\r\n" + "Outside service is any 3rd party repair that is billable to the customer.\r\n\r\n" + "This setting is mandatory / required."; s2.QBItems = QBItems; s2.SelectedQBItem = QDat.OutsideServiceChargeAs; //s2.SelectedQBItem=QDat.PreWOStatus; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.OutsideServiceChargeAs = s2.SelectedQBItem; s2.Dispose(); s2 = null; OUTSIDESERVICECHARGEASOK: #endregion #region Misc expense charge as //Validate any existing if (SetEverything == false && QDat.MiscExpenseChargeAs != null && QDat.MiscExpenseChargeAs != "") { //if(QBItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto MISCCHARGEASOK; // else // { // MessageBox.Show("The QuickBooks Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "QuickBooks Item."); // } } //We've arrived here because there is no valid setting for Misc expense s2 = new SetQBChargeAs(); s2.DialogTitle = "AyaNova QBI setup - Charge Misc. Expense as?"; s2.OptionTitle = "Miscellaneous expenses"; s2.OptionDescription = "QBI needs to know what QuickBooks Item you want \r\n" + "to use when invoicing the AyaNova \"Miscellaneous expense\" portion of a work order.\r\n\r\n" + "This setting is mandatory / required."; s2.QBItems = QBItems; s2.SelectedQBItem = QDat.MiscExpenseChargeAs; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.MiscExpenseChargeAs = s2.SelectedQBItem; s2.Dispose(); s2 = null; MISCCHARGEASOK: #endregion #region Workorder item loan charge as //Validate any existing if (SetEverything == false && QDat.WorkorderItemLoanChargeAs != null && QDat.WorkorderItemLoanChargeAs != "") { //if(QBItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto LOANCHARGEASOK; // else // { // MessageBox.Show("The QuickBooks Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "QuickBooks Item."); // } } //We've arrived here because there is no valid setting for Misc expense s2 = new SetQBChargeAs(); s2.DialogTitle = "AyaNova QBI setup - Charge loan item as?"; s2.OptionTitle = "Work order loans"; s2.OptionDescription = "QBI needs to know what QuickBooks Item you want \r\n" + "to use when invoicing the AyaNova \"Workorder item loan\" portion of a work order.\r\n\r\n" + "This setting is mandatory / required."; s2.QBItems = QBItems; s2.SelectedQBItem = QDat.WorkorderItemLoanChargeAs; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.WorkorderItemLoanChargeAs = s2.SelectedQBItem; s2.Dispose(); s2 = null; LOANCHARGEASOK: #endregion #region QB Transaction class //Validate any existing if (SetEverything == false && QDat.TransactionClass != null && QDat.TransactionClass != "") { //if something is set but there are no tr classes //then just clear it and move along if (QBClasses.Rows.Count == 1) { QDat.TransactionClass = TRANSACTION_CLASS_NO_CLASS_SELECTED;//case 3268 goto TRCLASSOK; } //Something is set and there *are* tr classes so //let's validate it... if (QBClasses.Rows.Contains(QDat.TransactionClass)) goto TRCLASSOK; else { MessageBox.Show("The QuickBooks transaction class previously set for invoicing\r\n" + "no longer appears to be valid. You will next be prompted to re-select it."); } } //Perhaps there are no transaction classes, this is the default //if not then don't prompt for it obviously :) //also if it was empty and were not in first setup mode then //don't bother prompting it might be the users choice. //case 3228 commented out the following as it's a bad idea //if (QBClasses.Rows.Count == 1 || SetEverything == false) // goto TRCLASSOK; //We've arrived here because there is no setting for transaction classes //but there are some defined in QB SetQBClass s3 = new SetQBClass(); s3.DialogTitle = "AyaNova QBI setup - Transaction class"; s3.OptionTitle = "Transaction class"; s3.OptionDescription = "QBI needs to know what QuickBooks Transaction Class you want \r\n" + "to use when invoicing Work orders.\r\n\r\n" + "If you do not use transaction classes or are not sure what they are\r\n" + "select < Do not use classes> from the list below. Classes are off by default in QuickBooks.\r\n\r\n" + "This setting is Optional and not required."; s3.QBClasses = QBClasses; if (s3.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.TransactionClass = s3.SelectedQBClass; s3.Dispose(); s3 = null; TRCLASSOK: #endregion #region QB InvoiceTemplate //Templates are only supported in xml 3 or greater (all countries) //if Set everything (first run) then display a dialog about it if (QVersion < 3 && SetEverything == true) { SetInfoOnly s3a = new SetInfoOnly(); s3a.DialogTitle = "AyaNova QBI setup - Invoice template"; s3a.OptionTitle = "Invoice template - NOT SUPPORTED"; s3a.OptionDescription = "QBI can use a specific QuickBooks Invoice template for printing work orders.\r\n" + "However, your version of QuickBooks does not support integrating this \r\n" + "feature with 3rd party applications such as AyaNova QBI.\r\n" + "Supported versions of QuickBooks for using Invoice templates with QBI are:\r\n\r\n" + "U.S., Canadian or U.K. QuickBooks 2004 or newer\r\n\r\n" + "If you upgrade your QuickBooks in future you will be able to select this option\r\n" + "for now it is disabled and the default invoice template will be used"; s3a.ShowDialog(); goto TRInvoiceTemplateOK; } //Subsequent, non-setup, runs with unsupported version if (QVersion < 3) goto TRInvoiceTemplateOK; //Validate any existing if (QDat.QBInvoiceTemplate != null && QDat.QBInvoiceTemplate != "") { //if something is set but there are no InvoiceTemplates //then just clear it and move along if (QBInvoiceTemplates.Rows.Count == 1) { QDat.QBInvoiceTemplate = ""; goto TRInvoiceTemplateOK; } //Something is set and there *are* tr InvoiceTemplates so //let's validate it... if (QBInvoiceTemplates.Rows.Contains(QDat.QBInvoiceTemplate)) { if (!SetEverything) goto TRInvoiceTemplateOK; } else { MessageBox.Show("The QuickBooks Invoice Template previously set for invoicing\r\n" + "no longer appears to be valid. You will next be prompted to re-select it."); } } //Perhaps there are no InvoiceTemplates, this is the default //if not then don't prompt for it obviously :) //also if it was empty and were not in first setup mode then //don't bother prompting it might be the users choice. //todo: make something besides and empty string to indicate //deliberately non selected items if (QBInvoiceTemplates.Rows.Count == 1 || SetEverything == false) goto TRInvoiceTemplateOK; //We've arrived here because there is no setting for InvoiceTemplates //Or the user want's to change it //and there are some defined in QB SetQBInvoiceTemplate s3b = new SetQBInvoiceTemplate(); s3b.DialogTitle = "AyaNova QBI setup - Invoice template"; s3b.OptionTitle = "Invoice template"; s3b.OptionDescription = "QBI needs to know what QuickBooks Invoice template you want \r\n" + "QBI to set for invoices created from Work orders.\r\n\r\n" + "QuickBooks Invoice templates are used in QuickBooks to specify different print formats\r\n" + "for invoices. If you do not use Invoice templates or are not sure what they are\r\n" + "select < Use default > from the list below.\r\n\r\n" + "This setting is required."; s3b.QBInvoiceTemplates = QBInvoiceTemplates; s3b.SelectedQBInvoiceTemplate = QDat.QBInvoiceTemplate; if (s3b.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.QBInvoiceTemplate = s3b.SelectedQBInvoiceTemplate; s3b.Dispose(); s3b = null; TRInvoiceTemplateOK: #endregion #region QB Terms //Validate any existing //case 3228 added extra condition set everything is false if (SetEverything == false && !string.IsNullOrEmpty(QDat.TermsDefault)) { //if something is set but there are no terms //then just clear it and move along if (QBTerms.Rows.Count == 1) { QDat.TermsDefault = ""; goto TermsOK; } //Something is set and there *are* terms so //let's validate it... if (QBTerms.Rows.Contains(QDat.TermsDefault)) { if (!SetEverything) goto TermsOK; } else { MessageBox.Show("The QuickBooks default terms previously set for invoicing\r\n" + "no longer appears to be valid. You will next be prompted to re-select it."); } } //We've arrived here because there is no setting for Terms //Or the user want's to change it //and there are some defined in QB SetQBTerms termsdialog = new SetQBTerms(); termsdialog.DialogTitle = "AyaNova QBI setup - Customer default invoice terms"; termsdialog.OptionTitle = "Default terms"; termsdialog.OptionDescription = "QBI needs to know what QuickBooks terms you want \r\n" + "QBI to set for customers imported from AyaNova.\r\n\r\n" + "When an invoice for a customer is created the selected terms will be applied.\r\n\r\n" + "This setting is required."; termsdialog.QBTerms = QBTerms; termsdialog.SelectedQBTerm = QDat.TermsDefault; if (termsdialog.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.TermsDefault = termsdialog.SelectedQBTerm; termsdialog.Dispose(); termsdialog = null; TermsOK: #endregion #region ToBePrinted //No validation possible //so prompt only if not setup yet if (!SetEverything) { //if(QBItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto TBPOK; // else // { // MessageBox.Show("The QuickBooks Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "QuickBooks Item."); // } } SetToBePrinted s4 = new SetToBePrinted(); s4.DialogTitle = "AyaNova QBI setup - Set invoice to be printed?"; s4.OptionTitle = "Invoice to be printed"; s4.OptionDescription = "QBI needs to know if you want invoices that it creates \r\n" + "in QuickBooks to be set to \"To be printed\".\r\n\r\n" + "(Please note that \"To be emailed\" which is available in some\r\n" + "versions of QuickBooks is not an option at this time as\r\n" + "QuickBooks has not exposed that property to developers)"; s4.ToBePrinted = QDat.ToBePrinted; if (s4.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.ToBePrinted = s4.ToBePrinted; s4.Dispose(); s4 = null; TBPOK: #endregion #region SetMemoField //No validation possible //so prompt only if not setup yet if (!SetEverything) { //if(QBItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto SETMEMOOK; // else // { // MessageBox.Show("The QuickBooks Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "QuickBooks Item."); // } } SetMemoField s5 = new SetMemoField(); s5.DialogTitle = "AyaNova QBI setup - Set Memo field?"; s5.OptionTitle = "Invoice memo field"; s5.OptionDescription = "QBI needs to know if you want invoices that it creates \r\n" + "in QuickBooks to have their \"Memo\" field set with\r\n" + "information about the work order(s) that were the basis for\r\n" + "the invoice and the name of the AyaNova user who generated them.\r\n\r\n" + "This may be useful as a back reference, this setting is optional"; s5.FillMemoField = QDat.SetMemoField; if (s5.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.SetMemoField = s5.FillMemoField; s5.Dispose(); s5 = null; SETMEMOOK: #endregion #region SetAutoCloseField Case 7 //No validation possible //so prompt only if not setup yet if (!SetEverything) { goto SETAUTOCLOSEOK; } SetAutoClose s6 = new SetAutoClose(); s6.DialogTitle = "AyaNova QBI setup - Close when invoiced?"; s6.OptionTitle = "Close work order after invoicing"; s6.OptionDescription = "QBI needs to know if you want work orders that it invoices \r\n" + "automatically set to closed"; s6.AutoClose = QDat.AutoClose; if (s6.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else QDat.AutoClose = s6.AutoClose; s6.Dispose(); s6 = null; SETAUTOCLOSEOK: #endregion //Save if changes made if (QDat.IsDirty) { //Case 299 QBI.AIObject = QDat.XMLData; //QBI.AIObject=QDat; QBI = (Integration)QBI.Save(); QDat.IsDirty = false; } return pfstat.OK; } #endregion #endregion pfc #region AyaNova cached lists public static void PopulateAyaListCache() { //Get the cached QB data Waiting w = new Waiting(); w.Show(); w.Ops = "Reading from AyaNova..."; w.Step = "Clients"; PopulateAyaClientList(); w.Step = "Vendors"; PopulateAyaVendorList(); w.Step = "Rates"; PopulateAyaRateList(); w.Step = "Parts"; PopulateAyaPartList(); w.Close(); } #region AyaNova clients private static ClientPickList _clientlist = null; /// /// AyaNova ClientPickList /// public static ClientPickList AyaClientList { get { return _clientlist; } } public static void PopulateAyaClientList() { _clientlist = ClientPickList.GetList(); } #endregion ayanova clients #region AyaNova Vendors private static VendorPickList _vendorlist = null; /// /// AyaNova vendor list /// public static VendorPickList AyaVendorList { get { return _vendorlist; } } public static void PopulateAyaVendorList() { _vendorlist = VendorPickList.GetList(); } #endregion ayanova vendors #region AyaNova Rates private static RatePickList _ratelist = null; /// /// AyaNova rate list /// public static RatePickList AyaRateList { get { return _ratelist; } } public static void PopulateAyaRateList() { _ratelist = RatePickList.GetListAllActiveRates(); } #endregion ayanova rates #region AyaNova Parts private static PartPickList _partlist = null; /// /// AyaNova part list /// public static PartPickList AyaPartList { get { return _partlist; } } public static void PopulateAyaPartList() { _partlist = PartPickList.GetAllParts(); } #endregion ayanova parts #endregion #region QB API helper methods/ attributes/cached lists /// /// Populate or repopulate the list of /// public static void PopulateQBListCache() { //Get the cached QB data Waiting w = new Waiting(); w.Show(); w.Ops = "Reading from QuickBooks..."; w.Step = "Classes"; PopulateQBClassCache(); w.Step = "Vendors"; PopulateQBVendorCache(); w.Step = "Customers"; PopulateQBClientCache(); w.Step = "Items"; PopulateQBItemCache(); if (!(QVersion < 3))//qbXML 3.0 or higher (QB 2004 any country or newer) { w.Step = "Invoice templates"; PopulateQBInvoiceTemplates(); } //case 632 w.Step = "Accounts"; PopulateQBAccountCache(); //case 519 w.Step = "Terms"; PopulateQBTermsCache(); w.Close(); } #region QuickBooks "items" public enum qbitemtype { Inventory, NonInventory, Service, OtherCharge, Assembly } private static DataTable _dtQBItems = null; /// /// qb items /// public static DataTable QBItems { get { return _dtQBItems; } } /// /// Given a QB Item ID, return the /// AyaNova Vendor ID linked to that items /// QB preferred Vendor ID or /// Guid empty on any problem or not found /// /// /// public static Guid AyaVendorForQBItem(string QBItemID) { if (QBItemID == null || QBItemID == "") return Guid.Empty; DataRow dr = _dtQBItems.Rows.Find(QBItemID); if (dr == null || dr["VendorID"] == null || dr["VendorID"].ToString() == "") return Guid.Empty; DataRow drVendor = _dtQBVendors.Rows.Find(dr["VendorID"].ToString()); if (drVendor == null) return Guid.Empty; if (!QBI.Maps.Contains(drVendor["ID"].ToString(), RootObjectTypes.Vendor)) return Guid.Empty; //Ok we have a matching vendor in the list, return the guid of it return QBI.Maps[drVendor["ID"].ToString(), RootObjectTypes.Vendor].RootObjectID; } /// /// Populate the cached qb data /// billable /// private static void PopulateQBItemCache() { if (_dtQBItems == null) { _dtQBItems = new DataTable("QBItems"); //setup the columns _dtQBItems.Columns.Add("ID", typeof(string)); _dtQBItems.Columns.Add("FullName", typeof(string)); _dtQBItems.Columns.Add("Type", typeof(qbitemtype)); _dtQBItems.Columns.Add("Modified", typeof(DateTime)); _dtQBItems.Columns.Add("Price", typeof(decimal)); _dtQBItems.Columns.Add("Cost", typeof(decimal)); _dtQBItems.Columns.Add("SalesDesc", typeof(string)); _dtQBItems.Columns.Add("ReorderPoint", typeof(decimal)); _dtQBItems.Columns.Add("VendorID", typeof(string)); _dtQBItems.PrimaryKey = new DataColumn[] { _dtQBItems.Columns[0] }; //Case 237 _dtQBItems.DefaultView.Sort = "FullName asc"; } else _dtQBItems.Clear(); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IItemQuery ItemQ = requestSet.AppendItemQueryRq(); //Active items only ItemQ.ORListQuery.ListFilter.ActiveStatus.SetValue(ENActiveStatus.asActiveOnly); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //Added: 21-June-2006 nonzero status codes 500 and higher are serious errors, less than 500 could just mean no matching items in list if (response.StatusCode > 499) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } //Added: 21-June-2006 outer if to avoid crash on no match response (code 1) if (response.StatusCode == 0) { IORItemRetList orItemRetList = response.Detail as IORItemRetList; if (!(orItemRetList.Count == 0)) { for (int ndx = 0; ndx <= (orItemRetList.Count - 1); ndx++) { IORItemRet orItemRet = orItemRetList.GetAt(ndx); // IY: The ortype property returns an enum // of the elements that can be contained in the OR object switch (orItemRet.ortype) { case ENORItemRet.orirItemServiceRet: { // orir prefix comes from OR + Item + Ret IItemServiceRet ItemServiceRet = orItemRet.ItemServiceRet; IORSalesPurchase sp = ItemServiceRet.ORSalesPurchase; _dtQBItems.Rows.Add(new object[] { ItemServiceRet.ListID.GetValue(), ItemServiceRet.FullName.GetValue(), qbitemtype.Service, ItemServiceRet.TimeModified.GetValue(), PriceGrabber(sp), CostGrabber(sp), SalesDescGrabber(sp), 0, PrefVendorGrabber(sp) }); } break; case ENORItemRet.orirItemInventoryRet: { IItemInventoryRet ItemInventoryRet = orItemRet.ItemInventoryRet; _dtQBItems.Rows.Add(new object[] { ItemInventoryRet.ListID.GetValue(), ItemInventoryRet.FullName.GetValue(), qbitemtype.Inventory, ItemInventoryRet.TimeModified.GetValue(), PriceGrabber(ItemInventoryRet.SalesPrice), PriceGrabber(ItemInventoryRet.PurchaseCost), ProcessQBString(ItemInventoryRet.SalesDesc), QuanGrabber(ItemInventoryRet.ReorderPoint), BaseRefIDGrabber(ItemInventoryRet.PrefVendorRef) }); } break; case ENORItemRet.orirItemNonInventoryRet: { IItemNonInventoryRet ItemNonInventoryRet = orItemRet.ItemNonInventoryRet; _dtQBItems.Rows.Add(new object[] { ItemNonInventoryRet.ListID.GetValue(), ItemNonInventoryRet.FullName.GetValue(), qbitemtype.NonInventory, ItemNonInventoryRet.TimeModified.GetValue(), PriceGrabber(ItemNonInventoryRet.ORSalesPurchase), CostGrabber(ItemNonInventoryRet.ORSalesPurchase), SalesDescGrabber(ItemNonInventoryRet.ORSalesPurchase), 0, PrefVendorGrabber(ItemNonInventoryRet.ORSalesPurchase) }); } break; case ENORItemRet.orirItemOtherChargeRet: { IItemOtherChargeRet ItemOtherChargeRet = orItemRet.ItemOtherChargeRet; _dtQBItems.Rows.Add(new object[] { ItemOtherChargeRet.ListID.GetValue(), ItemOtherChargeRet.FullName.GetValue(), qbitemtype.OtherCharge, ItemOtherChargeRet.TimeModified.GetValue(), PriceGrabber(ItemOtherChargeRet.ORSalesPurchase), CostGrabber(ItemOtherChargeRet.ORSalesPurchase), SalesDescGrabber(ItemOtherChargeRet.ORSalesPurchase), 0, PrefVendorGrabber(ItemOtherChargeRet.ORSalesPurchase) }); } break; case ENORItemRet.orirItemInventoryAssemblyRet: { IItemInventoryAssemblyRet ItemInventoryAssemblyRet = orItemRet.ItemInventoryAssemblyRet; _dtQBItems.Rows.Add(new object[] { ItemInventoryAssemblyRet.ListID.GetValue(), ItemInventoryAssemblyRet.FullName.GetValue(), qbitemtype.Assembly, ItemInventoryAssemblyRet.TimeModified.GetValue(), PriceGrabber(ItemInventoryAssemblyRet.SalesPrice), PriceGrabber(ItemInventoryAssemblyRet.PurchaseCost), ProcessQBString(ItemInventoryAssemblyRet.SalesDesc), QuanGrabber(ItemInventoryAssemblyRet.BuildPoint), BaseRefIDGrabber(ItemInventoryAssemblyRet.PrefVendorRef) }); } break; } } // for loop } // if } // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBItems: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } /// /// Given a qb BaseRef object /// return the ListID or empty string if null /// /// /// private static string BaseRefIDGrabber(IQBBaseRef b) { if (b != null && b.ListID != null) { return b.ListID.GetValue(); } return ""; } /// /// Given a qb Pricetype object /// return the price or zero on any issue /// /// /// private static decimal PriceGrabber(IQBPriceType p) { decimal d = 0;//default if (p != null) { d = System.Convert.ToDecimal(p.GetValue()); } return d; } /// /// Given a qb IQBQuanType object /// return the value as decimal or zero on any issue /// /// /// private static decimal QuanGrabber(IQBQuanType p) { decimal d = 0;//default if (p != null) { d = System.Convert.ToDecimal(p.GetValue()); } return d; } /// /// Given a qb salespurchase object /// return the price /// /// /// private static decimal PriceGrabber(IORSalesPurchase sp) { decimal d = 0;//default decimal d1 = 0; decimal d2 = 0; if (sp != null) { if (sp.SalesOrPurchase != null && sp.SalesOrPurchase.ORPrice != null && sp.SalesOrPurchase.ORPrice.Price != null) { d1 = System.Convert.ToDecimal(sp.SalesOrPurchase.ORPrice.Price.GetValue()); } if (sp.SalesAndPurchase != null && sp.SalesAndPurchase.SalesPrice != null) { d2 = System.Convert.ToDecimal(sp.SalesAndPurchase.SalesPrice.GetValue()); } //get the highest price of the two if (d1 > d2) d = d1; else d = d2; } return d; } /// /// Given a qb salespurchase object /// return the purchase cose /// /// /// private static decimal CostGrabber(IORSalesPurchase sp) { decimal d = 0;//default if (sp != null) { if (sp.SalesAndPurchase != null && sp.SalesAndPurchase.PurchaseCost != null) { d = System.Convert.ToDecimal(sp.SalesAndPurchase.PurchaseCost.GetValue()); } } return d; } /// /// return the sales description if available /// /// /// private static string SalesDescGrabber(IORSalesPurchase sp) { string str = ""; if (sp != null) { if (sp.SalesOrPurchase != null && sp.SalesOrPurchase.Desc != null) { str = sp.SalesOrPurchase.Desc.GetValue(); } if (sp.SalesAndPurchase != null && sp.SalesAndPurchase.SalesDesc != null) { str = sp.SalesAndPurchase.SalesDesc.GetValue(); } } return str; } /// /// return the preferred vendor if available /// /// /// private static string PrefVendorGrabber(IORSalesPurchase sp) { string str = ""; if (sp != null) { if (sp.SalesAndPurchase != null && sp.SalesAndPurchase.PrefVendorRef != null && sp.SalesAndPurchase.PrefVendorRef.ListID != null) { str = sp.SalesAndPurchase.PrefVendorRef.ListID.GetValue(); } } return str; } #endregion quickbooks items #region QuickBooks "Transactionclasses" private static DataTable _dtQBClasses = null; /// /// QB Transaction Classes /// public static DataTable QBClasses { get { return _dtQBClasses; } } /// /// Populate the cached qb data /// billable /// private static void PopulateQBClassCache() { if (_dtQBClasses == null) { _dtQBClasses = new DataTable("QBClasses"); //setup the columns _dtQBClasses.Columns.Add("ID", typeof(string)); _dtQBClasses.Columns.Add("FullName", typeof(string)); _dtQBClasses.PrimaryKey = new DataColumn[] { _dtQBClasses.Columns[0] }; //Case 237 _dtQBClasses.DefaultView.Sort = "FullName asc"; } else _dtQBClasses.Clear(); //case 3268 _dtQBClasses.Rows.Add(new object[] { TRANSACTION_CLASS_NO_CLASS_SELECTED, "< Do not use classes >" }); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IClassQuery cq = requestSet.AppendClassQueryRq(); //transactions cq.ORListQuery.ListFilter.ActiveStatus.SetValue(ENActiveStatus.asActiveOnly); //This is intended to be called in a secondary thread immediately after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error, likely no classes defined if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); IClassRetList cl = response.Detail as IClassRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { IClassRet clitem = cl.GetAt(ndx); if (clitem != null && clitem.FullName != null && clitem.ListID != null) { //add a record to the datatable _dtQBClasses.Rows.Add(new object[] { clitem.ListID.GetValue(), clitem.FullName.GetValue() }); } } // for loop } // if }//if status ==0 nonzero means some error, probably no classes defined // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBClasses: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion quickbooks transaction classes #region QuickBooks Invoice Templates private static DataTable _dtQBInvoiceTemplates = null; /// /// QB Transaction Templates /// public static DataTable QBInvoiceTemplates { get { return _dtQBInvoiceTemplates; } } /// /// Populate the cached qb data /// Invoice templates /// private static void PopulateQBInvoiceTemplates() { if (_dtQBInvoiceTemplates == null) { _dtQBInvoiceTemplates = new DataTable("QBInvoiceTemplates"); //setup the columns _dtQBInvoiceTemplates.Columns.Add("ID", typeof(string)); _dtQBInvoiceTemplates.Columns.Add("FullName", typeof(string)); _dtQBInvoiceTemplates.PrimaryKey = new DataColumn[] { _dtQBInvoiceTemplates.Columns[0] }; //Case 237 _dtQBInvoiceTemplates.DefaultView.Sort = "FullName asc"; } else _dtQBInvoiceTemplates.Clear(); _dtQBInvoiceTemplates.Rows.Add(new object[] { "", "< Use default >" }); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ITemplateQuery cq = requestSet.AppendTemplateQueryRq(); //This is intended to be called in a secondary thread immediately after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //sLastRequestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error, likely no templates defined if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); ITemplateRetList cl = response.Detail as ITemplateRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { try { ITemplateRet clitem = cl.GetAt(ndx); bool b = clitem.IsActive.GetValue(); b = clitem.TemplateType == null; if (clitem != null && clitem.Name != null && clitem.ListID != null && clitem.TemplateType.IsSet() && clitem.TemplateType.GetValue() == ENTemplateType.tttInvoice) { //add a record to the datatable _dtQBInvoiceTemplates.Rows.Add(new object[] { clitem.ListID.GetValue(), clitem.Name.GetValue() }); } } catch (System.Runtime.InteropServices.COMException ex)//QBFC7 - throwing this exception here on last item in list can't pin it down { string s = ex.ToString(); } } // for loop } // if }//if status ==0 nonzero means some error, probably no classes defined // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBInvoiceTemplates: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion quickbooks Templates #region QuickBooks "Customers" private static string GetQBCustomerEditSequence(string customerid) { string strEditSequence = ""; //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ICustomerQuery cq = requestSet.AppendCustomerQueryRq(); cq.IncludeRetElementList.Add("EditSequence"); cq.ORCustomerListQuery.ListIDList.Add(customerid); sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception //Changed: 21-June-2006 nonzero status codes 500 and higher are serious errors, less than 500 could just mean no matching items in list if (response.StatusCode > 499) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } //Added: 21-June-2006 outer if to avoid crash on no match response (code 1) if (response.StatusCode == 0) { ICustomerRetList cl = response.Detail as ICustomerRetList; if (!(cl.Count == 0)) { ICustomerRet clitem = cl.GetAt(0); if (clitem != null) strEditSequence = clitem.EditSequence.GetValue(); } } // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "GetClientEditSequence: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } return strEditSequence; } private static DataTable _dtQBClients = null; /// /// QB Transaction Clients /// public static DataTable QBClients { get { return _dtQBClients; } } #region Address structure /// /// Address properties /// public struct Address { public string DeliveryAddress; public string City; public string StateProv; public string Country; public string Postal; } #endregion /// /// Populate the cached qb data /// of customers / clients /// private static void PopulateQBClientCache() { if (_dtQBClients == null) { _dtQBClients = new DataTable("QBClients"); //setup the columns _dtQBClients.Columns.Add("ID", typeof(string)); _dtQBClients.Columns.Add("FullName", typeof(string)); _dtQBClients.Columns.Add("MailAddress", typeof(Address)); _dtQBClients.Columns.Add("StreetAddress", typeof(Address)); _dtQBClients.Columns.Add("Phone", typeof(string)); _dtQBClients.Columns.Add("Fax", typeof(string)); _dtQBClients.Columns.Add("AltPhone", typeof(string)); _dtQBClients.Columns.Add("Email", typeof(string)); _dtQBClients.Columns.Add("Contact", typeof(string)); _dtQBClients.Columns.Add("Created", typeof(DateTime)); _dtQBClients.Columns.Add("Modified", typeof(DateTime)); _dtQBClients.Columns.Add("Account", typeof(string)); _dtQBClients.PrimaryKey = new DataColumn[] { _dtQBClients.Columns[0] }; //Case 237 _dtQBClients.DefaultView.Sort = "FullName asc"; } else _dtQBClients.Clear(); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ICustomerQuery cq = requestSet.AppendCustomerQueryRq(); //Active Customers only cq.ORCustomerListQuery.CustomerListFilter.ActiveStatus.SetValue(ENActiveStatus.asActiveOnly); //case 1664 //not sure why these were ever in here to be honest //whups, it's *necessary* see case 1100 //there is a bug in qbfc7 which blows if there are "special" characters //it was fixed later but we use qbfc7 currently cq.ORCustomerListQuery.CustomerListFilter.ORNameFilter.NameRangeFilter.FromName.SetValue("0"); cq.ORCustomerListQuery.CustomerListFilter.ORNameFilter.NameRangeFilter.ToName.SetValue("ZZ"); sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML // string requestXML = requestSet.ToXMLString(); // MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); // MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception //Changed: 21-June-2006 nonzero status codes 500 and higher are serious errors, less than 500 could just mean no matching items in list if (response.StatusCode > 499) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } //Added: 21-June-2006 outer if to avoid crash on no match response (code 1) if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); ICustomerRetList cl = response.Detail as ICustomerRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { ICustomerRet clitem = cl.GetAt(ndx); if (clitem != null) { //add a record to the datatable DataRow dr = _dtQBClients.Rows.Add( new object[]{ clitem.ListID.GetValue(), clitem.FullName.GetValue(), ProcessAddress(clitem.BillAddress), ProcessAddress(clitem.ShipAddress), ProcessQBString(clitem.Phone), ProcessQBString(clitem.Fax), ProcessQBString(clitem.AltPhone), ProcessQBString(clitem.Email), ProcessQBString(clitem.Contact), clitem.TimeCreated.GetValue(), clitem.TimeModified.GetValue(), ProcessQBString(clitem.AccountNumber), }); } } // for loop } // if } // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBClients: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } /// /// Take a qb address and return an AyaNova friendly /// address structure /// /// /// private static Address ProcessAddress(IAddress a) { Address b = new Address(); b.City = ""; b.Country = ""; b.DeliveryAddress = ""; b.Postal = ""; b.StateProv = ""; if (a == null) return b; //Append each line of the address, add cr/lf for each line if present after //the first line //Assumption: First line always has *something* in it b.DeliveryAddress = ProcessQBString(a.Addr1); b.DeliveryAddress += AyaBizUtils.SS("\r\n", ProcessQBString(a.Addr2), ""); b.DeliveryAddress += AyaBizUtils.SS("\r\n", ProcessQBString(a.Addr3), ""); //Address line 4 is a qbxml 2 or higher feature if (QVersion > 1.1) b.DeliveryAddress += AyaBizUtils.SS("\r\n", ProcessQBString(a.Addr4), ""); //Country specific: b.City = ProcessQBString(a.City); //QBFC7 unifies county and province to "state" b.StateProv = ProcessQBString(a.State); //switch(QCountry) //{ // case "CA": // b.StateProv=ProcessQBString(a.Province); // break; // case "UK": // b.StateProv=ProcessQBString(a.County); // break; // default: // b.StateProv=ProcessQBString(a.State); // break; //} b.Country = ProcessQBString(a.Country); b.Postal = ProcessQBString(a.PostalCode); return b; } /// /// Handle null qb string types with "aplomb" :) /// /// /// private static string ProcessQBString(IQBStringType qs) { if (qs == null) return ""; return qs.GetValue(); } #endregion quickbooks transaction Clients #region QuickBooks "Vendors" private static DataTable _dtQBVendors = null; /// /// QB Vendors /// public static DataTable QBVendors { get { return _dtQBVendors; } } /// /// Populate the cached qb data /// of Vendors /// private static void PopulateQBVendorCache() { if (_dtQBVendors == null) { _dtQBVendors = new DataTable("QBVendors"); //setup the columns _dtQBVendors.Columns.Add("ID", typeof(string)); _dtQBVendors.Columns.Add("FullName", typeof(string)); _dtQBVendors.Columns.Add("MailAddress", typeof(Address)); _dtQBVendors.Columns.Add("StreetAddress", typeof(Address)); _dtQBVendors.Columns.Add("Phone", typeof(string)); _dtQBVendors.Columns.Add("Fax", typeof(string)); _dtQBVendors.Columns.Add("AltPhone", typeof(string)); _dtQBVendors.Columns.Add("Email", typeof(string)); _dtQBVendors.Columns.Add("Contact", typeof(string)); _dtQBVendors.Columns.Add("Created", typeof(DateTime)); _dtQBVendors.Columns.Add("Modified", typeof(DateTime)); _dtQBVendors.Columns.Add("Account", typeof(string)); _dtQBVendors.PrimaryKey = new DataColumn[] { _dtQBVendors.Columns[0] }; //Case 237 _dtQBVendors.DefaultView.Sort = "FullName asc"; } else _dtQBVendors.Clear(); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IVendorQuery cq = requestSet.AppendVendorQueryRq(); //Active Vendors only cq.ORVendorListQuery.VendorListFilter.ActiveStatus.SetValue(ENActiveStatus.asActiveOnly); sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error; this is unrecoverable //so throw an exception //Changed: 21-June-2006 nonzero status codes 500 and higher are serious errors, less than 500 could just mean no matching items in list if (response.StatusCode > 499) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } //Added: 21-June-2006 outer if to avoid crash on no match response (code 1) if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); IVendorRetList cl = response.Detail as IVendorRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { IVendorRet clitem = cl.GetAt(ndx); if (clitem != null) { //add a record to the datatable DataRow dr = _dtQBVendors.Rows.Add( new object[] { clitem.ListID.GetValue(), clitem.Name.GetValue(), ProcessAddress(clitem.VendorAddress), ProcessAddress(clitem.VendorAddress), ProcessQBString(clitem.Phone), ProcessQBString(clitem.Fax), ProcessQBString(clitem.AltPhone), ProcessQBString(clitem.Email), ProcessQBString(clitem.Contact), clitem.TimeCreated.GetValue(), clitem.TimeModified.GetValue(), ProcessQBString(clitem.AccountNumber) }); } } // for loop } // if } // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBVendors: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion quickbooks Vendors #region technical mumbo jumbo /// /// /// /// /// public static IMsgSetRequest getLatestMsgSetRequest(QBSessionManager sessionManager) { // Find and adapt to supported version of QuickBooks short qbXMLMajorVer = 0; short qbXMLMinorVer = 0; if (QVersion >= 5.0) { qbXMLMajorVer = 5; qbXMLMinorVer = 0; } else if (QVersion >= 4.0) { qbXMLMajorVer = 4; qbXMLMinorVer = 0; } else if (QVersion >= 3.0) { qbXMLMajorVer = 3; qbXMLMinorVer = 0; } else if (QVersion >= 2.0) { qbXMLMajorVer = 2; qbXMLMinorVer = 0; } else if (QVersion >= 1.1) { qbXMLMajorVer = 1; qbXMLMinorVer = 1; } else { qbXMLMajorVer = 1; qbXMLMinorVer = 0; throw new System.NotSupportedException("QuickBooks 1.0 (2002 initial release) is not supported, use QuickBooks online update feature now."); } // Create the message set request object IMsgSetRequest requestMsgSet = sessionManager.CreateMsgSetRequest(QCountry, qbXMLMajorVer, qbXMLMinorVer); return requestMsgSet; } #endregion tech mumbo jumbo #region QuickBooks "accounts" private static DataTable _dtQBAccounts = null; /// /// QB Transaction Classes /// public static DataTable QBAccounts { get { return _dtQBAccounts; } } /// /// Populate the cached qb account list data /// private static void PopulateQBAccountCache() { if (_dtQBAccounts == null) { _dtQBAccounts = new DataTable("QBAccounts"); //setup the columns _dtQBAccounts.Columns.Add("ID", typeof(string)); _dtQBAccounts.Columns.Add("FullName", typeof(string)); _dtQBAccounts.Columns.Add("Type", typeof(string)); _dtQBAccounts.PrimaryKey = new DataColumn[] { _dtQBAccounts.Columns[0] }; //Case 237 _dtQBAccounts.DefaultView.Sort = "FullName asc"; } else _dtQBAccounts.Clear(); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IAccountQuery cq = requestSet.AppendAccountQueryRq(); //accounts (active ones only) cq.ORAccountListQuery.AccountListFilter.ActiveStatus.SetValue(ENActiveStatus.asActiveOnly); //This is intended to be called in a secondary thread immediately after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error, likely no classes defined if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); IAccountRetList cl = response.Detail as IAccountRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { IAccountRet clitem = cl.GetAt(ndx); if (clitem != null && clitem.FullName != null && clitem.ListID != null) { //add a record to the datatable _dtQBAccounts.Rows.Add(new object[] { clitem.ListID.GetValue(), clitem.AccountType.GetAsString() + " - " + clitem.FullName.GetValue(), clitem.AccountType.GetAsString() }); } } // for loop } // if }//if status ==0 nonzero means some error, probably no classes defined // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBAccounts: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion quickbooks accounts #region QuickBooks "Terms" private static DataTable _dtQBTerms = null; /// /// QB Transaction Classes /// public static DataTable QBTerms { get { return _dtQBTerms; } } /// /// Populate the cached qb terms list data /// private static void PopulateQBTermsCache() { if (_dtQBTerms == null) { _dtQBTerms = new DataTable("QBTerms"); //setup the columns _dtQBTerms.Columns.Add("ID", typeof(string)); _dtQBTerms.Columns.Add("FullName", typeof(string)); _dtQBTerms.PrimaryKey = new DataColumn[] { _dtQBTerms.Columns[0] }; //Case 237 _dtQBTerms.DefaultView.Sort = "FullName asc"; } else _dtQBTerms.Clear(); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ITermsQuery cq = requestSet.AppendTermsQueryRq(); //This is intended to be called in a secondary thread immediately after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error, likely no classes defined if (response.StatusCode == 0) { //int statusCode = response.StatusCode; //string statusMessage = response.StatusMessage; //string statusSeverity = response.StatusSeverity; //MessageBox.Show("Status:\nCode = " + statusCode + "\nMessage = " + statusMessage + "\nSeverity = " + statusSeverity); IORTermsRetList cl = response.Detail as IORTermsRetList; if (!(cl.Count == 0)) { for (int ndx = 0; ndx <= (cl.Count - 1); ndx++) { IORTermsRet clitem = cl.GetAt(ndx); if (clitem != null) { //add a record to the datatable if (clitem.StandardTermsRet != null && clitem.StandardTermsRet.ListID.IsSet()) _dtQBTerms.Rows.Add(new object[] { clitem.StandardTermsRet.ListID.GetValue(), clitem.StandardTermsRet.Name.GetValue() }); else _dtQBTerms.Rows.Add(new object[] { clitem.DateDrivenTermsRet.ListID.GetValue(), clitem.DateDrivenTermsRet.Name.GetValue() }); } } // for loop } // if }//if status ==0 nonzero means some error, probably no classes defined // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBTerms: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion quickbooks Terms #endregion #region Import Export/refresh #region Import / refresh to AyaNova #region Import / refresh customer #region Refresh Customer public static void RefreshAyaNovaClientFromQB(List objectIDList) { PopulateQBClientCache(); foreach (Guid g in objectIDList) { try { Client c = Client.GetItemNoMRU(g); RefreshAyaNovaClientFromQB(c); if (c.IsSavable) c.Save(); } catch { }; } } public static void RefreshAyaNovaClientFromQB(Client c) { PopulateQBClientCache(); IntegrationMap im = QBI.Maps[c.ID]; if (im == null) return;//this client is not linked DataRow dr = _dtQBClients.Rows.Find(im.ForeignID); if (dr == null) return; //QBListID not found in client list? CopyQBCustomerInfoToAyaNovaClient(dr, c); string sName = dr["FullName"].ToString(); if (sName.Length > 255) sName = sName.Substring(0, 255); c.Name = sName; } #endregion refresh customer #region Import customer /// /// Import the indicated customer /// to an AyaNova client record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportQBCustomer(string QuickBooksID, ArrayList alErrors) { DataRow dr = _dtQBClients.Rows.Find(QuickBooksID); //QBListID not found in client list? if (dr == null) { alErrors.Add("ImportQBCustomer: ID not found " + QuickBooksID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportQBCustomer: QuickBooks customer name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a client by that name if (Client.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportQBCustomer: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Client c = Client.NewItem(); c.Name = sName;//1 CopyQBCustomerInfoToAyaNovaClient(dr, c); if (!c.IsSavable) { alErrors.Add("ImportQBCustomer: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } c = (Client)c.Save(); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Client; m.LastSync = DateTime.Now; m.ForeignID = QuickBooksID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind alErrors.Add("ImportQBCustomer: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + CrackException(ex)); } } #endregion Import customer #region Copy QB Customer info to AyaNova client public static void CopyQBCustomerInfoToAyaNovaClient(DataRow dr, Client c) { Address a = (Address)dr["MailAddress"]; c.MailToAddress.DeliveryAddress = a.DeliveryAddress;//2 c.MailToAddress.City = a.City;//3 c.MailToAddress.StateProv = a.StateProv;//4 c.MailToAddress.Country = a.Country;//5 c.MailToAddress.Postal = a.Postal;//6 a = (Address)dr["StreetAddress"]; c.GoToAddress.DeliveryAddress = a.DeliveryAddress;//7 c.GoToAddress.City = a.City;//8 c.GoToAddress.StateProv = a.StateProv;//9 c.GoToAddress.Country = a.Country;//10 c.GoToAddress.Postal = a.Postal;//11 //Case 518 c.PopulateBothAddresses(); //Contact cn=c.Contacts.Add(RootObjectTypes.Client,c.ID); c.Contact = dr["Contact"].ToString(); //Phone field if (dr["Phone"].ToString() != "") { c.Phone1 = dr["Phone"].ToString(); } //Fax field if (dr["Fax"].ToString() != "") { c.Phone2 = dr["Fax"].ToString();//15 } //AltPhone field if (dr["AltPhone"].ToString() != "") { c.Phone3 = dr["AltPhone"].ToString();//16 } //Email field if (dr["Email"].ToString() != "") { c.Email = dr["Email"].ToString();//17 } //Account number field if (dr["Account"].ToString() != "") { c.AccountNumber = dr["Account"].ToString();//18 } } #endregion copy qb customer info to aya client #endregion import refresh customer #region Vendor /// /// Import the indicated Vendor /// to an AyaNova vendor record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportQBVendor(string QuickBooksID, VendorTypes AsVendorType, ArrayList alErrors) { DataRow dr = _dtQBVendors.Rows.Find(QuickBooksID); //QBListID not found in Vendor list? if (dr == null) { alErrors.Add("ImportQBVendor: ID not found " + QuickBooksID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportQBVendor: QuickBooks Vendor name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a Vendor by that name if (Vendor.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportQBVendor: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Vendor c = Vendor.NewItem(); c.VendorType = AsVendorType; c.Name = sName; Address a = (Address)dr["MailAddress"]; c.MailToAddress.DeliveryAddress = a.DeliveryAddress; c.MailToAddress.City = a.City; c.MailToAddress.StateProv = a.StateProv; c.MailToAddress.Country = a.Country; c.MailToAddress.Postal = a.Postal; a = (Address)dr["StreetAddress"]; c.GoToAddress.DeliveryAddress = a.DeliveryAddress; c.GoToAddress.City = a.City; c.GoToAddress.StateProv = a.StateProv; c.GoToAddress.Country = a.Country; c.GoToAddress.Postal = a.Postal; //Contact cn=c.Contacts.Add(RootObjectTypes.Vendor,c.ID); c.Contact = dr["Contact"].ToString(); ////if it's completely empty we'll substitute the word Contact instead //if(qbcontact=="") // cn.FirstName="Contact"; //else //{ // //default it first // cn.FirstName=qbcontact; // string [] contactnames=null; // if(qbcontact.IndexOf(" ")!=-1) // contactnames=qbcontact.Replace(",","").Split(' ');//replace any commas if present // else // if(qbcontact.IndexOf(",")!=-1) // contactnames=qbcontact.Split(','); // //Quickbooks just has one field for contact so // //we'll assume the english speaking tradition of firstname space lastname // //if there is a space otherwise we'll assume it's just a first name if there are no spaces in it // if(contactnames!=null && contactnames.GetLength(0)>1) // { // cn.FirstName=contactnames[0]; // cn.LastName=contactnames[1]; // } //} //Phone field if (dr["Phone"].ToString() != "") { //ContactPhone cp=cn.Phones.Add(cn); //cp.ContactPhoneType=ContactPhoneTypes.Business; c.Phone1 = dr["Phone"].ToString(); //case 124 //cp.PhoneDefault=true; } //Fax field if (dr["Fax"].ToString() != "") { //ContactPhone cp=cn.Phones.Add(cn); //cp.ContactPhoneType=ContactPhoneTypes.Fax; c.Phone2 = dr["Fax"].ToString(); } //AltPhone field if (dr["AltPhone"].ToString() != "") { //ContactPhone cp=cn.Phones.Add(cn); //cp.ContactPhoneType=ContactPhoneTypes.Business; c.Phone3 = dr["AltPhone"].ToString(); } //Email field if (dr["Email"].ToString() != "") { c.Email = dr["Email"].ToString(); } //Account number if (dr["Account"].ToString() != "") { c.AccountNumber = dr["Account"].ToString(); } if (!c.IsSavable) { alErrors.Add("ImportQBVendor: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } c = (Vendor)c.Save(); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Vendor; m.LastSync = DateTime.Now; m.ForeignID = QuickBooksID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportQBVendor: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion #region Rate /// /// Import the indicated QB Item /// to an AyaNova Rate record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportQBRate(string QuickBooksID, RateTypes AsRateType, Guid MostLikelyRateUnitChargeDescriptionID, ArrayList alErrors) { DataRow dr = _dtQBItems.Rows.Find(QuickBooksID); //QBListID not found in Rate list? if (dr == null) { alErrors.Add("ImportQBRate: ID not found " + QuickBooksID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportQBRate: QuickBooks Rate name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a Rate by that name if (Rate.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportQBRate: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Rates rates = Rates.GetItems(false); Rate c = rates.Add(); c.RateType = AsRateType; c.Name = sName; c.Charge = (decimal)dr["Price"]; c.ContractRate = false; c.Cost = (decimal)dr["Cost"]; c.Description = T(255, dr["SalesDesc"].ToString()); c.RateUnitChargeDescriptionID = MostLikelyRateUnitChargeDescriptionID; if (!c.IsSavable) { alErrors.Add("ImportQBRate: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } rates = (Rates)rates.Save(); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Rate; m.LastSync = DateTime.Now; m.ForeignID = QuickBooksID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportQBRate: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion #region Part public static void RefreshAyaNovaPartFromQB(List objectIDList) { PopulateQBItemCache(); foreach (Guid g in objectIDList) { try { Part c = Part.GetItem(g); RefreshAyaNovaPartFromQB(c); if (c.IsSavable) c.Save(); } catch { }; } } public static void RefreshAyaNovaPartFromQB(Part c) { PopulateQBItemCache(); IntegrationMap im = QBI.Maps[c.ID]; if (im == null) return;//this part is not linked DataRow dr = _dtQBItems.Rows.Find(im.ForeignID); if (dr == null) return; //QBListID not found in part list? string sName = dr["FullName"].ToString(); if (sName.Length > 255) sName = sName.Substring(0, 255); c.PartNumber = sName; c.Name = T(255, dr["SalesDesc"].ToString()); c.WholesalerID = AyaVendorForQBItem(im.ForeignID); c.Retail = (decimal)dr["Price"]; c.Cost = (decimal)dr["Cost"]; } /// /// Import the indicated QB Item /// to an AyaNova Part record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportQBPart(string QuickBooksID, ArrayList alErrors) { DataRow dr = _dtQBItems.Rows.Find(QuickBooksID); //QBListID not found in Part list? if (dr == null) { alErrors.Add("ImportQBPart: ID not found " + QuickBooksID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportQBPart: QuickBooks Part name exceeds 255 character limit for AyaNova part number\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a Part by that number? if (Part.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportQBPart: Part number " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Part c = Part.NewItem(); c.PartNumber = sName; c.Name = T(255, dr["SalesDesc"].ToString()); c.WholesalerID = AyaVendorForQBItem(QuickBooksID); c.Retail = (decimal)dr["Price"]; c.Cost = (decimal)dr["Cost"]; if (!c.IsSavable) { alErrors.Add("ImportQBPart: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } //Save and get ready to provide the ID c = (Part)c.Save(); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Part; m.LastSync = DateTime.Now; m.ForeignID = QuickBooksID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportQBPart: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion #endregion Import to AyaNova #region Export / refresh to QuickBooks #region Customer #region Refresh to QB public static void RefreshQBClientFromAyaNova(List objectIDList) { foreach (Guid g in objectIDList) { try { Client c = Client.GetItemNoMRU(g); RefreshQBClientFromAyaNova(c); } catch { }; } } /// /// Refresh the indicated AyaNova client /// to it's linked QuickBooks customer record /// public static void RefreshQBClientFromAyaNova(Client c) { IntegrationMap im = QBI.Maps[c.ID]; if (im == null) return;//this client is not linked DataRow dr = _dtQBClients.Rows.Find(im.ForeignID); if (dr == null) return; //QBListID not found in client list? string strEditSequence = GetQBCustomerEditSequence(im.ForeignID); if (string.IsNullOrEmpty(strEditSequence)) { MessageBox.Show("RefreshQBClientFromAyaNova -> Error: unable to fetch edit sequence from QuickBooks. No changes made."); return; } string sName = c.Name; if (sName.Length > 41) sName = sName.Substring(0, 41); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ICustomerMod ca = requestSet.AppendCustomerModRq(); //Set field value for ListID ca.ListID.SetValue(im.ForeignID); // ca.ParentRef.ListID.SetValue(im.ForeignID); ca.EditSequence.SetValue(strEditSequence); dr["FullName"] = sName; dr["MailAddress"] = new Address(); dr["StreetAddress"] = new Address(); dr["Phone"] = ""; dr["Fax"] = ""; dr["AltPhone"] = ""; dr["Email"] = ""; dr["Contact"] = ""; //dr["Created"] = DateTime.MinValue;//flag indicating fresh record incomplete dr["Modified"] = DateTime.Now;//ditto dr["Account"] = ""; ca.Name.SetValue(sName); //ca.FirstName.SetValue(T(25,Primary.FirstName)); //Case 686 ca.Contact.SetValue(T(41, c.Contact)); //ca.LastName.SetValue(T(25, c.Contact)); if (c.Phone1 != null) ca.Phone.SetValue(T(21, c.Phone1)); else ca.Phone.SetValue(""); ca.Fax.SetValue(T(21, c.Phone2)); ca.AltPhone.SetValue(T(21, c.Phone3)); ca.Email.SetValue(T(99, c.Email)); #region Addresses bool bHasBillAddress = false; //Mailing address //Changed:4-Sept-2006 client name is now addr1 //rest shuffle down a spot //Also changed max length of address lines from 21 characters to 41 //as that is what is documented, not sure why they were set to 21 here before ca.BillAddress.Addr1.SetValue(T(41, c.Name)); if (c.MailToAddress.DeliveryAddress != "") { bHasBillAddress = true; string[] sad = c.MailToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) ca.BillAddress.Addr2.SetValue(T(41, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) ca.BillAddress.Addr3.SetValue(T(41, sad[1].TrimEnd())); //if(sad.GetLength(0)>2) // ca.BillAddress.Addr3.SetValue(T(21,sad[2].TrimEnd())); if (QVersion > 1.1 && sad.GetLength(0) > 3)//4th address line is 2 or newer ca.BillAddress.Addr4.SetValue(T(41, sad[2].TrimEnd())); } ca.BillAddress.City.SetValue(T(31, c.MailToAddress.City)); ca.BillAddress.Country.SetValue(T(31, c.MailToAddress.Country)); ca.BillAddress.PostalCode.SetValue(T(13, c.MailToAddress.Postal)); //QBFC7 - unifies county and province to "State" ca.BillAddress.State.SetValue(T(21, c.MailToAddress.StateProv)); //switch(QCountry) //{ // case "CA": // ca.BillAddress.Province.SetValue(T(21,c.MailToAddress.StateProv)); // break; // case "UK": // ca.BillAddress.County.SetValue(T(21,c.MailToAddress.StateProv)); // break; // default: // ca.BillAddress.State.SetValue(T(21,c.MailToAddress.StateProv)); // break; //} bool bHasShipAddress = false; //Delivery address //Changed:4-Sept-2006 client name is now addr1 //rest shuffle down a spot //Also changed max length of address lines from 21 characters to 41 //as that is what is documented, not sure why they were set to 21 here before ca.ShipAddress.Addr1.SetValue(T(41, c.Name)); if (c.GoToAddress.DeliveryAddress != "") { bHasShipAddress = true; string[] sad = c.GoToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) ca.ShipAddress.Addr2.SetValue(T(41, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) ca.ShipAddress.Addr3.SetValue(T(41, sad[1].TrimEnd())); //if(sad.GetLength(0)>2) // ca.ShipAddress.Addr3.SetValue(T(21,sad[2].TrimEnd())); if (QVersion > 1.1 && sad.GetLength(0) > 3)//4th address line is 2 or newer ca.ShipAddress.Addr4.SetValue(T(41, sad[2].TrimEnd())); } ca.ShipAddress.City.SetValue(T(31, c.GoToAddress.City)); ca.ShipAddress.Country.SetValue(T(31, c.GoToAddress.Country)); ca.ShipAddress.PostalCode.SetValue(T(13, c.GoToAddress.Postal)); //QBFC7 - unifies county and province to "State" ca.ShipAddress.State.SetValue(T(21, c.GoToAddress.StateProv)); //switch(QCountry) //{ // case "CA": // ca.ShipAddress.Province.SetValue(T(21,c.GoToAddress.StateProv)); // break; // case "UK": // ca.ShipAddress.County.SetValue(T(21,c.GoToAddress.StateProv)); // break; // default: // ca.ShipAddress.State.SetValue(T(21,c.GoToAddress.StateProv)); // break; //} #endregion //Added: 18-Nov-2006 CASE 95 //ensure that if only one address in ayanova that both types in QB get it if (!bHasShipAddress || !bHasBillAddress) { if (bHasShipAddress && !bHasBillAddress) { //copy shipping address to billing address CopyAddress(ca.ShipAddress, ca.BillAddress); } else if (bHasBillAddress && !bHasShipAddress) { CopyAddress(ca.BillAddress, ca.ShipAddress); } } ca.AccountNumber.SetValue(T(99, c.AccountNumber)); //case 519 ca.TermsRef.ListID.SetValue(QDat.TermsDefault); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // //// Uncomment the following to view and save the request and response XML // string requestXML = requestSet.ToXMLString(); // MessageBox.Show(requestXML); // ////SaveXML(requestXML); // string responseXML = responseSet.ToXMLString(); // MessageBox.Show(responseXML); ////// SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } ICustomerRet cr = response.Detail as ICustomerRet; string sNewCustID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item // dr["ID"] = sNewCustID; //add the new row for the newly imported object // _dtQBClients.Rows.Add(dr); //Link //IntegrationMap m = QBI.Maps.Add(QBI); im.Name = sName; //m.RootObjectID = c.ID; //m.RootObjectType = RootObjectTypes.Client; im.LastSync = DateTime.Now; //m.ForeignID = sNewCustID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; MessageBox.Show("RefreshQBClientFromAyaNova: QuickBooks won't allow refresh of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion Refresh to qb #region Export to QB public static void ImportAyaClient(List objectIDList) { ArrayList alErrors = new ArrayList(); foreach (Guid g in objectIDList) { try { ImportAyaClient(g, alErrors); } catch { }; } if (alErrors.Count != 0) { StringBuilder sb = new StringBuilder(); sb.Append("Export completed with some errors:\r\n\r\n"); foreach (object o in alErrors) { sb.Append((string)o); sb.Append("\r\n************\r\n"); } CopyableMessageBox cb = new CopyableMessageBox(sb.ToString()); cb.ShowDialog(); cb.Dispose(); } } /// /// Import the indicated client /// to QuickBooks customer record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaClient(Guid ClientID, ArrayList alErrors) { if (!Client.Exists(ClientID, "")) { alErrors.Add("ImportAyaClient: Client not found in AyaNova (deleted recently?) (" + ClientID.ToString() + ")"); return; } Client c = Client.GetItem(ClientID); string sName = c.Name; if (sName.Length > 41) { sName = sName.Substring(0, 41); alErrors.Add("ImportAyaClient: AyaNova client name exceeds 41 character limit for QuickBooks\r\n" + "Name: " + c.Name + "\r\n" + "will be imported as: " + sName); } //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object ICustomerAdd ca = requestSet.AppendCustomerAddRq(); //create a new row to add to the client cache datatable DataRow dr = _dtQBClients.NewRow(); dr["FullName"] = sName; dr["MailAddress"] = new Address(); dr["StreetAddress"] = new Address(); dr["Phone"] = ""; dr["Fax"] = ""; dr["AltPhone"] = ""; dr["Email"] = ""; dr["Contact"] = ""; dr["Created"] = DateTime.MinValue;//flag indicating fresh record incomplete dr["Modified"] = DateTime.MinValue;//ditto dr["Account"] = ""; ca.Name.SetValue(sName); //ca.FirstName.SetValue(T(25,Primary.FirstName)); //Case 686 ca.Contact.SetValue(T(41, c.Contact)); //ca.LastName.SetValue(T(25, c.Contact)); if (c.Phone1 != null) ca.Phone.SetValue(T(21, c.Phone1)); else ca.Phone.SetValue(""); ca.Fax.SetValue(T(21, c.Phone2)); ca.AltPhone.SetValue(T(21, c.Phone3)); ca.Email.SetValue(T(99, c.Email)); #region Addresses bool bHasBillAddress = false; //Mailing address //Changed:4-Sept-2006 client name is now addr1 //rest shuffle down a spot //Also changed max length of address lines from 21 characters to 41 //as that is what is documented, not sure why they were set to 21 here before ca.BillAddress.Addr1.SetValue(T(41, c.Name)); if (c.MailToAddress.DeliveryAddress != "") { bHasBillAddress = true; string[] sad = c.MailToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) ca.BillAddress.Addr2.SetValue(T(41, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) ca.BillAddress.Addr3.SetValue(T(41, sad[1].TrimEnd())); //if(sad.GetLength(0)>2) // ca.BillAddress.Addr3.SetValue(T(21,sad[2].TrimEnd())); if (QVersion > 1.1 && sad.GetLength(0) > 3)//4th address line is 2 or newer ca.BillAddress.Addr4.SetValue(T(41, sad[2].TrimEnd())); } ca.BillAddress.City.SetValue(T(31, c.MailToAddress.City)); ca.BillAddress.Country.SetValue(T(31, c.MailToAddress.Country)); ca.BillAddress.PostalCode.SetValue(T(13, c.MailToAddress.Postal)); //QBFC7 - unifies county and province to "State" ca.BillAddress.State.SetValue(T(21, c.MailToAddress.StateProv)); //switch(QCountry) //{ // case "CA": // ca.BillAddress.Province.SetValue(T(21,c.MailToAddress.StateProv)); // break; // case "UK": // ca.BillAddress.County.SetValue(T(21,c.MailToAddress.StateProv)); // break; // default: // ca.BillAddress.State.SetValue(T(21,c.MailToAddress.StateProv)); // break; //} bool bHasShipAddress = false; //Delivery address //Changed:4-Sept-2006 client name is now addr1 //rest shuffle down a spot //Also changed max length of address lines from 21 characters to 41 //as that is what is documented, not sure why they were set to 21 here before ca.ShipAddress.Addr1.SetValue(T(41, c.Name)); if (c.GoToAddress.DeliveryAddress != "") { bHasShipAddress = true; string[] sad = c.GoToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) ca.ShipAddress.Addr2.SetValue(T(41, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) ca.ShipAddress.Addr3.SetValue(T(41, sad[1].TrimEnd())); //if(sad.GetLength(0)>2) // ca.ShipAddress.Addr3.SetValue(T(21,sad[2].TrimEnd())); if (QVersion > 1.1 && sad.GetLength(0) > 3)//4th address line is 2 or newer ca.ShipAddress.Addr4.SetValue(T(41, sad[2].TrimEnd())); } ca.ShipAddress.City.SetValue(T(31, c.GoToAddress.City)); ca.ShipAddress.Country.SetValue(T(31, c.GoToAddress.Country)); ca.ShipAddress.PostalCode.SetValue(T(13, c.GoToAddress.Postal)); //QBFC7 - unifies county and province to "State" ca.ShipAddress.State.SetValue(T(21, c.GoToAddress.StateProv)); //switch(QCountry) //{ // case "CA": // ca.ShipAddress.Province.SetValue(T(21,c.GoToAddress.StateProv)); // break; // case "UK": // ca.ShipAddress.County.SetValue(T(21,c.GoToAddress.StateProv)); // break; // default: // ca.ShipAddress.State.SetValue(T(21,c.GoToAddress.StateProv)); // break; //} #endregion //Added: 18-Nov-2006 CASE 95 //ensure that if only one address in ayanova that both types in QB get it if (!bHasShipAddress || !bHasBillAddress) { if (bHasShipAddress && !bHasBillAddress) { //copy shipping address to billing address CopyAddress(ca.ShipAddress, ca.BillAddress); } else if (bHasBillAddress && !bHasShipAddress) { CopyAddress(ca.BillAddress, ca.ShipAddress); } } ca.AccountNumber.SetValue(T(99, c.AccountNumber)); //case 519 ca.TermsRef.ListID.SetValue(QDat.TermsDefault); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } ICustomerRet cr = response.Detail as ICustomerRet; string sNewCustID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item dr["ID"] = sNewCustID; //add the new row for the newly imported object _dtQBClients.Rows.Add(dr); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Client; m.LastSync = DateTime.Now; m.ForeignID = sNewCustID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportAyaClient: QuickBooks won't allow import of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion export to qb /// /// Copy the contents of one address to the other /// /// /// private static void CopyAddress(IAddress from, IAddress to) { if (from.Addr1.IsSet()) to.Addr1.SetValue(from.Addr1.GetValue()); if (from.Addr2.IsSet()) to.Addr2.SetValue(from.Addr2.GetValue()); if (from.Addr3.IsSet()) to.Addr3.SetValue(from.Addr3.GetValue()); if (QVersion > 1.1)//4th address line is 2 or newer { if (from.Addr4.IsSet()) to.Addr4.SetValue(from.Addr4.GetValue()); } if (from.City.IsSet()) to.City.SetValue(from.City.GetValue()); if (from.Country.IsSet()) to.Country.SetValue(from.Country.GetValue()); if (from.PostalCode.IsSet()) to.PostalCode.SetValue(from.PostalCode.GetValue()); //QBFC7 - unifies county and province to "State" if (from.State.IsSet()) to.State.SetValue(from.State.GetValue()); //switch (QCountry) //{ // case "CA": // if (from.Province.IsSet()) // to.Province.SetValue(from.Province.GetValue()); // break; // case "UK": // if (from.County.IsSet()) // to.County.SetValue(from.County.GetValue()); // break; // default: // if (from.State.IsSet()) // to.State.SetValue(from.State.GetValue()); // break; //} } #endregion customer #region Vendor /// /// Import the indicated Vendor /// to QuickBooks Vendor record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaVendor(Guid VendorID, ArrayList alErrors) { if (!Vendor.Exists(VendorID, "")) { alErrors.Add("ImportAyaVendor: Vendor not found in AyaNova (deleted recently?) (" + VendorID.ToString() + ")"); return; } Vendor c = Vendor.GetItem(VendorID); string sName = c.Name; if (sName.Length > 41) { sName = sName.Substring(0, 41); alErrors.Add("ImportAyaVendor: AyaNova Vendor name exceeds 41 character limit for QuickBooks\r\n" + "Name: " + c.Name + "\r\n" + "will be imported as: " + sName); } //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IVendorAdd ca = requestSet.AppendVendorAddRq(); //create a new row to add to the client cache datatable DataRow dr = _dtQBVendors.NewRow(); dr["FullName"] = sName; dr["MailAddress"] = new Address(); dr["StreetAddress"] = new Address(); dr["Phone"] = ""; dr["Fax"] = ""; dr["AltPhone"] = ""; dr["Email"] = ""; dr["Contact"] = ""; dr["Created"] = DateTime.MinValue;//flag indicating fresh record incomplete dr["Modified"] = DateTime.MinValue;//ditto dr["Account"] = ""; ca.Name.SetValue(sName); //Case 687 ca.Contact.SetValue(T(41, c.Contact)); //ca.FirstName.SetValue(T(25,Primary.FirstName)); //ca.LastName.SetValue(T(25, c.Contact)); if (c.Phone1 != null) ca.Phone.SetValue(T(21, c.Phone1)); else ca.Phone.SetValue(""); ca.Fax.SetValue(T(21, c.Phone2)); ca.AltPhone.SetValue(T(21, c.Phone3)); ca.Email.SetValue(T(99, c.Email)); if (c.MailToAddress.DeliveryAddress != "") { string[] sad = c.MailToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) ca.VendorAddress.Addr1.SetValue(T(21, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) ca.VendorAddress.Addr2.SetValue(T(21, sad[1].TrimEnd())); if (sad.GetLength(0) > 2) ca.VendorAddress.Addr3.SetValue(T(21, sad[2].TrimEnd())); if (QVersion > 1.1 && sad.GetLength(0) > 3)//4th address line is 2 or newer ca.VendorAddress.Addr4.SetValue(T(21, sad[3].TrimEnd())); } ca.VendorAddress.City.SetValue(T(31, c.MailToAddress.City)); ca.VendorAddress.Country.SetValue(T(31, c.MailToAddress.Country)); ca.VendorAddress.PostalCode.SetValue(T(13, c.MailToAddress.Postal)); //QBFC7 - unifies county and province to "State" ca.VendorAddress.State.SetValue(T(21, c.MailToAddress.StateProv)); //switch(QCountry) //{ // case "CA": // ca.VendorAddress.Province.SetValue(T(21,c.MailToAddress.StateProv)); // break; // case "UK": // ca.VendorAddress.County.SetValue(T(21,c.MailToAddress.StateProv)); // break; // default: // ca.VendorAddress.State.SetValue(T(21,c.MailToAddress.StateProv)); // break; //} ca.AccountNumber.SetValue(T(99, c.AccountNumber)); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } IVendorRet cr = response.Detail as IVendorRet; string sNewCustID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item dr["ID"] = sNewCustID; //add the new row for the newly imported object _dtQBVendors.Rows.Add(dr); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Vendor; m.LastSync = DateTime.Now; m.ForeignID = sNewCustID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportAyaVendor: QuickBooks won't allow import of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion vendor #region Part case 632 /// /// Refresh the list of AyaNova parts to their linked QuickBooks item records /// /// public static void RefreshQBPartFromAyaNova(List objectIDList) { foreach (Guid g in objectIDList) { try { Part c = Part.GetItem(g); RefreshQBPartFromAyaNova(c); } catch { }; } } /// /// Refresh the indicated AyaNova part /// to it's linked QuickBooks item record /// public static void RefreshQBPartFromAyaNova(Part c) { IntegrationMap im = QBI.Maps[c.ID]; if (im == null) return;//this part is not linked DataRow dr = _dtQBItems.Rows.Find(im.ForeignID); if (dr == null) return; //QBListID not found in part list? string sName = c.PartNumber; if (sName.Length > 31) sName = sName.Substring(0, 31); string sDescription = PartPickList.GetOnePart(c.ID)[0].DisplayName(AyaBizUtils.GlobalSettings.DefaultPartDisplayFormat); if (sDescription.Length > 4095) sDescription = sDescription.Substring(0, 4095); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IItemInventoryMod ca = requestSet.AppendItemInventoryModRq(); //Get and set edit sequence string sEditSequence = GetInventoryItemEditSequence(im.ForeignID); //create a new row to add to the client cache datatable dr["FullName"] = sName; dr["Type"] = qbitemtype.Inventory; dr["Price"] = c.Retail; dr["Cost"] = c.Cost; dr["SalesDesc"] = sDescription; //dr["ReorderPoint"] = 0; dr["Modified"] = DateTime.Now; dr["VendorID"] = c.WholesalerID.ToString(); //Set the QB item identification and edit sequence ca.ListID.SetValue(im.ForeignID); ca.EditSequence.SetValue(sEditSequence); //------------------------ //Set the qb item values ca.Name.SetValue(sName); //ca.IncomeAccountRef.ListID.SetValue(QDat.QBInventoryIncomeAccountReference); // ca.COGSAccountRef.ListID.SetValue(QDat.QBInventoryCOGSAccountRef); // ca.AssetAccountRef.ListID.SetValue(QDat.QBInventoryAssetAccountRef); ca.SalesDesc.SetValue(sDescription); ca.PurchaseDesc.SetValue(sDescription); ca.SalesPrice.SetValue((double)c.Retail); ca.PurchaseCost.SetValue((double)c.Cost); //Inventory values cannot be updated this way //they require generating a PO or making an invoice so //the refresh command won't support inventory values //if (AyaBizUtils.GlobalSettings.UseInventory) //{ // PartInventoryValuesFetcher pbw = PartInventoryValuesFetcher.GetItem(c.ID); // ca.QuantityOnHand.SetValue((double)pbw.QuantityOnHand); // ca.ReorderPoint.SetValue((double)pbw.MinStockLevel); //} //------------------------ //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } // IItemInventoryRet cr = response.Detail as IItemInventoryRet; // string sNewID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item // dr["ID"] = sNewID; //add the new row for the newly imported object // _dtQBItems.Rows.Add(dr); //Link // IntegrationMap m = QBI.Maps.Add(QBI); //m.Name = sName; // m.RootObjectID = c.ID; // m.RootObjectType = RootObjectTypes.Part; im.LastSync = DateTime.Now; //m.ForeignID = sNewID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; CopyableMessageBox cp = new CopyableMessageBox( "RefreshQBPartFromAyaNova: QuickBooks won't allow import of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); cp.ShowDialog(); } } #region AyaNova Part to Quickbooks /// /// Import a list of ayanova parts to QuickBooks /// /// public static void ImportAyaPart(List objectIDList) { ArrayList alErrors = new ArrayList(); foreach (Guid g in objectIDList) { try { ImportAyaPart(g, alErrors); } catch { }; } if (alErrors.Count != 0) { StringBuilder sb = new StringBuilder(); sb.Append("Export AyaNova parts to QuickBooks completed with some errors:\r\n\r\n"); foreach (object o in alErrors) { sb.Append((string)o); sb.Append("\r\n************\r\n"); } CopyableMessageBox cb = new CopyableMessageBox(sb.ToString()); cb.ShowDialog(); cb.Dispose(); } } /// /// Import the indicated part /// to QuickBooks item record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaPart(Guid PartID, ArrayList alErrors) { if (!Part.Exists(PartID, "")) { alErrors.Add("ImportAyaPart: Part not found in AyaNova (deleted recently?) (" + PartID.ToString() + ")"); return; } Part c = Part.GetItem(PartID); string sName = c.PartNumber; if (sName.Length > 31) { sName = sName.Substring(0, 31); alErrors.Add("ImportAyaPart: AyaNova part number exceeds 31 character limit for QuickBooks\r\n" + "Number: " + c.PartNumber + "\r\n" + "will be imported as: " + sName); } string sDescription = PartPickList.GetOnePart(c.ID)[0].DisplayName(AyaBizUtils.GlobalSettings.DefaultPartDisplayFormat); if (sDescription.Length > 4095) { sDescription = sDescription.Substring(0, 4095); alErrors.Add("ImportAyaPart: AyaNova part full display name exceeds 4095 character limit for sales description in QuickBooks\r\n" + "will be imported as: " + sDescription); } //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IItemInventoryAdd ca = requestSet.AppendItemInventoryAddRq(); //create a new row to add to the client cache datatable DataRow dr = _dtQBItems.NewRow(); dr["FullName"] = sName; dr["Type"] = qbitemtype.Inventory; dr["Price"] = c.Retail; dr["Cost"] = c.Cost; dr["SalesDesc"] = sDescription; dr["ReorderPoint"] = 0; dr["Modified"] = DateTime.MinValue; dr["VendorID"] = c.WholesalerID.ToString(); //------------------------ //Set the qb item values ca.Name.SetValue(sName); ca.IncomeAccountRef.ListID.SetValue(QDat.QBInventoryIncomeAccountReference); ca.COGSAccountRef.ListID.SetValue(QDat.QBInventoryCOGSAccountRef); ca.AssetAccountRef.ListID.SetValue(QDat.QBInventoryAssetAccountRef); ca.SalesDesc.SetValue(sDescription); ca.PurchaseDesc.SetValue(sDescription); ca.SalesPrice.SetValue((double)c.Retail); ca.PurchaseCost.SetValue((double)c.Cost); if (AyaBizUtils.GlobalSettings.UseInventory) { PartInventoryValuesFetcher pbw = PartInventoryValuesFetcher.GetItem(c.ID); ca.QuantityOnHand.SetValue((double)pbw.QuantityOnHand); ca.ReorderPoint.SetValue((double)pbw.MinStockLevel); } //------------------------ //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } IItemInventoryRet cr = response.Detail as IItemInventoryRet; string sNewID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item dr["ID"] = sNewID; //add the new row for the newly imported object _dtQBItems.Rows.Add(dr); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Part; m.LastSync = DateTime.Now; m.ForeignID = sNewID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportAyaPart: QuickBooks won't allow import of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion AyaNova part to Quickbooks #endregion part #region Rate case 632 /// /// Import the indicated service rate /// to QuickBooks item record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaRate(Guid RateID, RatePickList ratelist, ArrayList alErrors) { if (!ratelist.Contains(RateID)) { alErrors.Add("ImportAyaRate: Rate not found in AyaNova (deleted recently?) (" + RateID.ToString() + ")"); return; } RatePickList.RatePickListInfo c = ratelist[RateID]; string sName = c.Name; if (sName.Length > 31) { sName = sName.Substring(0, 31); alErrors.Add("ImportAyaRate: AyaNova Rate name exceeds 31 character limit for QuickBooks\r\n" + "Name: " + c.Name + "\r\n" + "will be imported as: " + sName); } string sDescription = c.Description; if (string.IsNullOrEmpty(sDescription)) sDescription = c.Name; if (sDescription.Length > 4095) { sDescription = sDescription.Substring(0, 4095); alErrors.Add("ImportAyaRate: AyaNova Rate description exceeds 4095 character limit for sales description in QuickBooks\r\n" + "will be imported as: " + sDescription); } //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { //Import seems safe... // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object IItemServiceAdd ca = requestSet.AppendItemServiceAddRq(); //create a new row to add to the client cache datatable DataRow dr = _dtQBItems.NewRow(); dr["FullName"] = sName; dr["Type"] = qbitemtype.Service; dr["Price"] = c.Charge; dr["Cost"] = c.Cost; dr["SalesDesc"] = sDescription; dr["ReorderPoint"] = 0; dr["Modified"] = DateTime.MinValue; dr["VendorID"] = ""; //------------------------ //Set the qb item values ca.Name.SetValue(sName); ca.ORSalesPurchase.SalesOrPurchase.Desc.SetValue(sDescription); ca.ORSalesPurchase.SalesOrPurchase.ORPrice.Price.SetValue((double)c.Charge); ca.ORSalesPurchase.SalesOrPurchase.AccountRef.ListID.SetValue(QDat.QBServiceIncomeAccountRef); //------------------------ //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); // Uncomment the following to view and save the request and response XML //string requestXML = requestSet.ToXMLString(); //MessageBox.Show(requestXML); // SaveXML(requestXML); //string responseXML = responseSet.ToXMLString(); //MessageBox.Show(responseXML); // SaveXML(responseXML); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } IItemServiceRet cr = response.Detail as IItemServiceRet; string sNewID = cr.ListID.GetValue(); requestSet.ClearRequests(); //---------------- // Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //catch the new ID for the QB Item dr["ID"] = sNewID; //add the new row for the newly imported object _dtQBItems.Rows.Add(dr); //Link IntegrationMap m = QBI.Maps.Add(QBI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Rate; m.LastSync = DateTime.Now; m.ForeignID = sNewID; QBI = (Integration)QBI.Save(); } catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportAyaRate: QuickBooks won't allow import of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); } } #endregion rate private static string T(int nLength, string s) { if (s == null || s == "") return ""; if (s.Length <= nLength) return s; else return s.Substring(0, nLength); } #endregion export to quickbooks #endregion importexport #region Workorder mismatch scanning public enum MisMatchReason { NotLinkedToQB = 0, PriceDifferent = 1, NothingToInvoice = 2 } /// /// Mismatch properties /// A structure for storing mismatches identified ///so user can resolve them. /// public struct MisMatch { //public Guid WorkorderID; public Guid mRootObjectID; public RootObjectTypes mObjectType; public string mName; public MisMatchReason mReason; public decimal mAyaPrice; public decimal mQBPrice; public Guid mWorkorderItemPartID; public string mQBListID; public Guid RootObjectID { get { return mRootObjectID; } } public RootObjectTypes ObjectType { get { return mObjectType; } } public string Name { get { return mName; } } public MisMatchReason Reason { get { return mReason; } } public decimal AyaPrice { get { return mAyaPrice; } } public decimal QBPrice { get { return mQBPrice; } } public Guid WorkorderItemPartID { get { return mWorkorderItemPartID; } } public string QBListID { get { return mQBListID; } } } /// /// Given a workorder ID /// scans the objects in the workorder /// that need to be linked to QB for invoicing /// and on any error found adds them to the /// mismatched object array list /// /// Id of workorder being scanned /// An arraylist of mismatch objects /// An array of GUID values of workorderitemparts that have been set by /// user to forcibly use the price set on the workorderitem part even though /// it differs from the quickbooks price /// True if all links ok, false if there are any mismatches at all public static bool ScanLinksOK(Guid WorkorderID, ArrayList MisMatches, ArrayList PriceOverrides) { bool bReturn = true; bool bSomethingToInvoice = false; Workorder w = Workorder.GetItem(WorkorderID); //Client ok? if (!QBI.Maps.Contains(w.ClientID)) { bReturn = false; AddMisMatch(AyaClientList[w.ClientID].Name, w.ClientID, RootObjectTypes.Client, MisMatchReason.NotLinkedToQB, MisMatches); } //Service rates: foreach (WorkorderItem wi in w.WorkorderItems) { #region Labor foreach (WorkorderItemLabor wl in wi.Labors) { //If there's *any* labor then there is something to invoice bSomethingToInvoice = true; //Check that rate isn't actually guid.empty //it's possible that some users have not selected a rate on the workorder if (wl.ServiceRateID == Guid.Empty) throw new System.ApplicationException("ERROR: Workorder " + w.WorkorderService.ServiceNumber.ToString() + " has a labor item with no rate selected\r\n" + "This is a serious problem for QBI and needs to be rectified before QBI can be used.\r\n"); if (!QBI.Maps.Contains(wl.ServiceRateID)) { bReturn = false; AddMisMatch(AyaRateList[wl.ServiceRateID].Name, wl.ServiceRateID, RootObjectTypes.Rate, MisMatchReason.NotLinkedToQB, MisMatches); } } #endregion #region Travel foreach (WorkorderItemTravel wt in wi.Travels) { //If there's *any* travel then there is something to invoice bSomethingToInvoice = true; //Check that rate isn't actually guid.empty //it's possible that some users have not selected a rate on the workorder if (wt.TravelRateID == Guid.Empty) throw new System.ApplicationException("ERROR: Workorder " + w.WorkorderService.ServiceNumber.ToString() + " has a travel item with no rate selected\r\n" + "This is a serious problem for QBI and needs to be rectified before QBI can be used.\r\n"); if (!QBI.Maps.Contains(wt.TravelRateID)) { bReturn = false; AddMisMatch(AyaRateList[wt.TravelRateID].Name, wt.TravelRateID, RootObjectTypes.Rate, MisMatchReason.NotLinkedToQB, MisMatches); } } #endregion #region Parts foreach (WorkorderItemPart wp in wi.Parts) { //If there's *any* parts then there is something to invoice bSomethingToInvoice = true; //Changed: 14-Nov-2006 to check that linked item id exists in qb if (!QBI.Maps.Contains(wp.PartID) || QBItems.Rows.Find(QBI.Maps[wp.PartID].ForeignID) == null) { bReturn = false; //Changed: 21-June-2006 to use display formatted name AddMisMatch(AyaPartList[wp.PartID].DisplayName(Util.GlobalSettings.DefaultPartDisplayFormat), wp.PartID, RootObjectTypes.Part, MisMatchReason.NotLinkedToQB, MisMatches); } else { //check the price if (!PriceOverrides.Contains(wp.ID)) { decimal qbPrice = (decimal)QBItems.Rows.Find(QBI.Maps[wp.PartID].ForeignID)["Price"]; //------------DISCOUNT----------------- string disco = ""; //Added:20-July-2006 to incorporate discounts on parts into qb invoice decimal charge; //Changed: 18-Nov-2006 CASE 158 //this is all wrong, it was multiplying price by quantity to calculate charge when it shouldn't //removed quanty * price in next line to just price charge = decimal.Round(wp.Price, 2, MidpointRounding.AwayFromZero); charge = charge - (decimal.Round(charge * wp.Discount, 2, MidpointRounding.AwayFromZero)); if (wp.Discount != 0) { disco = " (Price " + wp.Price.ToString("c") + " discounted on workorder " + wp.Discount.ToString("p") + ") \r\n"; } //----------------------------- //It's a match, let's see if the price matches as well if (charge != qbPrice) { bReturn = false; AddMisMatch("WO: " + w.WorkorderService.ServiceNumber.ToString() + disco + " Part: " + AyaPartList[wp.PartID].DisplayName(Util.GlobalSettings.DefaultPartDisplayFormat),//Changed: 21-June-2006 to use display formatted name wp.PartID, RootObjectTypes.Part, MisMatchReason.PriceDifferent, MisMatches, qbPrice, charge, wp.ID, QBI.Maps[wp.PartID].ForeignID); } } } } #endregion #region Outside service charges if (wi.HasOutsideService) { if (wi.OutsideService.RepairPrice != 0 || wi.OutsideService.ShippingPrice != 0) { bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a QB charge defined for outside service if (QDat.OutsideServiceChargeAs == null || QDat.OutsideServiceChargeAs == "" || !QBItems.Rows.Contains(QDat.OutsideServiceChargeAs)) { bReturn = false; AddMisMatch("Outside service", Guid.Empty, RootObjectTypes.WorkorderItemOutsideService, MisMatchReason.NotLinkedToQB, MisMatches); } } } #endregion #region Workorder item loan charges if (wi.HasLoans) { foreach (WorkorderItemLoan wil in wi.Loans) { if (wil.Charges != 0) { //case 772 bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a QB charge defined for loaned item charges if (QDat.WorkorderItemLoanChargeAs == null || QDat.WorkorderItemLoanChargeAs == "" || !QBItems.Rows.Contains(QDat.WorkorderItemLoanChargeAs)) { bReturn = false; AddMisMatch("Workorder item loan", Guid.Empty, RootObjectTypes.WorkorderItemLoan, MisMatchReason.NotLinkedToQB, MisMatches); break; } } } } #endregion #region Workorder item misc expenses if (wi.HasExpenses) { foreach (WorkorderItemMiscExpense wie in wi.Expenses) { if (wie.ChargeToClient) { bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a QB charge defined for misc expense item charges if (QDat.MiscExpenseChargeAs == null || QDat.MiscExpenseChargeAs == "" || !QBItems.Rows.Contains(QDat.MiscExpenseChargeAs)) { bReturn = false; AddMisMatch("Workorder item expense", Guid.Empty, RootObjectTypes.WorkorderItemMiscExpense, MisMatchReason.NotLinkedToQB, MisMatches); break; } } } } #endregion }//workorder items loop //If there are no mismatches so far, //maybe it's because it's got nothing to invoice? if (bReturn && !bSomethingToInvoice) { bReturn = false; AddMisMatch("WO: " + w.WorkorderService.ServiceNumber.ToString() + " - Nothing chargeable on it, will not be invoiced", Guid.Empty, RootObjectTypes.Nothing, MisMatchReason.NothingToInvoice, MisMatches); } return bReturn; } private static void AddMisMatch(string Name, Guid RootObjectID, RootObjectTypes RootObjectType, MisMatchReason Reason, ArrayList Mismatches) { AddMisMatch(Name, RootObjectID, RootObjectType, Reason, Mismatches, 0m, 0m, Guid.Empty, ""); } private static void AddMisMatch(string Name, Guid RootObjectID, RootObjectTypes RootObjectType, MisMatchReason Reason, ArrayList Mismatches, decimal QBPrice, decimal AyaPrice, Guid WorkorderItemPartID, string QBListID) { bool bDuplicate = false; //scan through list of existing mismatches, //only add a not linked item if it's //not there already //other types of mismatches need to be added because //they need to be resolved on a case by case basis or are unresolvable if (Reason == MisMatchReason.NotLinkedToQB) { foreach (object o in Mismatches) { MisMatch m = (MisMatch)o; //Have to check ID and type here because for outside service //and loans and misc expenses the id is always empty so type is //the only way to differentiate if (m.RootObjectID == RootObjectID && m.ObjectType == RootObjectType) { bDuplicate = true; break; } } } if (!bDuplicate) { MisMatch m = new MisMatch(); m.mName = Name; m.mRootObjectID = RootObjectID; m.mObjectType = RootObjectType; m.mReason = Reason; m.mAyaPrice = AyaPrice; m.mQBPrice = QBPrice; m.mWorkorderItemPartID = WorkorderItemPartID; m.mQBListID = QBListID; Mismatches.Add(m); } } #endregion wo_mismatch_scan #region Invoice /// /// Invoice out the workorders contained in the array list /// as one single invoice /// /// Put in descriptive text as necessary and when sucessfully invoiced /// set workorders in list to status selected for post invoicing and /// set invoice number and then close them /// /// /// public static void Invoice(ArrayList alWorkorders, ArrayList alErrors) { if (alWorkorders.Count == 0) return; //Connect to QB // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; // IY: Add the request to the message set request object //Create invoice IInvoiceAdd i = requestSet.AppendInvoiceAddRq(); //a string to hold the memo field StringBuilder sbMemo = new StringBuilder(); if (QDat.SetMemoField) { sbMemo.Append("Imported by: " + Thread.CurrentPrincipal.Identity.Name); if (alWorkorders.Count > 1) { sbMemo.Append(" Workorders: "); } else { sbMemo.Append(" Workorder: "); } } //Keep track if first time through so //we can set invoice header stuff based on first workorder's client etc bool bFirstLoop = true; //Loop through alworkorders foreach (object o in alWorkorders) { Workorder w = Workorder.GetItem((Guid)o); if (bFirstLoop) { bFirstLoop = false; //Set client i.CustomerRef.ListID.SetValue(QBI.Maps[w.ClientID].ForeignID); //Set QB Invoice template if (!(QVersion < 3))//templates are qbxml 3 or greater { if (QDat.QBInvoiceTemplate != null && QDat.QBInvoiceTemplate != "") i.TemplateRef.ListID.SetValue(QDat.QBInvoiceTemplate); } //Set Class if (QDat.TransactionClass != null && QDat.TransactionClass != "" && QDat.TransactionClass != TRANSACTION_CLASS_NO_CLASS_SELECTED)//case 3268 i.ClassRef.ListID.SetValue(QDat.TransactionClass); //Set ToBePrinted if (QDat.ToBePrinted) i.IsToBePrinted.SetValue(true); } else { //put a comma after the last workorder number that was //added to the memo string if (QDat.SetMemoField) sbMemo.Append(", "); } //if set memo true then build memo string from workorder service numbers etc if (QDat.SetMemoField) { sbMemo.Append(w.WorkorderService.ServiceNumber.ToString()); } #region Invoice header text if (QDat.HasInvoiceHeaderTemplate) { string s = QDat.InvoiceHeaderTemplate; if (s.IndexOf("~WO#~") != -1) { s = s.Replace("~WO#~", w.WorkorderService.ServiceNumber.ToString()); } if (s.IndexOf("~CONTACT~") != -1) { s = s.Replace("~CONTACT~", w.CustomerContactName); } if (s.IndexOf("~CREF#~") != -1) { s = s.Replace("~CREF#~", w.CustomerReferenceNumber); } if (s.IndexOf("~OURREF#~") != -1) { s = s.Replace("~OURREF#~", w.InternalReferenceNumber); } if (s.IndexOf("~PROJ~") != -1) { if (w.ProjectID == Guid.Empty) s = s.Replace("~PROJ~", ""); else s = s.Replace("~PROJ~", NameFetcher.GetItem("aProject", "aName", w.ProjectID).RecordName); } if (s.IndexOf("~CLIENT~") != -1) { s = s.Replace("~CLIENT~", NameFetcher.GetItem("aClient", "aName", w.ClientID).RecordName); } if (s.IndexOf("~SERVDATE~") != -1) { s = s.Replace("~SERVDATE~", w.WorkorderService.ServiceDate.ToString()); } if (s.IndexOf("~STAT~") != -1) { if (w.WorkorderService.WorkorderStatusID == Guid.Empty) s = s.Replace("~STAT~", ""); else s = s.Replace("~STAT~", NameFetcher.GetItem("aWorkorderStatus", "aName", w.WorkorderService.WorkorderStatusID).RecordName); } if (s.IndexOf("~DESC~") != -1) { s = s.Replace("~DESC~", w.Summary); } InvoiceAddText(i, s); } #endregion header text #region Part charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemPart p in it.Parts) { //------------DISCOUNT----------------- //Added:20-July-2006 to incorporate discounts on parts into qb invoice decimal charge; //Case 269 this is incorrect: //charge = decimal.Round(p.Quantity * p.Price, 2, MidpointRounding.AwayFromZero); charge = decimal.Round(1 * p.Price, 2, MidpointRounding.AwayFromZero); charge = charge - (decimal.Round(charge * p.Discount, 2, MidpointRounding.AwayFromZero)); //----------------------------- InvoiceAddCharge(i, QBI.Maps[p.PartID].ForeignID, p.Quantity, charge); string sn = ""; if (p.PartSerialID != Guid.Empty) { sn = PartSerial.GetSerialNumberFromPartSerialID(p.PartSerialID); if (sn != "") InvoiceAddText(i, "SN: " + sn); } //Added:18-Nov-2006 case 125 //checks for nonempty description, also checks to see if description is //same as serial number because it's copied there in some cases and no sense //in showing it twice if (p.Description != "" && sn != p.Description) InvoiceAddText(i, p.Description); } } #endregion part charges #region Service charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemLabor l in it.Labors) { //Added 20-July-2006 to not charge for banked hours if (l.ServiceBankID != Guid.Empty) { InvoiceAddCharge(i, QBI.Maps[l.ServiceRateID].ForeignID, l.ServiceRateQuantity, 0); } else InvoiceAddCharge(i, QBI.Maps[l.ServiceRateID].ForeignID, l.ServiceRateQuantity, AyaRateList[l.ServiceRateID].Charge); } } #endregion Service charges #region Travel charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemTravel l in it.Travels) { InvoiceAddCharge(i, QBI.Maps[l.TravelRateID].ForeignID, l.TravelRateQuantity, AyaRateList[l.TravelRateID].Charge); } } #endregion Travel charges #region MiscExpense charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemMiscExpense l in it.Expenses) { if (l.ChargeToClient) { InvoiceAddCharge(i, QDat.MiscExpenseChargeAs, 1, l.ChargeAmount); } } } #endregion MiscExpense charges #region Loaner charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemLoan l in it.Loans) { InvoiceAddCharge(i, QDat.WorkorderItemLoanChargeAs, 1, l.Charges); } } #endregion Loaner charges #region OutsideService charges foreach (WorkorderItem it in w.WorkorderItems) { if (it.HasOutsideService && (it.OutsideService.ShippingPrice != 0 || it.OutsideService.RepairPrice != 0)) { InvoiceAddCharge(i, QDat.OutsideServiceChargeAs, 1, it.OutsideService.ShippingPrice + it.OutsideService.RepairPrice); } } #endregion OutsideService charges //Descriptive footer text //Loop through workorder items //inserting descriptive text as required if (QDat.HasAnyInvoiceFooterTemplateFields) { foreach (WorkorderItem it in w.WorkorderItems) { #region Item (footer) fields if (QDat.InvoiceFooterTemplate != "") { string s = QDat.InvoiceFooterTemplate; if (s.IndexOf("~ITEM_SUMMARY~") != -1) { s = s.Replace("~ITEM_SUMMARY~", it.Summary); } if (s.IndexOf("~ITEM_SERVICE_NOTES~") != -1) { s = s.Replace("~ITEM_SERVICE_NOTES~", it.TechNotes); } if (s.IndexOf("~ITEM_TYPE~") != -1) { if (it.TypeID == Guid.Empty) s = s.Replace("~ITEM_TYPE~", ""); else s = s.Replace("~ITEM_TYPE~", NameFetcher.GetItem("aWorkorderItemType", "aName", it.TypeID).RecordName); } if (s.IndexOf("~ITEM_REQUEST_DATE~") != -1) { s = s.Replace("~ITEM_REQUEST_DATE~", it.RequestDate.ToString()); } if (s.IndexOf("~ITEM_STATUS~") != -1) { if (it.WorkorderStatusID == Guid.Empty) s = s.Replace("~ITEM_STATUS~", ""); else s = s.Replace("~ITEM_STATUS~", NameFetcher.GetItem("aWorkorderStatus", "aName", it.WorkorderStatusID).RecordName); } InvoiceAddText(i, s); } #endregion item #region Unit fields if (QDat.InvoiceUnitTemplate != "" && it.UnitID != Guid.Empty) { string s = QDat.InvoiceUnitTemplate; UnitPickList up = UnitPickList.GetListOfOneSpecificUnit(it.UnitID); if (s.IndexOf("~AYAFORMAT~") != -1) { s = s.Replace("~AYAFORMAT~", up[0].UnitName()); } if (s.IndexOf("~UNIT_SN~") != -1) { s = s.Replace("~UNIT_SN~", up[0].Serial); } if (s.IndexOf("~UNIT_METER~") != -1) { if (!up[0].Metered) s = s.Replace("~UNIT_METER~", ""); else s = s.Replace("~UNIT_METER~", Unit.LastMeterReading(up[0].ID).ToString()); } if (s.IndexOf("~UNIT_MAKE~") != -1) { s = s.Replace("~UNIT_MAKE~", up[0].VendorName); } if (s.IndexOf("~UNIT_MODEL_NAME~") != -1) { s = s.Replace("~UNIT_MODEL_NAME~", up[0].ModelName); } if (s.IndexOf("~UNIT_MODEL_NUMBER~") != -1) { s = s.Replace("~UNIT_MODEL_NUMBER~", up[0].ModelNumber); } InvoiceAddText(i, s); } #endregion unit #region Labor fields if (QDat.InvoiceServiceTemplate != "" && it.HasLabor) { foreach (WorkorderItemLabor wl in it.Labors) { string s = QDat.InvoiceServiceTemplate; if (s.IndexOf("~SERVICE_START~") != -1) { s = s.Replace("~SERVICE_START~", wl.ServiceStartDate.ToString()); } if (s.IndexOf("~SERVICE_STOP~") != -1) { s = s.Replace("~SERVICE_STOP~", wl.ServiceStopDate.ToString()); } if (s.IndexOf("~SERVICE_QUANTITY~") != -1) { s = s.Replace("~SERVICE_QUANTITY~", wl.ServiceRateQuantity.ToString()); } if (s.IndexOf("~NO_CHARGE_QUANTITY~") != -1) { s = s.Replace("~NO_CHARGE_QUANTITY~", wl.NoChargeQuantity.ToString()); } if (s.IndexOf("~RATE_NAME~") != -1) { s = s.Replace("~RATE_NAME~", AyaRateList[wl.ServiceRateID].Name); } if (s.IndexOf("~SERVICE_TECH~") != -1) { s = s.Replace("~SERVICE_TECH~", UserPickList.GetListOfOneSpecificUser(wl.UserID)[0].Name); } if (s.IndexOf("~DETAILS~") != -1) { s = s.Replace("~DETAILS~", wl.ServiceDetails); } InvoiceAddText(i, s); } } #endregion service #region Travel fields if (QDat.InvoiceTravelTemplate != "" && it.HasTravel) { foreach (WorkorderItemTravel wt in it.Travels) { string s = QDat.InvoiceTravelTemplate; if (s.IndexOf("~TRAVEL_START~") != -1) { s = s.Replace("~TRAVEL_START~", wt.TravelStartDate.ToString()); } if (s.IndexOf("~TRAVEL_STOP~") != -1) { s = s.Replace("~TRAVEL_STOP~", wt.TravelStopDate.ToString()); } if (s.IndexOf("~TRAVEL_QUANTITY~") != -1) { s = s.Replace("~TRAVEL_QUANTITY~", wt.TravelRateQuantity.ToString()); } if (s.IndexOf("~TRAVEL_NO_CHARGE_QUANTITY~") != -1) { s = s.Replace("~TRAVEL_NO_CHARGE_QUANTITY~", wt.NoChargeQuantity.ToString()); } if (s.IndexOf("~TRAVEL_RATE_NAME~") != -1) { s = s.Replace("~TRAVEL_RATE_NAME~", AyaRateList[wt.TravelRateID].Name); } if (s.IndexOf("~TRAVEL_TECH~") != -1) { s = s.Replace("~TRAVEL_TECH~", UserPickList.GetListOfOneSpecificUser(wt.UserID)[0].Name); } if (s.IndexOf("~TRAVEL_DETAILS~") != -1) { s = s.Replace("~TRAVEL_DETAILS~", wt.TravelDetails); } if (s.IndexOf("~TRAVEL_DISTANCE~") != -1) { s = s.Replace("~TRAVEL_DISTANCE~", wt.Distance.ToString()); } InvoiceAddText(i, s); } } #endregion travel fields #region Outside fields if (QDat.OutsideServiceChargeAs != "" && it.HasOutsideService) { string s = QDat.InvoiceOutsideServiceTemplate; if (s.IndexOf("~REPAIR_PRICE~") != -1) { s = s.Replace("~REPAIR_PRICE~", it.OutsideService.RepairPrice.ToString("c")); } if (s.IndexOf("~SHIP_CHARGE~") != -1) { s = s.Replace("~SHIP_CHARGE~", it.OutsideService.ShippingPrice.ToString("c")); } if (s.IndexOf("~SENT~") != -1) { s = s.Replace("~SENT~", it.OutsideService.DateSent.ToString()); } if (s.IndexOf("~RETURNED~") != -1) { s = s.Replace("~RETURNED~", it.OutsideService.DateReturned.ToString()); } if (s.IndexOf("~NOTES~") != -1) { s = s.Replace("~NOTES~", it.OutsideService.Notes); } InvoiceAddText(i, s); } #endregion outside service #region Misc expense fields if (QDat.InvoiceMiscExpenseTemplate != "" && it.HasExpenses) { foreach (WorkorderItemMiscExpense e in it.Expenses) { string s = QDat.InvoiceMiscExpenseTemplate; if (s.IndexOf("~CHARGES~") != -1) { s = s.Replace("~CHARGES~", e.ChargeAmount.ToString("c")); } if (s.IndexOf("~SUMMARY~") != -1) { s = s.Replace("~SUMMARY~", e.Name); } if (s.IndexOf("~DESCRIPTION~") != -1) { s = s.Replace("~DESCRIPTION~", e.Description); } if (s.IndexOf("~TECH~") != -1) { s = s.Replace("~TECH~", UserPickList.GetListOfOneSpecificUser(e.UserID)[0].Name); } InvoiceAddText(i, s); } } #endregion misc expense #region Loan item fields if (QDat.InvoiceLoanItemTemplate != "" && it.HasLoans) { foreach (WorkorderItemLoan l in it.Loans) { string s = QDat.InvoiceLoanItemTemplate; if (s.IndexOf("~CHARGE~") != -1) { s = s.Replace("~CHARGE~", l.Charges.ToString("c")); } if (s.IndexOf("~ITEM~") != -1) { s = s.Replace("~ITEM~", NameFetcher.GetItem("aLoanItem", "aName", l.LoanItemID).RecordName); } if (s.IndexOf("~LOANED~") != -1) { s = s.Replace("~LOANED~", l.OutDate.ToString()); } if (s.IndexOf("~LOAN_RETURNED~") != -1) { s = s.Replace("~LOAN_RETURNED~", l.ReturnDate.ToString()); } if (s.IndexOf("~LOAN_NOTES~") != -1) { s = s.Replace("~LOAN_NOTES~", l.Notes); } InvoiceAddText(i, s); } } #endregion loan item expense } } } //Set memo field if (QDat.SetMemoField) { i.Memo.SetValue(T(4094, sbMemo.ToString())); } //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); IResponse response = responseSet.ResponseList.GetAt(0); //nonzero indicates an error this is unrecoverable //so throw an exception if (response.StatusCode != 0) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } string InvoiceNumber = ""; IInvoiceRet ir = (IInvoiceRet)response.Detail; if (ir.RefNumber != null) InvoiceNumber = ir.RefNumber.GetValue(); // Close the session and connection with QuickBooks requestSet.ClearRequests(); sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); //If Successful post: //Loop through all workorders again and set their invoice number, status and close them //Loop through alworkorders foreach (object o in alWorkorders) { Workorder w = Workorder.GetItem((Guid)o); if (QDat.PostWOStatus != Guid.Empty) { w.WorkorderService.WorkorderStatusID = QDat.PostWOStatus; } w.WorkorderService.InvoiceNumber = InvoiceNumber; //Case 7 if (QDat.AutoClose) w.Closed = true; w.Save(); } }//end try block catch (Exception ex) { if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("Invoice: Invoicing failed due to the following error:\r\n" + ex.Message); } } /// /// Add text to the invoice /// chopping into chunks less than the max 4095 characters limit /// as necessary /// private static void InvoiceAddText(IInvoiceAdd i, string Text) { if (Text == null || Text == "") return; ArrayList lines = new ArrayList(); while (Text.Length > 4094) { lines.Add(Text.Substring(0, 4094)); Text = Text.Remove(0, 4094); } //Get the last bit if (Text.Length > 0) lines.Add(Text); //Loop through lines and add them one at a time to the invoice foreach (object o in lines) { i.ORInvoiceLineAddList.Append().InvoiceLineAdd.Desc.SetValue((string)o); } } /// /// Add charge line to the invoice /// /// private static void InvoiceAddCharge(IInvoiceAdd i, string QBListID, decimal Quantity, decimal rate) { IInvoiceLineAdd line = i.ORInvoiceLineAddList.Append().InvoiceLineAdd;//.InvoiceLineAdd.Desc.SetValue((string)o); line.ItemRef.ListID.SetValue(QBListID); line.Quantity.SetValue((double)Quantity); //QBFC7 - unified, now seems to use US style for all editions //if(QCountry!="US") // line.ORRate.Rate.SetValue(Convert.ToDouble(rate)); //else line.ORRatePriceLevel.Rate.SetValue(Convert.ToDouble(rate)); // } #endregion #region Change QB Item price public static void ChangeQBItemPrice(string QBListID, decimal NewPrice) { //Added: 18-Nov-2006 CASE 92 //check if inventory item as this code only handles price changing for inventory //items in qb qbitemtype qtype = (qbitemtype)QBItems.Rows.Find(QBListID)["Type"]; if (qtype != qbitemtype.Inventory) { MessageBox.Show("Only inventory items in QuickBooks can have their price changed\r\n" + "The current item is of type: " + qtype.ToString()); return; } //----Get the edit sequence----- string sEditSequence = GetInventoryItemEditSequence(QBListID); //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; //------change the price---- // IY: Add the request to the message set request object IItemInventoryMod ItemQ = requestSet.AppendItemInventoryModRq(); ItemQ.ListID.SetValue(QBListID); ItemQ.EditSequence.SetValue(sEditSequence); ItemQ.SalesPrice.SetValue((double)NewPrice); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); //------------------------- //-------------- // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); QBItems.Rows.Find(QBListID)["Price"] = NewPrice; } catch (Exception ex) { IntegrationLog.Log(QBID, "ChangeQBItemPrice: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } public static string GetInventoryItemEditSequence(string QBListID) { //Connect to QB and fill // IY: Create the session manager object using QBFC QBSessionManager sessionManager = new QBSessionManager(); // IY: We want to know if we begun a session so we can end it if an // error happens bool booSessionBegun = false; try { // IY: Get the RequestMsgSet based on the correct QB Version IMsgSetRequest requestSet = getLatestMsgSetRequest(sessionManager); // IY: Initialize the message set request object requestSet.Attributes.OnError = ENRqOnError.roeStop; //----Get the edit sequence----- // IY: Add the request to the message set request object IItemInventoryQuery iq = requestSet.AppendItemInventoryQueryRq(); iq.ORListQuery.ListIDList.Add(QBListID); //This is intended to be called after already sucessfully connected //to get version info so no special safety checks here sessionManager.OpenConnection2("", "AyaNova QBI", ENConnectionType.ctLocalQBDLaunchUI); sessionManager.BeginSession("", ENOpenMode.omDontCare); booSessionBegun = true; // IY: Do the request and get the response message set object IMsgSetResponse responseSet = sessionManager.DoRequests(requestSet); //------------------------- IResponse response = responseSet.ResponseList.GetAt(0); //Added: 18-Nov-2006 nonzero status codes 500 and higher are serious errors, less than 500 could just mean no matching items in list if (response.StatusCode > 499) { throw new ApplicationException(response.StatusMessage + " Code: " + response.StatusCode); } IItemInventoryRetList il = response.Detail as IItemInventoryRetList; IItemInventoryRet i = il.GetAt(0); string EditSequence = i.EditSequence.GetValue(); //-------------- // IY: Close the session and connection with QuickBooks sessionManager.EndSession(); booSessionBegun = false; sessionManager.CloseConnection(); return EditSequence; } catch (Exception ex) { IntegrationLog.Log(QBID, "PopulateQBItems: Failed with exception:" + ex.Message); if (booSessionBegun) { sessionManager.EndSession(); sessionManager.CloseConnection(); } throw; } } #endregion public static string CrackException(Exception ex) { while (ex.InnerException != null) { ex = ex.InnerException; } return ex.Message + "\r\n-------TRACE------\r\n" + ex.StackTrace; } //case 3717 public static void CrackDisplayAndIntegrationLogException(Exception ex, string methodText="n/a", string extraText="n/a" ) { string message="Exception error\r\nMethod: " + methodText + "\r\nExtra: " + extraText + "\r\n Error:" + CrackException(ex); //Log it so we have it forever in the log IntegrationLog.Log(QBID, message); //display it to the user CopyableMessageBox cb = new CopyableMessageBox(message); cb.ShowDialog(); } /// /// Case 262 addition /// Convert a string to a double handling french canadian locale /// , since qb is not locale aware in it's api /// conversion can fail because .net expects a comma that isn't there /// /// case 262 redux, changed to use tryparse and explicitly replace comma if present /// for french canadian versions /// /// /// /// public static double SafeToDouble(string s) { if (string.IsNullOrEmpty(s)) return 0; if (s.Contains(",")) s = s.Replace(",", "."); double retvalue = 0; if (!double.TryParse(s, out retvalue)) { try { retvalue = Convert.ToDouble(s, System.Globalization.CultureInfo.InvariantCulture); } catch (System.FormatException) { CopyableMessageBox cp = new CopyableMessageBox("SafeToDouble: Can't parse QB string double version number:\r\n[" + s + "]\r\nPlease copy and send this message to AyaNova tech support at support@ayanova.com"); cp.ShowDialog(); throw new System.ApplicationException("SafeToDouble: Can't parse QB string double value number: \"" + s + "\""); } } return retvalue; } //case 632 (sort of) public static void SaveQBIData() { if (QDat.IsDirty) { QBI.AIObject = QDat.XMLData; QBI = (Integration)QBI.Save(); QDat.IsDirty = false; } } //-------------------------------------------------------------------------------- } }