Thursday, August 27, 2015

QuickBooks Web Connector with WCF services

I have been working a project to try and get some backend data plugged into QuickBooks so we can push that data into QuickBooks. We are using desktop QuickBooks hosted by another provided so our integration options are limited. We ended up using the QuickBooks Web Connector. First let me say that documentation on this front is limited and what you can find is normally old. Here is some of the reference we were able to find:
Web connector overview
QuickBooks error codes
Helpful UI for how to construct QuickBooks API calls
Web connector SDK
QBWC Dev Guide
You will need to create a QWC file and here is a QWC Example.
One of the biggest issues we ran into was all the documents and examples we could find were based on the older ASMX services. Because of this we struggle for a bit on understanding how the service needed to be attributed. So here is what our WCF service contract ended need to be set as.
 

   [GeneratedCode("wsdl", "4.6.57.0")]

   [WebServiceBinding(Name = "QuickBookConnectorService", Namespace = "http://developer.intuit.com/")]

   public class QuickBookConnectorService : QBWebConnectorSvc, IQuickBookConnector

   {

       /// <summary>Provides a way for web-service to notify QBWC of it's version. This version string shows up in 

       /// the More Information pop-up dialog in QBWC.</summary>

       /// <returns>The <see cref="string"/>.</returns>

       [OperationContract(Action = "http://developer.intuit.com/serverVersion")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string serverVersion()

       {

       }

 

       /// <summary>Optional callback allows the web service to evaluate the current web connector version and react to it.</summary>

       /// <param name="strVersion">The version of the QB web connector supplied in the web connector's call to clientVersion</param>

       /// <returns>The <see cref="string"/>See QB Dev guide for details on return values</returns>

       [OperationContract(Action = "http://developer.intuit.com/clientVersion")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string clientVersion(string strVersion)

       {

       }

 

       /// <summary>The authenticate.</summary>

       /// <param name="strUserName">The username the connector should be running as to authenticate.</param>

       /// <param name="strPassword">The password the connector should be running as to authenticate.</param>

       /// <returns>The <see cref="string[]"/>Must contain 2 elements. 1) NONE, NVU, BUSY, string.empty or Company file name. 

       /// 2) Value noted how long to postpone the update 

       /// 3) Sets lower limit on how often QBWC can call 

       /// 4) Sets the min run seconds</returns>

       [OperationContract(Action = "http://developer.intuit.com/authenticate")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string[] authenticate(string strUserName, string strPassword)

       {

       }

 

       /// <summary>Tells your web service about an error the web connector encountered in its attempt to connect to QuickBooks or QuickBooks POS.</summary>

       /// <param name="ticket">The ticket from the web connector. This is the session token your web service returned to the web connector's

       ///  authenticate call, as the first element of the returned string array</param>

       /// <param name="hresult">The HRESULT (in HEX) from the exception thrown by the request processor</param>

       /// <param name="message">The error message that accompanies the HRESULT from the request processor.</param>

       /// <returns>The <see cref="string"/>.</returns>

       [OperationContract(Action = "http://developer.intuit.com/connectionError")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string connectionError(string ticket, string hresult, string message)

       {

       }

 

       /// <summary>The web connector's invitation to the web service to send a request.</summary>

       /// <param name="ticket">The ticket from the web connector. This is the session token your web service returned to the web connector's

       ///  authenticate call, as the first element of the returned string array.</param>

       /// <param name="strHCPResponse">Only for the first sendRequestXML call in a data exchange session will this parameter contains response 

       /// data from a HostQuery, a CompanyQuery, and a PreferencesQuery request. This data is provided at the outset of a data exchange because

       /// it is normally useful for a web service to have this data.In the ensuing data exchange session, subsequent sendRequestXML calls from 

       /// the web processor do not contain this data, (only an empty string is supplied) as it is assumed your web service already has 

       /// it for the session. </param>

       /// <param name="strCompanyFileName">The company file being used in the current data exchange.</param>

       /// <param name="qbXMLCountry">The country version of QuickBooks or QuickBooks POS product being used to access the company. .</param>

       /// <param name="qbXMLMajorVers">The qb xml major vers.</param>

       /// <param name="qbXMLMinorVers">The qb xml minor vers.</param>

       /// <returns>The <see cref="string"/>Either string of "NoOp" to tell the QBWC to pause or a valid qbXML string</returns>

       [OperationContract(Action = "http://developer.intuit.com/sendRequestXML")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string sendRequestXML(

           string ticket,

           string strHCPResponse,

           string strCompanyFileName,

           string qbXMLCountry,

           int qbXMLMajorVers,

           int qbXMLMinorVers)

       {

       }

 

       /// <summary>Returns the data request response from QuickBooks or QuickBooks POS.</summary>

       /// <param name="ticket">The ticket from the web connector. This is the session token your web service returned to the web connector's

       ///  authenticate call, as the first element of the returned string array.</param>

       /// <param name="response">Contains the qbXML response from QuickBooks or qbposXML response from QuickBooks POS.</param>

       /// <param name="hresult">The hresult and message could be returned as a result of certain errors that could occur when 

       /// QuickBooks or QuickBooks POS sends requests is to the QuickBooks/QuickBooks POS request processor

       /// via the ProcessRequest call.If this call to the request processor resulted in an error (exception) instead of a 

       /// response, then the web connector will return the corresponding HRESULT and its text message in the hresult and message 

       /// parameters.If no such error occurred, hresult and message will be empty strings.</param>

       /// <param name="message">See above under hresult.</param>

       /// <returns>The <see cref="int"/>A positive integer less than 100 represents the percentage of work completed.</returns>

       [OperationContract(Action = "http://developer.intuit.com/receiveResponseXML")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override int receiveResponseXML(string ticket, string response, string hresult, string message)

       {

       }

 

       /// <summary>Allows your web service to return the last web service error, normally for display to the user, before 

       /// causing the update action to stop.</summary>

       /// <param name="ticket">The ticket from the web connector. This is the session token your web service returned to the web connector's 

       /// authenticate call, as the first element of the returned string array.</param>

       /// <returns>The message <see cref="string"/> describing the problem and any other information that you want your user to see</returns>

       [OperationContract(Action = "http://developer.intuit.com/getLastError")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string getLastError(string ticket)

       {

       }

 

       /// <summary>Tells your web service that the web connector is finished with the update session</summary>

       /// <param name="ticket">The ticket from the web connector. This is the session token your web service returned to the web connector's 

       /// authenticate call, as the first element of the returned string array</param>

       /// <returns>The <see cref="string"/>Specify a string that you want the web connector to display to the user showing the status of

       /// the web service action on behalf of your user.This string will be displayed in the web connector UI in the status column.</returns>

       [OperationContract(Action = "http://developer.intuit.com/closeConnection")]

       [XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]

       public override string closeConnection(string ticket)

       {

       }
Consolibyte has some good information on his wiki as well.






Tuesday, August 25, 2015

Installing Tridion 2013 SP 1 on Hyper-V

I recently needed to install Tridion on Hyper-V. To do so I found a great series of blog posts on the Tridion website. The series starts with setting up a VM image instead of a Hyper-V. Really there is no difference (you install the OS on the image). One issue I did run into with Hyper-V though was my local network started to run a bit slower. I did some digging and found out it was because when the network bridge was setup for the Hyper-V image the priority order of the adapters put the Hyper-V image first. IT Writing has a great blog post on how to fix this.

Once you have Hyper-V ready you can follow the post step for step to get everything installed. I needed once change in that I already had a Content Management database setup. I wanted to move my database from production down into this Hyper-V instance so I did not need to setup the database from scratch.Here is what I did and a few of the tweaks I needed to make.

After installing I would try and open up the Tridion CM website. However, my login always failed. Looking into the event viewer’s security log I found the following:

An account failed to log on.

Subject:
    Security ID:        NULL SID
    Account Name:        -
    Account Domain:        -
    Logon ID:        0x0

Logon Type:            3

Account For Which Logon Failed:
    Security ID:        NULL SID
    Account Name:        MTSUser
    Account Domain:        WIN-M3213T5Q594

Failure Information:
    Failure Reason:        An Error occured during Logon.
    Status:            0xC000006D
    Sub Status:        0x0

Since the Hyper-V image was running outside of a domain controller I did some digging and found a Microsoft KB article that fixed the issues. My resolution was method 1.

The other thing I had to do was update the Trustees table in the Content Manager database. Keep in mind that I am restoring an existing database. That existing one is on a domain controller so the MTSUser account that was created was linked to a different domain. So in the Trustees table find your MTSUser and in the name column update the domain part of the domain to whatever your Hyper-V’s machine name is (<machine name>\MTSUser). This assumes that during your install you said your system account was MTSUser.

After these steps I was able to login and all was good.