Sunday, 15 January 2017

Integrating with your favorite CRM/ERP web based client

- or poor mans integration?

Sometimes you need to integrate to other systems in the organisation. And the ways of doing it always matters on which interfaces/skills/tools are at hand - but also whether you need a tight dependency and if you want or can customized anything on that client.

In the below example I will just illustrate how a standard Microsoft Dynamics 2016 CRM "Lookup Up Record" dialog can be used to link some other data/application that has interest in the CRM data - so that doing BI across systems might be more fun.

There are several ways of getting interacting with the data:

- Used something like CData's Data Binding/ODBC components. TMS might also have some.
- Use the Web API - and REST.
- Use the SDK and C# to create a simple wrapper.

Some of these are either not cheap, needs a bit of work or requires additional authentication work -
which is how it should be.

But if the user is already authenticated and user of Microsoft Dynamics CRM system, and you just want to be able to read a bit of data like Ids and Names and things - then just utilizing the standard dialog could be a way to do that.

This also has the bonus that you do not have to re-implement the search and filtering the user is used to from the CRM system - even if you could had ended up with a better result yourself :).

So I just created a form, threw a TWebBrowser component and a couple of TLabel in there.

On the FormCreate event we navigate to the URL of "Dialog" you want to show:

WebBrowser1.Navigate('<Your org.>/_controls/lookup/lookupinfo.aspx?LookupStyle=single&browse=0&showpropbutton=1&AllowFilterOff=1&objecttypes=1');

The objecttypes parameter is referring to what type of objects/entities you want the dialog to work on:

  1. Account
  2. Contact
  3. Opportunity
  4. Lead
  5. ...

You can see a full list here: Organization service entities

The user might not have access to some of these so do check for that in you code, you might also want to check for the availability of the page - some of these systems might be down for regular maintenance or upgrade :)

So running the program shows me the Account "Lookup Up Record" dialog from Microsoft Dynamics CRM:

 - since the dialog and the TWebBrowser in this case isn't that helpfully to me - I need to do a bit more.

One thing I might be able to do was to run a bit of JavaScript to load and capture the result back to me that the dialog might decide that I should have, but in this case I used a bit of generic "brute-force" to stay in control.

And in regards to JavaScript - do not use window.showModalDialog - it has been deprecated for a while - just saying.

I wanted to figure out when the "Add" button was pressed - everything else was of no interest to me, so I just added a message handler:

procedure TForm1.MyMessages(var Msg: TMsg; var Handled: Boolean);
  X, Y: Integer;
  document: OleVariant;
  E: OleVariant;
  if (Msg.message = WM_LBUTTONDOWN) and IsDialogMessage(WebBrowser1.Handle, Msg) then
    X := LOWORD(Msg.lParam);
    Y := HIWORD(Msg.lParam);
    document := WebBrowser1.Document;
    E := document.elementFromPoint(X, Y);
    Added := (E.innerHTML='Add');
  Handled := False;

and assigned it to the application OnMessage in the FormCreate:

Application.OnMessage := MyMessages;

Now I can see if the element that is clicked in the TWebBrowser is the 'Add' button - a word of caution - since this is conceptual, you might want to check on something else if you could risk that the dialog did show in other languages.

Now since the dialog closes when any of the buttons are clicked, I did add a bit of code to the TWebBrowser.OnWindowClosing event:

procedure TForm1.WebBrowser1WindowClosing(ASender: TObject;
  IsChildWindow: WordBool; var Cancel: WordBool);
  lst : TStringList;
  i : Integer;
  if Added then // The boolean set by MyMessages handler. 
// Code parsing the HTML by looking for the Class='ms-crm-List-SelectedRow'
// and the content within like the 'oid' attibute and what is in the 
// 'ms-crm-List-DataCell inner-grid-cellPadding' classes.
// Grab what you need at assign it to some properties or labels.
  Cancel := True;

All set and done - running the program again and clicking the 'Add' button:

gives me the GUID from the 'oid' attibute and the Account "Name".

So now I can simply "link" my data to an CRM object - without throwing customized scripts at my CRM installation, or making a tight integration to my perfectly working legacy system :)