Saturday, December 1, 2018

Generic utility to Copy Notes and attachment in Microsoft Dynamic 365 CRM


How to Copy notes/attachments from one Entity to another, when there is a One to Many relationship exists between them.

To illustrate we have Account with many Contacts and would like to have all notes n attachment copied from its parent account when contact is created.

Here are the configuration steps, (workflow code is at the end)

Step 1) Create a new workflow processes, so we can call the custom workflow assembly and set its parameter accordingly.



Step 2) Need to set parameter as
Source Attribute Name : parentcustomerid
Target Entity Name              : contact



Demo Steps

Step 1) Create an Account record and attach file in notes and click save.


Step 2) Create child Contact records and Observe Notes and attachment gets the attachment from parent account record.




The workflow Code

public sealed class CopyNotesWorkflow : CodeActivity
{
    #region Workflow Parameters

        [RequiredArgument]
        [Input("Source Attribute Name")]
        public InArgument<string> SourceAttributeName { get; set; }

        [RequiredArgument]
        [Input("Target Entity Name")]
        public InArgument<string> TargetEntityName { get; set; }

    #endregion Input Parameters


    protected override void Execute(CodeActivityContext executionContext)
    {
    #region Manage Tracing and Organization Service
    // Create the tracing service
    ITracingService tracingService = executionContext.GetExtension<ITracingService>();

    if (tracingService == null)
    {
        throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
    }

    tracingService.Trace("Entered CopyNotes.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
        executionContext.ActivityInstanceId,
        executionContext.WorkflowInstanceId);

    // Create the context
    IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

    if (context == null)
    {
        throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
    }

    tracingService.Trace("CopyNotes.Execute(), Correlation Id: {0}, Initiating User: {1}",
        context.CorrelationId,
        context.InitiatingUserId);

    IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
    IOrganizationService orgService = serviceFactory.CreateOrganizationService(context.UserId);

    #endregion

    try
    {
        var sourceAttribute = this.SourceAttributeName.Get(executionContext);
        var targetEntity = this.TargetEntityName.Get(executionContext);


        if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
        {
            Entity reqResponse = (Entity)context.InputParameters["Target"];

            if (!reqResponse.Attributes.Contains(sourceAttribute))
                throw new InvalidPluginExecutionException("Source Relationship Not found !!!");

            var accountId = ((EntityReference)(reqResponse.Attributes[sourceAttribute])).Id;

            // Fetch all Notes and Attachment for associated Leave Request
            QueryExpression Notes = new QueryExpression { EntityName = "annotation", ColumnSet = new ColumnSet(true) };
            Notes.Criteria.AddCondition("objectid", ConditionOperator.Equal, accountId);
            EntityCollection NotesRetrieve = orgService.RetrieveMultiple(Notes);

            if (NotesRetrieve.Entities.Count > 0)
            {
                // For all Notes n Attachments retrieved, loop and create new duplicate notes.
                foreach (var entity in NotesRetrieve.Entities)
                {
                    Entity note = new Entity("annotation");
                    note["objectid"] = new EntityReference(targetEntity, reqResponse.Id);
                    note["objecttypecode"] = targetEntity;// Associate Notes to Request Response

                    if (entity.Attributes.Contains("isdocument"))
                        note["isdocument"] = entity["isdocument"];

                    if (entity.Attributes.Contains("notetext"))
                        note["notetext"] = entity["notetext"];

                    if (entity.Attributes.Contains("filename"))
                        note["filename"] = entity["filename"];

                    if (entity.Attributes.Contains("documentbody"))
                        note["documentbody"] = entity["documentbody"];

                    if (entity.Attributes.Contains("mimetype"))
                        note["mimetype"] = entity["mimetype"];

                    orgService.Create(note);
                }
            }
        }
    }
    catch (Exception ex)
    {
        throw new InvalidPluginExecutionException(ex.ToString(), ex);
    }
    }

}


Some other commonly used examples are Lead to Opportunity.

Configuration for such would be




Thanks.
Vipin Jaiswal
Vipinjaiswal12@gmail.com

Guidelines to write good JavaScript code in Microsoft Dynamic CRMhttps://vjcity.blogspot.com/2019/08/guidelines-to-write-good-javascript.html




8 comments:

Yvan said...

Hi Vipin,

The workflow is not working for me.

When on Demand, the workflow shows no error, but the Notes don't get copied over.

When on Create, the workflow throws an error: Source Relationship Not found !!!

Also, I don't understand why you would use the relationship account_primary_contact, as there can only be one per account. So how could notes get copied over all new contacts? This is a N:1 relationship, not a 1:N.

The 1:N relationship between account and contact is contact_customer_accounts, but even with this set as Source Relationship Name, the workflow does not work for me.

Thanks!

Vipin Jaiswal said...

Hi Yvan,

I have updated my post after looking at your comments.

The workflow parameter does not require relationship name instead it requires the Relationship Attribute name.

I tried it in my trail instance and now it working like a charm.

Thanks for your post.

Yvan said...

Hi Vipin,

Thanks for the update! I just gave this a new try and it's working, but not entirely:)

On create, the workflow works as expecter and all Notes get copied to the child record.

But when I run the workflow on-demand from the contact form, it's does not work. No error message either.

Do you have an idea why?

Swathi said...

Hello Yvan,

Can I know if you found any solution for your requirement? I have same requirement to copy existing records to new entity.

Thanks,
Swathi

Anonymous said...

On Demand Workflow probably doesn't send a Target as Entity which is in source code handled. Add else and throw a new Exception with serialized all names and types of InputParameters and you will know what came :-)

Anonymous said...

Hi

I'd love to give this a try, but what do I do with the workflow code? Is that a plugin or webresource or...where do I put that?

Many thanks,
Pete.

Yvan said...

@Swathi Yes it works fine, but only using in a workflow upon creation. It will not work on-demand, or as a child workflow.

Yvan said...

@Anonymous You need to create a custom workflow. There are a lot of tutorials on YoutTube. This will result in a DLL that you need to register in CRM using Plugin Registration Tool.