locked
Urgent help needed understanding and correcting a custom workflow RRS feed

  • Question

  • CRM2013

    Our company retrieves multiple reports on a daily basis in xml format, for our customers which are imported into CRM entities to produce readable reports with SSRS. Currently we are manually creating PDF files and manually emailing them to customers. I have been trying to create a custom workflow that will automate the task when a record is created. In the instance below, the xml file is imported into the entity along with supporting data and lookups. I have been trying to follow several examples but am running into several problems.

    Entity – “new_bacsddicreports

    With the code below, I get the following error ( at the bottom ) when running the workflow. I am currently logged onto CRA system Administrator

    The entity has a lookup to account (Account – Name, new_bacsddicreport lookup field  - new_licence)

    This is a test report, but I would like to progress this to looking up the email address from Account (new_ReportsEmail & new_ReportsemailCC)

    Once I can get to that stage, I should be able to work out how to pass a parameter to the real report

    Thank you for any help in advance

    Current Workflow code (I have masked the passwords etc)

    // <copyright file="SendReports.cs" company="">

    // Copyright (c) 2014 All Rights Reserved

    // </copyright>

    // <author></author>

    // <date>10/9/2014 3:04:48 PM</date>

    // <summary>Implements the SendReports Workflow Activity.</summary>

    namespace FMAutoReports.Workflow

    {

        using System;

        using System.Activities;

        using System.ServiceModel;

        using Microsoft.Xrm.Sdk;

        using Microsoft.Xrm.Sdk.Workflow;

        using Microsoft.Xrm.Sdk.Messages;

        using Microsoft.Crm.Sdk.Messages;

        using FMAutoReports.Workflow.ReportService;

        public sealed class SendReports : CodeActivity

        {

            /// <summary>

            /// Executes the workflow activity.

            /// </summary>

            /// <param name="executionContext">The execution context.</param>

            protected override void Execute(CodeActivityContext executionContext)

            {

                // Create the tracing service

                ITracingService tracingService = executionContext.GetExtension<ITracingService>();

                if (tracingService == null)

                {

                    throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");

                }

                tracingService.Trace("Entered SendReports.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("SendReports.Execute(), Correlation Id: {0}, Initiating User: {1}",

                    context.CorrelationId,

                    context.InitiatingUserId);

                IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();

                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                try

                {

                    // TODO: Implement your custom Workflow business logic.

                                    // MY CODE

                    Guid new_bacsddicreportsId = context.PrimaryEntityId;

                    byte[] result = null;

                    FMAutoReports.Workflow.ReportService.ReportExecutionService rs = new ReportExecutionService();

                    // Credential to connect with CRM

                    rs.Credentials = new System.Net.NetworkCredential("xxxx", "xxxx", "xxx");

                    // Setting the URL of the Reporting Server

                    rs.Url = "http://xxx.xxx.xx/ReportServer/ReportExecution2005.asmx";

                    string reportPath = "/xxxxx_MSCRM/Facilities Management ARUDD Reconocile";

                    // Specify the report path from the reporting server

                    // Note: To get the report name, report must be published for the external use.

                    // To do this edit the report from CRM and publish it for external use.

                    // After publishing it for external use report name will be visible in the reporting server instead of the report id.

                    string format = "PDF";

                    string historyID = null;

                    string devInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";

                    string encoding;

                    string mimeType;

                    string extension;

                    Warning[] warnings = null;

                    string[] streamIDs = null;

                    ExecutionInfo execInfo = new ExecutionInfo();

                    ExecutionHeader execHeader = new ExecutionHeader();

                    rs.ExecutionHeaderValue = execHeader;

                    execInfo = rs.LoadReport(reportPath, historyID);

                    String SessionId = rs.ExecutionHeaderValue.ExecutionID;

                    result = rs.Render(format, devInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);

                    //Create email activity

                    Entity email = new Entity();

                    email.LogicalName = "email";

                    email.Attributes.Add("regardingobjectid", new EntityReference("new_bacsddicreports", new_bacsddicreportsId));

                    //Creating EntityReference for from, to and cc. Need to be changed according to your requirement

                    //               EntityReference from = new EntityReference("systemuser", senderUserId);

                     //              EntityReference to = new EntityReference("account", );

                    //                EntityReference cc = new EntityReference("contact", recieverCCUserId);

                   // EntityReference from = new EntityReference("systemuser", senderUserId);

                   // EntityReference to = new EntityReference("systemuser",recieverUserId);

                    //Creating party list

                    // Adding from, to and cc to the email

                    email.Attributes.Add("from", "me@home.co.uk");

                    email.Attributes.Add("to", "him@home.co.uk");

                    email.Attributes.Add("cc", "her@home.co.uk");

                    email.Attributes.Add("subject", "TEST SENDING PDF FROM CRM ");

                    email.Attributes.Add("description", "This is a test to email a report direct from crm when a record is created");

                    Guid emailID = service.Create(email); // Create the email

                    // Attaching Pdf Report

                    int NextActorID = new int();

                    RetrieveEntityRequest request = new RetrieveEntityRequest();

                    request.LogicalName = "email";

                    RetrieveEntityResponse response = (RetrieveEntityResponse)service.Execute(request);

                    int objecttypecode = response.EntityMetadata.ObjectTypeCode.Value;

                    Entity attachment = new Entity("activitymimeattachment");

                    attachment.Attributes.Add("subject", "Report");

                    attachment.Attributes.Add("filename", "Test Report.pdf");

                    attachment.Attributes.Add("body", Convert.ToBase64String(result));

                    attachment.Attributes.Add("filesize", Convert.ToInt32(result.Length.ToString()));

                    attachment.Attributes.Add("mimetype", "text/plain");

                    attachment.Attributes.Add("attachmentnumber", NextActorID);

                    attachment.Attributes.Add("objectid", new EntityReference("email", new Guid(email.Id.ToString())));

                    attachment.Attributes.Add("objecttypecode", objecttypecode);

                    service.Create(attachment); //Create the attachment

                    // Sending email

                    SendEmailRequest reqSendEmail = new SendEmailRequest();

                    reqSendEmail.EmailId = emailID;

                    reqSendEmail.TrackingToken = "";

                    reqSendEmail.IssueSend = true;

                    SendEmailResponse res = (SendEmailResponse)service.Execute(reqSendEmail);

                    // END MY CODE

                }

                catch (FaultException<OrganizationServiceFault> e)

                {

                    tracingService.Trace("Exception: {0}", e.ToString());

                    // Handle the exception.

                    throw;

                }

                tracingService.Trace("Exiting SendReports.Execute(), Correlation Id: {0}", context.CorrelationId);

            }

        }

    }

    Current error when running the workflow

    Plugin Trace:

    [FMAutoReports.Workflow: FMAutoReports.Workflow.SendReports]

    [BACS Reports: ARUDD Listing]

    Entered SendReports.Execute(), Activity Instance Id: 6, Workflow Instance Id: 6ccb1b04-533a-4655-a121-71a49e81b025

    SendReports.Execute(), Correlation Id: 773cbd84-8542-4a9b-b86f-a6bef6d59385, Initiating User: 68413345-e5b9-e311-80b8-00155d04fa2d

    Exception: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: The e-mail must have at least one recipient before it can be sent (Fault Detail is equal to Microsoft.Xrm.Sdk.OrganizationServiceFault).

    Error Message:

    Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: The e-mail must have at least one recipient before it can be sentDetail:

    <OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">

      <ErrorCode>-2147218684</ErrorCode>

      <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />

      <Message>The e-mail must have at least one recipient before it can be sent</Message>

      <Timestamp>2014-10-09T16:29:08.7600823Z</Timestamp>

      <InnerFault>

        <ErrorCode>-2147218684</ErrorCode>

        <ErrorDetails xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />

        <Message>The e-mail must have at least one recipient before it can be sent</Message>

        <Timestamp>2014-10-09T16:29:08.7600823Z</Timestamp>

        <InnerFault i:nil="true" />

        <TraceText i:nil="true" />

      </InnerFault>

      <TraceText>[FMAutoReports.Workflow: FMAutoReports.Workflow.SendReports]

    [BACS Reports: ARUDD Listing]

    Entered SendReports.Execute(), Activity Instance Id: 6, Workflow Instance Id: 6ccb1b04-533a-4655-a121-71a49e81b025

    SendReports.Execute(), Correlation Id: 773cbd84-8542-4a9b-b86f-a6bef6d59385, Initiating User: 68413345-e5b9-e311-80b8-00155d04fa2d

    Exception: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: The e-mail must have at least one recipient before it can be sent (Fault Detail is equal to Microsoft.Xrm.Sdk.OrganizationServiceFault).</TraceText>

    </OrganizationServiceFault>

       at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.Execute(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType, Boolean checkAdminMode)

       at Microsoft.Crm.Extensibility.InprocessServiceProxy.ExecuteCore(OrganizationRequest request)

       at FMAutoReports.Workflow.SendReports.Execute(CodeActivityContext executionContext)

       at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)

       at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)


    Dont ask me .. i dont know

    Thursday, October 9, 2014 5:47 PM

Answers

All replies

  • Hi Pete,

    the from, to and cc attributes in an email entity are an array of ActivityParty Entities.

    See the following code (sorry, just copied from VB.NET):

    Dim party As New Entity("activityparty")
    party("partyid") = rcpt.ToEntityReference()
    Dim recipient As New EntityCollection({party})
    mail("to") = recipient

    rcpt is an Entity (account or contact) here. If you just have an email address, then use the addressused attribute of the activityparty instead of partyid.

    You can also have a look here: http://msdn.microsoft.com/en-us/library/hh210217.aspx

    HTH

    Frank



    Friday, October 10, 2014 11:51 AM
  • Hello Pete,

    The error message is "The e-mail must have at least one recipient before it can be sent". I am not sure if you can declare

    email.Attributes.Add("to", "him@home.co.uk");

    a combination of

    EntityReference from = new EntityReference("systemuser", senderUserId);
    EntityReference to = new EntityReference("account", accoutID?????);
    EntityReference cc = new EntityReference("contact", recieverCCUserId);

    And approximatly

    // Create the 'From:' activity party for the email
                ActivityParty fromParty = new ActivityParty
                {
                    PartyId = new EntityReference(SystemUser.EntityLogicalName, _userId)
                };
    
                // Create the 'To:' activity party for the email
                ActivityParty toParty = new ActivityParty
                {
                    PartyId = new EntityReference(Contact.EntityLogicalName, _contactId)
                };
                Console.WriteLine("Created activity parties.");
    
                // Create an e-mail message.
                Email email = new Email
                {
                    To = new ActivityParty[] { toParty },
                    From = new ActivityParty[] { fromParty },
                    Subject = "SDK Sample e-mail",
                    Description = "SDK Sample for SendEmail Message.",
                    DirectionCode = true
                };

    as shown in http://msdn.microsoft.com/en-us/library/hh210217.aspx

    What i wish to highlight is that activityparty (from, to, cc) etc is of type activity type array. you got to fix this before the email will get processed,

    Cheers,

    Jithesh

    Friday, October 10, 2014 11:59 AM
  • Add the input parameter just at the end as

            [RequiredArgument]
            [Input("Sender")]
            [ReferenceTarget("systemuser")]
            public InArgument<EntityReference> Sender { get; set; }
    
            [RequiredArgument]
            [Input("Recipient")]
            [ReferenceTarget("account")]
            public InArgument<EntityReference> Recipient { get; set; }

    and use this input parameter in code as below

                    Guid senderUserId = Sender.Get<EntityReference>(executionContext).Id;
                    Guid recieverUserId = Recipient.Get<EntityReference>(executionContext).Id;
    
                    EntityReference from = new EntityReference("systemuser", senderUserId);
                    EntityReference to = new EntityReference("account", recieverUserId);
    
                    //Creating party list
                    Entity fromParty = new Entity("activityparty");
                    fromParty.Attributes.Add("partyid", from);
                    Entity toParty = new Entity("activityparty");
                    toParty.Attributes.Add("partyid", to);
    
                    EntityCollection collFromParty = new EntityCollection();
                    collFromParty.EntityName = "systemuser";
                    collFromParty.Entities.Add(fromParty);
    
                    EntityCollection collToParty = new EntityCollection();
                    collToParty.EntityName = "account";
                    collToParty.Entities.Add(toParty);



    Muhammad Sohail




    • Edited by sohail450 Friday, October 10, 2014 12:25 PM
    Friday, October 10, 2014 12:21 PM
  • HI,

    Thank you for your help, although I am still struggling trying to get my head around this.

    Please could you show an example,

    The entity that the record is being added to (new_bacsddicreports) has a lookup (new_licence) to the accounts entity. The Accounts entity has 2 custom fields Reports Email and Reports Email CC

    I cannot work out how to adapt your sample so that when I add a record in the new_bacsddicreport that the email is sent out to the email address in the accounts record ie

    in new_bacsddicreports I add a record that has a lookup to account for customer 123456. Customer 123456 has a new_reportsemail field value of Him@home.co.uk and a new_reportsemailcc value of her@home.co.uk 

    thank you in advance for your help


    Dont ask me .. i dont know


    • Edited by Pete Newman Saturday, October 11, 2014 5:50 PM
    Saturday, October 11, 2014 5:46 PM
    • Marked as answer by Pete Newman Tuesday, January 13, 2015 10:09 PM
    Saturday, October 11, 2014 10:44 PM