In Microsoft Dynamics 365, users often need to download multiple records from different views in a structured format, such as a PDF. To achieve this, we have implemented a ribbon button that allows users to select and download either active or inactive accounts in a PDF format.
Below is a step-by-step guide on how we accomplished this functionality.
Implementation Steps
- Creating a Flyout Button in the Ribbon
First, we added a Flyout Button to the ribbon and named it “Download PDF”. A flyout button allows us to add multiple actions under a single dropdown menu.

2.Adding Menu Section and Buttons
To the flyout, we added a menu section that contains two buttons:
- active Account
- inactive Account
Each button is responsible for downloading records based on their state.

3.Defining Command and Parameters for Each Button
For both buttons, we configured commands and added two parameters:
- PrimaryControl (optional)
- String Parameter (used to specify whether the user wants active or inactive records)

Command

For the Active Account button, we passed the string parameter value as “Active”. For the
Inactive Account button, we passed the string parameter value as “Inactive”.
4.Implementing JavaScript Logic
Next, we created a JavaScript function to handle the selection and PDF generation. The function checks the string parameter and retrieves the corresponding records.
JavaScript Code Implementation
// Function to dynamically load scripts function loadScript(src, callback) {
debugger;
var script = document.createElement('script'); script.src = src;
script.onload = callback; document.head.appendChild(script);
}
// Load jsPDF and AutoTable plugin dynamically loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.
js', function() {
loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf- autotable/3.5.24/jspdf.plugin.autotable.min.js', function() {
console.log('jsPDF and AutoTable plugin loaded successfully.');
// Now you can use jsPDF
// Main function to download account records as PDF function exportContactsToPDF(primaryControl,status) {
debugger;
//
var currentUrl = window.location.href; console.log("Current URL: " + currentUrl);
//
// Hardcoded view names and their FetchXML filters var viewFetchXmlMap = {
"Active Accounts": `<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="account">
<attribute name="name" />
<attribute name="accountnumber" />
<attribute name="telephone1" />
<attribute name="emailaddress1" />
<attribute name="address1_city" />
<attribute name="accountid" />
<filter type="and">
<condition attribute="statecode" operator="eq" value="0" /> <!-- Active accounts -->
</filter>
</entity>
</fetch>`,
"Inactive Accounts": `<fetch version="1.0" output-format="xml- platform" mapping="logical" distinct="false">
<entity name="account">
<attribute name="name" />
<attribute name="accountnumber" />
<attribute name="telephone1" />
<attribute name="emailaddress1" />
<attribute name="address1_city" />
<attribute name="accountid" />
<filter type="and">
<condition attribute="statecode" operator="eq" value="1" /> <!-- Inactive accounts -->
</filter>
</entity>
</fetch>`,
"All Accounts": `<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="account">
<attribute name="name" />
<attribute name="accountnumber" />
<attribute name="telephone1" />
<attribute name="emailaddress1" />
<attribute name="address1_city" />
<attribute name="accountid" />
</entity>
</fetch>`
};
use)
var viewName = ""; if(status=='active'){
// Hardcoded view name (replace with the actual view name you want to
viewName= "Active Accounts"; // Example: Change this to "Inactive
Accounts" or "All Accounts"
}
else if(status=='inactive') { viewName= "Inactive Accounts";
}
// Get the FetchXML for the selected view var fetchXml = viewFetchXmlMap[viewName];
if (!fetchXml) {
Xrm.Navigation.openAlertDialog({ text: "View not found or FetchXML not defined." });
return;
}
// Retrieve account records using the FetchXML Xrm.WebApi.retrieveMultipleRecords("account", "?fetchXml=" +
encodeURIComponent(fetchXml)).then(
function (result) {
if (result.entities.length > 0) {
// Show an alert with the count of retrieved records Xrm.Navigation.openAlertDialog({
text: "Total Accounts Retrieved: " +
result.entities.length
}).then(function() {
// Generate PDF with the retrieved records generatePdf(result.entities);
});
} else {
// No records found
});
Xrm.Navigation.openAlertDialog({ text: "No records found!"
}
},
function (error) {
// Handle API errors
console.error("Error retrieving records: ", error.message);
Xrm.Navigation.openAlertDialog({ text: "An error occurred while retrieving records. Please try again." });
}
);
}
// Function to generate PDF using jsPDF function generatePdf(accounts) {
// Check if jsPDF is loaded
if (typeof window.jspdf === 'undefined') {
console.error('jsPDF is not loaded. Please check the script tags.');
Xrm.Navigation.openAlertDialog({ text: "PDF generation library is not loaded. Please try again." });
return;
}
// Initialize jsPDF
var doc = new window.jspdf.jsPDF();
// Set PDF title doc.setFontSize(14); doc.text("Account Records", 10, 10);
// Define table headers
var headers = [["Account Name", "Account Number", "Phone", "Email", "City"]];
// Map account data to table rows
var data = accounts.map(a => [
a.name || "N/A", // Handle missing fields
a.accountnumber || "N/A",
a.telephone1 || "N/A",
a.emailaddress1 || "N/A",
a.address1_city || "N/A"
]);
// Generate table using autoTable plugin
doc.autoTable({
head: headers, body: data, startY: 20, theme: 'striped'
});
// Save the PDF doc.save("Account_Records.pdf");
}
5.Registering the JavaScript Function in the Ribbon
- For the Active Account button, we registered the exportContactsToPDF
(primaryControl, “Active”) function.

- For the Inactive Account button, we registered the downloa exportContactsToPDF
dAccounts(primaryControl, “Inactive”) function.

Final Outcome
Once a user clicks the Download PDF button, a dropdown appears with two options:
- Active Account → Downloads all active accounts in a PDF.
- Inactive Account → Downloads all inactive accounts in a PDF.
This approach streamlines the process of retrieving and exporting records, improving efficiency for Dynamics 365 users.
This guide demonstrates how to extend Dynamics 365 functionality using ribbon customizations and JavaScript
Readmore : how to display document templates using a custom subgrid in crm
FAQ’s
You can add a custom ribbon button with JavaScript to select and download active or inactive records in PDF format.
Yes! The implementation allows filtering based on active or inactive accounts using a Flyout button in the ribbon.
No, this solution uses JavaScript and jsPDF to generate PDFs directly within Dynamics 365.