How Can I Join On Two Columns with a LinkEntity in CRM 2011? - dynamics-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.

Related

How to access 'Abbreviation' field of a custom list in NetSuite custom lists

I have a custom list that is used as a matrix option of Inventory item. Its 'Color'. This custom list has an abbreviation column. I am creating a saved search on item and using Color field(join) and trying to access 'Abbreviation' field of color.
Abbreviation on custom list is available on when 'Matrix Option List' is checked.
Can someone please help me achieve this? I tried to do this through script and it seems like we cannot access 'Abbreviation' column through script. I also tried to use script to write a search directly on 'Color' - custom list and get the 'abbreviation' through search columns. It did not work. Is there a way to access 'Abbreviation' from custom lists?
Thanks in Advance
You can access it via suitescript by using the record type "customlist" and the internal id of the list like so:
var rec = nlapiLoadRecord('customlist', 5);
var abbreviation = rec.getLineItemValue('customvalue', 'abbreviation', 1);
nlapiLogExecution('DEBUG', 'abbreviation', abbreviation);
Keep in mind that the third argument of getLineItemValue is the line number, not the internal ID of the item in the list. If you want to find a specifc line item, you may want to use rec.findLineItemValue(group, fldnam, value).
Unfortunately, it doesn't look like this translates to saved searches. The suiteanswer at https://netsuite.custhelp.com/app/answers/detail/a_id/10653 has the following code:
var col = new Array();
col[0] = new nlobjSearchColumn('name');
col[1] = new nlobjSearchColumn('internalid');
var results = nlapiSearchRecord('customlist25', null, null, col);
for ( var i = 0; results != null && i < results.length; i++ )
{
var res = results[i];
var listValue = (res.getValue('name'));
var listID = (res.getValue('internalid'));
nlapiLogExecution('DEBUG', (listValue + ", " + listID));
}
However, whatever part of the application layer translates this into a query doesn't handle the abbreviation field. One thing to keep in mind is that the 'custom list' record is basically a header record, and each individual entry is it's own record that ties to it. You can see some of the underlying structure here, but the takeaway is that you'd need some way to drill-down into the list entries, and the saved search interface doesn't really support it.
I could be wrong, but I don't think there's any way to get it to execute in a saved search as-is. I thought the first part of my answer might help you find a workaround though.
Here is a NetSuite SuiteScript 2.0 search I use to find the internalId for a given abbreviation in a custom list.
/**
* look up the internal id value for an abbreviation in a custom list
* #param string custom_list_name
* #param string abbreviation
* #return int
* */
function lookupNetsuiteCustomListInternalId( custom_list_name, abbreviation ){
var internal_id = -1;
var custom_list_search = search.create({
type: custom_list_name,
columns: [ { name:'internalId' }, { name:'abbreviation' } ]
});
var filters = [];
filters.push(
search.createFilter({
name: 'formulatext',
formula: "{abbreviation}",
operator: search.Operator.IS,
values: abbreviation
})
);
custom_list_search.filters = filters;
var result_set = custom_list_search.run();
var results = result_set.getRange( { start:0, end:1 } );
for( var i in results ){
log.debug( 'found custom list record', results[i] );
internal_id = results[i].getValue( { name:'internalId' } );
}
return internal_id;
}
Currently NetSuite does not allows using join on matrix option field. But as you mentioned, you can use an extra search to get the result, you could first fetch color id from item and then use search.lookupFields as follows
search.lookupFields({ type: MATRIX_COLOR_LIST_ID, id: COLOR_ID, columns: ['abbreviation'] });
Note: Once you have internalid of the color its better to use search.lookupFields rather than creating a new search with search.create.

How to set conditions/filters on references when using Load* methods

I have two tables: Customer and Orders. The customer has a reference to Orders like such:
[Reference]
public List<Order> Orders { get; set; }
The Order class has an attribute Deleted. I'd like to load all Customers (or a subset), and include the Orders, but not the ones with Deleted=true. Can this be done with LoadSelect methods, or what is the recommended way?
Something that would roughly equal the following SQL:
select * from Customers C
join Orders O on O.CustomerId = C.Id
where O.Deleted = False
Is this the best way?
var orderIds = resp.Customers.Select(q => q.Id).ToList();
var allOrders = Db.Select<Order>(o => orderIds.Contains(o.CustomerId) && !o.Deleted);
foreach (var order in allOrders)
{
var cust = resp.Customers.First(q => q.Id == order.custivityId);
if (cust.Orders == null) cust.Orders = new List<Order>();
cust.Orders.Add(order);
}
I've just added a new Merge API in this commit to automatically join a parent collection with their child references that will make this a little easier.
With the new API you can select customers and orders separately and merge the collections together, e.g:
//Select only Customers which have valid orders
var customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Deleted == false)
.SelectDistinct());
//Select valid orders
var orders = db.Select<Order>(o => o.Deleted == false);
customers.Merge(orders); //merge the results together
customers.PrintDump(); //print the results of the merged collection
This change is available from v4.0.39+ that's now available on MyGet.

Webmatrix - Locking down page

I have a page where users can modify their own records in my database. This works well, but i'm trying to tidy up all the security flaws. As you can see from the code below, This page gets data using a querystring parameters. At the moment, anyone can change this query string and edit any record.
How do i incorporate a check using the "RequireAuthenticatedUser" and the matching UserID field in the PropertyInfo table?
Current code:
#{
Layout = "~/_SiteLayout.cshtml";
WebSecurity.RequireAuthenticatedUser();
var db = Database.Open("StayInFlorida");
var rPropertyId = Request.QueryString["PropertyID"];
var Propertyinfo = "SELECT * FROM PropertyInfo WHERE PropertyID=#0";
var qPropertyinfo = db.QuerySingle(Propertyinfo, rPropertyId);
}
Presumably, your PropertyInfo table also holds the property owner's user ID? If so, just include it in the WHERE clause and pass in the CurrentUserId value:
var PropertyInfo = "SELECT * FROM PropertyInfo WHERE PropertyId = #0 AND UserId = #1";
var qPropertyinfo = db.QuerySingle(Propertyinfo, rPropertyId, WebSecurity.CurrentUserId);

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

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 }
}
}
}
};

How do I reorder the fields/columns in a SharePoint view?

I'm adding a new field to a list and view. To add the field to the view, I'm using this code:
view.ViewFields.Add("My New Field");
However this just tacks it on to the end of the view. How do I add the field to a particular column, or rearrange the field order? view.ViewFields is an SPViewFieldCollection object that inherits from SPBaseCollection and there are no Insert / Reverse / Sort / RemoveAt methods available.
I've found removing all items from the list and readding them in the order that I'd like works well (although a little drastic). Here is the code I'm using:
string[] fieldNames = new string[] { "Title", "My New Field", "Modified", "Created" };
SPViewFieldCollection viewFields = view.ViewFields;
viewFields.DeleteAll();
foreach (string fieldName in fieldNames)
{
viewFields.Add(fieldName);
}
view.Update();
You can use the default method:
int newFieldOrderIndex = 1;
SPViewFieldCollection viewFields = view.ViewFields;
viewFields.MoveFieldTo(fieldName, newFieldOrderIndex);
view.Update();
https://msdn.microsoft.com/EN-US/library/microsoft.sharepoint.spviewfieldcollection.movefieldto.aspx
You have to use the follow method to reorder the field
string reorderMethod = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<Method ID=""0,REORDERFIELDS"">
<SetList Scope=""Request"">{0}</SetList>
<SetVar Name=""Cmd"">REORDERFIELDS</SetVar>
<SetVar Name=""ReorderedFields"">{1}</SetVar>
<SetVar Name=""owshiddenversion"">{2}</SetVar>
</Method>";
I had two different lists and similar view. I wanted to update destination list view field order if user change order in source view.
ViewFieldCollection srcViewFields = srcView.ViewFields;
ViewFieldCollection destViewFields = destView.ViewFields;
var srcArray = srcViewFields.ToArray<string>();
var destArray = destViewFields.ToArray<string>();
foreach (var item in destArray)
{
destViewFields.MoveFieldTo(item, Array.IndexOf(srcArray, item));
destView.Update();
}

Resources