Dynamics CRM 2011 - Getting too many rows back from a retrievemultiple on salesordersdetail - dynamics-crm-2011

I have a plugin that is registered Update, Order, Post Operation. In the plugin I perform a retrievemultiple on the salesorderdetail. The problem I'm having is that there are 3 products that make up the order but I am returning 5 rows from the retrieve operation. I have added and deleted the same product multiple times during my testing and I'm not sure if that's what's causing the problem. I was thinking that after deleting a product from the order it may set a flag and get deleted after, but I don't see a status code or state code as an attribute. Why would it return too many rows?
Here is my code...
// Set the properties of the QueryExpression object.
orderDetailQuery.EntityName = "salesorderdetail";
orderDetailQuery.ColumnSet = orderDetailColumnSet;
EntityCollection salesOrderDetail = service.RetrieveMultiple(orderDetailQuery);
orderProductQuery.EntityName = "product";
orderProductQuery.ColumnSet = orderProductColumnSet;
foreach (var orderDetail in salesOrderDetail.Entities)
{
if(orderDetail.Attributes.Contains("productid"))
{
productGuid = ((EntityReference)orderDetail["productid"]).Id;
Entity product = service.Retrieve("product", productGuid, orderProductColumnSet);
}
}
Thank you for the help!!

The code you posted does not show you filtering for the specific Order.
I would expect that to retrieve all entities of that type in the system.
To filter, assuming you are using a QueryByAttribute, is to add an filter along the lines of:
var query = new QueryByAttribute();
query.EntityName = "salesorderdetail";
query.AddAttributeValue("orderid", orderId);//orderId is the Id of the parent Order
orderDetailQuery.EntityName = "salesorderdetail";
orderDetailQuery.ColumnSet = orderDetailColumnSet;
var results = service.RetrieveMultiple(query);
That way you are restricting your query to just products for the given order.

I'm not sure that your filtering is implemented. Here's a shot from the hip on how you could query for instances of SalesOrderDetail entity, fetching the values of fieldName1 and fieldName2 fields provided that the it's linked to the order with guid orderId.
QueryExpression query = new QueryExpression
{
EntityName = "salesorderdetail",
ColumnSet = new ColumnSet("fieldName1", "fieldName2"),
Criteria = new FilterExpression
{
Conditions =
{
new ConditionExpression
{
AttributeName = "orderid",
Operator = ConditionOperator.Equal,
Values = { orderId }
}
}
}
};

Related

NetSuite SuiteScript Join Saved Search not returning values

I have tried all manner of different ways to get this search to work properly. I even removed the filter to return values that I know the search returns. I then added the filter, and got different project ID's and verified the search (through the UI) did the job correctly.
It's a JOIN type search that should accept one parameter (project id) and return two pieces of data. That's it. Any ideas on why my parameter (filter) is not working the way it should?
var filter = new Array();
filter[0] = new nlobjSearchFilter('entityid', 'job', 'is', int_Project_ID);
var results = nlapiSearchRecord('transaction', 'customsearch_[...]', filter, null);
if ( !isNull(results) && results.length > 0 )
Here is a link to the criteria and results tabs of the search in NetSuite.
Imgur Link
try this to set the filters after loading search .
var filter = mySearch.filters;
var newfilters = [];
var filterss = {};
filterss.name = 'department';
filterss.operator = 'IS';
filterss.values = dep_fil;
filterss.join = 'employee';
filterss.summary = 'max';
newfilters.push(search.createFilter(filterss));
mySearch.filters = newfilters;
var searchResults = mySearch.run().getRange({
start : 0,
end : 500
});
you can get the values by using
searchResult.getValue({
name : 'email',
join : 'employee',
summary : 'max'
});
There are diffrent type of summary like max,sum,group etc I think the summmary is the reason.
note that this is suitescript 2 method.

Netsuite Userevent Script

I have a userevent script to change the Field in Contract record from PO record. The Script is running fine. But whenever I edit a contract record and try to submit it : It throws the error "Another user has updated this record since you began editing it. Please close the record and open it again to make your changes".
May I know the reason behind this ?
/*---------------------------------------------------------------------------------------------------------------
Description : Whenever the PO vendor is changed(due to Split vendor) that should replace the same in Contract page record automatically.
Script type : User Event Script
Script id : customscript452
Version : 1.0
Applied to : Contract
----------------------------------------------------------------------------------------------------------------*/
function srchfield()
{
var stRecordid = nlapiGetRecordId(); //returns the contract id
if(stRecordid== undefined || stRecordid== null || stRecordid==' ')
{
}
else
{
var stRecordtype = nlapiGetRecordType(); //returns the contract record type = jobs
var stRecord = nlapiLoadRecord(nlapiGetRecordType(), stRecordid);
nlapiLogExecution('debug','Load Object',stRecord);
var stContractID = stRecord.getFieldValue('entityid'); //returns the value of the field contractid whose fieldid is = entityid
nlapiLogExecution('debug','stContractID',stContractID);
var stCompanyName = stRecord.getFieldValue('companyname'); //returns the value of the field company name whose fieldid is = companyname
nlapiLogExecution('debug','stCompanyName',stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
var arrFilters = new Array(); // This is Array Filters all the Purchase Order Record Search
arrFilters.push(new nlobjSearchFilter('type', null, 'anyof',
[
'PurchOrd'
]));
arrFilters.push(new nlobjSearchFilter('mainline', null, 'is', 'T')); //This is to exclude line level results
arrFilters.push(new nlobjSearchFilter('custbodycontract', null, 'is', stRecordid)); //This is Filters in Contracts Search
var arrColumns = new Array();
arrColumns.push(new nlobjSearchColumn('entity')); //This is Search Column Field in Records
var arrSearchresults = nlapiSearchRecord('purchaseorder', null, arrFilters, arrColumns); //This is Filters in Search Result Purchase Order
if(arrSearchresults== undefined || arrSearchresults== null || arrSearchresults==' ')
{
}
else
{
var length = arrSearchresults.length;
}
if(length== undefined || length== null || length==' ')
{
}
else
{
for (var i = 0; arrSearchresults != null && i < arrSearchresults.length; i++)
{
var objResult = arrSearchresults[i];
var stRecId = objResult.getId();
var stRecType = objResult.getRecordType();
var stCntrctName = objResult.getValue('entity'); //This is Value are Get Purchase Order Records and Field for Vendor = entity
}
}
//var record = nlapiLoadRecord(nlapiGetRecordType(), stRecordid, stCntrctName);
if (stCntrctName =='custentityranking_vendor_name')
{
}
else
{
var stChangeName = stRecord.setFieldValue('custentityranking_vendor_name', stCntrctName); //This is Value are the Set in Main Vendor Field = custentityranking_vendor_name
nlapiSubmitRecord(stRecord, null, null); // Submit the Field Value in Record Type
}
}
}
The User Event script executes as the Contract record is being saved to the database. At the same time, you are loading a second copy of the record from the database and trying to submit the copy as well. This is causing the error you're seeing.
You fix this by just using nlapiSetFieldValue to set the appropriate field on the Contract.
I might also recommend getting more familiar with JavaScript by going through the JavaScript Guide over at MDN. In particular, take a look at the Boolean description so that you know how JavaScript evaluates Boolean expressions. This will help you greatly reduce the amount of code you've written here, as many of your conditionals are unnecessary.
What userevent do you have? It is happening depending on what type of user event and API you are using. Looking at your code, you are trying to load contract record that is already updated at the database. So you might consider below to address your issue. Hope, it helps.
If it is a before submit, you don't need to load the record where the script is deployed.
Just use nlapiGet* and nlapiSet* to get and set values. You also don't need to use nlapiSubmitRecord to reflect the change. With before submit, it executes before the record is being saved to the database. So your changes will still be reflected.
Then if it is after submit, it will be executed after the record has been saved to the database, Thus you might use the following API depending on your needs. Actually, this is the best practice to make sure the solution .
nlapiGetNewRecord - only use this if the script only needs to retrieve info from header and sublists. And nothing to set.
nlapiLookupField - use this if the script only needs to get value/s at the header and nothing from the line.
nlapiSubmitField - the script don't need to load and submit record if the changes only on header. Just use this API.
nlapiLoadRecord and nlapiSubmitRecord- use the former if the script will have changes at the line and then use the latter api to commit it on the database.
Being a user event script code, The code you showed is very not good considering performance.
Here is the sample you can merge
var stRecordid = nlapiGetRecordId(); //returns the contract id
// Every record has an internal id associated with it. No need to add condition explicitly to check if its null
var stRecordtype = nlapiGetRecordType();
var fields = ['entityid','companyname'];
var columns = nlapiLookupField(stRecordtype, stRecordid, fields);
var stContractID = columns.entityid;
var stCompanyName = columns.companyname;
nlapiLogExecution('debug','stContractID/stCompanyName',stContractID+'/'+stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
//
//your code of search
//you can improve that code also by using nlapilook up
nlapiSubmitField(stRecordtype, stRecordid, 'custentityranking_vendor_name', 'name to be updated');

SharePoint CSOM: How to update person or group field using ValidateUpdateListItem?

I am updating a SharePoint list item using the ValidateUpdateListItem method of the client-side object model to prevent creation of a new item version. This basically works fine for all fields except the ones with person or group field type. Does anyone know what is the correct string representation of a user or group value to be used as FieldValue of an ListItemFormUpdateValue object? I have already tried everything that seems reasonable to me (user ID from User Info, login name, lookup-value like combinations of these data etc.) without any success.
I just ran into a problem where updating more than 12 person or group fields with item update caused it to throw an exception. Apparently this is caused due to the list view look up threshold in SP online (12 as of this date).
http://blog.vanmeeuwen-online.nl/2012/07/value-does-not-fall-within-expected.html
To work around that I used the ValidateUpdateListItem method to update the person or group ids. The trick is to assign it a json in the format of
[{"Key":"i:0#.f|membership|user#yoursite.onmicrosoft.com"}]
formValues.Add(new ListItemFormUpdateValue() { FieldName = "AssignedTo", FieldValue = "[{'Key':'i:0#.f|membership|user#yoursite.onmicrosoft.com'}]" });
For multiple values, it can be comma separated. Have not tried it with group but i think it should work.
Hopefully this can be useful for someone.
Unfortunately ListItem.ValidateUpdateListItem method does not support the update of user field value. For example, in the following example AssignedTo field will not be updated:
using (var ctx = GetContext(webUri, userName, password))
{
var list = ctx.Web.Lists.GetByTitle(listTitle);
var item = list.GetItemById(itemId);
var formValues = new List<ListItemFormUpdateValue>();
formValues.Add(new ListItemFormUpdateValue() { FieldName = "Title", FieldValue = taskName});
formValues.Add(new ListItemFormUpdateValue() { FieldName = "AssignedTo", FieldValue = userId.ToString() }); //not supported
item.ValidateUpdateListItem(formValues, true, string.Empty);
ctx.ExecuteQuery();
}
Instead consider ListItem.Update Method to update user field value as demonstrated below:
using (var ctx = GetContext(webUri, userName, password))
{
var list = ctx.Web.Lists.GetByTitle(listTitle);
var item = list.GetItemById(itemId);
item["Title"] = taskName;
var assignedToValue = new FieldUserValue() { LookupId = userId };
var assignedToValues = new[] { assignedToValue };
item["AssignedTo"] = assignedToValues; //multi-valued user field
item.Update();
ctx.ExecuteQuery();
}

Reconstructing an ODataQueryOptions object and GetInlineCount returning null

In an odata webapi call which returns a PageResult I extract the requestUri from the method parameter, manipulate the filter terms and then construct a new ODataQueryOptions object using the new uri.
(The PageResult methodology is based on this post:
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options )
Here is the raw inbound uri which includes %24inlinecount=allpages
http://localhost:59459/api/apiOrders/?%24filter=OrderStatusName+eq+'Started'&filterLogic=AND&%24skip=0&%24top=10&%24inlinecount=allpages&_=1376341370337
Everything works fine in terms of the data returned except Request.GetInLineCount returns null.
This 'kills' paging on the client side as the client ui elements don't know the total number of records.
There must be something wrong with how I'm constructing the new ODataQueryOptions object.
Please see my code below. Any help would be appreciated.
I suspect this post may contain some clues https://stackoverflow.com/a/16361875/1433194 but I'm stumped.
public PageResult<OrderVm> Get(ODataQueryOptions<OrderVm> options)
{
var incomingUri = options.Request.RequestUri.AbsoluteUri;
//manipulate the uri here to suit the entity model
//(related to a transformation needed for enumerable type OrderStatusId )
//e.g. the query string may include %24filter=OrderStatusName+eq+'Started'
//I manipulate this to %24filter=OrderStatusId+eq+'Started'
ODataQueryOptions<OrderVm> options2;
var newUri = incomingUri; //pretend it was manipulated as above
//Reconstruct the ODataQueryOptions with the modified Uri
var request = new HttpRequestMessage(HttpMethod.Get, newUri);
//construct a new options object using the new request object
options2 = new ODataQueryOptions<OrderVm>(options.Context, request);
//Extract a queryable from the repository. contents is an IQueryable<Order>
var contents = _unitOfWork.OrderRepository.Get(null, o => o.OrderByDescending(c => c.OrderId), "");
//project it onto the view model to be used in a grid for display purposes
//the following projections etc work fine and do not interfere with GetInlineCount if
//I avoid the step of constructing and using a new options object
var ds = contents.Select(o => new OrderVm
{
OrderId = o.OrderId,
OrderCode = o.OrderCode,
CustomerId = o.CustomerId,
AmountCharged = o.AmountCharged,
CustomerName = o.Customer.FirstName + " " + o.Customer.LastName,
Donation = o.Donation,
OrderDate = o.OrderDate,
OrderStatusId = o.StatusId,
OrderStatusName = ""
});
//note the use of 'options2' here replacing the original 'options'
var settings = new ODataQuerySettings()
{
PageSize = options2.Top != null ? options2.Top.Value : 5
};
//apply the odata transformation
//note the use of 'options2' here replacing the original 'options'
IQueryable results = options2.ApplyTo(ds, settings);
//Update the field containing the string representation of the enum
foreach (OrderVm row in results)
{
row.OrderStatusName = row.OrderStatusId.ToString();
}
//get the total number of records in the result set
//THIS RETURNS NULL WHEN USING the 'options2' object - THIS IS MY PROBLEM
var count = Request.GetInlineCount();
//create the PageResult object
var pr = new PageResult<OrderVm>(
results as IEnumerable<OrderVm>,
Request.GetNextPageLink(),
count
);
return pr;
}
EDIT
So the corrected code should read
//create the PageResult object
var pr = new PageResult<OrderVm>(
results as IEnumerable<OrderVm>,
request.GetNextPageLink(),
request.GetInlineCount();
);
return pr;
EDIT
Avoided the need for a string transformation of the enum in the controller method by applying a Json transformation to the OrderStatusId property (an enum) of the OrderVm class
[JsonConverter(typeof(StringEnumConverter))]
public OrderStatus OrderStatusId { get; set; }
This does away with the foreach loop.
InlineCount would be present only when the client asks for it through the $inlinecount query option.
In your modify uri logic add the query option $inlinecount=allpages if it is not already present.
Also, there is a minor bug in your code. The new ODataQueryOptions you are creating uses a new request where as in the GetInlineCount call, you are using the old Request. They are not the same.
It should be,
var count = request.GetInlineCount(); // use the new request that your created, as that is what you applied the query to.

How Can I Join On Two Columns with a LinkEntity in CRM 2011?

I am using a Link Entity to join a two entities:
LinkEntity pricelevelentity = new LinkEntity();
pricelevelentity.JoinOperator = JoinOperator.Inner;
pricelevelentity.LinkFromEntityName = "product";
pricelevelentity.LinkFromAttributeName = "productid";
pricelevelentity.LinkToEntityName = "productpricelevel";
pricelevelentity.LinkToAttributeName = "productid";
query.LinkEntities.Add(pricelevelentity);
The above is joining Product and ProductPriceLevel on the ProductId attribute. I also want to add to the join Product.uomid on ProductPriceLevel.DefaultUomId
How can i do this?
If you are attempting to return a column for a linked/joined entity to be able to perform additional filtering on the client side, LinkEntity has a Column property you should use, for example:
new QueryExpression("product")
{
ColumnSet = new ColumnSet("oumid"),
LinkEntities =
{
new LinkEntity("product", "productpricelevel", "productid", "productid", JoinOperator.Inner)
{
Columns = new ColumnSet("defaultuomid")
}
}
};
Not sure how James's answer was marked as the answer, but you can not join on more than one column using a linked entity. You can use James's answer to return both values, and perform a join on the client side though, but CRM doesn't support this type of Join:
Select * FROM A INNER JOIN B ON A.OneId = B.OneId AND A.TwoId = B.TwoId
If you know the value of the Product's UomId, you can add it to a where clause though:
var qe = new QueryExpression("product");
var productPriceLevel = new LinkEntity("product", "productpricelevel", "productid", "productid", JoinOperator.Inner);
qe.LinkEntities = productPriceLevel;
productPriceLevel.LinkCriteria.AddCondition("defaultuomid", ConditionOperator.Equal, product.UomId);
Which would effectively look like this:
Select * FROM A INNER JOIN B ON A.OneId = B.OneId AND B.TwoId = 10 -- the Value of whatever the single A.TwoId value is you're looking for.

Resources