Attaching an SSRS Report In Dynamics 365
Being customer-centric isn’t just an added value that an organization has to offer anymore. It is a quintessential feature that defines a company. It has been proven that it is cost-effective to retain old customers and one of the best ways of doing that is taking care of them. This could mean going the extra mile, observing their consumerist (sans the ideology) behavior, and predicting their requirements before they even narrate. Now in a parallel world would require a mind-reader or a hypnotist but thank God for Microsoft Dynamics 365, you do not have to venture far.
Microsoft is like that magician who keeps on pulling out a trail of colorful kerchiefs from his hat. For a five-year-old, this is the most fascinating act but for us adults, we tend to look up his sleeve. And the sleeve doesn’t seem to disappoint either. In 2001, Microsoft with the acquisition of iCommunicate stepped into the world of CRM and paved the path for revolutionizing the most sought-after sector of any business. Fast forward to 2018, Dynamics 365 was released combining ERP and CRM applications.
So why do we have a plethora of organizations, restricting themselves and employing this CRM application? The reasons are plain and plenty. It is easier, flexible, and scalable. With this, a company can have a unified system and the users do not have to shuffle between systems. This essentially saves time and money. All the teams, sales, marketing, service, and delivery can access the same system at one go, without the issue of restricted access.
Now when companies use CRM software, they do it with the purpose of making their business lives a little less difficult. I would like to take the example of an entertainment company when talking about ease of business.
Usually, with emails that are sent to clients, the attachments must be static in nature. Such as standard boilerplate, and terms and conditions documents. These kinds of documents do not need to be altered. As such, attaching and sending doesn’t take much of a hassle.
However, when such a company requests for dynamic documents such as payment receipts etc. to be sent out, then it becomes the job of the developer to assist the client with the request. Although, sending varied documents isn’t a precondition that Microsoft had insisted upon, the CRM system is scalable and can be tailor-made as per the client’s requirement.
In our case, for the entertainment company, a specific code had to be written. Let’s visit some of the points that had to be covered while taking care of the request.
Prerequisite – The report needs to be created already in the CRM Organization and we need to have the report name and report guide.
Implementation stage:
1. Creating the workflow and mention the triggering action.
2. Creating the email body – mentioning the sender and receiver. Entering the subject and body of the email.
3. Open Visual Studio and create a web reference with the report server name. example for report server name –
http://reportssrs-01/Reportserver/ReportExecution2005.asmx
4. Now create a custom workflow with the following code:
1) using System;
2) using System.Linq;
3) using System.Text;
4) using System.Threading.Tasks;
5) using Microsoft.Xrm.Sdk.Workflow;
6) using Microsoft.Xrm.Sdk;
7) using Microsoft.Xrm.Sdk.Query;
8) using System.Activities;
9) using Microsoft.Xrm.Sdk.Client;
10) using System.Web.Services.Protocols;
11) using Microsoft.Crm.Sdk.Messages;
12) using System.Net;
13)
14) namespace customNamespace
15) {
16) //4077,(Contract) //4182,
17) ////http://rich-cent01/reportserver/ReportService2010.asmx?wsdl
18) public class AttachContractToEmail : CodeActivity
19) {
20) const string ReportPathFromSettings = “Report Path”;
21) const string ReportServerFromSettings = “Report Server”;
22)
23) [Input(“E-Mail”)]
24) [ReferenceTarget(“email”)]
25)
26) public InArgument Email { get; set; }
27)
28) [Input(“inputParameterNeededForReport”)]
29) [ReferenceTarget(“inputparameterneededforreport “)]
30) public InArgument inputParameterNeededForReport { get; set; }
31)
32)
33) protected override void Execute(CodeActivityContext executionContext)
34) {
35) IWorkflowContext context = executionContext.GetExtension();
36) IOrganizationServiceFactory servicefactory = executionContext.GetExtension();
37) IOrganizationService _service = servicefactory.CreateOrganizationService(context.UserId);
38)
39) //Sample guid of contract : 4AE5200F-1000-E611-80C5-005056AA8521
40) string test = inputparameterneededforreport.Get(executionContext);
41) Entity attachment = new Entity(“activitymimeattachment”);
42) byte[] reportresult;
43) DateTime startDate = DateTime.Now.AddMonths(-15);
44) DateTime endDate = DateTime.Now;
45) if (test != null)
46) {
47) ParameterValue[] parameters = new ParameterValue[1];
48) parameters[0] = new ParameterValue();
49) parameters[0].Name = “QuoteId”;
50) parameters[0].Value = string.Format(test);
51)
52) var reportServer = //reportServerNameGoesHere
53) //example for report server name – http://reportssrs-01/Reportserver/ReportExecution2005.asmx
54) CommonMethods rg = new CommonMethods(reportServer, new NetworkCredential(userid, password, domain));
55) reportresult = rg.Render(reportPathGoesHere, FormatType.PDF, parameters);
56)
57) attachment[“objectid”] = Email.Get(executionContext);
58) attachment[“objecttypecode”] = “email”;
59) attachment[“filename”] = //fileNameGoesHere;
60) attachment[“subject”] = //subjectgoeshere;
61) attachment[“body”] = System.Convert.ToBase64String(reportresult);
62) }
63)
64)
65)
66)
67)
68) try
69) {
70) _service.Create(attachment);
71) }
72) catch (Exception ex)
73) {
74) throw new Exception(string.Format(“Error creating attachment – {0}”, ex.Message));
75) }
76)
77) try
78) {
79) _service.Execute(new SendEmailRequest()
80) {
81) EmailId = Email.Get(executionContext).Id,
82) IssueSend = true,
83) TrackingToken = string.Empty
84) });
85) }
86) catch (Exception ex)
87) {
88) throw new Exception(string.Format(“Error sending email – {0}”, ex.Message));
89) }
90) }
91) }
92) }
93)
94)
95)
96) internal CommonMethods(string ServiceUrl, ICredentials credentials)
97) {
98) if (string.IsNullOrEmpty(ServiceUrl))
99) throw new Exception(“Parameter ServiceUrl has to contain value”);
100)
101) if (credentials == null)
102) throw new Exception(“Parameter Credentials has to contain value”);
103)
104) _reportexecutionservice = new ReportExecutionService()
105) {
106) Credentials = credentials,
107) Url = ServiceUrl
108) };
109) }
110)
111)
112) internal byte[] Render(string Report, FormatType formattype)
113) {
114) return this.Render(Report, formattype, new ParameterValue[] { });
115) }
116)
117) internal byte[] Render(string Report, FormatType formattype, ParameterValue[] parameters)
118) {
119) byte[] result = null;
120) string format = GetFormatType(formattype);
121) string historyID = null;
122) string devInfo = @”False”;
123) string encoding;
124) string mimeType;
125) string extension;
126) Warning[] warnings = null;
127) string[] streamIDs = null;
128)
129) try
130) {
131) ExecutionInfo execInfo = new ExecutionInfo();
132) ExecutionHeader execHeader = new ExecutionHeader();
133) _reportexecutionservice.ExecutionHeaderValue = execHeader;
134) execInfo = _reportexecutionservice.LoadReport(Report, historyID);
135) _reportexecutionservice.SetExecutionParameters(parameters, “en-us”);
136) result = _reportexecutionservice.Render(format, devInfo, out extension,
137) out mimeType, out encoding, out warnings, out streamIDs);
138) }
139) catch (Exception ex)
140) {
141) if (ex is SoapException)
142) {
143) SoapException sexc = ex as SoapException;
144) throw new Exception(string.Format(“Error generating report – {0}”, sexc.Detail.InnerText));
145) }
146) else
147) {
148) throw new Exception(string.Format(“Error generating report – {0}”, ex.Message));
149) }
150) }
151)
152) return result;
153) }
154)
155) private string GetFormatType(FormatType formattype)
156) {
157) switch (formattype)
158) {
159) case FormatType.XML:
160) case FormatType.CSV:
161) case FormatType.IMAGE:
162) case FormatType.PDF:
163) case FormatType.MHTML:
164) case FormatType.EXCEL:
165) case FormatType.Word:
166) return formattype.ToString();
167) case FormatType.HTML40:
168) return “HTML4.0”;
169) case FormatType.HTML32:
170) return “HTML3.2”;
171) default:
172) throw new Exception(string.Format(“Rendering type {0} is not available”, formattype));
173) }
174) }
175)
176)
177) }
178)
179) internal enum FormatType
180) {
181) XML, CSV, IMAGE, PDF, HTML40, HTML32, MHTML, EXCEL, Word
182) }
5. Deploying the custom workflow.
6. Once the custom workflow has been deployed, we need to call the custom workflow inside the main workflow. To do it, click on the add step and find the custom workflow that has just been deployed.
7. Once the custom workflow has been called, you need to declare the input parameters for the workflow. The sample code has been written with one input parameter. You can modify the code to accept more input parameters.
I would like to reiterate, that one of the primary reasons that Dynamics 365 has gained leverage is because of its scalability. The future of CRM is more than maintaining customer relationships. It is the future of any business. With an application like Dynamics 365, Microsoft has revolutionized the whole industry. Now entrepreneurs don’t just yearn to gain more customers but strive to find applied business intelligence to retain the old ones as well.
It’s fascinating to witness this kind of radical change brought to the genre of customer relationships by Microsoft. All we can do is wait with bated breath for the next revolution. And we know it’s not going to be too far.
We at AhaApps employ Microsoft’s applications to help our customers in making their business agile and seamless. Our Staffing Solutions and CRM consultancy services ensure that we take care of our client’s requirements through applied business intelligence, analytics, and best practices. We endeavor to drive efficiency, find solutions, and help businesses grow. To know more about our exclusive services, do contact us today.