I work for a charity and we want to use our Dynamics 365 to support our fundraising team. We have created a 'Fundraising Event' entity with an N:N relationship to the Contact entity (renamed to 'Individual' in our Org. I have a sub grid on each entity so that we can add contacts to events either from the Contact record or from the event record.
We need to be able to send emails to everyone registered for an event to wish them good luck before the event. I have created a workflow against the 'Fundraising Event' entity to run 7 days before an event start date however when I an testing this the System job shows "Error; Needs Attention" and when I look at the process it shows a message "The email must have at least one recipient before it can be sent."
Process error image
In the WF send email properties, I have tried using 'Contact' and 'Named Contact(Lookup)' from the list I have available (see screen shot below) in the TO field but I get the same result with both options.
To line option list image
My test event has 2 Individuals (Contacts) linked to it and I can see them both in the sub grid on the event record but I can't seem to be able to send emails to them.
Here are the details from a failed process:
Plugin Trace:
[Microsoft.Xrm.Sdk.Workflow: Microsoft.Xrm.Sdk.Workflow.Activities.SendEmail]
[SendEmailStep1]
Error Message:
Unhandled Exception:
System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: The e-mail must have at least one recipient before it can be sentDetail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>9161cfb2-308e-4b19-af39-b859a78f132f</ActivityId>
<ErrorCode>-2147218684</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>The e-mail must have at least one recipient before it can be sent</Message>
<Timestamp>2017-03-31T11:07:27.6562583Z</Timestamp>
<ExceptionSource i:nil="true" />
<InnerFault>
<ActivityId>9161cfb2-308e-4b19-af39-b859a78f132f</ActivityId>
<ErrorCode>-2147218684</ErrorCode>
<ErrorDetails xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>The e-mail must have at least one recipient before it can be sent</Message>
<Timestamp>2017-03-31T11:07:27.6562583Z</Timestamp>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText i:nil="true" />
</InnerFault>
<OriginalException i:nil="true" />
<TraceText>[Microsoft.Xrm.Sdk.Workflow: Microsoft.Xrm.Sdk.Workflow.Activities.SendEmail]
[SendEmailStep1]
</TraceText>
</OrganizationServiceFault>
at Microsoft.Crm.Workflow.Services.SendEmailActivityService.Execute(ActivityContext executionContext, SendEmail sendEmail)
at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)
Is it possible to do what I'm attempting using out of the box functionality? If so, what am I missing?
Thanks,
Paul
What you are trying to achieve here is not possible just by configuration. You have this error because you are not referring to the contacts that you have in your subgrid, you are referring to an attribute named "Contact" which is probably empty and that is causing the error. The only way would be to create a Custom Workflow Activity, that will get all the necessary contacts and put it in your email. If you do not want to code, you can use Aiden Kaskela's workflow elements to achieve your goal:
https://kaskelasolutions.com/how-to-add-dynamic-recipients-to-an-email/
We did something like this.
Register a plugin in Pre-operation of Email Create message & have this logic in there.
This will add the recipients from N:N contact intersect entity in email To list.
ConditionExpression frCondition1 = new ConditionExpression();
ConditionExpression frCondition2 = new ConditionExpression();
EntityCollection frResponse = new EntityCollection();
EntityCollection toOwner = new EntityCollection();
toOwner = targetEntity.GetAttributeValue<EntityCollection>("to");
frCondition1 = CreateConditionExpression("parentIdcolummn", ConditionOperator.Equal, new string[] { parent.Id.ToString() });
frCondition2 = CreateConditionExpression(statuscode, ConditionOperator.Equal, new object[] { 1 });
ConditionExpression[] conditions = new ConditionExpression[] { frCondition1, frCondition2 };
FilterExpression filterExpression = new FilterExpression();
filterExpression.Conditions.AddRange(conditions);
filterExpression.FilterOperator = LogicalOperator.And;
QueryExpression queryExpression = new QueryExpression();
queryExpression.ColumnSet = GenerateColumnSet("columnNames");
queryExpression.EntityName = "entityName";
queryExpression.Criteria = filterExpression;
queryExpression.NoLock = true;
frResponse = (EntityCollection)service.RetrieveMultiple(queryExpression);
if(frResponse != null && frResponse.Entities.Count > 0)
{
//Adding all FRs in the email
foreach (Entity FilingRep in frResponse.Entities)
{
Guid frGuid = ((EntityReference)FilingRep["contactLookupfieldname"]).Id;
Entity toParty = new Entity("activityparty");
toParty[ActivityParty.ActivityPartId] = new EntityReference("contact", frGuid);
ToOwner.Entities.Add(toParty);
}
}
targetEntity[EmailEntityAttributeName.To] = toOwner;
Related
I have cretaed an Emaail service and associated an Apex class with it. The Apex class does a simple job of checking the subject of the mail and insert a record in the contact object, if the suject matches witha particular string. The last name of the contact is extracted from the inbound mail body.
The code is as follows:
global class CreateContactFrmEmail implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
String subToCompare = 'Create Contact';
if(email.subject.equalsIgnoreCase(subToCompare))
{
Contact c = new Contact();
c.LastName = email.plainTextBody;
insert c;
}
result.success = true;
return result;
}
}
However, whenever I send a mail to the generated email address, it is unable to create the contact, the mail bounces and the following exception is reported.
Any help in this regard is appreciated. TIA.
I'm using the REST interface detailed here -https://learn.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ff521587(v%3Doffice.14)
As a proof of concept, I'm trying to access an item in a list from our Sharepoint 2010 intranet site and update a field.
To do this, I created a Connected OData service to the endpoint (AccountingWorkflowsDataContext) and here is my code -
var tasks = new AccountingWorkflowsDataContext(new Uri(
"https://mysite/_vti_bin/ListData.svc"));
tasks.Credentials = new NetworkCredential("username", "password", "domain");
var task = tasks.GLRecsTasks.Where(x => x.StatusValue != "Completed" && (x.AssignedToId == 1 || x.AssignedToId == 2)).First();
task.DueDate = DateTime.Now.AddDays(5);
tasks.UpdateObject(task);
tasks.SaveChanges();
I can connect to the list and I'm picking up the correct item and I'm tracking changes to it as well, however, the call to SaveChanges always fails with the message "An error occurred while processing this request." and the innner exception is -
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code></code>
<message xml:lang="en-US">An error occurred while processing this request.</message>
</error>
Can someone shed some light on this.
I had to use the Managed Client Object Model to accomplish this.
I was attempting to retrieve an open Workflow Task assigned to a specific user and assign it to a different person and for some reason, the REST interface was throwing an error during the reassignment.
Here is my code utilizing the Managed Client Object Model (NOT Production-ready but something to start out with)
using (ClientContext context = new ClientContext("siteURL"))
{
context.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo("login", "password");
Web web = context.Web;
var result = context.Web.Lists.GetByTitle("TaskListName");
var query = new CamlQuery()
{
ViewXml =
"<View><Query><Where><And><Eq><FieldRef Name='Status' /><Value Type='Text'>Not Started</Value></Eq><Eq><FieldRef Name='AssignedTo' /><Value Type='User'>UserName</Value></Eq></And></Where></Query></View>"
};
var items = result.GetItems(query);
context.Load(items);
context.ExecuteQuery();
foreach (var item in items)
{
item["AssignedTo"] = new FieldUserValue() { LookupId = NewUserID };
item.Update();
}
context.ExecuteQuery();
}
I'm working on a WCF CustomBehavior that plugs into a BizTalk 2010 SendPort in order to call a JSON/REST service. The SendPort is WCF/Custom with Binding Type=webHttpBinding.
I need to add the four headers to the request.
When I include this code in my BeforeSendRequest method:
var newHttpRequestMessageProperty = new HttpRequestMessageProperty
{
Method = request.Headers.Action,
QueryString = string.Empty,
SuppressEntityBody = false,
};
newHttpRequestMessageProperty.Headers.Add("X-ST-PartnerID", partnerIDFromSSO);
newHttpRequestMessageProperty.Headers.Add("X-ST-Token", tokenIDFromSSO);
newHttpRequestMessageProperty.Headers.Add("X-ST-AuthType", "TOKEN");
newHttpRequestMessageProperty.Headers.Add("Accept-Language", "EN");
request.Properties[HttpRequestMessageProperty.Name] = newHttpRequestMessageProperty;
It gives me this error:
System.ArgumentException: Cannot set null or blank methods on request.
Parameter name: value
Server stack trace:
at System.Net.HttpWebRequest.set_Method(String value)
at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.PrepareHttpSend(Message message)
at System.ServiceModel.Channels.HttpOutput.BeginSendCore(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, Object state)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.SendWebRequest()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.OnGetWebRequestCompleted(IAsyncResult result)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.BeginSendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, Object state)
at System.ServiceModel.Dispatcher
When I leave that code out, I get a 415 back from the partner's web service, so I know that I'm getting there at least.
What I'm doing is similar to this post: How to add a custom HTTP header to every WCF call?
From a co-worker, I changed:
//Method = request.Headers.Action,
Method = "POST",
and it worked...
The reason was, it turns out that when you have an invalid "OperationName", then request.Headers.Action had the entire CustomAction string from the BizTalk sendport:
<BtsActionMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Operation Name="DELETE" Action="DELETE" />
<Operation Name="GET" Action="GET" />
<Operation Name="POST" Action="POST" />
</BtsActionMapping>
I'm modeling my code after sample here:
https://biztalkrest.codeplex.com/discussions/657185
After the above fix, it dawned on me to match the "Operation Name" in the orchestration's logical port, and sure enough, I had "Post" instead of "POST".
Ughhh... So apparently when the OperationName doesn't match the CustomAction operations, BizTalk just sends the entire CustomAction XML block on through... Amazing...
In picture below is where I changed from "Post" to "POST".
I have a situation to use a <int-http:outbound-channel-adapter ... /> to send an object with information stored in the header.
Following works when I call the <int-http:inbound-channel-adapter ... /> like follows:
public void openTicket(final Profile profile, final Ticket ticket) {
final HttpHeaders headers = new HttpHeaders();
headers.set("profile", profile.toString());
final HttpEntity<Ticket> entity = new HttpEntity<Ticket>(ticket, headers);
template.exchange(URL, HttpMethod.PUT, entity, Ticket.class);
}
This calls my inboung-channel-adapter successful with the given profile in the headers:
<int-http:inbound-channel-adapter
channel="api_app_integration_request_channel"
supported-methods="PUT"
path="/process/ticket"
request-payload-type="*.model.Ticket"
mapped-request-headers="profile"
error-channel="internal-client-rest-ticket-error-channel"
>
<int-http:request-mapping consumes="application/json" />
</int-http:inbound-channel-adapter>
What doesnt work is calling the service via outbound-channel-adapter, the call itself works, but my header 'profile' is gone.
<int-http:outbound-channel-adapter
channel="client_rest_ticket_outbound_channel"
http-method="PUT"
url="http://localhost:8080/process/ticket"
mapped-request-headers="profile"
/>
I am using Spring-Boot 1.3.6.RELEASE.
Custom headers are (currently) mapped with an X- prefix by default; to map them without the prefix you need to wire up a DefaultHttpHeaderMapper with userDefinedHeaderPrefix set to null (or "") as well as the outbound header name(s) you want to map.
See the documentation.
EDIT:
<bean class="org.springframework.integration.http.support.DefaultHttpHeaderMapper" id="headerMapper"
p:userDefinedHeaderPrefix=""
p:inboundHeaderNames="profile"
p:outboundHeaderNames="profile"
/>
I'm trying to create my custom validation handler for some custom fields on workflow form. A have two fields: project begin date and project end date.
For those two field i need two types of validation:
project begin date can not be less then today's date
project end date must be equal or grater then project begin date
However i'm still on beginning of creation custom validation-handlers. For start i just tried some simple validation handler for text field with numbers only but this isn't functioning. I did the following:
In my share-config-custom.xml i add
<config>
<forms>
<dependencies>
<js src="/custom-form-validation.js" />
</dependencies>
</forms>
and connect to my field
<field id="mcwm:shortProjectNumber" set="general" mandatory="true">
<constraint-handlers>
<constraint type="MANDATORY" validation-handler="Alfresco.forms.validation.checkNumber" event="keyup" />
</constraint-handlers>
</field>
custom-form-validation.js file is
Alfresco.forms.validation.checkNumber = function checkNumber(field, args, event, form, silent, message) {
var ok = (field.value=="") || field.value.match("^\\d{4}\\/\\d{4}$");
var valid = ok != null && ok;
if (!valid) {
YAHOO.util.Dom.setStyle(field.id, "border", "2px solid red");
}
else {
YAHOO.util.Dom.setStyle(field.id, "border", "");
}
// Inform the user if invalid
if (!valid && !silent && form)
{
var msg = "The number must match the pattern 1234/5678.";
form.addError(form.getFieldLabel(field.id) + " " + msg, field);
}
return valid;
};
I put custom-form-validation.js file in my opt\tomcat\shared\classes\alfresco\web-extension folder, but there is no field validation at all? Any suggestion on this?
Regards,
Aleksadnar
Field validation in Share is implemented as client-side JavaScript. So your JavaScript file goes in the root of your web app (preferably a directory structure below that), not under WEB-INF. For example, you might want to put it under $TOMCAT_HOME/webapps/share/extension/js. You may have to adjust your dependencies reference accordingly.
Next, don't put custom code in Alfresco's namespace. Create your own. You can create your own namespace by placing something like this in the beginning of your JavaScript file:
if (typeof SomeCo == "undefined" || !SomeCo)
{
var SomeCo = {};
}
You may also want to minify your JavaScript. Then, turn on Share debug. You do that by placing the following in share-config-custom.xml (which lives under $TOMCAT_HOME/webapps/share/WEB-INF/classes/alfresco/web-extension):
<alfresco-config>
<!-- Put Share Client in debug mode -->
<config replace="true">
<flags>
<client-debug>false</client-debug>
<client-debug-autologging>false</client-debug-autologging>
</flags>
</config>
</alfresco-config>
With that in place you can use your browser's developer console to step through the client side JavaScript and figure out why your validator isn't working.
You can use an alternative way to implement the form field validation handler, using javascript directly. I mean, you can add a listener to control when the form is submitted, and add a condition to cancel the submition. So, you could use a code like this when the form is loaded:
//Add a listener to control when the form is submitted
document.body.addEventListener("submit", function (event) {
//If there are wrong values
if(wrongValues()){
//Cancel the event
event.stopPropagation();
event.preventDefault();
}
}, true);
If you want more details about this solution, you can visit this post