Lookup Filter in Business Process flow - dynamics-crm

I try to change the lookup filter for the field "Existing Contact?" (in the Lead process flow) but I cannot access the property of this field.

Unfortunately BPF fields property window is not accessible and lookup field property cannot be customized the same way we do any other lookup in form sections.
But there is a way, we can use Javascript to apply the lookup filter using addPreSearch method. This is totally supported. Read more
function filterBpfLookup = function(formContext){
if (formContext.getControl("header_process_mag_contactlookupid") != null){
formContext.getControl("header_process_mag_contactlookupid").addPreSearch(filterDefinition);
}
}
function filterDefinition = function(formContext) {
var accountId = null;
var accountLookup;
var filterBpf;
if (formContext.getControl("header_process_mag_contactlookupid") != null &&
formContext.getControl("header_process_mag_contactlookupid").getAttribute().getValue() != null){
accountLookup = formContext.getControl("header_process_mag_accountlookupid").getAttribute().getValue();
accountId = accountLookup[0].id;
if (accountId != null || accountId != undefined){
filterBpf = "<filter type='and'>" +
"<condition attribute='mag_accountlookupid' operator='eq' value='" + accountId + "' />" +
"</filter>";
formContext.getControl("header_process_mag_contactlookupid").addCustomFilter(filterBpf);
}
}
}

Related

Why won't records delete?

I wrote a plugin that gets the GUID IDs through fetchXML and uses Batch delete to delete the records in batches of 1000.
I debugged the plugin and it shows that the plugin executes all the way through to service.RetrieveMultiple(new FetchExpression(fetchxml)), however, the fetched records are not getting deleted. Can someone explain why? Here is the plugin code:
using System;
using System.Linq;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin will trimm off unit orders after a contract is cancelled before the end of the contract duration
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PostOperation,
ExecutionModeEnum.Asynchronous,
"statecode",
"Post-Update On Cancel Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "")]
public class UnitPluginOnCancel : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Will be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// Output Parameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
//Get the before image of the updated contract
Entity PreImage = context.PreEntityImages["PreImage"];
//verify that target entity is contract and contains a cancellation date
if (entity.LogicalName != "contract" || entity.GetAttributeValue<OptionSetValue>("statecode").Value != 4)
return;
if (PreImage.GetAttributeValue<OptionSetValue>("statecode").Value == 0 || entity.Contains("cancelon"))
return;
if (PreImage.GetAttributeValue<OptionSetValue>("statecode").Value == 3 || PreImage.GetAttributeValue<OptionSetValue>("statecode").Value == 1)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//Core Plugin code in Try Block
try
{
//Get Contract line start date
var startDate = PreImage.GetAttributeValue<DateTime>("cancelon");
//Get Contract Line End Date
DateTime endDate = (DateTime)PreImage["expireson"];
//Get Contract range into weekdays list
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate);
//Get Unit Order Lookup Id
EntityReference unitOrder = (EntityReference)PreImage.Attributes["new_unitorderid"];
var unitOrders = service.Retrieve(unitOrder.LogicalName, unitOrder.Id, new ColumnSet("new_name"));
var unitOrdersId = unitOrders.Id;
var uiName = unitOrders.GetAttributeValue<string>("new_name");
//Get Entity Collection to delete
string fetchXml = #" <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' top='2000'>
<entity name='new_units'>
<link-entity name='new_alterunitorder' from ='new_orderlineid' to = 'new_unitsid' >
<attribute name='new_alterunitorderid' />
<filter type='and'>
<condition attribute='new_orderdate' operator='on-or-after' value='" + startDate.ToString("yyyy-MM-dd") + #"' />
<condition attribute='new_orderdate' operator='on-or-before' value='" + endDate.ToString("yyyy-MM-dd") + #"' />
<condition attribute='new_orderlineid' operator='eq' uiname='" + uiName + #"' uitype='new_units' value='" + unitOrdersId + #"' />
</filter>
</link-entity>
</entity>
</fetch>";
var result = service.RetrieveMultiple(new FetchExpression(fetchXml));
var entityRefs = result.Entities.Select(e => e.GetAttributeValue<EntityReference>("new_alterunitorderid"));
var batchSize = 1000;
var batchNum = 0;
var numDeleted = 0;
while (numDeleted < entityRefs.Count())
{
var multiReq = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = false,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
var currentList = entityRefs.Skip(batchSize * batchNum).Take(batchSize).ToList();
currentList.ForEach(r => multiReq.Requests.Add(new DeleteRequest { Target = r }));
service.Execute(multiReq);
numDeleted += currentList.Count;
batchNum++;
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible. ", ex);
}
catch (Exception ex)
{
tracing.Trace("An error occured: {0}", ex.ToString());
throw;
}
}
}
}
}
I tried this:
var entityGuids = result.Entities.Select(ent => ent.GetAttributeValue<Guid>("new_alterunitorderid"));
var entityRefs = entityGuids.Select(guid => new EntityReference("new_alterunitorder", guid));
Got this:
I think the error is immediately after you execute the RetrieveMultiple request
var entityRefs = result.Entities.Select(e => e.GetAttributeValue<EntityReference>("new_alterunitorderid"));
new_alterunitorderid is not an EntityReference. It is a primary key.
Instead try:
var entityRefs = result.Entities.Select(e => e.ToEntityReference());
You should also make use of your tracing object to include more output in your trace logs:
tracing.Trace("Executing query");
var result = service.RetrieveMultiple(new FetchExpression(fetchXml));
tracing.Trace("{0} results found. Converting to EntityReferences", result.Entities.Count);
var entityRefs = result.Entities.Select(e => e.ToEntityReference());
Usually batch jobs are asynchronous. Go in Settings > System Jobs. Search for your delete job. I never used this specific syntax but the logic seems ok.

System.NullReferenceException: Object not set to an instance of an object?

I am writing a plugin that deletes records between two dates when a contract is cancelled... The records to be deleted are from the cancellation date to the end of the contract. Here is the code I am using:
using System;
using System.Linq;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin will trimm off unit orders after a contract is cancelled before the end of the contract duration
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PostOperation,
ExecutionModeEnum.Asynchronous,
"statecode",
"Post-Update On Cancel Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "")]
public class UnitPluginOnCancel : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Will be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
//Get the before image of the updated contract
Entity PreImage = context.PreEntityImages["PreImage"];
//verify that the target entity represents the the contract entity has been cancelled
if (entity.LogicalName != "contract" || entity.GetAttributeValue<OptionSetValue>("statecode").Value != 4)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//Core Plugin code in Try Block
try
{
//Get Contract line start date
var startDate = entity.GetAttributeValue<DateTime>("cancelon");
//Get Contract Line End Date
DateTime endDate = (DateTime)PreImage["expireson"];
//Get Contract range into weekdays list
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate);
//Get Unit Order Lookup Id
EntityReference unitOrder = (EntityReference)PreImage.Attributes["new_unitorderid"];
var unitOrderId = unitOrder.Id;
var unitOrders = service.Retrieve(unitOrder.LogicalName, unitOrder.Id, new ColumnSet("new_name"));
var uiName = unitOrders.GetAttributeValue<string>("new_name");
//Get Entity Collection to delete
string fetchXml = #" <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' top='2000'>
<entity name='new_units'>
<link-entity name='new_alterunitorder' from ='new_orderlineid' to = 'new_unitsid' >
<attribute name='new_alterunitorderid' />
<filter type='and'>
<condition attribute='new_orderdate' operator='on-or-after' value='" + startDate.ToShortDateString() + #"' />
<condition attribute='new_orderdate' operator='on-or-before' value='" + endDate.ToShortDateString() + #"' />
<condition attribute='new_orderlineid' operator='eq' uiname='" + uiName + #"' uitype='new_units' value='" + unitOrderId + #"' />
</filter>
</link-entity>
</entity>
</fetch>";
var result = service.RetrieveMultiple(new FetchExpression(fetchXml));
var entityRefs = result.Entities.Select(e => e.GetAttributeValue<EntityReference>("new_alterunitorderid"));
var batchSize = 1000;
var batchNum = 0;
var numDeleted = 0;
while (numDeleted < entityRefs.Count())
{
var multiReq = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = false,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
var currentList = entityRefs.Skip(batchSize * batchNum).Take(batchSize).ToList();
currentList.ForEach(r => multiReq.Requests.Add(new DeleteRequest { Target = r }));
service.Execute(multiReq);
numDeleted += currentList.Count;
batchNum++;
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible. ", ex);
}
catch (Exception ex)
{
tracing.Trace("An error occured: {0}", ex.ToString());
throw;
}
}
}
}
}
I am getting a NullReferenceException on line 55... I have literally used the same line for a previous plugin without any problems.. The statecode for a cancelled contract has a value of 4 and I only want the plugin to execute when the contract has been cancelled. Here is an image of the debugging.
I have used this statement before for another plugin that acts on the contract entity and it worked fine, I don't know why this time it's not working. Here is the statement:
//verify that the target entity represents the the contract entity has been cancelled
if (entity.LogicalName != "contract" || entity.GetAttributeValue<OptionSetValue>("statecode").Value != 4)
return;
The entity you get from the input parameters may not have StateCode in it, so .Value is failing.
Maybe try entity.GetAttributeValue<OptionSetValue>("statecode") or entity.Contains("statecode") to see if there's anything there before you dereference .Value.
Since you have the PreImage, you may want to look there for the statecode.
Check to see if you are using the right profile while debugging... If you are using the wrong profile, a null reference will through on a method when it shouldn't!
Hope that helps!

Trying to pull certain data pieces of an LRS record, need catch-try assistance

I have a script that is 90% complete and working. I'm using the older code for getting LRS statements. It is the one that uses tincan.js. I am trying to pull down and parse the following: Actor, verb, object, timestamp and the id under contextActivities. It is the last one that is giving me fits. Could you please look at my last catch/try statement and tell me where I'm going wrong? Thank you.
<!DOCTYPE html>
<!--Parsing "resumed" statements-->
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Get my Statements</title>
<script type="text/javascript" src="build/tincan.js"></script>
</head>
<body>
<h1>Get statements 101</h1>
<div id='response'></div>
<script>
var lrs;
try {
lrs = new TinCan.LRS(
{
endpoint: "https://lrs.adlnet.gov/xapi/",
username: "xapi-tools",
password: "xapi-tools",
allowFail: false
}
);
}
catch (ex) {
console.log("Failed to setup LRS object: " + ex);
// TODO: do something with error, can't communicate with LRS
}
//Pulls all of the resumed statements
lrs.queryStatements(
{
params: {
verb: new TinCan.Verb(
{
id: "http://adlnet.gov/expapi/verbs/completed"
}
),
since: "2016-01-05T08:34:16Z"
},
callback: function (err, sr) {
if (err !== null) {
console.log("Failed to query statements: " + err);
// TODO: do something with error, didn't get statements
return;
}
if (sr.more !== null) {
// TODO: additional page(s) of statements should be fetched
}
var container = document.getElementById('response');
//container.innerHTML = (err !== null ? 'ERROR' : JSON.stringify(sr.statements));
container.innerHTML = (err !== null ? 'ERROR' : parseMyData(sr));
}
}
);
parseMyData = function(result) {
var statements = result.statements;
var output = '';
var name,verb,activity, timestamp, context;
for(var i=0;i<statements.length;i++){
// check the statement for a usable name value
// (priority = actor.name, actor.mbox, actor.account.name)
if(statements[i].actor.name != null && statements[i].actor.name != "") {
name = statements[i].actor.name
}else if(statements[i].actor.mbox != null && statements[i].actor.mbox != "") {
name = statements[i].actor.mbox
}else{
name = statements[i].actor.account.name
}
// check the statement for a usable verb value
// (priority = verb.display['en-US'], verb.id)
try{
verb = statements[i].verb.display['en-US'];
}catch(e){
verb = statements[i].verb.id;
}
// check the activity for a usable value
// (priority = definition.name['en-US'], id)
try{
activity = statements[i].target.definition.name['en-US'];
}catch(e){
activity = statements[i].target.id;
}
try{
timestamp = statements[i].timestamp;
}catch(e){
timestamp = statements[i].timestamp;
}
try{
context = statements[i].target.contextActivities.other['id'];
}catch(e){
context = statements[i].activity.other['id'];
}
output += name + ' - ' +
verb + ' - ' +
activity + ' - ' +
timestamp + ' - ' +
context +
'<br>'
}
return output;
}
</script>
</body>
</html>
You are trying to access a property that will never exist:
statements[i].target.contextActivities
The last portion of that will exist in statements[i].context when it exists, which is not always unless you know the statement ahead of time. Pretty much all of the properties should be checked for null before accessing them, doing so will allow you to remove your try/catch blocks. The property names themselves are very stable and the library explicitly assigns null values to known properties. Additionally the other property will hold an array of Activities (when it is populated) so you should access them by index, so contextActivities.other[0].id etc.
For the target property you should check that it is an instance of a particular class as it can contain multiple types of objects (so can actor but they effectively match). In general for this specific task you may also want to leverage the toString methods where possible, they do very similar work.

Get only one value from the table using LINQ

I have a Customer Table and a Address Table (customer_id, address_forename, address_surname, address_street, address_city, address_zip,
address_storedtime) where customer_id as a foreign key.
One customer can have several address.
Now I am trying to get only the last entered address using LINQ as bellow which should allow me put the address in a string and return that:
CODE:
var customerAddress = (from c in myDB.address
where (c.customer_id == customerId)
select new
{
c.customer_id,
c.address_forename,
c.address_surname,
c.address_street,
c.address_city,
c.address_zip,
c.address_storedtime
}).GroupBy(g => new
{
Customer = .customer_id,
Address = g.address_forename + " " + g.address_surname + " " + g.address_street + " " + g.address_city + " " + g.address_zip
}).Select(g => new
{
g.Key.Customer,
g.Key.Address,
StoredTime = g.Max(x => x.address_storedtime)
}).Disinct();/*First();*/
string result = "";
foreach (var ad in customerAddress)
{
if (ad.Address != null)
{
result = ad.Address;
}
break;
}
return result;
I am getting same address string for different addresses in DB for the Customer whereas I am trying to get only one.
Since you're already filtering by customer id, the grouping clause isn't necessary. You should be able to just order the results for the customer descending and project the address much more simply.
var customerAddress = (from c in myDB.address
where (c.customer_id == customerId)
orderby c.address_storedtime descending
select c.address_forename + " " + c.address_surname + " " + c.address_street + " " + c.address_city + " " + c.address_zip)
.FirstOrDefault();
Replace your foreach by
var result =
customerAdress.Any(ad => ad.Address != null)
? customerAdress.Last(ad => ad.Address != null).Address
: default(string);

Date fiIter Issues in LINQ

I have an ASPX page that collects 5 optional search criteria from a user and returns the result in a grid. Once the criteria are collected and the view button clicked, the code behind generates the filter like we have below
// aSearchCriteria is a class that holds the criteria
...
string filter = string.Empty;
if (!string.IsNullOrEmpty(aSearchCriteria.RegistrationNumber)) filter =
"f.BusinessRegistrationNumber = '" +
aSearchCriteria.BusinessRegistrationNumber + "'";
if (aSearchCriteria.ChangedStartDate != null && aSearchCriteria.ChangedEndDate != null)
{
if (!string.IsNullOrEmpty(filter))
{
filter += " && f.ChangedDate >= '" +
aSearchCriteria.ChangedStartDate.ToShortDateString() +
"' && f.ChangedDate <= '" +
aSearchCriteria.ChangedEndDate.ToShortDateString() + "'";
}
else
{
...
}
}
...
Using (CustomerEntities db = new CustomerEntities())
{
if (!string.IsNullOrEmpty(filter))
{
filter = "f => " + filter;
**return db.Customers.Where(filter).ToList();**
}
else
...
}
...
Sample:
"filter" value: f => f.ChangedDate > '01/01/2012' && f.ChangedDate < '14/01/2012'
Anytime I execute this I get this error message:
The query syntax is not valid. Near term '>', line 6, column 5.
you don't want to make the filter a string. What you should do instead is apply the filtering to the IQueryable you get. Something like:
var customers = db.Customers.AsQueryable();
if (aSearchCriteria.ChangedStartDate != null && aSearchCriteria.ChangedEndDate != null)
{
customers = customers
.Where(c => c.ChangedDate >= aSearchCriteria.ChangedStartDate &&
c.ChangedDate >= aSearchCriteria.ChangedEndDate);
}
// ... apply other filters as necessary
return customers.ToList();
Assuming you are using ObjectQuery.Where(string)
the string predicate should look like
"it.ChangedDate > '01/01/2012' && it.ChangedDate < '14/01/2012'"
(no f=> and the alias of the table is it)
Please confirm it helped :)

Resources