Files
ayanova7/source/Plugins/AyaNova.Plugin.QBI/Util.cs
2018-06-29 19:47:36 +00:00

6070 lines
234 KiB
C#

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>
/// Summary description for Util.
/// </summary>
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;
/// <summary>
/// The official and unchanging integration ID for
/// QBI
/// </summary>
public static Guid QBID
{
get
{
return new Guid("{0F8AC430-D304-4726-8361-3FDA33233E68}");
}
}
#endregion
static Util()
{
}
/// <summary>
/// Are you sure prompt
/// </summary>
/// <param name="msg"></param>
/// <param name="title"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Gets icon image from assembly
/// </summary>
/// <param name="ImageName"></param>
/// <returns></returns>
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));
}
/// <summary>
/// Invert a color so it's readable against the passed
/// in color
/// </summary>
/// <param name="col"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Checks to see if an item is linked already
/// </summary>
/// <param name="AyaNovaID"></param>
/// <returns></returns>
static public bool IsLinked(Guid AyaNovaID)
{
return (QBI.Maps[AyaNovaID]!=null);
}
#endregion
#region Pre flight check
/// <summary>
/// Pre flight check, prompt user as required to repair
/// or add missing settings / failed ones
///
/// This method is called AFTER login.
/// </summary>
/// <returns></returns>
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
/// <summary>
/// Check if integration object is present in database, if not then
/// add it.
/// </summary>
/// <returns></returns>
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
/// <summary>
/// Open QB connection
/// gather info required for future
/// transactions
/// </summary>
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 = "<GZNOCLASS>";
/// <summary>
/// Validate the users preferences
/// if any are missing or invalid prompt for them
/// </summary>
/// <returns></returns>
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;
/// <summary>
/// AyaNova ClientPickList
/// </summary>
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;
/// <summary>
/// AyaNova vendor list
/// </summary>
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;
/// <summary>
/// AyaNova rate list
/// </summary>
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;
/// <summary>
/// AyaNova part list
/// </summary>
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
/// <summary>
/// Populate or repopulate the list of
/// </summary>
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;
/// <summary>
/// qb items
/// </summary>
public static DataTable QBItems
{
get
{
return _dtQBItems;
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="QBItemID"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Populate the cached qb data
/// billable
/// </summary>
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;
}
}
/// <summary>
/// Given a qb BaseRef object
/// return the ListID or empty string if null
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
private static string BaseRefIDGrabber(IQBBaseRef b)
{
if (b != null && b.ListID != null)
{
return b.ListID.GetValue();
}
return "";
}
/// <summary>
/// Given a qb Pricetype object
/// return the price or zero on any issue
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
private static decimal PriceGrabber(IQBPriceType p)
{
decimal d = 0;//default
if (p != null)
{
d = System.Convert.ToDecimal(p.GetValue());
}
return d;
}
/// <summary>
/// Given a qb IQBQuanType object
/// return the value as decimal or zero on any issue
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
private static decimal QuanGrabber(IQBQuanType p)
{
decimal d = 0;//default
if (p != null)
{
d = System.Convert.ToDecimal(p.GetValue());
}
return d;
}
/// <summary>
/// Given a qb salespurchase object
/// return the price
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Given a qb salespurchase object
/// return the purchase cose
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
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;
}
/// <summary>
/// return the sales description if available
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
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;
}
/// <summary>
/// return the preferred vendor if available
/// </summary>
/// <param name="sp"></param>
/// <returns></returns>
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;
/// <summary>
/// QB Transaction Classes
/// </summary>
public static DataTable QBClasses
{
get
{
return _dtQBClasses;
}
}
/// <summary>
/// Populate the cached qb data
/// billable
/// </summary>
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;
/// <summary>
/// QB Transaction Templates
/// </summary>
public static DataTable QBInvoiceTemplates
{
get
{
return _dtQBInvoiceTemplates;
}
}
/// <summary>
/// Populate the cached qb data
/// Invoice templates
/// </summary>
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;
/// <summary>
/// QB Transaction Clients
/// </summary>
public static DataTable QBClients
{
get
{
return _dtQBClients;
}
}
#region Address structure
/// <summary>
/// Address properties
/// </summary>
public struct Address
{
public string DeliveryAddress;
public string City;
public string StateProv;
public string Country;
public string Postal;
}
#endregion
/// <summary>
/// Populate the cached qb data
/// of customers / clients
/// </summary>
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;
}
}
/// <summary>
/// Take a qb address and return an AyaNova friendly
/// address structure
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Handle null qb string types with "aplomb" :)
/// </summary>
/// <param name="qs"></param>
/// <returns></returns>
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;
/// <summary>
/// QB Vendors
/// </summary>
public static DataTable QBVendors
{
get
{
return _dtQBVendors;
}
}
/// <summary>
/// Populate the cached qb data
/// of Vendors
/// </summary>
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
/// <summary>
///
/// </summary>
/// <param name="sessionManager"></param>
/// <returns></returns>
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;
/// <summary>
/// QB Transaction Classes
/// </summary>
public static DataTable QBAccounts
{
get
{
return _dtQBAccounts;
}
}
/// <summary>
/// Populate the cached qb account list data
/// </summary>
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;
/// <summary>
/// QB Transaction Classes
/// </summary>
public static DataTable QBTerms
{
get
{
return _dtQBTerms;
}
}
/// <summary>
/// Populate the cached qb terms list data
/// </summary>
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<Guid> 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
/// <summary>
/// Import the indicated customer
/// to an AyaNova client record
/// </summary>
/// <param name="QuickBooksID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated Vendor
/// to an AyaNova vendor record
/// </summary>
/// <param name="QuickBooksID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated QB Item
/// to an AyaNova Rate record
/// </summary>
/// <param name="QuickBooksID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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<Guid> 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"];
}
/// <summary>
/// Import the indicated QB Item
/// to an AyaNova Part record
/// </summary>
/// <param name="QuickBooksID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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<Guid> objectIDList)
{
foreach (Guid g in objectIDList)
{
try
{
Client c = Client.GetItemNoMRU(g);
RefreshQBClientFromAyaNova(c);
}
catch { };
}
}
/// <summary>
/// Refresh the indicated AyaNova client
/// to it's linked QuickBooks customer record
/// </summary>
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<Guid> 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();
}
}
/// <summary>
/// Import the indicated client
/// to QuickBooks customer record
/// </summary>
/// <param name="ClientID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Copy the contents of one address to the other
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
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
/// <summary>
/// Import the indicated Vendor
/// to QuickBooks Vendor record
/// </summary>
/// <param name="VendorID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Refresh the list of AyaNova parts to their linked QuickBooks item records
/// </summary>
/// <param name="objectIDList"></param>
public static void RefreshQBPartFromAyaNova(List<Guid> objectIDList)
{
foreach (Guid g in objectIDList)
{
try
{
Part c = Part.GetItem(g);
RefreshQBPartFromAyaNova(c);
}
catch { };
}
}
/// <summary>
/// Refresh the indicated AyaNova part
/// to it's linked QuickBooks item record
/// </summary>
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
/// <summary>
/// Import a list of ayanova parts to QuickBooks
/// </summary>
/// <param name="objectIDList"></param>
public static void ImportAyaPart(List<Guid> 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();
}
}
/// <summary>
/// Import the indicated part
/// to QuickBooks item record
/// </summary>
/// <param name="VendorID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated service rate
/// to QuickBooks item record
/// </summary>
/// <param name="VendorID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
}
/// <summary>
/// Mismatch properties
/// A structure for storing mismatches identified
///so user can resolve them.
/// </summary>
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;
}
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="WorkorderID">Id of workorder being scanned</param>
/// <param name="MisMatches">An arraylist of mismatch objects</param>
/// <param name="PriceOverrides">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</param>
/// <returns>True if all links ok, false if there are any mismatches at all</returns>
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
/// <summary>
/// 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
/// </summary>
/// <param name="alWorkorders"></param>
/// <returns></returns>
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);
}
}
/// <summary>
/// Add text to the invoice
/// chopping into chunks less than the max 4095 characters limit
/// as necessary
/// </summary>
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);
}
}
/// <summary>
/// Add charge line to the invoice
///
/// </summary>
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;
}
/// <summary>
/// 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
///
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
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;
}
}
//--------------------------------------------------------------------------------
}
}