One of the long requested and anticipated missing components in Dynamics 365 CE is the creation of PDF documents.
As of version 9.0.1905.2010 you will have the native capability to create a PDF document for quote.
Yes, finally great feature, let’s enable it! Navigate to the sales hub “app settings” and select “PDF generation” in the left hand menu.
After enabling the “Create Pdf” and “Email as PDF” menu will appear on quote’s.
Microsoft Docs: https://docs.microsoft.com/en-us/dynamics365/customer-engagement/sales-enterprise/create-quote-pdf
But as you might have already noticed, the devil is in the details, it’s only available on quotes (or at least for the moment). I expect that they will open up this feature to other document template enabled entities.
For the people who can’t wait, it’s possible to already use the export to PDF functionality for other entities, but it will require to write some code.
First let’s having a closer look at what happens when selecting a template to generate a PDF document. Dynamics is calling the following endpoint:
https://{org}.{region}.dynamics.com/api/data/v9.0/ExportPdfDocument
And the following data is submitted:
{ "EntityTypeCode": 1084, "SelectedTemplate": { "@odata.type": "Microsoft.Dynamics.CRM.documenttemplate", "documenttemplateid": "153dc496-d79d-e711-8109-e0071b65ce81" }, "SelectedRecords": "[\"{E3A79DA1-9B91-E811-8133-E0071B65CE81}\"]" }
The response will contain a “PdfFile” attribute which is a base64 representation of the PDF file.
{
"@odata.context": "https://{org}.{region}.dynamics.com/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.ExportPdfDocumentResponse",
"PdfFile": "JVBERi0xLjUNCjQgMCBvYmoNCjw8L1R5cGUgL1BhZ2UvUGFyZW50IDMgMCBSL0..."
}
Now let’s try to do this from a C# console app for an invoice record.
Then the captured information from the above call would translate in something like:
//Endpoint based on https://{org}.{region}.dynamics.com/api/data/v9.0/ExportPdfDocument OrganizationRequest request = new OrganizationRequest("ExportPdfDocument"); request["EntityTypeCode"] = 1090; //invoice request["SelectedTemplate"] = new EntityReference("documenttemplate", new Guid("cbbcd2d8-e48e-e711-8136-e0071b65cea1")); // invoice document template //serialize JSON array of the records List<Guid> records = new List<Guid> { new Guid("2609328d-296c-e911-a989-000d3ab982f2") }; request["SelectedRecords"] = JsonConvert.SerializeObject(records); OrganizationResponse pdfResponse= (OrganizationResponse)_client.Execute(request); //Write to file string b64File = Convert.ToBase64String((byte[])pdfResponse["PdfFile"]); File.WriteAllBytes("invoicedocument.pdf", pdfResponse["PdfFile"] as byte[]);
Imaging adding this code to a workflow assembly with the appropriate input and output parameters, and the possiblities this would give….
Hi, I have a MIcrosoft Power automate flow where i use the "invoke an https request for this purpose. However, i get the message "An unexpected error occurred." for some quotes, some quotes work just fine and i can´t see that there are any differences between the quotes. Have you any suggestions?
You can also integrate your system with an external API, such as gendoc.io and generate your PDFs directly from here, with customized templates, …
Indeed. It's a bit further from the OOB functionalities, but very usefull when you run into limitations.
iT DOES NOT work for an array of records. E.g. WHen I specified 2 case Guids and used with the OOB Case Summary template, it generated a pdf for only the first case guid. am i missing anything here?
Hi Kris, Couple of questions, does this works for custom entities on Dynamics 365 crm online. I tried a CUSTOM workflow activity with the way explained in the above console app code, it throws exception saying below. Is there a workaround for this? I would really appreciate for your response. Thanks The request ExportPdfDocument cannot be invoked from the Sandbox. Unhandled exception: Exception type: Microsoft.Crm.CrmException Message: The request ExportPdfDocument cannot be invoked from the Sandbox. at Microsoft.Crm.Sandbox.SandboxCodeUnit.ProcessException(Exception originalException, IExecutionContext context, SandboxClient client, SandboxCallTracker callTracker, Boolean isSafeToRetry, DateTime performanceExecutionStartTime, SandboxTracker tracker, Guid parentExecutionId, CrmException& crmException, String& assemblyContents) at Microsoft.Crm.Sandbox.SandboxCodeUnit.c__DisplayClass24_0.b__0()
So if you really want to get this working within a plugin there are a few steps. The Basics: -Create an Application User -Call the ExportPdfDocument WebAPI endpoint using the Application User. The details on how to accomplish this can be found in the link below. The article describes how to retrieve records but the RetrieveAccounts methods could easily be updated to get the ExportPdfDocument endpoint and you could build out the JSON for the request. https://debajmecrm.com/2018/11/18/solved-call-web-api-from-within-a-plugin-in-dynamics-365-online/ Recommendation: Instead of hard coding the values for authentication information and endpoints I would probably create a configuration entity and get the values from that. You could make the Client ID and Client Secret values have field level security so that only admins could see them then make sure you get that data using the System Organization Service instead of the User Organization Service in your plugin. That or you could use the secure plugin configuration to store this data... but who wants to have to manage adding/updating that on multiple environments.... not me :) --Rick
Hi, First of all thanks Rick & Nathan for the article. It was so useful for me. I found out the following code works in plugin or customer activity context. using (OrganizationWebProxyClient webProxyClient = new OrganizationWebProxyClient(new Uri(api), false)) { webProxyClient.HeaderToken = authRes.AccessToken; using (OrganizationServiceContext XrmContext = new OrganizationServiceContext((IOrganizationService)webProxyClient)) { } } Basically create "OrganizationServiceContext" from OrganizationWebProxyClient and use it to create ExportPdfDocument request and execute it. Cheers.
Thanks for the addition Rick, really helpful information. It's curious that creating a new OrganizationService within a plugin works... so I'm genuinely wondering why MS would block calling the ExportPdfDocument from the IOrganizationService available within the plugin context.
Can be runned in Console App, but not from an Custom Activity The request ExportPdfDocument cannot be invoked from the Sandbox.
We are having the same issue, did you solved it?
A bit late... but see Rick's answer above, could be a good solution for your problem.
I did not quite understand the reason why this type of operations (SetWordTemplate, ExportPdfDocument,....) using "OrganizationRequest" cannot be performed in sandbox mode. The only way to do this in Plugin or Custom Activity is to create a new "IOrganizationService" and use the new connection in the Request
hI kRIS, JUST TESTED IT AND ITS WORKING ON QUOTE AND CASE. sEEMS IT'S UPDATED TO WORK ON OTHER ENTITIES AS WELL
Hi Niels, do you mean that this is working from within a Custom Activity / Plugin now? That would be really nice!
[…] will be available for other entities as well? The answer is: technically it is. I stumbled upon a blog post at the site of Thrives where the web requests were analysed when calling the ‘Create PDF’ action. It turns out […]
[…] bericht Generate a PDF document from a quote record (and more) verscheen eerst op […]