Blog

How to Integrate Craft CMS with Microsoft Dynamics GP Using SmartConnect by eOne Solutions

We were contacted by a design and dev shop who was making a pitch to a new client to use Craft CMS for a site redesign. The whole deal was gated on Craft being able to get form submission data into the client's Microsoft Dynamics GP install. Going from PHP into Windows usually includes a little bit of death and dismemberment, but we at Solspace dig on that sort of thing. So we took the deal.

When Solspace is faced with a new and unknown challenge, we have a process. An important part of that process is to find out who the right people are who can help get answers to questions and do stuff with code and servers. In other words, difficult problems on the web are best solved through good relationships. So we went about building relationships with our dev shop client and their client.

The first step in building a new relationship is to build trust. And one of the first steps in building trust is to tell the truth. In this case telling the truth meant admitting that we had no clue how to get a LAMP stack tool like Craft to talk to MS Dynamics GP. In fact, that's the main part of our discovery and proof of concept process. The goal is to walk into a problem situation with very little clue about the known unknowns and the unknown unknowns and then to walk out with a plan.

So we built up a relationship and we learned quickly. We did a search on the methods people use to connect to MS Dynamics GP. (It's an enterprise accounting tool by the way.) We learned that you really need to buy a subscription to a 3rd party connection provider. You need to load a 3rd party company's product onto the Windows server running GP so that you can then make connections into that machine. We interviewed some principals at the client's company and learned that their support staff had used a product called SmartConnect from eOne Solutions in the past. We got their technical sales group in the line and asked some probing questions. We were reasonably satisfied that we had found a viable candidate.

Before we go into deep technical territory and look at code, for those who choose to leave now, the procedure and code below is what we ended up with once our discovery and proof of concept process was completed. No fewer than 10 IT professionals were involved in getting to this successful conclusion. (No animals were hurt during this event.) Now that discovery was complete we were in a position to write a complete proposal for the full integration build with accurate budget estimates and timelines.

So here are some of the useful technical bits in case you have to do this. And honestly, that's the main reason I'm writing this post. No Google search ever turned up this information for me. So maybe I save you a couple of grey hairs.

  • First, you will be using the WCF REST Service that comes bundled with SmartConnect 2017. It has to be turned on and configured after SmartConnect is installed.
  • Second, in order for that web service to be reachable from the outside, the IT staff who control the Windows server running SmartConnect and GP need to open up a port in the firewall; 5557.
  • Third, SmartConnect WCF needs to be configured to accept incoming traffic. Here's the link for that from their docs.
  • Fourth, the IT team needs to rig up a subdomain, not just an IP address, but a subdomain or top level domain to point into SmartConnect, like this: http://smartconnect.slapakittendaily.com:5557/SmartConnect.svc.
  • Fifth, SmartConnect needs to respond to that fully qualified url, spelled out here I think.
  • Sixth, your SmartConnect admin, not you because it's in a Windows box locked inside a secure server closet on the client's premises, needs to create a map inside SmartConnect. A map tells SmartConnect how to get your data into GP. Get the name of this map. You'll need it in your web service urls.
  • Finally, here's the PHP cURL call that you use to feed data into the SmartConnect map that was created which then translates data into GP. The curl call is POSTing XML to SmartConnect. The XML format is something I hijacked from the docs here.
$xml	= '<NewDataSet> 
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> 
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> 
      <xs:complexType> 
        <xs:choice minOccurs="0" maxOccurs="unbounded"> 
          <xs:element name="Table"> 
            <xs:complexType> 
              <xs:sequence> 
                <xs:element name="CustomerPurchaseOrderNumber" type="xs:string" minOccurs="0" />
                <xs:element name="CustomerNumber" type="xs:string" minOccurs="0" />
                <xs:element name="SOPType" type="xs:string" minOccurs="0" />
                <xs:element name="BatchNumber" type="xs:string" minOccurs="0" />
                <xs:element name="DocumentID" type="xs:string" minOccurs="0" />
                <xs:element name="DocumentDate" type="xs:string" minOccurs="0" />
                <xs:element name="DocumentAmount_Total" type="xs:string" minOccurs="0" />
                <xs:element name="FreightAmount" type="xs:string" minOccurs="0" />
                <xs:element name="PrimaryShiptoAddressCode" type="xs:string" minOccurs="0" />
                <xs:element name="ItemNumber" type="xs:string" minOccurs="0" />
                <xs:element name="UnitofMeasure" type="xs:string" minOccurs="0" />
                <xs:element name="UnitPrice" type="xs:string" minOccurs="0" />
                <xs:element name="ExtendedPrice" type="xs:string" minOccurs="0" />
                <xs:element name="Quantity" type="xs:string" minOccurs="0" />
              </xs:sequence> 
            </xs:complexType> 
          </xs:element> 
        </xs:choice> 
      </xs:complexType> 
    </xs:element> 
  </xs:schema> 
  <Table> 
    <CustomerPurchaseOrderNumber>PO12345</CustomerPurchaseOrderNumber>
    <CustomerNumber>ZAP001</CustomerNumber>
    <SOPType>1</SOPType>
    <BatchNumber>TEST</BatchNumber>
    <DocumentID>STDQTE</DocumentID>
    <DocumentDate>10/26/2017</DocumentDate>
    <DocumentAmount_Total>997.58</DocumentAmount_Total>
    <FreightAmount>99.58</FreightAmount>
    <PrimaryShiptoAddressCode>DALLAS</PrimaryShiptoAddressCode>
    <ItemNumber>12345-K</ItemNumber>
    <UnitPrice>0</UnitPrice>
    <ExtendedPrice>0</ExtendedPrice>
    <UnitofMeasure>Each</UnitofMeasure>
    <Quantity>1</Quantity>
  </Table> 
</NewDataSet>';

//	YOUR_SMARTCONNECT_MAP_NAME is the name of the map that your IT friend created inside SmartConnet.
$url	= 'http://smartconnect.slapakittendaily.com:5557/SmartConnect.svc/runmapdatatable/YOUR_SMARTCONNECT_MAP_NAME';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, "your_username:your_password");	// This is authentication information that SmartConnect is expecting for valid API calls.
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
	'Content-type: application/xml', 
	'Content-length: ' . strlen($xml)
));
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');	// Don't omit this argument. For some reason this CURLOPT_CUSTOMREQUEST matters more than CURLOPT_POST. Both seem to be required.
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

$output = curl_exec($ch);

if (curl_errno($ch))
{
	print curl_error($ch);
	exit();
} else {
	curl_close($ch);
}