Creating Attachments in D365

This isn’t one of my usual posts as I always like to start with a business case. I intend to create a blog about the business case / solution that created this issue once all the other  pieces fall into place, so watch out for that (keep them keen by teasing they say)

The Problem

You might not know there is a problem, but there is. Squirrels are a problem, but not my field of expertise, @dynamiccrmcat will have other opinions.

Attachments are a problem. More specifically, trying to add an attachment to D365 via a Flow or PowerApp is a problem. Yes I know we should be using SharePoint or Teams etc, but sometimes you want to keep your data in D365.

You should just be able to “Patch” the Notes entity in a PowerApp with the file you want in the documentbody field of the entity, but you run into problems (bug) with objecttypecodes (it is expecting a GUID and all you know is an entity name).

@JukkaN pointed me to this article on the PowerApp forum which explains the problem in more detail and clearer than I have. This was via a conversation with him and @TattooedCRMGuy where we came to the conclusion that there is a bug in the CDS connector.

So my next thought was to call a Flow from the PowerApp, passing in the file as a parameter. This doesn’t work either. Any which way you try, it ends up with the file being passed as the link to the Azure blob the PowerApp is temporarily using to store the attachment.

Then I started using the developers friend, Google. My first thought is that if I could pass to the Flow a file rather than the URL, this would work. This thought led me to an excellent Youtube video by Paul Culmsee. He shows how to pass a Photo from PowerApps to Flow to save it to Sharepoint. He has the same issue, how to pass a file to flow, and thanks to him, I have duplicated it for saving a file to D365.

So the logic I have deployed is

PowerApps → Custom Connector → Flow → Custom Connector into D365

2 custom connectors here, this is a relative expensive solution, as the user require a PowerApps Plan 1 license for this which might be an addon to their license.

The D365 Connector

Starting at the final step, I utilised a custom connector like in my previous posts to interact with the D365 API directly. I am not going to go through the method of creating the connector, just this custom action. Again, PostMan is your friend. Using this tool, I generated a JSON template to post against the Annotations entity. Firstly, add an action to the connector. 

Next, select Import from sample, and populate as below, obviously using your own instance API endpoint.

Couple of things here, Annotation is the name of the table that Notes are stored in. “objectid_contact@odata.bind” is the single value navigation for linking Contact to the note. In the CDS connector, this field is not visible, you are expected to enter a pair of _objectid_value and objecttypecode, which doesn’t work, hence this blog post (I hope they fix it, but not too soon now I have worked it out). Finally, documentbody is the field where the attachment is stored, as a Base64 string. This is weirdly different than the binary data store used by Sharepoint.

Select Import and the first connector is done.

The Flow

The flow is pretty simple, a HTTP trigger, messing around with the inputs and sending the information to the D365 connector. Simple, but I needed the video by Paul Culmsee to guide me through. The premise being, rather than the usual approach of looking at the body of the web call, we need to take out parts from the query string and the content of the call would be a file. He does a much better job at explaining it that I do, so head over to the video to actually learn.

A standard HTTP trigger. I then use a Compose data operation to take data from the trigger using a formula based on the query parameters passed.

trigger()['outputs']['queries']['filename']

This states that I want find and store the filename parameter passed in the url when the trigger was triggered

The Get File Body does the same but looks at the content of the body that was passed in.

triggerMultipartBody(0)['$content']

The final part is a call to the D365 custom connector, passing in the compose operation outputs

Another Custom Connector

You can’t call a webservice triggered flow from a PowerApp directly. You can call a Flow, but the flow doesn’t pass the appropriate parameters. It only deals with strings. You can’t convert your file to a string in PowerApps. I am obviously going to be proved wrong here, but I will learn. That’s one of the reasons I blog.

Hence, why you need to create a custom connector to pass from PowerApps to the Flow above.

Connectors can be created by uploading a swagger definition. Swagger is an open-source framework to document APIs and has been recently converted to the OpenAPI specification. Obviously connectors can be built from scratch, but because of the file upload that is required, a definition of the API is required.

Paul again comes to my rescue, he has a great blog post that he goes through in detail the file that was produced to support his video. In the Youtube post, he uses a tool that I can not find, but this file walkthrough was enough for me to produce my own file. I am not going to go through the detail here, Paul does a much better job.

In custom connectors, hit the +, then select Import an OpenAPI file.

Give your new connector a name and select your newly created file definition. If your file is correct, you are now presented with a pre-populated definition of your connector. The query parameters displayed include lots of configuration items that should be pre-populated from your Swagger file.

You can see the connector is not expecting a Body.

If you test the connector action there is also an extra parameter it is expecting

This File parameter is essential. Now it’s reading for use!

The PowerApp

To demonstrate the connector, create a new PowerApp. Because this is for the contact entity, use a drop down to get a list of Contacts to attach the note to from D365.

Associate this with D365 using the Common Data Service

Select the Contacts Entity, back in the properties of the control, use Full Name as the Value. Next, add a “Add Picture” control.

Also add a datatable. This time connect to the Annotation (Notes) entity in D365. Make sure you use the D365 connector though. For some reason, the Common Data Service connector does not return the Regarding as a field you can use. Select a few relevant fields, Title, Note, File Name and Document.

In the Values field, filter the datatable by the Contact that is selected in the dropdown and only show those where there is an attachment.

Filter(Notes, Regarding = GUID(ContactDD.Selected.Contact) && !IsBlank(Document))

Selecting a contact now should deliver a list of notes that are attached to that record.

To Upload a document, add a button. This will trigger the custom connector, so this needs to be added to the PowerApp. Select Data sources, add data source

The custom connector that was created earlier should appear. Select it.

In the button OnSelect action, if all is well, enter the name of the connector and action and it should give you a list of parameters you need.

The final call to the connector looks like this

FileUploader.UploadFile(
    AddMediaButton1.FileName,
    ContactDD.Selected.Contact,
    "Added from PowerApps",
    "Added from PowerApps",
    UploadedImage1.Image
);
Refresh(Notes)

The filename comes from the control within the Image upload control, the contact Id is from the selected contact, some text stuff to fill out (you could add a text control to take that input obviously) and then the image.

I refresh the notes data set after I am done so that the list has got the data.

So that’s it, a complicated solution to a “bug/feature” currently in Powerapps.

A screenshot of that squirrel note against the contact in D365, just so you know I am not bluffing.

BTW, squirrels are evil, rats with marketing, don’t believe everything @dynamiccrmcat says, though it is probably just squirrels she is wrong about.

IFTTT / Flow / D365

This will be a quick post, as the connector doesn’t take a lot of configuration.

The Business Scenario

Big Energy has a lot of sales people, all over the country, which make a lot of calls, usually on their mobiles, to potential or current customers, arranging opportunities and resolving any issues.

The Sales Director at Big Energy is concerned about the number of calls that the sales people don’t always log those calls, only when they deem it important and have no traceability about how many calls an individual has made.

Could a solution be found that would log every call a sales person makes to a number D365 knows about?

IFTTT

IFTTT (IF This Then That) is a free web service that allows users to create applets to connect their devices with their services. There are lots of sample applets to automate tasks, my favourite is linking Alexa’s shopping list with Tesco to add everything I add to Alexa to my Tesco order. Simples.

IFTTT works with a lot of devices and it has a stand alone app for smart phones, allowing interaction with the device.

Getting Started

Log in to IFTTT and go to My Applets, then New Applet. Hit the big blue this

Search for phone, and select Android Phone Call (sorry think this is only Android users)

This presents you with several triggers, the IF part. Select Any Phone Call placed. You will have to make another App for received, but same logic.

The next step is to tell IFTTT what you want to do, select the big That button

IFTTT lets you search for all the available services that you can trigger from your data. Search for Web and select Webhooks.

The only option here is to make a web request.

If you have read my previous articles (if not go here or here now and shame on you), creating a Flow trigger from a web request should be straight forward.

Create the Flow

As in the previous articles, start with a generic http trigger in Flow. Firstly, select HTTP trigger, and enter a default body for the JSON. This is generic enough to allow the call so the schema passed from IFTTT can be created.

As the action, send yourself an email, with the Body of the email being the body of the request.

Hit save and go back to the trigger. This URL is the bit you need to pop back into IFTTT.

Connecting Flow and IFTTT

Back in the IFTTT Web Request, paste in the URL. I have changed the Method to Post, content type to json and included in the Body 3 ingredients (data returned by the call placed method trigger into the body. This is formatted appropriately to become a valid JSON Request

This is now ready for testing.

Testing

For testing to commence, a call needs to be logged. This means you need to install the IFTTT app on your phone, log in and check the app you created is available.

Once this is done, make a call.

If successfully, your Flow should run, sending you an email. If not, you can look in the IFTTT log by checking the activity log.

The log highlights what happened for each run, when it was updated etc. Not as user friendly as Flow, but at least you get an error code. I found I was getting 400 errors, as the format for the JSON wasn’t correct.

My email also has the information sent from the request, this is used to tell Flow properly what is expected, allowing access to the properties.

Is the Contact known?

The assumption is that not all calls made by the user will be to known contacts. Either unknown to D365 or personal calls. The first thing that is required is to find the contact. Using the List Records component, use a filter query to return all contacts that match the data coming from IFTTT with any of the phone numbers held against contact. Be careful here, as the number as entered by the user in the contact on their phone or as dialed is used here.

The next step is to check if any contacts were found. As previously, check the length of the return from the previous step has one or more contacts in it. Add the contact id to a variable if contacts were returned, terminate with grace if not.

Create the Phone Call

Creating the phone call is a straight forward call to the CDS Create Record action.

Couple of formulas here, firstly the timestamp that comes from IFTTT looks like

May 01, 2019 at 05:50PM

Flow doesn’t like this, so the expression for Due is

replace(triggerBody()?['occuredat'], ' at ',' ')

Duration from IFTTT is in seconds, in D365 is minutes. A simple divide by 60 puts in the right value

div(int(triggerBody()?['callLength']),60)

Flow is complete.

Can it be be recorded better?

The call that Flow creates is missing 2 key components, the From and To

The CDS connector doesn’t support activity parties (these fields are both party fields, the user can type and search for multiple contacts, users, leads etc to populate this normally). You can get at these via standard API calls, so back to the custom connector.

My previous post on LUIS used a custom connector to close the incident using an action. This walks through creating the connector, so I won’t repeat myself.

I will step through the specific action for creating the call, as it isn’t straight forward. Further, Postman is still your friend. The only way I managed to get this configured is relying on this great tool.

Using Postman, the format of the JSON can be defined, based on the API reference for Phonecalls. This highlights that activity parties are created, and associated with the call to create the phonecall.

{
    "subject": "CC Test",
    "scheduledend": "2019-05-01 12:00",
    "regardingobjectid_contact@odata.bind": "/contacts(A651968A-5660-E911-A973-000D3A3ACAF8)",
    "directioncode": true,
    "scheduleddurationminutes": 6,
    "phonenumber": "test",
    "phonecall_activity_parties": [
        {
            "partyid_contact@odata.bind": "/contacts(A651968A-5660-E911-A973-000D3A3ACAF8)",
            "participationtypemask": "2"
        },
        {
            "partyid_systemuser@odata.bind": "/systemusers(C3AE1146-AD6D-E911-A984-000D3A3AC0C2)",
            "participationtypemask": "1"
        }
    ]
}

Take this JSON body and copy into a new action in your custom connector.

Enter details in the general page, just enough to uniquely identify your action. Of course, you need to be a bit more descriptive if you plan on shipping out the connector.

Select Import from Sample, Select Post, the URL should be just PhoneCalls, paste the JSON above into the body

Flow does some magic and now you can update your connector. I tried testing this, but got a little confused (me of little brain) so I assumed that Flow is good and would handle it and went straight to using the connector in Flow.

Back in Flow, select a new action, custom connector, the new one you just created and the action established.

The populate as below. I know the parameters are not the best names, someone with a bit more time would have tidied this up.

These are the same formulas that were used earlier. You need to add the “/contacts(” etc to each GUID as the data bind requires it.

Run the Flow and just like that, the Phone Call is created in D365 with a Call To correctly established.

Call From is missing, IFTTT doesn’t let the Web call know who called it. I have searched, but IFTTT is meant for home grown activity, so if you have registered the app, you should know the call. The easiest way around this is to pass in a hardcoded value (email) specific to the end user so a System user can be looked up and populated in the parties field.