6070 lines
234 KiB
C#
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;
|
|
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------
|
|
}
|
|
}
|