Friday, 23 November 2012

Dynamics CRM 2011 - Creating an e-mail activity with PDF format for custom SSRS report as an attachment


Last updated – 18th Nov 2017

This blog has come up with a Good news for all CRM Developers, who are struggling with PDF loading issues. Sometimes, after implemeting this code, developers can generate the PDF file but when they download and open it, they get an error that the file is currpted or not able to open the PDF file.

This is happening because of the report paramenters. Please perform below steps if you're facing the problem,

First, open your '.rdl' file (e.g. report.rdl) in XML form.
Then, find <QueryParameters> tab in report XML and validate your parameters with the parameters you're passing in the GetReportingSession() function in your code.

Hope this will be helpful.


Last updated – 10th June 2016

This blog post uses SDK.SOAP.js to perform all CRM web service calls.
Please refer following link to know more about the SDK.SOAP.js.
http://ankit.inkeysolutions.com/2014/10/sdksoapjs.html


Recently, I came across a requirement where I had to create a functionality that will create an email activity on “Order” entity that would be having a custom report in PDF format as an attachment. This functionality needs to be executed on a ribbon button click. I couldn’t find any complete example that covers all these functionalities. Hence, in this post, I will be sharing the complete solution to generate PDF file from a custom SSRS report and attach the same to newly created email activity.

Please follow below steps to achieve this functionality:

1) Variable initialization and EmailReport(), a starting point, this method needs be called via JavaScript code.
2) Creating an email activity for the “Order” entity.
3) On this step, we will fetch the SSRS report's GUID by using report name.
4) Creating an attachment for the email activity and binding the report in a PDF format as attachment.

Variable initialization and EmailReport() method that can be called via JavaScript code. – Step 1

This code initializes the variables, which are required in next steps of this post. Plus, it defines two JavaScript methods,


var reportName = "SampleReport"; //Please specify your report name.
var reportId = null;
var fileName = "PDF01"; //Please specify a file which you want to save.
var fromFieldId = null;
var fromFieldEntityName = null;
var toFieldId = null;
var toFieldEntityName = null;
var emailId = null;

//This function is need to be called from action side e.g. Ribbon button click.
function EmailReport() {
  //This function does not work in add mode of form.
  var type = Xrm.Page.ui.getFormType();
  if (type != 1) {
    GetIds(); // Set ids in global variables.
    CreateEmail(); //Create Email
  }
}

//Gets the fromFieldId and toFieldEntityName used to set Email To & From fields
function GetIds() {
  fromFieldId = "00000000-0000-0000-0000-000000000000";//The Guid which needs to be set in form field.
  fromFieldEntityName = "systemuser";//Please specify entity name for which you have specified above Guid. Most probably it's systemuser.
  toFieldId = "00000000-0000-0000-0000-000000000000"; //The Guid which needs to be set in to field.
  toFieldEntityName = "contact";//Please specify entity name for which you have specified above Guid.
}


Creating an email activity for the “Order” entity – Step 2

With this step we will first create an email activity for the “Order” entity. When you create an email activity for an entity you must associate the entity and activity with either To, From or Regarding participation type. In this example the entity “Order” for which the email activity is created, is associated as Regarding in the email activity.

Now, we will have to set “To” and “From” fields of the email activity. The “To” and “From” are Activity parties associated with the email activity.

//Create Email and link it with Order as Reagrding field
function CreateEmail() {
  var id = Xrm.Page.data.entity.getId();
  id = id.replace('{', "");
  id = id.replace('}', "");
  var entityLogicalName = Xrm.Page.data.entity.getEntityName();
  var regardingObjectId = new Sdk.EntityReference(entityLogicalName, id);

  var email = new Sdk.Entity("email");
  email.addAttribute(new Sdk.String("subject", "Your Booking"));
  email.addAttribute(new Sdk.Lookup("regardingobjectid", regardingObjectId));
  var fromParties = PrepareActivityParty(fromFieldId, fromFieldEntityName);
  email.addAttribute(new Sdk.PartyList("from", fromParties));
  var toParties = PrepareActivityParty(toFieldId, toFieldEntityName);
  email.addAttribute(new Sdk.PartyList("to", toParties));
  Sdk.Async.create(email, EmailCallBack, function (error) { alert(error.message); });
}

//This method get entity's id and logical name and return entitycollection of it.
function PrepareActivityParty(partyId, partyEntityName) {
  var activityParty = new Sdk.Entity("activityparty");
  activityParty.addAttribute(new Sdk.Lookup("partyid", new Sdk.EntityReference(partyEntityName, partyId)));
  var activityParties = new Sdk.EntityCollection();
  activityParties.addEntity(activityParty);
  return activityParties;
}

// Email Call Back function
function EmailCallBack(result) {
  emailId = result;
  GetReportId();
}
 

On this step, we will fetch the SSRS report's GUID by using report name. - Step 3

Use below code to fetch the SSRS report GUID using the SSRS report name.

//This method will get the reportId based on a report name

function GetReportId() {
  var columns = "reportid";
  var fetchQuery = new Sdk.Query.QueryByAttribute("report");
  fetchQuery.setColumnSet(columns);
  fetchQuery.addAttributeValue(new Sdk.String("name", reportName));
  Sdk.Async.retrieveMultiple(fetchQuery, RetrieveMultiple);
}

function RetrieveMultiple(entityCollection) {
  if (entityCollection.getEntities().getCount() > 0) {
    var entities = entityCollection.getEntities();
    var getAttribute = entities.getByIndex(0).getAttributes("reportid");
    if (getAttribute != null) {
      reportId = getAttribute.getValue();
    }
  }

  //get reporting session and use the params to convert a report in PDF
  var params = GetReportingSession();
  EncodePdf(params);
}


Creating an attachment for the email activity and binding the report in a PDF format as attachment - Step 4

Once we are done with creating an email activity and fetching the GUID value of the SSRS report in above steps, we will come to this step. Here it is creating an attachment for this newly created email activity.

The function GetReportingSession() gets the required details for converting a report into PDF. Using the function EncodePdf(), the report is converted into PDF format.

//Gets the report contents
function GetReportingSession() {

  var pth = Xrm.Page.context.getClientUrl() + "/CRMReports/rsviewer/reportviewer.aspx";
  var retrieveEntityReq = new XMLHttpRequest();
  var Id = Xrm.Page.data.entity.getId();

  retrieveEntityReq.open("POST", pth, false);
  retrieveEntityReq.setRequestHeader("Accept", "*/*");
  retrieveEntityReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  var orgUniqueName = Xrm.Page.context.getOrgUniqueName();

  //The following line is required only when you have parameters to be passed to your SSRS custom report.
  var reportPrefilter = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'><entity name='salesorder'><all-attributes /><filter type='and'><condition attribute='salesorderid' operator='eq' value='" + Xrm.Page.data.entity.getId() + "' /></filter></entity></fetch>";

var req = "id=%7B" + reportId + "%7D&uniquename=" + orgUniqueName + "&iscustomreport=true&reportName=" + reportName + "&isScheduledReport=false&p:CRM_FilteredSalesOrder=" + reportPrefilter;
  retrieveEntityReq.send(req);


  var StartAfterComment = retrieveEntityReq.responseText.indexOf("e440105867d249ce8a45696677d42bcb") + 32;
  var x = retrieveEntityReq.responseText.lastIndexOf("ReportSession=");
  var ret = new Array();
  ret[0] = retrieveEntityReq.responseText.substr(x + 14, 24); //the session id
  x = retrieveEntityReq.responseText.lastIndexOf("ControlID=");
  ret[1] = retrieveEntityReq.responseText.substr(x + 10, 32); //the control id
  return ret;
}

function EncodePdf(params) {
  var retrieveEntityReq = new XMLHttpRequest();

  var pth = Xrm.Page.context.getClientUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";
  retrieveEntityReq.open('GET', pth, true);
  retrieveEntityReq.setRequestHeader("Accept", "*/*");
  retrieveEntityReq.responseType = "arraybuffer";

  retrieveEntityReq.onload = function (e) {
    if (this.status == 200) {
      var uInt8Array = new Uint8Array(this.response);
      var base64 = Encode64(uInt8Array);
      CreateEmailAttachment(base64);
    }
  };
  retrieveEntityReq.send();
}

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function Encode64(input) {
  var output = new StringMaker();
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;

  while (i < input.length) {
    chr1 = input[i++];
    chr2 = input[i++];
    chr3 = input[i++];

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if (isNaN(chr2)) {
      enc3 = enc4 = 64;
    } else if (isNaN(chr3)) {
      enc4 = 64;
    }
    output.append(keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4));
  }
  return output.toString();
}

var StringMaker = function () {
  this.parts = [];
  this.length = 0;
  this.append = function (s) {
    this.parts.push(s);
    this.length += s.length;
  }
  this.prepend = function (s) {
    this.parts.unshift(s);
    this.length += s.length;
  }
  this.toString = function () {
    return this.parts.join('');
  }
}

//Create attachment for the created email
function CreateEmailAttachment(encodedPdf) {

  //Get order number to name a newly created PDF report
  var orderNumber = Xrm.Page.getAttribute("ordernumber");
  var emailEntityReference = new Sdk.EntityReference("email", emailId);
  var newFileName = fileName + ".pdf";
  if (orderNumber != null)
    newFileName = fileName + orderNumber.getValue() + ".pdf";

  var activitymimeattachment = new Sdk.Entity("activitymimeattachment");
  activitymimeattachment.addAttribute(new Sdk.String("body", encodedPdf));
  activitymimeattachment.addAttribute(new Sdk.String("subject", "File Attachment"));
  activitymimeattachment.addAttribute(new Sdk.String("objecttypecode", "email"));
  activitymimeattachment.addAttribute(new Sdk.String("filename", newFileName));
  activitymimeattachment.addAttribute(new Sdk.Lookup("objectid", emailEntityReference));
  activitymimeattachment.addAttribute(new Sdk.String("mimetype", "application/pdf"));
  Sdk.Async.create(activitymimeattachment, ActivityMimeAttachmentCallBack, function (error) { alert(error.message); });

}

//ActivityMimeAttachment CallBack function
function ActivityMimeAttachmentCallBack(result) {
  Xrm.Utility.openEntityForm("email", emailId);
}


Note: This functionality is tested with Parameterized Custom Report in CRM Online.


The simplest and quickest way to insert data for Many-to-Many relationship in Dynamics CRM is the Drag and drop listbox.
http://www.inkeysolutions.com/DynamicCRMDragAndDropListBox.html

162 comments:

  1. Hi Ankit,

    You are a CRM genius! I did not think there was any way to create an e-mail with a PDF attachment in CRM Online and you could only do this sort of thing with On Premises + a 3rd party tool.

    Is there any way to do something similar with Workflow in CRM Online, so that this could be automated and not require a user to click a button?

    Thanks!
    Len

    ReplyDelete
    Replies
    1. Hi Len,

      I am not sure that you are looking for the same solution but can try this.

      You can create custom entity and "on required event" you can create a record in this custom entity and on Creation of record of this custom entity your plugin will fire.

      Is it help full or you want me explain more?

      Delete
  2. Dear Len,

    Thank you for your comment.

    Honestly, I am not genius!! I am far away from it! 

    In my opinion there must be some action on which you would like to send such e-mails activities directly to customer.
    The given code is completely based on JavaScript & VBScript.
    In workflow either you cannot inject such code or you do not have any actions on which you could run such code. Do we?

    However, instead of workflow we can send the newly created e-mail activity via JavaScript code without user intervention.

    ReplyDelete
  3. How can you do it with JavaScript without user intervention?

    I have a couple of cases where the user is assigning a "Claim" record to an Appraiser and could run the script from there.

    I also have a case though where we want a document to go out if there is no action after X number of days, which is why I had thought to use a workflow. Is there a workaround for that?

    Thanks very much!
    Len

    ReplyDelete
  4. Dear Len,

    For claim record I assume you would like to send an e-mail with claim as an attachment to appraiser.
    If my understanding is correct then you can inject the above code on assign button click or any other custom button click on claim record form wherein the given code will convert the custom SSRS claim report to PDF & creates an e-mail activity with that PDF. In continuation you can further send the same e-mail synchronously to appraiser. Refer below link to send an e-mail Synchronously using JavaScript.
    http://mileyja.blogspot.in/2012/02/send-email-synchronously-in-microsoft.html

    For second scenario we need a workflow or some background programs that wait for X number of days.
    Unfortunately, in CRM 2011 online you cannot send an e-mail activity (that is being already created) via a workflow.
    Hence we cannot use the above code in such scenarios.

    I hope this will be helpful.

    ReplyDelete
  5. Thanks Ankit - that is definitely very helpful!

    ReplyDelete
  6. this is very slow for bigger report data

    ReplyDelete
  7. Hello Daniel,

    Yes, that's correct. This solution is taking too much of time for bigger report data. I would be happy to work out on this again to make it more faster with bulky report data.

    ReplyDelete
  8. Hi Ankit, do you have a CRM solution to make a quick try? Thanks in advance. Marco

    ReplyDelete
  9. Dear Marco,

    At present I do not have a separate solution for this, if you want then I can prepare it for you.

    Please share your interest.

    ReplyDelete
  10. Dear Ankit , please how i can send you my code to tell me what's wrong? Thank you very much

    ReplyDelete
  11. You can reach out to me at ankithimmatlalshah@gmail.com

    ReplyDelete
  12. hi Ankit,
    what could be the reason when only the first call to create the E-Mail activity is very slow? (about 30 seconds) afterwards it takes only about 3sec..
    anyway your solution works great.
    thanks in advance

    ReplyDelete
  13. Hello,

    In my opinion all call should take the same time.
    Could you please share some more details how you think that the first call is very slow? Did you check the attachment file size when the execution was completed in 3 sec.?

    ReplyDelete
  14. How do you get all of this working together? Did you combine them all into one script and but inside a ribbon button ? and what did you do with the HTML File?

    ReplyDelete
  15. There are two web resource components. One a JavaScript type which is being called from a custom ribbon button. It creates the e-mail activity and attachment file of custom report. It also calls the other HTML web resource component to convert binary to byte array. The other HTML web resource component is embedded on entity form where we have placed our ribbon button.

    ReplyDelete
    Replies
    1. Can you please share the screen shots of this workflow to get a clear idea,how it works...Thnx in advance and I'm working for the same requirement but i didn't use any custom ribbon Buttons.It woulld be a great help for me and i've been trying it since 4-5dsys.

      Delete
    2. Hello Ankit!
      My requirement is: I generated custom Report using BIDS for Quote(Entity).Here im trying to attach the generated custom Report as PDF Attachment and send Email.I tried using Note(Annotation) way but that doesn't meet all my requirements.So while googling,i found ur code helpful.I added buttons for each function in ur code like-(Create,Attach,GetReport).But i still dnt get why you didn't mention any buttons in ur HTML Web resource...But if it is because you have a custom Button already,pls do guide me into a right direction to get through my problem.

      Thanks,
      Manu.

      Delete
    3. Hello Manu,

      Sorry for the late response.

      The HTML web resource should be added/embedded on the form where you wish to execute this functionality. In your case it should be added as hidden web resource component on quote entity form. Then the scripting part of step 3 will use it in conversion from binary to byte array.

      I have implemented this functionality on Order form. I have one custom button on top ribbon bar on click on that one of the custom reports get converted into PDF and the same will be attached to an e-mail activity which is being created for that particular Order record during the same process.

      Please provide more details where are you right now about this implementation? Are you facing any errors?

      Delete
    4. Hello Ankit,
      What im trying to do is-
      1.I generated a Custom Report for Quote Entity using BIDS.
      2.Trying to attach that report as PDF attachment to Email Entity using "ActivityMimeAttachment" instead of "Annotation".
      3.I want to attach the Report to selected record and send Email directly.
      4.All this-"attach report and send email" has to be done in one Click.
      5.Pls find the code in the following link which I used and is similar to your code.
      http://xrmmatrix.blogspot.nl/2011/06/creating-report-as-pdf-attachment-in.html

      Problem:
      The problem here is-
      1.I created a workflow to send a mail when the status of the Quote is in Draft.
      2.When i run the workflow,I can send the mail with email activity being generated but with no attachment.
      3.Can you tell me what the problem might be?
      4.I even tried an AddOn which is "Report to PDF" which fulfill all my requirements.
      5.So is it necessary that i hav to make a ribbon control and work on it?

      Delete
    5. Hello Manu,

      As said earlier, according to my knowledge you cannot perform the above action via workflow or any other automatic mechanisms. You have to consider the user action somewhere and for that the best is to customize the ribbon. However, in the upcoming Dynamics CRM release, Orion, there will be no ribbon buttons at all on record forms. So, be careful while customizing ribbons for CRM 2011 online.

      Delete
    6. Hello Ankit!

      Thanks for the reply.So it is not possible to fulfill my requirement using workflows.So is creating a ribbon control a best option? If so, as you said -the upgraded CRM Online doesn't have ribbon control.What would be the best approach to make my requirement done?
      And one more problem is- While attaching the report as email Attachment,all the record's reports are being attached.
      Means-I can see the report with all the record's info instead of particular selected record.
      Any idea or help would be appreaciated...

      Thanks,
      Manu.

      Delete
    7. Hello Manu,

      According to my knowledge at present there is no option except ribbon button to call the above functionality. Additionally, you could think on other events like form load, onchange event of control or any system ribbon buttons if it sits with your business need.

      On all records problem, please validate that have you applied the CRM filtration on report? Did you use the CRM_ parameter for pre-filtration?

      Delete
  16. Hi Ankit,

    I want to send the report per day as an email with PDF as an attachement in CRM Online. Is it possible? Becoz i havw done it with creating the views and convert that data into PDF and attach this with the email activity.Can u please share me the easy way to do it.


    Regards
    RASHMI GUPTA

    ReplyDelete
  17. Hello Rashmi,

    Sorry for the late reply.

    The above code executes on client side and requires user action to start the process of e-mail activity creation and PDF generation. According to my knowledge you cannot initiate such client side actions via workflow, plug-ins or any other automatic mechanisms. So, I believe at present it is not possible to send the custom SSRS report, attached as PDF to an e-mail activity which would be sent on daily basis for CRM 2011 online.

    ReplyDelete
  18. Hi Ankit,
    I have implemented the code above. However I am facing one issue here. I have a SSRS custom report that I am running on CRM online environment. This report runs in the context on current record.
    The issues is that the report gets generated in the pdf format but the report is generated everytime for some specific incident id, the report does not change if I run the report in context of other record.

    ReplyDelete
  19. Hi Ankit,

    I am trying to implement your code on the order entity but i am stuck with all kinds of errors, first some basic javascript errors for undefined variables. And then the report is also not created because i am trying to use the context sensitive Order report which does not have any parameters but the report prints for the current order record.
    I would really appreciate if you can provide this as a n unmanaged solution which i can import and try

    ReplyDelete
  20. Hi Ankit,
    I went through you post. When I tried implementing the same code I was able to achieve the functionality with a little changes in my Report design.

    If there is a report which runs in context of the entity record i.e. if enableprefiltering is set to true then this code wont work, here the report will get generated as an attachment but when we try to open pdf attachment it will give decode error saying the file is corrupt.

    The resolution is that we need to remove enableprefiltering and add a parameter in report that accepts id and then when we pass the report id in the request string in the getReportSession() function report after which the report gets generated in context of the current record.

    ReplyDelete
    Replies
    1. Hi, I have tried this solution and it's not working for me. I am also getting the same error that it is unable to decode the report. Can anyone please let me know the resolution?

      Delete
  21. Hi Ankit,

    Thank you for this clear guide. I'm trying to implement this at the moment.

    I'm trying to adapt the code on 3 parts:

    1) I want to be able to print the report in a separate button.
    2) I want to get the report through a reportserver URL;
    3) If possible, I want to use a prefiltered report.

    ReplyDelete
  22. Hello Nate,

    To print a report, the below URL might help you.
    http://blog.customereffective.com/blog/2011/08/printing-in-crm-2011.html

    On prefiltering a report, at present this solution works only at record level. (Not at list level with multiple records.)

    ReplyDelete
  23. Hi Ankit,

    I wonder if there is a way to choose the email template, to populate the description? Thanks

    Renato

    ReplyDelete
  24. Hello Renato,

    Yes, you should get the e-mail template data via the REST call and setup the description to e-mail body. I never did it before but I believe it should work.

    ReplyDelete
  25. Hi Ankit,
    Thank you very much for your response!
    I am having a problem passing parameter to my report.
    I need to get the entity Guid to the report but it returns nothing when I do.
    Thats the code without the parameter:
    retrieveEntityReq.send("id=%7B" + reportId + "%7D&uniquename=" + Xrm.Page.context.getOrgUniqueName() + "&iscustomreport=true&reportnameonsrs=&reportName=" + reportName + "&isScheduledReport=false");

    Thats is the code with the parameter:
    retrieveEntityReq.send("id=%7B" + reportId + "%7D&uniquename=" + Xrm.Page.context.getOrgUniqueName() + "&iscustomreport=true&reportnameonsrs=&reportName=" + reportName + "&isScheduledReport=false&p:isite_appointmentserviceid=" + Id );

    Can you see what I am doing wrong??
    Thanks again!!!!!

    ReplyDelete
  26. Hi Ankit;

    as you said you used only one button to execute your code ..i am just confuse how did you call your html web resource and also what code need to be go in jacascirpt file and html file could you please give suggestion

    ReplyDelete
    Replies
    1. Yes, there is a custom ribbon button that executes this logic. The html web resource should be added as hidden component on the entity where you are trying to achieve above solution. The code specified in Step 3 is for HTML web resource. The source code specified in step 1 and step 2 are JavaScript file. There is no progress bar is implemented in above solution.

      Delete
  27. and also how we can use a progress bar while the email activity is being generated

    ReplyDelete
  28. Hello Renato,

    The URL that you specified above seems correct to me. Can you confirm on below points:
    1. Whether it is Fetxml or SQL query based report?
    2. At present, the above solution does not support report prefiltering, did you apply prefiltering?
    3. Are you getting any specific errors?

    ReplyDelete
    Replies
    1. Hi Ankit,
      Thank you so much for your time.
      1 - The Report is fetchxml
      2 - It is prefiltering... Should I Disable that?
      3 - The report returns with the fields empty.. The structure is there but the fields not populated.
      Cheers...

      Delete
    2. Hi Ankit,
      Problem solved... thank you very much for your help!

      Delete
    3. Hello Renato,

      Sorry, I am so much busy during this week. Please feel free to update this post with your resolution.

      Delete
    4. What was the solution Renato? I am about to start working on this issue with a fetchxml report.

      Delete
    5. Hello Renato,
      I am facing same problem with report.
      So can you please guide me what to do?

      Delete
    6. This comment has been removed by a blog administrator.

      Delete
  29. Thanks ankit for reply ok so when i put the function for the button which function should i call (create email)???

    ReplyDelete
  30. It works for me.I used fetch when I pass prefiltering parameter.Thanks

    ReplyDelete
  31. Ankit,

    I am confused in the 2nd step in the function GetReportID....

    what do we put in the oDataSetName, columns and filter variables? I don't know because these fields names are confusing.

    Please help! Thanks.

    ReplyDelete
  32. Hi Ankit,

    I am trying to implement your solution, unfortunately not able to get any where near to it. Can you please help and advise how I can use this.

    I have put all javascript functions in one web resource, hidden the html web resource on the form. Called function GetReportId with parameter of report name on click of a custom button.

    Getting no where with it. I have tried calling CreateEmail as well on the same button click, this creates an email, but fails to attach anything to it.

    Sorry for the hassle. Many Thanks.

    ReplyDelete
    Replies
    1. Can you please let me know what error are you getting right now?

      Delete
    2. Hi Ankit,

      I am getting various errors. When I try to call CreateEmail and GetReportId on the button click I get email is not defined.

      If I only call GetReportId, I get $ is undefined although I have added Jquery1.4.1min file as a web resource to the form.

      It will be very helpful if you could advise what functions to call from the button click? How are the web resources to be included , i.e , jscript 1 and jscript 2 included in one web resource? or Jscript3 to be added in the same web resource as well? are there any modifications to be made to the html web resource that is used to call the functions?

      Sorry, a bit new to all this.

      Thanks.

      Delete
  33. Hello Mike,

    The variables had been already declared with values in GetReportID method. Do not change them, keep them "as is". Make sure you pass correct report name in this method.

    ReplyDelete
  34. Hi Ankit, thank you for the blog again.

    I was having troubles passing my FetchXML parameters so I created my report in SQL. I am on premise. I made sure to select Default Value, Specify Value for my parameter and it worked like a charm passing my parameter.

    I had some difficulty with passing the ReportName in your function so I also just hardcoded the Report Name and Report GUID.

    ReplyDelete
  35. Hello Ankit,
    I am trying to change email activities in my entity to cancelled. but i am unable to do it.. can you please help me?

    ReplyDelete
  36. Hello Naveen,

    Can you please confirm what you mean by Cancel here?

    If you mean to close the newly created email activity then you need to managed the same in OData call itself.

    ReplyDelete
  37. Hi Ankit,

    Thanks for the solution. I have implemented your solution successfully but I have now hit a road block here.

    I have a report which has many fields, thus spans to more than one page in the PDF generated. This is not acceptable by the client.

    Any idea how I can convert this in a landscape mode rather than portrait as it is the case now?

    Any help will be much appreciated.

    Thanks in anticipation.

    Shaffana.

    ReplyDelete
  38. Hello Ankit,

    Here is my code, I am trying to generate report using Button click from ribbon.

    I don't able to identity what the issue is ?

    Please help me as i am very urgent this time.

    My Objective is to Download Report using JavaScript

    function getQuote() {
    var pdfid = 'new_pdfid';
    var result = "null";
    control = Xrm.Page.getControl(pdfid);
    attribute = control.getAttribute();
    attribute.setValue(result);
    Xrm.Page.data.entity.save();
    var pdfid = 'new_pdfid';
    alert("Ok1");
    var result = "23A0-E111-A390-1CC13228EA01";
    control = Xrm.Page.getControl(pdfid);
    attribute = control.getAttribute();
    attribute.setValue(result);
    Xrm.Page.data.entity.save();

    var reportName = "Quote"; //set this to the report you are trying to download
    alert(reportName);
    var reportId = "2d012dc2-1144-e311-881f-6c3be5bebd30";
    var guid = Xrm.Page.data.entity.getId();
    guid = guid.replace('{', '');
    guid = guid.replace('}', '');
    var etc = Xrm.Page.context.getQueryStringParameters().etc;
    //&records=%7B” + guid + “%7D
    alert(guid);
    var pth = Xrm.Page.context.getServerUrl() + "/CRMReports/rsviewer/reportviewer.aspx";
    alert(pth);

    var retrieveEntityReq = new XMLHttpRequest();
    retrieveEntityReq.open("POST", pth, false);
    retrieveEntityReq.setRequestHeader("Accept", "*/*");
    retrieveEntityReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    retrieveEntityReq.send("id=%7B" + reportId + "%7D&uniquename=" + Xrm.Page.context.getOrgUniqueName() + "&iscustomreport=true&reportnameonsrs=&reportName=" + reportName + "&isScheduledReport=false");
    alert("Ok2");
    var x = retrieveEntityReq.responseText.indexOf("ReportSession=");
    var ret = new Array();
    ret[0] = retrieveEntityReq.responseText.substr(x + 14, retrieveEntityReq.responseText.indexOf("&", x) - x - 14);
    x = retrieveEntityReq.responseText.indexOf("ControlID=");
    ret[1] = retrieveEntityReq.responseText.substr(x + 10, retrieveEntityReq.responseText.indexOf("&", x) - x - 10);
    alert("Ok3");
    window.open(Xrm.Page.context.getServerUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + ret[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + ret[1] + "&OpType=Export&FileName=Quote&ContentDisposition=OnlyHtmlInline&Format=PDF", 'name', 'width=10,height=10');
    }

    ReplyDelete
  39. Hello Ankit,

    Nice article. I wanted to know your opinion/advice on the task I am working on.

    I wanted to create word file and copy the content of the description field of EMAIL entity. now creating word document and copying content is done but document is getting created with the text including HTML element since description field is rich text editor.

    I tried to strip the HTML tags but then that is disturbing the format of the text which was originally available in the field.

    can you please guide me in right direction.

    Regards.

    ReplyDelete
    Replies
    1. Sorry, but I never did it before, I don't have any ideas how to strip the HTML tags. However, if you will succeed in your implementation then please feel free to update this post with the approach and findings.

      Delete
  40. Hello Shaffana,

    You need to change the SSRS Report Page Orientation or increase the report width to make it landscape and rest is same.

    ReplyDelete
  41. Hello Ankit

    I have a little Timing out problem with a report, If I run it via crm, it timeout after 5 minutes.

    The report itself takes 9-10 minutes everytime to run on server, hence the crm timing out, as I believe the default timeout is 300,000 milliseconds. The problem i have is the customer for some reason is unwilling to change this timeout. (Please dont ask me why ).

    I beleive it is the NormalTimeout entry that does that ..

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\NormalTimeout
    In milliseconds. Specifies the SOAP call timeout for most operations
    Default is 300,000

    So I was thinking to have this report emailed as pdf. (awaiting clients decision on that)..

    but meantime is there any other place or way to change this timeout that will effect Only THIS particular report.

    It is on the CRM side as the report works fine when run directly on the report server.

    Thanks in advance.

    Bikram

    ReplyDelete
  42. Hi Ankit, I already asked you if you have a CRM solution to make a quick try.
    I yes, could you please send it to mfoldes@alteanet.it?
    I'd like to try your solution.
    Coul'd I call the code from a javascript on the form?
    Doe's it work on an online CRM?
    Thanks a lot
    Marco

    ReplyDelete
    Replies
    1. Hello Macro,

      No, I don't have a ready made CRM solution. Yes you can call the code from a JavaScript. Yes, it works for CRM online as well.

      Delete
  43. Dear Ankit,

    pls help!! I dont know where exactly set my report name in your code!??
    Which Command action parameter I have to set??

    THANK YOU!
    tubka

    ReplyDelete
    Replies
    1. Hi,
      - Provide you custom report name in step tow for this activitymimeattachment.FileName.
      - You can pass your parameters in step 3 code for the pth variable.
      Refer the following code:

      var pth = Xrm.Page.context.getServerUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";

      Delete
  44. hello ,

    is this worked with CRM 2013 ? (i already have a problem in getting session parametres)
    thx for help.

    Regards.

    ReplyDelete
  45. Hi Khaled,

    It is working with CRM 2013 in IE11 (Add dynamics.com site in compatibility mode in IE11.) It is not working in Chrome and Firefox.

    ReplyDelete
  46. Hello Ankit,

    pls. could you help me to install your great work.

    - What parameter should be set on the ribbon button?
    - Where should I define my Report in your script?
    - Where can I get SDK.JScriptRESTDataOperations.js ?

    It would be very kind if you could give me a little help for that.

    ReplyDelete
    Replies
    1. Hi,

      - You need to call above specified CreateEmail function on your custom JavaScript ribbon button.
      - Provide you custom report name in step tow for this activitymimeattachment.FileName.
      - Refer the following link to download SDK.JScriptRESTDataOperations.js
      http://social.microsoft.com/Forums/en-US/f501c019-aa7f-4d78-8390-5f11d1dd8820/crm-2011-sdk-crud-operation-samples?forum=crmdevelopment

      Delete
  47. Hi Ankit
    I have two problems: first when I create the attachment the annotation is not connect to my email.
    Second problem is that when I download the pdf file Acrobat Reader says that is in a format not recognize.
    Any idea?
    Thanks
    Marco

    ReplyDelete
    Replies
    1. Hi Marco,

      Sorry for the delay. Are you getting any errors? Did you try to debug your JavaScript code? Seems both the problems are related to each other.

      Delete
  48. Hi Ankit
    Creating an email activity party gives an error
    the 'create' method does not support entities of type 'activityparty'

    CRM 2013 online

    also how could i pass a fetchxml report parameters ?????

    ReplyDelete
    Replies
    1. Hello Ahmed,

      Sorry for the late response. At present this solution is not working in chrome and fire fox for CRM 2013 online. But, it is working fine with IE 11 by adding "dynamics.com" in Compatibility View settings window.

      Delete
    2. You can pass your parameters in step 3 code for the pth variable.
      Refer the following code:

      var pth = Xrm.Page.context.getServerUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";

      Delete
  49. hi ankit Im not able call vb script function BinaryToArray.Im woking on this since days.

    ReplyDelete
    Replies
    1. Hi Anbu, Sorry for the late response. Can you share what error are you getting?

      Delete
    2. Hi,

      The vb script will only work from IE, it is not recognized by other browsers

      Delete
  50. Hi Ankit,

    Did you have to make any changes to this so it would work after Rollup 12? We are currently on RU11 and are upgrading our Test environment to Rollup 15 later this week. I am wondering if there are any changes that I will need to plan on making? Thanks again!!

    ReplyDelete
  51. Hi Ankit,

    Can you pass the CRM_FilteredInvoice parameter inside the call to "CRMReports/rsviewer/reportviewer.aspx" ? like " ... &p:CRM_FilteredInvoice=" + invoiceId ?

    Your code definitely works, but it always creates a report for the first invoice it finds, not for the invoice I select (from the invoice grid). That's why I think I should pass the invoiceId somehow.

    Thank you.

    ReplyDelete
    Replies
    1. Hello Mihai,

      The above code is still not further developed to make it functional from the grid area of entities. But, yes it is working for the parameterised report, hence you could try the way you specified above. Please feel free to update this blog if you will be able to achieve the expected results successfully. Good luck.

      Delete
    2. Hi, I managed to get it working. You have to add a filter, as I suspected. But the filter has to be something like :



      I noticed this when using Fiddler and printed the report I wanted (from CRM), this is actually what happens behind the scenes.

      I created a .js function called "buildInvoiceFilter" that replaces the invoice id with the right value and returns this string, then I concatenate it to the "/CRMReports/rsviewer/reportviewer.aspx" request, so now I have something like:

      ...blah..blah.."&isScheduledReport=false&CRM_Filter=" + buildInvoiceFilter(invoiceId))

      Seems to also work if I have invoice details (even if I don't populate the CRM_FilteredInvoiceDetail parameter)

      Delete
    3. Hello Mihai,

      Can you please share you code of buildInvoiceFilter(invoiceId) function. That will help a lot.

      Thanks,

      Delete
  52. Hi Ankit,

    Is this solution feasible for System reports in MS CRM 2013?
    I could not get report session parameters, and xml http request returns a generic error - "Try this action again. If probelm continues..... contact administrator"

    Please share your thoughts.

    - Praveena

    ReplyDelete
    Replies
    1. Hello Praveena,

      Yes, it should work with system reports too. Are you using the correct report name GetReportId() method of step 2?

      Delete
  53. Hi Ankit,

    Will this work for System reports?

    ReplyDelete
  54. Was anyone able to make this work for Chrome or Firefox?

    ReplyDelete
  55. Or rather I should say, was any one able to convert the VBSCRIPT function to JAVASCRIPT?

    ReplyDelete
  56. Code was working fine in CRM 2013 but today (05-29-2014)above code is attaching corrupted file.Attached PDF size is 7782 and it is not exceeding this limit.We have done it for many clients but now it is reducing the file size to 7782.
    Any help?

    ReplyDelete
  57. Anyone else who is experiencing problems after the spring release? Had this running on crm 2013 online without problems until the spring release came. The pdf:s generated are now corrupt.

    ReplyDelete
  58. Hi, yes you are right. The code is unable to generate the PDFs after CRM 2012 online spring 14 release. I am working on this problem and would try to resolve it as soon as I can.

    ReplyDelete
  59. Hi, for crm 2013 spring edition there are some required changes to the code.

    ReplyDelete
  60. FOR CRM 2013 Spring edition which is SP1

    the page you get the session id and control id is changed from
    pth = Xrm.Page.context.getServerUrl() + "CRMReports/rsviewer/reportviewer.aspx";

    to

    pth = Xrm.Page.context.getServerUrl() + "CRMReports/rsviewer/QuirksReportViewer.aspx";

    And the substring method to get the ControlID and ReportSession is like below:

    ret[0] = retrieveEntityReq.responseText.substr(x + 14, retrieveEntityReq.responseText.indexOf("u0026", x) - x - 15); //the session id

    ret[1] = retrieveEntityReq.responseText.substr(x + 10, retrieveEntityReq.responseText.indexOf("u0026", x) - x - 11);; //the control id


    Rest of the code remains the same.

    Hope this helps somebody...

    All the best

    Oğuz

    ReplyDelete
  61. Hello Oğuz ERDEVE,

    Thank you for sharing the solution. Actually, I tried with it for one of my customers but it did not work. Did you cover all changes? Can you please share the entire getReportingSession() function?

    Thanks,
    Ankit

    ReplyDelete
  62. Hi All,

    I was able to get this working for CRM 2013 SP1 by doing the changes as mentioned by Oğuz ERDEVE. I did not had to make changes to the Session Id and Control Id though.

    Hope it helps some one.

    Thanks all for the input.

    ReplyDelete
  63. Hi All,
    this code run very well, but i have error when i open the pdf file, i don't now why.
    Any idea?
    I changed the encoded funtion but not work in same.

    Error message: not a supported file type or because the file has been damaged.

    ReplyDelete
    Replies
    1. Hi Andre,

      I have also getting the same problem as you mentioned above. I think, we need to decode the pdf file.

      Thanks,
      Ganesh.J

      Delete
  64. i am having the error

    not a supported file type or because the file has been damaged

    ReplyDelete
  65. Hi Ankit,

    I couldn't able to read/open the pdf file after created the email record. May I know the reason for this problem from the above code?

    Thanks,
    Ganesh.J.

    ReplyDelete
  66. i replaced reportname and id as mentioned under function getReportingSession()
    also i removed getServerUrl() since it not longer supports in CRM2013 and i replaced with getClientUrl() and also did as Oğuz ERDEVE suggested but still no luck

    ReplyDelete
  67. Hi,
    Nice work. Its very informative.
    could you please guide me on how to do this below requirement?

    I want to generate a pdf form on click of a custom ribbon button. Your work looks similiar to my requirement but I don't want to attach it to any email.

    Please suggest some solution to this. Thanks.

    ReplyDelete
  68. CRM 2015 Online Update: Hope it helps someone

    function getReportingSession()...

    var ret = new Array();

    ret[0] = retrieveEntityReq.responseText.substr(x + 14, retrieveEntityReq.responseText.indexOf("\\u", x) - x - 14); //the session id

    x = retrieveEntityReq.responseText.indexOf("ControlID=");

    ret[1] = retrieveEntityReq.responseText.substr(x + 10, retrieveEntityReq.responseText.indexOf("\\u", x) - x - 10); //the control id

    return ret;

    ReplyDelete
  69. Hello,
    I have a same issue for corrupt pdf.
    Error message: not a supported file type or because the file has been damaged.
    I am working on crm 2015 online.
    Please provide a solution if any one resolved it....

    ReplyDelete
  70. Hi All,

    Is there a way of using this functionality on multi browser? Currently it is using the VB script which I think is only supported in IE and not any other browser.

    Has any of the Gurus came across this or solved this. Please share your solutions to the wider weaker world.

    Thanks

    ReplyDelete
  71. Hello All,

    Because the CRM 2015 Update 1 gives problems with the VB script (and because we really don't like using VB anymore) we've reworked the JS a little to work without the VB script. The change is fairly simple and you only need to change the "EncodePdf" function. This way you also don't need the WebResource on the form anymore! Here is the code for it:

    // Encodes a report into a PDF
    this.encodePdf = function (params) {

    bdy = new Array();
    bdyLen = 0;

    var retrieveEntityReq = new XMLHttpRequest();
    var pth = Xrm.Page.context.getClientUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";

    retrieveEntityReq.open("GET", pth, false);
    retrieveEntityReq.setRequestHeader("Accept", "*/*");
    retrieveEntityReq.send();
    return encode64(retrieveEntityReq.responseBody.toArray());
    }

    ReplyDelete
    Replies
    1. Sven Can you provide your entire create e-mail and attach pdf report in Version 2015?

      Delete
    2. Sven, Can you supply your entire solution for email creation with PDF SSRS Report attached.

      Delete
  72. Hello everyone,

    Thank you for sharing your feedback till date at this blog.
    Today, I've applied required changes to this blog. The updated code should now work well with CRM 2011 UR 12 and above.

    Thanks,
    Ankit

    ReplyDelete
  73. Still receiving the following error on CRM 2015

    not a supported file type or because the file has been damaged

    Anwar

    ReplyDelete
    Replies
    1. Hi,

      Is it CRM 2015 online or on-premises?
      Can you run the report in CRM from the reports area?

      Delete
    2. it is mscrm 2015 online

      Delete
  74. Hello Guys

    Good work Ankit...
    I was able to get this code working on CRM 2015 spring release with minor changes. Here is the change i made to get this code working.

    var x = retrieveEntityReq.responseText.lastIndexOf("ReportSession=");
    var ret = new Array();
    ret[0] = retrieveEntityReq.responseText.substr(x + 14, 24); //the session id
    x = retrieveEntityReq.responseText.lastIndexOf("ControlID=");
    ret[1] = retrieveEntityReq.responseText.substr(x + 10, 32); //the control id

    If anyone needs complete code drop me comment in by blog..

    ReplyDelete
    Replies
    1. Hello Chitrarasan,

      Please know, I've already changed this blog code for the piece of code that you described here in your comment.

      If you've copied the complete code from this blog then it should work as is for CRM 2015 spring release.

      Thanks,
      Ankit

      Delete
    2. Hi Chitrarasan,
      I am trying to implement this in crm online 2015
      but not able to get reportsession id.
      Please provide me the complete code
      Email id- pankajgpt31@gmail.com

      Delete
  75. Hi Ankit,

    Thanks for sharing this code.
    The code works fine. The PDF attachment gets created but cannot be opened. The error message thrown is - Adobe Acrobat could not open "FileName.pdf" because it is either not a supported file type or because the file has been damaged (for example, it was sent as an email attachment and wasn't correctly decoded).

    I am trying this on CRM 2015 Online. Any suggestions?

    When I debug the code I get responseBody value as "undefined" for the below line

    return encode64(retrieveEntityReq.responseBody.toArray());

    Thanks,
    Swaroop

    ReplyDelete
    Replies
    1. Hi Swaroop,

      First, please verify, your report is working accurately or not. Also, is this report contains any parameter(s)? If yes, then these parameters are also needs to be accurately setup. Can you please share source code for following two JavaScript methods?
      getReportingSession
      encodePdf


      Thanks,
      Ankit

      Delete
    2. Hi Ankit,

      Thanks for your reply. Here's the code I am using. Using the code below I get the Session Id and Control Id of the report properly.

      getReportingSession: function () {
      var reportParams = new Array();
      var url = Xrm.Page.context.getClientUrl() + "/CRMReports/rsviewer/reportviewer.aspx";
      var quoteId = Xrm.Page.data.entity.getId();
      var quotationGUID = quoteId.replace('{', ""); //set this to selected quotation GUID
      quotationGUID = quotationGUID.replace('}', "");

      var reportName = "Quote Report"; //set this to the report you are trying to download
      var reportId = "c1d271ab-8154-e411-94ab-6c3be5a8d218"; //set this to the guid of the report you are trying to download

      var reportFilter = " ";
      var reportData = "id=%7B" + reportId + "%7D&uniquename=" + Xrm.Page.context.getOrgUniqueName() + "&iscustomreport=true&reportnameonsrs=&reportName=" +
      reportName + "&isScheduledReport=false&p:CRM_quote=" + reportFilter;

      var executeReportRequest = new XMLHttpRequest();
      executeReportRequest.open("POST", url, false);
      executeReportRequest.setRequestHeader("Accept", "*/*");
      executeReportRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      executeReportRequest.send(reportData);

      var response = executeReportRequest.responseText;
      if (response != null && response.indexOf("ReportSession=") != -1) {
      var sessionIdIndex = response.indexOf("ReportSession=");
      var controlIdIndex = response.indexOf("ControlID=");

      reportParams[0] = executeReportRequest.responseText.substr(sessionIdIndex + 14, executeReportRequest.responseText.indexOf("&", sessionIdIndex) - sessionIdIndex - 14); //the session id

      reportParams[1] = executeReportRequest.responseText.substr(controlIdIndex + 10, executeReportRequest.responseText.indexOf("&", controlIdIndex) - controlIdIndex - 10); //the control id
      }
      return reportParams;
      },


      encodePdf:function(reportParameters) {


      var retrieveEntityReq = new XMLHttpRequest();
      var url = Xrm.Page.context.getClientUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + reportParameters[0] +
      "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + reportParameters[1] +
      "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";
      retrieveEntityReq.open("GET", url, false);
      retrieveEntityReq.setRequestHeader("Accept", "*/*");
      retrieveEntityReq.send();


      return encode64(retrieveEntityReq.responseBody.toArray());

      },


      Thanks,
      Swaroop

      Delete
    3. Hello Swaroop,

      Sorry for the delayed response.

      Please correct the following two lines and run your code again.

      reportParams[0] = executeReportRequest.responseText.substr (sessionIdIndex + 14, 24); //the session id
      reportParams[1] = executeReportRequest.responseText.substr (controlIdIndex + 10, 32); //the control id

      Delete
    4. Hi Ankit,

      The change didn't work. I am still facing the same error. I am facing this error on IE 11 and on Chrome even the attachment is not getting created and associated for the email record.

      Thanks,
      Swaroop

      Delete
  76. Hi Ankit,
    Wanted to know whether it would work for MSCRM 2013?

    Thanks in advance

    ReplyDelete
    Replies
    1. Yes, it works with MSCRM 2013 too.

      Thanks,
      Ankit

      Delete
  77. HI Ankit,

    Getting error in Chrome browser.

    Error at this line :
    bdy = encode64(retrieveEntityReq.responseBody.toArray());

    Please help me how to resolve issue.

    Thanks,
    Benarji

    ReplyDelete
    Replies
    1. Hi,

      For CRM 2011 to run it in Chrome browser you need to perform following changes.

      //Need to change a body of this function.
      function onSuccess(data) {
      reportId = data[0].ReportId.replace('{', ").replace('}', ");

      //get reporting session and use the params to convert a report in PDF
      var params = getReportingSession();
      encodePdf(params);
      }

      //Need to change a definition of this function, just pass one parameter.
      CreateEmailAttachment(encodedPdf) {

      //In the function "CreateEmailAttachment" just change a code of assignment the body part.
      activitymimeattachment.Body = encodedPdf;

      //Need to change the body part of this function.
      function encodePdf(params) {
      var retrieveEntityReq = new XMLHttpRequest();

      var pth = Xrm.Page.context.getClientUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";
      retrieveEntityReq.open('GET', pth, true);
      retrieveEntityReq.setRequestHeader("Accept", "*/*");
      retrieveEntityReq.responseType = "arraybuffer";

      retrieveEntityReq.onload = function (e) {
      if (this.status == 200) {
      var uInt8Array = new Uint8Array(this.response);
      var base64 = encode64(uInt8Array);
      CreateEmailAttachment(base64);
      }
      };
      retrieveEntityReq.send();
      }

      Let me know if you have any further questions about these changes.

      Thanks,
      Ankit

      Delete
  78. Hi,
    Is this code also working for CRM 2016 online?...please reply.

    Thanks,
    Tin

    ReplyDelete
    Replies
    1. Hello Tin,

      Yes it is working for CRM 2016 online.

      Thanks,
      Ankit

      Delete
    2. Ankit, Thanks for your response.
      Can you Please share the code.

      Thanks,
      Tin

      Delete
    3. Code is here on the post itself. Please take it from the post and update it as per your need.

      Delete
    4. Hello Ankit,

      Where from ican find the library Sdk.Soap.js?
      Can you please Help me.
      Thanks,
      Tin

      Delete
    5. Here it is,
      https://code.msdn.microsoft.com/SdkSoapjs-9b51b99a

      Delete
    6. Hello Ankit,
      Thanks for your response.
      I added the library "Sdk.Soap.min.js"
      and getting error on the line below:

      var regardingObjectId = new Sdk.EntityReference(entityLogicalName, id);

      Can you please verify which library need to add.
      Thanks,
      Tin

      Delete
  79. Tin, please share the error details.

    ReplyDelete
  80. Ankit Thanks!
    The code works fine. The PDF attachment gets created but cannot be opened. The error showing in Foxit Reader- "Format Error: Not a PDF or corrupted."

    I am trying this on CRM 2016 Online. Any suggestions?
    Thanks,
    Tin

    ReplyDelete
    Replies
    1. Hello Tin,

      Can you please share the complete code?

      Thanks,
      Ankit

      Delete
  81. Yeah seen Ankit thank you soo much your code is real charm !!!
    It helps me a lot many many thanks you once again bro.

    ReplyDelete
  82. Hi
    is there a way to send the email directly
    without opening the form of the email

    ReplyDelete
    Replies
    1. Hi Abood,

      Yes, you can do it. You could use send email message of SDK.Soap call. http://ankit.inkeysolutions.com/2014/10/the-sdksoapjs-code-samples-of-sdksync.html

      Delete
  83. This is soooo excellent! But I get the error in trying to open the pdf:
    Acrobat could not open 'PDF01TryTest.pdf' becue it is either not a supported file type or because the file has been damaged (for example, it was sent as an email attachment and wasn't correctly decoded).
    function EncodePdf(params) {
    var retrieveEntityReq = new XMLHttpRequest();

    var pth = Xrm.Page.context.getClientUrl() + "/Reserved.ReportViewerWebControl.axd?ReportSession=" + params[0] + "&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=" + params[1] + "&OpType=Export&FileName=Public&ContentDisposition=OnlyHtmlInline&Format=PDF";
    retrieveEntityReq.open('GET', pth, true);
    retrieveEntityReq.setRequestHeader("Accept", "*/*");
    retrieveEntityReq.responseType = "arraybuffer";

    retrieveEntityReq.onload = function (e) {
    if (this.status == 200) {
    var uInt8Array = new Uint8Array(this.response);
    var base64 = Encode64(uInt8Array);
    CreateEmailAttachment(base64);
    }
    };
    retrieveEntityReq.send();
    }

    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    function Encode64(input) {
    var output = new StringMaker();
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;

    while (i < input.length) {
    chr1 = input[i++];
    chr2 = input[i++];
    chr3 = input[i++];

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if (isNaN(chr2)) {
    enc3 = enc4 = 64;
    } else if (isNaN(chr3)) {
    enc4 = 64;
    }
    output.append(keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4));
    }
    return output.toString();
    }

    var StringMaker = function () {
    this.parts = [];
    this.length = 0;
    this.append = function (s) {
    this.parts.push(s);
    this.length += s.length;
    }
    this.prepend = function (s) {
    this.parts.unshift(s);
    this.length += s.length;
    }
    this.toString = function () {
    return this.parts.join('');
    }
    }

    //Create attachment for the created email
    function CreateEmailAttachment(encodedPdf) {

    //Get order number to name a newly created PDF report
    var orderNumber = Xrm.Page.getAttribute("new_ordernumber");
    var emailEntityReference = new Sdk.EntityReference("email", emailId);
    var newFileName = fileName + ".pdf";
    if (orderNumber != null)
    newFileName = fileName + orderNumber.getValue() + ".pdf";

    var activitymimeattachment = new Sdk.Entity("activitymimeattachment");
    activitymimeattachment.addAttribute(new Sdk.String("body", encodedPdf));
    activitymimeattachment.addAttribute(new Sdk.String("subject", "File Attachment"));
    activitymimeattachment.addAttribute(new Sdk.String("objecttypecode", "email"));
    activitymimeattachment.addAttribute(new Sdk.String("filename", newFileName));
    activitymimeattachment.addAttribute(new Sdk.Lookup("objectid", emailEntityReference));
    activitymimeattachment.addAttribute(new Sdk.String("mimetype", "application/pdf"));
    Sdk.Async.create(activitymimeattachment, ActivityMimeAttachmentCallBack, function (error) { alert(error.message); });

    }

    ReplyDelete
    Replies
    1. Hi KD,

      A few queries,

      1. Is it CRM online or on-premises? CRM version?
      2. Is this report running well from CRM?
      3. Is this report running well at SSRS server?
      4. Is it parameterized report?

      Thanks,
      Ankit

      Delete
  84. 1. Online, latest version
    2. Runs fine from Crm
    3. Created using report wizard, could that be the issue?
    4. No parameters
    Thanks!

    ReplyDelete
    Replies
    1. Hi,

      One thing can be validated is below code, as your report has no parameters then I would suggest to remove "&p:salesorderid=" + Id + "&p:createdon=1/1/9999"" from the following line,

      var req = "id=%7B" + reportId + "%7D&uniquename=" + orgUniqueName + "&iscustomreport=true&reportName=" + reportName + "&isScheduledReport=false&p:salesorderid=" + Id + "&p:createdon=1/1/9999";


      Thanks,
      Ankit

      Delete
  85. Hi Ankit,

    Really great concept. It worked great for me even in latest version of 365 online.
    But one thing am trying to figure is adding multiple recipients? I tried adding it as as array and entity collection. Its only taking last email address from the loop condition?
    is there something am missing?
    Any guidance or direction is greatly appreciated.
    THanks
    Kum

    ReplyDelete
    Replies
    1. Hi Kum,

      In above code there is a method, named as PrepareActivityParty(), apply change in this method to loop through all parties and prepare a collection and return that collection for TO field of your email.

      Feel free to ask if you have any further questions.

      Delete
  86. Hi,
    I followed your code but i'm facing problem.

    I want convert an invoice to PDF format using JavaScript and send with an attachment, but main issue on opening PDF file, attachment already done and working properly and PDF file also carry some size. At the time of open an PDF file it show error for opening.

    ReplyDelete
  87. Hi,
    Great Blog,

    Here my issue is that I am using ms crm online version and every thing is Ok like attachment of PDF file and send an email, but main issue is on opening attached PDF file, It also have taken some size and at the time of opening it will not open.It will show "Format Error"

    kindly share your suggestion.

    ReplyDelete
    Replies
    1. Hi Surya,

      Please share your code here. Also, in which browser are you validating this? Did you try with Chrome?

      Thanks,
      Ankit

      Delete
    2. I am unable to share code with you here, so plz share email-id and I am also check both IE and chrome browser, but it was same issue.

      Delete
    3. Share the same at ankithimmatlalshah@gmail.com

      Delete
  88. Hello Ankit

    I am getting below retrieveEntityReq.responseText in dynamics 365.


    window.location.href = '/_common/error/errorhandler.aspx?BackUri=https%3a%2f%2fpragmasys90.crm8.dynamics.com%2fmain.aspx%3fetc%3d1084%26extraqs%3d%253f_gridType%253d1084%2526etc%253d1084%2526id%253d%25257bEE6460EA-6335-E711-811E-C4346BDC6E11%25257d%2526rskey%253d%25257b008FBB15-98E9-4C5D-A81A-E685FF8CCC2C%25257d%26pagemode%3diframe%26pagetype%3dentityrecord%26rskey%3d%257b008FBB15-98E9-4C5D-A81A-E685FF8CCC2C%257d&ErrorCode=0x8004832C&Parm0=%0d%0a%0d%0a&Parm1=%28rsItemNotFound%29&RequestUri=%2fCRMReports%2frsviewer%2fQuirksReportViewer.aspx&user_lcid=1033';
    Thnaks

    ReplyDelete
    Replies
    1. Hi Mahak,

      The given is error handler page URL and not the actual error. Please provide some more details about an error.

      Thanks,
      Ankit

      Delete
    2. Actually code is working for custom reports but not working for oob reports.

      Delete
    3. Ideally it should not be case I suppose. However can you share the error that you're getting?

      Delete
    4. Ankit,

      I am getting above response (retrieveEntityReq.responseText) when sending request. Not getting what else you are asking.
      When i am trying to open the pdf file it is giving below error.
      Acrobat could not open 'Quote.pdf' because it is either not a supported file type or because the file has been damaged (for example, it was sent as an email attachment and wasn't correctly decoded).

      Delete
    5. Code for oob Quote.rdl report

      function getReportingSession() {
      var reportName = "Quote";
      reportID = "262D5F7E-4625-E711-811D-C4346BDD21B1";
      var pth = Xrm.Page.context.getClientUrl() + "/CRMReports/rsviewer/QuirksReportViewer.aspx";
      var retrieveEntityReq = new XMLHttpRequest();
      retrieveEntityReq.open("POST", pth, false);
      retrieveEntityReq.setRequestHeader("Accept", "*/*");
      retrieveEntityReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

      rptPathString = "id=%7B" + reportID + "%7D"+
      "&uniquename=" + Xrm.Page.context.getOrgUniqueName() +
      "&iscustomreport=true&reportnameonsrs=&reportName=" + reportName +
      "&isScheduledReport=false";

      retrieveEntityReq.send(rptPathString);

      var x = retrieveEntityReq.responseText.lastIndexOf("ReportSession=");
      var ret = new Array();
      ret[0] = retrieveEntityReq.responseText.substr(x + 14, 24); //Report session id
      x = retrieveEntityReq.responseText.lastIndexOf("ControlID=");
      ret[1] = retrieveEntityReq.responseText.substr(x + 10, 32); //Report control id
      return ret;
      }

      even if we change &iscustomreport=false, it is not working. Giving same error response.

      Delete
    6. Hello Mahak,

      Sorry for the delayed response.

      I've made some changes in the GetReportingSession()function to get rid of the "Failed load Pdf Document" Error.

      Thanks,
      Ankit

      Delete
  89. Hi Ankit, thanks for sharing your knowlege.
    I facing a problem. My Report is execute in context of quote entity (FetchXML Report), the report is attach to the email activity correctly but when I open the file (pdf) the data in the report is not the data of the entity context but the data of other quote (the first(?)). My report Runs well when execute directly in CRM and show the data of the context quote (prefilter), but when i use the code to generate the email and attach to, the information in the report is not of the conext quote. There is a way to acomplish this in the conext report entity?.

    ReplyDelete
  90. Hi Ankit,

    Thanks for a gr8 solution.
    But my requirement is same as many before, need to send a custom report as pdf attachment every night in Dynamics 365. Can you provide a solution for the same.
    Read through a lot of posts but could find your most apt with the only exception of manual intervention.

    ReplyDelete
    Replies
    1. Hi,

      You can use Microsoft Flow and trigger this Js with your custom logic.

      Thanks
      Jay

      Delete
  91. Hi Ankit,

    Thanks for the great post, , i uesd your code in my dynamics 365 online version , Email and pdf attachment creating successfully , when open attached pdf i got "Failed load Pdf Document" Error How to resolve this issue please help me to do this

    ReplyDelete
    Replies
    1. Hello Ishu,

      Sorry for the delayed response.

      I've made some changes in the GetReportingSession()function to get rid of the "Failed load Pdf Document" Error.

      Thanks,
      Ankit

      Delete
  92. i am having the error while calling Create Email Method It shows below error,

    Sdk is not defined at CreateEmail

    ReplyDelete
    Replies
    1. Hi,

      Sorry for the delayed response. I would recommend to go with MS Flow instead of using this approach. This is an old way to achieve the need.

      Thanks,
      Ankit

      Delete
  93. Hi Ankit,
    Can this be used for default reports in CRM like Account Distribution and supply the parameters for TopAccounts and GroupBy? I tried it but does not work, how to set these parameters? Any examples if feasible or snippet.

    ReplyDelete
    Replies
    1. Dear Srini,

      This solution works only with the custom SSRS reports.

      Delete