diff --git a/AyaNovaQBI/AyaNovaQBI.csproj b/AyaNovaQBI/AyaNovaQBI.csproj
index 302c51c..d87ac84 100644
--- a/AyaNovaQBI/AyaNovaQBI.csproj
+++ b/AyaNovaQBI/AyaNovaQBI.csproj
@@ -12,6 +12,7 @@
512
true
true
+ false
publish\
true
Disk
@@ -24,7 +25,6 @@
true
0
1.0.0.%2a
- false
false
true
@@ -37,6 +37,7 @@
DEBUG;TRACE
prompt
4
+ false
AnyCPU
@@ -51,7 +52,7 @@
False
True
- ..\libs\QuickBooks\Interop.QBFC15.dll
+ ..\..\..\..\Program Files\Common Files\Intuit\QuickBooks\Interop.QBFC15.dll
packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
@@ -78,6 +79,12 @@
+
+ Form
+
+
+ CopyableMessageBox.cs
+
Form
@@ -101,6 +108,9 @@
auth.cs
+
+ CopyableMessageBox.cs
+
MainForm.cs
diff --git a/AyaNovaQBI/CopyableMessageBox.Designer.cs b/AyaNovaQBI/CopyableMessageBox.Designer.cs
new file mode 100644
index 0000000..45f56da
--- /dev/null
+++ b/AyaNovaQBI/CopyableMessageBox.Designer.cs
@@ -0,0 +1,105 @@
+namespace AyaNovaQBI
+{
+ partial class CopyableMessageBox
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.edOut = new System.Windows.Forms.TextBox();
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.panel2 = new System.Windows.Forms.Panel();
+ this.btnCopy = new System.Windows.Forms.Button();
+ this.panel1.SuspendLayout();
+ this.panel2.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // edOut
+ //
+ this.edOut.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.edOut.Location = new System.Drawing.Point(0, 0);
+ this.edOut.Multiline = true;
+ this.edOut.Name = "edOut";
+ this.edOut.ReadOnly = true;
+ this.edOut.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.edOut.Size = new System.Drawing.Size(488, 356);
+ this.edOut.TabIndex = 0;
+ //
+ // panel1
+ //
+ this.panel1.Controls.Add(this.edOut);
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel1.Location = new System.Drawing.Point(0, 0);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(488, 356);
+ this.panel1.TabIndex = 1;
+ //
+ // panel2
+ //
+ this.panel2.Controls.Add(this.btnCopy);
+ this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.panel2.Location = new System.Drawing.Point(0, 281);
+ this.panel2.Name = "panel2";
+ this.panel2.Size = new System.Drawing.Size(488, 75);
+ this.panel2.TabIndex = 2;
+ //
+ // btnCopy
+ //
+ this.btnCopy.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnCopy.Location = new System.Drawing.Point(135, 23);
+ this.btnCopy.Name = "btnCopy";
+ this.btnCopy.Size = new System.Drawing.Size(208, 40);
+ this.btnCopy.TabIndex = 0;
+ this.btnCopy.Text = "Copy all to clipboard";
+ this.btnCopy.UseVisualStyleBackColor = true;
+ this.btnCopy.Click += new System.EventHandler(this.btnCopy_Click);
+ //
+ // CopyableMessageBox
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(488, 356);
+ this.Controls.Add(this.panel2);
+ this.Controls.Add(this.panel1);
+ this.Name = "CopyableMessageBox";
+ this.ShowInTaskbar = false;
+ this.Load += new System.EventHandler(this.CopyableMessageBox_Load);
+ this.panel1.ResumeLayout(false);
+ this.panel1.PerformLayout();
+ this.panel2.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox edOut;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Panel panel2;
+ private System.Windows.Forms.Button btnCopy;
+ }
+}
\ No newline at end of file
diff --git a/AyaNovaQBI/CopyableMessageBox.cs b/AyaNovaQBI/CopyableMessageBox.cs
new file mode 100644
index 0000000..63575c7
--- /dev/null
+++ b/AyaNovaQBI/CopyableMessageBox.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace AyaNovaQBI
+{
+ public partial class CopyableMessageBox : Form
+ {
+ private string mDisplay = string.Empty;
+ public CopyableMessageBox(string Display)
+ {
+ InitializeComponent();
+ mDisplay=Display;
+ }
+
+ private void btnCopy_Click(object sender, EventArgs e)
+ {
+ Clipboard.SetData(System.Windows.Forms.DataFormats.Text, mDisplay);
+ }
+
+ private void CopyableMessageBox_Load(object sender, EventArgs e)
+ {
+ edOut.Text = mDisplay;
+
+ }
+ }
+}
diff --git a/AyaNovaQBI/CopyableMessageBox.resx b/AyaNovaQBI/CopyableMessageBox.resx
new file mode 100644
index 0000000..29dcb1b
--- /dev/null
+++ b/AyaNovaQBI/CopyableMessageBox.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/AyaNovaQBI/MainForm.cs b/AyaNovaQBI/MainForm.cs
index ee753aa..5c64cd9 100644
--- a/AyaNovaQBI/MainForm.cs
+++ b/AyaNovaQBI/MainForm.cs
@@ -19,7 +19,7 @@ namespace AyaNovaQBI
async private void MainForm_Load(object sender, EventArgs e)
{
-
+
//Initialize
StringBuilder initErrors = new StringBuilder();
if (await util.InitializeQBI(initErrors) == false)
diff --git a/AyaNovaQBI/Properties/AssemblyInfo.cs b/AyaNovaQBI/Properties/AssemblyInfo.cs
index 3fc2e4a..587b876 100644
--- a/AyaNovaQBI/Properties/AssemblyInfo.cs
+++ b/AyaNovaQBI/Properties/AssemblyInfo.cs
@@ -17,7 +17,7 @@ using System.Runtime.InteropServices;
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
+[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("82cd8e13-3297-472b-8c59-c4a50a69cb5c")]
diff --git a/AyaNovaQBI/Timestamp.cs b/AyaNovaQBI/Timestamp.cs
index 5ff832d..7b7b79a 100644
--- a/AyaNovaQBI/Timestamp.cs
+++ b/AyaNovaQBI/Timestamp.cs
@@ -22,7 +22,7 @@ namespace AyaNovaQBI
///
/// Do not modify the definition of BuildAt as your changes will be discarded.
///
- public static DateTime BuildAt { get { return new DateTime(637915929965140980); } } //--**
+ public static DateTime BuildAt { get { return new DateTime(637915984703608685); } } //--**
///
/// The program that time stamped it.
///
diff --git a/AyaNovaQBI/util.cs b/AyaNovaQBI/util.cs
index 97464b1..f48c129 100644
--- a/AyaNovaQBI/util.cs
+++ b/AyaNovaQBI/util.cs
@@ -7,6 +7,9 @@ using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
+using Interop.QBFC15;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
namespace AyaNovaQBI
{
@@ -456,6 +459,21 @@ namespace AyaNovaQBI
#endregion
#region QB STUFF
+
+
+ public static string QCountry = "US";
+ public static double QVersion = 1.1;
+ public static string QCompanyFile = "";
+ public static string QCompanyName = "";
+ public static string sLastRequestXML = "";
+
+ public enum pfstat
+ {
+ OK = 0,
+ Failed = 1,
+ Cancel = 2
+ }
+
public static List GetInvoiceableItems()
{
var random = new Random();
@@ -488,7 +506,7 @@ namespace AyaNovaQBI
return false;
}
-
+
//Get license
var r = await GetAsync("license");
ALicense = r.ObjectResponse["data"]["license"].ToObject();
@@ -518,30 +536,35 @@ namespace AyaNovaQBI
return false;
}
+ //PFC - integration object check (fetch or create if not present)
+ //Need this early so can log any issues with other aspects
+ if (!await IntegrationCheck(initErrors))
+ return false;
+ LOG_AVAILABLE = true;
+
- //PFC - Util.QBValidate stuff:
//Validate QB connection can be made and open connection and start session with QB (see Util.QBValidate in v7
+ try
+ {
+
+ var pfstatus = await QBValidate();
+ }
+ catch(Exception ex)
+ {
+ initErrors.AppendLine($"QuickBooks connection validation failed before connecting\r\n{ex.Message}");
+
+ return false;
+ }
//once connected collect the country, version we are dealing with (Util.qbValidate)
//confirm qb is 2008 or newer and bail if not (util.qbvalidate)
//cache company name and other qb info
//PFC - PopulateQBListCache()
//PFC - PopulateAyaListCache()
- //PFC - integration object check (fetch or create if not present)
- if (!await IntegrationCheck(initErrors))
- return false;
- LOG_AVAILABLE = true;
-
- //todo: logger needs to be enabled for integration next in a convenient to call util method and then make it work in UI
- await IntegrationLog("Test: boot up");
- for (int x = 0; x < 25; x++)
- await IntegrationLog($"Test entry {x}");
-
- var res=await GetAsync($"integration/log/{QBIntegration.Id}");
-
- //TODO: if QBIntegration.IntegrationData==null then nothing is set yet and it's fresh so trigger the setup stuff
//PFC - Validate settings, create if necessary (Util.ValidateSettings()) and save
+ //TODO: if QBIntegration.IntegrationData==null then nothing is set yet and it's fresh so trigger the setup stuff
+
//PFC - verify integration mapped objects still exist at each end (Util.PreFlightCheck() line 199)
//DONE
@@ -602,10 +625,277 @@ namespace AyaNovaQBI
public static async Task IntegrationLog(string logLine)
{
- await PostAsync("integration/log", Newtonsoft.Json.JsonConvert.SerializeObject(new NameIdItem { Id=QBIntegration.Id, Name=logLine}));
+ await PostAsync("integration/log", Newtonsoft.Json.JsonConvert.SerializeObject(new NameIdItem { Id = QBIntegration.Id, Name = logLine }));
}
- #endregion
+
+
+ #region PFC QB side
+ ///
+ /// Open QB connection
+ /// gather info required for future
+ /// transactions
+ ///
+ public async static Task 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
+ {
+ await IntegrationLog("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)
+ {
+ await IntegrationLog("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)
+ {
+ await IntegrationLog("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");
+
+ await IntegrationLog("PFC: Failed with exception:" + ex.Message);
+ if (booSessionBegun)
+ {
+ sessionManager.EndSession();
+ sessionManager.CloseConnection();
+ }
+ throw;
+ }
+ }
+ #endregion pfc qb side
+
+
+
+
+
+ #region QB Specific utils
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Handle null qb string types with "aplomb" :)
+ ///
+ ///
+ ///
+ private static string ProcessQBString(IQBStringType qs)
+ {
+ if (qs == null) return "";
+ return qs.GetValue();
+ }
+
+ ///
+ /// Case 262 addition
+ /// Convert a string to a double handling french canadian locale
+ /// , since qb is not locale aware in it's api
+ /// conversion can fail because .net expects a comma that isn't there
+ ///
+ /// case 262 redux, changed to use tryparse and explicitly replace comma if present
+ /// for french canadian versions
+ ///
+ ///
+ ///
+ ///
+ public static double SafeToDouble(string s)
+ {
+ if (string.IsNullOrEmpty(s)) return 0;
+ if (s.Contains(","))
+ s = s.Replace(",", ".");
+
+ double retvalue = 0;
+ if (!double.TryParse(s, out retvalue))
+ {
+ try
+ {
+ retvalue = Convert.ToDouble(s, System.Globalization.CultureInfo.InvariantCulture);
+ }
+ catch (System.FormatException)
+ {
+ CopyableMessageBox cp = new CopyableMessageBox("SafeToDouble: Can't parse QB string double version number:\r\n[" +
+ s +
+ "]\r\nPlease copy and send this message to AyaNova tech support at support@ayanova.com");
+ cp.ShowDialog();
+ throw new System.ApplicationException("SafeToDouble: Can't parse QB string double value number: \"" + s + "\"");
+ }
+ }
+ return retvalue;
+ }
+
+ #endregion qb specific utils
+
+
+
+
+ #endregion qb stuff
}
diff --git a/libs/QuickBooks/QBFC15_0Installer.exe b/libs/QuickBooks/QBFC15_0Installer.exe
new file mode 100644
index 0000000..ec2d8a6
Binary files /dev/null and b/libs/QuickBooks/QBFC15_0Installer.exe differ