Dynamics Email Subject

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

Are you as excited as me now that things are coming together?!?!🙌😜 Probably not .. hehe

So the title might not sound exciting, but when you combine the work done in earlier posts with the work we will do in this post, you have got yourself a good setup for Email support system.

JavaScript

So we continue with the JavaScript from the last post, but here we will add a little something from the CASE. If you remember from the Autonumber post we now have a better CASE number that somewhat makes sense. Let’s use JavaScript to retrieve it.

Go all the way down to the last function

var formContext = "";

function OnCrmPageLoad(executionContext) {
    formContext = executionContext.getFormContext();
    
    //
    //You don't need to change this. Just understand that forms have one the following states when opening
    //
    var FormTypes =
    {
        Undefined: 0,
        Create: 1,
        Update: 2,
        ReadOnly: 3,
        Disabled: 4,
        QuickCreate: 5,
        BulkEdit: 6
    }
    runAlways(formContext);

    switch (formContext.ui.getFormType()) {
        case FormTypes.Create: OnNewFormLoad(); break;
        case FormTypes.Update: OnUpdateFormLoad(); break;
        case FormTypes.ReadOnly: OnReadOnlyFormLoad(); break;
        case FormTypes.Disabled: OnDisabledFormLoad(); break;
        case FormTypes.QuickCreate: OnQuickCreateFormLoad(); break;
        case FormTypes.BulkEdit: OnBulkEditFormLoad(); break;
        case FormTypes.Undefined: alert("Error"); break;
    }
}

//
//I only use the RunAlways, OnNewFormLoad and OnUpdateFormLoad, but i keep the others here if i ever would need. 
//When looking at this you can always know what funtion is running. Easy to read and debug. 
//On my OnNewFOrmLoad I am now calling a function "GetDefaultQueueAndSignature"
//
function runAlways() { }
function OnNewFormLoad() {
    //
    //On new form load we call get Signature and Queue
    //
    GetDefaultQueueAndSignature();
    //
    //For regarding we have to check that it contains ID before getting data, and we have to check that it is case
    //
    var RegardingObject = formContext.getAttribute("regardingobjectid").getValue();
    if (RegardingObject != null) {
        if (RegardingObject[0].entityType == "incident") {
            GetCaseIDSetSubject();
        }
    }
}
function OnUpdateFormLoad() {
    //
    //On new UpdateForm load we call get Signature and Queue
    //
    GetDefaultQueueAndSignature();
    //
    //For regarding we have to check that it contains ID before getting data, and we have to check that it is case
    //
    var RegardingObject = formContext.getAttribute("regardingobjectid").getValue();
    if (RegardingObject != null) {
        if (RegardingObject[0].entityType == "incident") {
            GetCaseIDSetSubject();
        }
    }
}
function OnReadOnlyFormLoad() { }
function OnDisabledFormLoad() { }
function OnQuickCreateFormLoad() { }
function OnBulkEditFormLoad() { }


//******************************************************************** */
//CUSTOM FUNCTIONS are added below here. Below this point you add all types of functions you need. 
//******************************************************************** */
function GetDefaultQueueAndSignature() {
    //Get User GUID and replace "{" and "}" with blanks. 
    var UserID = Xrm.Utility.getGlobalContext().userSettings.userId;
    UserID = UserID.replace("{", "");
    UserID = UserID.replace("}", "");

    //Get User Default Queue and Signature via WebApi
    Xrm.WebApi.online.retrieveRecord("systemuser", UserID, "?$select=_queueid_value&$expand=cs_Signature($select=cs_htmlsignature)").then(
        function success(result) {
            var Id = "{" + result["_queueid_value"] + "}";
            var Name = result["_queueid_value@OData.Community.Display.V1.FormattedValue"];
            var LogicalName = result["_queueid_value@Microsoft.Dynamics.CRM.lookuplogicalname"];
            if (result.hasOwnProperty("cs_Signature")) {
                var Signature = result["cs_Signature"]["cs_htmlsignature"];
            }
            if (LogicalName == null || Signature == null) {
                alert("User Record missing Queue and/or Signature");
                return;
            }

            //Set FROM lookup to queue
            formContext.getAttribute("from").setValue([{ id: Id, name: Name, entityType: LogicalName }]);
            //Set signature before current text in body
            var Body = formContext.getAttribute("description").getValue();
            if(Body != null){
                formContext.getAttribute("description").setValue("<br /><br />" + Signature + Body);
            }else{
                formContext.getAttribute("description").setValue("<br /><br />" + Signature);
            }
            
        },
        function (error) {
            Xrm.Utility.alertDialog(error.message);
        }
    );
}

function GetCaseIDSetSubject() {
    //Get Uswer GUID and replace "{" and "}" with blanks. 
    var CaseID = formContext.getAttribute("regardingobjectid").getValue()[0].id;
    CaseID = CaseID.replace("{", "");
    CaseID = CaseID.replace("}", "");

    Xrm.WebApi.online.retrieveRecord("incident", CaseID, "?$select=ticketnumber").then(
        function success(result) {
            var CaseNumber = result["ticketnumber"];
            var Subject = formContext.getAttribute("subject");
            //
            //Check if Subject contains data
            //
            if (Subject.getValue() != null) {
                var SubjectContainsID = Subject.getValue().includes(CaseNumber);
                //
                //IF subject does not contain casenumber, i add the casenumber to the subject
                //
                if (SubjectContainsID == false) {
                    formContext.getAttribute("subject").setValue(Subject.getValue() + " - " + CaseNumber);
                }
            } 
            //
            //This is a new email without a subject. Get the CaseNumber, and inform that topic has to be set
            //
            else {
                formContext.getAttribute("subject").setValue("[Insert Topic] - " + CaseNumber);
            }
        },
        function (error) {
            Xrm.Utility.alertDialog(error.message);
        }
    );

}

We are getting the CASE number from Case / Incident. Then we perform a few checks to see if there is an active subject in the email, and if the email contains the case number from before.

New Email

Let’s see what happens when I now open a new email from a case. Not as a reply to email, but as a new email thread.

Look at that. You have the subject pre populated with a notification that the customer agent should add a topic. This of course could be defaulted to whatever you want in the script, but I left it like this.

Existing Email reply

In this case we see what happens when I reply to an email a customer sent to my CRM system

The CRM Email will look like this. Now I hit reply

Now this is what the subject line should look like.

Conclusion

This small detail means the world of a difference for the people working with customer service and the customers. Whenever they are referring to a case, they can use the same case number. No more tracking token idiocrasy. When you use the global search in dynamics you get hits from the emails and the cases. That is what I call being just a little more efficient!

Please implement this as OOTB Microsoft.

As you can see my posts are now coming together. Next post includes a little extra for quality of data, before I add a video of how to use it.

Dynamics 365 Email – HTML Signature and Sender

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

Every time I setup Customer Service I encounter the same 2 problems.
1. I want to send from a QUEUE
2. I need to automatically add a signature

Am I really the only one having these problems, or are we all doing the same silly tasks every time?

Standard Signature OOTB

You can enter a standard signature per user, but this signature does not support HTML formatting for some reason. The email you are writing to is in fact a HTML email. Signature that is injected is text into a HTML, but the signature editor is a simple text editor.

When we open the email, the default signature will appear.

PlussMinus
1. You can use standard MS functions to auto insert signature

2. Users can easily make changes to personal signature.


1. Does not support HTML formatting of signature

2. Every user can configure personal signature, and you can’t control it for company profiling.

3. If you work with customer service you have to 4-5 clicks to find the correct signature for Customer Service to enter.

Standard Sender FROM

Whenever you open an email in Dynamics it will default to your email address. Normally I would say this if fine, but why on earth would you want to do this when setting up a Customer Service system?

A user would have to change the FROM to the Customer service

Imagine having to do this every single time you want to answer an email in Dynamics Customer Service! I personally would go crazy.

Design

I solve this by creating a custom entity called Signature and use a standard field on User “Default Queue” to solve the 2 problems. This involves some javascript, but will provide everything you need. In my last post of this series, I will also include the managed and unmanaged files you need to do it yourself.

From the Email I will now retrieve the HTML signature and default sender for this contact.

1. HTML Signature

I found this little tool that generates an HTML signature. It seems like a cool little piece of free software that will create nice HTML signatures. The HTML you generate, can be copy pasted directly into the signature entity.

https://si.gnatu.re/

After messing about for a while I got the following result.

Example HTML

<table width="351" cellspacing="0" cellpadding="0" border="0"> <tr> <td style="vertical-align: top; text-align:left;color:#000000;font-size:12px;font-family:helvetica, arial;; text-align:left"> <span><span style="margin-right:5px;color:#000000;font-size:15px;font-family:helvetica, arial">Customer Service Team</span> <br><span style="margin-right:5px;color:#000000;font-size:12px;font-family:helvetica, arial">Super Duper Helper</span></span> <br> <span style="font:12px helvetica, arial;">E-mail&nbsp;<a href="mailto:support@support.com" style="color:#3388cc;text-decoration:none;">support@support.com</a></span> <br> <span style="font:12px helvetica, arial;">Telephone&nbsp;<span style="color:#000000">22222222</span></span> <br> <table cellpadding="0" cellpadding="0" border="0"><tr><td style="padding-right:5px"><a href="https://facebook.com/https://www.facebook.com/pointtakenas" style="display: inline-block;"><img width="40" height="40" src="https://s1g.s3.amazonaws.com/d927102059ee8fca87e6e3f49e77e9e9.png" alt="Facebook" style="border:none;"></a></td><td style="padding-right:5px"><a href="https://twitter.com/https://twitter.com/point_taken_" style="display: inline-block;"><img width="40" height="40" src="https://s1g.s3.amazonaws.com/83255843bbcec47d809669aff3b22691.png" alt="Twitter" style="border:none;"></a></td><td style="padding-right:5px"><a href="https://linkedin.com/company/https://www.linkedin.com/company/pointtaken-as" style="display: inline-block;"><img width="40" height="40" src="https://s1g.s3.amazonaws.com/85f05a94630abd611533bc2566751eb7.png" alt="LinkedIn (Company)" style="border:none;"></a></td><td style="padding-right:5px"><a href="https://youtube.com/https://www.youtube.com/channel/UC4rPzEDfHPp393oqOozUqaw" style="display: inline-block;"><img width="40" height="40" src="https://s1g.s3.amazonaws.com/a6f64a587978d338c0b8f6cc2d90bd0b.png" alt="YouTube" style="border:none;"></a></td><td style="padding-right:5px"><a href="https://instagram.com/https://www.instagram.com/pointtakenas/" style="display: inline-block;"><img width="40" height="40" src="https://s1g.s3.amazonaws.com/05f979c6025f3a336303ec824ebb815c.png" alt="Instagram" style="border:none;"></a></td></tr></table><a href="https://www.pointtaken.no" style="text-decoration:none;color:#3388cc;">www.pointtaken.no</a> </td> </tr> </table> 

Next you click on COPY HTML and open the Support Signature record (CUSTOM) on the user entity.

Just copy paste the HTML right inn to the text area. Save and Close

2. Javascript

Now you have to create a JavaScript file on the Email form. In the solution you can download in my last post you will find that I add a form to Email that you can use. I chose not to overwrite the default form.

var formContext = "";

function OnCrmPageLoad(executionContext) {
    formContext = executionContext.getFormContext();
    
    //
    //You don't need to change this. Just understand that forms have one the following states when opening
    //
    var FormTypes =
    {
        Undefined: 0,
        Create: 1,
        Update: 2,
        ReadOnly: 3,
        Disabled: 4,
        QuickCreate: 5,
        BulkEdit: 6
    }
    runAlways(formContext);

    switch (formContext.ui.getFormType()) {
        case FormTypes.Create: OnNewFormLoad(); break;
        case FormTypes.Update: OnUpdateFormLoad(); break;
        case FormTypes.ReadOnly: OnReadOnlyFormLoad(); break;
        case FormTypes.Disabled: OnDisabledFormLoad(); break;
        case FormTypes.QuickCreate: OnQuickCreateFormLoad(); break;
        case FormTypes.BulkEdit: OnBulkEditFormLoad(); break;
        case FormTypes.Undefined: alert("Error"); break;
    }
}

//
//I only use the RunAlways, OnNewFormLoad and OnUpdateFormLoad, but i keep the others here if i ever would need. 
//When looking at this you can always know what funtion is running. Easy to read and debug. 
//On my OnNewFOrmLoad I am now calling a function "GetDefaultQueueAndSignature"
//
function runAlways() { }
function OnNewFormLoad() {
    GetDefaultQueueAndSignature();
}
function OnUpdateFormLoad() {
    GetDefaultQueueAndSignature();
}
function OnReadOnlyFormLoad() { }
function OnDisabledFormLoad() { }
function OnQuickCreateFormLoad() { }
function OnBulkEditFormLoad() { }


//
//CUSTOM FUNCTIONS are added below here. Below this point you add all types of functions you need. 
//
function GetDefaultQueueAndSignature() {
    //Get User GUID and replace "{" and "}" with blanks. 
    var UserID = Xrm.Utility.getGlobalContext().userSettings.userId;
    UserID = UserID.replace("{", "");
    UserID = UserID.replace("}", "");

    //Get User Default Queue and Signature via WebApi
    Xrm.WebApi.online.retrieveRecord("systemuser", UserID, "?$select=_queueid_value&$expand=cs_Signature($select=cs_htmlsignature)").then(
        function success(result) {
            var Id = "{" + result["_queueid_value"] + "}";
            var Name = result["_queueid_value@OData.Community.Display.V1.FormattedValue"];
            var LogicalName = result["_queueid_value@Microsoft.Dynamics.CRM.lookuplogicalname"];
            if (result.hasOwnProperty("cs_Signature")) {
                var Signature = result["cs_Signature"]["cs_htmlsignature"];
            }
            if (LogicalName == null || Signature == null) {
                alert("User Record missing Queue and/or Signature");
                return;
            }

            //Set FROM lookup to queue
            formContext.getAttribute("from").setValue([{ id: Id, name: Name, entityType: LogicalName }]);
            //Set signature before current text in body
            var Body = formContext.getAttribute("description").getValue();
            if(Body != null){
                formContext.getAttribute("description").setValue("<br /><br />" + Signature + Body);
            }else{
                formContext.getAttribute("description").setValue("<br /><br />" + Signature);
            }
            
        },
        function (error) {
            Xrm.Utility.alertDialog(error.message);
        }
    );
}

Save and publish.

The Result is beautiful

We have now saved the service technician many clicks, and the signature looks great. Follow the next post to see how we manage the subject field to “master” the Smart Matching function.

Dynamics 365 Case Cascading Rules

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

The OOTB cascading rules for Case are not optimal for the setup that I like to use. I will try to explain why, and at the same time show what I feel needs to be done for it to work better. Keyword is Parental Relationships that we have to STOP. If you wonder what I am talking about, below is the timeline from Case displaying activities.

What happens when you have parental relationship on, and you assign the case to another user, all of the related entities like Task, Emai, Phone Call etc will also be reassigned. Normally you you could ask why that would matter, but for CASE that is a real issue.

You also happen to change the “Modified On” data for the records leaving them potentially unsorted in a manner that will confuse the service agents. This applies to all activities, even the ones that have been closed as complete.

There are MANY other ways to sort activities, but Emails are almost always historical activities and happen at a given time. Tasks and Phone calls can be in the future. This is why I like modified on date, to keep a sort of chronological order. If possible the best sorting would be chronological order for completed activities, and due date sorted on top of that for open activities. Don’t think that is possible unfortunately.

Configuration

As you can see, the default behavior is Parental for Email, Phonecall and Task. I could have chosen more, but these are the entities I use the most in Case.

Do the following for all 3 relationships. Select Configurable Cascading and then Cascade None for the Assign. Microsoft DOCS cascading – If you wan to see more about Cascading Rules.

Why not NEW UI Config?

At the time of writing this blog, the configuration still isn’t possible for relationships. If you are familiar with configuring, the main point is just showing what I need to do for Dynamics.

Dynamics 365 – Email to Case

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

The new name for this function is now called “Automatic Record Creation and Update Rules”. You find it under Service Management in the settings area.

Setup

I will try to explain why I believe this is the proper way to set it up, even though many of you probably will have a different preference.

Queue:
Choose the queue that we created in the previous post

Create Cases for activities associated with a resolved case:
The purpose of this field is to create a new Case when you receive an email on an old existing case.

I believe this setting together with 1 minute is the correct setting. Many will argue that I m wrong, but there is a reason why. Dynamics doesn’t have an automatic case reopen when an email is received on a closed case (A mystery why they haven’t done anything about it yet). Without this feature the email would just land on the closed case and never bee seen. With this setup a new case will be created once you receive an email for a case that is closed.

Certain support metrics work against their purpose. Just think about the good old
“Number of cases closed in a day”
“Time Case was open before closed”

In the end, the customer wants to be treated well, and feel confident that the organizations can help with the issue. I try to educate that the case close confirmation should be done for every case.

With the current setting, emails will create Cases that Customer Service will be able to follow up. Sure you might get more cases, but you won’t loose any. A case not answered might be a customer lost!

Email Template:
The template provided here is a nice start. Do what you need for changes. Notice that the case number here is included in the subject, and this is something we want to use later on also! Stay tuned for later post:)

Creating the record

Once you choose CASE as entity to create, the system will provide with default values that are fine.

Email Router Tracking – Smart Matching vs Tracking Token

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

I am sure there are equally many opinions about the best practice here as there are for the Managed vs UnManaged discussion for solutions in Dynamics. I have my preference, and I hope it all makes sense when I am done with my blog series about Customer Service:)

Why I hate Tracking Token

Why someone at Microsoft thought this was a smart number to add to an email is beyond anything i can understand. Every support system I have tried uses this type of functionality in emails, but the number is always the same as the case number. Microsoft managed to choose this logic🤢

Struktur for sporingstoken

While this gives the system a fairly unique number to work with, the number itself is completely useless for reference. If a customer calls inn regarding this case number you will not get many hits in the Case search. This number is not even unique pr case. It will change based on who is answering the Case, just to confuse the users even more.

This number should be the case number, and that is what I will fix in a later post.

Why I hate Smart Matching

So Smart Matching is supposed enables us to not use Tracking Token…. Or does it? Smart Matching tries to understand the subject, to/from etc etc to figure out where to track this email. Does this process fail? You bet your sweet ass it does:) If you have a customer sending a new email with the same subject but for 2 completely different matters, it might connect it to the wrong record.

Is there a better solution?

Some prefer to combine the two options. I still don’t want the tracking token, so I have a slightly different approach. I will show you in a later post in the series, where I discuss how I handle the email subject.

Settings

Back to the actual post.

Lets open the System Settings to see what I choose. You can also go via https://admin.powerplatform.microsoft.com to navigate via new setup, but for some reason Microsoft doesn’t auto fill out the numbers below. I still do this old school.

Filter: ^[\s]([\w]+\s?:[\s])+
Max number of subject: 20
Min diff: 0
Min Number: 2

Other Bloggers

You can head on over to CRM Ninja and Neil Parkhurst to see their opinions about the matter. Neil explains what is possible, and CRM Ninja talks about a certain case he had. https://neilparkhurst.com/2016/11/19/mb2-710-track-and-correlate-email/
https://dynamicsninja.blog/2019/04/18/when-smart-matching-became-dumb-matching/

Dynamics 365 QUEUE setup with shared mailbox

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here
I assume you have a Dynamics Environment at this point.

Exchange

Open https://admin.microsoft.com – login with the credentials you got from the trial, or make sure you are a Global Admin to complete the following tasks.

You now have a shared mailbox in Exchange. We will be using this email address to configure a queue in Dynamics Customer Service.

Dynamics 365 – Queue

If you don’t remember the address to your dynamics environment, you can always find your environments at https://admin.powerplatform.microsoft.com <- click … and then open.

Locate the queue setup from Dynamics Customer Service Hub. This is where we create a new queue for Support@. For the owner we choose Customer Service Team.

Click Save, and the system will automatically give you a new mailbox.

To configure the mailbox, we need to navigate to Advanced Settings.

Email Configure

Open Mailboxes and find Support@ that we created.

Your picture should look something like below. Following order
1. Approve Email – You must be global admin to do so.
2. Test & Enable Mailbox
3. Follow progress in Alerts

If you have done it right, you should see the following. This process can be tricky, and there are lots of great guides out there that might cover this better. Connecting Exchange with Dynamics it typically really easy, or really hard.

Lets se if it works

So after waiting for a few minutes, I now see the email in the queue 🙂

Dynamics Customer Service Autonumber

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

Why on earth did anyone think that this was a reasonable number to include in Customer Service cases?

This number is way to hard to relate to, and too hard to explain why came to be. It has absolutely nothing to do with the Email Tracking Token, leaving the customer utterly confused when talking about numbers.

Might be a little hard to see here, but I open my solution from https://make.powerapps.com and select the case entity. In the Case entity I have the default Case Number field. When choosing to edit this field I simplify the autonumber. This will make even more sense later on.

A larger image

All of this will be included in my Customer Service Solution later on in the guide.

Dynamics 365 Customer Service Team

This post is a part of a series of posts for Customer Service. The complete GUIDE can be found here

I prefer to define a team with a security role when i create Customer Service solutions. The idea is that the incoming case is assigned to the Customer Service team. This way it is clear to see what cases are new and not yet assigned.

NB! Reassigning Cases to a new user Out Of The Box is not great. The cascading rules changes the sorting of emails because of modified on dates. I will therefore include a post on how to modify the cascading rules.

Security Role

Open Dynamics in any app and find the Advanced Settings

This will open up the good old Dynamics Setup. This is where we can access the security model.

We need to create a security role for the team.

These are the entities that I needed to make my scenario work. It is a very minimal role, but I won’t be needing more than this for Customer Service Team.

Team

Open teams and create a new team. I will call mine Customer Service. Make sure you assign the security role to the newly created team.

Dynamics 365 Customer Service Trial

There are many guides out there for this exact step. I only recreated this so all the content is on one page under my control.

In Private

Open a browser in private mode

Open Dynamics 365 Trials

Click the “Sign up here” !!

If you are a customer, click “no, continue signing up”. If you are a partner you should look for different places to create demos. Head on over to CDX Expereiences

It doesn’t matter what you choose here. If you ever turn this into production, the domain name will be something you can change at a later time.

Continue proving you are not a robot, and then wait for everything to finish up.

Start the setup when it’s done. Doesn’t matter what you choose in your setup of apps. It usually installs everything anyway. Eventually you will end up here, and you can open Customer Service HUB:

We have now completed the first step, and created a new trial for Customer Service.

Adding Exchange

With out Exchange added to your trial we won’t get far. Open https://admin.microsoft.com

Start “Get Free Trial”, and click the next next etc etc.

Back in the portal you want to open users, and locate your user. Choose the Microsoft E3 subscription and attach this to your contact. Remember to click “SAVE CHANGES” in the bottom when you are done.

SSIS – Importing 94,883 contacts to Dynamics/CDS/Dataverse

This is a quick guide to the simplest form of SSIS import. I will not cover all of the amazing ETL possibilities you can do with SSIS, because I am not the correct person to set this up:)

The background for this post is also trying to learn the differences between DataFlow and SSIS in regards to API stats online.

The setup

  1. Install BIDS LINK HERE – If you don’t have visual studio
  2. Install KingswaySoft LINK HERE
  3. Get Excel File with contacts (94,883)

The structure is very simple here. Get file, add one data column and then import to dynamics.

The flat file I am getting is a simple .csv file and you see the following columns.

Next step is a bit odd, but that’s how lookups work in SSIS. I have to create a new column that is called “account”, because i have to define the lookup type. This Derived Column does nothing else.

In the end we connect to Dynamics via the KingswaySoft connector.

I have chosen to use alternate keys. If you wonder what this is, look at my previous post about alternate keys and DataFlow.

Then I click on columns to map the fields

For the lookup we connect to ParentCustomer. Once you have mapped the field, i can choose to what field to match on the Account. I open the “…” and here I choose the primary key for account as matching. You could use whatever you want, but I have created alternate key for account on AccoundNumber.

The result

I ran this test during the night time at the same time as DataFlow. SSIS used 1 hour and 22 minutes for the exact same file, and that is fine by me. There are so many different variables in online load times, that 20 minutes doesn’t really prove anything.

What about the API?

I wrote a post about DataFlow and API. Mostly because I was curious about the analytics within CDS. I don’t understand the numbers i see, so I have written to Support about the numbers. What I heard back was because of the batch of records being entered, this should be about right…

If this is correct, SSIS is a lot more efficient in the API calls to Dynamics, but took more time in this one single test i performed.

SSIS = 3,501 API Calls
DataFlow = 13,544 API Calls

Some good tips from the pros

https://twitter.com/CrmKeeper/status/1242722929248526338